[hamradio-commits] [qsstv] 01/02: Imported Upstream version 8.2.12

Dave Hibberd hibby-guest at moszumanska.debian.org
Tue May 24 19:42:18 UTC 2016


This is an automated email from the git hooks/post-receive script.

hibby-guest pushed a commit to branch master
in repository qsstv.

commit 51cf4b5c7694be6d755d8ce0a944f2931c0d1baa
Author: hibby <hibby at dashie.foxk.it>
Date:   Tue May 24 20:41:37 2016 +0100

    Imported Upstream version 8.2.12
---
 qsstv/COPYING                                      |  684 ++++
 qsstv/Doxyfile                                     | 1813 +++++++++
 qsstv/configdialog.cpp                             |  557 +++
 qsstv/configdialog.h                               |   52 +
 qsstv/configform.ui                                | 2670 +++++++++++++
 qsstv/configparams.cpp                             |  110 +
 qsstv/configparams.h                               |   91 +
 qsstv/dispatcher.cpp                               |  568 +++
 qsstv/dispatcher.h                                 |   80 +
 qsstv/dispatchevents.h                             |  567 +++
 qsstv/drmprofileform.cpp                           |  167 +
 qsstv/drmprofileform.h                             |   40 +
 qsstv/drmprofileform.ui                            |  785 ++++
 qsstv/drmrx/bits2bytes.cpp                         |  114 +
 qsstv/drmrx/channeldecode.cpp                      | 1140 ++++++
 qsstv/drmrx/crc16_bytewise.cpp                     |  138 +
 qsstv/drmrx/crc8_c.cpp                             |   31 +
 qsstv/drmrx/deflate_uncompress.cpp                 |  294 ++
 qsstv/drmrx/deinterleaver.cpp                      |  127 +
 qsstv/drmrx/demodulator.cpp                        | 1122 ++++++
 qsstv/drmrx/demodulator.h                          |  123 +
 qsstv/drmrx/drm.cpp                                |  146 +
 qsstv/drmrx/drm.h                                  |  107 +
 qsstv/drmrx/drmconstellationframe.cpp              |   78 +
 qsstv/drmrx/drmconstellationframe.h                |   28 +
 qsstv/drmrx/drmconstellationframe.ui               |  173 +
 qsstv/drmrx/drmdatainput.cpp                       |  879 +++++
 qsstv/drmrx/drmdatainput.h                         |   29 +
 qsstv/drmrx/drmdefs.h                              |   40 +
 qsstv/drmrx/drmproto.h                             |   97 +
 qsstv/drmrx/drmpsdframe.cpp                        |   71 +
 qsstv/drmrx/drmpsdframe.h                          |   27 +
 qsstv/drmrx/drmpsdframe.ui                         |  173 +
 qsstv/drmrx/drmstatusframe.cpp                     |  253 ++
 qsstv/drmrx/drmstatusframe.h                       |   45 +
 qsstv/drmrx/drmstatusframe.ui                      | 1157 ++++++
 qsstv/drmrx/filter1.cpp                            |   50 +
 qsstv/drmrx/filter1c.cpp                           |   50 +
 qsstv/drmrx/fixform.cpp                            |   44 +
 qsstv/drmrx/fixform.h                              |   25 +
 qsstv/drmrx/fixform.ui                             |  157 +
 qsstv/drmrx/getfoffsint.cpp                        |  163 +
 qsstv/drmrx/getmode.cpp                            |  314 ++
 qsstv/drmrx/getofdm.cpp                            |  332 ++
 qsstv/drmrx/getofdmsync.cpp                        |  352 ++
 qsstv/drmrx/getsymbolidx.cpp                       |  139 +
 qsstv/drmrx/hybridcrypt.cpp                        |  135 +
 qsstv/drmrx/lubksb.cpp                             |   21 +
 qsstv/drmrx/ludcmp.cpp                             |   58 +
 qsstv/drmrx/mkfacmap.cpp                           |  259 ++
 qsstv/drmrx/mkmap.h                                |    4 +
 qsstv/drmrx/mkmscmap.cpp                           |  423 +++
 qsstv/drmrx/msd_hard.h                             |   47 +
 qsstv/drmrx/msd_hard_sdc.h                         |   89 +
 qsstv/drmrx/msdhardfac.cpp                         |  736 ++++
 qsstv/drmrx/msdhardmsc.cpp                         |  700 ++++
 qsstv/drmrx/newfft.cpp                             |  206 +
 qsstv/drmrx/nrutil.cpp                             |  702 ++++
 qsstv/drmrx/nrutil.h                               |  103 +
 qsstv/drmrx/psdcmean.cpp                           |  100 +
 qsstv/drmrx/psdmean.cpp                            |   85 +
 qsstv/drmrx/resamplefilter.h                       |   49 +
 qsstv/drmrx/sourcedecoder.cpp                      |  803 ++++
 qsstv/drmrx/sourcedecoder.h                        |  213 ++
 qsstv/drmrx/structtemplates.h                      |  185 +
 qsstv/drmrx/viterbi_decode.cpp                     |  305 ++
 qsstv/drmrx/viterbi_decode.h                       |   92 +
 qsstv/drmtx/bsrform.cpp                            |   92 +
 qsstv/drmtx/bsrform.h                              |   41 +
 qsstv/drmtx/bsrform.ui                             |   85 +
 qsstv/drmtx/common/DRMSignalIO.cpp                 |  142 +
 qsstv/drmtx/common/DRMSignalIO.h                   |  110 +
 qsstv/drmtx/common/DataIO.cpp                      |   72 +
 qsstv/drmtx/common/DataIO.h                        |  129 +
 qsstv/drmtx/common/DrmTransmitter.cpp              |  232 ++
 qsstv/drmtx/common/DrmTransmitter.h                |  127 +
 qsstv/drmtx/common/FAC/FAC.cpp                     |  545 +++
 qsstv/drmtx/common/FAC/FAC.h                       |   71 +
 qsstv/drmtx/common/GlobalDefinitions.h             |  290 ++
 qsstv/drmtx/common/OFDM.cpp                        |  133 +
 qsstv/drmtx/common/OFDM.h                          |   74 +
 qsstv/drmtx/common/Parameter.cpp                   | 1362 +++++++
 qsstv/drmtx/common/Parameter.h                     | 1189 ++++++
 qsstv/drmtx/common/SDC/SDC.h                       |   70 +
 qsstv/drmtx/common/SDC/SDCTransmit.cpp             |  556 +++
 qsstv/drmtx/common/csoundout.cpp                   |   25 +
 qsstv/drmtx/common/csoundout.h                     |   16 +
 qsstv/drmtx/common/datadecoding/DABMOT.cpp         | 1923 ++++++++++
 qsstv/drmtx/common/datadecoding/DABMOT.h           |  615 +++
 qsstv/drmtx/common/datadecoding/DataDecoder.cpp    |  176 +
 qsstv/drmtx/common/datadecoding/DataDecoder.h      |   80 +
 qsstv/drmtx/common/datadecoding/MOTSlideShow.cpp   |  131 +
 qsstv/drmtx/common/datadecoding/MOTSlideShow.h     |   68 +
 .../drmtx/common/interleaver/BlockInterleaver.cpp  |   69 +
 qsstv/drmtx/common/interleaver/BlockInterleaver.h  |   48 +
 .../drmtx/common/interleaver/SymbolInterleaver.cpp |  113 +
 qsstv/drmtx/common/interleaver/SymbolInterleaver.h |   67 +
 qsstv/drmtx/common/matlib/Matlib.h                 |  862 +++++
 qsstv/drmtx/common/matlib/MatlibSigProToolbox.cpp  |  606 +++
 qsstv/drmtx/common/matlib/MatlibSigProToolbox.h    |  136 +
 qsstv/drmtx/common/matlib/MatlibStdToolbox.cpp     |  777 ++++
 qsstv/drmtx/common/matlib/MatlibStdToolbox.h       |  291 ++
 qsstv/drmtx/common/mlc/BitInterleaver.cpp          |  145 +
 qsstv/drmtx/common/mlc/BitInterleaver.h            |   75 +
 qsstv/drmtx/common/mlc/ChannelCode.cpp             |  189 +
 qsstv/drmtx/common/mlc/ChannelCode.h               |   70 +
 qsstv/drmtx/common/mlc/ConvEncoder.cpp             |  236 ++
 qsstv/drmtx/common/mlc/ConvEncoder.h               |   71 +
 qsstv/drmtx/common/mlc/EnergyDispersal.cpp         |   90 +
 qsstv/drmtx/common/mlc/EnergyDispersal.h           |   55 +
 qsstv/drmtx/common/mlc/MLC.cpp                     |  492 +++
 qsstv/drmtx/common/mlc/MLC.h                       |  142 +
 qsstv/drmtx/common/mlc/QAMMapping.cpp              |  175 +
 qsstv/drmtx/common/mlc/QAMMapping.h                |   60 +
 .../common/ofdmcellmapping/CellMappingTable.cpp    |  621 ++++
 .../common/ofdmcellmapping/CellMappingTable.h      |  150 +
 .../common/ofdmcellmapping/OFDMCellMapping.cpp     |  152 +
 .../drmtx/common/ofdmcellmapping/OFDMCellMapping.h |   59 +
 qsstv/drmtx/common/soundinterface.h                |   56 +
 .../common/sourcedecoders/AudioSourceDecoder.cpp   |  138 +
 .../common/sourcedecoders/AudioSourceDecoder.h     |  120 +
 qsstv/drmtx/common/tables/TableAMSS.h              |   52 +
 qsstv/drmtx/common/tables/TableCarMap.h            |  242 ++
 qsstv/drmtx/common/tables/TableCarrier.h           |   97 +
 qsstv/drmtx/common/tables/TableDRMGlobal.h         |   88 +
 qsstv/drmtx/common/tables/TableFAC.cpp             |  992 +++++
 qsstv/drmtx/common/tables/TableFAC.h               |  102 +
 qsstv/drmtx/common/tables/TableMLC.h               |  488 +++
 qsstv/drmtx/common/tables/TableQAMMapping.h        |   87 +
 qsstv/drmtx/common/util/Buffer.h                   |  344 ++
 qsstv/drmtx/common/util/CRC.cpp                    |  219 ++
 qsstv/drmtx/common/util/CRC.h                      |   60 +
 qsstv/drmtx/common/util/Modul.h                    |  411 ++
 qsstv/drmtx/common/util/Utilities.cpp              |  310 ++
 qsstv/drmtx/common/util/Utilities.h                |  123 +
 qsstv/drmtx/config.h                               |  114 +
 qsstv/drmtx/drmtransmitter.cpp                     |  158 +
 qsstv/drmtx/drmtransmitter.h                       |   39 +
 qsstv/dsp/downsamplefilter.cpp                     |  146 +
 qsstv/dsp/downsamplefilter.h                       |   53 +
 qsstv/dsp/filter.cpp                               |  241 ++
 qsstv/dsp/filter.h                                 |   69 +
 qsstv/dsp/filterparam.cpp                          | 3917 ++++++++++++++++++++
 qsstv/dsp/filterparam.h                            |   69 +
 qsstv/dsp/nco.h                                    |  117 +
 qsstv/dsp/synthes.cpp                              |  263 ++
 qsstv/dsp/synthes.h                                |   73 +
 qsstv/editor/editor.cpp                            |  311 ++
 qsstv/editor/editor.h                              |   87 +
 qsstv/editor/editorform.ui                         | 1661 +++++++++
 qsstv/editor/editorscene.cpp                       |  751 ++++
 qsstv/editor/editorscene.h                         |  140 +
 qsstv/editor/editorview.cpp                        |  612 +++
 qsstv/editor/editorview.h                          |  108 +
 qsstv/editor/gradientdialog.cpp                    |  304 ++
 qsstv/editor/gradientdialog.h                      |   79 +
 qsstv/editor/gradientform.ui                       |  438 +++
 qsstv/editor/graphicitems.cpp                      |  595 +++
 qsstv/editor/graphicitems.h                        |  219 ++
 qsstv/editor/qdialog_p.h                           |  113 +
 qsstv/editor/textform.ui                           |   96 +
 qsstv/gallerywidget.cpp                            |  320 ++
 qsstv/gallerywidget.h                              |   48 +
 qsstv/gallerywidget.ui                             |  215 ++
 qsstv/icons/arrow.png                              |  Bin 0 -> 298 bytes
 qsstv/icons/camera.png                             |  Bin 0 -> 1627 bytes
 qsstv/icons/colorfill.png                          |  Bin 0 -> 1338 bytes
 qsstv/icons/colorline.png                          |  Bin 0 -> 1253 bytes
 qsstv/icons/colorpicker.png                        |  Bin 0 -> 317 bytes
 qsstv/icons/colorselector.png                      |  Bin 0 -> 2101 bytes
 qsstv/icons/doubletone.png                         |  Bin 0 -> 233 bytes
 qsstv/icons/edit.png                               |  Bin 0 -> 1249 bytes
 qsstv/icons/eraser.png                             |  Bin 0 -> 1226 bytes
 qsstv/icons/fcircle.png                            |  Bin 0 -> 959 bytes
 qsstv/icons/filenew.png                            |  Bin 0 -> 825 bytes
 qsstv/icons/fileopen.png                           |  Bin 0 -> 1356 bytes
 qsstv/icons/filesave.png                           |  Bin 0 -> 821 bytes
 qsstv/icons/frect.png                              |  Bin 0 -> 394 bytes
 qsstv/icons/gradient.png                           |  Bin 0 -> 787 bytes
 qsstv/icons/image.png                              |  Bin 0 -> 1140 bytes
 qsstv/icons/line.png                               |  Bin 0 -> 624 bytes
 qsstv/icons/mgc.raw                                |  Bin 0 -> 168 bytes
 qsstv/icons/mgc2.raw                               |    1 +
 qsstv/icons/qsstv.png                              |  Bin 0 -> 1091 bytes
 qsstv/icons/qsstvsplash.png                        |  Bin 0 -> 103931 bytes
 qsstv/icons/replay.png                             |  Bin 0 -> 1417 bytes
 qsstv/icons/start.png                              |  Bin 0 -> 1263 bytes
 qsstv/icons/stop.png                               |  Bin 0 -> 1122 bytes
 qsstv/icons/sweep.png                              |  Bin 0 -> 278 bytes
 qsstv/icons/text.png                               |  Bin 0 -> 517 bytes
 qsstv/icons/tone.png                               |  Bin 0 -> 358 bytes
 qsstv/icons/transparency.png                       |  Bin 0 -> 152 bytes
 qsstv/icons/whatsthis.png                          |  Bin 0 -> 216 bytes
 qsstv/logbook/logbook.cpp                          |  100 +
 qsstv/logbook/logbook.h                            |   32 +
 qsstv/main.cpp                                     |   84 +
 qsstv/mainwindow.cpp                               |  380 ++
 qsstv/mainwindow.h                                 |   59 +
 qsstv/mainwindow.ui                                |  257 ++
 qsstv/qsstv.pro                                    |  439 +++
 qsstv/qsstv.qrc                                    |   31 +
 qsstv/qsstvdefs.h                                  |   37 +
 qsstv/qsstvglobal.cpp                              |   95 +
 qsstv/qsstvglobal.h                                |   88 +
 qsstv/rig/freqdisplay.cpp                          |   19 +
 qsstv/rig/freqdisplay.h                            |   23 +
 qsstv/rig/freqdisplay.ui                           |  372 ++
 qsstv/rig/rigcontrol.cpp                           |  369 ++
 qsstv/rig/rigcontrol.h                             |   57 +
 qsstv/rig/rigcontrolform.cpp                       |  277 ++
 qsstv/rig/rigcontrolform.h                         |   47 +
 qsstv/rig/rigcontrolform.ui                        | 2140 +++++++++++
 qsstv/rig/rigparams.h                              |   32 +
 qsstv/rxfunctions.cpp                              |  466 +++
 qsstv/rxfunctions.h                                |   74 +
 qsstv/rxwidget.cpp                                 |  260 ++
 qsstv/rxwidget.h                                   |   62 +
 qsstv/rxwidget.ui                                  |  877 +++++
 qsstv/scope/plotform.ui                            |  262 ++
 qsstv/scope/scopeoffset.cpp                        |   27 +
 qsstv/scope/scopeoffset.h                          |   24 +
 qsstv/scope/scopeoffset.ui                         |   92 +
 qsstv/scope/scopeplot.cpp                          |  548 +++
 qsstv/scope/scopeplot.h                            |  200 +
 qsstv/scope/scopeview.cpp                          |  285 ++
 qsstv/scope/scopeview.h                            |   87 +
 qsstv/sound/calibration.cpp                        |  209 ++
 qsstv/sound/calibration.h                          |   49 +
 qsstv/sound/calibration.ui                         |  747 ++++
 qsstv/sound/calibrationform.ui                     |  127 +
 qsstv/sound/resamplefilter.cpp                     |  149 +
 qsstv/sound/resamplefilter.h                       |   14 +
 qsstv/sound/resampler.cpp                          |  146 +
 qsstv/sound/resampler.cpp_new                      |  147 +
 qsstv/sound/resampler.h                            |   28 +
 qsstv/sound/soundcontrol.cpp                       |  120 +
 qsstv/sound/soundcontrol.h                         |   31 +
 qsstv/sound/soundcontrol.ui                        |  281 ++
 qsstv/sound/soundio.cpp                            |  855 +++++
 qsstv/sound/soundio.cpp_new                        |  851 +++++
 qsstv/sound/soundio.h                              |  142 +
 qsstv/sound/waterfalltext.cpp                      |  190 +
 qsstv/sound/waterfalltext.h                        |   50 +
 qsstv/sound/wavio.cpp                              |  337 ++
 qsstv/sound/wavio.h                                |  119 +
 qsstv/sstv/cw.cpp                                  |  219 ++
 qsstv/sstv/cw.h                                    |   28 +
 qsstv/sstv/modes/modeavt.cpp                       |  267 ++
 qsstv/sstv/modes/modeavt.h                         |   52 +
 qsstv/sstv/modes/modebase.cpp                      |  532 +++
 qsstv/sstv/modes/modebase.h                        |  159 +
 qsstv/sstv/modes/modebw.cpp                        |  126 +
 qsstv/sstv/modes/modebw.h                          |   56 +
 qsstv/sstv/modes/modegbr.cpp                       |  147 +
 qsstv/sstv/modes/modegbr.h                         |   42 +
 qsstv/sstv/modes/modegbr2.cpp                      |  183 +
 qsstv/sstv/modes/modegbr2.h                        |   55 +
 qsstv/sstv/modes/modepd.cpp                        |  206 +
 qsstv/sstv/modes/modepd.h                          |   55 +
 qsstv/sstv/modes/modergb.cpp                       |  158 +
 qsstv/sstv/modes/modergb.h                         |   52 +
 qsstv/sstv/modes/moderobot1.cpp                    |  228 ++
 qsstv/sstv/modes/moderobot1.h                      |   55 +
 qsstv/sstv/modes/moderobot2.cpp                    |  201 +
 qsstv/sstv/modes/moderobot2.h                      |   53 +
 qsstv/sstv/modes/modes.h                           |   31 +
 qsstv/sstv/sstvparam.cpp                           |  423 +++
 qsstv/sstv/sstvparam.h                             |  191 +
 qsstv/sstv/syncprocessor.cpp                       |  725 ++++
 qsstv/sstv/syncprocessor.h                         |  162 +
 qsstv/txfunctions.cpp                              |  740 ++++
 qsstv/txfunctions.h                                |  154 +
 qsstv/txwidget.cpp                                 |  749 ++++
 qsstv/txwidget.h                                   |   99 +
 qsstv/txwidget.ui                                  | 1493 ++++++++
 qsstv/utils/buffermanag.h                          |  204 +
 qsstv/utils/ftp.cpp                                |  390 ++
 qsstv/utils/ftp.h                                  |   91 +
 qsstv/utils/hybridcrypt.cpp                        |  267 ++
 qsstv/utils/hybridcrypt.h                          |   35 +
 qsstv/utils/logging.cpp                            |  281 ++
 qsstv/utils/logging.h                              |   63 +
 qsstv/utils/loggingform.ui                         |  111 +
 qsstv/utils/loggingparams.cpp                      |   56 +
 qsstv/utils/loggingparams.h                        |   40 +
 qsstv/utils/macroexpansion.cpp                     |   55 +
 qsstv/utils/macroexpansion.h                       |   23 +
 qsstv/utils/qftp.cpp                               | 2403 ++++++++++++
 qsstv/utils/qftp.h                                 |  161 +
 qsstv/utils/qjp2io.cpp                             |  400 ++
 qsstv/utils/qjp2io.h                               |   61 +
 qsstv/utils/qurlinfo.cpp                           |  728 ++++
 qsstv/utils/qurlinfo.h                             |  126 +
 qsstv/utils/reedsolomoncoder.cpp                   |  325 ++
 qsstv/utils/reedsolomoncoder.h                     |   54 +
 qsstv/utils/rs.cpp                                 |  462 +++
 qsstv/utils/rs.h                                   |   60 +
 qsstv/utils/supportfunctions.cpp                   |  631 ++++
 qsstv/utils/supportfunctions.h                     |  104 +
 qsstv/utils/vector.h                               |  516 +++
 qsstv/videocapt/videocapture.cpp                   |  823 ++++
 qsstv/videocapt/videocapture.h                     |   92 +
 qsstv/widgets/blockview.cpp                        |   55 +
 qsstv/widgets/blockview.h                          |   34 +
 qsstv/widgets/blockview.ui                         |   28 +
 qsstv/widgets/cameracontrol.cpp                    |  144 +
 qsstv/widgets/cameracontrol.h                      |   46 +
 qsstv/widgets/cameracontrol.ui                     |  400 ++
 qsstv/widgets/fftdisplay.cpp                       |  254 ++
 qsstv/widgets/fftdisplay.h                         |   71 +
 qsstv/widgets/freqform.ui                          |  281 ++
 qsstv/widgets/imageviewer.cpp                      |  549 +++
 qsstv/widgets/imageviewer.h                        |  133 +
 qsstv/widgets/markerwidget.cpp                     |   25 +
 qsstv/widgets/markerwidget.h                       |   25 +
 qsstv/widgets/qscale.cpp                           |  371 ++
 qsstv/widgets/qscale.h                             |  111 +
 qsstv/widgets/spectrumwidget.cpp                   |  111 +
 qsstv/widgets/spectrumwidget.h                     |   36 +
 qsstv/widgets/spectrumwidget.ui                    |  629 ++++
 qsstv/widgets/sweepform.ui                         |  287 ++
 qsstv/widgets/textdisplay.cpp                      |   31 +
 qsstv/widgets/textdisplay.h                        |   24 +
 qsstv/widgets/textdisplay.ui                       |   78 +
 qsstv/widgets/vumeter.cpp                          |  287 ++
 qsstv/widgets/vumeter.h                            |   99 +
 qsstv/widgets/waterfallform.cpp                    |  101 +
 qsstv/widgets/waterfallform.h                      |   38 +
 qsstv/widgets/waterfallform.ui                     |  219 ++
 qsstv/xmlrpc/ipcmessage.cpp                        |   83 +
 qsstv/xmlrpc/ipcmessage.h                          |   36 +
 qsstv/xmlrpc/maiaFault.cpp                         |   50 +
 qsstv/xmlrpc/maiaFault.h                           |   46 +
 qsstv/xmlrpc/maiaObject.cpp                        |  327 ++
 qsstv/xmlrpc/maiaObject.h                          |   56 +
 qsstv/xmlrpc/maiaXmlRpcClient.cpp                  |  111 +
 qsstv/xmlrpc/maiaXmlRpcClient.h                    |   65 +
 qsstv/xmlrpc/maiaXmlRpcServer.cpp                  |   91 +
 qsstv/xmlrpc/maiaXmlRpcServer.h                    |   65 +
 qsstv/xmlrpc/maiaXmlRpcServerConnection.cpp        |  332 ++
 qsstv/xmlrpc/maiaXmlRpcServerConnection.h          |  106 +
 qsstv/xmlrpc/xmlinterface.cpp                      |  156 +
 qsstv/xmlrpc/xmlinterface.h                        |   52 +
 qsstv_8_2.pro                                      |   15 +
 qwt/qwt.h                                          |   23 +
 qwt/qwt.pro                                        |  213 ++
 qwt/qwt_abstract_legend.cpp                        |   38 +
 qwt/qwt_abstract_legend.h                          |   71 +
 qwt/qwt_abstract_scale.cpp                         |  449 +++
 qwt/qwt_abstract_scale.h                           |  105 +
 qwt/qwt_abstract_scale_draw.cpp                    |  419 +++
 qwt/qwt_abstract_scale_draw.h                      |  141 +
 qwt/qwt_abstract_slider.cpp                        |  820 ++++
 qwt/qwt_abstract_slider.h                          |  167 +
 qwt/qwt_analog_clock.cpp                           |  244 ++
 qwt/qwt_analog_clock.h                             |   93 +
 qwt/qwt_arrow_button.cpp                           |  333 ++
 qwt/qwt_arrow_button.h                             |   52 +
 qwt/qwt_clipper.cpp                                |  510 +++
 qwt/qwt_clipper.h                                  |   40 +
 qwt/qwt_color_map.cpp                              |  444 +++
 qwt/qwt_color_map.h                                |  200 +
 qwt/qwt_column_symbol.cpp                          |  293 ++
 qwt/qwt_column_symbol.h                            |  161 +
 qwt/qwt_compass.cpp                                |  308 ++
 qwt/qwt_compass.h                                  |   83 +
 qwt/qwt_compass_rose.cpp                           |  269 ++
 qwt/qwt_compass_rose.h                             |   89 +
 qwt/qwt_compat.h                                   |   42 +
 qwt/qwt_counter.cpp                                |  776 ++++
 qwt/qwt_counter.h                                  |  161 +
 qwt/qwt_curve_fitter.cpp                           |  453 +++
 qwt/qwt_curve_fitter.h                             |  139 +
 qwt/qwt_date.cpp                                   |  654 ++++
 qwt/qwt_date.h                                     |  128 +
 qwt/qwt_date_scale_draw.cpp                        |  269 ++
 qwt/qwt_date_scale_draw.h                          |   77 +
 qwt/qwt_date_scale_engine.cpp                      | 1247 +++++++
 qwt/qwt_date_scale_engine.h                        |   77 +
 qwt/qwt_dial.cpp                                   |  872 +++++
 qwt/qwt_dial.h                                     |  168 +
 qwt/qwt_dial_needle.cpp                            |  440 +++
 qwt/qwt_dial_needle.h                              |  187 +
 qwt/qwt_dyngrid_layout.cpp                         |  591 +++
 qwt/qwt_dyngrid_layout.h                           |   83 +
 qwt/qwt_event_pattern.cpp                          |  265 ++
 qwt/qwt_event_pattern.h                            |  240 ++
 qwt/qwt_global.h                                   |   41 +
 qwt/qwt_graphic.cpp                                |  986 +++++
 qwt/qwt_graphic.h                                  |  174 +
 qwt/qwt_interval.cpp                               |  354 ++
 qwt/qwt_interval.h                                 |  320 ++
 qwt/qwt_interval_symbol.cpp                        |  319 ++
 qwt/qwt_interval_symbol.h                          |   87 +
 qwt/qwt_knob.cpp                                   |  845 +++++
 qwt/qwt_knob.h                                     |  178 +
 qwt/qwt_legend.cpp                                 |  801 ++++
 qwt/qwt_legend.h                                   |  117 +
 qwt/qwt_legend_data.cpp                            |  129 +
 qwt/qwt_legend_data.h                              |   87 +
 qwt/qwt_legend_label.cpp                           |  421 +++
 qwt/qwt_legend_label.h                             |   80 +
 qwt/qwt_magnifier.cpp                              |  492 +++
 qwt/qwt_magnifier.h                                |   86 +
 qwt/qwt_math.cpp                                   |   74 +
 qwt/qwt_math.h                                     |  149 +
 qwt/qwt_matrix_raster_data.cpp                     |  298 ++
 qwt/qwt_matrix_raster_data.h                       |   74 +
 qwt/qwt_null_paintdevice.cpp                       |  593 +++
 qwt/qwt_null_paintdevice.h                         |  126 +
 qwt/qwt_painter.cpp                                | 1266 +++++++
 qwt/qwt_painter.h                                  |  188 +
 qwt/qwt_painter_command.cpp                        |  237 ++
 qwt/qwt_painter_command.h                          |  173 +
 qwt/qwt_panner.cpp                                 |  538 +++
 qwt/qwt_panner.h                                   |  103 +
 qwt/qwt_picker.cpp                                 | 1577 ++++++++
 qwt/qwt_picker.h                                   |  329 ++
 qwt/qwt_picker_machine.cpp                         |  526 +++
 qwt/qwt_picker_machine.h                           |  214 ++
 qwt/qwt_pixel_matrix.cpp                           |   51 +
 qwt/qwt_pixel_matrix.h                             |   98 +
 qwt/qwt_plot.cpp                                   | 1163 ++++++
 qwt/qwt_plot.h                                     |  312 ++
 qwt/qwt_plot_abstract_barchart.cpp                 |  367 ++
 qwt/qwt_plot_abstract_barchart.h                   |   97 +
 qwt/qwt_plot_axis.cpp                              |  719 ++++
 qwt/qwt_plot_barchart.cpp                          |  455 +++
 qwt/qwt_plot_barchart.h                            |  118 +
 qwt/qwt_plot_canvas.cpp                            | 1097 ++++++
 qwt/qwt_plot_canvas.h                              |  171 +
 qwt/qwt_plot_curve.cpp                             | 1191 ++++++
 qwt/qwt_plot_curve.h                               |  337 ++
 qwt/qwt_plot_dict.cpp                              |  191 +
 qwt/qwt_plot_dict.h                                |   58 +
 qwt/qwt_plot_directpainter.cpp                     |  317 ++
 qwt/qwt_plot_directpainter.h                       |  100 +
 qwt/qwt_plot_grid.cpp                              |  438 +++
 qwt/qwt_plot_grid.h                                |   87 +
 qwt/qwt_plot_histogram.cpp                         |  690 ++++
 qwt/qwt_plot_histogram.h                           |  139 +
 qwt/qwt_plot_intervalcurve.cpp                     |  603 +++
 qwt/qwt_plot_intervalcurve.h                       |  132 +
 qwt/qwt_plot_item.cpp                              |  698 ++++
 qwt/qwt_plot_item.h                                |  307 ++
 qwt/qwt_plot_layout.cpp                            | 1444 ++++++++
 qwt/qwt_plot_layout.h                              |  122 +
 qwt/qwt_plot_legenditem.cpp                        |  865 +++++
 qwt/qwt_plot_legenditem.h                          |  136 +
 qwt/qwt_plot_magnifier.cpp                         |  145 +
 qwt/qwt_plot_magnifier.h                           |   54 +
 qwt/qwt_plot_marker.cpp                            |  610 +++
 qwt/qwt_plot_marker.h                              |  130 +
 qwt/qwt_plot_multi_barchart.cpp                    |  740 ++++
 qwt/qwt_plot_multi_barchart.h                      |  127 +
 qwt/qwt_plot_panner.cpp                            |  275 ++
 qwt/qwt_plot_panner.h                              |   60 +
 qwt/qwt_plot_picker.cpp                            |  378 ++
 qwt/qwt_plot_picker.h                              |  111 +
 qwt/qwt_plot_rasteritem.cpp                        |  946 +++++
 qwt/qwt_plot_rasteritem.h                          |  152 +
 qwt/qwt_plot_renderer.cpp                          |  938 +++++
 qwt/qwt_plot_renderer.h                            |  170 +
 qwt/qwt_plot_rescaler.cpp                          |  631 ++++
 qwt/qwt_plot_rescaler.h                            |  142 +
 qwt/qwt_plot_scaleitem.cpp                         |  458 +++
 qwt/qwt_plot_scaleitem.h                           |   94 +
 qwt/qwt_plot_seriesitem.cpp                        |  112 +
 qwt/qwt_plot_seriesitem.h                          |   66 +
 qwt/qwt_plot_shapeitem.cpp                         |  491 +++
 qwt/qwt_plot_shapeitem.h                           |  111 +
 qwt/qwt_plot_spectrocurve.cpp                      |  321 ++
 qwt/qwt_plot_spectrocurve.h                        |   79 +
 qwt/qwt_plot_spectrogram.cpp                       |  660 ++++
 qwt/qwt_plot_spectrogram.h                         |  118 +
 qwt/qwt_plot_svgitem.h                             |   61 +
 qwt/qwt_plot_textlabel.cpp                         |  246 ++
 qwt/qwt_plot_textlabel.h                           |   75 +
 qwt/qwt_plot_tradingcurve.cpp                      |  682 ++++
 qwt/qwt_plot_tradingcurve.h                        |  174 +
 qwt/qwt_plot_xml.cpp                               |   42 +
 qwt/qwt_plot_zoneitem.cpp                          |  315 ++
 qwt/qwt_plot_zoneitem.h                            |   65 +
 qwt/qwt_plot_zoomer.cpp                            |  604 +++
 qwt/qwt_plot_zoomer.h                              |  140 +
 qwt/qwt_point_3d.cpp                               |   22 +
 qwt/qwt_point_3d.h                                 |  189 +
 qwt/qwt_point_data.cpp                             |  304 ++
 qwt/qwt_point_data.h                               |  146 +
 qwt/qwt_point_mapper.cpp                           |  721 ++++
 qwt/qwt_point_mapper.h                             |   89 +
 qwt/qwt_point_polar.cpp                            |  121 +
 qwt/qwt_point_polar.h                              |  201 +
 qwt/qwt_raster_data.cpp                            |  397 ++
 qwt/qwt_raster_data.h                              |   95 +
 qwt/qwt_round_scale_draw.cpp                       |  314 ++
 qwt/qwt_round_scale_draw.h                         |   66 +
 qwt/qwt_samples.h                                  |  239 ++
 qwt/qwt_sampling_thread.cpp                        |  106 +
 qwt/qwt_sampling_thread.h                          |   50 +
 qwt/qwt_scale_div.cpp                              |  331 ++
 qwt/qwt_scale_div.h                                |  110 +
 qwt/qwt_scale_draw.cpp                             |  926 +++++
 qwt/qwt_scale_draw.h                               |  120 +
 qwt/qwt_scale_engine.cpp                           | 1118 ++++++
 qwt/qwt_scale_engine.h                             |  220 ++
 qwt/qwt_scale_map.cpp                              |  248 ++
 qwt/qwt_scale_map.h                                |  175 +
 qwt/qwt_scale_widget.cpp                           |  942 +++++
 qwt/qwt_scale_widget.h                             |  136 +
 qwt/qwt_series_data.cpp                            |  346 ++
 qwt/qwt_series_data.h                              |  355 ++
 qwt/qwt_series_store.h                             |  199 +
 qwt/qwt_slider.cpp                                 |  991 +++++
 qwt/qwt_slider.h                                   |  130 +
 qwt/qwt_spline.cpp                                 |  384 ++
 qwt/qwt_spline.h                                   |  101 +
 qwt/qwt_symbol.cpp                                 | 1651 +++++++++
 qwt/qwt_symbol.h                                   |  258 ++
 qwt/qwt_system_clock.cpp                           |  396 ++
 qwt/qwt_system_clock.h                             |   47 +
 qwt/qwt_text.cpp                                   |  684 ++++
 qwt/qwt_text.h                                     |  223 ++
 qwt/qwt_text_engine.cpp                            |  345 ++
 qwt/qwt_text_engine.h                              |  172 +
 qwt/qwt_text_label.cpp                             |  324 ++
 qwt/qwt_text_label.h                               |   77 +
 qwt/qwt_thermo.cpp                                 | 1004 +++++
 qwt/qwt_thermo.h                                   |  178 +
 qwt/qwt_transform.cpp                              |  165 +
 qwt/qwt_transform.h                                |  137 +
 qwt/qwt_wheel.cpp                                  | 1293 +++++++
 qwt/qwt_wheel.h                                    |  178 +
 qwt/qwt_widget_overlay.cpp                         |  373 ++
 qwt/qwt_widget_overlay.h                           |  148 +
 535 files changed, 150950 insertions(+)

diff --git a/qsstv/COPYING b/qsstv/COPYING
new file mode 100644
index 0000000..555f746
--- /dev/null
+++ b/qsstv/COPYING
@@ -0,0 +1,684 @@
+
+QSSTV uses Qt see http://qt.digia.com/ if any restrictions apply.
+
+
+As far as I know, QSSTV does not use any none-public software.
+If there are doubts, please let me know by email: on4qz at telenet.be
+
+If part of whole of this code is used, you have to include a statement to indicate that the program is based in whole or in part on QSSTV.
+
+
+ GNU GENERAL PUBLIC LICENSE
+											 Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+														Preamble
+
+	The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+	The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+	When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+	To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+	For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+	Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+	For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+	Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+	Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+	The precise terms and conditions for copying, distribution and
+modification follow.
+
+											 TERMS AND CONDITIONS
+
+	0. Definitions.
+
+	"This License" refers to version 3 of the GNU General Public License.
+
+	"Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+	"The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+	To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+	A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+	To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+	To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+	An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+	1. Source Code.
+
+	The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+	A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+	The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+	The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+	The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+	The Corresponding Source for a work in source code form is that
+same work.
+
+	2. Basic Permissions.
+
+	All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+	You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+	Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+	3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+	No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+	When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+	4. Conveying Verbatim Copies.
+
+	You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+	You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+	5. Conveying Modified Source Versions.
+
+	You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+		a) The work must carry prominent notices stating that you modified
+		it, and giving a relevant date.
+
+		b) The work must carry prominent notices stating that it is
+		released under this License and any conditions added under section
+		7.  This requirement modifies the requirement in section 4 to
+		"keep intact all notices".
+
+		c) You must license the entire work, as a whole, under this
+		License to anyone who comes into possession of a copy.  This
+		License will therefore apply, along with any applicable section 7
+		additional terms, to the whole of the work, and all its parts,
+		regardless of how they are packaged.  This License gives no
+		permission to license the work in any other way, but it does not
+		invalidate such permission if you have separately received it.
+
+		d) If the work has interactive user interfaces, each must display
+		Appropriate Legal Notices; however, if the Program has interactive
+		interfaces that do not display Appropriate Legal Notices, your
+		work need not make them do so.
+
+	A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+	6. Conveying Non-Source Forms.
+
+	You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+		a) Convey the object code in, or embodied in, a physical product
+		(including a physical distribution medium), accompanied by the
+		Corresponding Source fixed on a durable physical medium
+		customarily used for software interchange.
+
+		b) Convey the object code in, or embodied in, a physical product
+		(including a physical distribution medium), accompanied by a
+		written offer, valid for at least three years and valid for as
+		long as you offer spare parts or customer support for that product
+		model, to give anyone who possesses the object code either (1) a
+		copy of the Corresponding Source for all the software in the
+		product that is covered by this License, on a durable physical
+		medium customarily used for software interchange, for a price no
+		more than your reasonable cost of physically performing this
+		conveying of source, or (2) access to copy the
+		Corresponding Source from a network server at no charge.
+
+		c) Convey individual copies of the object code with a copy of the
+		written offer to provide the Corresponding Source.  This
+		alternative is allowed only occasionally and noncommercially, and
+		only if you received the object code with such an offer, in accord
+		with subsection 6b.
+
+		d) Convey the object code by offering access from a designated
+		place (gratis or for a charge), and offer equivalent access to the
+		Corresponding Source in the same way through the same place at no
+		further charge.  You need not require recipients to copy the
+		Corresponding Source along with the object code.  If the place to
+		copy the object code is a network server, the Corresponding Source
+		may be on a different server (operated by you or a third party)
+		that supports equivalent copying facilities, provided you maintain
+		clear directions next to the object code saying where to find the
+		Corresponding Source.  Regardless of what server hosts the
+		Corresponding Source, you remain obligated to ensure that it is
+		available for as long as needed to satisfy these requirements.
+
+		e) Convey the object code using peer-to-peer transmission, provided
+		you inform other peers where the object code and Corresponding
+		Source of the work are being offered to the general public at no
+		charge under subsection 6d.
+
+	A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+	A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+	"Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+	If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+	The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+	Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+	7. Additional Terms.
+
+	"Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+	When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+	Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+		a) Disclaiming warranty or limiting liability differently from the
+		terms of sections 15 and 16 of this License; or
+
+		b) Requiring preservation of specified reasonable legal notices or
+		author attributions in that material or in the Appropriate Legal
+		Notices displayed by works containing it; or
+
+		c) Prohibiting misrepresentation of the origin of that material, or
+		requiring that modified versions of such material be marked in
+		reasonable ways as different from the original version; or
+
+		d) Limiting the use for publicity purposes of names of licensors or
+		authors of the material; or
+
+		e) Declining to grant rights under trademark law for use of some
+		trade names, trademarks, or service marks; or
+
+		f) Requiring indemnification of licensors and authors of that
+		material by anyone who conveys the material (or modified versions of
+		it) with contractual assumptions of liability to the recipient, for
+		any liability that these contractual assumptions directly impose on
+		those licensors and authors.
+
+	All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+	If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+	Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+	8. Termination.
+
+	You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+	However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+	Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+	Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+	9. Acceptance Not Required for Having Copies.
+
+	You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+	10. Automatic Licensing of Downstream Recipients.
+
+	Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+	An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+	You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+	11. Patents.
+
+	A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+	A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+	Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+	In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+	If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+	If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+	A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+	Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+	12. No Surrender of Others' Freedom.
+
+	If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+	13. Use with the GNU Affero General Public License.
+
+	Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+	14. Revised Versions of this License.
+
+	The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+	Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+	If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+	Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+	15. Disclaimer of Warranty.
+
+	THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+	16. Limitation of Liability.
+
+	IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+	17. Interpretation of Sections 15 and 16.
+
+	If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+										 END OF TERMS AND CONDITIONS
+
+						How to Apply These Terms to Your New Programs
+
+	If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+	To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+		<one line to give the program's name and a brief idea of what it does.>
+		Copyright (C) <year>  <name of author>
+
+		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 3 of the License, or
+		(at your option) any later version.
+
+		This program is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+	If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+		<program>  Copyright (C) <year>  <name of author>
+		This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+		This is free software, and you are welcome to redistribute it
+		under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+	You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+	The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/qsstv/Doxyfile b/qsstv/Doxyfile
new file mode 100644
index 0000000..f6853d7
--- /dev/null
+++ b/qsstv/Doxyfile
@@ -0,0 +1,1813 @@
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should 
+# identify the project. Note that if you do not use Doxywizard you need 
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = "QSSTV"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 8.2.7
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description 
+# for a project that appears at the top of each page and should give viewer 
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "SSTV and HAMDRM"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is 
+# included in the documentation. The maximum height of the logo should not 
+# exceed 55 pixels and the maximum width should not exceed 200 pixels. 
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = Documentation
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, 
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful if your file system 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only). 
+# A mapping has the form "name=value". For example adding 
+# "class=itcl::class" will allow you to use the command class in the 
+# itcl::class meaning.
+
+TCL_SUBST              = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it 
+# parses. With this tag you can assign which parser to use for a given extension. 
+# Doxygen has a built-in mapping, but you can override or extend it using this 
+# tag. The format is ext=language, where ext is a file extension, and language 
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, 
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make 
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C 
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions 
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      = 
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also makes the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and 
+# unions are shown inside the group in which they are included (e.g. using 
+# @ingroup) instead of on a separate page (for HTML and Man pages) or 
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 
+# unions with only public data fields will be shown inline in the documentation 
+# of the scope in which they are defined (i.e. file, namespace, or group 
+# documentation), provided this scope is documented. If set to NO (the default), 
+# structs, classes, and unions are shown on a separate page (for HTML and Man 
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
+# determine which symbols to keep in memory and which to flush to disk. 
+# When the cache is full, less often used symbols will be written to disk. 
+# For small to medium size projects (<1000 input files) the default value is 
+# probably good enough. For larger projects a too small cache size can cause 
+# doxygen to be busy swapping symbols to and from disk most of the time 
+# causing a significant performance penalty. 
+# If the system has enough physical memory increasing the cache will improve the 
+# performance by keeping more symbols in memory. Note that the value works on 
+# a logarithmic scale so increasing the size by one will roughly double the 
+# memory usage. The cache size is given by this formula: 
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be 
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given 
+# their name and scope. Since this can be an expensive process and often the 
+# same symbol appear multiple times in the code, doxygen keeps a cache of 
+# pre-resolved symbols. If the cache is too small doxygen will become slower. 
+# If the cache is too large, memory is wasted. The cache size is given by this 
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 
+# will list include files with double quotes in the documentation 
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen 
+# will sort the (brief and detailed) documentation of class members so that 
+# constructors and destructors are listed first. If set to NO (the default) 
+# the constructors will appear in the respective orders defined by 
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. 
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO 
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to 
+# do proper type resolution of all parameters of a function it will reject a 
+# match between the prototype and the implementation of a member function even 
+# if there is only one candidate or it is obvious which candidate to choose 
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen 
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or macro consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and macros in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index 
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 
+# by doxygen. The layout file controls the global structure of the generated 
+# output files in an output format independent way. The create the layout file 
+# that represents doxygen's defaults, run doxygen with the -l option. 
+# You can optionally specify a file name after the option, if omitted 
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            = 
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files 
+# containing the references data. This must be a list of .bib files. The 
+# .bib extension is automatically appended if omitted. Using this command 
+# requires the bibtex tool to be installed. See also 
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = ./.
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh 
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.for \
+                         *.vhd \
+                         *.vhdl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag. 
+# Note that relative paths are relative to the directory from which doxygen is 
+# run.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 
+# directories that are symbolic links (a Unix file system feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty or if 
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file 
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) 
+# and it is also possible to disable source filtering for a specific pattern 
+# using *.ext= (so without naming a filter). This option only has effect when 
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS = 
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
+# link to the source code.  Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header. Note that when using a custom header you are responsible  
+# for the proper inclusion of any scripts and style sheets that doxygen 
+# needs, which is dependent on the configuration options used. 
+# It is advised to generate a default header using "doxygen -w html 
+# header.html footer.html stylesheet.css YourConfigFile" and then modify 
+# that header. Note that the header is subject to change so you typically 
+# have to redo this when upgrading to a newer version of doxygen or when 
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 
+# other source files which should be copied to the HTML output directory. Note 
+# that these files will be copied to the base HTML output directory. Use the 
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these 
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that 
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       = 
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 
+# Doxygen will adjust the colors in the style sheet and background images 
+# according to this color. Hue is specified as an angle on a colorwheel, 
+# see http://en.wikipedia.org/wiki/Hue for more information. 
+# For instance the value 0 represents red, 60 is yellow, 120 is green, 
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 
+# the colors in the HTML output. For a value of 0 the output will use 
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 
+# the luminance component of the colors in the HTML output. Values below 
+# 100 gradually make the output lighter, whereas values above 100 make 
+# the output darker. The value divided by 100 is the actual gamma applied, 
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
+# page will contain the date and time when the page was generated. Setting 
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup. 
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html 
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify 
+# the documentation publisher. This should be a reverse domain-name style 
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
+# content.
+
+CHM_INDEX_ENCODING     = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and 
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated 
+# that can be used as input for Qt's qhelpgenerator to generate a 
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
+# be used to specify the file name of the resulting .qch file. 
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to 
+# add. For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   = 
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the 
+# custom filter to add. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> 
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  = 
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this 
+# project's 
+# filter section matches. 
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> 
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  = 
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
+# be used to specify the location of Qt's qhelpgenerator. 
+# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# .qhp file.
+
+QHG_LOCATION           = 
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files  
+# will be generated, which together with the HTML files, form an Eclipse help 
+# plugin. To install this plugin and make it available under the help contents 
+# menu in Eclipse, the contents of the directory containing the HTML and XML 
+# files needs to be copied into the plugins directory of eclipse. The name of 
+# the directory within the plugins directory should be the same as 
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before 
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin 
+# the directory name containing the HTML and XML files should also have 
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 
+# at top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it. Since the tabs have the same information as the 
+# navigation tree you can set this option to NO if you already set 
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
+# structure should be generated to display hierarchical information. 
+# If the tag value is set to YES, a side panel will be generated 
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 
+# Windows users are probably better off using the HTML help feature. 
+# Since the tree basically has the same information as the tab index you 
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML 
+# documentation. Note that a value of 0 will completely suppress the enum 
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open 
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images 
+# generated for formulas are transparent PNGs. Transparent PNGs are 
+# not supported properly for IE 6.0, but are supported on all modern browsers. 
+# Note that when changing this option you need to delete any form_*.png files 
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax 
+# (see http://www.mathjax.org) which uses client side Javascript for the 
+# rendering instead of using prerendered bitmaps. Use this if you do not 
+# have LaTeX installed or if you want to formulas look prettier in the HTML 
+# output. When enabled you also need to install MathJax separately and 
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the 
+# HTML output directory using the MATHJAX_RELPATH option. The destination 
+# directory should contain the MathJax.js script. For instance, if the mathjax 
+# directory is located at the same level as the HTML output directory, then 
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the 
+# mathjax.org site, so you can quickly see the result without installing 
+# MathJax, but it is strongly recommended to install a local copy of MathJax 
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension 
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     = 
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box 
+# for the HTML output. The underlying search engine uses javascript 
+# and DHTML and should work on any modern browser. Note that when using 
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets 
+# (GENERATE_DOCSET) there is already a search function so this one should 
+# typically be disabled. For large projects the javascript based search engine 
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
+# implemented using a PHP enabled web server instead of at the web client 
+# using Javascript. Doxygen will generate the search PHP script and index 
+# file to put on the web server. The advantage of the server 
+# based approach is that it scales better to large projects and allows 
+# full text search. The disadvantages are that it is more difficult to setup 
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name. 
+# Note that when enabling USE_PDFLATEX this option is only used for 
+# generating bitmaps for formulas in the HTML output, but not in the 
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for 
+# the generated latex document. The footer should contain everything after 
+# the last chapter. If it is left blank doxygen will generate a 
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include 
+# source code with syntax highlighting in the LaTeX output. 
+# Note that which sources are shown also depends on other settings 
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the 
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See 
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition that 
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all references to function-like macros 
+# that are alone on a line, have an all uppercase name, and do not end with a 
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links. 
+# Note that each tag file must have a unique name 
+# (where the name does NOT include the path) 
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option also works with HAVE_DOT disabled, but it is recommended to 
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is 
+# allowed to run in parallel. When set to 0 (the default) doxygen will 
+# base this on the number of processors available in the system. You can set it 
+# explicitly to a value larger than 0 to get control over the balance 
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that 
+# doxygen generates. When you want a differently looking font you can specify 
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find 
+# the font, which can be done by putting it in a standard location or by setting 
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the 
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font. 
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to 
+# set the path where dot can find it.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include 
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are svg, png, jpg, or gif. 
+# If left blank png will be used. If you choose svg you need to set 
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to 
+# enable generation of interactive SVG images that allow zooming and panning. 
+# Note that this requires a modern browser other than Internet Explorer. 
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you 
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that 
+# contain msc files that are included in the documentation (see the 
+# \mscfile command).
+
+MSCFILE_DIRS           = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, because dot on Windows does not 
+# seem to support this out of the box. Warning: Depending on the platform used, 
+# enabling this option may lead to badly anti-aliased labels on the edges of 
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/qsstv/configdialog.cpp b/qsstv/configdialog.cpp
new file mode 100644
index 0000000..b650fff
--- /dev/null
+++ b/qsstv/configdialog.cpp
@@ -0,0 +1,557 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "configdialog.h"
+#include "configparams.h"
+
+#include <QPushButton>
+#include <QComboBox>
+#include <QSettings>
+#include "sstv/sstvparam.h"
+//#include <QImage>
+#include <QImageWriter>
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+#include "videocapt/videocapture.h"
+#include "rig/rigcontrol.h"
+#include "mainwindow.h"
+#include "sound/soundcontrol.h"
+#include "txwidget.h"
+#include "utils/ftp.h"
+#include "utils/hybridcrypt.h"
+#include <errno.h>
+
+/**
+	\class configDialog 
+	
+	The configDialog provides access to the application-wide configuration parameters.
+*/
+
+
+
+/**
+  constructor for configDialog
+*/
+configDialog::configDialog(QWidget *, const char *) :Ui::configForm()
+{
+	setupUi(this);
+  int i=0;
+  setModal(false);
+  foreach (QByteArray format, QImageWriter::supportedImageFormats())
+    {
+      QString text = tr("%1").arg(QString(format));
+      defaultImageFormatComboBox->addItem(text);
+      ftpDefaultImageFormatComboBox->addItem(text);
+    }
+  for(i=0;i<(NUMSSTVMODES-1);i++) // exclude Calibrate
+    {
+      repeaterTxModeComboBox->addItem(getSSTVModeNameLong((esstvMode)i));
+    }
+  rigWidget->attachRigController(rigControllerR1);
+//  rigR2Widget->attachRigController(rigControllerR2);
+  readSettings();
+	connect(rxImagesPathBrowseButton,SIGNAL(clicked()),SLOT(slotBrowseRxImagesPath()));
+	connect(txImagesPathBrowseButton,SIGNAL(clicked()),SLOT(slotBrowseTxImagesPath()));
+	connect(templatesPathBrowseButton,SIGNAL(clicked()),SLOT(slotBrowseTemplatesPath()));
+  connect(audioPathBrowseButton,SIGNAL(clicked()),SLOT(slotBrowseAudioPath()));
+  connect(rp1BrowseButton,SIGNAL(clicked()),SLOT(slotRp1BrowseButton()));
+  connect(rp2BrowseButton,SIGNAL(clicked()),SLOT(slotRp2BrowseButton()));
+  connect(rp3BrowseButton,SIGNAL(clicked()),SLOT(slotRp3BrowseButton()));
+  connect(rp4BrowseButton,SIGNAL(clicked()),SLOT(slotRp4BrowseButton()));
+  connect(repeaterTemplateBrowseButton,SIGNAL(clicked()),SLOT(slotRepeaterTemplateBrowseButton()));
+  connect(idleTemplateBrowseButton,SIGNAL(clicked()),SLOT(slotIdleTemplateBrowseButton()));
+  drmProfilePtr=drmProfileWidget;
+  tabWidget->setCurrentIndex(0);
+  connect(testFTPPushButton,SIGNAL(clicked()),SLOT(slotTestFTPPushButton()));
+  connect(testHybridPushButton,SIGNAL(clicked()),SLOT(slotTestHybridPushButton()));
+}
+
+/**
+	destructor for configDialog
+*/
+configDialog::~configDialog()
+{
+}
+
+/** read variables from config file
+
+	Read the config file and initializes the variables. If not found, default values will be used.
+	\sa writeSettings
+	
+*/
+void configDialog::readSettings()
+{
+    QSettings qSettings;
+    qSettings.beginGroup("Config");
+    myCallsign=qSettings.value("callsign",QString("NOCALL")).toString();
+    myQth=qSettings.value("qth",QString("NOWHERE")).toString();
+    myLastname=qSettings.value("lastname",QString("NONAME")).toString();
+    myFirstname=qSettings.value("firstname",QString("NOFIRSTNAME")).toString();
+    myLocator=qSettings.value("locator",QString("NOLOCATOR")).toString();
+
+    rxImagesPath=qSettings.value("rxImagesPath",QString(getenv("HOME"))+"/").toString();
+    txImagesPath=qSettings.value("txImagesPath",QString(getenv("HOME"))+"/").toString();
+    templatesPath=qSettings.value("templatesPath",QString(getenv("HOME"))+"/").toString();
+    docURL=qSettings.value("docURL","http://users.telenet.be/on4qz/qsstv/manual").toString();
+    audioPath=qSettings.value("audioPath",QString(getenv("HOME"))+"/").toString();
+    defaultImageFormat=qSettings.value("defaultImageFormat","png").toString();
+    //	fontString=qSettings.value("fontString","").toString();
+    samplingrate=qSettings.value("samplingrate",11025).toInt();
+    cwText=qSettings.value("cwtext","").toString();
+    cwTone=qSettings.value("cwtone",800).toInt();
+    cwWPM=qSettings.value("cwWPM",12).toInt();
+    enableFTP=qSettings.value("enableFTP",false).toBool();
+    ftpPort=qSettings.value("ftpPort",21).toInt();
+    ftpRemoteHost=qSettings.value("ftpRemoteHost","").toString();
+    ftpRemoteDirectory=qSettings.value("ftpRemoteDirectory","").toString();
+    ftpLogin=qSettings.value("ftpLogin","").toString();
+    ftpPassword=qSettings.value("ftpPassword","").toString();
+    ftpFilename=qSettings.value("ftpFilename","").toString();
+    ftpDefaultImageFormat=qSettings.value("ftpDefaultImageFormat","png").toString();
+    ftpSaveFormat=(eftpSaveFormat)qSettings.value("ftpSaveFormat",0).toInt();
+    ftpNumImages=qSettings.value("ftpNumImages",30).toInt();
+
+
+    enableHybridRx=qSettings.value("enableHybridRx",true).toBool();
+    enableSpecialServer=qSettings.value("enableSpecialServer",false).toBool();
+    hybridFtpPort=qSettings.value("hybridFtpPort",21).toInt();
+    hybridFtpRemoteHost=qSettings.value("hybridFtpRemoteHost","").toString();
+    hybridFtpRemoteDirectory=qSettings.value("hybridFtpRemoteDirectory","").toString();
+    hybridFtpHybridFilesDirectory=qSettings.value("hybridFtpHybridFilesDirectory","HybridFiles1").toString();
+    hybridFtpLogin=qSettings.value("hybridFtpLogin","").toString();
+    hybridFtpPassword=qSettings.value("hybridFtpPassword","").toString();
+    repeaterImageInterval=qSettings.value("repeaterImageInterval",10).toInt();
+    repeaterEnable=qSettings.value("repeaterEnable",false).toBool();
+    repeaterTxMode=(esstvMode)qSettings.value("repeaterTxMode",0).toInt();
+    repeaterImage1=qSettings.value("repeaterImage1","").toString();
+    repeaterImage2=qSettings.value("repeaterImage2","").toString();
+    repeaterImage3=qSettings.value("repeaterImage3","").toString();
+    repeaterImage4=qSettings.value("repeaterImage4","").toString();
+    repeaterAcknowledge=qSettings.value("repeaterAcknowledge","").toString();
+    repeaterTemplate=qSettings.value("repeaterTemplate","").toString();
+    idleTemplate=qSettings.value("idleTemplate","").toString();
+    videoDevice=qSettings.value("videoDevice","/dev/video0").toString();
+    channelNumber=qSettings.value("channelNumber",0).toInt();
+    //	framesPerSecondIndex=qSettings.value("framesPerSecondIndex",0).toInt();
+    colorFormatIndex=qSettings.value("colorFormatIndex",0).toInt();
+    // DRM
+    startPicWF=qSettings.value("startPicWF","START PIC").toString();
+    endPicWF=qSettings.value("endPicWF","END PIC").toString();
+    fixWF=qSettings.value("fixWF","FIX").toString();
+    bsrWF=qSettings.value("bsrWF","BSR").toString();
+
+    sizeIndex=qSettings.value("sizeIndex",0).toInt();
+    qSettings.endGroup();
+    setParams();
+    soundWidget->readSettings();
+    rigWidget->readSettings();
+}
+
+/**
+ write variables to config file
+        \sa readSettings
+
+*/
+void configDialog::writeSettings()
+{
+    rigWidget->writeSettings();
+    soundWidget->writeSettings();
+    getParams();
+    QSettings qSettings;
+    qSettings.beginGroup("Config");
+    qSettings.setValue("callsign",myCallsign);
+    qSettings.setValue("qth",myQth);
+    qSettings.setValue("locator",myLocator);
+    qSettings.setValue("lastname",myLastname);
+    qSettings.setValue("firstname",myFirstname);
+    qSettings.setValue("rxImagesPath",rxImagesPath);
+    qSettings.setValue("txImagesPath",txImagesPath);
+    qSettings.setValue("templatesPath",templatesPath);
+    qSettings.setValue("audioPath",audioPath);
+    qSettings.setValue("docURL",docURL);
+    qSettings.setValue("defaultImageFormat",defaultImageFormat);
+    //	qSettings.setValue("fontString",fontString);
+    qSettings.setValue("samplingrate",samplingrate);
+    qSettings.setValue("cwtext",cwText);
+    qSettings.setValue("cwtone",cwTone);
+    qSettings.setValue("cwWPM",cwWPM);
+    qSettings.setValue("enableFTP",enableFTP);
+    qSettings.setValue("ftpPort",ftpPort);
+    qSettings.setValue("ftpRemoteHost",ftpRemoteHost);
+    qSettings.setValue("ftpRemoteDirectory",ftpRemoteDirectory);
+    qSettings.setValue("ftpLogin",ftpLogin);
+    qSettings.setValue("ftpPassword",ftpPassword);
+    qSettings.setValue("ftpFilename",ftpFilename);
+    qSettings.setValue("ftpDefaultImageFormat",ftpDefaultImageFormat);
+    qSettings.setValue("ftpSaveFormat",(int)ftpSaveFormat);
+    qSettings.setValue("ftpNumImages",ftpNumImages);
+    qSettings.setValue("repeaterImageInterval",repeaterImageInterval);
+    qSettings.setValue("enableHybridRx",enableHybridRx);
+    qSettings.setValue("enableSpecialServer",enableSpecialServer);
+    qSettings.setValue("hybridFtpPort",hybridFtpPort);
+    qSettings.setValue("hybridFtpRemoteHost",hybridFtpRemoteHost);
+    qSettings.setValue("hybridFtpRemoteDirectory",hybridFtpRemoteDirectory);
+    qSettings.setValue("hybridFtpHybridFilesDirectory",hybridFtpHybridFilesDirectory);
+    qSettings.setValue("hybridFtpLogin",hybridFtpLogin);
+    qSettings.setValue("hybridFtpPassword",hybridFtpPassword);
+    qSettings.setValue("repeaterEnable",repeaterEnable);
+    qSettings.setValue("repeaterTxMode",repeaterTxMode);
+    qSettings.setValue("repeaterImage1",repeaterImage1);
+    qSettings.setValue("repeaterImage2",repeaterImage2);
+    qSettings.setValue("repeaterImage3",repeaterImage3);
+    qSettings.setValue("repeaterImage4",repeaterImage4);
+    qSettings.setValue("repeaterAcknowledge",repeaterAcknowledge);
+    qSettings.setValue("repeaterTemplate",repeaterTemplate);
+    qSettings.setValue("idleTemplate",idleTemplate);
+    qSettings.setValue("videoDevice",videoDevice);
+    qSettings.setValue("channelNumber",channelNumber);
+    //	qSettings.setValue("framesPerSecondIndex",framesPerSecondIndex);
+    qSettings.setValue("colorFormatIndex",colorFormatIndex);
+    qSettings.setValue("sizeIndex",sizeIndex);
+    qSettings.setValue("startPicWF",startPicWF);
+    qSettings.setValue("endPicWF",endPicWF);
+    qSettings.setValue("fixWF",fixWF);
+    qSettings.setValue("bsrWF",bsrWF);
+    qSettings.endGroup();
+    drmProfileWidget->writeSettings();
+}
+
+
+/**
+    Opens the configuration dialog
+*/
+
+int configDialog::exec()
+{
+    videoCapture vc;
+    int i=0;
+    colorFormatComboBox->clear();
+    sizeComboBox->clear();
+
+    if(vc.open())
+    {
+        QList <QString> description;
+        vc.getFormatList(description);
+        for(i=0;i<description.count();i++)
+        {
+            colorFormatComboBox->addItem(description[i]);
+        }
+
+        QList<QSize> sizeList(vc.getSizesList());
+
+        for(i=0;i<sizeList.count();i++)
+        {
+            sizeComboBox->addItem(QString::number(sizeList[i].width())+ " x " + QString::number(sizeList[i].height()));
+        }
+        vc.close();
+    }
+    setParams();
+    soundWidget->setParams();
+    drmProfileWidget->setParams();
+    if(QDialog::exec())
+    {
+        writeSettings();
+        txWidgetPtr->setProfileNames();
+        mainWindowPtr->setNewFont();
+        return QDialog::Accepted;
+    }
+    else
+    {
+        return QDialog::Rejected;
+    }
+}
+
+/**
+ copies the values from the variables to the userinterface
+		\sa getParams
+	
+*/
+
+void configDialog::setParams()
+{
+
+    setValue(myCallsign,callsignLineEdit);
+    setValue(myLastname,lastnameLineEdit);
+    setValue(myFirstname,firstnameLineEdit);
+    setValue(myQth,qthLineEdit);
+    setValue(myLocator,locatorLineEdit);
+    setValue(rxImagesPath, rxImagesPathLineEdit);
+    setValue(txImagesPath, txImagesPathLineEdit);
+    setValue(templatesPath, templatesPathLineEdit);
+    setValue(audioPath, audioPathLineEdit);
+    setValue(docURL, docPathLineEdit);
+    setValue(defaultImageFormat, defaultImageFormatComboBox);
+
+    QFont fnt;
+    //  fnt.fromString(fontString);
+    //  fontComboBox->setCurrentFont(fnt);
+    //  setValue(fnt.pointSize(),fontSizeSpinBox);
+
+    setValue(cwText,cwTextLineEdit);
+    setValue(cwTone,cwToneSpinBox);
+    setValue(cwWPM,cwWPMSpinBox);
+    setValue(enableFTP,enableFTPCheckBox);
+    setValue(ftpPort,ftpPortSpinBox);
+    setValue(ftpRemoteHost,remoteHostLineEdit);
+    setValue(ftpRemoteDirectory,remoteDirectoryLineEdit);
+    setValue(ftpLogin,ftpLoginLineEdit);
+    setValue(ftpPassword,ftpPasswordLineEdit);
+    if(ftpSaveFormat==FTPIM)
+      {
+        imageRadioButton->setChecked(true);
+      }
+    else
+      {
+        filenameRadioButton->setChecked(true);
+      }
+    setValue(ftpNumImages,ftpNumImagesSpinBox);
+    setValue(enableHybridRx,enableHybridRxCheckBox);
+    setValue(enableSpecialServer,enableSpecialServerCheckBox);
+    setValue(hybridFtpPort,hybridFtpPortSpinBox);
+    setValue(hybridFtpRemoteHost,hybridRemoteHostLineEdit);
+    setValue(hybridFtpRemoteDirectory,hybridRemoteDirectoryLineEdit);
+    setValue(hybridFtpHybridFilesDirectory,hybridFilesDirectoryLineEdit);
+    setValue(hybridFtpLogin,hybridFtpLoginLineEdit);
+    setValue(hybridFtpPassword,hybridFtpPasswordLineEdit);
+
+    setValue(ftpDefaultImageFormat,ftpDefaultImageFormatComboBox);
+    setValue(repeaterEnable,repeaterEnableCheckBox);
+    setIndex(repeaterTxMode,repeaterTxModeComboBox);
+    setValue(repeaterImage1,repeaterImage1LineEdit);
+    setValue(repeaterImage2,repeaterImage2LineEdit);
+    setValue(repeaterImage3,repeaterImage3LineEdit);
+    setValue(repeaterImage4,repeaterImage4LineEdit);
+    setValue(idleTemplate,idleTemplateLineEdit);
+    setValue(repeaterTemplate,repeaterTemplateLineEdit);
+    setValue(videoDevice,videoDeviceLineEdit);
+    setValue(channelNumber, channelSpinBox);
+    setIndex(colorFormatIndex,colorFormatComboBox);
+    setIndex(sizeIndex,sizeComboBox);
+
+    setValue(startPicWF,startPicTextEdit);
+    setValue(endPicWF,endPicTextEdit);
+    setValue(fixWF,fixTextEdit);
+    setValue(bsrWF,bsrTextEdit);
+}
+
+/**
+ copies the values from the userinterface to the variables
+
+		\sa setParams
+	
+*/
+
+void configDialog::getParams()
+{
+    getValue(myCallsign,callsignLineEdit);
+    getValue(myLastname,lastnameLineEdit);
+    getValue(myFirstname,firstnameLineEdit);
+    getValue(myQth,qthLineEdit);
+    getValue(myLocator,locatorLineEdit);
+    getValue(rxImagesPath, rxImagesPathLineEdit);
+    getValue(txImagesPath, txImagesPathLineEdit);
+    getValue(templatesPath, templatesPathLineEdit);
+    getValue(audioPath, audioPathLineEdit);
+    getValue(docURL, docPathLineEdit);
+    getValue(defaultImageFormat, defaultImageFormatComboBox);
+    //	QFont fnt(fontComboBox->currentFont());
+    //	fnt.setPointSize(fontSizeSpinBox->value());
+    //	fontString=fnt.toString();
+
+    getValue(cwText,cwTextLineEdit);
+    getValue(cwTone,cwToneSpinBox);
+    getValue(cwWPM,cwWPMSpinBox);
+    getValue(enableFTP,enableFTPCheckBox);
+    getValue(ftpPort,ftpPortSpinBox);
+    getValue(ftpRemoteHost,remoteHostLineEdit);
+    getValue(ftpRemoteDirectory,remoteDirectoryLineEdit);
+    getValue(ftpLogin,ftpLoginLineEdit);
+    getValue(ftpPassword,ftpPasswordLineEdit);
+//    getValue(ftpFilename,ftpFilenameLineEdit);
+    if(imageRadioButton->isChecked())
+      {
+       ftpSaveFormat=FTPIM;
+      }
+    else
+      {
+        ftpSaveFormat=FTPFILE;
+      }
+    getValue(ftpNumImages,ftpNumImagesSpinBox);
+
+    getValue(ftpDefaultImageFormat,ftpDefaultImageFormatComboBox);
+    getValue(enableHybridRx,enableHybridRxCheckBox);
+    getValue(enableSpecialServer,enableSpecialServerCheckBox);
+    getValue(hybridFtpPort,hybridFtpPortSpinBox);
+    getValue(hybridFtpRemoteHost,hybridRemoteHostLineEdit);
+    getValue(hybridFtpRemoteDirectory,hybridRemoteDirectoryLineEdit);
+    getValue(hybridFtpHybridFilesDirectory,hybridFilesDirectoryLineEdit);
+    getValue(hybridFtpLogin,hybridFtpLoginLineEdit);
+    getValue(hybridFtpPassword,hybridFtpPasswordLineEdit);
+
+    getValue(repeaterEnable,repeaterEnableCheckBox);
+    int temp;
+    getIndex(temp,repeaterTxModeComboBox);
+    repeaterTxMode=esstvMode(temp);
+    getValue(repeaterImage1,repeaterImage1LineEdit);
+    getValue(repeaterImage2,repeaterImage2LineEdit);
+    getValue(repeaterImage3,repeaterImage3LineEdit);
+    getValue(repeaterImage4,repeaterImage4LineEdit);
+    getValue(idleTemplate,idleTemplateLineEdit);
+    getValue(repeaterTemplate,repeaterTemplateLineEdit);
+    getValue(videoDevice,videoDeviceLineEdit);
+    getValue(channelNumber, channelSpinBox);
+    getIndex(colorFormatIndex,colorFormatComboBox);
+    getIndex(sizeIndex,sizeComboBox);
+    getValue(startPicWF,startPicTextEdit);
+    getValue(endPicWF,endPicTextEdit);
+    getValue(fixWF,fixTextEdit);
+    getValue(bsrWF,bsrTextEdit);
+}
+
+/**
+	Browse function for path where the rximages are stored
+*/
+
+
+void configDialog::slotBrowseRxImagesPath()
+{
+	browseDir(rxImagesPathLineEdit,rxImagesPath);
+}
+
+/**
+	Browse function for path where the tximages are stored
+*/
+
+void configDialog::slotBrowseTxImagesPath()
+{
+	browseDir(txImagesPathLineEdit,txImagesPath);
+}
+
+/**
+	Browse function for path where the templates are stored
+*/
+
+void configDialog::slotBrowseTemplatesPath()
+{
+	browseDir(templatesPathLineEdit,templatesPath);
+}
+
+/**
+	Browse function for audio path
+*/
+
+void configDialog::slotBrowseAudioPath()
+{
+	browseDir(audioPathLineEdit,audioPath);
+}
+
+/**
+	Browse function for documentation path
+*/
+
+
+
+void configDialog::slotRp1BrowseButton()
+{
+  browseGetFile(repeaterImage1LineEdit,txImagesPath);
+}
+
+void configDialog::slotRp2BrowseButton()
+{
+  browseGetFile(repeaterImage2LineEdit,txImagesPath);
+}
+
+void configDialog::slotRp3BrowseButton()
+{
+  browseGetFile(repeaterImage3LineEdit,txImagesPath);
+}
+
+void configDialog::slotRp4BrowseButton()
+{
+  browseGetFile(repeaterImage4LineEdit,txImagesPath);
+}
+
+void configDialog::slotRepeaterTemplateBrowseButton()
+{
+  browseGetFile(repeaterTemplateLineEdit,templatesPath);
+}
+
+void configDialog::slotIdleTemplateBrowseButton()
+{
+  browseGetFile(idleTemplateLineEdit,templatesPath);
+}
+
+
+void configDialog::slotTestFTPPushButton()
+{
+  ftpInterface fInt("TestUploadConnection");
+  getParams();
+  fInt.setupConnection(ftpRemoteHost,ftpPort,ftpLogin,ftpPassword,ftpRemoteDirectory);
+  execFTPTest(&fInt);
+}
+
+
+void configDialog::slotTestHybridPushButton()
+{
+  ftpInterface fInt("TestHybridConnection");
+  getParams();
+  hybridCrypt hc;
+  fInt.setupConnection(hc.host(),hc.port(),hc.user(),hc.passwd(),hc.dir()+"/"+hybridFtpHybridFilesDirectory);
+  execFTPTest(&fInt);
+
+
+}
+
+
+void configDialog::execFTPTest(ftpInterface *ftpPtr)
+{
+  eftpError ftpResult;
+  QString fn(QString("%1/test.txt").arg(rxImagesPath));
+  //checkConnection
+  QFile tst(fn);
+  if(tst.open(QIODevice::WriteOnly)<=0)
+    {
+      QMessageBox::critical(this, tr("File Error"),QString("").arg(strerror(errno)));
+    }
+  tst.write("connection test\n");
+  tst.close();
+  ftpResult=ftpPtr->uploadFile(fn,QString("test_%1.txt").arg( myCallsign),true);
+  switch(ftpResult)
+    {
+    case FTPCANCELED:
+      QMessageBox::information(this, tr("FTP Info"),"Connection Canceled");
+    break;
+    case FTPOK:
+      QMessageBox::information(this, tr("FTP Info"),"Connection OK");
+    break;
+    case FTPERROR:
+       QMessageBox::critical(this, tr("FTP Error"),ftpPtr->getLastError());
+    break;
+    case FTPNAMEERROR:
+     QMessageBox::critical(this, tr("FTP Error"),"Error in filename");
+    break;
+    case FTPTIMEOUT:
+      QMessageBox::critical(this, tr("FTP Error"),"FTP timed out");
+      return;
+    }
+  tst.remove();
+}
+
+
diff --git a/qsstv/configdialog.h b/qsstv/configdialog.h
new file mode 100644
index 0000000..146b769
--- /dev/null
+++ b/qsstv/configdialog.h
@@ -0,0 +1,52 @@
+#ifndef CONFIGDIALOG_H
+#define CONFIGDIALOG_H
+
+#include "ui_configform.h"
+
+/** @author Johan Maes - ON4QZ */
+
+
+class ftpInterface;
+
+
+
+class configForm;
+
+//! configuration Dialog
+
+class configDialog: public QDialog, private Ui::configForm
+{
+Q_OBJECT
+public:
+    configDialog(QWidget *parent = 0, const char *name = 0);
+    ~configDialog();
+		void writeSettings();
+		void readSettings();
+    int exec();
+
+		
+public slots:
+		void slotBrowseRxImagesPath();
+		void slotBrowseTxImagesPath();
+		void slotBrowseTemplatesPath();
+		void slotBrowseAudioPath();
+    void slotRp1BrowseButton();
+    void slotRp2BrowseButton();
+    void slotRp3BrowseButton();
+    void slotRp4BrowseButton();
+    void slotRepeaterTemplateBrowseButton();
+    void slotIdleTemplateBrowseButton();
+    void slotTestFTPPushButton();
+    void slotTestHybridPushButton();
+
+
+private:
+		void getParams();
+		void setParams();
+    void execFTPTest(ftpInterface *ftpPtr);
+
+};
+
+
+
+#endif
diff --git a/qsstv/configform.ui b/qsstv/configform.ui
new file mode 100644
index 0000000..6515cad
--- /dev/null
+++ b/qsstv/configform.ui
@@ -0,0 +1,2670 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>configForm</class>
+ <widget class="QDialog" name="configForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>733</width>
+    <height>441</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>ConfigForm</string>
+  </property>
+  <layout class="QVBoxLayout">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="leftMargin">
+    <number>1</number>
+   </property>
+   <property name="topMargin">
+    <number>1</number>
+   </property>
+   <property name="rightMargin">
+    <number>1</number>
+   </property>
+   <property name="bottomMargin">
+    <number>1</number>
+   </property>
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="tabShape">
+      <enum>QTabWidget::Rounded</enum>
+     </property>
+     <property name="currentIndex">
+      <number>9</number>
+     </property>
+     <widget class="QWidget" name="tb1">
+      <attribute name="title">
+       <string>Personal Settings</string>
+      </attribute>
+      <layout class="QVBoxLayout">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <property name="leftMargin">
+        <number>1</number>
+       </property>
+       <property name="topMargin">
+        <number>1</number>
+       </property>
+       <property name="rightMargin">
+        <number>1</number>
+       </property>
+       <property name="bottomMargin">
+        <number>1</number>
+       </property>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="callsignLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>130</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Callsign</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="callsignLineEdit">
+           <property name="alignment">
+            <set>Qt::AlignRight</set>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>120</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="firstnameLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>130</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Firstname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="firstnameLineEdit">
+           <property name="alignment">
+            <set>Qt::AlignRight</set>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>120</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="lastnameLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>130</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Lastname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="lastnameLineEdit">
+           <property name="alignment">
+            <set>Qt::AlignRight</set>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>120</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="qthLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>130</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>QTH</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="qthLineEdit">
+           <property name="alignment">
+            <set>Qt::AlignRight</set>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>120</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="locatorLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>130</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Locator</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="locatorLineEdit">
+           <property name="alignment">
+            <set>Qt::AlignRight</set>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>120</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>21</width>
+           <height>30</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb2">
+      <attribute name="title">
+       <string>Directories</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_4">
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="rxImLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>RX Images</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="rxImagesPathLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="rxImagesPathBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="txImLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>TX Images</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="txImagesPathLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="txImagesPathBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="templLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Templates</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="templatesPathLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="templatesPathBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="audioRecordsLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Audio Records</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="audioPathLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="audioPathBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_4">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="docLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Documentation </string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="docPathLineEdit"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="defaultImageFormatLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>150</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Default Image format</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QComboBox" name="defaultImageFormatComboBox"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb3">
+      <attribute name="title">
+       <string>Audio</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <widget class="soundControl" name="soundWidget" native="true"/>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb4">
+      <attribute name="title">
+       <string>CAT</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="rigControlForm" name="rigWidget" native="true"/>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb5">
+      <attribute name="title">
+       <string>CW</string>
+      </attribute>
+      <layout class="QVBoxLayout">
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="toneLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Tone</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="cwToneSpinBox">
+           <property name="minimum">
+            <number>300</number>
+           </property>
+           <property name="maximum">
+            <number>2300</number>
+           </property>
+           <property name="singleStep">
+            <number>50</number>
+           </property>
+           <property name="value">
+            <number>800</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="wpmLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Words PerMinute</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="cwWPMSpinBox">
+           <property name="minimum">
+            <number>4</number>
+           </property>
+           <property name="maximum">
+            <number>30</number>
+           </property>
+           <property name="value">
+            <number>12</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="textToSendLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Text to send</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="cwTextLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>160</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb6">
+      <attribute name="title">
+       <string>Repeater</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QCheckBox" name="repeaterEnableCheckBox">
+           <property name="text">
+            <string>Enable Repeater</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <layout class="QHBoxLayout">
+           <property name="spacing">
+            <number>6</number>
+           </property>
+           <property name="leftMargin">
+            <number>0</number>
+           </property>
+           <property name="topMargin">
+            <number>0</number>
+           </property>
+           <property name="rightMargin">
+            <number>0</number>
+           </property>
+           <property name="bottomMargin">
+            <number>0</number>
+           </property>
+           <item>
+            <widget class="QLabel" name="imageIntervalLabel">
+             <property name="text">
+              <string>Image Interval (min)</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QSpinBox" name="imageIntervalSpinBox">
+             <property name="minimum">
+              <number>5</number>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>60</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <layout class="QHBoxLayout">
+           <property name="spacing">
+            <number>6</number>
+           </property>
+           <property name="leftMargin">
+            <number>0</number>
+           </property>
+           <property name="topMargin">
+            <number>0</number>
+           </property>
+           <property name="rightMargin">
+            <number>0</number>
+           </property>
+           <property name="bottomMargin">
+            <number>0</number>
+           </property>
+           <item>
+            <widget class="QLabel" name="repeaterTxModeLabel">
+             <property name="text">
+              <string>Repeater TX mode</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QComboBox" name="repeaterTxModeComboBox"/>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="rep1Label">
+           <property name="text">
+            <string>Idle Image 1</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="repeaterImage1LineEdit">
+           <property name="modified">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="rp1BrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="rp2Label">
+           <property name="text">
+            <string>Idle Image 2</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="repeaterImage2LineEdit">
+           <property name="modified">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="rp2BrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="rp3Label">
+           <property name="text">
+            <string>Idle Image 3</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="repeaterImage3LineEdit">
+           <property name="modified">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="rp3BrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="rp4Label">
+           <property name="text">
+            <string>Idle Image 4</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="repeaterImage4LineEdit">
+           <property name="modified">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="rp4BrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_6">
+         <item>
+          <widget class="QLabel" name="Label">
+           <property name="text">
+            <string>Idle Template</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="idleTemplateLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="idleTemplateBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_7">
+         <item>
+          <widget class="QLabel" name="ackLabel">
+           <property name="text">
+            <string>Repeater Template</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="repeaterTemplateLineEdit"/>
+         </item>
+         <item>
+          <widget class="QPushButton" name="repeaterTemplateBrowseButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>Browse ...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb7">
+      <attribute name="title">
+       <string>FTP</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_9">
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QCheckBox" name="enableFTPCheckBox">
+           <property name="text">
+            <string>Enable File Upload to FTP Server</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QLabel" name="ftpPortLabel">
+           <property name="text">
+            <string>FTP Port</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="ftpPortSpinBox">
+           <property name="minimum">
+            <number>1</number>
+           </property>
+           <property name="maximum">
+            <number>10000</number>
+           </property>
+           <property name="value">
+            <number>21</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QLabel" name="defaultImageModeLabel">
+           <property name="text">
+            <string>Default Image Format</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QComboBox" name="ftpDefaultImageFormatComboBox"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="remoteHostLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Remote Hostname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="remoteHostLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="remoteDirectoryLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Remote Directory</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="remoteDirectoryLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="ftploginLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>FTP Loginname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="ftpLoginLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="passwordLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>FTP Password</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="ftpPasswordLineEdit">
+           <property name="echoMode">
+            <enum>QLineEdit::Password</enum>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="title">
+          <string>Image store</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_8">
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout_3">
+            <item>
+             <widget class="QRadioButton" name="imageRadioButton">
+              <property name="text">
+               <string>image1 to image</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QSpinBox" name="ftpNumImagesSpinBox">
+              <property name="minimum">
+               <number>1</number>
+              </property>
+              <property name="maximum">
+               <number>50</number>
+              </property>
+              <property name="value">
+               <number>30</number>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="horizontalSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <widget class="QRadioButton" name="filenameRadioButton">
+            <property name="text">
+             <string>filename</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+         <item>
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>This ftp server is used to upload your received pictures. You can save them as a rotating sequence of images (image1 being the latest and image30 being the oldest) or simply under their own filename.</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="testFTPPushButton">
+           <property name="text">
+            <string>Test connection</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>66</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb8">
+      <attribute name="title">
+       <string>Camera</string>
+      </attribute>
+      <layout class="QVBoxLayout">
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="videoDeviceLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Video device</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="videoDeviceLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>245</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="colorFormatLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Color Format</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QComboBox" name="colorFormatComboBox"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>221</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="sizeLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Size</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QComboBox" name="sizeComboBox"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>131</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout">
+         <item>
+          <widget class="QLabel" name="channelLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Channel</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="channelSpinBox">
+           <property name="maximum">
+            <number>8</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>399</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>21</width>
+           <height>50</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb9">
+      <attribute name="title">
+       <string>Waterfall</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_5">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_12" stretch="0,1">
+         <item>
+          <layout class="QGridLayout" name="gridLayout">
+           <item row="0" column="0">
+            <widget class="QLabel" name="pictureLabel">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>145</width>
+               <height>25</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Start Picture</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QPlainTextEdit" name="startPicTextEdit"/>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="pictureLabel_2">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>145</width>
+               <height>25</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>End Picture</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QPlainTextEdit" name="endPicTextEdit"/>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="fixLabel">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>145</width>
+               <height>25</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>FIX</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QPlainTextEdit" name="fixTextEdit"/>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="nsrLabel">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>145</width>
+               <height>25</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>BSR</string>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QPlainTextEdit" name="bsrTextEdit"/>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_9">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tb10">
+      <attribute name="title">
+       <string>Hybrid</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QCheckBox" name="enableHybridRxCheckBox">
+         <property name="text">
+          <string>Enable Reception in Hybrid Mode </string>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_3">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QCheckBox" name="enableSpecialServerCheckBox">
+           <property name="text">
+            <string>Enable Special Server</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Expanding</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QLabel" name="ftpPortLabel_2">
+           <property name="text">
+            <string>FTP Port</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QSpinBox" name="hybridFtpPortSpinBox">
+           <property name="minimum">
+            <number>1</number>
+           </property>
+           <property name="maximum">
+            <number>10000</number>
+           </property>
+           <property name="value">
+            <number>21</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_2">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="hybridRemoteHostLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Remote Hostname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="hybridRemoteHostLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_7">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="hybridFtpLoginLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>FTP Loginname</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="hybridFtpLoginLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_6">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="hybridPasswordLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>FTP Password</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="hybridFtpPasswordLineEdit">
+           <property name="echoMode">
+            <enum>QLineEdit::Password</enum>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_8">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="hybridRemoteDirectoryLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Remote Directory</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="hybridRemoteDirectoryLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="_9">
+         <property name="spacing">
+          <number>6</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="hybridRemoteDirectoryLabel_2">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>145</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Hybrid Files Directory</string>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="hybridFilesDirectoryLineEdit"/>
+         </item>
+         <item>
+          <spacer>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Preferred</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
+         <item>
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>By default all hybrid files are uploaded to a default server ("Enable Special Server"  not checked). If you wish to use your own server for sending in Hybrid mode then  you need to check "Enable Special Server" and fill in the details. See manual for more information.</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="testHybridPushButton">
+           <property name="text">
+            <string>Test connection</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="spacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Expanding</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>191</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="drmPFTab">
+      <attribute name="title">
+       <string>DRM Profiles</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_7">
+       <item>
+        <widget class="drmProfileForm" name="drmProfileWidget" native="true"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <property name="leftMargin">
+      <number>0</number>
+     </property>
+     <property name="topMargin">
+      <number>0</number>
+     </property>
+     <property name="rightMargin">
+      <number>0</number>
+     </property>
+     <property name="bottomMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <customwidgets>
+  <customwidget>
+   <class>soundControl</class>
+   <extends>QWidget</extends>
+   <header>sound/soundcontrol.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>rigControlForm</class>
+   <extends>QWidget</extends>
+   <header>rig/rigcontrolform.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>drmProfileForm</class>
+   <extends>QWidget</extends>
+   <header>drmprofileform.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>okButton</sender>
+   <signal>clicked()</signal>
+   <receiver>configForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>cancelButton</sender>
+   <signal>clicked()</signal>
+   <receiver>configForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/configparams.cpp b/qsstv/configparams.cpp
new file mode 100644
index 0000000..14bcfcb
--- /dev/null
+++ b/qsstv/configparams.cpp
@@ -0,0 +1,110 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "configparams.h"
+
+QString rxImagesPath; //!< path to the received images directory
+QString txImagesPath; //!< path to the transmit images directory
+QString templatesPath; //!< path to the templates directory
+QString audioPath; //!< directory path to the audio files
+QString docURL; //!< url of documentation
+QString defaultImageFormat; //!< the default format for to save the image in (e.g. png, jpg ..)
+//QString fontString;
+//int fontSize;
+int samplingrate; //!< samplingrate of the souncard (the nominal sampling frequency i.e. 8000, 11025 ...)
+double rxClock;   //!< adjusted receive Clock
+double txClock;	 //!< adjusted transmit Clock
+
+QString serialPort;
+QString pttSerialPort;
+QString radioModel;
+int radioModelNumber;
+int civAddress;
+int baudrate;
+QString parity;
+int stopbits;
+int databits;
+QString handshake;
+bool enableCAT;
+//bool enableSerialPTT;
+QString cwText; //!< message to send in CW after sending the image
+int cwTone; //!< frequency of the CW signal
+int cwWPM; //!< CW words per minute
+
+bool enableFTP;
+int ftpPort;
+QString ftpRemoteHost;
+QString ftpRemoteDirectory;
+QString ftpLogin;
+QString ftpPassword;
+QString ftpFilename;
+QString ftpDefaultImageFormat;
+eftpSaveFormat ftpSaveFormat;
+int ftpNumImages;
+
+
+bool enableHybridRx;
+bool enableSpecialServer;
+int hybridFtpPort;
+QString hybridFtpRemoteHost;
+QString hybridFtpRemoteDirectory;
+QString hybridFtpHybridFilesDirectory;
+QString hybridFtpLogin;
+QString hybridFtpPassword;
+bool useHybrid;
+
+int repeaterImageInterval;
+bool repeaterEnable;
+esstvMode repeaterTxMode;
+QString repeaterImage1;
+QString repeaterImage2;
+QString repeaterImage3;
+QString repeaterImage4;
+QString idleTemplate;
+QString repeaterTemplate;
+QString repeaterAcknowledge;
+
+
+
+QString myCallsign;
+QString myQth;
+QString myLocator;
+QString myLastname;
+QString myFirstname;
+
+QString startPicWF;
+QString endPicWF;
+QString fixWF;
+QString bsrWF;
+
+
+QString drmCallsign;
+drmProfileForm *drmProfilePtr;
+
+
+
+QString videoDevice;
+//int framesPerSecondIndex;
+int colorFormatIndex;
+int sizeIndex;
+int channelNumber;
+etransmissionMode transmissionModeIndex;  // SSTV or DRM or FAX
+unsigned int dataScopeOffset;
+
diff --git a/qsstv/configparams.h b/qsstv/configparams.h
new file mode 100644
index 0000000..8b99e30
--- /dev/null
+++ b/qsstv/configparams.h
@@ -0,0 +1,91 @@
+#ifndef CONFIGPARAMS_H
+#define CONFIGPARAMS_H
+#include "sstv/sstvparam.h"
+#include "drmprofileform.h"
+#include <QString>
+
+
+extern QString rxImagesPath;
+extern QString txImagesPath;
+extern QString templatesPath;
+extern QString audioPath;
+extern QString docURL;
+extern QString defaultImageFormat;
+//extern QString fontString;
+//extern int fontSize;
+extern int samplingrate;
+extern double rxClock;
+extern double txClock;
+extern QString serialPort; /**<  serial port device*/
+extern QString radioModel;
+extern int radioModelNumber;
+extern int civAddress;
+extern int baudrate; /**<  serial port baudrate*/
+extern QString parity;
+extern int stopbits;
+extern int databits;
+extern QString handshake;
+extern bool enableCAT;
+//extern bool enableSerialPTT;
+extern QString pttSerialPort;
+
+
+extern QString myCallsign;
+extern QString myQth;
+extern QString myLocator;
+extern QString myLastname;
+extern QString myFirstname;
+
+extern QString cwText;
+extern int cwTone;
+extern int cwWPM;
+
+
+extern bool enableFTP;
+extern int ftpPort;
+extern QString ftpRemoteHost;
+extern QString ftpRemoteDirectory;
+extern QString ftpLogin;
+extern QString ftpPassword;
+extern QString ftpFilename;
+extern QString ftpDefaultImageFormat;
+extern int ftpNumImages;
+extern bool enableHybridRx;
+extern bool enableSpecialServer;
+extern int hybridFtpPort;
+extern QString hybridFtpRemoteHost;
+extern QString hybridFtpRemoteDirectory;
+extern QString hybridFtpHybridFilesDirectory;
+
+extern QString hybridFtpLogin;
+extern QString hybridFtpPassword;
+extern bool useHybrid;
+enum eftpSaveFormat {FTPIM,FTPFILE};
+extern eftpSaveFormat ftpSaveFormat;
+extern bool repeaterEnable;
+extern int repeaterImageInterval;
+extern esstvMode repeaterTxMode;
+extern QString repeaterImage1;
+extern QString repeaterImage2;
+extern QString repeaterImage3;
+extern QString repeaterImage4;
+extern QString repeaterAcknowledge;
+extern QString repeaterTemplate;
+extern QString idleTemplate;
+
+extern QString startPicWF;
+extern QString endPicWF;
+extern QString fixWF;
+extern QString bsrWF;
+extern QString drmCallsign;
+extern drmProfileForm *drmProfilePtr;
+
+
+extern QString videoDevice;
+//extern int framesPerSecondIndex;
+extern int colorFormatIndex;
+extern int sizeIndex;
+extern int channelNumber;
+extern etransmissionMode transmissionModeIndex;  // SSTV , DRM or FAX
+extern unsigned int dataScopeOffset;
+#endif // CONFIGPARAMS_H
diff --git a/qsstv/dispatcher.cpp b/qsstv/dispatcher.cpp
new file mode 100644
index 0000000..a665cc2
--- /dev/null
+++ b/qsstv/dispatcher.cpp
@@ -0,0 +1,568 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+/*!
+  The dispatcher is the central system that routes all messages from the different threads.
+It also starts, stops and synchronizes the threads.
+
+*/
+#include "dispatcher.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include "rxwidget.h"
+#include "txwidget.h"
+#include "gallerywidget.h"
+#include <QSettings>
+#include "widgets/spectrumwidget.h"
+#include "widgets/vumeter.h"
+
+#include "rig/rigcontrol.h"
+#include "editor/editor.h"
+#include "sound/soundio.h"
+#include "sound/waterfalltext.h"
+#include "mainwindow.h"
+#include "drmrx/drm.h"
+#include "utils/ftp.h"
+#include "utils/hybridcrypt.h"
+
+
+
+
+
+/*!
+creates dispatcher instance
+ */
+
+dispatcher::dispatcher()
+{
+  mbox = new QMessageBox(mainWindowPtr);
+  progressFTP=NULL;
+  serialP=0;
+}
+
+/*!
+delete dispatcher instance
+ */
+
+dispatcher::~dispatcher()
+{
+  if(serialP!=0)
+    {
+      close(serialP);
+      serialP=0;
+    }
+}
+
+QString dispatcher::init()
+{
+  QString ret;
+  editorActive=false;
+  rxWidgetPtr->fftDisplayPtr()->init(RXSTRIPE,1,BASESAMPLERATE/SUBSAMPLINGRATIO);
+  galleryWidgetPtr->init();
+  rxWidgetPtr->init();
+  txWidgetPtr->init();
+  waterfallPtr=new waterfallText;
+  waterfallPtr->init();
+  if(!rigController->init())
+    {
+      ret=rigController->initError;
+    }
+  restartRXFlag=false;
+  infoTextPtr=new textDisplay(mainWindowPtr);
+  infoTextPtr->hide();
+  return ret;
+}
+
+
+
+void dispatcher::stopRX()
+{
+  rxWidgetPtr->start(false);
+}
+
+void dispatcher::stopTX()
+{
+  rigController->activatePTT(false);
+  txWidgetPtr->start(false);
+}
+
+void dispatcher::stopRXTX()
+{
+  stopRX();
+  stopTX();
+}
+
+void dispatcher::startRX(bool st)
+{
+  stopTX();
+  rxWidgetPtr->start(st);
+  if(st)
+    {
+      addToLog("dispatcher: starting RX",LOGDISPAT);
+    }
+  else
+    {
+      addToLog("dispatcher: stopping RX",LOGDISPAT);
+    }
+}
+
+void dispatcher::startTX(bool st)
+{
+  stopRX();
+  if(st)
+    {
+      addToLog("dispatcher: starting TX",LOGDISPAT);
+    }
+  else
+    {
+      addToLog("dispatcher: stopping TX",LOGDISPAT);
+    }
+  rigController->activatePTT(st);
+  txWidgetPtr->start(st);
+}
+
+
+
+void dispatcher::readSettings()
+{
+  QSettings qSettings;
+  logfile->readSettings(qSettings);
+}
+
+void dispatcher::writeSettings()
+{
+  QSettings qSettings;
+  logfile->writeSettings(qSettings);
+}
+
+/*!
+  All communication between the threads are passed via this eventhandler.
+*/
+
+void dispatcher::customEvent( QEvent * e )
+{
+  dispatchEventType type;
+  QString fn;
+  type=(dispatchEventType)e->type();
+  addToLog(((baseEvent*)e)->description,LOGDISPAT);
+  switch(type)
+    {
+    case displayFFT:
+      addToLog("dispatcher: displayFFT",LOGDISPAT);
+      rxWidgetPtr->fftDisplayPtr()->realFFT(((displayFFTEvent*)e)->data());
+      rxWidgetPtr->fftDisplayPtr()->repaint();
+      break;
+    case displaySync:
+      // addToLog("dispatcher: displaySync",LOGDISPAT);
+      uint s;double v;
+      ((displaySyncEvent*)e)->getInfo(s,v);
+      rxWidgetPtr->sMeterPtr()->setValue((double)s);
+      rxWidgetPtr->vMeterPtr()->setValue(v);
+      break;
+    case rxSSTVStatus:
+      rxWidgetPtr->setSSTVStatusText(((statusMsgEvent*)e)->getStr());
+      break;
+    case rxDRMStatus:
+      rxWidgetPtr->setDRMStatusText(((statusMsgEvent*)e)->getStr());
+      break;
+    case startImageRX:
+      addToLog("dispatcher: clearing RxImage",LOGDISPAT);
+      rxWidgetPtr->getImageViewerPtr()->createImage( ((startImageRXEvent*)e)->getSize(),QColor(255,255,0));
+      rxWidgetPtr->getImageViewerPtr()->clear();
+      break;
+    case lineDisplay:
+      {
+        rxWidgetPtr->getImageViewerPtr()->displayImage(false);
+      }
+      break;
+      //    case syncLost:
+      //    case verticalRetrace:
+      //      addToLog("dispatcher: verticalRetrace",LOGDISPAT);
+
+      //      if(autoSave)
+      //        {
+      //          addToLog("dispatcher: verticalRetrace savingRxImage",LOGDISPAT);
+      //          saveRxSSTVImage();
+      //        }
+      //      rxWidgetPtr->functionsPtr()->retraceVertical();
+      //    break;
+    case endImageRX:
+      if(autoSave)
+        {
+          addToLog("dispatcher:endImage savingRxImage",LOGDISPAT);
+          saveRxSSTVImage();
+        }
+      break;
+    case callEditor:
+      if(editorActive) break;
+      editorActive=true;
+      ed=new editor();
+      ed->show();
+      iv=((callEditorEvent*)e)->getImageViewer();
+      addToLog (QString(" callEditorEvent imageViewPtr: %1").arg(QString::number((ulong)iv,16)),LOGDISPAT);
+      addToLog(QString("editor: filename %1").arg(((callEditorEvent*)e)->getFilename()),LOGDISPAT);
+      ed->openFile(((callEditorEvent*)e)->getFilename());
+      break;
+
+    case editorFinished:
+      if(!editorActive) break;
+      if(((editorFinishedEvent*)e)->isOK())
+        {
+          addToLog (QString(" editorFinishedEvent imageViewPtr: %1").arg(QString::number((ulong)iv,16)),LOGDISPAT);
+          iv->reload();
+        }
+      editorActive=false;
+      delete ed;
+      break;
+
+    case templatesChanged:
+      txWidgetPtr->setupTemplatesComboBox();
+      break;
+    case progressTX:
+      txTimeCounter=0;
+      prTimerIndex=startTimer(((progressTXEvent*)e)->getInfo()*10); // time in seconds -> times 1000 for msec,divide by 100 for progress
+      break;
+    case stoppingTX:
+      addToLog("dispatcher: endTXImage",LOGDISPAT);
+      while(!soundIOPtr->stoppedPlaying())
+        {
+          qApp->processEvents();
+        }
+      rigController->activatePTT(false);
+      break;
+
+    case endImageTX:
+      addToLog("dispatcher: endTXImage",LOGDISPAT);
+      while(!soundIOPtr->stoppedPlaying())
+        {
+          qApp->processEvents();
+        }
+      rigController->activatePTT(false);
+      restartRX();
+      break;
+    case displayDRMInfo:
+      //      rxWidgetPtr->psdWdg()->setPSD();
+      rxWidgetPtr->mscWdg()->setConstellation(MSC);
+      rxWidgetPtr->facWdg()->setConstellation(FAC);
+      rxWidgetPtr->statusWdg()->setStatus();
+      break;
+
+    case displayDRMStat:
+      DSPFLOAT s1;DSPFLOAT v1;
+      ((displayDRMStatEvent*)e)->getInfo(s1,v1);
+      rxWidgetPtr->sMeterPtr()->setValue(s1);
+      rxWidgetPtr->vMeterPtr()->setValue(v1);
+      break;
+
+    case loadRXImage:
+      {
+        ((loadRXImageEvent*)e)->getFilename(fn);
+        rxWidgetPtr->getImageViewerPtr()->openImage(fn,true,false);
+      }
+      break;
+
+    case saveDRMImage:
+      {
+        ((saveDRMImageEvent*)e)->getFilename(fn);
+        rxWidgetPtr->getImageViewerPtr()->openImage(fn,true,false);
+        saveImage(fn);
+      }
+      break;
+    case prepareFix:
+      addToLog("prepareFix",LOGDISPAT);
+      startDRMFIXTx( ((prepareFixEvent*)e)->getData());
+      break;
+    case displayText:
+      infoTextPtr->clear();
+      infoTextPtr->setWindowTitle(QString("Received from %1").arg(drmCallsign));
+      infoTextPtr->append(((displayTextEvent*)e)->getStr());
+      infoTextPtr->show();
+
+      break;
+
+    case displayMBox:
+      mbox->setWindowTitle(((displayMBoxEvent*)e)->getTitle());
+      mbox->setText(((displayMBoxEvent*)e)->getStr());
+      mbox->show();
+      QTimer::singleShot(4000, mbox, SLOT(hide()));
+      break;
+    case displayProgressFTP:
+      {
+        if(((displayProgressFTPEvent*)e)->getTotal()==0)
+          {
+            delete progressFTP;
+            progressFTP=NULL;
+            break;
+          }
+        if(progressFTP==NULL)
+          {
+            progressFTP=new QProgressDialog("FTP Transfer","Cancel",0,0,mainWindowPtr);
+          }
+        progressFTP->show();
+        progressFTP->setMaximum(((displayProgressFTPEvent*)e)->getTotal());
+        progressFTP->setValue(((displayProgressFTPEvent*)e)->getBytes());
+      }
+      break;
+    default:
+      addToLog(QString("unsupported event: %1").arg(((baseEvent*)e)->description), LOGALL);
+      break;
+    }
+  ((baseEvent *)e)->setDone();
+}
+
+
+
+void dispatcher::receiveImage()
+{
+}
+
+void dispatcher::restartRX()
+{
+  txTimeCounter=9999999; //force stop timer
+  startRX(true);
+}
+
+void dispatcher::startSSTVTx()
+{
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSSTVIMAGE);
+  addToLog("send SSTV Image",LOGDISPAT);
+
+}
+
+void dispatcher::sendTone(double duration,double freq)
+{
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  txWidgetPtr->functionsPtr()->setToneParam(duration,freq);
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDTONE);
+  addToLog("sendTone",LOGDISPAT);
+}
+
+void dispatcher::sendWF(QString txt)
+{
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  waterfallPtr->setText(txt);
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDID);
+  addToLog("sendID",LOGDISPAT);
+}
+
+void dispatcher::startDRMTx()
+{
+  stopRX();
+
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  //  waterfallPtr->setText(pictureWF);
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDDRM);
+  addToLog("sendDRM",LOGDISPAT);
+}
+
+void dispatcher::startDRMBSRTx(QByteArray *ba)
+{
+  if(ba==NULL) return;
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->initDRMBSR(ba);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDDRMBSR);
+  addToLog("sendDRMBSR",LOGDISPAT);
+}
+
+void dispatcher::startDRMFIXTx(QByteArray ba)
+{
+  if(!txWidgetPtr->prepareFIX(ba)) return;
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDDRMFIX);
+  addToLog("sendDRMFIX",LOGDISPAT);
+}
+
+void dispatcher::startDRMHybridTx(QString fn)
+{
+  eftpError ftpResult;
+  QByteArray ba;
+  QTemporaryFile ftmp;
+  ftpInterface ftpIntf("HybridTX");
+  hybridCrypt hc;
+  ftpIntf.setupConnection(hc.host(),hc.port(),hc.user(),hc.passwd(),hc.dir()+"/"+hybridFtpHybridFilesDirectory);
+  //  txWidgetPtr->getImageViewerPtr()->getFilename();
+  txWidgetPtr->getImageViewerPtr()->copyToBuffer(&ba);
+  if(!ftmp.open()) return;
+  ftmp.write(ba);
+  ftmp.close();
+  ftpResult=ftpIntf.uploadFile(ftmp.fileName(),fn,true);
+  switch(ftpResult)
+    {
+    case FTPCANCELED:
+      QMessageBox::information(mainWindowPtr, tr("FTP Info"),"Connection Canceled");
+      return;
+      break;
+    case FTPOK:
+      break;
+    case FTPERROR:
+      QMessageBox::critical(mainWindowPtr, tr("FTP Error"),ftpIntf.getLastError());
+      return;
+      break;
+    case FTPNAMEERROR:
+      QMessageBox::critical(mainWindowPtr, tr("FTP Error"),"Error in filename");
+      return;
+      break;
+    case FTPTIMEOUT:
+      QMessageBox::critical(mainWindowPtr, tr("FTP Error"),"FTP timed out");
+      return;
+      break;
+    }
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDDRM);
+  addToLog("sendDRMHybrid",LOGDISPAT);
+}
+
+void dispatcher::startDRMHybridText(QString txt)
+{
+  if(!txWidgetPtr->prepareText(txt)) return;
+  stopRX();
+  txWidgetPtr->start(true,false);
+  if(txWidgetPtr->functionsPtr()->getTXState()!=txFunctions::TXIDLE) return;
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDDRMTXT);
+  addToLog("sendDRMTxt",LOGDISPAT);
+}
+
+
+
+void dispatcher::sendSweepTone(double duration,double lowerFreq,double upperFreq)
+{
+  stopRX();
+  txWidgetPtr->start(true,false);
+  txWidgetPtr->functionsPtr()->setToneParam(duration,lowerFreq,upperFreq);
+  rigController->activatePTT(true);
+  txWidgetPtr->functionsPtr()->setTXState(txFunctions::TXSENDTONE);
+  addToLog("sendSweepTone",LOGDISPAT);
+}
+
+
+
+
+void dispatcher::saveRxSSTVImage()
+{
+  if (rxWidgetPtr->functionsPtr()->getModeString().isEmpty()) return;
+  if(!rxWidgetPtr->functionsPtr()->saveOK()) return;
+  QString s,fileName;
+  QDateTime dt(QDateTime::currentDateTime().toUTC()); //this is compatible with QT 4.6
+  dt.setTimeSpec(Qt::UTC);
+  fileName=QString("%1/%2_%3.%4").arg(rxImagesPath).arg(rxWidgetPtr->functionsPtr()->getModeString()).arg(dt.toString("yyyyMMdd_HHmmss")).arg(defaultImageFormat);
+  addToLog(QString("dispatcher: saveRxImage():%1 ").arg(fileName),LOGDISPAT);
+  rxWidgetPtr->getImageViewerPtr()->save(fileName,defaultImageFormat,true);
+  saveImage(fileName);
+}
+
+void dispatcher::saveImage(QString fileName)
+{
+  QImage im;
+  QFileInfo info(fileName);
+  eftpError ftpResult;
+  displayMBoxEvent *stmb=0;
+  QString fn="/tmp/"+info.baseName()+"."+ftpDefaultImageFormat;
+  galleryWidgetPtr->putRxImage(fileName);
+  txWidgetPtr->setPreviewWidget(fileName);
+  if(enableFTP)
+    {
+      ftpInterface ftpIntf("Save RX Image");
+      rxWidgetPtr->getImageViewerPtr()->save(fn,ftpDefaultImageFormat,true);
+      ftpIntf.setupConnection(ftpRemoteHost,ftpPort,ftpLogin,ftpPassword,ftpRemoteDirectory);
+      ftpResult=ftpIntf.uploadToRXServer(fn);
+      switch(ftpResult)
+        {
+        case FTPOK:
+          break;
+        case FTPERROR:
+          stmb= new displayMBoxEvent("FTP Error",QString("Host: %1: %2").arg(ftpRemoteHost).arg(ftpIntf.getLastError()));
+          break;
+        case FTPNAMEERROR:
+          stmb= new displayMBoxEvent("FTP Error",QString("Host: %1, Error in filename").arg(ftpRemoteHost));
+          break;
+        case FTPCANCELED:
+          stmb= new displayMBoxEvent("FTP Error",QString("Connection to %1 Canceled").arg(ftpRemoteHost));
+          break;
+        case FTPTIMEOUT:
+          stmb= new displayMBoxEvent("FTP Error",QString("Connection to %1 timed out").arg(ftpRemoteHost));
+          break;
+        }
+      if(ftpResult!=FTPOK)
+        {
+          QApplication::postEvent( dispatcherPtr, stmb );  // Qt will delete it when done
+          return;
+        }
+
+    }
+}
+
+
+
+void dispatcher::timerEvent(QTimerEvent *event)
+{
+  if(event->timerId()==prTimerIndex)
+    {
+      txWidgetPtr->setProgress(++txTimeCounter);
+      if(txTimeCounter>=100)
+        {
+          if(prTimerIndex>=0)
+            {
+              killTimer(prTimerIndex);
+              prTimerIndex=-1;
+              txWidgetPtr->setProgress(0);
+            }
+        }
+      txWidgetPtr->setProgress(txTimeCounter);
+    }
+  else if(event->timerId()==logTimerIndex)
+    {
+      //      addToLog(QString("dumping dispatcher status"),LOGALL);
+      //      if( rxFuncPtr) rxFuncPtr->logStatus();
+      //      if( txFuncPtr)txFuncPtr->logStatus();
+      //      if( sndIO)sndIO->logStatus();
+    }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/qsstv/dispatcher.h b/qsstv/dispatcher.h
new file mode 100644
index 0000000..e9b4ca7
--- /dev/null
+++ b/qsstv/dispatcher.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef DISPATCHER_H
+#define DISPATCHER_H
+#include "dispatchevents.h"
+#include "qsstvglobal.h"
+#include <QByteArray>
+#include "widgets/textdisplay.h"
+class editor;
+class imageViewer;
+#include <QProgressDialog>
+
+
+/**
+ at author Johan Maes
+*/
+class dispatcher : public QObject
+{
+	Q_OBJECT
+
+public:
+
+	dispatcher();
+	~dispatcher();
+  QString init();
+  void readSettings();
+  void writeSettings();
+  void customEvent( QEvent * e );
+  void receiveImage();
+  void restartRX();
+  void startSSTVTx();
+  void sendTone(double duration,double freq);
+  void sendWF(QString txt);
+  void startDRMTx();
+  void startDRMBSRTx(QByteArray *ba);
+  void startDRMFIXTx(QByteArray ba);
+  void startDRMHybridTx(QString fn);
+  void startDRMHybridText(QString txt);
+  void sendSweepTone(double duration,double lowerFreq,double upperFreq);
+  void startRX(bool st);
+  void startTX(bool st);
+  void stopRX();
+  void stopTX();
+  void stopRXTX();
+  void saveImage(QString fileName);
+private:
+    int serialP;
+    void saveRxSSTVImage();
+    void timerEvent(QTimerEvent *event);
+    bool editorActive;
+    editor *ed;
+    imageViewer *iv;
+    int txTimeCounter;
+    int prTimerIndex;
+    int logTimerIndex;
+    bool restartRXFlag;
+    textDisplay *infoTextPtr;
+    QMessageBox *mbox;
+    QProgressDialog *progressFTP;
+};
+#endif
+
diff --git a/qsstv/dispatchevents.h b/qsstv/dispatchevents.h
new file mode 100644
index 0000000..abaeabb
--- /dev/null
+++ b/qsstv/dispatchevents.h
@@ -0,0 +1,567 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef DISPATCHEVENT_H
+#define DISPATCHEVENT_H
+#include <QEvent>
+#include "widgets/imageviewer.h"
+#include "qsstvdefs.h"
+#include <unistd.h>
+
+
+/** dispatch events are used to communicate with the different threads */
+enum dispatchEventType
+{
+  info = QEvent::User, //!< send when dsp stops running
+  soundcardIdle, //!< send when soundcard stops running
+  displayFFT,
+  displaySync,
+  displayDRMStat,
+  displayDRMInfo,
+  syncDisp,				//!< synchro display event
+  lineDisplay,				//!< display 1 line
+  eraseDisp,
+  createMode,
+  startImageRX,
+  endImageRX,
+  endImageTX,
+  stoppingTX,
+  progressTX,
+//  verticalRetrace,
+//  syncLost,
+  outOfSync,
+  statusMsg,  	//!<  display status message
+  rxSSTVStatus,     //! shows message in sstv tab
+  rxDRMStatus,     //! shows message in drm tab
+  closeWindows,
+  callEditor,
+  templatesChanged,
+  editorFinished,
+  changeRXFilter,
+  startAutoRepeater,
+  startRepeater,
+  stopRxTx,
+  loadRXImage,
+  saveDRMImage,
+  prepareFix,
+  displayText,
+  displayMBox,
+  displayProgressFTP
+};
+
+class baseEvent: public QEvent
+{
+public:
+  baseEvent(QEvent::Type t):QEvent(t) {doneIt=NULL;}
+  void waitFor(bool *d) {doneIt=d;}
+  void setDone()
+  {
+    if(doneIt!=NULL) *doneIt=true;
+  }
+  QString description;
+private:
+  bool *doneIt;
+
+};
+
+/**
+  this event is send when the dspfunc thread stops running
+*/
+class infoEvent : public  baseEvent
+{
+public:
+  /** create event */
+  infoEvent(QString t):baseEvent( (QEvent::Type) info ), str(t)
+  {
+    description="infoEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+private:
+  QString str;
+};
+
+/**
+  this event is send when the soundcard thread goes to idle
+*/
+class soundcardIdleEvent : public baseEvent
+{
+public:
+  /** create event */
+  soundcardIdleEvent():baseEvent( (QEvent::Type) soundcardIdle )
+  {
+    {
+      description=" soudcardIdleEvent";
+    }
+  }
+};
+
+
+//class rxDataAvailableEvent : public baseEvent
+//{
+//public:
+//	/** create event */
+//	rxDataAvailableEvent(uint idx,uint numSamples):baseEvent( (QEvent::Type)rxData ), index(idx),len(numSamples) {}
+//	/** returns length and pointer  from the event */
+//  uint getIndex(uint &idx) const { idx=index; return len;}
+
+//private:
+//  uint index;
+//	uint len;
+//};
+
+
+/**
+  this event is send with teh sync quality info and the signal volume
+*/
+class displaySyncEvent : public baseEvent
+{
+public:
+  /** create event */
+  displaySyncEvent(uint s,double v):baseEvent( (QEvent::Type) displaySync), sync(s), vol(v)
+  {
+    description=" displaySyncEvent";
+  }
+  /** returns length and pointer  from the event */
+  void getInfo(uint &s,double &v)  {s=sync; v=vol;}
+
+private:
+  uint sync;
+  DSPFLOAT vol;
+};
+
+class displayDRMStatEvent  : public baseEvent
+{
+public:
+  /** create event */
+  displayDRMStatEvent(uint s,DSPFLOAT v):baseEvent( (QEvent::Type) displayDRMStat), snr(s), vol(v)
+  {
+    description=" displayDRMStatEvent";
+  }
+  /** returns length and pointer  from the event */
+  void getInfo(DSPFLOAT &s,DSPFLOAT &v)  {s=snr; v=vol;}
+
+private:
+  DSPFLOAT snr;
+  DSPFLOAT vol;
+};
+
+class statusMsgEvent : public baseEvent
+{
+public:
+  /** create event */
+  statusMsgEvent(QString t):baseEvent( (QEvent::Type)statusMsg ), str(t)
+  {
+    description="statusMsgEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+private:
+  QString str;
+};
+
+
+
+class rxSSTVStatusEvent : public baseEvent
+{
+public:
+  /** create event */
+  rxSSTVStatusEvent(QString t):baseEvent( (QEvent::Type)rxSSTVStatus ), str(t)
+  {
+    description="rxSSTVStatusEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+private:
+  QString str;
+};
+
+class rxDRMStatusEvent : public baseEvent
+{
+public:
+  /** create event */
+  rxDRMStatusEvent(QString t):baseEvent( (QEvent::Type)rxDRMStatus ), str(t)
+  {
+    description="rxDRMStatusEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+private:
+  QString str;
+};
+
+class lineDisplayEvent : public baseEvent
+{
+public:
+  /** create event */
+  lineDisplayEvent(uint lineNbr):baseEvent( (QEvent::Type)lineDisplay ), lineNumber(lineNbr)
+  {
+    description="lineDisplayEvent";
+  }
+  /** returns length and pointer  from the event */
+  void getInfo(uint &lineNbr) const { lineNbr=lineNumber;}
+
+private:
+  uint lineNumber;
+};
+
+class eraseDisplayEvent : public baseEvent
+{
+public:
+  /** create event */
+  eraseDisplayEvent():baseEvent( (QEvent::Type)eraseDisp )
+  {
+    description="eraseDisplayEvent";
+  }
+};
+
+
+
+class displayDRMInfoEvent : public baseEvent
+{
+public:
+  /** create event */
+  displayDRMInfoEvent():baseEvent( (QEvent::Type)displayDRMInfo)
+  {
+    description="displayDRMInfo";
+  }
+};
+
+class startAutoRepeaterEvent: public baseEvent
+{
+public:
+  /** create event */
+  startAutoRepeaterEvent():baseEvent( (QEvent::Type)startAutoRepeater )
+  {
+    description="startAutoRepeaterEvent";
+  }
+};
+
+class startRepeaterEvent: public baseEvent
+{
+public:
+  /** create event */
+  startRepeaterEvent():baseEvent( (QEvent::Type)startRepeater )
+  {
+    description="startRepeaterEvent";
+  }
+};
+
+
+class createModeEvent : public baseEvent
+{
+public:
+  /** create event */
+  createModeEvent(uint m,QString t):baseEvent( (QEvent::Type)createMode ), mode(m) ,str(t)
+  {
+    description="createModeEvent";
+  }
+  /** returns info string from the event */
+  void getMode(uint &m,QString &s) const { m=mode;s=str; }
+private:
+  uint mode;
+  QString str;
+};
+
+class loadRXImageEvent : public baseEvent
+{
+public:
+  loadRXImageEvent(QString fn):baseEvent( (QEvent::Type)loadRXImage),fileName(fn)
+  {
+    description="loadRXImageEvent";
+  }
+  void getFilename(QString &fn) {fn=fileName;}
+private:
+  QString fileName;
+};
+
+
+
+class saveDRMImageEvent : public baseEvent
+{
+public:
+  saveDRMImageEvent(QString fn):baseEvent( (QEvent::Type)saveDRMImage),fileName(fn)
+  {
+    description="saveDRMImageEvent";
+  }
+  void getFilename(QString &fn) {fn=fileName;}
+private:
+  QString fileName;
+};
+
+
+
+class startImageRXEvent : public baseEvent
+{
+public:
+  /** create event */
+  startImageRXEvent(QSize ims):baseEvent( (QEvent::Type)startImageRX ),imSize(ims)
+  {
+    description="startImageRXEvent";
+  }
+  QSize getSize()  {return imSize;}
+private:
+  QSize imSize;
+
+};
+
+class endImageRXEvent : public baseEvent
+{
+public:
+  /** create event */
+  endImageRXEvent():baseEvent( (QEvent::Type)endImageRX )
+  {
+    description="endImageRXEvent";
+  }
+};
+
+class endImageTXEvent : public baseEvent
+{
+public:
+  /** create event */
+  endImageTXEvent():baseEvent( (QEvent::Type)endImageTX )
+  {
+    description="endImageTXEvent";
+  }
+};
+
+
+class stopTXEvent : public baseEvent
+{
+public:
+  /** create event */
+  stopTXEvent():baseEvent( (QEvent::Type)stoppingTX )
+  {
+    description="stopTXEvent";
+  }
+};
+
+//class verticalRetraceEvent : public baseEvent
+//{
+//public:
+//  /** create event */
+//  verticalRetraceEvent():baseEvent( (QEvent::Type) verticalRetrace )
+//  {
+//    description="verticalRetraceEvent";
+//  }
+//};
+
+//class syncLostEvent : public baseEvent
+//{
+//public:
+//  /** create event */
+//  syncLostEvent():baseEvent( (QEvent::Type) syncLost )
+//  {
+//    description="syncLostEvent";
+//  }
+//};
+
+
+
+class outOfSyncEvent : public baseEvent
+{
+public:
+  /** create event */
+  outOfSyncEvent():baseEvent( (QEvent::Type)outOfSync )
+  {
+    description="outOfSyncEvent";
+  }
+};
+
+
+
+class progressTXEvent : public baseEvent
+{
+public:
+  /** create event */
+  progressTXEvent(double tim):baseEvent( (QEvent::Type)progressTX ), txTime(tim)
+  {
+    description="progressTXEvent";
+  }
+  /** returns length and pointer  from the event */
+  double getInfo() { return txTime;}
+
+private:
+  double txTime;
+};
+
+class closeWindowsEvent : public baseEvent
+{
+public:
+  /** create event */
+  closeWindowsEvent():baseEvent( (QEvent::Type)closeWindows)
+  {
+    description="closeWindowEvent";
+  }
+  /** returns length and pointer  from the event */
+};
+
+
+
+class callEditorEvent : public baseEvent
+{
+public:
+  /** create event */
+  callEditorEvent(imageViewer *iv,QString fn):baseEvent( (QEvent::Type) callEditor ), filename(fn),imviewer(iv)
+  {
+    description="callEditorEvent";
+  }
+  /** returns info string from the event */
+  QString getFilename() const { return filename; }
+  imageViewer *getImageViewer() { return imviewer; }
+private:
+  QString filename;
+  imageViewer *imviewer;
+};
+
+
+class templatesChangedEvent : public baseEvent
+{
+public:
+  /** create event */
+  templatesChangedEvent():baseEvent( (QEvent::Type) templatesChanged )
+  {
+    description="templateChangeEvent";
+  }
+};
+
+class editorFinishedEvent : public baseEvent
+{
+public:
+  /** create event */
+  editorFinishedEvent(bool b,QString fn):baseEvent( (QEvent::Type)editorFinished),ok(b),filename(fn)
+  {
+    description="editorFinishedEvent";
+  }
+  bool isOK() { return ok;}
+  QString getFilename() const { return filename; }
+
+private:
+  bool ok;
+  QString filename;
+
+};
+
+
+class displayFFTEvent : public baseEvent
+{
+public:
+  /** create event */
+  displayFFTEvent(DSPFLOAT *buf):baseEvent( (QEvent::Type)displayFFT),buffer(buf)
+  {
+    description="displayFFTEvent";
+  }
+  DSPFLOAT *data() { return buffer;}
+private:
+  DSPFLOAT *buffer;
+
+};
+
+class filterRXChangedEvent: public baseEvent
+{
+public:
+  /** create event */
+  filterRXChangedEvent(int fIndex):baseEvent( (QEvent::Type)changeRXFilter),filterIndex(fIndex)
+  {
+    description="filterChangedEvent";
+  }
+  int index() { return filterIndex;}
+private:
+  int filterIndex;
+};
+
+class stopRxTxEvent : public baseEvent
+{
+public:
+  /** create event */
+  stopRxTxEvent():baseEvent( (QEvent::Type)stopRxTx)
+  {
+    description="stopRxTxEvent";
+  }
+};
+
+
+class prepareFixEvent: public baseEvent
+{
+public:
+  prepareFixEvent(QByteArray ba):baseEvent( (QEvent::Type)prepareFix),data(ba)
+  {
+    description="filterChangedEvent";
+  }
+  QByteArray &getData() {return data;}
+private:
+  QByteArray data;
+};
+
+
+/**
+  this event is send when the dspfunc thread stops running
+*/
+class displayTextEvent : public  baseEvent
+{
+public:
+  /** create event */
+  displayTextEvent(QString t):baseEvent( (QEvent::Type) displayText ), str(t)
+  {
+    description="displayTextEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+private:
+  QString str;
+};
+
+class displayMBoxEvent : public  baseEvent
+{
+public:
+  /** create event */
+  displayMBoxEvent(QString title,QString text):baseEvent( (QEvent::Type) displayMBox ), str(text), title(title)
+  {
+    description="displayMBoxEvent";
+  }
+  /** returns info string from the event */
+  QString getStr() const { return str; }
+  QString getTitle() const { return title; }
+
+private:
+  QString str;
+  QString title;
+};
+
+
+class displayProgressFTPEvent : public  baseEvent
+{
+public:
+  /** create event */
+  displayProgressFTPEvent(quint64 byts,quint64 tot):baseEvent( (QEvent::Type) displayProgressFTP ),  bytes(byts),total(tot)
+  {
+    description="displayMBoxEvent";
+  }
+  /** returns info string from the event */
+  quint64 getTotal() const { return total; }
+  quint64 getBytes() const { return bytes; }
+
+private:
+  quint64 bytes;
+  quint64 total;
+
+};
+
+#endif
diff --git a/qsstv/drmprofileform.cpp b/qsstv/drmprofileform.cpp
new file mode 100644
index 0000000..c91fa60
--- /dev/null
+++ b/qsstv/drmprofileform.cpp
@@ -0,0 +1,167 @@
+#include "drmprofileform.h"
+#include "ui_drmprofileform.h"
+#include "utils/supportfunctions.h"
+#include "configparams.h"
+
+drmProfileForm::drmProfileForm(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::drmProfileForm)
+{
+  ui->setupUi(this);
+  readSettings();
+}
+
+drmProfileForm::~drmProfileForm()
+{
+    writeSettings();
+  delete ui;
+}
+
+
+void drmProfileForm::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup ("DRM Profile" );
+  drmPFArray[0].name=qSettings.value ("drmPF1Name","Profile 1").toString();
+  drmPFArray[0].params.robMode=qSettings.value ("drmPF1Mode",0).toInt();
+  drmPFArray[0].params.qam=qSettings.value("drmPF1QAM",0).toInt();
+  drmPFArray[0].params.bandwith=qSettings.value("drmPF1Bandwidth",0).toInt();
+  drmPFArray[0].params.protection=qSettings.value("drmPF1Protection",0).toInt();
+  drmPFArray[0].params.interleaver=qSettings.value("drmPF1Interleave",0).toInt();
+  drmPFArray[0].params.reedSolomon=qSettings.value("drmPF1ReedSolomon",0).toInt();
+
+  drmPFArray[1].name=qSettings.value ("drmPF2Name","Profile 2").toString();
+  drmPFArray[1].params.robMode=qSettings.value ("drmPF2Mode",0).toInt();
+  drmPFArray[1].params.qam=qSettings.value("drmPF2QAM",0).toInt();
+  drmPFArray[1].params.bandwith=qSettings.value("drmPF2Bandwidth",0).toInt();
+  drmPFArray[1].params.protection=qSettings.value("drmPF2Protection",0).toInt();
+  drmPFArray[1].params.interleaver=qSettings.value("drmPF2Interleave",0).toInt();
+  drmPFArray[1].params.reedSolomon=qSettings.value("drmPF2ReedSolomon",0).toInt();
+
+  drmPFArray[2].name=qSettings.value ("drmPF3Name","Profile 3").toString();
+  drmPFArray[2].params.robMode=qSettings.value ("drmPF3Mode",0).toInt();
+  drmPFArray[2].params.qam=qSettings.value("drmPF3QAM",0).toInt();
+  drmPFArray[2].params.bandwith=qSettings.value("drmPF3Bandwidth",0).toInt();
+  drmPFArray[2].params.protection=qSettings.value("drmPF3Protection",0).toInt();
+  drmPFArray[2].params.interleaver=qSettings.value("drmPF3Interleave",0).toInt();
+  drmPFArray[2].params.reedSolomon=qSettings.value("drmPF3ReedSolomon",0).toInt();
+
+
+  qSettings.endGroup();
+  setParams();
+
+}
+
+void drmProfileForm::writeSettings()
+{
+  QSettings qSettings;
+  getParams();
+  qSettings.beginGroup ("DRM Profile" );
+
+  qSettings.setValue ("drmPF1Name",drmPFArray[0].name);
+  qSettings.setValue ("drmPF1Mode",drmPFArray[0].params.robMode);
+  qSettings.setValue("drmPF1QAM",drmPFArray[0].params.qam);
+  qSettings.setValue("drmPF1Bandwidth",drmPFArray[0].params.bandwith);
+  qSettings.setValue("drmPF1Protection",drmPFArray[0].params.protection);
+  qSettings.setValue("drmPF1Interleave",drmPFArray[0].params.interleaver);
+  qSettings.setValue("drmPF1ReedSolomon",drmPFArray[0].params.reedSolomon);
+
+  qSettings.setValue ("drmPF2Name",drmPFArray[1].name);
+  qSettings.setValue ("drmPF2Mode",drmPFArray[1].params.robMode);
+  qSettings.setValue("drmPF2QAM",drmPFArray[1].params.qam);
+  qSettings.setValue("drmPF2Bandwidth",drmPFArray[1].params.bandwith);
+  qSettings.setValue("drmPF2Protection",drmPFArray[1].params.protection);
+  qSettings.setValue("drmPF2Interleave",drmPFArray[1].params.interleaver);
+  qSettings.setValue("drmPF2ReedSolomon",drmPFArray[1].params.reedSolomon);
+
+  qSettings.setValue ("drmPF3Name",drmPFArray[2].name);
+  qSettings.setValue ("drmPF3Mode",drmPFArray[2].params.robMode);
+  qSettings.setValue("drmPF3QAM",drmPFArray[2].params.qam);
+  qSettings.setValue("drmPF3Bandwidth",drmPFArray[2].params.bandwith);
+  qSettings.setValue("drmPF3Protection",drmPFArray[2].params.protection);
+  qSettings.setValue("drmPF3Interleave",drmPFArray[2].params.interleaver);
+  qSettings.setValue("drmPF3ReedSolomon",drmPFArray[2].params.reedSolomon);
+  qSettings.endGroup();
+}
+
+
+void drmProfileForm::getParams()
+{
+  getValue(drmPFArray[0].name,ui->namePF1LineEdit);
+  drmPFArray[0].params.callsign=myCallsign;
+  getIndex(drmPFArray[0].params.robMode,ui->drmPF1ModeComboBox);
+  getIndex(drmPFArray[0].params.qam,ui->drmPF1QAMComboBox);
+  getIndex(drmPFArray[0].params.bandwith,ui->drmPF1BandwidthComboBox);
+  getIndex(drmPFArray[0].params.protection,ui->drmPF1ProtectionComboBox);
+  getIndex(drmPFArray[0].params.interleaver,ui->drmPF1InterleaveComboBox);
+  getIndex(drmPFArray[0].params.reedSolomon,ui->drmPF1ReedSolomonComboBox);
+
+  getValue(drmPFArray[1].name,ui->namePF2LineEdit);
+  drmPFArray[1].params.callsign=myCallsign;
+  getIndex(drmPFArray[1].params.robMode,ui->drmPF2ModeComboBox);
+  getIndex(drmPFArray[1].params.qam,ui->drmPF2QAMComboBox);
+  getIndex(drmPFArray[1].params.bandwith,ui->drmPF2BandwidthComboBox);
+  getIndex(drmPFArray[1].params.protection,ui->drmPF2ProtectionComboBox);
+  getIndex(drmPFArray[1].params.interleaver,ui->drmPF2InterleaveComboBox);
+  getIndex(drmPFArray[1].params.reedSolomon,ui->drmPF2ReedSolomonComboBox);
+
+  getValue(drmPFArray[2].name,ui->namePF3LineEdit);
+  drmPFArray[2].params.callsign=myCallsign;
+  getIndex(drmPFArray[2].params.robMode,ui->drmPF3ModeComboBox);
+  getIndex(drmPFArray[2].params.qam,ui->drmPF3QAMComboBox);
+  getIndex(drmPFArray[2].params.bandwith,ui->drmPF3BandwidthComboBox);
+  getIndex(drmPFArray[2].params.protection,ui->drmPF3ProtectionComboBox);
+  getIndex(drmPFArray[2].params.interleaver,ui->drmPF3InterleaveComboBox);
+  getIndex(drmPFArray[2].params.reedSolomon,ui->drmPF3ReedSolomonComboBox);
+
+}
+
+void  drmProfileForm::setParams()
+{
+  setValue(drmPFArray[0].name,ui->namePF1LineEdit);
+  setIndex(drmPFArray[0].params.robMode,ui->drmPF1ModeComboBox);
+  setIndex(drmPFArray[0].params.qam,ui->drmPF1QAMComboBox);
+  setIndex(drmPFArray[0].params.bandwith,ui->drmPF1BandwidthComboBox);
+  setIndex(drmPFArray[0].params.protection,ui->drmPF1ProtectionComboBox);
+  setIndex(drmPFArray[0].params.interleaver,ui->drmPF1InterleaveComboBox);
+  setIndex(drmPFArray[0].params.reedSolomon,ui->drmPF1ReedSolomonComboBox);
+
+  setValue(drmPFArray[1].name,ui->namePF2LineEdit);
+  setIndex(drmPFArray[1].params.robMode,ui->drmPF2ModeComboBox);
+  setIndex(drmPFArray[1].params.qam,ui->drmPF2QAMComboBox);
+  setIndex(drmPFArray[1].params.bandwith,ui->drmPF2BandwidthComboBox);
+  setIndex(drmPFArray[1].params.protection,ui->drmPF2ProtectionComboBox);
+  setIndex(drmPFArray[1].params.interleaver,ui->drmPF2InterleaveComboBox);
+  setIndex(drmPFArray[1].params.reedSolomon,ui->drmPF2ReedSolomonComboBox);
+
+  setValue(drmPFArray[2].name,ui->namePF3LineEdit);
+  setIndex(drmPFArray[2].params.robMode,ui->drmPF3ModeComboBox);
+  setIndex(drmPFArray[2].params.qam,ui->drmPF3QAMComboBox);
+  setIndex(drmPFArray[2].params.bandwith,ui->drmPF3BandwidthComboBox);
+  setIndex(drmPFArray[2].params.protection,ui->drmPF3ProtectionComboBox);
+  setIndex(drmPFArray[2].params.interleaver,ui->drmPF3InterleaveComboBox);
+  setIndex(drmPFArray[2].params.reedSolomon,ui->drmPF3ReedSolomonComboBox);
+}
+
+
+bool drmProfileForm::getDRMParams(int idx,drmTxParams &d)
+{
+  if((idx<0)||(idx>=NUMBEROFPROFILES))
+  {
+      return false;
+  }
+  d=drmPFArray[idx].params;
+  return true;
+}
+
+bool drmProfileForm::getName(int idx, QString &n)
+{
+
+    if((idx<0)||(idx>=NUMBEROFPROFILES))
+    {
+        return false;
+    }
+    n=drmPFArray[idx].name;
+    return true;
+
+}
diff --git a/qsstv/drmprofileform.h b/qsstv/drmprofileform.h
new file mode 100644
index 0000000..0631e1b
--- /dev/null
+++ b/qsstv/drmprofileform.h
@@ -0,0 +1,40 @@
+#ifndef DRMPROFILEFORM_H
+#define DRMPROFILEFORM_H
+#include "drmtx/drmtransmitter.h"
+
+#include <QWidget>
+
+#define NUMBEROFPROFILES 3
+
+struct sprofile
+{
+    QString name;
+    drmTxParams params;
+
+};
+
+namespace Ui {
+class drmProfileForm;
+}
+
+class drmProfileForm : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit drmProfileForm(QWidget *parent = 0);
+    ~drmProfileForm();
+    void readSettings();
+    void writeSettings();
+    bool getDRMParams(int idx, drmTxParams &d);
+    bool getName(int idx,QString &n);
+    void setParams();
+
+private:
+    Ui::drmProfileForm *ui;
+    sprofile drmPFArray[NUMBEROFPROFILES];
+    void getParams();
+
+};
+
+#endif // DRMPROFILEFORM_H
diff --git a/qsstv/drmprofileform.ui b/qsstv/drmprofileform.ui
new file mode 100644
index 0000000..a197f71
--- /dev/null
+++ b/qsstv/drmprofileform.ui
@@ -0,0 +1,785 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>drmProfileForm</class>
+ <widget class="QWidget" name="drmProfileForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>434</width>
+    <height>330</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="Profile1">
+      <attribute name="title">
+       <string>Profile 1</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_4">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Profile Name</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="namePF1LineEdit"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QLabel" name="drmModeLabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Mode</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QComboBox" name="drmPF1ModeComboBox">
+           <item>
+            <property name="text">
+             <string>A</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>B</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>E</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="0" column="2">
+          <widget class="QLabel" name="drmBandwidthLabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>BW</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QComboBox" name="drmPF1BandwidthComboBox">
+           <item>
+            <property name="text">
+             <string>2.2 KHz</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>2.5 KHz</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="drmQAMLabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>QAM</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QComboBox" name="drmPF1QAMComboBox">
+           <item>
+            <property name="text">
+             <string>4</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>16</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>64</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QLabel" name="drmInterleavelabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Interleave</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="3">
+          <widget class="QComboBox" name="drmPF1InterleaveComboBox">
+           <item>
+            <property name="text">
+             <string>Short</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Long</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="drmProtectionLabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Prot.</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="drmPF1ProtectionComboBox">
+           <item>
+            <property name="text">
+             <string>High</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Low</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="2">
+          <widget class="QLabel" name="drmReadSolomonLabel">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Rs</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="3">
+          <widget class="QComboBox" name="drmPF1ReedSolomonComboBox">
+           <item>
+            <property name="text">
+             <string>None</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS1</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS2</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS3</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS4</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="Profile2">
+      <attribute name="title">
+       <string>Profile 2</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>Profile Name</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="namePF2LineEdit"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout_2">
+         <item row="0" column="0">
+          <widget class="QLabel" name="drmModeLabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Mode</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QComboBox" name="drmPF2ModeComboBox">
+           <item>
+            <property name="text">
+             <string>A</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>B</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>E</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="0" column="2">
+          <widget class="QLabel" name="drmBandwidthLabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>BW</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QComboBox" name="drmPF2BandwidthComboBox">
+           <item>
+            <property name="text">
+             <string>2.2 KHz</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>2.5 KHz</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="drmQAMLabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>QAM</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QComboBox" name="drmPF2QAMComboBox">
+           <item>
+            <property name="text">
+             <string>4</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>16</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>64</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QLabel" name="drmInterleavelabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Interleave</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="3">
+          <widget class="QComboBox" name="drmPF2InterleaveComboBox">
+           <item>
+            <property name="text">
+             <string>Short</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Long</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="drmProtectionLabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Prot.</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="drmPF2ProtectionComboBox">
+           <item>
+            <property name="text">
+             <string>High</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Low</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="2">
+          <widget class="QLabel" name="drmReadSolomonLabel_2">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Rs</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="3">
+          <widget class="QComboBox" name="drmPF2ReedSolomonComboBox">
+           <item>
+            <property name="text">
+             <string>None</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS1</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS2</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS3</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS4</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="Profile3">
+      <attribute name="title">
+       <string>Profile 3</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <item>
+          <widget class="QLabel" name="label_3">
+           <property name="text">
+            <string>Profile Name</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="namePF3LineEdit"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout_3">
+         <item row="0" column="0">
+          <widget class="QLabel" name="drmModeLabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Mode</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QComboBox" name="drmPF3ModeComboBox">
+           <item>
+            <property name="text">
+             <string>A</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>B</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>E</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="0" column="2">
+          <widget class="QLabel" name="drmBandwidthLabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>BW</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3">
+          <widget class="QComboBox" name="drmPF3BandwidthComboBox">
+           <item>
+            <property name="text">
+             <string>2.2 KHz</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>2.5 KHz</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="drmQAMLabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>QAM</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QComboBox" name="drmPF3QAMComboBox">
+           <item>
+            <property name="text">
+             <string>4</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>16</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>64</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <widget class="QLabel" name="drmInterleavelabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Interleave</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="3">
+          <widget class="QComboBox" name="drmPF3InterleaveComboBox">
+           <item>
+            <property name="text">
+             <string>Short</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Long</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="drmProtectionLabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Prot.</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="drmPF3ProtectionComboBox">
+           <item>
+            <property name="text">
+             <string>High</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Low</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="2">
+          <widget class="QLabel" name="drmReadSolomonLabel_3">
+           <property name="minimumSize">
+            <size>
+             <width>30</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="text">
+            <string>Rs</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="3">
+          <widget class="QComboBox" name="drmPF3ReedSolomonComboBox">
+           <item>
+            <property name="text">
+             <string>None</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS1</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS2</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS3</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>RS4</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/drmrx/bits2bytes.cpp b/qsstv/drmrx/bits2bytes.cpp
new file mode 100644
index 0000000..af8c34a
--- /dev/null
+++ b/qsstv/drmrx/bits2bytes.cpp
@@ -0,0 +1,114 @@
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */
+
+/*                                                                            */
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */
+
+/*  Project start: 23.07.2004                                                 */
+
+/*  Last change  : 23.07.2004                                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is free software; you can redistribute it and/or modify      */
+
+/*  it under the terms of the GNU General Public License as published by      */
+
+/*  the Free Software Foundation; either version 2 of the License, or         */
+
+/*  (at your option) any later version.                                       */
+
+/*                                                                            */
+
+/*  This program is distributed in the hope that it will be useful,           */
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+
+/*  GNU General Public License for more details.                              */
+
+/*                                                                            */
+
+/*  You should have received a copy of the GNU General Public License         */
+
+/*  along with this program; if not, write to the Free Software               */
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  bits2bytes.c                                                              */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*  Description:                                                              */
+
+/*  Deinterleaver/Interleaver generation for DRM frames                       */
+
+/*  Usage:                                                                    */
+
+/*                                                                            */
+
+/*  bytes = bits2bytes(bits);                                                 */
+
+/*                                                                            */
+
+/*  converts a serial double bit-stream into a uint8 byte-stream              */
+
+/******************************************************************************/
+
+
+/*
+*   modified for use directly in  C-language without Matlab interface
+*   M.Bos - PA0MBO
+*   Date Nov 15th 2007
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+
+void bits2bytes(double *inbits, int N, unsigned char /*@out@ */ *outbytes)
+{
+  unsigned char single_byte;
+  int m, n;
+
+  if (N % 8 != 0)
+    {
+      return;
+    }
+  for (m = 0; m < N / 8; m++)
+    {
+      single_byte = '\0';
+      for (n = 7; n >= 0; n--)
+	{
+	  single_byte |=
+	    ((fabs(inbits[8 * m + 7 - n]) > DBL_EPSILON) & 0x01) << n;
+	}
+      outbytes[m] = single_byte;
+    }
+  return;
+}
diff --git a/qsstv/drmrx/channeldecode.cpp b/qsstv/drmrx/channeldecode.cpp
new file mode 100644
index 0000000..d785557
--- /dev/null
+++ b/qsstv/drmrx/channeldecode.cpp
@@ -0,0 +1,1140 @@
+
+/*
+*   file channeldecode.c
+*
+*   Author M.Bos - PA0MBO
+*   Date Feb 21st 2009
+*
+*   superframe sync of drm demapping
+*   deinterleaving channel decoding of FAC  and MSC frame
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <math.h>
+#include <malloc.h>
+#include <float.h>
+#include "structtemplates.h"
+#include "drmproto.h"
+#include "drmdefs.h"
+#include <fftw3.h>
+#include "configparams.h"
+
+#define PI (4.0*atan(1.0))
+
+#define CHANNELDECODING 1
+char localDrmCallsign[9]= {0, 0, 0, 0, 0, 0, 0, 0, 0}; //changed joma
+
+#define MSD_ITER 4
+extern int transmission_frame_buffer_data_valid;
+extern int fac_valid;
+extern int robustness_mode;
+extern int spectrum_occupancy;
+extern int symbols_per_frame;
+extern int K_modulo;
+extern int K_dc;
+extern float mean_energy_of_used_cells;
+extern int FAC_cells_k[65];
+extern float transmission_frame_buffer[82980];
+extern float channel_transfer_function_buffer[82980];
+extern int transmission_frame_buffer_wptr;
+extern int lFAC;
+extern int runstate;
+extern struct mplex_desc multiplex_description;
+extern struct audio_info audio_information;
+extern struct appl_info application_information;
+extern struct stream_info stream_information;
+extern struct time_info time_and_date;
+extern int channel_decoded_data_buffer_data_valid;
+extern double channel_decoded_data_buffer[110000];
+extern int audio_data_flag;
+extern int length_decoded_data;
+extern int MSC_Demapper[6][2959];
+
+int lMSC;  // joma
+float MSC_cells_sequence[2 * 2959]; //joma
+bool MSCAvailable;
+int spectrum_occupancy_new;
+int msc_mode_new;
+int interleaver_depth_new;
+bool callsignValid;
+
+char getfacchar(double *);
+
+void channel_decoding(void)
+{
+  static int /*@only@ */ *FAC_Deinterleaver;
+  static double RX[13] = { 1, 3, 1, 4, 1, 4, 3, 2, 8, 3, 4, 7, 8 };
+  static double RY[13] = { 4, 10, 3, 11, 2, 7, 5, 3, 11, 4, 5, 8, 9 };
+  //  static int RYlcmSM16[2] = { 3, 4 };
+  //  static int RYlcmSM64[4] = { 4, 15, 8, 45 };
+  static int RatesSM16[2][2] = { {3, 8}, {5, 10} };
+  static int RatesSM64[4][3] =
+  { {1, 5, 10}, {3, 8, 11}, {5, 10, 12}, {8, 11, 13}
+  };
+  static int frame_index;
+  static int enough_frames;
+  static int frame_count;
+  static int msc_parameters_valid;
+  static int robustness_mode_old;
+  //  static int sdc_mode;
+  static int msc_mode;
+  static int interleaver_depth;
+  //  static float z_SDC[2];
+
+
+  static int MSC_Demapper_symbolwise[6][2959];
+  static double SNR_estimation[2959];
+  static double squared_noise_signal_buffer[121490];
+  static double noise_power_density[461];
+  static int SNR_estimation_valid;
+  //  int audio_service_index;
+  int i, j, k, n;
+  //  int iterations;
+  static int msc_parameters_changed;
+  //  static int sdc_parameters_changed;
+  int symbol_period;
+  int trxfrmbufptr;
+  double received_real[3000], received_imag[3000], snr[3000];
+  float transfer_function_FAC[3000];	/* complex */
+  static double L[6];
+  static double L_FAC[4];
+  static int PL[6];
+  static int PL_FAC[4];
+  static double fac_data[72];
+  double checksum;
+  static double channel_parameters[20];
+  //  static double service_parameters[44];
+  double facblock[72];
+  double temp;
+  static int identity;
+  static int identityCount;
+  static int old_ptr;
+
+  static int MSC_carrier_usage[288];
+  static int cnt_MSC_used_carriers;
+  static int MSC_used_carriers[288];
+  static int min_index_equal_samples;
+  static int max_index_equal_samples;
+  static int /*@only@ */ *Part_Deinterleaver = NULL;
+  static int Deinterleaver[18000];
+  int no_of_streams = 1;
+  static int N_MUX;
+  //  static int rylcm;
+  static int N1, N2;
+  static int ratesA[3], ratesB[3], Lvspp, xin1, xin2;
+  float sum1;
+  float transfer_function_MSC[2 * 2959];	/* complex nrs */
+
+  static int rowdimL, coldimL;
+  double SPPhard[10628];
+  int n_SPPhard;
+  double VSPPhard[100];
+  static double res_iters, calc_variance;
+  static double noise_signal[2 * 2959];
+  static double squared_noise_signal[2959];
+  //  static double calc_weighted_variance;
+  double sum2;
+  double weighted_noise_power_density[288];
+  //  double signal_to_noise_ratio[288];
+  //  static int cnt_npwrpos;
+  //  static int noise_power_positions[288];
+  double samples_resorted[288][15];
+  int posrow, poscolumn, totindex;
+  int VSPPlength, HPPlength;
+  int Tu_list[] = { Tu_A, Tu_B, Tu_C, Tu_D };
+  double part1, part2;
+
+
+  if (runstate == RUN_STATE_POWER_ON)
+
+    {
+      if (FAC_Deinterleaver != NULL)
+        free(FAC_Deinterleaver);
+      FAC_Deinterleaver = deinterleaver(0, 1, 90, 21);
+
+      return;
+    }
+  if (runstate == RUN_STATE_INIT)
+
+    {
+      transmission_frame_buffer_wptr = 0;
+      frame_index = 1;
+      enough_frames = 0;
+      frame_count = 0;
+      msc_parameters_valid = 0;
+      robustness_mode_old = -1;
+      //      sdc_mode = -1;
+      msc_mode = -1;
+      interleaver_depth = -1;
+      //      z_SDC[0] = 0.0;		/* complex */
+      //      z_SDC[1] = 1.0E-12;
+      fac_valid = -1;
+      multiplex_description.HM_length = 0;
+      callsignValid=false;
+      return;
+    }
+  channel_decoded_data_buffer_data_valid = 0;
+
+  if (transmission_frame_buffer_data_valid == 0)
+
+    {
+      MSCAvailable=false;
+      /* clear various datastructures */
+      frame_index = 1;
+      enough_frames = 0;
+      frame_count = 0;
+      msc_parameters_valid = 0;
+      transmission_frame_buffer_wptr = 0;
+      multiplex_description.PL_PartA = -1;
+      multiplex_description.PL_PartB = -1;
+      multiplex_description.HM_length = 0;
+      multiplex_description.PL_HM = -1;
+      audio_information.ID[0] = 0;
+      audio_information.ID[1] = 0;
+      audio_information.ID[2] = 0;
+      audio_information.ID[3] = 0;
+      audio_information.stream_ID[0] = -1;
+      audio_information.stream_ID[1] = -1;
+      audio_information.stream_ID[2] = -1;
+      audio_information.stream_ID[3] = -1;
+      audio_information.audio_coding[0] = 0;
+      audio_information.audio_coding[1] = 0;
+      audio_information.audio_coding[2] = 0;
+      audio_information.audio_coding[3] = 0;
+      audio_information.SBR_flag[0] = 0;
+      audio_information.SBR_flag[1] = 0;
+      audio_information.SBR_flag[2] = 0;
+      audio_information.SBR_flag[3] = 0;
+      audio_information.audio_mode[0] = 0;
+      audio_information.audio_mode[1] = 0;
+      audio_information.audio_mode[2] = 0;
+      audio_information.audio_mode[3] = 0;
+      audio_information.sampling_rate[0] = 0;
+      audio_information.sampling_rate[1] = 0;
+      audio_information.sampling_rate[2] = 0;
+      audio_information.sampling_rate[3] = 0;
+      audio_information.text_flag[0] = 0;
+      audio_information.text_flag[1] = 0;
+      audio_information.text_flag[2] = 0;
+      audio_information.text_flag[3] = 0;
+      audio_information.enhancement_flag[0] = 0;
+      audio_information.enhancement_flag[1] = 0;
+      audio_information.enhancement_flag[2] = 0;
+      audio_information.enhancement_flag[3] = 0;
+      audio_information.coder_field[0] = 0;
+      audio_information.coder_field[1] = 0;
+      audio_information.coder_field[2] = 0;
+      audio_information.coder_field[3] = 0;
+      audio_information.bytes_per_frame[0] = 0;
+      audio_information.bytes_per_frame[1] = 0;
+      audio_information.bytes_per_frame[2] = 0;
+      audio_information.bytes_per_frame[3] = 0;
+      application_information.ID[0] = 0;
+      application_information.ID[1] = 0;
+      application_information.ID[2] = 0;
+      application_information.ID[3] = 0;
+      application_information.stream_ID[0] = -1;
+      application_information.stream_ID[1] = -1;
+      application_information.stream_ID[2] = -1;
+      application_information.stream_ID[3] = -1;
+      application_information.packet_mode[0] = 0;
+      application_information.packet_mode[1] = 0;
+      application_information.packet_mode[2] = 0;
+      application_information.packet_mode[3] = 0;
+      application_information.data_unit_indicator[0] = 0;
+      application_information.data_unit_indicator[1] = 0;
+      application_information.data_unit_indicator[2] = 0;
+      application_information.data_unit_indicator[3] = 0;
+      application_information.packet_ID[0] = 0;
+      application_information.packet_ID[1] = 0;
+      application_information.packet_ID[2] = 0;
+      application_information.packet_ID[3] = 0;
+      application_information.enhancement_flag[0] = 0;
+      application_information.enhancement_flag[1] = 0;
+      application_information.enhancement_flag[2] = 0;
+      application_information.enhancement_flag[3] = 0;
+      application_information.application_domain[0] = 0;
+      application_information.application_domain[1] = 0;
+      application_information.application_domain[2] = 0;
+      application_information.application_domain[3] = 0;
+      application_information.packet_length[0] = 0;
+      application_information.packet_length[1] = 0;
+      application_information.packet_length[2] = 0;
+      application_information.packet_length[3] = 0;
+      application_information.user_application_type[0] = 0;
+      application_information.user_application_type[1] = 0;
+      application_information.user_application_type[2] = 0;
+      application_information.user_application_type[3] = 0;
+      application_information.user_application_identifier[0] = 0;
+      application_information.user_application_identifier[1] = 0;
+      application_information.user_application_identifier[2] = 0;
+      application_information.user_application_identifier[3] = 0;
+      application_information.label[0][0] = '\0';
+      application_information.label[1][0] = '\0';
+      application_information.label[2][0] = '\0';
+      application_information.label[3][0] = '\0';
+      application_information.country[0][0] = '\0';
+      application_information.country[1][0] = '\0';
+      application_information.country[2][0] = '\0';
+      application_information.country[3][0] = '\0';
+      application_information.language_code[0] = 0;
+      application_information.language_code[1] = 0;
+      application_information.language_code[2] = 0;
+      application_information.language_code[3] = 0;
+      application_information.programme_type_code[0] = 0;
+      application_information.programme_type_code[1] = 0;
+      application_information.programme_type_code[2] = 0;
+      application_information.programme_type_code[3] = 0;
+      application_information.bytes_per_frame[0] = 0;;
+      application_information.bytes_per_frame[1] = 0;;
+      application_information.bytes_per_frame[2] = 0;;
+      application_information.bytes_per_frame[3] = 0;;
+      stream_information.number_of_audio_services = 0;
+      stream_information.number_of_data_services = 0;
+      stream_information.number_of_streams = 0;
+      stream_information.number_of_audio_streams = 0;
+      stream_information.number_of_data_streams = 0;
+      time_and_date.day = -1;
+      time_and_date.month = -1;
+      time_and_date.year = -1;
+      time_and_date.hours = -1;
+      time_and_date.minutes = -1;
+      //      sdc_mode = -1;
+      msc_mode = -1;
+      robustness_mode_old = -1;
+      interleaver_depth = -1;
+      for (i = 0; i < 41490; i++)
+        squared_noise_signal_buffer[i] = 0.0;
+      for (i = 0; i < 461; i++)
+        noise_power_density[i] = 0.0;
+      SNR_estimation_valid = 0;
+      fac_valid = -1;
+      //      audio_service_index = 1;
+      return;
+    }
+  symbol_period = Tu_list[robustness_mode];
+  if (CHANNELDECODING == 0)
+
+    {
+      frame_index = (frame_index % 6) + 1;
+
+      /* in matlab check for existence of symbol_period & symbols_per_frame */
+      if ((symbol_period != -1) & (symbols_per_frame != -1))
+
+        {
+          transmission_frame_buffer_wptr =
+              (transmission_frame_buffer_wptr +
+               symbol_period * symbols_per_frame) % (symbol_period *
+                                                     symbols_per_frame * 6);
+
+        }
+      SNR_estimation_valid = 0;
+
+
+      return;
+    }
+  //  iterations = 0;
+  calc_variance = -0.05;
+  msc_parameters_changed = 0;
+
+  if (robustness_mode != robustness_mode_old)
+
+    {
+      if (robustness_mode < 0)
+        return;
+      symbol_period = Tu_list[robustness_mode];
+      lFAC = mkfacmap(robustness_mode, K_dc, K_modulo, FAC_cells_k);
+
+    }
+
+  /* FAC decoding */
+  fac_valid = 1;
+
+  for (i = 0; i < lFAC; i++)
+
+    {
+      trxfrmbufptr =
+          (frame_index - 1) * symbol_period * symbols_per_frame +
+          FAC_cells_k[i];
+      received_real[i] = (double) transmission_frame_buffer[2 * trxfrmbufptr];
+      received_imag[i] =
+          (double) transmission_frame_buffer[2 * trxfrmbufptr + 1];
+      transfer_function_FAC[i * 2] =
+          channel_transfer_function_buffer[2 * trxfrmbufptr];
+      transfer_function_FAC[i * 2 + 1] =
+          channel_transfer_function_buffer[2 * trxfrmbufptr + 1];
+
+      snr[i] =
+          sqrt(transfer_function_FAC[i * 2] * transfer_function_FAC[i * 2] +
+               transfer_function_FAC[i * 2 + 1] * transfer_function_FAC[i * 2 +
+               1]);
+
+    } received_real[9] = 0.0;
+  received_imag[9] = 0.0;
+  L_FAC[0] = 0.0;
+  L_FAC[1] = 48.0;
+  PL_FAC[0] = 0;
+  PL_FAC[1] = 6;
+  (void) msdhardfac(received_real, received_imag, lFAC, snr, 0, L_FAC, 2, 0,
+                    FAC_Deinterleaver, PL_FAC, 4, 0, fac_data);
+
+  for (i = 0; i < 40; i++)
+
+    {
+      facblock[i] = fac_data[i];
+    }
+  for (i = 40; i < 48; i++)
+
+    {
+      facblock[i] = 1.0 - fac_data[i];
+    }
+  for (i = 0; i < 10; i++)
+
+    {
+      channel_parameters[i] = fac_data[i];
+    }
+
+  crc8_c(&checksum, facblock, 48);
+
+  if (fabs(checksum) > DBL_EPSILON)
+
+    {
+      spectrum_occupancy = -1;
+      fac_valid = 0;
+      identityCount=0;
+
+      msc_parameters_valid = 0 ;  /* added pa0mbo 23 nov 2011 */
+      return;
+    }
+  msc_parameters_valid = 1;
+
+  /* frame alignment */
+  temp = 2.0 * channel_parameters[0] + channel_parameters[1];
+  identity = (int) temp % 3;
+
+  if (identity != ((frame_index - 1) % 3))
+
+    {
+      old_ptr = transmission_frame_buffer_wptr;
+      transmission_frame_buffer_wptr =
+          identity * symbol_period * symbols_per_frame;
+      for (i = 0; i < symbol_period * symbols_per_frame; i++)
+
+        {
+          trxfrmbufptr = transmission_frame_buffer_wptr + i;
+          transmission_frame_buffer[2 * trxfrmbufptr] =
+              transmission_frame_buffer[2 * (old_ptr + i)];
+          transmission_frame_buffer[2 * trxfrmbufptr + 1] =
+              transmission_frame_buffer[2 * (old_ptr + i) + 1];
+          channel_transfer_function_buffer[2 * trxfrmbufptr] =
+              channel_transfer_function_buffer[2 * (old_ptr + i)];
+          channel_transfer_function_buffer[2 * trxfrmbufptr + 1] =
+              channel_transfer_function_buffer[2 * (old_ptr + i) + 1];
+        }
+      frame_index = identity + 1;
+    }
+  interleaver_depth_new = (int) channel_parameters[3];
+  msc_mode_new = (int) channel_parameters[4];
+  if (fabs(channel_parameters[6] - 1.0) < DBL_EPSILON)
+
+    {
+      msc_mode_new = msc_mode_new + 2 * (int) channel_parameters[9];
+    }
+  spectrum_occupancy_new = (int) channel_parameters[2];
+  if (spectrum_occupancy_new > 1)
+
+    {
+      spectrum_occupancy = -1;
+      fac_valid = 0;
+      identityCount=0;
+      return;
+    }
+  identityCount++;
+  audio_data_flag = (int) channel_parameters[6];
+  // we need 3 consequetive valid fac's to have a complete call
+
+  /* decoding of text in fac data */
+      localDrmCallsign[3*identity] = getfacchar(&facblock[10]);
+      localDrmCallsign[3*identity+1] = getfacchar(&facblock[17]);
+      localDrmCallsign[3*identity+2] = getfacchar(&facblock[24]);
+      localDrmCallsign[3*identity+3] = '\0';
+      if ((identity == 2) && (identityCount>=3))
+        {
+          identityCount=0;
+          drmCallsign=localDrmCallsign;
+          callsignValid=true;
+        }
+
+
+
+  if ((spectrum_occupancy != spectrum_occupancy_new)
+      || (robustness_mode_old != robustness_mode))
+
+    {
+      spectrum_occupancy = spectrum_occupancy_new;
+      interleaver_depth = interleaver_depth_new;
+
+
+      lMSC = mkmscmap(robustness_mode, spectrum_occupancy, interleaver_depth, K_dc, K_modulo);
+
+      for (j = 0; j < 6; j++)
+
+        {
+
+          for (i = 0; i < lMSC; i++)
+
+            {
+              MSC_Demapper_symbolwise[j][i] =
+                  MSC_Demapper[j][i] % symbol_period;
+
+
+            }
+        }
+
+      /* do the binning and calc carrier usage */
+      for (i = 0; i < symbol_period; i++)
+
+        {
+          MSC_carrier_usage[i] = 0;
+          for (j = 0; j < 6; j++)
+
+            {
+              for (k = 0; k < lMSC; k++)
+
+                {
+                  if (MSC_Demapper_symbolwise[j][k] == i)
+
+                    {
+                      (MSC_carrier_usage[i])++;
+                    }
+                }
+            }
+        }
+      cnt_MSC_used_carriers = 0;
+      for (i = 0; i < symbol_period; i++)
+
+        {
+          if (MSC_carrier_usage[i] != 0)
+
+            {
+              MSC_used_carriers[cnt_MSC_used_carriers++] = i;
+            }
+        }
+      msc_parameters_changed = 1;
+      //      sdc_parameters_changed = 1;
+    }
+
+  else
+
+    {
+      if (interleaver_depth != interleaver_depth_new)
+
+        {
+          interleaver_depth = interleaver_depth_new;
+
+          lMSC = mkmscmap(robustness_mode, spectrum_occupancy, interleaver_depth,K_dc, K_modulo);
+
+
+
+          /* do the binning and calc carrier usage */
+          for (i = 0; i < symbol_period; i++)
+
+            {
+              MSC_carrier_usage[i] = 0;
+              for (j = 0; j < 6; j++)
+
+                {
+                  for (k = 0; k < lMSC; k++)
+
+                    {
+                      if (MSC_Demapper_symbolwise[j][k] == i)
+
+                        {
+                          (MSC_carrier_usage[i])++;
+                        }
+                    }
+                }
+            }
+          cnt_MSC_used_carriers = 0;
+          for (i = 0; i < symbol_period; i++)
+
+            {
+              if (MSC_carrier_usage[i] != 0)
+
+                {
+                  MSC_used_carriers[cnt_MSC_used_carriers++] = i;
+                }
+            }
+          msc_parameters_changed = 1;
+
+        }
+    }
+
+
+  robustness_mode_old = robustness_mode;
+
+  interleaver_depth = interleaver_depth_new;
+
+  /* frame count : deinterleaving possible after 2 received frames
+     for short and after 6 received frames for long interleaving */
+  frame_count++;
+  if (frame_count >= 6 - 4 * interleaver_depth)
+
+    {
+      enough_frames = 1;
+    }
+
+  else
+
+    {
+      if (frame_count == 1)
+
+        {
+          min_index_equal_samples = transmission_frame_buffer_wptr;
+          max_index_equal_samples = transmission_frame_buffer_wptr + symbol_period * symbols_per_frame;
+        }
+
+      else
+
+        {
+          if (transmission_frame_buffer_wptr < min_index_equal_samples)
+
+            {
+              min_index_equal_samples = transmission_frame_buffer_wptr;
+            }
+          if (transmission_frame_buffer_wptr +
+              symbol_period * symbols_per_frame > max_index_equal_samples)
+
+            {
+              max_index_equal_samples =
+                  transmission_frame_buffer_wptr +
+                  symbol_period * symbols_per_frame;
+            }
+        }
+    }
+  if (msc_mode != msc_mode_new)
+
+    {
+      msc_mode = msc_mode_new;
+      msc_parameters_changed = 1;
+      msc_parameters_valid = 1;
+    }
+  multiplex_description.HM_length = 0;
+  multiplex_description.PL_HM = 0;
+  if (fabs(fac_data[5] - 1.0) < DBL_EPSILON)
+
+    {
+      multiplex_description.PL_PartA = 1;
+      multiplex_description.PL_PartB = 1;
+
+    }
+
+  else
+
+    {
+      multiplex_description.PL_PartA = 0;
+      multiplex_description.PL_PartB = 0;
+
+    }
+  application_information.stream_ID[0] = 0;
+  application_information.stream_ID[1] = -1;
+  application_information.stream_ID[2] = -1;
+  application_information.stream_ID[3] = -1;
+  application_information.packet_mode[0] = 1;
+  application_information.packet_mode[1] = 0;
+  application_information.packet_mode[2] = 0;
+  application_information.packet_mode[3] = 0;
+  application_information.data_unit_indicator[0] = 1;
+  application_information.data_unit_indicator[1] = 0;
+  application_information.data_unit_indicator[2] = 0;
+  application_information.data_unit_indicator[3] = 0;
+  application_information.application_domain[0] = 1;
+  application_information.application_domain[1] = 0;
+  application_information.application_domain[2] = 0;
+  application_information.application_domain[3] = 0;
+  for (i = 0; i < 16; i++)
+    application_information.application_data[0][i] = 0;
+
+  /* ***   MSC DECODING *** */
+  N_MUX = lMSC;
+
+  /* MSC parameters settings */
+  if ((msc_parameters_changed == 1) && (msc_parameters_valid == 1))
+
+    {
+      if (msc_mode == 0)	/* 64-QAM SM */
+
+        {
+          //	  rylcm = RYlcmSM64[multiplex_description.PL_PartA];
+          for (i = 0; i < 3; i++)
+
+            {
+              ratesA[i] = RatesSM64[multiplex_description.PL_PartA][i] - 1;
+            }
+          N1 = 0;
+          N2 = N_MUX - N1;
+          for (i = 0; i < 3; i++)
+
+            {
+              ratesB[i] = RatesSM64[multiplex_description.PL_PartB][i] - 1;
+            }
+          for (i = 0; i < 3; i++)
+
+            {
+              L[i] = 2 * N1 * (RX[ratesA[i]] / RY[ratesA[i]]);
+              L[i + 3] =
+                  (RX[ratesB[i]] * floor((2 * N2 - 12) / RY[ratesB[i]]));
+
+            }
+          Lvspp = 0;
+          rowdimL = 3;
+          coldimL = 2;
+          xin1 = 2 * N1;
+          xin2 = 2 * N2;
+
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i] = i;
+          if (Part_Deinterleaver != NULL) free(Part_Deinterleaver);
+          Part_Deinterleaver = deinterleaver(xin1, 13, xin2, 13);
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i + xin1 + xin2] = Part_Deinterleaver[i];
+          free(Part_Deinterleaver);
+          Part_Deinterleaver = deinterleaver(xin1, 21, xin2, 21);
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i + 2 * (xin1 + xin2)] = Part_Deinterleaver[i];
+
+          for (i = 0; i < 3; i++)
+
+            {
+              PL[i] = ratesA[i];
+
+              PL[i + 3] = ratesB[i];
+
+            }
+        }
+
+      else if (msc_mode == 1)	/* 16-QAM SM */
+
+        {
+          //	  rylcm = RYlcmSM16[multiplex_description.PL_PartA];
+          for (i = 0; i < 2; i++) //joma
+
+            {
+              ratesA[i] = RatesSM16[multiplex_description.PL_PartA][i] - 1;
+            }
+          N1 = 0;
+          N2 = N_MUX - N1;
+          for (i = 0; i < 2; i++) //joma
+
+            {
+              ratesB[i] = RatesSM16[multiplex_description.PL_PartB][i] - 1;
+            }
+          for (i = 0; i < 2; i++)
+
+            {
+              L[i] = 2 * N1 * (RX[ratesA[i]] / RY[ratesA[i]]);
+              L[i + 2] =
+                  (RX[ratesB[i]] * floor((2 * N2 - 12) / RY[ratesB[i]]));
+
+            }
+          rowdimL = 2;
+          coldimL = 2;
+          Lvspp = 0;
+          xin1 = 2 * N1;
+          xin2 = 2 * N2;
+          if (Part_Deinterleaver != NULL)
+            free(Part_Deinterleaver);
+          Part_Deinterleaver = deinterleaver(xin1, 13, xin2, 13);
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i] = Part_Deinterleaver[i];
+          free(Part_Deinterleaver);
+          Part_Deinterleaver = deinterleaver(xin1, 21, xin2, 21);
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i + (xin1 + xin2)] = Part_Deinterleaver[i];
+          for (i = 0; i < 2; i++)
+
+            {
+              PL[i] = ratesA[i];
+              PL[i + 2] = ratesB[i];
+            }
+        }
+
+      else if (msc_mode == 3)	/* 4-QAM */
+
+        {
+          //	  rylcm = (int) RY[multiplex_description.PL_PartA];
+          ratesA[0] = (int) RX[multiplex_description.PL_PartA] - 1;
+          N1 = 0;
+          N2 = N_MUX - N1;
+          ratesB[0] = (int) RX[multiplex_description.PL_PartB] - 1;
+          if (robustness_mode == 0)
+
+            {
+              if (spectrum_occupancy == 0)
+
+                {
+                  L[0] = 0;
+                  L[1] = 768;
+                }
+
+              else
+
+                {
+                  L[0] = 0;
+                  L[1] = 837;
+                }
+            }
+          if (robustness_mode == 1)
+
+            {
+              if (spectrum_occupancy == 0)
+
+                {
+                  L[0] = 0;
+                  L[1] = 537;
+                }
+
+              else
+
+                {
+                  L[0] = 0;
+                  L[1] = 627;
+                }
+            }
+          if (robustness_mode == 2)
+
+            {
+              if (spectrum_occupancy == 0)
+
+                {
+                  L[0] = 0;
+                  L[1] = 399;
+                }
+
+              else
+
+                {
+                  L[0] = 0;
+                  L[1] = 435;
+                }
+            }
+          Lvspp = 0;
+          xin1 = 2 * N1;
+          xin2 = 2 * N2;
+          if (Part_Deinterleaver != NULL)
+            free(Part_Deinterleaver);
+          Part_Deinterleaver = deinterleaver(xin1, 21, xin2, 21);
+          for (i = 0; i < xin1 + xin2; i++)
+            Deinterleaver[i] = Part_Deinterleaver[i];
+          PL[0] = 0;
+          PL[1] = 6;
+          rowdimL = 1;
+          coldimL = 2;
+        }
+    }
+
+  if (msc_parameters_valid != 0)
+
+    {
+      for (i = 0; i < lMSC; i++)
+
+        {
+          trxfrmbufptr = MSC_Demapper[frame_index - 1][i];
+          received_real[i] =
+              (double) transmission_frame_buffer[2 * trxfrmbufptr];
+          received_imag[i] =
+              (double) transmission_frame_buffer[2 * trxfrmbufptr + 1];
+
+          MSC_cells_sequence[2 * i] = (float) received_real[i];
+          MSC_cells_sequence[2 * i + 1] = (float) received_imag[i];
+          transfer_function_MSC[i * 2] =
+              channel_transfer_function_buffer[2 * trxfrmbufptr];
+          transfer_function_MSC[i * 2 + 1] =
+              channel_transfer_function_buffer[2 * trxfrmbufptr + 1];
+        }
+
+      if (enough_frames == 0)
+
+        {
+          for (i = 0; i < lMSC; i++)
+
+            {
+              if ((MSC_Demapper[frame_index - 1][i] > max_index_equal_samples)
+                  || (MSC_Demapper[frame_index - 1][i] <
+                      min_index_equal_samples))
+
+                {
+                  transfer_function_MSC[2 * i] = 0.0;
+                  transfer_function_MSC[2 * i + 1] = 0.0;
+                }
+              SNR_estimation[i] =
+                  sqrt(transfer_function_MSC[2 * i] *
+                       transfer_function_MSC[i * 2] +
+                       transfer_function_MSC[i * 2 +
+                       1] * transfer_function_MSC[i * 2 +
+                       1]);
+            }
+
+          n_SPPhard =
+              msdhardmsc(received_real, received_imag, lMSC, SNR_estimation, N1,
+                         L, rowdimL, coldimL, Lvspp, Deinterleaver, PL,
+                         MSD_ITER, 1, SPPhard, VSPPhard, &res_iters,
+                         &calc_variance, noise_signal);
+          channel_decoded_data_buffer_data_valid = 2;
+          length_decoded_data = n_SPPhard;
+        }
+
+      else
+
+        {
+
+          MSCAvailable=true;
+
+
+
+          SNR_estimation_valid = 0;
+          if (SNR_estimation_valid < 1)
+
+            {
+              for (i = 0; i < lMSC; i++)
+
+                {
+                  SNR_estimation[i] =
+                      sqrt(transfer_function_MSC[2 * i] *
+                           transfer_function_MSC[i * 2] +
+                           transfer_function_MSC[i * 2 +
+                           1] * transfer_function_MSC[i *
+                           2 +
+                           1]);
+                }
+            }
+
+          else
+
+            {
+
+
+              for (i = 0; i < 461; i++)
+
+                {
+                  if (fabs(noise_power_density[i] - 0.0) < DBL_EPSILON)
+                    noise_power_density[i] = 1.0;
+
+
+                }
+              for (i = 0; i < lMSC; i++)
+
+                {
+
+
+                  if (noise_power_density
+                      [MSC_Demapper_symbolwise[frame_index - 1][i]] <= 0.0)
+
+                    {
+                      exit(EXIT_FAILURE);
+                    }
+                  part1 =
+                      sqrt(transfer_function_MSC[i * 2] *
+                           transfer_function_MSC[i * 2] +
+                           transfer_function_MSC[i * 2 +
+                           1] * transfer_function_MSC[i *
+                           2 +
+                           1]);
+                  part2 =
+                      sqrt(noise_power_density
+                           [MSC_Demapper_symbolwise[frame_index - 1][i]]);
+                  SNR_estimation[i] = part1 / part2;
+
+
+                }
+            }
+
+
+          n_SPPhard =
+              msdhardmsc(received_real, received_imag, lMSC, SNR_estimation, N1,
+                         L, rowdimL, coldimL, Lvspp, Deinterleaver, PL,
+                         MSD_ITER, 1, SPPhard, VSPPhard, &res_iters,
+                         &calc_variance, noise_signal);
+          length_decoded_data = n_SPPhard;
+          sum1 = 0.0;
+          sum2 = 0.0;
+          for (i = 0; i < lMSC; i++)
+
+            {
+              squared_noise_signal[i] =
+                  noise_signal[2 * i] * noise_signal[2 * i] +
+                  noise_signal[2 * i + 1] * noise_signal[2 * i + 1];
+              sum1 +=
+                  (transfer_function_MSC[2 * i] * transfer_function_MSC[2 * i] +
+                   transfer_function_MSC[2 * i +
+                   1] * transfer_function_MSC[2 * i +
+                   1]) *
+                  squared_noise_signal[i];
+              sum2 +=
+                  (transfer_function_MSC[2 * i] * transfer_function_MSC[2 * i] +
+                   transfer_function_MSC[2 * i +
+                   1] * transfer_function_MSC[2 * i +
+                   1]) *
+                  mean_energy_of_used_cells;
+            }
+
+          //	  calc_weighted_variance = sum1 / sum2;
+          for (i = 0; i < lMSC; i++)
+
+            {
+
+
+              squared_noise_signal_buffer[MSC_Demapper[frame_index - 1][i]] =
+                  squared_noise_signal[i];
+            }
+
+
+          /* now calc the sum of the reshaped "symbol_period" number of columns */
+          for (i = 0; i < symbol_period; i++)
+
+            {
+              weighted_noise_power_density[i] = 0.0;
+              for (j = 0; j < 6 * symbols_per_frame; j++)
+
+                {
+                  weighted_noise_power_density[i] +=
+                      squared_noise_signal_buffer[i + j * symbol_period];
+                }
+            }
+
+          //	  cnt_npwrpos = 0;
+          //    for (i = 0; i < symbol_period; i++)
+          //      {
+          //        if (fabs(weighted_noise_power_density[i]) > DBL_EPSILON)
+
+          //          {
+          //            noise_power_positions[cnt_npwrpos++] = i;
+          //          }
+          //      }
+
+          //	  for (i = 0; i < symbol_period; i++)
+
+          //	    {
+          //	      signal_to_noise_ratio[i] = 0.0;
+          //	    }
+          //	  for (i = 0; i < cnt_npwrpos; i++)
+
+          //	    {
+          //	      signal_to_noise_ratio[noise_power_positions[i]] = MSC_carrier_usage[noise_power_positions[i]] /weighted_noise_power_density[noise_power_positions[i]];
+          //	    }
+
+          for (i = 0; i < symbol_period; i++)	/* rows */
+            for (j = 0; j < symbols_per_frame; j++)	/* columns */
+              samples_resorted[i][j] = 0.0;
+
+          for (i = 0; i < lMSC; i++)
+
+            {
+              totindex =
+                  (MSC_Demapper[frame_index - 1][i]) % (symbol_period *
+                                                        symbols_per_frame);
+              posrow = totindex % symbol_period;
+              poscolumn = totindex / symbol_period;
+              samples_resorted[posrow][poscolumn] = squared_noise_signal[i] *
+                  (transfer_function_MSC[i * 2] * transfer_function_MSC[i * 2] +
+                   transfer_function_MSC[i * 2 +
+                   1] * transfer_function_MSC[i * 2 + 1]);
+            }
+
+
+          for (i = 0; i < cnt_MSC_used_carriers; i++)
+
+            {
+              sum1 = 0.0;
+              for (j = 0; j < symbols_per_frame; j++)
+                sum1 += samples_resorted[MSC_used_carriers[i]][j];
+              noise_power_density[MSC_used_carriers[i]] =
+                  noise_power_density[MSC_used_carriers[i]] * (1.0 - 0.2) +
+                  0.2 * sum1 / MSC_carrier_usage[MSC_used_carriers[i]];
+            }
+          if (SNR_estimation_valid < 1)
+
+            {
+              SNR_estimation_valid++;
+            }
+          channel_decoded_data_buffer_data_valid = 1;
+        }
+      if (Lvspp != 0)
+
+        {
+          VSPPlength =
+              multiplex_description.stream_lengths[0][0] * 8 +
+              multiplex_description.stream_lengths[1][0] * 8;
+          HPPlength = 0;
+          for (i = 0; i < no_of_streams; i++)
+
+            {
+              HPPlength += 8 * multiplex_description.stream_lengths[0][i];
+            }
+
+          n = 0;
+          for (i = 0; i < HPPlength; i++)
+            channel_decoded_data_buffer[n++] = SPPhard[i];
+          for (i = 0; i < VSPPlength; i++)
+            channel_decoded_data_buffer[n++] = VSPPhard[i];
+          for (i = 0; i < n_SPPhard - HPPlength; i++)
+            channel_decoded_data_buffer[n++] = SPPhard[HPPlength + i];
+        }
+
+      else
+
+        {
+          for (i = 0; i < n_SPPhard; i++)
+
+            {
+              channel_decoded_data_buffer[i] = SPPhard[i];
+
+
+            }
+
+
+        }
+    }
+  frame_index = (frame_index % 6) + 1;
+  transmission_frame_buffer_wptr =((transmission_frame_buffer_wptr +symbol_period * symbols_per_frame) % (symbol_period *symbols_per_frame * 6));
+
+  return;
+}
+
+char getfacchar(double *facdata)
+{
+  char karakter;
+  int macht, i;
+
+  macht=64;
+  karakter =0;
+  for (i=0; i < 7 ; i++)
+    {
+      if (facdata[i] == 1.0)
+        {
+          karakter += macht;
+        }
+      macht /= 2;
+    }
+  return(karakter);
+}
+
+
diff --git a/qsstv/drmrx/crc16_bytewise.cpp b/qsstv/drmrx/crc16_bytewise.cpp
new file mode 100644
index 0000000..06e3ff3
--- /dev/null
+++ b/qsstv/drmrx/crc16_bytewise.cpp
@@ -0,0 +1,138 @@
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+
+/*  Copyright (C) 2004 Andreas Dittrich, Torsten Schorr                       */
+
+/*                                                                            */
+
+/*  Author(s)    : Andreas Dittrich (dittrich at eit.uni-kl.de),                 */
+
+/*                 Torsten Schorr (schorr at eit.uni-kl.de)                      */
+
+/*  Project start: 27.07.2004                                                 */
+
+/*  Last change  : 27.07.2004                                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is free software; you can redistribute it and/or modify      */
+
+/*  it under the terms of the GNU General Public License as published by      */
+
+/*  the Free Software Foundation; either version 2 of the License, or         */
+
+/*  (at your option) any later version.                                       */
+
+/*                                                                            */
+
+/*  This program is distributed in the hope that it will be useful,           */
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+
+/*  GNU General Public License for more details.                              */
+
+/*                                                                            */
+
+/*  You should have received a copy of the GNU General Public License         */
+
+/*  along with this program; if not, write to the Free Software               */
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  crc16_bytewise.c                                                          */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*  Description:                                                              */
+
+/*  CRC-16 checksum calculation of a byte stream                              */
+
+/*  Usage:                                                                    */
+
+/*                                                                            */
+
+/*  crc16_bytewise(double *checksum, unsigned char *in, long N);                                         */
+
+/*                                                                            */
+
+/*  calculates double checksum of uint8 bytes                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/*************
+*
+*   adjusted for use in own plain C programa
+*   by M.Bos - PA0MBO
+*
+*   Date Dec 9th 2007
+*/
+
+#include <math.h>
+#include <stdlib.h>
+
+
+/******************************************************************************/
+
+/* function                                                                   */
+
+/******************************************************************************/
+void crc16_bytewise(double /*@out@ */ checksum[],unsigned char in[], long N)
+{
+  long int i;
+  int j;
+  unsigned int b = 0xFFFF;
+  unsigned int x = 0x1021;	/* (1) 0001000000100001 */
+  unsigned int y;
+
+  for (i = 0; i < N - 2; i++)
+
+    {
+      for (j = 7; j >= 0; j--)
+	{
+	  y = (((b >> 15) + (unsigned int) (in[i] >> j)) & 0x01) & 0x01;	/* extra parenth pa0mbo */
+	  if (y == 1)
+	    b = ((b << 1) ^ x);
+
+	  else
+	    b = (b << 1);
+	}
+    }
+  for (i = N - 2; i < N; i++)
+
+    {
+      for (j = 7; j >= 0; j--)
+	{
+	  y = (((b >> 15) + (unsigned int) ((in[i] >> j) & 0x01)) ^ 0x01) & 0x01;	/* extra parent pa0mbo */
+	  if (y == 1)
+	    b = ((b << 1) ^ x);
+
+	  else
+	    b = (b << 1);
+	}
+    }
+  *checksum = (double) (b & 0xFFFF);
+}
diff --git a/qsstv/drmrx/crc8_c.cpp b/qsstv/drmrx/crc8_c.cpp
new file mode 100644
index 0000000..aba102c
--- /dev/null
+++ b/qsstv/drmrx/crc8_c.cpp
@@ -0,0 +1,31 @@
+
+/*
+*   File crc8_c.c
+*
+*   from diorama-1.1.1 by A. Dittrich & T. Schorr
+*
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+void crc8_c( /*@out@ */ double checksum[], double in[], int N)
+{
+  int i;
+  unsigned int b = 0xFF;
+  unsigned int x = 0x1D;	/* (1) 00011101 */
+  unsigned int y;
+
+  for (i = 0; i < N; i++)
+
+    {
+      y = ((b >> 7) + (int) floor(in[i] + 0.5)) & 0x01;
+      if (y == 1)
+	b = ((b << 1) ^ x);
+
+      else
+	b = (b << 1);
+    }
+  *checksum = (double) (b & 0xFF);
+}
diff --git a/qsstv/drmrx/deflate_uncompress.cpp b/qsstv/drmrx/deflate_uncompress.cpp
new file mode 100644
index 0000000..6188f23
--- /dev/null
+++ b/qsstv/drmrx/deflate_uncompress.cpp
@@ -0,0 +1,294 @@
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */
+
+/*                                                                            */
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */
+
+/*  Project start: 15.07.2004                                                 */
+
+/*  Last change  : 03.08.2004                                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is free software; you can redistribute it and/or modify      */
+
+/*  it under the terms of the GNU General Public License as published by      */
+
+/*  the Free Software Foundation; either version 2 of the License, or         */
+
+/*  (at your option) any later version.                                       */
+
+/*                                                                            */
+
+/*  This program is distributed in the hope that it will be useful,           */
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+
+/*  GNU General Public License for more details.                              */
+
+/*                                                                            */
+
+/*  You should have received a copy of the GNU General Public License         */
+
+/*  along with this program; if not, write to the Free Software               */
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  deflate_uncompress.c                                                      */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*  Description:                                                              */
+
+/*  Decompressor for data in gzip file format (RFC 1952)                      */
+
+/*  compressed with the DEFLATE compressed data format                        */
+
+/*  (RFC 1951). Used for Broadcast Web-Site application                       */
+
+/*  ETSI TS 101 498                                                           */
+
+/*                                                                            */
+
+/*  Usage:                                                                    */
+
+/*                                                                            */
+
+/*  [data, origname, zerror] = deflate_uncompress(input);                     */
+
+/*                                                                            */
+
+/*  uncompresses uint8 input bytes into uint8 data with original filename     */
+
+/*  origname. zerror: error during decompression                              */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is based on the 'zlib' general purpose compression library   */
+
+/*  version 1.1.3, July 9th, 1998 Jean-loup Gailly and Mark Adler             */
+
+/*                                                                            */
+
+/*                                                                            */
+
+/*  The data format used by the zlib library is described by RFCs (Request for*/
+
+/*  Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt */
+
+/*  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).*/
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/* code adjusted for use outside MATLAB
+*
+*  by M.Bos - PA0MBO
+*  
+*  date Jan 18th 2008
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#define Nargs_rhs_str "1"
+#define Nargs_rhs 1
+#define PROGNAME "deflate_uncompress"
+static int gz_magic[2] = { 0x1f, 0x8b };	/* gzip magic header */
+
+
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01	/* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02	/* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04	/* bit 2 set: extra field present */
+#define ORIG_NAME    0x08	/* bit 3 set: original file name present */
+#define COMMENT      0x10	/* bit 4 set: file comment present */
+#define RESERVED     0xE0	/* bits 5..7: reserved */
+int
+deflate_uncompress(unsigned char *input, int l_in, char /*@out@ */ *origname,
+           int , unsigned char /*@out@ */ *data, int *,
+		   double /*@out@ */ *lhs2)
+{
+  unsigned char *compressed_data, *uncompressed_data;
+  int N, zerror;
+//  int dims[2];
+  int expected_size, len, method, flags, startpos, i;
+
+  uLong crc;
+  z_stream stream, *streamp = &stream;
+
+  /* in */
+  N = l_in;
+  compressed_data = input;
+  if (N < 10)
+    {
+      printf("not enough input data\n");
+      exit(EXIT_FAILURE);
+    }
+  streamp->zalloc = (alloc_func) NULL;
+  streamp->zfree = (free_func) NULL;
+  streamp->opaque = (voidpf) 0;
+  streamp->next_out = Z_NULL;
+  streamp->avail_in = 0;
+  streamp->next_in = compressed_data;
+  zerror = inflateInit2(streamp, -MAX_WBITS);
+
+  /* windowBits is passed < 0 to tell that there is no zlib header.
+   * Note that in this case inflate *requires* an extra "dummy" byte
+   * after the compressed stream in order to complete decompression and
+   * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+   * present after the compressed stream.
+   */
+  if (zerror != Z_OK)
+    {
+      inflateEnd(streamp);
+      printf("Error using inflateInit2\n");
+      exit(1);
+    }
+
+  /* Check the gzip magic header */
+  for (len = 0; len < 2; len++)
+    {
+      if (compressed_data[len] != gz_magic[len])
+	{
+	  printf("No gzip format\n");
+	  exit(1);
+	}
+    }
+  method = compressed_data[2];
+  flags = compressed_data[3];
+  if (method != Z_DEFLATED || (flags & RESERVED) != 0)
+    {
+      inflateEnd(streamp);
+      printf("No deflate format or reserved flags!\n");
+      exit(1);
+    }
+
+  /* Discard time, xflags and OS code: */
+  startpos = 10;
+  if ((flags & EXTRA_FIELD) != 0)
+    {				/* skip the extra field */
+      len = compressed_data[startpos] + (compressed_data[startpos + 1] << 8);
+      startpos += len + 2;
+    }
+  if ((flags & ORIG_NAME) != 0)
+
+    {				/* skip the original file name */
+      i = 0;
+
+      {
+	origname[i] = compressed_data[startpos];
+      }
+      while ((compressed_data[startpos++] != 0) && (startpos < N));
+    }
+  else
+
+    {
+      origname[0] = '\0';
+    }
+  if ((flags & COMMENT) != 0)
+    {				/* skip the .gz file comment */
+      while ((compressed_data[startpos++] != 0) && (startpos < N));
+    }
+  if ((flags & HEAD_CRC) != 0)
+    {				/* skip the header crc */
+      startpos += 2;
+    }
+  if (startpos > N - 8)
+    {
+      inflateEnd(streamp);
+      printf("not enough input data\n");
+      exit(1);
+    }
+  expected_size =
+    ((int) compressed_data[N - 1] << 24) +
+    ((int) compressed_data[N - 2] << 16) +
+    ((int) compressed_data[N - 3] << 8) + ((int) compressed_data[N - 4]);
+  if (expected_size > (0x01 << 30))
+    {
+      inflateEnd(streamp);
+      printf
+	("error in expected file size or file too large (support for 1GB files)\n");
+      exit(1);
+    }
+  uncompressed_data =
+    (unsigned char *) malloc(expected_size * sizeof(unsigned char));
+  if (uncompressed_data == NULL)
+    {
+      inflateEnd(streamp);
+      printf("error allocating memory\n");
+      exit(1);
+    }
+
+  /* prepare z_stream data structure */
+  streamp->avail_in = N - startpos;
+  streamp->next_in = compressed_data + startpos;
+  streamp->avail_out = expected_size;
+  streamp->next_out = uncompressed_data;
+
+  /* uncompress data */
+  zerror = inflate(streamp, Z_FINISH);
+  *lhs2 = 0.0;
+  if (zerror == Z_STREAM_END)
+    {
+
+      /* check crc over uncompressed data: */
+      crc = crc32(0L, Z_NULL, 0);
+      crc =
+	crc32(crc, uncompressed_data,
+	      (uInt) (streamp->next_out - uncompressed_data));
+      if ((((int) compressed_data[N - 5] << 24) +
+	   ((int) compressed_data[N - 6] << 16) +
+	   ((int) compressed_data[N - 7] << 8) +
+     ((uLong) compressed_data[N - 8]) != crc) | (expected_size != streamp->next_out - uncompressed_data))
+	{
+	  *lhs2 = -1.0;
+	}
+//      dims[0] = 1;
+//      dims[1] = streamp->next_out - uncompressed_data;
+      memcpy(data, uncompressed_data,
+	     (streamp->next_out - uncompressed_data) * sizeof(unsigned char));
+    }
+  else
+    {
+      *data = (double) zerror;
+    } inflateEnd(streamp);
+  free(uncompressed_data);
+  return zerror;
+}
diff --git a/qsstv/drmrx/deinterleaver.cpp b/qsstv/drmrx/deinterleaver.cpp
new file mode 100644
index 0000000..8e8d122
--- /dev/null
+++ b/qsstv/drmrx/deinterleaver.cpp
@@ -0,0 +1,127 @@
+
+/*
+*    File deinterleaver.c
+*
+*    follows deinterleaver.m by
+*    Torsten Schorr
+*
+*    Author M.Bos - PA0MBO
+*    Date Feb 21st 2009
+*
+*
+*    N.B. separate routines for different return arguments
+*/
+
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <math.h>
+int *deinterleaver(int xinA, int tA, int xinB, int tB)
+{
+  int *deinterl;
+  int i;
+  int sA, sB, qA, qB;
+  int PIofi;
+  double part1, part2;
+  double part3;
+  deinterl = (int *) malloc((xinA + xinB) * sizeof(int));
+  if (deinterl == NULL)
+
+    {
+      printf("Cannot malloc space for deinterl in routine deinterleaver\n");
+      exit(EXIT_FAILURE);
+    }
+  if (xinA < 0)
+
+    {
+      printf("deinterleaver: xinA must be > = 0!\n");
+      free(deinterl);
+      exit(EXIT_FAILURE);
+    }
+  if (tA < 1)
+
+    {
+      printf("deinterleaver: tA must be a natural number!\n");
+      free(deinterl);
+      exit(EXIT_FAILURE);
+    }
+  if (xinB < 6)
+
+    {
+      printf("deinterleaver: xinB must be >= 6!\n");
+      free(deinterl);
+      exit(EXIT_FAILURE);
+    }
+  if (tB < 1)
+
+    {
+      printf("deinterleaver: tB must be a natural number\n");
+      free(deinterl);
+      exit(EXIT_FAILURE);
+    }
+  if (xinA == 0)
+    sA = 0;
+
+  else
+
+    {
+      part1 = log((double) xinA);
+      part2 = log(2.0);
+      part3 = ceil(part1 / part2);
+      sA = (int) pow(2, part3);
+    } qA = sA / 4 - 1;
+  if (xinB == 0)
+    sB = 0;
+
+  else
+
+    {
+      part1 = log((double) xinB);
+      part2 = log(2.0);
+      part3 = ceil(part1 / part2);
+      sB = (int) pow(2, part3);
+    } qB = sB / 4 - 1;
+  deinterl[0] = 0;
+  PIofi = 0;
+  for (i = 1; i <= xinA - 1; i++)
+
+    {
+      PIofi = (tA * PIofi + qA) % sA;
+      while (PIofi >= xinA)
+
+	{
+	  PIofi = (tA * PIofi + qA) % sA;
+	}
+      deinterl[PIofi] = i;
+    }
+  deinterl[xinA] = xinA;
+  PIofi = 0;
+  for (i = 1; i <= xinB - 1; i++)
+
+    {
+      PIofi = (tB * PIofi + qB) % sB;
+      while (PIofi >= xinB)
+
+	{
+	  PIofi = (tB * PIofi + qB) % sB;
+	}
+      deinterl[PIofi + xinA] = i + xinA;
+    }
+  return (deinterl);
+}
diff --git a/qsstv/drmrx/demodulator.cpp b/qsstv/drmrx/demodulator.cpp
new file mode 100644
index 0000000..d12f4c0
--- /dev/null
+++ b/qsstv/drmrx/demodulator.cpp
@@ -0,0 +1,1122 @@
+#include "demodulator.h"
+#include "qsstvglobal.h"
+#include "drm.h"
+#include <math.h>
+#include <float.h>
+#include "nrutil.h"
+#include "utils/supportfunctions.h"
+
+
+
+demodulator *demodulatorPtr;
+
+void ludcmp(float **, int, int *, float *);
+void lubksb(float **, int, int *, float *);
+
+int Ts_list[DRMNUMMODES] = { Ts_A, Ts_B, Ts_C, Ts_D };
+int Tu_list[DRMNUMMODES] = { Tu_A, Tu_B, Tu_C, Tu_D };
+int Tg_list[DRMNUMMODES] = { Tg_A, Tg_B, Tg_C, Tg_D };
+
+float FAC_cells_sequence[200];
+bool  FACAvailable;
+float deltaFS;
+float freqOffset;
+float tempbuf[30000];
+
+demodulator::demodulator()
+{
+  srcDecoder.init();
+}
+
+demodulator::~demodulator()
+{
+  fftwf_destroy_plan(p1);
+}
+
+
+void demodulator::init()
+{
+  int i,j,k;
+  FACAvailable=false;
+  N_symbols_mode_detection = 20;
+  N_symbols_frequency_pilot_search = 15;
+  time_offset_log_last = 0;
+  symbol_counter = 0;
+  N_samples_needed = N_symbols_mode_detection * 320;
+  input_samples_buffer_request = N_samples_needed;
+  SNR_time_out_counter = SNR_TIMEOUT;
+  fac_not_valid_counter = FACVALIDCNTR;
+  mode_and_occupancy_code_last = -1;
+  timeSyncFlag = false;
+  frequencySyncFlag = false;
+  frameSyncFlag = false;
+  doSynchronize=false;
+  rsbufwidx = 0;
+  symbufwidx = 0;
+  iterationCounter=0;
+
+  samplerate_offset_estimation = 0;
+  samplerate_offset=0;
+  smp_rate_conv_fft_phase_diff = 0;
+  smp_rate_conv_fft_phase_offset = 0;
+  smp_rate_conv_in_out_delay = 0;
+
+
+  transmission_frame_buffer_data_valid = 0;
+  p1 = fftwf_plan_dft_1d(256,(fftwf_complex *)ss,(fftwf_complex *)S, FFTW_FORWARD, FFTW_PATIENT);
+  counter=0;
+  k = 0;
+  for (i = 0; i < 4; i++)	// number of robustness modes
+    {
+      for (j = 0; j < 6; j++)	/* number of spectrumoccupancies */
+        {
+          no_of_used_cells_per_frame_list[k++] = (K_min_K_max_list[1][j + i * 6] - K_min_K_max_list[0][j + i * 6] + 1 - no_of_unused_carriers_list[i]) * symbols_per_frame_list[i];
+        }
+    }
+  sigmaq_noise_list[0] = (float) pow(10.0, -16.0 / 10.0);
+  sigmaq_noise_list[1] = (float) pow(10.0, -14.0 / 10.0);
+  sigmaq_noise_list[2] = (float) pow(10.0, -14.0 / 10.0);
+  sigmaq_noise_list[3] = (float) pow(10.0, -12.0 / 10.0);
+  SNR_timeout_counter=0;
+  delta_freq_offset=0;
+}
+
+bool demodulator::demodulate(float *sigin,int numSamples)
+{
+  int i;
+  numberOfSamples=numSamples;
+  transmission_frame_buffer_data_valid = 0;
+  // fac_valid can be -1,0,1
+
+  addToLog(QString("block %1 samples %2" ).arg(iterationCounter).arg(numSamples),LOGDRMDEMOD);
+//  if(iterationCounter<40) arrayDump(QString("DEM %1").arg(iterationCounter),sigin,128,true);
+//  arrayDump(QString("bl%1").arg(iterationCounter),sigin,32,true);
+//  logfile->addToAux(QString("block %1").arg(iterationCounter));
+//  arrayDump(QString("rs"),sigin,numSamples*2,true);
+//  if (fac_valid == 0)
+//    {
+//      fac_not_valid_counter--;
+//      if (fac_not_valid_counter <= 0)
+//        {
+//          doSynchronize = true;
+//          fac_not_valid_counter = FACVALIDCNTR;
+//        }
+//    }
+//  else
+//    {
+//      if (fac_valid == 1)
+//        {
+//          fac_not_valid_counter = FACVALIDCNTR;
+//        }
+//    }
+  if (doSynchronize)
+    {
+      doSynchronize = false;
+      init();
+    }
+  if (numberOfSamples> 0)
+    {
+      ++iterationCounter;
+//      logfile->addToAux(QString("iterationCounter %1").arg(iterationCounter));
+//      arrayDump("sig1",sigin,RXSTRIPE,true);
+//      logfile->addToAux(QString("rsbufwidx %1 offset %2").arg(rsbufwidx).arg(320 * N_symbols_mode_detection/2));
+
+//      arrayDump("sigD",&rs_buffer[320 * N_symbols_mode_detection],16,true);
+      for(i=0;i<numberOfSamples;i++)
+        {
+          rs_buffer[(i + rsbufwidx)*2] = sigin[i*2];
+          rs_buffer[(i + rsbufwidx) * 2 + 1] = sigin[i*2+1];
+        }
+      rsbufwidx += numberOfSamples;	/* index of next complex number to fill rs_buffer */
+      int block=rsbufwidx/256;
+      if(block>20)
+        {
+          for(i=0;i<(256*block);i++)tempbuf[i]=rs_buffer[2*i];
+          psdmean(tempbuf, psd, 256, block);	/* globals   pa0mbo */
+          for(i=0;i<(256*block);i++)tempbuf[i]=rs_buffer[2*i+1];
+          psdmean(tempbuf, cpsd, 256, block);	/* globals   pa0mbo */
+        }
+
+    }
+ if(!timeSync()) return false;
+ if(!frequencySync()) return false;
+ if(!frameSync()) return false;
+ if(channelEstimation())
+   {
+     channel_decoding();
+     srcDecoder.decode();
+   }
+ if(doSynchronize)
+   {
+     // see if we have to save the data (for BSR and endOfImage)
+//     srcDecoder.checkSaveImage();
+   }
+
+ return true;
+}
+
+bool demodulator::timeSync()
+{
+  int i;
+  samplerate_offset= samplerate_offset_estimation;
+  if (!timeSyncFlag)
+    {
+      frequencySyncFlag=false;
+      // enough data for time sync ?
+      N_samples_needed = N_symbols_mode_detection * 320 - rsbufwidx;
+      if (N_samples_needed > 0)
+        {
+          input_samples_buffer_request = N_samples_needed;
+          return false;
+        }
+      spectrum_occupancy = -1;	/* -1 denotes unknown */
+      getmode(rs_buffer, N_symbols_mode_detection * 320, &mode_block);
+
+      robustness_mode = mode_block.mode_indx;
+      time_offset = mode_block.time_offset;
+      samplerate_offset_estimation = mode_block.sample_rate_offset;
+      frequency_offset_fractional_init = mode_block.freq_offset_fract;
+      time_offset_integer = (int) floor(time_offset + 0.5);
+      if(robustness_mode!=99)
+        {
+      addToLog(QString("numSamples %1, robustmode %2,timeoffset %3,smplrateOffsetEst %4,freqOffsetFracInit %5,timeOffsetInteger %6")
+               .arg(numberOfSamples).arg( robustness_mode).arg(time_offset).arg(samplerate_offset_estimation).arg(frequency_offset_fractional_init).arg(time_offset_integer),LOGDRMDEMOD);
+        }
+
+      if ((fabsf(samplerate_offset_estimation) > 200.0E-5) && (robustness_mode != 99))
+        {
+          // sample_rate offset too large
+          N_samples_needed = N_symbols_mode_detection * 320;
+          input_samples_buffer_request = N_samples_needed;
+          rsbufwidx = 0;
+//          samplerate_offset_estimation=0;
+          return false;
+        }
+      if (robustness_mode != 99)
+        {
+//          logfile->addToAux("timesync found");
+          addToLog(QString("found robustness_mode: %1").arg(robustness_mode),LOGDRMDEMOD);
+          timeSyncFlag = true;
+          Ts = Ts_list[robustness_mode];
+          Tu = Tu_list[robustness_mode];
+          Tg = Tg_list[robustness_mode];
+          Tgh = (int) floor(Tg / 2 + 0.5);
+          symbols_per_frame = symbols_per_frame_list[robustness_mode];
+          K_dc = Tu / 2;
+          K_modulo = Tu;
+          for (i = 0; i < 21; i++) time_ref_cells_k[i] = time_ref_cells_k_list[robustness_mode][i];
+          for (i = 0; i < 21; i++) time_ref_cells_theta_1024[i] = time_ref_cells_theta_1024_list[robustness_mode][i];
+          y = y_list[robustness_mode];
+          symbols_per_2D_window = symbols_per_2D_window_list[robustness_mode];
+          symbols_to_delay = symbols_to_delay_list[robustness_mode];
+
+          //  symbol align rs_buffer
+          rsbufwidx = rsbufwidx - time_offset_integer;
+          for (i = 0; i < rsbufwidx; i++)
+            {
+              rs_buffer[i * 2] = rs_buffer[(i + time_offset_integer) * 2];
+              rs_buffer[i * 2 + 1] =rs_buffer[(i + time_offset_integer) * 2 + 1];
+            }
+          counter++;
+          (void) getofdm(NULL, 0.0, 0.0, 0.0, Ts, Tu, NULL, NULL, 1, 1, 1);	/* initialisation */
+        }
+
+      else
+        {
+          samplerate_offset_estimation = 0.0;
+          int shift=320 * N_symbols_mode_detection;
+//          int shift=512;
+          memmove(rs_buffer,&rs_buffer[shift*2],sizeof(float)*2*(rsbufwidx-shift));
+          rsbufwidx -= shift;
+//          arrayDump("sig2",rs_buffer,16,true);
+//          for (i = 0; i < rsbufwidx; i++)	/* pa0mbo was rsbufwidx-1 ? */
+//            {
+//              rs_buffer[i * 2] = rs_buffer[(shift + i) * 2];
+//              rs_buffer[i * 2 + 1] = rs_buffer[(shift + i) * 2 + 1];
+//            }
+          N_samples_needed = N_symbols_mode_detection * 320 - rsbufwidx;
+          if (N_samples_needed > 0)
+            {
+              input_samples_buffer_request = N_samples_needed;
+            }
+          else
+            {
+              input_samples_buffer_request = 0;
+            }
+          return false;
+        }
+      addToLog(QString("timeSync found robustness mode:%1").arg(robustness_mode),LOGDRMDEMOD);
+    }
+  return true;
+}
+
+
+
+bool demodulator::frequencySync()
+{
+  int i,j;
+  int sp_idx, K_min_, K_max_, K_dc_indx, K_dc_plus2_indx;
+  int K_min_indx, K_min_minus4_indx, K_max_indx, K_max_plus1_indx;
+  float tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
+  //  float energy_ratio_K2_to_K0;
+  float energy_ratio_K_max_to_K_max_p1;
+  float energy_ratio_K_min_to_K_min_m4;
+  float spectrum_occupancy_indicator[6];
+  int t_smp;
+  struct drmComplex S_buffer[288][20];	/* pa0mbo check */
+
+
+  if (!frequencySyncFlag)
+    {
+      frameSyncFlag=false;
+      //  enough f=date for pilot search?
+      N_samples_needed = (N_symbols_frequency_pilot_search + 1) * Ts - (rsbufwidx);
+      if (N_samples_needed > 0)
+        {
+          input_samples_buffer_request = N_samples_needed;
+          return false;
+        }
+      Zi[0] = -1.0;
+      delta_time_offset_integer = 0;
+      freq_offset_init = frequency_offset_fractional_init;
+      time_offset_fractional_init = time_offset - time_offset_integer;
+      delta_time_offset_I_init = samplerate_offset_estimation * Ts;
+
+      t_smp = 0;
+      for (i = 0; i < N_symbols_frequency_pilot_search; i++)
+        {
+          delta_time_offset_integer = getofdm(&rs_buffer[2 * t_smp], time_offset_fractional_init,freq_offset_init, delta_time_offset_I_init, Ts, Tu, Zi,
+                                              symbol_temp, 0, 1,1);
+          for (j = 0; j < K_modulo; j++)
+            {
+              symbol_buffer[(i * K_modulo + j) * 2] = symbol_temp[j * 2];
+              symbol_buffer[(i * K_modulo + j) * 2 + 1] = symbol_temp[j * 2 + 1];
+            }
+          t_smp = t_smp + Ts + delta_time_offset_integer;
+         }
+
+      freq_offset_integer = getfoffsint(symbol_buffer, N_symbols_frequency_pilot_search, K_dc,K_modulo, Tu);
+      //  prepare for new round
+      delta_time_offset_integer = 0;
+      freq_offset_init = -freq_offset_integer + frequency_offset_fractional_init;
+      time_offset_fractional_init = 0.0;
+      Zi[0] = -1.0;
+
+      // detmn frequency occupancy
+      // start with reshaping symbol_buffer to S_buffer
+      for (i = 0; i < N_symbols_frequency_pilot_search; i++)
+        {
+          for (j = 0; j < K_modulo; j++)
+            {
+              (S_buffer[j][i]).re = symbol_buffer[(j + i * K_modulo) * 2];
+              (S_buffer[j][i]).im = symbol_buffer[(j + i * K_modulo) * 2 + 1];
+
+            }
+        }
+
+      /*
+       now per freq occupancy mode
+     */
+      for (sp_idx = 0; sp_idx < 2; sp_idx++)
+
+        {
+          K_min_ = K_min_K_max_list[0][sp_idx + robustness_mode * 6];
+          K_max_ = K_min_K_max_list[1][sp_idx + robustness_mode * 6];
+
+          if (K_min_ != K_max_)
+
+            {
+              K_dc_indx = (int) floor(freq_offset_integer / (2.0 * M_PI) + 0.5) + Tu / 2;
+
+              K_dc_indx = K_dc_indx % Tu;
+              K_dc_plus2_indx = (K_dc_indx + 2 + Tu) % Tu;
+              K_min_indx = (K_dc_indx + K_min_ + Tu) % Tu;
+              K_min_minus4_indx = (K_min_indx - 4 + Tu) % Tu;
+              K_max_indx = (K_dc_indx + K_max_ + Tu) % Tu;
+              K_max_plus1_indx = (K_max_indx + 1 + Tu) % Tu;
+
+              //   calc energy ratios
+              tmp1 = 0.0;
+              tmp2 = 0.0;
+              tmp3 = 0.0;
+              tmp4 = 0.0;
+              tmp5 = 0.0;
+              tmp6 = 0.0;
+              for (i = 0; i < N_symbols_frequency_pilot_search; i++)
+
+                {
+                  tmp1 +=
+                      (S_buffer[K_dc_plus2_indx][i]).re *
+                      (S_buffer[K_dc_plus2_indx][i]).re +
+                      (S_buffer[K_dc_plus2_indx][i]).im *
+                      (S_buffer[K_dc_plus2_indx][i]).im;
+                  tmp2 +=
+                      (S_buffer[K_dc_indx][i]).re *
+                      (S_buffer[K_dc_indx][i]).re +
+                      (S_buffer[K_dc_indx][i]).im * (S_buffer[K_dc_indx][i]).im;
+                  tmp3 +=
+                      (S_buffer[K_max_indx][i]).re *
+                      (S_buffer[K_max_indx][i]).re +
+                      (S_buffer[K_max_indx][i]).im *
+                      (S_buffer[K_max_indx][i]).im;
+                  tmp4 +=
+                      (S_buffer[K_max_plus1_indx][i]).re *
+                      (S_buffer[K_max_plus1_indx][i]).re +
+                      (S_buffer[K_max_plus1_indx][i]).im *
+                      (S_buffer[K_max_plus1_indx][i]).im;
+                  tmp5 +=
+                      (S_buffer[K_min_indx][i]).re *
+                      (S_buffer[K_min_indx][i]).re +
+                      (S_buffer[K_min_indx][i]).im *
+                      (S_buffer[K_min_indx][i]).im;
+                  tmp6 +=
+                      (S_buffer[K_min_minus4_indx][i]).re *
+                      (S_buffer[K_min_minus4_indx][i]).re +
+                      (S_buffer[K_min_minus4_indx][i]).im *
+                      (S_buffer[K_min_minus4_indx][i]).im;
+                }
+              //              energy_ratio_K2_to_K0 = tmp1 / tmp2;
+              energy_ratio_K_max_to_K_max_p1 = tmp3 / tmp4;
+              energy_ratio_K_min_to_K_min_m4 = tmp5 / tmp6;
+              spectrum_occupancy_indicator[sp_idx] =
+                  energy_ratio_K_min_to_K_min_m4 +
+                  energy_ratio_K_max_to_K_max_p1;
+            }
+
+          else  spectrum_occupancy_indicator[sp_idx] = 0.0;
+        }
+
+      // detmn max in spectrum_occupancy_indicator and its index
+
+      tmp1 = 0.0;
+      for (sp_idx = 0; sp_idx < 2; sp_idx++)
+        {
+          if (spectrum_occupancy_indicator[sp_idx] > tmp1)
+
+            {
+              tmp1 = spectrum_occupancy_indicator[sp_idx];
+              spectrum_occupancy_estimation = sp_idx;
+            }
+        }
+      frequencySyncFlag = true;
+      addToLog(QString("spectrum occupancy estimation:%1").arg(spectrum_occupancy_estimation),LOGDRMDEMOD);
+    }
+  return true;
+}
+
+bool demodulator::frameSync()
+{
+ int i,j,k;
+  int symbol0;
+
+  int symbol_no_to_equalize;
+  int t_smp;
+  if (!frameSyncFlag)
+    {
+      initChannelEstimation=true;
+      // enough data ?
+      N_symbols_needed = symbols_per_frame + symbols_per_2D_window - 1;
+      N_samples_needed = (N_symbols_needed + 1) * Ts - (rsbufwidx);
+      if (N_samples_needed > 0)
+        {
+          input_samples_buffer_request = N_samples_needed;
+          return false;
+        }
+
+      t_smp = 0;
+      for (i = 0; i < symbols_per_frame; i++)
+        {
+          delta_time_offset_integer =
+              getofdm(&rs_buffer[2 * t_smp], time_offset_fractional_init,freq_offset_init, delta_time_offset_I_init, Ts, Tu, Zi,symbol_temp, 0, 1,1);
+          for (j = 0; j < K_modulo; j++)
+            {
+              symbol_buffer[(i * K_modulo + j) * 2] = symbol_temp[j * 2];
+              symbol_buffer[(i * K_modulo + j) * 2 + 1] = symbol_temp[j * 2 + 1];
+            }
+          t_smp = t_smp + Ts + delta_time_offset_integer;
+        }
+
+      //  search first symbol of frame using time ref cells
+      symbol0 = getsymbolidx(symbol_buffer, symbols_per_frame, time_ref_cells_k,time_ref_cells_theta_1024, K_dc, K_modulo, 21);
+      symbol_no_to_equalize =((symbol0 - symbols_to_delay + symbols_per_frame) % symbols_per_frame) + 1;
+
+      frameSyncFlag = true;
+      symbol_counter = 0;
+
+      // frame align rs_buffer
+      if (symbol_no_to_equalize != 1)
+        {
+          rsbufwidx -= (symbol_no_to_equalize - 1) * Ts;
+
+          for (j = 0; j < rsbufwidx; j++)	/* pa0mbo was rsbufwidx  22-4-07 now better */
+            {
+              rs_buffer[j * 2] = rs_buffer[((symbol_no_to_equalize - 1) * Ts + j) * 2];
+              rs_buffer[j * 2 + 1] = rs_buffer[((symbol_no_to_equalize - 1) * Ts + j) * 2 + 1];
+            }
+        }
+      symbufwidx = 0;
+      Zi[0] = -1.0;
+      t_smp = 0;
+      for(i=0;i<(Tu_A * 2 * 26);i++)
+        {
+          symbol_buffer[i]=0;
+        }
+
+      for (i = 0; i <symbols_per_2D_window; i++)
+
+        {
+          delta_time_offset_integer =  getofdm(&rs_buffer[2 * t_smp], time_offset_fractional_init,freq_offset_init, delta_time_offset_I_init, Ts, Tu, Zi,symbol_temp, 0, 1,1);
+//          arrayDump("sm1",symbol_temp,symbols_per_2D_window*2,true);
+          for (j = 0; j < K_modulo; j++)
+            {
+              symbol_buffer[(i * K_modulo + j) * 2] = symbol_temp[j * 2];
+              symbol_buffer[(i * K_modulo + j) * 2 + 1] = symbol_temp[j * 2 + 1];
+            }
+
+          symbufwidx++;
+          t_smp += Ts + delta_time_offset_integer;
+        }
+//       arrayDump("sm1",symbol_buffer,symbols_per_2D_window*K_modulo*2,true);
+      // symbol align rs_buffer
+      rsbufwidx -= t_smp;
+
+      for (i = 0; i < rsbufwidx; i++)	/* pa0mbo was rxbufwidx 22-4-07 */
+        {
+          rs_buffer[i * 2] = rs_buffer[(i + t_smp) * 2];
+          rs_buffer[i * 2 + 1] = rs_buffer[(i + t_smp) * 2 + 1];
+        }
+      for (i = 0; i < (2*70); i++)
+        {
+          next_pilots[i] = 1.1;	/* real part */  // modified joma was j now i
+        }
+      for (i = 0; i < 458; i++)
+        {
+          actual_pilots[i] = 0.0;	/* real part */  // modified joma was j now i
+        }
+      for(i=0;i<5;i++)
+        for(j=0;j<208;j++)
+          for (k=0;k<205;k++)
+        {
+         W_pilots_blk[i][j][k]=0;
+        }
+
+    }
+
+  return true;
+}
+
+#define  MIN_ABS_H (8.0E-5 * 8.0E-5)
+
+bool demodulator::channelEstimation()
+{
+  int i,j,k,m,p,temp;
+  float t1, t2;
+  float sigmaq_noise;
+  int rndcnt;
+  double rest;
+  float a;
+  int indx;
+  int k_index1, k_index2, t1_pos, t2_pos;
+  int k1_pos, k2_pos;
+  double xsinc1, xsinc2;
+  double f_cut_t, f_cut_k;
+  //  double f_D_max;
+  //  double tau_max;
+  int *indxlu;
+  float dlu = 0.0, *collu;
+  float **amatrix;
+  int NP,sortbrkpnt;
+  //  float freq_offset_log[100];
+  //  float time_offset_log[100];
+  int cnt_time_ref_cells;
+  int s, nnn, p_min, p_max, theta_1024;
+  int sorttmp[1024], sorttmp2[1024];
+  int t_smp,nn,ntwee,mtwee,cnt_actual_pilots_rel_indx;
+  int ntc_indx;
+  int trxbuf_indx, symbuf_indx;
+  float hoek,tmpreal,tmpimag;
+  float freq_offset;
+  //  float h_absq[256];
+  //  int lH;
+  float tmp1, tmp2, tmp3;
+  int gain_ref_cells_per_window;
+  float temp1, temp2;
+  float sum_MERFAC, sum_WMERFAC, sum_weight_FAC, SNR_dB;
+  float MERFAC;
+  float FAC_squared_noise_sequence[200];
+  float squared_weight_sequence[200];
+
+  //  channel estimation based on pilots
+
+  N_symbols_needed = symbols_per_frame + symbols_per_2D_window - symbufwidx;	// pa0mbo 24-4-07
+  N_samples_needed = (N_symbols_needed + 1) * Ts - (rsbufwidx);	// pa0mbo was rsbufwidx-1
+  if (N_samples_needed > 0)
+    {
+      input_samples_buffer_request = N_samples_needed;
+      return false;
+    }
+  //     now set the parameter spectrum_occupancy
+  if (spectrum_occupancy <= 0)
+    {
+      if (spectrum_occupancy_estimation < 0) spectrum_occupancy = 3;
+      else spectrum_occupancy = spectrum_occupancy_estimation;
+    }
+  if (spectrum_occupancy > 3) spectrum_occupancy = 3;	// 4 and 5 not yet supported
+  mode_and_occupancy_code = mode_and_occupancy_code_table[robustness_mode * 6 + spectrum_occupancy];
+  if (mode_and_occupancy_code < 0)
+    {
+      spectrum_occupancy = 1;
+      mode_and_occupancy_code = mode_and_occupancy_code_table[robustness_mode * 6 + spectrum_occupancy];
+    }
+  if (mode_and_occupancy_code != mode_and_occupancy_code_last)
+    {
+      K_min = K_min_K_max_list[0][spectrum_occupancy + robustness_mode * 6];
+      K_max = K_min_K_max_list[1][spectrum_occupancy + robustness_mode * 6];
+      carrier_per_symbol = K_max - K_min + 1;
+      (void) getofdmsync(NULL, Ts, Tu, NULL, K_max - K_min + 1, 0, NULL, NULL, 1, 1, 1);	/* initialisation */
+
+      //  reformat pilot index stuff into th K_dc/K_modulo block
+      //  first call listsinit to get gain_ref_cells_k etc
+      //  Jan 5th 2009 changed listsinit() call to an include of all code from listsinit.c
+
+      Tu = Tu_list[robustness_mode];
+      Ts = Ts_list[robustness_mode];
+      Tg = Ts - Tu;
+      sigmaq_noise = sigmaq_noise_list[robustness_mode];
+      symbols_per_frame = symbols_per_frame_list[robustness_mode];
+      for (i = 0; i < 3; i++)
+        {
+          freq_ref_cells_k[i] = freq_ref_cells_k_list[robustness_mode][i];
+          freq_ref_cells_theta_1024[i] = freq_ref_cells_theta_1024_list[robustness_mode][i];
+        }
+      cnt_time_ref_cells = time_ref_cells_cnt_list[robustness_mode];
+      for (i = 0; i < cnt_time_ref_cells; i++)
+        {
+          time_ref_cells_k[i] = time_ref_cells_k_list[robustness_mode][i];
+          time_ref_cells_theta_1024[i] =
+              time_ref_cells_theta_1024_list[robustness_mode][i];
+        }
+      K_min =
+          K_min_K_max_list[0][spectrum_occupancy_estimation +
+          robustness_mode * 6];
+      K_max =
+          K_min_K_max_list[1][spectrum_occupancy_estimation +
+          robustness_mode * 6];
+      carrier_per_symbol = K_max - K_min + 1;
+      for (i = 0; i < 4; i++)
+        {
+          power_boost[i] = power_boost_list[robustness_mode][spectrum_occupancy_estimation][i];
+        }
+      x = x_list[robustness_mode];
+      y = y_list[robustness_mode];
+      k0 = k0_list[robustness_mode];
+      Q_1024 = Q_1024_list[robustness_mode];
+      mean_energy_of_used_cells =(float) (no_of_used_cells_per_frame_list[spectrum_occupancy_estimation + robustness_mode * 6] + 3 + cnt_time_ref_cells);
+      rndcnt = 0;
+      for (s = 0; s < symbols_per_frame; s++)
+        {
+          nnn = s % y;
+          m = (int) floor((double) (s / y));
+          p_min = (int) ceil((double) ((K_min - k0 - x * nnn) / (x * y)));
+          p_max = (int) floor((double) ((K_max - k0 - x * nnn) / (x * y)));
+          for (p = p_min; p <= p_max; p++)
+
+            {
+              k = k0 + x * nnn + x * y * p;
+              theta_1024 = (4*Z_256_list[robustness_mode][nnn][m]+p*W_1024_list[robustness_mode][nnn][m]+p*p*(1 +s)*Q_1024) % 1024;
+              a = sqrtf(2.0);
+              // power boost
+              for (i = 0; i < 4; i++)
+                {
+                  if (k == power_boost[i]) a = 2;
+                }
+
+              // is time ref cell ?
+
+              if (s == 0)
+                {
+                  for (i = 0; i < cnt_time_ref_cells; i++)
+                    {
+                      if (k == time_ref_cells_k[i])
+                        {
+                          indx = i;
+                          theta_1024 = time_ref_cells_theta_1024[indx];
+                          a = sqrtf(2.0);
+                          mean_energy_of_used_cells -= 1.0;
+                        }
+                    }
+                }
+
+              //  is frequence reference cell?
+              for (i = 0; i < 3; i++)
+                {
+                  if (k == freq_ref_cells_k[i])
+                    {
+                      indx = i;
+                      theta_1024 = freq_ref_cells_theta_1024[indx];
+                      if (robustness_mode == 3)
+                        {
+                          theta_1024 = (theta_1024 + 512 * s) % 1024;
+                        }
+                      a = sqrtf(2.0);
+                      mean_energy_of_used_cells -= 1.0;
+                    }
+                }
+              gain_ref_cells_k[rndcnt] = k + s * carrier_per_symbol;
+              gain_ref_cells_theta_1024[rndcnt] = theta_1024;
+              gain_ref_cells_a[rndcnt++] = a;
+              mean_energy_of_used_cells =(float) (mean_energy_of_used_cells - 1.0 + a * a);
+            }
+        }
+      mean_energy_of_used_cells /=no_of_used_cells_per_frame_list[spectrum_occupancy_estimation + robustness_mode * 6];
+
+      addToLog(QString("mean_energy_of_used_cells %1").arg(mean_energy_of_used_cells),LOGDRMDEMOD);
+
+      //   precompute 2-D Wiener filter matrix
+      //       uses gain_ref_cells etc
+      //       and rndcnt
+      symbols_per_2D_window = symbols_per_2D_window_list[robustness_mode];
+      symbols_to_delay = symbols_to_delay_list[robustness_mode];
+      f_cut_t = 0.0675 / (1.0 * (double) y);
+      f_cut_k = 1.75 * (float) Tg / (float) Tu;
+
+      for(i=0;i<5;i++) cnt_tr_cells[i]=0;
+      //    start nnn-loop
+      for (nnn = 0; nnn < y; nnn++)
+        {
+          for (i = 0; i < rndcnt; i++)
+            {
+              training_cells_k[nnn][i] = (gain_ref_cells_k[i] - K_min +(symbols_per_frame -nnn) * carrier_per_symbol) % (symbols_per_frame * carrier_per_symbol) + K_min;
+
+            }
+
+//          cnt_tr_cells[nnn] = 0;
+          for (i = 0; i < rndcnt; i++)
+            {
+              if ((training_cells_k[nnn][i] - K_min) <(carrier_per_symbol * symbols_per_2D_window))
+                {
+                  gain_ref_cells_subset_index[nnn][cnt_tr_cells[nnn]] = i;
+                  gain_ref_cells_subset[nnn][(cnt_tr_cells[nnn])++] =training_cells_k[nnn][i];
+                }
+            }
+          cnt_next_pilot_cells[nnn] = 0;
+          for (i = 0; i < rndcnt; i++)
+            {
+              if (((training_cells_k[nnn][i] - K_min) >= (carrier_per_symbol * symbols_per_2D_window)) &&
+                  ((training_cells_k[nnn][i] - K_min) < carrier_per_symbol * (symbols_per_2D_window + 1)))
+
+                {
+                  next_pilot_cells_k_index[nnn][cnt_next_pilot_cells[nnn]] = i;
+                  next_pilot_cells_k[nnn][(cnt_next_pilot_cells[nnn])++] = ((training_cells_k[nnn][i]- K_min) % carrier_per_symbol) + K_min;
+                }
+            }
+
+          //  now sort training cells in subset if necessary
+          sortbrkpnt = 0;
+          for (i = 1; i < cnt_tr_cells[nnn]; i++)
+            {
+              if (gain_ref_cells_subset[nnn][i] < gain_ref_cells_subset[nnn][i - 1]) sortbrkpnt = i; // break in data ?
+            }
+          if (sortbrkpnt > 0)
+            {
+              // keep first part in sorttmp
+              for (i = 0; i < sortbrkpnt; i++)
+                {
+                  sorttmp[i] = gain_ref_cells_subset[nnn][i];
+                  sorttmp2[i] = gain_ref_cells_subset_index[nnn][i];	/* pa0mbo added 22-jan-2009 */
+                }
+              // now  shift smaller ones to start of vector
+              for (i = 0; i < cnt_tr_cells[nnn] - sortbrkpnt; i++)
+                {
+                  gain_ref_cells_subset[nnn][i] = gain_ref_cells_subset[nnn][i + sortbrkpnt];
+                  gain_ref_cells_subset_index[nnn][i] =gain_ref_cells_subset_index[nnn][i + sortbrkpnt];
+                }
+
+              //           replace last part from tmp
+
+              for (i = cnt_tr_cells[nnn] - sortbrkpnt; i < cnt_tr_cells[nnn];i++)
+                {
+                  gain_ref_cells_subset[nnn][i] =sorttmp[i + sortbrkpnt - cnt_tr_cells[nnn]];
+                  gain_ref_cells_subset_index[nnn][i] =sorttmp2[i + sortbrkpnt - cnt_tr_cells[nnn]];
+                }
+              sortbrkpnt = 0;
+            }
+
+          //  copy to training_cells_k
+          for (i = 0; i < cnt_tr_cells[nnn]; i++)
+            {
+              training_cells_k[nnn][i] = gain_ref_cells_subset[nnn][i];
+            }
+
+          gain_ref_cells_per_window = cnt_tr_cells[nnn];
+          for (k_index1 = 0; k_index1 < gain_ref_cells_per_window; k_index1++)
+            {
+              for (k_index2 = 0; k_index2 < gain_ref_cells_per_window;k_index2++)
+                {
+                  k1_pos = (((gain_ref_cells_subset[nnn][k_index1] -K_min)) % carrier_per_symbol) + K_min;
+                  t1_pos = (gain_ref_cells_subset[nnn][k_index1] - K_min) / carrier_per_symbol;
+                  k2_pos = ((gain_ref_cells_subset[nnn][k_index2] -K_min)) % carrier_per_symbol + K_min;
+                  t2_pos = ((gain_ref_cells_subset[nnn][k_index2] - K_min)) / carrier_per_symbol;
+                  xsinc1 = (k1_pos - k2_pos) * f_cut_k;
+                  xsinc2 = (t1_pos - t2_pos) * f_cut_t;
+                  if (k1_pos == k2_pos) xsinc1 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc1);
+                      xsinc1 = rest / (M_PI * xsinc1);
+                    }
+                  if (fabs(xsinc2) < DBL_EPSILON) xsinc2 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc2);
+                      xsinc2 = rest / (M_PI * xsinc2);
+                    }
+                  PHI[k_index1][k_index2] = (float) (xsinc1 * xsinc2);
+                }
+            }
+          for (i = 0; i < gain_ref_cells_per_window; i++)
+            {
+              PHI[i][i] += sigmaq_noise * 2.0 /((gain_ref_cells_a[gain_ref_cells_subset_index[nnn][i]]) * (gain_ref_cells_a[gain_ref_cells_subset_index[nnn][i]]));
+            }
+
+          //   now the matrix inversion from numerical recipes
+
+          NP = gain_ref_cells_per_window;
+          amatrix = matrix(1, NP, 1, NP);
+          indxlu = ivector(1, NP);
+          collu = fvector(1, NP);
+          for (i = 1; i <= NP; i++)
+            for (j = 1; j <= NP; j++)
+              amatrix[i][j] = PHI[i - 1][j - 1];
+          ludcmp(amatrix, NP, indxlu, &dlu);	/* decompose just once */
+          for (j = 1; j <= NP; j++)
+            {
+              for (i = 1; i <= NP; i++) collu[i] = 0.0;
+              collu[j] = 1.0;
+              lubksb(amatrix, NP, indxlu, collu);
+              for (i = 1; i <= NP; i++) PHI_INV[i - 1][j - 1] = collu[i];
+            }
+          free_fvector(collu, 1, NP);
+          free_ivector(indxlu, 1, NP);
+          free_matrix(amatrix, 1, NP, 1, NP);
+
+          for (k_index1 = 0; k_index1 < (K_max - K_min + 1); k_index1++)
+            {
+              for (k_index2 = 0; k_index2 < gain_ref_cells_per_window; k_index2++)
+                {
+                  k1_pos = k_index1 + K_min;
+                  t1_pos = symbols_to_delay;
+                  k2_pos = (training_cells_k[nnn][k_index2] - K_min) % (K_max -K_min + 1) + K_min;
+                  t2_pos = (training_cells_k[nnn][k_index2] - K_min) / (K_max -K_min + 1);
+                  xsinc1 = (k1_pos - k2_pos) * f_cut_k;
+                  xsinc2 = (t1_pos - t2_pos) * f_cut_t;
+                  if (k1_pos == k2_pos) xsinc1 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc1);
+                      xsinc1 = rest / (M_PI * xsinc1);
+                    }
+                  if (t1_pos == t2_pos) xsinc2 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc2);
+                      xsinc2 = rest / (M_PI * xsinc2);
+                    }
+                  THETA[k_index2] = (float) (xsinc1 * xsinc2);
+                }
+              // calc matrix product THETA*PHI_INV
+              for (j = 0; j < NP; j++)
+
+                {
+                  W_symbol[j] = 0.0;
+                  for (k = 0; k < NP; k++) W_symbol[j] += THETA[k] * PHI_INV[k][j];
+                }
+              for (j = 0; j < NP; j++)
+                {
+                  W_symbol_blk[nnn][j][k_index1] = W_symbol[j];
+                }
+            }
+          for (k_index1 = 0; k_index1 < cnt_next_pilot_cells[nnn]; k_index1++)
+            {
+              for (k_index2 = 0; k_index2 < gain_ref_cells_per_window;k_index2++)
+
+                {
+                  k1_pos = next_pilot_cells_k[nnn][k_index1];
+                  t1_pos = symbols_per_2D_window - 1;
+                  k2_pos = (training_cells_k[nnn][k_index2] - K_min) % (K_max -K_min + 1) +K_min;
+                  t2_pos = (training_cells_k[nnn][k_index2] - K_min) / (K_max -K_min + 1);
+                  xsinc1 = (k1_pos - k2_pos) * f_cut_k;
+                  xsinc2 = (t1_pos - t2_pos) * f_cut_t;
+                  if (k1_pos == k2_pos) xsinc1 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc1);
+                      xsinc1 = rest / (M_PI * xsinc1);
+                    }
+                  if (t1_pos == t2_pos) xsinc2 = 1.0;
+                  else
+                    {
+                      rest = sin(M_PI * xsinc2);
+                      xsinc2 = rest / (M_PI * xsinc2);
+                    }
+                  THETA[k_index2] = (float) (xsinc1 * xsinc2);
+                }		/* end k_index2-loop */
+
+              // calc matrix product THETA*PHI_INV
+              for (j = 0; j < NP; j++)
+                {
+                  W_pilots[j] = 0.0;
+                  for (k = 0; k < NP; k++) W_pilots[j] += THETA[k] * PHI_INV[k][j];
+                }
+              for (j = 0; j < NP; j++)
+                {
+                  W_pilots_blk[nnn][j][k_index1] = W_pilots[j];
+                }
+            }			/* end k_index1-loop */
+        }			/* end nnn-loop  pa0mbo 26-5-2007 */
+
+
+      for (i = 0; i < rndcnt; i++)
+        {
+          temp = ((gain_ref_cells_k[i] - K_min) / (carrier_per_symbol)) * (K_modulo - carrier_per_symbol);
+          gain_ref_cells_k[i] += temp + K_dc - 1;
+        }
+      lFAC = mkfacmap(robustness_mode, K_dc, K_modulo, FAC_cells_k);
+      mode_and_occupancy_code_last = mode_and_occupancy_code;
+
+      for (i = 0; i < cnt_tr_cells[y-1]; i++) // joma y-1 was y
+        {
+          next_pilots[2 * i] = 0.0;	/* real part */  // modified joma was j now i
+          next_pilots[2 * i + 1] = 0.0;	/* imag */
+        }
+      gain_ref_cells_per_frame = rndcnt;
+      gain_ref_cells_per_y_symbols = rndcnt / (symbols_per_frame / y);
+    }
+  //  in matlab code here for display toctic_equalization = 0 etc
+
+  t_smp = 0;
+
+  for (i = 0; i < symbols_per_frame; i++)
+    {
+      symbol_counter++;
+      //  shifted symbol index
+      nn = (i - symbols_to_delay + symbols_per_frame) % symbols_per_frame;
+      ntwee = nn % y;
+      mtwee = nn / y;
+      for (j = 0; j < cnt_tr_cells[ntwee]; j++)
+        {
+          gain_ref_cells_subset_nn[j] =(mtwee * gain_ref_cells_per_y_symbols + gain_ref_cells_subset_index[ntwee][j]) % gain_ref_cells_per_frame;
+          training_cells_relative_index[j] =(gain_ref_cells_k[gain_ref_cells_subset_nn[j]] +(symbols_per_frame -nn) * K_modulo) % (K_modulo * symbols_per_frame);
+        }
+
+     cnt_actual_pilots_rel_indx = 0;
+//     logfile->addToAux(QString("block %1").arg(iterationCounter));
+      for (j = 0; j < cnt_tr_cells[ntwee]; j++)
+        {
+          if ((training_cells_relative_index[j] - (symbols_per_2D_window-1) * K_modulo) >= 0)
+            {
+              actual_pilots_relative_index[cnt_actual_pilots_rel_indx++] = j;
+            }
+          ntc_indx = training_cells_relative_index[j] + i * K_modulo + 1;	/* pa0mbo in  matlab +1 =OK trcrindx 1 lager dan in M   */
+
+          hoek =(float) (2.0 * M_PI *(float)gain_ref_cells_theta_1024[gain_ref_cells_subset_nn[j]] /1024.0);
+
+          tmpreal =(float) (cos(hoek) / gain_ref_cells_a[gain_ref_cells_subset_nn[j]]);
+          tmpimag =(float) (-sin(hoek) /gain_ref_cells_a[gain_ref_cells_subset_nn[j]]);
+          normalized_training_cells[2 * j] = symbol_buffer[2 * ntc_indx] * tmpreal - symbol_buffer[2 * ntc_indx + 1] * tmpimag;	/* real part */
+          normalized_training_cells[2 * j + 1] = symbol_buffer[2 * ntc_indx + 1] * tmpreal + symbol_buffer[2 * ntc_indx] * tmpimag;	/* imag part */
+        }
+
+
+      for (j = 0; j < cnt_actual_pilots_rel_indx; j++)
+        {
+          actual_pilots[2 * j] = normalized_training_cells[2 * (actual_pilots_relative_index[j])];	/* real part */
+          actual_pilots[2 * j + 1] = normalized_training_cells[2 * (actual_pilots_relative_index[j]) + 1];	/* imag */
+        }
+      temp1 = 0.0;
+      temp2 = 0.0;
+      for (j = 0; j < cnt_actual_pilots_rel_indx; j++)
+        {
+          temp1 += actual_pilots[2 * j] * next_pilots[2 * j] + actual_pilots[2 * j + 1] * next_pilots[2 * j + 1];	/* real part */
+          temp2 += actual_pilots[2 * j] * next_pilots[2 * j + 1] -actual_pilots[2 * j + 1] * next_pilots[2 * j];
+
+        }
+      if (i != 0)  delta_freq_offset = (float) atan2(temp2, temp1 + MIN_ABS_H);
+
+      for (j = 0; j < K_max - K_min + 1; j++)
+        {
+          H[2 * j] = 0.0;
+          H[2 * j + 1] = 0.0;
+          for (k = 0; k < cnt_tr_cells[ntwee]; k++)
+            {
+              H[2 * j] += normalized_training_cells[2 * k] * W_symbol_blk[ntwee][k][j];
+              H[2 * j + 1] += normalized_training_cells[2 * k + 1] * W_symbol_blk[ntwee][k][j];
+            }
+        }
+
+
+
+      for (j = 0; j < cnt_actual_pilots_rel_indx; j++)
+        {
+          next_pilots[2 * j] = 0.0;
+          next_pilots[2 * j + 1] = 0.0;
+          for (k = 0; k < cnt_tr_cells[ntwee]; k++)
+            {
+              next_pilots[2 * j] +=  normalized_training_cells[2 * k] * W_pilots_blk[ntwee][k][j];
+              next_pilots[2 * j + 1] += normalized_training_cells[2 * k + 1] * W_pilots_blk[ntwee][k][j];
+
+            }
+        }
+
+
+
+      for (j = K_min; j <= K_max; j++)
+        {
+          trxbuf_indx = transmission_frame_buffer_wptr + i * K_modulo + K_dc + j;
+          symbuf_indx = (i + symbols_to_delay) * K_modulo + K_dc + j;
+          tmp1 = H[2 * (j - K_min)] * H[2 * (j - K_min)] + H[2 * (j - K_min) +1] * H[2 * (j -K_min) +1] + MIN_ABS_H;
+          tmp2 = H[2 * (j - K_min)] / tmp1;
+          tmp3 = -H[2 * (j - K_min) + 1] / tmp1;
+          transmission_frame_buffer[2 * trxbuf_indx] =symbol_buffer[2 * symbuf_indx] * tmp2 -symbol_buffer[2 * symbuf_indx + 1] * tmp3;
+          transmission_frame_buffer[2 * trxbuf_indx + 1] =symbol_buffer[2 * symbuf_indx] * tmp3 +symbol_buffer[2 * symbuf_indx + 1] * tmp2;
+          channel_transfer_function_buffer[2 * trxbuf_indx] =H[2 * (j - K_min)];
+          channel_transfer_function_buffer[2 * trxbuf_indx + 1] =H[2 * (j - K_min) + 1];
+        }
+
+      //  get next symbol
+      if ((iterationCounter>=0) && (iterationCounter<=180))
+        {
+//          arrayDump("b179",&rs_buffer[2 * t_smp],Ts,true);
+//          logfile->addToAux(QString("block %1").arg(iterationCounter));
+//          arrayDump("h179",H,K_max - K_min + 1,true);
+        }
+      delta_time_offset_integer =getofdmsync(&rs_buffer[2 * t_smp], Ts, Tu, H, K_max - K_min + 1,delta_freq_offset, Zi, symbol_temp, 0, 1,1);
+//      arrayDump("symtmp",symbol_temp,K_modulo,true);
+
+//      addToLog(QString("delta_time_offset_integer %1").arg(delta_time_offset_integer),LOGDRMDEMOD);
+      for (j = 0; j < K_modulo; j++)
+        {
+          symbol_buffer[(symbufwidx * K_modulo + j) * 2] = symbol_temp[j * 2];
+          symbol_buffer[(symbufwidx * K_modulo + j) * 2 + 1] = symbol_temp[j * 2 + 1];
+        }
+      symbufwidx++;
+      t_smp += Ts + delta_time_offset_integer;	/* next symbol */
+    }				/* end i-loop over symbols_per_frame */
+
+
+  // fac_valid=0;
+
+  freq_offset = Zi[4];
+  time_offset_fractional = Zi[5];
+  delta_time_offset_I = Zi[2];
+  addToLog("GREEN on channel estimation",LOGDRMDEMOD);
+
+  //  clock too slow or too fast: adjust playing speed
+
+  smp_rate_conv_fft_phase_diff = 4 * (t_smp - N_symbols_needed * Ts);
+  smp_rate_conv_fft_phase_offset = 4 * time_offset_fractional;
+  smp_rate_conv_in_out_delay += smp_rate_conv_fft_phase_diff;
+
+
+  addToLog(QString("Rate %1 %2 %3").arg(smp_rate_conv_fft_phase_diff).arg(smp_rate_conv_fft_phase_offset).arg(smp_rate_conv_in_out_delay),LOGDRMDEMOD);
+  //    display results
+  freqOffset=-freq_offset * 12000.0 / (float) Tu / (2.0 * M_PI);
+  deltaFS= (1.0 / (delta_time_offset_I / Ts + 1.0) - 1.0) ;
+  samplerate_offset=delta_time_offset_I /Ts;
+
+
+  //   symbol align rs_buffer
+
+
+
+  //     swap symbol buffer
+
+  symbufwidx -= symbols_per_frame;
+  for (i = 0; i < symbufwidx * K_modulo; i++)
+    {
+      symbol_buffer[i * 2] = symbol_buffer[2 * symbols_per_frame * K_modulo + 2 * i];
+      symbol_buffer[i * 2 + 1] = symbol_buffer[2 * symbols_per_frame * K_modulo + 2 * i + 1];
+    }
+
+  //     SNR estimation using FAC  cells
+  //     mean energy of used cells set in listsinit()
+
+  sum_MERFAC = 0.0;
+  sum_WMERFAC = 0.0;
+  sum_weight_FAC = 0.0;
+  for (i = 0; i < lFAC; i++)
+
+    {
+      trxbuf_indx = transmission_frame_buffer_wptr + FAC_cells_k[i];	/* pa0mbo 18-5-2007 checked  */
+      FAC_cells_sequence[i * 2] = transmission_frame_buffer[2 * trxbuf_indx];
+      FAC_cells_sequence[i * 2 + 1] = transmission_frame_buffer[2 * trxbuf_indx + 1];
+      t1 = (float) (fabs(FAC_cells_sequence[i * 2]) - sqrt(0.5));
+      t1 = t1 * t1;
+      t2 = (float) (fabs(FAC_cells_sequence[i * 2 + 1]) - sqrt(0.5));
+      t2 = t2 * t2;
+      FAC_squared_noise_sequence[i] = t1 + t2;
+      t1 = channel_transfer_function_buffer[trxbuf_indx * 2];
+      t1 = t1 * t1;
+      t2 = channel_transfer_function_buffer[trxbuf_indx * 2 + 1];
+      t2 = t2 * t2;
+      squared_weight_sequence[i] = t1 + t2;
+      sum_MERFAC += FAC_squared_noise_sequence[i];
+      sum_WMERFAC += FAC_squared_noise_sequence[i] * (squared_weight_sequence[i] + 1.0E-10);
+      sum_weight_FAC += squared_weight_sequence[i];
+    }
+
+  FACAvailable=true;
+  MERFAC = (float) (log(sum_MERFAC / lFAC + 1.0E-10));
+  MERFAC /= (log(10.0));
+  MERFAC *= -10.0;
+  WMERFAC =(float) (log(sum_WMERFAC /(mean_energy_of_used_cells * (sum_weight_FAC + lFAC * 1.0E-10))));
+  WMERFAC /= (log(10.0));
+  WMERFAC *= -10.0;
+  SNR_dB = WMERFAC;
+
+  addToLog(QString("SNR-FAC =%1").arg(SNR_dB),LOGDRMDEMOD);
+//  N_samples_needed = (symbols_per_frame + 1) * Ts - rsbufwidx;
+
+  if (SNR_dB < SNR_MIN_DB)
+    {
+      SNR_timeout_counter--;
+      if (SNR_timeout_counter <= 0)
+        {
+          doSynchronize = true;
+          SNR_timeout_counter = SNR_TIMEOUT;
+        }
+      transmission_frame_buffer_data_valid = 0;
+      fac_not_valid_counter--;
+      if (fac_not_valid_counter <= 0)
+        {
+          doSynchronize = true;
+          fac_not_valid_counter = FACVALIDCNTR;
+        }
+    }
+  else
+    {
+      SNR_timeout_counter = SNR_TIMEOUT;
+      transmission_frame_buffer_data_valid = 1;
+      fac_valid = 1;
+    }
+  if (doSynchronize) N_samples_needed = N_symbols_mode_detection * 320 - rsbufwidx;
+  else
+    {
+      rsbufwidx -= t_smp;
+      for (i = 0; i < rsbufwidx; i++)
+        {
+          rs_buffer[i * 2]     = rs_buffer[(i + t_smp) * 2];
+          rs_buffer[i * 2 + 1] = rs_buffer[(i + t_smp) * 2 + 1];
+        }
+      N_samples_needed = (symbols_per_frame + 1) * Ts - rsbufwidx;
+    }
+  if (N_samples_needed > 0)
+    {
+      input_samples_buffer_request = N_samples_needed;
+    }
+  else
+    {
+      input_samples_buffer_request = 0;
+    }
+  return true;
+}
diff --git a/qsstv/drmrx/demodulator.h b/qsstv/drmrx/demodulator.h
new file mode 100644
index 0000000..62b7830
--- /dev/null
+++ b/qsstv/drmrx/demodulator.h
@@ -0,0 +1,123 @@
+#ifndef DEMODULATOR_H
+#define DEMODULATOR_H
+#include <fftw3.h>
+#include "drmproto.h"
+#include "drmdefs.h"
+#include "sourcedecoder.h"
+
+
+#define SNR_TIMEOUT  10
+#define FACVALIDCNTR 10
+#define SNR_MIN_DB 3
+#define DRMNUMMODES 4
+
+
+//original
+//#define SNR_TIMEOUT 3
+//#define FAC_NOT_VALID 4
+//#define SNR_MIN_DB 5
+//#define FAC_NOT_VALID_TIMEOUT 4
+
+
+extern int Ts_list[DRMNUMMODES];
+extern int Tu_list[DRMNUMMODES];
+extern int Tg_list[DRMNUMMODES];
+class demodulator
+{
+public:
+  demodulator();
+  ~demodulator();
+  void init();
+  bool demodulate(float *sigin, int  numSamples);
+  bool isTimeSync() {return timeSyncFlag;}
+  bool isFrequencySync() {return frequencySyncFlag;}
+  bool isFrameSync() {return frameSyncFlag;}
+private:
+  int iterationCounter;
+  bool timeSync();
+  bool frequencySync();
+  bool frameSync();
+  bool channelEstimation();
+//  bool FACAvailable;
+  int symbol_counter;
+  int N_samples_needed;
+  int SNR_time_out_counter;
+  int fac_not_valid_counter;
+  int mode_and_occupancy_code_last;
+  int rsbufwidx;
+  int symbufwidx;
+  int smp_rate_conv_fft_phase_diff;
+  float smp_rate_conv_fft_phase_offset;
+  int smp_rate_conv_in_out_delay;
+  fftwf_plan p1;
+  drmComplex ss[256], S[256];
+//  float rs_buffer[DRMBUFSIZE];
+  float rs_buffer[10*8000];
+  bool doSynchronize;
+  bool timeSyncFlag;
+  bool frequencySyncFlag;
+  bool frameSyncFlag;
+  int numberOfSamples;
+  smode_info mode_block;
+  float time_offset;
+  float frequency_offset_fractional_init;
+  int time_offset_integer;
+  int counter;
+  int Ts, Tu, Tg, Tgh;
+  float freq_offset_integer;
+  int x,y, k0;
+  int symbols_per_2D_window;
+  int delta_time_offset_integer;
+  float time_offset_fractional_init;
+  float freq_offset_init;
+  float delta_time_offset_I_init;
+  float Zi[6];
+  float symbol_temp[2 * Tu_A];
+  float symbol_buffer[Tu_A * 2 * 26];
+  int time_ref_cells_k[21];
+  int time_ref_cells_theta_1024[21];
+  int symbols_to_delay;
+  int N_symbols_needed;
+  int no_of_used_cells_per_frame_list[24];
+  float sigmaq_noise_list[4];
+  int gain_ref_cells_k[712];
+  int gain_ref_cells_theta_1024[712];
+  float gain_ref_cells_a[712];
+  int training_cells_k[5][712];
+  int cnt_tr_cells[5];
+  int gain_ref_cells_per_frame;
+  int gain_ref_cells_per_y_symbols;
+  float next_pilots[2*70];	/* complex */   //joma
+  int K_min,K_max;
+  float W_symbol_blk[5][208][229];
+  float W_symbol[208];
+  float W_pilots_blk[5][208][205];
+  float W_pilots[208];
+  int SNR_timeout_counter;
+  float delta_freq_offset;
+  int mode_and_occupancy_code;
+  int carrier_per_symbol;
+  int freq_ref_cells_k[3];
+  int freq_ref_cells_theta_1024[3];
+  int power_boost[4];
+  int Q_1024;
+  int gain_ref_cells_subset[5][209];
+  int gain_ref_cells_subset_index[5][209];
+  int cnt_next_pilot_cells[5];
+  int next_pilot_cells_k_index[5][40];
+  int next_pilot_cells_k[5][40];
+  float PHI[208][208];
+  float PHI_INV[208][208];
+  float THETA[208];
+  int gain_ref_cells_subset_nn[209];
+  int training_cells_relative_index[209];
+  int actual_pilots_relative_index[209];
+  float normalized_training_cells[1424];
+  float actual_pilots[458];
+  float H[458];
+  float delta_time_offset_I;
+  float time_offset_fractional;
+  bool initChannelEstimation;
+};
+
+#endif // DEMODULATOR_H
diff --git a/qsstv/drmrx/drm.cpp b/qsstv/drmrx/drm.cpp
new file mode 100644
index 0000000..2c904d4
--- /dev/null
+++ b/qsstv/drmrx/drm.cpp
@@ -0,0 +1,146 @@
+/*
+*          File drm.h
+*      
+*          M.Bos - PA0MBO
+*          Date feb 21st 2009
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+
+#include "drm.h"
+
+//float acq_signal[2*DRMBUFSIZE]; //contains complex numbers
+int input_samples_buffer_request;
+int symbols_per_frame_list[4] = { 15, 15, 20, 24 };
+int time_ref_cells_k_list[4][21] =
+{
+  {6, 7, 11, 12, 15, 16, 23, 29, 30, 33, 34, 38, 39, 41, 45, 46, 0, 0, 0, 0, 0},
+  {6, 10, 11, 14, 17, 18, 27, 28, 30, 33, 34, 38, 40, 41, 44, 0, 0, 0, 0, 0, 0}, 
+  {7, 8, 13, 14, 21, 22, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
+  {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0}
+};
+int time_ref_cells_theta_1024_list[4][21] =
+  {
+  {973, 205, 717, 264, 357, 357, 952, 440, 856, 88,   88, 68, 836, 836, 836, 1008, 0, 0, 0, 0, 0},
+  {304, 331, 108, 620, 192, 704,  44, 432, 588, 844, 651,651, 651, 460, 950,    0, 0, 0, 0, 0, 0},
+  {432, 331, 108, 620, 192, 704,  44, 304,   0,   0,   0,  0,   0,   0,   0,    0, 0, 0, 0, 0, 0},
+  {1,     2,   3,   4,   5,   6,   7,   8,   9,  10,  11, 12,  13,  14,  15,   16, 0, 0, 0, 0, 0}
+};
+
+int y_list[4] = { 5, 3, 4, 3 };
+int symbols_per_2D_window_list[4] = { 10, 6, 8, 6 };
+int symbols_to_delay_list[4] = { 5, 3, 4, 3 };
+float cpsd[513], psd[513];
+int N_symbols_frequency_pilot_search = 15;
+int K_min_K_max_list[2][24] =
+  {
+    {2, 2, -102, -114, -98, -110, 1, 1, -91, -103, -87, -99, 1, 1, 0, -69, 0, -67, 0, 0, 0, -44, 0, -43},
+    {54, 58, 102, 114, 314, 350, 45, 51, 91,  103, 279, 311, 29,31, 0, 69, 0, 213, 0, 0, 0, 44, 0, 135}
+};
+float samplerate_offset_estimation;
+float samplerate_offset;
+int N_symbols_mode_detection;
+int time_offset_log_last;
+int transmission_frame_buffer_data_valid;
+int fac_valid=0;
+int no_of_unused_carriers_list[4] = { 2, 1, 1, 1 };
+int freq_ref_cells_k_list[4][3] = { {9, 27, 36}, {8, 24, 32}, {5, 15, 20}, {5, 15, 20}};
+int freq_ref_cells_theta_1024_list[4][3] = { {205, 836, 215}, {331, 651, 555}, {788, 1014, 332}, {788, 1014, 332}
+};
+int x_list[4] = { 4, 2, 1, 1 };
+int k0_list[4] = { 2, 1, 1, 1 };
+int dimw1024[4][2] = { {5, 3}, {3, 5}, {2, 10}, {3, 8} };      /* matrix[mode][n][m] */
+int W_1024_list[4][5][10] = 
+  { {{228, 341, 455, 0, 0, 0, 0, 0, 0, 0}, {455, 569, 683, 0, 0, 0, 0, 0, 0, 0}, {683, 796, 910, 0, 0, 0, 0, 0, 0, 0}, 
+     {910, 0, 114, 0, 0, 0, 0, 0, 0, 0}, {114, 228, 341, 0, 0, 0, 0, 0, 0, 0}}, 
+  {{512, 0, 512, 0, 512, 0, 0, 0, 0, 0}, {0, 512, 0, 512, 0, 0, 0, 0, 0, 0}, {512, 0, 512, 0, 512, 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}}, 
+  {{512, 0, 512, 0, 512, 0, 0, 0, 0, 0}, {0, 512, 0, 512, 0, 0, 0, 0, 0, 0}, 
+   {512, 0, 512, 0, 512, 0, 0, 0, 0, 0}, {0, 512, 0, 512, 0, 0, 0, 0, 0, 0}, 
+   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 
+  {{465, 372, 279, 186, 93, 0, 931, 838, 745, 652}, 
+   {931, 838, 745, 652, 559, 465, 372, 279, 186, 93}, 
+   {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}}
+};
+int Z_256_list[4][5][10] = 
+  { {{0, 81, 248, 0, 0, 0, 0, 0, 0, 0}, 
+     {18, 106, 106, 0, 0, 0, 0, 0, 0, 0}, 
+     {122, 116, 31, 0, 0, 0, 0, 0, 0, 0}, 
+     {129, 129, 39, 0, 0, 0, 0, 0, 0, 0}, 
+     {33, 32, 111, 0, 0, 0, 0, 0, 0, 0}}, 
+  {{0, 57, 164, 64, 12, 0, 0, 0, 0, 0}, 
+   {168, 255, 161, 106, 118, 0, 0, 0, 0, 0}, 
+   {25, 232, 132, 233, 38, 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, 57, 164, 64, 12, 0, 0, 0, 0, 0}, {168, 255, 161, 106, 118, 0, 0, 0, 0, 0}, 
+   {25, 232, 132, 233, 38, 0, 0, 0, 0, 0}, {168, 255, 161, 106, 118, 0, 0, 0, 0, 0}, 
+   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 
+  {{0, 240, 17, 60, 220, 38, 151, 101, 0, 0}, 
+   {110, 7, 78, 82, 175, 150, 106, 25, 0, 0}, 
+   {165, 7, 252, 124, 253, 177, 197, 142, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} 
+};
+int Q_1024_list[4] = { 36, 12, 10, 14 };
+int power_boost_list[4][6][4] = 
+  { {{2, 6, 50, 54}, {2, 6, 54, 58}, {0, 0, 0, 0}, 
+     {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, 
+  {{1, 3, 43, 45}, {1, 3, 49, 51}, {0, 0, 0, 0}, 
+   {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, 
+  {{1, 29, 0, 0}, {1, 31, 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}, {0, 0, 0, 0}} 
+};
+int mode_and_occupancy_code_table[14] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+int robustness_mode, spectrum_occupancy_estimation;
+int spectrum_occupancy;
+int symbols_per_frame;
+int time_ref_cells_cnt_list[4] = { 16, 15, 8, 16 };
+int K_modulo, K_dc;
+float mean_energy_of_used_cells;
+int FAC_cells_k[65];
+float transmission_frame_buffer[82980];        /* complex */
+float channel_transfer_function_buffer[82980]; /* complex */
+int transmission_frame_buffer_wptr = 0;
+int lFAC;
+
+//Display * display;
+int runstate;
+struct mplex_desc multiplex_description;
+struct audio_info audio_information;
+struct appl_info application_information;
+struct stream_info stream_information;
+struct time_info time_and_date;
+struct dflttmsg default_text_message;
+struct dfltdunitasmbly default_data_unit_assembly;
+struct dfltMOTdirasmbly default_MOT_directory_assembly;
+struct dfltMOTobjasmbly default_MOT_object_assembly;
+struct dfltMOTobjasmblyinfo default_MOT_object_assembly_information;
+struct dfltMOTobj default_MOT_object;
+int channel_decoded_data_buffer_data_valid;
+double channel_decoded_data_buffer[110000];
+float WMERFAC;
+
+
+
+/* char text_message[1000]; */ 
+int audio_data_flag;
+int length_decoded_data;
+int MSC_Demapper[6][2959];
+long bufaucnt[2048];
+
+
+
diff --git a/qsstv/drmrx/drm.h b/qsstv/drmrx/drm.h
new file mode 100644
index 0000000..aeec164
--- /dev/null
+++ b/qsstv/drmrx/drm.h
@@ -0,0 +1,107 @@
+#ifndef DRM_H
+#define DRM_H
+/*
+*          File drm.h
+*      
+*          M.Bos - PA0MBO
+*          Date feb 21st 2009
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+
+#include <fftw3.h>
+#include "drmdefs.h"
+#include "structtemplates.h"
+
+
+class demodulator;
+extern int input_samples_buffer_request;
+extern int symbols_per_frame_list[4];
+extern int time_ref_cells_k_list[4][21];
+extern int time_ref_cells_theta_1024_list[4][21];
+extern int y_list[4];
+extern int symbols_per_2D_window_list[4];
+extern int symbols_to_delay_list[4];
+extern int N_symbols_frequency_pilot_search;
+extern int K_min_K_max_list[2][24];
+extern float samplerate_offset_estimation;
+extern float samplerate_offset; //ON4QZ
+extern int N_symbols_mode_detection;
+extern int time_offset_log_last;
+extern int transmission_frame_buffer_data_valid;
+extern int fac_valid;
+extern int no_of_unused_carriers_list[4];
+extern int freq_ref_cells_k_list[4][3];
+extern int freq_ref_cells_theta_1024_list[4][3];
+extern int x_list[4];
+extern int k0_list[4];
+extern int dimw1024[4][2]; /* matrix[mode][n][m] */
+extern int W_1024_list[4][5][10];
+extern int Z_256_list[4][5][10];
+extern int Q_1024_list[4];
+extern int power_boost_list[4][6][4];
+extern int mode_and_occupancy_code_table[14];
+extern int robustness_mode, spectrum_occupancy_estimation;
+extern int spectrum_occupancy;
+extern int symbols_per_frame;
+extern int time_ref_cells_cnt_list[4];
+extern int K_modulo, K_dc;
+extern float mean_energy_of_used_cells;
+extern int FAC_cells_k[65];
+extern float transmission_frame_buffer[82980];        /* complex */
+extern float channel_transfer_function_buffer[82980]; /* complex */
+extern int transmission_frame_buffer_wptr;
+extern int lFAC;
+extern float cpsd[513], psd[513];
+
+//Display * display;
+extern int runstate;
+extern struct mplex_desc multiplex_description;
+extern struct audio_info audio_information;
+extern struct appl_info application_information;
+extern struct stream_info stream_information;
+extern struct time_info time_and_date;
+extern struct dflttmsg default_text_message;
+extern struct dfltdunitasmbly default_data_unit_assembly;
+extern struct dfltMOTdirasmbly default_MOT_directory_assembly;
+extern struct dfltMOTobjasmbly default_MOT_object_assembly;
+extern struct dfltMOTobjasmblyinfo default_MOT_object_assembly_information;
+extern struct dfltMOTobj default_MOT_object;
+extern int channel_decoded_data_buffer_data_valid;
+extern double channel_decoded_data_buffer[110000];
+extern float WMERFAC;
+extern bool callsignValid;
+
+
+
+/* char text_message[1000]; */ 
+extern int audio_data_flag;
+extern int length_decoded_data;
+extern int MSC_Demapper[6][2959];
+extern long bufaucnt[2048];
+
+extern int spectrum_occupancy_new;
+extern int msc_mode_new;
+extern int interleaver_depth_new;
+extern float freqOffset;
+extern float deltaFS;
+//extern char drmCallsign[9];
+extern demodulator *demodulatorPtr;
+
+#endif
+
+
diff --git a/qsstv/drmrx/drmconstellationframe.cpp b/qsstv/drmrx/drmconstellationframe.cpp
new file mode 100644
index 0000000..907942c
--- /dev/null
+++ b/qsstv/drmrx/drmconstellationframe.cpp
@@ -0,0 +1,78 @@
+#include "drmconstellationframe.h"
+#include "ui_drmconstellationframe.h"
+#include <QPainter>
+#include "drm.h"
+#include "math.h"
+#include "qsstvdefs.h"
+
+
+extern float MSC_cells_sequence[2 * 2959];
+extern int lMSC;
+extern bool MSCAvailable;
+
+extern int lFAC;
+extern float FAC_cells_sequence[200];
+extern bool FACAvailable;
+
+#define CSTRANGE 1.5
+#define CSTSPAN 3.
+
+drmConstellationFrame::drmConstellationFrame(QWidget *parent) :
+  QFrame(parent),
+  ui(new Ui::drmConstellationFrame)
+{
+  ui->setupUi(this);
+  lmsc=0;
+}
+
+drmConstellationFrame::~drmConstellationFrame()
+{
+  delete ui;
+}
+
+void drmConstellationFrame::paintEvent (QPaintEvent *e)
+{
+    int i,posx,posy;
+    QPainter qpainter (this);
+   // qpainter.drawRect (contentsRect());
+    qpainter.setPen (QPen (Qt::blue, 2));
+    qpainter.drawLine (contentsRect().x()+contentsRect().width()/2, contentsRect().y(), contentsRect().x()+contentsRect().width()/2,contentsRect().y()+contentsRect().height());
+    qpainter.drawLine (contentsRect().x(), contentsRect().y()+contentsRect().height()/2, contentsRect().x()+contentsRect().width(),contentsRect().y()+contentsRect().height()/2);
+    for(i=0;i<lmsc/2;i++)
+    {
+        posx=rint(((ConstellationArray[2*i]+CSTRANGE)/CSTSPAN)*(float)contentsRect().width());
+        posy=contentsRect().height()-rint(((ConstellationArray[2*i+1]+CSTRANGE)/CSTSPAN)*(float)contentsRect().height());
+        qpainter.drawEllipse(posx,posy,2,2);
+    }
+  QFrame::paintEvent(e);
+ }
+
+void drmConstellationFrame::setConstellation(econstellation constellation)
+{
+  if (MSCAvailable && constellation==MSC)
+    {
+      lmsc=lMSC;
+      for (int i=0; i < lMSC; i++)
+        {
+          ConstellationArray[i]=MSC_cells_sequence[i];
+        }
+      update();
+      MSCAvailable=false;
+    }
+  if (FACAvailable  && constellation==FAC)
+    {
+      lmsc=lFAC;
+      for (int i=0; i < lmsc; i++)
+        {
+          ConstellationArray[i]=FAC_cells_sequence[i];
+        }
+      update();
+      FACAvailable=false;
+    }
+}
+
+void drmConstellationFrame::clearConstellation()
+{
+  lmsc=0;
+  update();
+}
diff --git a/qsstv/drmrx/drmconstellationframe.h b/qsstv/drmrx/drmconstellationframe.h
new file mode 100644
index 0000000..bf25427
--- /dev/null
+++ b/qsstv/drmrx/drmconstellationframe.h
@@ -0,0 +1,28 @@
+#ifndef DRMCONSTELLATIONFRAME_H
+#define DRMCONSTELLATIONFRAME_H
+
+#include <QFrame>
+
+enum econstellation {FAC,MSC};
+
+namespace Ui {
+class drmConstellationFrame;
+}
+
+class drmConstellationFrame : public QFrame
+{
+  Q_OBJECT
+  
+public:
+  explicit drmConstellationFrame(QWidget *parent = 0);
+  ~drmConstellationFrame();
+  void setConstellation(econstellation constellation);
+  void clearConstellation();
+private:
+  Ui::drmConstellationFrame *ui;
+ void paintEvent (QPaintEvent *);
+ float ConstellationArray[2 * 2959];
+ int lmsc;
+};
+
+#endif // DRMCONSTELLATIONFRAME_H
diff --git a/qsstv/drmrx/drmconstellationframe.ui b/qsstv/drmrx/drmconstellationframe.ui
new file mode 100644
index 0000000..dfb4cfc
--- /dev/null
+++ b/qsstv/drmrx/drmconstellationframe.ui
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>drmConstellationFrame</class>
+ <widget class="QFrame" name="drmConstellationFrame">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="palette">
+   <palette>
+    <active>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>60</red>
+        <green>60</green>
+        <blue>60</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </active>
+    <inactive>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>60</red>
+        <green>60</green>
+        <blue>60</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </inactive>
+    <disabled>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </disabled>
+   </palette>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::Panel</enum>
+  </property>
+  <property name="frameShadow">
+   <enum>QFrame::Sunken</enum>
+  </property>
+  <property name="lineWidth">
+   <number>3</number>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/drmrx/drmdatainput.cpp b/qsstv/drmrx/drmdatainput.cpp
new file mode 100644
index 0000000..08f173c
--- /dev/null
+++ b/qsstv/drmrx/drmdatainput.cpp
@@ -0,0 +1,879 @@
+#include "drmdatainput.h"
+#include "qsstvglobal.h"
+#include "dsp/filter.h"
+#include "dsp/downsamplefilter.h"
+#include "drm.h"
+#include "drmproto.h"
+#include "sound/soundio.h"
+#include "utils/supportfunctions.h"
+#include "dsp/filterparam.h"
+
+
+#define FILTERLENGTH 8
+//#define HILBERT83
+#define HILBERT151
+//#define HILBERT360
+#ifdef HILBERT83
+#define HILBERTTAPS 83
+#endif
+
+#ifdef HILBERT151
+#define HILBERTTAPS 151
+#endif
+
+#ifdef HILBERT360
+#define HILBERTTAPS 360
+#endif
+
+//static float subfilt1[FILTERLENGTH] =
+
+//{
+//  -6.304691e-004,
+//  2.369513e-003,
+//  -9.174512e-003,
+//  3.102797e-002,
+//  2.437302e-001,
+//  -2.230165e-002,
+//  6.759297e-003,
+//  -1.587494e-003
+//};
+
+
+//static float subfilt2[FILTERLENGTH] =
+//{
+//  -1.818568e-003,
+//  8.332497e-003,
+//  -2.973091e-002,
+//  1.111435e-001,
+//  1.924554e-001,
+//  -3.981645e-002,
+//  1.180361e-002,
+//  -2.561942e-003
+//};
+
+//static float subfilt3[FILTERLENGTH] =
+//{
+//  -2.561942e-003,
+//  1.180361e-002,
+//  -3.981645e-002,
+//  1.924554e-001,
+//  1.111435e-001,
+//  -2.973091e-002,
+//  8.332497e-003,
+//  -1.818568e-003
+//};
+
+//static float subfilt4[FILTERLENGTH] =
+//{
+//  -1.587494e-003,
+//  6.759297e-003,
+//  -2.230165e-002,
+//  2.437302e-001,
+//  3.102797e-002,
+//  -9.174512e-003,
+//  2.369513e-003,
+//  -6.304691e-004
+//};
+
+//static float B_Inphase[HILBERTTAPS] =
+//{
+//  -0.000013096429498,
+//  0.000000663887755,
+//  -0.000069635287780,
+//  -0.000011931707517,
+//  -0.000144533153108,
+//  -0.000025373980217,
+//  -0.000391789228659,
+//  -0.000232426477943,
+//  -0.000478143393960,
+//  -0.000487558968789,
+//  -0.000685650336825,
+//  -0.001354704880460,
+//  -0.000546201344479,
+//  -0.002342397998132,
+//  -0.000346673289958,
+//  -0.004288405547670,
+//  0.000076514567549,
+//  -0.006214057213030,
+//  0.000471140764452,
+//  -0.008869259301553,
+//  0.000310928936475,
+//  -0.010853542810835,
+//  -0.000618723760850,
+//  -0.012558669622822,
+//  -0.003528318293013,
+//  -0.012565638823234,
+//  -0.008458796942648,
+//  -0.010998473138919,
+//  -0.016909533624041,
+//  -0.007029340421051,
+//  -0.028648813220546,
+//  -0.000916074544044,
+//  -0.045239295406484,
+//  0.007126861010101,
+//  -0.067146558987974,
+//  0.016470713456071,
+//  -0.099200587662524,
+//  0.025927955112860,
+//  -0.157545675961710,
+//  0.034759206835915,
+//  -0.403756659009051,
+//  0.872044314623357,
+//  0.298152956415124,
+//  0.045762401414113,
+//  0.057682372386628,
+//  0.046612304642337,
+//  0.009812557897304,
+//  0.044738697480695,
+//  -0.007877026958678,
+//  0.039793922738241,
+//  -0.014019542011307,
+//  0.033363547774391,
+//  -0.014228247558443,
+//  0.025586601071249,
+//  -0.011722710151015,
+//  0.018169441690637,
+//  -0.007774072820479,
+//  0.011227152592688,
+//  -0.004254206945683,
+//  0.005868718139545,
+//  -0.001002308783387,
+//  0.001901375136305,
+//  0.000729336127630,
+//  -0.000472570900136,
+//  0.001969539594901,
+//  -0.001560255803054,
+//  0.001905716822635,
+//  -0.001878235948282,
+//  0.001813958429404,
+//  -0.001475079548897,
+//  0.001117144708583,
+//  -0.001143142395249,
+//  0.000739068897549,
+//  -0.000539580485663,
+//  0.000277728151810,
+//  -0.000341625345467,
+//  0.000100860733753,
+//  -0.000052333537017,
+//  0.000001973643242,
+//  -0.000040013168559,
+//  -0.000013316614262,
+//  0.000006340573991,
+//  -0.000004973750149
+//};
+
+//static float B_Quad[HILBERTTAPS] =
+//  { 0.000000000000000, -0.000006581551523, -0.000014192774668,
+//0.000038355453153, -0.000061469802717, 0.000046155098672,
+//  -0.000269873790862, 0.000273874035623, -0.000496517495085,
+//    0.000383120218360, -0.001080412050939, 0.000679569009380,
+//    -0.001431859472243,
+//  0.000632904747289, -0.002103194937170, 0.000269803608080,
+//    -0.002028650339142, -0.000864482682011, -0.001935633229621,
+//    -0.003130320749406,
+//  -0.000660757624173, -0.006511157948451, 0.000829477180331,
+//    -0.011499838339182, 0.003150251021340, -0.017295118095470,
+//    0.004931046332886,
+//  -0.024156288476640, 0.005730140929231, -0.030544691199518,
+//    0.003619186767370, -0.036441773169957, -0.003417440897749,
+//    -0.040091021199004,
+//  -0.019051201727783, -0.041600273509535, -0.051332869304505,
+//    -0.039743071204460, -0.127032560999817, -0.035198771562362,
+//    -0.488057960837085,
+//  -0.584664836150859, 0.558860293285832, -0.018787192344116,
+//    0.193986224985834, -0.008891772659445, 0.111263353226189,
+//    0.000562232648398,
+//  0.069350999397921, 0.008632835092782, 0.043147713646936,
+//    0.014687263334153, 0.025127872636149, 0.018102917799341,
+//    0.013466436394485,
+//  0.019348490199617, 0.005952331932605, 0.018192510371929,
+//    0.002067567347773, 0.015985137219339, 0.000257349155187,
+//    0.012513166744168,
+//  -0.000036691339210, 0.009393580004274, 0.000299271454802,
+//    0.006076795140808, 0.000699657108499, 0.003864640455567,
+//    0.001119449029703,
+//  0.001926535004930, 0.001049068709402, 0.000995120503174,
+//    0.001044597409952, 0.000305528639009, 0.000630886520545,
+//    0.000111000803475,
+//  0.000464927709514, 0.000005944149118, 0.000157049271650,
+//    -0.000003528848644, 0.000069808136642, 0.000001885388254,
+//    0.000012115208419,
+//};
+
+#ifdef HILBERT83
+
+// blFIR  Hilbert
+// localSamplingrate = 12000.000000
+// number of taps = 83
+// corner frequency = 1000.000000
+// beta = 0.500000
+// window applied
+//Coefficients:
+const FILTERPARAMTYPE hilbert[HILBERTTAPS]=
+{
+
+     0.00195121951219512,
+     0,
+     0.00218951295028307,
+     0,
+     0.00274155836904681,
+     0,
+     0.00365039553708327,
+     0,
+     0.00496219678931242,
+     0,
+     0.00672774409495607,
+     0,
+     0.00900470037777058,
+     0,
+     0.01186106975868,
+     0,
+     0.0153804894372836,
+     0,
+     0.0196704386862718,
+     0,
+     0.024875273452504,
+     0,
+     0.0311975987201718,
+     0,
+     0.0389347935888508,
+     0,
+     0.0485447715399211,
+     0,
+     0.0607723902308125,
+     0,
+     0.0769136201887418,
+     0,
+     0.0994316691908993,
+     0,
+     0.13362885988773,
+     0,
+     0.193330225501393,
+     0,
+     0.329299964994593,
+     0,
+     0.998650268544521,
+     0,
+     -0.998650268544521,
+     0,
+     -0.329299964994593,
+     0,
+     -0.193330225501393,
+     0,
+     -0.13362885988773,
+     0,
+     -0.0994316691908993,
+     0,
+     -0.0769136201887418,
+     0,
+     -0.0607723902308125,
+     0,
+     -0.0485447715399211,
+     0,
+     -0.0389347935888508,
+     0,
+     -0.0311975987201718,
+     0,
+     -0.024875273452504,
+     0,
+     -0.0196704386862718,
+     0,
+     -0.0153804894372836,
+     0,
+     -0.01186106975868,
+     0,
+     -0.00900470037777058,
+     0,
+     -0.00672774409495607,
+     0,
+     -0.00496219678931242,
+     0,
+     -0.00365039553708327,
+     0,
+     -0.00274155836904681,
+     0,
+     -0.00218951295028307,
+     0,
+     -0.00195121951219512
+  };
+#endif
+
+#ifdef HILBERT151
+
+// blFIR  Hilbert
+// localSamplingrate = 12000.000000
+// number of taps = 151
+// corner frequency = 1000.000000
+// beta = 0.500000
+// window applied
+//Coefficients:
+
+const FILTERPARAMTYPE hilbert[HILBERTTAPS]=
+{
+     0.00106666666666667,
+     0,
+     0.00111799020198943,
+     0,
+     0.00121749046405025,
+     0,
+     0.00136886588233087,
+     0,
+     0.00157592342822028,
+     0,
+     0.00184260137668314,
+     0,
+     0.00217299884412417,
+     0,
+     0.00257141359464515,
+     0,
+     0.00304238999373074,
+     0,
+     0.00359077949800265,
+     0,
+     0.00422181674681683,
+     0,
+     0.00494121522949637,
+     0,
+     0.00575528773156199,
+     0,
+     0.00667109844581354,
+     0,
+     0.00769665596238367,
+     0,
+     0.0088411596130561,
+     0,
+     0.0101153162732349,
+     0,
+     0.0115317513868608,
+     0,
+     0.0131055477183722,
+     0,
+     0.0148549598147661,
+     0,
+     0.0168023740886606,
+     0,
+     0.0189756183245283,
+     0,
+     0.0214097779833233,
+     0,
+     0.0241497635092426,
+     0,
+     0.0272540175599975,
+     0,
+     0.0308,
+     0,
+     0.0348925322232539,
+     0,
+     0.0396769064421141,
+     0,
+     0.045360270349271,
+     0,
+     0.0522481015058711,
+     0,
+     0.0608098544941651,
+     0,
+     0.0718051968979872,
+     0,
+     0.0865479463372845,
+     0,
+     0.107521909278733,
+     0,
+     0.140052424123536,
+     0,
+     0.19798957926751,
+     0,
+     0.332124254201553,
+     0,
+     0.999596501845475,
+     0,
+     -0.999596501845475,
+     0,
+     -0.332124254201553,
+     0,
+     -0.19798957926751,
+     0,
+     -0.140052424123536,
+     0,
+     -0.107521909278733,
+     0,
+     -0.0865479463372845,
+     0,
+     -0.0718051968979872,
+     0,
+     -0.0608098544941651,
+     0,
+     -0.0522481015058711,
+     0,
+     -0.045360270349271,
+     0,
+     -0.0396769064421141,
+     0,
+     -0.0348925322232539,
+     0,
+     -0.0308,
+     0,
+     -0.0272540175599975,
+     0,
+     -0.0241497635092426,
+     0,
+     -0.0214097779833233,
+     0,
+     -0.0189756183245283,
+     0,
+     -0.0168023740886606,
+     0,
+     -0.0148549598147661,
+     0,
+     -0.0131055477183722,
+     0,
+     -0.0115317513868608,
+     0,
+     -0.0101153162732349,
+     0,
+     -0.0088411596130561,
+     0,
+     -0.00769665596238367,
+     0,
+     -0.00667109844581354,
+     0,
+     -0.00575528773156199,
+     0,
+     -0.00494121522949637,
+     0,
+     -0.00422181674681683,
+     0,
+     -0.00359077949800265,
+     0,
+     -0.00304238999373074,
+     0,
+     -0.00257141359464515,
+     0,
+     -0.00217299884412417,
+     0,
+     -0.00184260137668314,
+     0,
+     -0.00157592342822028,
+     0,
+     -0.00136886588233087,
+     0,
+     -0.00121749046405025,
+     0,
+     -0.00111799020198943,
+     0,
+     -0.00106666666666667
+  };
+#endif
+
+#ifdef HILBERT360
+
+// blFIR  Hilbert
+// localSamplingrate = 12000.000000
+// number of taps = 360
+// corner frequency = 8000.000000
+// beta = 1.000000
+// window applied
+//Coefficients:
+
+const FILTERPARAMTYPE hilbert[HILBERTTAPS]=
+{
+   0.000447320955694772,
+   0,
+   0.000455558927413046,
+   0,
+   0.000467201131289238,
+   0,
+   0.000482357657235191,
+   0,
+   0.000501139723312327,
+   0,
+   0.000523659757475149,
+   0,
+   0.000550031490215828,
+   0,
+   0.000580370059009746,
+   0,
+   0.000614792125543957,
+   0,
+   0.000653416006801779,
+   0,
+   0.000696361821178391,
+   0,
+   0.000743751650915635,
+   0,
+   0.000795709722270862,
+   0,
+   0.000852362604976147,
+   0,
+   0.000913839432702755,
+   0,
+   0.000980272146423472,
+   0,
+   0.00105179576276502,
+   0,
+   0.0011285486696673,
+   0,
+   0.001210672951919,
+   0,
+   0.0012983147494246,
+   0,
+   0.00139162465137996,
+   0,
+   0.00149075812989859,
+   0,
+   0.00159587601704457,
+   0,
+   0.00170714502969781,
+   0,
+   0.00182473834721175,
+   0,
+   0.00194883624743331,
+   0,
+   0.0020796268073506,
+   0,
+   0.0022173066754303,
+   0,
+   0.00236208192361964,
+   0,
+   0.00251416898803742,
+   0,
+   0.00267379570858608,
+   0,
+   0.00284120247911163,
+   0,
+   0.00301664352135053,
+   0,
+   0.00320038829777273,
+   0,
+   0.00339272308060342,
+   0,
+   0.00359395269683864,
+   0,
+   0.00380440247202873,
+   0,
+   0.00402442039907038,
+   0,
+   0.00425437956232035,
+   0,
+   0.00449468085214296,
+   0,
+   0.00474575601067502,
+   0,
+   0.0050080710563162,
+   0,
+   0.00528213014245216,
+   0,
+   0.00556847991546485,
+   0,
+   0.00586771444852104,
+   0,
+   0.00618048084138014,
+   0,
+   0.00650748559305694,
+   0,
+   0.00684950187428514,
+   0,
+   0.00720737785119832,
+   0,
+   0.00758204624155527,
+   0,
+   0.00797453532156124,
+   0,
+   0.00838598164664784,
+   0,
+   0.00881764480575967,
+   0,
+   0.00927092459873636,
+   0,
+   0.00974738111417594,
+   0,
+   0.0102487582958671,
+   0,
+   0.0107770117263209,
+   0,
+   0.0113343415352682,
+   0,
+   0.0119232315715625,
+   0,
+   0.0125464962755426,
+   0,
+   0.0132073370786259,
+   0,
+   0.0139094106696869,
+   0,
+   0.0146569121483854,
+   0,
+   0.015454676997362,
+   0,
+   0.0163083070387749,
+   0,
+   0.0172243272273486,
+   0,
+   0.0182103824647697,
+   0,
+   0.019275486886001,
+   0,
+   0.0204303427008687,
+   0,
+   0.0216877523409707,
+   0,
+   0.0230631574055806,
+   0,
+   0.0245753523839949,
+   0,
+   0.0262474430643373,
+   0,
+   0.028108153434625,
+   0,
+   0.0301936384591154,
+   0,
+   0.0325500469449935,
+   0,
+   0.0352372234345217,
+   0,
+   0.0383341869762954,
+   0,
+   0.0419474683535509,
+   0,
+   0.0462242114069099,
+   0,
+   0.0513735488362621,
+   0,
+   0.0577030667580263,
+   0,
+   0.0656844408554181,
+   0,
+   0.0760796596826624,
+   0,
+   0.0902049460455389,
+   0,
+   0.110546573465011,
+   0,
+   0.142432367889117,
+   0,
+   0.199714813347055,
+   0,
+   0.333186579940791,
+   0,
+   0.999982386895462,
+   0,
+   -0.999982386895462,
+   0,
+   -0.333186579940791,
+   0,
+   -0.199714813347055,
+   0,
+   -0.142432367889117,
+   0,
+   -0.110546573465011,
+   0,
+   -0.0902049460455389,
+   0,
+   -0.0760796596826624,
+   0,
+   -0.0656844408554181,
+   0,
+   -0.0577030667580263,
+   0,
+   -0.0513735488362621,
+   0,
+   -0.0462242114069099,
+   0,
+   -0.0419474683535509,
+   0,
+   -0.0383341869762954,
+   0,
+   -0.0352372234345217,
+   0,
+   -0.0325500469449935,
+   0,
+   -0.0301936384591154,
+   0,
+   -0.028108153434625,
+   0,
+   -0.0262474430643373,
+   0,
+   -0.0245753523839949,
+   0,
+   -0.0230631574055806,
+   0,
+   -0.0216877523409707,
+   0,
+   -0.0204303427008687,
+   0,
+   -0.019275486886001,
+   0,
+   -0.0182103824647697,
+   0,
+   -0.0172243272273486,
+   0,
+   -0.0163083070387749,
+   0,
+   -0.015454676997362,
+   0,
+   -0.0146569121483854,
+   0,
+   -0.0139094106696869,
+   0,
+   -0.0132073370786259,
+   0,
+   -0.0125464962755426,
+   0,
+   -0.0119232315715625,
+   0,
+   -0.0113343415352682,
+   0,
+   -0.0107770117263209,
+   0,
+   -0.0102487582958671,
+   0,
+   -0.00974738111417594,
+   0,
+   -0.00927092459873636,
+   0,
+   -0.00881764480575967,
+   0,
+   -0.00838598164664784,
+   0,
+   -0.00797453532156124,
+   0,
+   -0.00758204624155527,
+   0,
+   -0.00720737785119832,
+   0,
+   -0.00684950187428514,
+   0,
+   -0.00650748559305694,
+   0,
+   -0.00618048084138014,
+   0,
+   -0.00586771444852104,
+   0,
+   -0.00556847991546485,
+   0,
+   -0.00528213014245216,
+   0,
+   -0.0050080710563162,
+   0,
+   -0.00474575601067502,
+   0,
+   -0.00449468085214296,
+   0,
+   -0.00425437956232035,
+   0,
+   -0.00402442039907038,
+   0,
+   -0.00380440247202873,
+   0,
+   -0.00359395269683864,
+   0,
+   -0.00339272308060342,
+   0,
+   -0.00320038829777273,
+   0,
+   -0.00301664352135053,
+   0,
+   -0.00284120247911163,
+   0,
+   -0.00267379570858608,
+   0,
+   -0.00251416898803742,
+   0,
+   -0.00236208192361964,
+   0,
+   -0.0022173066754303,
+   0,
+   -0.0020796268073506,
+   0,
+   -0.00194883624743331,
+   0,
+   -0.00182473834721175,
+   0,
+   -0.00170714502969781,
+   0,
+   -0.00159587601704457,
+   0,
+   -0.00149075812989859,
+   0,
+   -0.00139162465137996,
+   0,
+   -0.0012983147494246,
+   0,
+   -0.001210672951919,
+   0,
+   -0.0011285486696673,
+   0,
+   -0.00105179576276502,
+   0,
+   -0.000980272146423472,
+   0,
+   -0.000913839432702755,
+   0,
+   -0.000852362604976147,
+   0,
+   -0.000795709722270862,
+   0,
+   -0.000743751650915635,
+   0,
+   -0.000696361821178391,
+   0,
+   -0.000653416006801779,
+   0,
+   -0.000614792125543957,
+   0,
+   -0.000580370059009746,
+   0,
+   -0.000550031490215828,
+   0,
+   -0.000523659757475149,
+   0,
+   -0.000501139723312327,
+   0,
+   -0.000482357657235191,
+   0,
+   -0.000467201131289238,
+   0,
+   -0.000455558927413046,
+   0,
+   -0.000447320955694772,
+   0
+};
+#endif
+
+
+//double lowpass[4*FILTERLENGTH];
+
+drmDataInput::drmDataInput(int blockSize)
+{
+  iqFilter=NULL;
+  init(blockSize);
+}
+
+drmDataInput::~drmDataInput()
+{
+//  delete downFilter;
+  delete iqFilter;
+}
+
+void drmDataInput::init(int blockSize)
+{
+
+  if(iqFilter) delete iqFilter;
+  iqFilter=new filter(0,hilbert,HILBERTTAPS,0,48000,false,1);
+  inputResampler.init(blockSize);
+  iterationCounter=0;
+}
+
+
+unsigned int drmDataInput::getData(DSPFLOAT *inputBuf,DSPFLOAT *outputBuf,DSPFLOAT rRation)
+{
+  int processedSamples;
+  inputResampler.resample(rRation,inputBuf);
+  processedSamples=inputResampler.rxBuffer.count();
+  inputResampler.rxBuffer.copy(outputBuf,processedSamples);
+  inputResampler.rxBuffer.skip(processedSamples);
+  iqFilter->processIQ(inputBuf,outputBuf,processedSamples);
+  return (processedSamples);
+}
+
+
+
diff --git a/qsstv/drmrx/drmdatainput.h b/qsstv/drmrx/drmdatainput.h
new file mode 100644
index 0000000..26a3b63
--- /dev/null
+++ b/qsstv/drmrx/drmdatainput.h
@@ -0,0 +1,29 @@
+#ifndef DRMDATAINPUT_H
+#define DRMDATAINPUT_H
+#include "qsstvglobal.h"
+#include "qsstvdefs.h"
+#include "drmrx/drmdefs.h"
+#include "sound/resampler.h"
+
+#define RESAMPLINGLEN 2048
+
+class filter;
+class downsampleFilter;
+
+class drmDataInput
+{
+public:
+  drmDataInput(int blockSize);
+  ~drmDataInput();
+//  void doResample(DSPFLOAT rRation,short int *inputBuffer);
+  unsigned int getData(DSPFLOAT *inputBuf, DSPFLOAT *outputBuf, DSPFLOAT rRation);
+  void init(int blockSize);
+
+private:
+//  downsampleFilter *downFilter;
+  filter *iqFilter;
+  resampler inputResampler;
+  int iterationCounter;
+};
+
+#endif // DRMDATAINPUT_H
diff --git a/qsstv/drmrx/drmdefs.h b/qsstv/drmrx/drmdefs.h
new file mode 100644
index 0000000..bd2f4a7
--- /dev/null
+++ b/qsstv/drmrx/drmdefs.h
@@ -0,0 +1,40 @@
+/*
+*    File drmdefs.h
+*
+*    M.Bos - PA0MBO
+*    Date Feb 21st 2009
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#define DRMBUFSIZE 50000
+#define Ts_A 320
+#define Tu_A 288
+#define Tg_A 32
+#define Ts_B 320
+#define Tu_B 256
+#define Tg_B 64
+#define Ts_C 240
+#define Tu_C 160
+#define Tg_C 64
+#define Ts_D 200
+#define Tu_D 112
+#define Tg_D 88
+#define RUN_STATE_POWER_ON -2
+#define RUN_STATE_INIT -1
+#define RUN_STATE_FIRST 0
+#define RUN_STATE_NORMAL  1
+
diff --git a/qsstv/drmrx/drmproto.h b/qsstv/drmrx/drmproto.h
new file mode 100644
index 0000000..207cd32
--- /dev/null
+++ b/qsstv/drmrx/drmproto.h
@@ -0,0 +1,97 @@
+#ifndef DRMPROTO_H
+#define DRMPROTO_H
+/*
+*   file drmproto.h
+*
+*   defines the protoypes for project RXAMADRM
+*
+*   PA0MBO - M.BOS
+*
+*   Date Feb 21st 2009
+*
+*/  
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+struct smode_info
+{
+  int mode_indx;
+   float time_offset;
+   float sample_rate_offset;
+   float freq_offset_fract;
+};
+
+struct soutblock_ofdm
+{
+  int OKflag;
+   float phifcl;
+   int dtoffsI;
+   int dfoffsI;
+   float foffs;
+   float toffsfr;
+ };
+
+void init_audio (void);
+//int monorec (short int *,int n);
+//void drmfilter (float *, float *, float *, float *, int, int);
+int demodulate ( /*@null@ */ float *, int, int);
+void drmfilter1c (float *, float *, float *, int, int);
+void drmfilter1 (float *, float *, float *, int, int);
+void initGetmode(int n);
+void getmode (/*@null@ */ float *, int, /*@null@ *//*@out@ */ smode_info *);
+float getfoffsint (float *, int, int, int, int);
+int resample (float *, /*@out@ */ float *, float, int, int);
+int getofdm ( /*@null@ */ float *, float, float, float, int, int, 
+              /*@null@ */ float *, /*@null@ */ float *, int, int, int);
+int getsymbolidx (float *, int, int *, int *, int, int, int);
+int getofdmsync ( /*@null@ */ float *, int, int, /*@null@ */ float *, int, 
+                  float, /*@null@ */ float *, /*@null@ */ float *, int, int, int);
+int mkfacmap (int, int, int, int /*@out@ */ *);
+void showFAC ( /*@null@ */ float *, int, /*@null@ */ char argv[], int argc, int);
+int *deinterleaver (int, int, int, int);
+int msdhardfac ( /*@out@ */ double *, /*@out@ */ double *, int, /*@out@ */ double *, int, double *, 
+                 int, int, int *, int *, int, int, double *);
+int mkmscmap (int, int, int, int, int);
+int msdhardsdc (double *, double *, int, double *, int, double *, int, int, int, int *, int *, int, int, double *);
+//void crc16_c (double *, double *, int);
+
+
+
+/*void copy_mplex_desc(struct mplex_desc *, struct mplex_desc *);
+int cmp_mplex_desc(struct mplex_desc *, struct mplex_desc *); */ 
+int msdhardmsc (double *, double *, int, double *, int, double *, int, int, int, int *, int *, int, int, /*@out@ */ double *,
+                /*@out@ */ double *, 
+                double *, double *, double *);
+void bits2bytes (double *, int, unsigned char /*@out@ */ *);
+void crc16_bytewise (double /*@out@ */ *, unsigned char *, long);
+int deflate_uncompress (unsigned char *, int, char /*@out@ */ *, int, 
+                         unsigned char /*@out@ */ *, int *, double /*@out@ */ *);
+float **matrix (long, long, long, long);
+int *ivector (long, long);
+float *fvector (long, long);
+void channel_decoding (void);
+void source_decoding (void);
+void crc8_c (double *, double *, int);
+void psdmean (float *, float *, int, int);
+void psdcmean (float *, float *, int, int);
+struct drmComplex
+{
+  float re;
+  float im;
+};
+
+#endif
+
+
diff --git a/qsstv/drmrx/drmpsdframe.cpp b/qsstv/drmrx/drmpsdframe.cpp
new file mode 100644
index 0000000..049544b
--- /dev/null
+++ b/qsstv/drmrx/drmpsdframe.cpp
@@ -0,0 +1,71 @@
+#include "drmpsdframe.h"
+#include "ui_drmpsdframe.h"
+#include <QPainter>
+#include "drm.h"
+#include "math.h"
+
+#define PSDRANGE 70.
+#define PSDLOW  0.
+
+drmPSDFrame::drmPSDFrame(QWidget *parent) :
+  QFrame(parent),
+  ui(new Ui::drmPSDFrame)
+{
+  ui->setupUi(this);
+}
+
+drmPSDFrame::~drmPSDFrame()
+{
+  delete ui;
+}
+
+void drmPSDFrame::paintEvent (QPaintEvent *e)
+{
+    int i,x1,x2,y1,y2;
+    float y;
+    QPainter qpainter (this);
+
+    //qpainter.drawRect (contentsRect());
+    qpainter.setPen (QPen (Qt::blue, 1));
+    qpainter.drawLine (contentsRect().x()+contentsRect().width()/2, contentsRect().y(), contentsRect().x()+contentsRect().width()/2,contentsRect().y()+contentsRect().height());
+    qpainter.drawLine (contentsRect().x(), contentsRect().y()+contentsRect().height()/2, contentsRect().x()+contentsRect().width(),contentsRect().y()+contentsRect().height()/2);
+    // draw PSD Info
+    x1=0;
+    y1=contentsRect().height();
+    for(i=0;i<PSDSPAN/4;i++)
+    {
+        y=psd[i]-PSDLOW;
+        if (y>PSDRANGE) y=PSDRANGE;
+        y2=contentsRect().height()-rint((y/PSDRANGE) *(float)contentsRect().height());
+        x2=(i*contentsRect().width())/(PSDSPAN/4);
+        qpainter.drawLine(x1,y1,x2,y2);
+        x1=x2;y1=y2;
+    }
+       qpainter.setPen (QPen (Qt::red, 1));
+       x1=0;
+       y1=contentsRect().height();
+       for(i=0;i<PSDSPAN/4;i++)
+       {
+           y=cpsd[i]-PSDLOW;
+           if (y>PSDRANGE) y=PSDRANGE;
+           y2=contentsRect().height()-rint((y/PSDRANGE) *(float)contentsRect().height());
+           x2=(i*contentsRect().width())/(PSDSPAN/4);
+           qpainter.drawLine(x1,y1,x2,y2);
+           x1=x2;y1=y2;
+       }
+
+  QFrame::paintEvent(e);
+ }
+
+void drmPSDFrame::setPSD()
+{
+   for(int i=0;i<PSDSPAN/4;i++)
+    {
+        psdArray[i]=psd[i];
+    }
+   for(int i=0;i<PSDSPAN/4;i++)
+    {
+        psdCArray[i]=cpsd[i];
+    }
+   update();
+}
diff --git a/qsstv/drmrx/drmpsdframe.h b/qsstv/drmrx/drmpsdframe.h
new file mode 100644
index 0000000..054c918
--- /dev/null
+++ b/qsstv/drmrx/drmpsdframe.h
@@ -0,0 +1,27 @@
+#ifndef DRMPSDFRAME_H
+#define DRMPSDFRAME_H
+
+#include <QFrame>
+#define PSDSPAN 512
+
+namespace Ui {
+class drmPSDFrame;
+}
+
+class drmPSDFrame : public QFrame
+{
+  Q_OBJECT
+  
+public:
+  explicit drmPSDFrame(QWidget *parent = 0);
+  ~drmPSDFrame();
+  void setPSD();
+  
+private:
+  Ui::drmPSDFrame *ui;
+  void paintEvent (QPaintEvent *);
+  float psdArray[PSDSPAN];
+  float psdCArray[PSDSPAN/4];
+};
+
+#endif // DRMPSDFRAME_H
diff --git a/qsstv/drmrx/drmpsdframe.ui b/qsstv/drmrx/drmpsdframe.ui
new file mode 100644
index 0000000..3b3a6bc
--- /dev/null
+++ b/qsstv/drmrx/drmpsdframe.ui
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>drmPSDFrame</class>
+ <widget class="QFrame" name="drmPSDFrame">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>390</width>
+    <height>239</height>
+   </rect>
+  </property>
+  <property name="palette">
+   <palette>
+    <active>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>60</red>
+        <green>60</green>
+        <blue>60</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </active>
+    <inactive>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>60</red>
+        <green>60</green>
+        <blue>60</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>76</red>
+        <green>76</green>
+        <blue>76</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </inactive>
+    <disabled>
+     <colorrole role="WindowText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Light">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>180</red>
+        <green>180</green>
+        <blue>180</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Dark">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="Text">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+     <colorrole role="ButtonText">
+      <brush brushstyle="SolidPattern">
+       <color alpha="255">
+        <red>0</red>
+        <green>0</green>
+        <blue>0</blue>
+       </color>
+      </brush>
+     </colorrole>
+    </disabled>
+   </palette>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::Panel</enum>
+  </property>
+  <property name="frameShadow">
+   <enum>QFrame::Sunken</enum>
+  </property>
+  <property name="lineWidth">
+   <number>3</number>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/drmrx/drmstatusframe.cpp b/qsstv/drmrx/drmstatusframe.cpp
new file mode 100644
index 0000000..cfa209e
--- /dev/null
+++ b/qsstv/drmrx/drmstatusframe.cpp
@@ -0,0 +1,253 @@
+#include "drmstatusframe.h"
+#include "ui_drmstatusframe.h"
+#include "drm.h"
+#include "qsstvglobal.h"
+#include "drmrx/demodulator.h"
+#include <math.h>
+#include "configparams.h"
+
+drmStatusFrame::drmStatusFrame(QWidget *parent) :
+  QFrame(parent),
+  ui(new Ui::drmStatusFrame)
+{
+  ui->setupUi(this);
+  greenPXM=new QPixmap(40,30);
+  greenPXM->fill(Qt::green);
+  redPXM=new QPixmap(40,30);
+  redPXM->fill(Qt::red);
+  yellowPXM=new QPixmap(40,30);
+  yellowPXM->fill(Qt::yellow);
+  init();
+}
+
+drmStatusFrame::~drmStatusFrame()
+{
+  delete ui;
+}
+
+void drmStatusFrame::init()
+{
+  mode="";
+  bandwidth=0;
+  interleave="";
+  protection="";
+  qam=0;
+  call="";
+  currentSegment=0;
+  transportID=0;
+  totalSeg=0;
+  rxSeg=0;
+  ui->mscLED->setPixmap(*redPXM);
+  ui->facLED->setPixmap(*redPXM);
+  ui->frameLED->setPixmap(*redPXM);
+  ui->timeLED->setPixmap(*redPXM);
+}
+
+void drmStatusFrame::paintEvent (QPaintEvent *)
+{
+  int freqOff;
+  ui->modeEdit->setText(mode);
+  ui->bandwidthEdit->setText(QString::number(bandwidth));
+  ui->interleaveEdit->setText(interleave);
+  ui->protectionEdit->setText(protection);
+  ui->qamEdit->setText(QString::number(qam));
+  ui->snrEdit->setText(QString::number(WMERFAC,'g',2)+" dB");
+  freqOff=(int)round(freqOffset-350);
+  ui->offsetEdit->setText(QString::number(freqOff)+" Hz");
+  //  ui->dfsEdit->setText(QString::number(deltaFS,'g',3) + " ppm");
+  ui->totalSegmentsEdit->setText(QString::number(totalSeg));
+  ui->blocksReceivedFrame->setMaxBlocks(totalSeg);
+  ui->rxSegmentsEdit->setText(QString::number(rxSeg));
+  ui->callEdit->setText(call.toUpper());
+  ui->transportIDEdit->setText(QString::number(transportID));
+  ui->currentSegmentEdit->setText(QString::number(currentSegment));
+  ui->blocksReceivedFrame->setBlocks(drmBlockList);
+}
+
+void drmStatusFrame::setStatus()
+{
+
+  if(demodulatorPtr->isTimeSync())
+    {
+      ui->timeLED->setPixmap(*greenPXM);
+    }
+  else
+    {
+      ui->timeLED->setPixmap(*redPXM);
+      ui->facLED->setPixmap(*redPXM);
+      ui->frameLED->setPixmap(*redPXM);
+      ui->mscLED->setPixmap(*redPXM);
+      // update();
+      return;
+    }
+  currentSegment=currentSegmentNumber;
+  transportID=rxTransportID;
+  totalSeg=bodyTotalSegments;
+  rxSeg=rxSegments;
+  switch(robustness_mode)
+    {
+    case 0: mode="A"; break;
+    case 1: mode="B"; break;
+    case 2: mode="E"; break;
+    default: mode=""; break;
+    }
+  if(callsignValid)
+    {
+      call=drmCallsign;
+    }
+
+  if(fac_valid==1)
+    {
+      ui->facLED->setPixmap(*greenPXM);
+    }
+  else
+    {
+      ui->facLED->setPixmap(*redPXM);
+    }
+
+  switch(msc_valid)
+    {
+
+    case INVALID:  ui->mscLED->setPixmap(*redPXM); break;
+    case VALID: ui->mscLED->setPixmap(*greenPXM); break;
+    case ALREADYRECEIVED: ui->mscLED->setPixmap(*yellowPXM); break;
+    }
+  if(demodulatorPtr->isFrameSync())
+    {
+      ui->frameLED->setPixmap(*greenPXM);
+    }
+  else
+    {
+      ui->frameLED->setPixmap(*redPXM);
+    }
+  if(demodulatorPtr->isTimeSync())
+    {
+      ui->timeLED->setPixmap(*greenPXM);
+    }
+  else
+    {
+      ui->timeLED->setPixmap(*redPXM);
+    }
+
+  if(mode=="") return;
+  switch(spectrum_occupancy_new)
+    {
+    case 0: bandwidth=2.3; break;
+    case 1: bandwidth=2.5;; break;
+    default:bandwidth=0; break;
+    }
+  switch (multiplex_description.PL_PartB)
+    {
+    case 0: protection="High"; break;
+    case 1: protection="Low"; break;
+    default: ; break;
+    }
+  switch(interleaver_depth_new)
+    {
+    case 0: interleave="Long"; break;
+    case 1: interleave="Short"; break;
+    default: ; break;
+    }
+
+  switch(msc_mode_new)
+    {
+    case 0: qam=64; break;
+    case 1: qam=16; break;
+    case 3: qam=4; break;
+    default: qam=0; break;
+    }
+  ui->totalSegmentsEdit->setText(QString::number(bodyTotalSegments));
+  ui->rxSegmentsEdit->setText(QString::number(rxSegments));
+  update();
+}
+
+
+QString modeToString(uint mode)
+{
+  QString tmp;
+  tmp+="Mode: ";
+  switch(mode/10000)
+    {
+    case 0: tmp+="A"; break;
+    case 1: tmp+="B"; break;
+    case 2: tmp+="E"; break;
+    default: tmp+="-"; break;
+    }
+  tmp+="\nBW: ";
+  mode-=(mode/10000)*10000;
+  switch(mode/1000)
+    {
+    case 0: tmp+="2.3"; break;
+    case 1: tmp+="2.5";; break;
+    default:tmp+="---"; break;
+    }
+  tmp+="\nProt: ";
+  mode-=(mode/1000)*1000;
+  switch(mode/100)
+    {
+    case 0: tmp+="High"; break;
+    case 1: tmp+="Low"; break;
+    default:tmp+="---" ; break;
+    }
+
+  tmp+="\nQAM: ";
+  mode-=(mode/100)*100;
+  switch(mode/10)
+    {
+    case 0: tmp+="4"; break;
+    case 1: tmp+="16"; break;
+    case 2: tmp+="64"; break;
+    default: tmp+="--"; break;
+    }
+  return tmp;
+}
+
+QString compactModeToString(uint mode)
+{
+  QString tmp;
+  switch(mode/10000)
+    {
+    case 0: tmp+="A"; break;
+    case 1: tmp+="B"; break;
+    case 2: tmp+="E"; break;
+    default: tmp+="-"; break;
+    }
+  tmp+="/"; // bandwidth
+  mode-=(mode/10000)*10000;
+  switch(mode/1000)
+    {
+    case 0: tmp+="2.3"; break;
+    case 1: tmp+="2.5";; break;
+    default:tmp+="---"; break;
+    }
+  tmp+="/";
+  mode-=(mode/1000)*1000;
+
+  switch(mode/100)
+    {
+    case 0: tmp+="Hi"; break;
+    case 1: tmp+="Lo"; break;
+    default:tmp+="--" ; break;
+    }
+
+  tmp+="/";
+  mode-=(mode/100)*100;
+  switch(mode/10)
+    {
+    case 0: tmp+="4"; break;
+    case 1: tmp+="16"; break;
+    case 2: tmp+="64"; break;
+    default: tmp+="--"; break;
+    }
+  tmp+="/";
+  switch(mode&1)
+    {
+    case 0: tmp+="Long"; break;
+    case 1: tmp+="Short"; break;
+    default:tmp+="--" ; break;
+    }
+
+
+  return tmp;
+}
+
diff --git a/qsstv/drmrx/drmstatusframe.h b/qsstv/drmrx/drmstatusframe.h
new file mode 100644
index 0000000..d1119ff
--- /dev/null
+++ b/qsstv/drmrx/drmstatusframe.h
@@ -0,0 +1,45 @@
+#ifndef DRMSTATUSFRAME_H
+#define DRMSTATUSFRAME_H
+
+#include <QFrame>
+#include <QPixmap>
+
+class demodulator;
+
+namespace Ui {
+class drmStatusFrame;
+}
+
+class drmStatusFrame : public QFrame
+{
+  Q_OBJECT
+  
+public:
+  explicit drmStatusFrame(QWidget *parent = 0);
+  ~drmStatusFrame();
+  void init();
+  void setStatus();
+
+private:
+  Ui::drmStatusFrame *ui;
+  void paintEvent (QPaintEvent *);
+  QString mode;
+  float bandwidth;
+  QString interleave;
+  QString protection;
+  int qam;
+  QString call;
+  QPixmap *greenPXM;
+  QPixmap *redPXM;
+  QPixmap *yellowPXM;
+  int currentSegment;
+  int transportID;
+  int totalSeg;
+  int rxSeg;
+};
+
+QString modeToString(uint mode);
+QString compactModeToString(uint mode);
+
+
+#endif // DRMSTATUSFRAME_H
diff --git a/qsstv/drmrx/drmstatusframe.ui b/qsstv/drmrx/drmstatusframe.ui
new file mode 100644
index 0000000..63e38aa
--- /dev/null
+++ b/qsstv/drmrx/drmstatusframe.ui
@@ -0,0 +1,1157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>drmStatusFrame</class>
+ <widget class="QFrame" name="drmStatusFrame">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>415</width>
+    <height>399</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::StyledPanel</enum>
+  </property>
+  <property name="frameShadow">
+   <enum>QFrame::Raised</enum>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="margin">
+    <number>1</number>
+   </property>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="3" column="1">
+      <widget class="QLabel" name="protectionEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="3">
+      <widget class="QLabel" name="rxSegmentsEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QLabel" name="offsetEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="snrLabel">
+       <property name="text">
+        <string>SNR</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="bandwidthLabel">
+       <property name="text">
+        <string>Bandwidth</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="3">
+      <widget class="QLabel" name="totalSegmentsEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QLabel" name="totalSegmentsLabel">
+       <property name="text">
+        <string>Total Segm.</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QLabel" name="rxSegmentsLabel">
+       <property name="text">
+        <string>RX  Sgmnts</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QLabel" name="snrEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="bandwidthEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLabel" name="qamEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="interleaveEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="3">
+      <widget class="QLabel" name="currentSegmentEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="interleaveLabel">
+       <property name="text">
+        <string>InterLeave</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="protectionLabel">
+       <property name="text">
+        <string>Protection</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="3">
+      <widget class="QLabel" name="transportIDEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="modeEdit">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="offsetLabel">
+       <property name="text">
+        <string>Offset</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="modeLabel">
+       <property name="text">
+        <string>Mode</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="qamLabel">
+       <property name="text">
+        <string>QAM</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="2">
+      <widget class="QLabel" name="curSegmentLabel">
+       <property name="text">
+        <string>Cur. Sgmnt</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="2">
+      <widget class="QLabel" name="transportIDLabel">
+       <property name="text">
+        <string>Transp. ID</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="2">
+      <widget class="QLabel" name="callsignLabel">
+       <property name="text">
+        <string>Callsign</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="3">
+      <widget class="QLabel" name="callEdit">
+       <property name="font">
+        <font>
+         <pointsize>12</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="blockView" name="blocksReceivedFrame">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>18</height>
+      </size>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::StyledPanel</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="lineWidth">
+      <number>3</number>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <property name="spacing">
+        <number>1</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QLabel" name="timeLEDLabel">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string>TIME</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLabel" name="frameLedLabel">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string>FRAME</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="facLEDLabel">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string>FAC</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLabel" name="mscLEDLabel">
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string>MSC</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="timeLED">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>1000</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <italic>false</italic>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Panel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Raised</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLabel" name="frameLED">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>1000</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <italic>false</italic>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Panel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Raised</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QLabel" name="facLED">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>1000</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <italic>false</italic>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Panel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Raised</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLabel" name="mscLED">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>1000</width>
+           <height>12</height>
+          </size>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>60</red>
+               <green>60</green>
+               <blue>60</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>76</red>
+               <green>76</green>
+               <blue>76</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="WindowText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Light">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>180</red>
+               <green>180</green>
+               <blue>180</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Dark">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Text">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="ButtonText">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>50</red>
+               <green>50</green>
+               <blue>50</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+           <weight>75</weight>
+           <italic>false</italic>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Panel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Raised</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>0</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>blockView</class>
+   <extends>QFrame</extends>
+   <header>widgets/blockview.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/drmrx/filter1.cpp b/qsstv/drmrx/filter1.cpp
new file mode 100644
index 0000000..4146da1
--- /dev/null
+++ b/qsstv/drmrx/filter1.cpp
@@ -0,0 +1,50 @@
+
+/*
+*    File filter1.c
+*
+* implements filter routine a la MATLAB
+* for real data in vector sigin[]
+*
+* PA0MBO - M.Bos
+* Date Feb 21st 2009
+*
+* result is stored in y[]
+* dataLen is number of elements in input (sigin) and mih is length of
+* the filter vector h[]
+*
+*/
+
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+
+void drmfilter1(float *sigin, float *out, float *coef, int dataLen, int coefLen)
+{
+  int i, j;
+
+
+  for (i = 0; i < dataLen; i++)
+
+    {
+      out[i] = 0.0;
+      for (j = 0; ((j <= i) && (j < coefLen)); j++)
+
+        {
+          out[i] += coef[j] * sigin[i - j];
+        }
+    }
+}
diff --git a/qsstv/drmrx/filter1c.cpp b/qsstv/drmrx/filter1c.cpp
new file mode 100644
index 0000000..09b4168
--- /dev/null
+++ b/qsstv/drmrx/filter1c.cpp
@@ -0,0 +1,50 @@
+
+/*
+*    File filter1c.c
+*
+* implements filter routine a la MATLAB
+*
+* PA0MBO - M.Bos
+* Date Feb 21st 2009
+*
+* input signal is complex and should be stored
+* real 1 / imag  component 2 , real 2, imag2 etc..
+* 
+* result is stored in y[] in the same manner (y[] is alsdo complex)
+* dataLen is number of elements in input (sigin) and coefLen is length of
+* the filter vector h[]
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+#include <stdio.h>
+void drmfilter1c(float *sigin, float *y, float *coef, int dataLen, int coefLen)
+{
+  int i, j;
+
+  for (i = 0; i < dataLen; i++)
+
+    {
+      y[i * 2] = 0.0;
+      y[i * 2 + 1] = 0.0;
+      for (j = 0; ((j <= i) && (j < coefLen)); j++)
+
+        {
+          y[i * 2] += coef[j] * sigin[(i - j) * 2];
+          y[i * 2 + 1] += coef[j] * sigin[(i - j) * 2 + 1];
+        }
+    }
+}
diff --git a/qsstv/drmrx/fixform.cpp b/qsstv/drmrx/fixform.cpp
new file mode 100644
index 0000000..f0681bf
--- /dev/null
+++ b/qsstv/drmrx/fixform.cpp
@@ -0,0 +1,44 @@
+#include "fixform.h"
+#include "ui_fixform.h"
+#include "drmstatusframe.h"
+#include "qsstvglobal.h"
+#include "drmrx/drm.h"
+#include "configparams.h"
+
+
+fixForm::fixForm(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::fixForm)
+{
+  ui->setupUi(this);
+}
+
+fixForm::~fixForm()
+{
+  delete ui;
+}
+
+void fixForm::setInfoInternal(int mode, QString fileName, int missing, QByteArray *ba)
+{
+  common(mode,fileName,missing);
+//  ui->infoTextEdit->appendPlainText("BSR for a picture you send");
+  ui->previewWidget->openImage(ba);
+}
+
+void fixForm::setInfoExternal(int mode, QString fileName, int missing)
+{
+  common(mode,fileName,missing);
+  ui->infoTextEdit->appendPlainText("BSR for a picture you received");
+  ui->previewWidget->openImage(fileName,false,false);
+}
+
+void fixForm::common(int mode,QString fileName,int missing)
+{
+  ui->filenameLineEdit->setText("Filename: "+fileName+"\n");
+  if(callsignValid)
+    {
+      ui->infoTextEdit->appendPlainText("From: "+ QString(drmCallsign));
+     }
+  ui->infoTextEdit->appendPlainText(modeToString(mode));
+  ui->infoTextEdit->appendPlainText(QString("Requested segments: %1").arg(missing));
+}
diff --git a/qsstv/drmrx/fixform.h b/qsstv/drmrx/fixform.h
new file mode 100644
index 0000000..434c199
--- /dev/null
+++ b/qsstv/drmrx/fixform.h
@@ -0,0 +1,25 @@
+#ifndef FIXFORM_H
+#define FIXFORM_H
+
+#include <QDialog>
+
+namespace Ui {
+class fixForm;
+}
+
+class fixForm : public QDialog
+{
+  Q_OBJECT
+  
+public:
+  explicit fixForm(QWidget *parent = 0);
+  ~fixForm();
+  void setInfoInternal(int mode, QString fileName, int missing, QByteArray *ba);
+  void setInfoExternal(int mode, QString fileName, int missing);
+  
+private:
+  Ui::fixForm *ui;
+  void common(int mode, QString fileName, int missing);
+};
+
+#endif // FIXFORM_H
diff --git a/qsstv/drmrx/fixform.ui b/qsstv/drmrx/fixform.ui
new file mode 100644
index 0000000..be1f804
--- /dev/null
+++ b/qsstv/drmrx/fixform.ui
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>fixForm</class>
+ <widget class="QDialog" name="fixForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>501</width>
+    <height>299</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>FIX</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLineEdit" name="filenameLineEdit"/>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout" stretch="2,1">
+     <item>
+      <widget class="QPlainTextEdit" name="infoTextEdit"/>
+     </item>
+     <item>
+      <widget class="imageViewer" name="previewWidget" native="true">
+       <property name="minimumSize">
+        <size>
+         <width>200</width>
+         <height>120</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>200</width>
+         <height>120</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="blockView" name="blocksReceivedFrame">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>18</height>
+      </size>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::StyledPanel</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="lineWidth">
+      <number>3</number>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelPushButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="fixPushButton">
+       <property name="text">
+        <string>Fix</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>blockView</class>
+   <extends>QFrame</extends>
+   <header>widgets/blockview.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>imageViewer</class>
+   <extends>QWidget</extends>
+   <header>widgets/imageviewer.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>cancelPushButton</sender>
+   <signal>clicked()</signal>
+   <receiver>fixForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>205</x>
+     <y>275</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>250</x>
+     <y>149</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>fixPushButton</sender>
+   <signal>clicked()</signal>
+   <receiver>fixForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>296</x>
+     <y>275</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>250</x>
+     <y>149</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/drmrx/getfoffsint.cpp b/qsstv/drmrx/getfoffsint.cpp
new file mode 100644
index 0000000..a7eea97
--- /dev/null
+++ b/qsstv/drmrx/getfoffsint.cpp
@@ -0,0 +1,163 @@
+
+/*
+*
+*    file getfoffsint.c
+*
+*    implements get_frequency_offset_integer function
+*    from diorama MATLAB code
+*
+*    Author M.Bos - PA0MBO
+*
+*    Date Feb 21st 2009
+*
+*    output:  freq_offset_integer (float)
+*    inputs:  symbol_buffer (filled bu getofdm), N_symbols, K_dc,
+*             K_modulo, Tu
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define PI (4.0*atan(1.0))
+struct cmplxnmbr
+{
+  float re;
+  float im;
+};
+float
+getfoffsint(float *symbolbuf, int N_symbols, int K_dc, int K_modulo, int Tu)
+{
+  int i, j;
+  struct cmplxnmbr S[288][30];	/* Tu x 30 ? pa0mbo? */
+  float dS_sum[288 * 2];
+  float tmp1, tmp2;
+  int k_pilot1, k_pilot2, k_pilot3;
+  float abs_dS_sum[288], pilot_indicator[288];
+  float dummy, freq_offset_integer;
+  int K_dc_offset;
+
+
+  /* reshape symbolbuf to matrix S */
+  for (i = 0; i < N_symbols; i++)
+
+    {
+      for (j = 0; j < K_modulo; j++)
+
+	{
+	  (S[j][i]).re = symbolbuf[(j + i * K_modulo) * 2];
+	  (S[j][i]).im = symbolbuf[(j + i * K_modulo) * 2 + 1];
+	}
+    }
+
+  /* now accumulate phase diffs all carriers */
+  for (i = 0; i < Tu; i++)
+
+    {
+      dS_sum[i * 2] = 0.0;
+      dS_sum[i * 2 + 1] = 0.0;
+    }
+  for (i = 1; i < N_symbols; i++)
+
+    {
+      for (j = 0; j < Tu; j++)
+
+	{
+	  tmp1 = (S[j][i - 1]).re * (S[j][i]).re +	/* real ac + bd */
+	    (S[j][i - 1]).im * (S[j][i]).im;
+	  tmp2 = (S[j][i - 1]).im * (S[j][i]).re -	/* imag. bc - ad */
+	    (S[j][i - 1]).re * (S[j][i]).im;
+	  dS_sum[j * 2] += cos(atan2(tmp2, tmp1));
+	  dS_sum[j * 2 + 1] += sin(atan2(tmp2, tmp1));
+	}
+    }
+
+  /* detect pilots */
+
+
+  k_pilot1 = (int) ceil((float) (9 * Tu / 288));
+  k_pilot2 = (int) ceil((float) (27 * Tu / 288));
+  k_pilot3 = (int) ceil((float) (36 * Tu / 288));
+
+
+
+  for (i = 0; i < Tu; i++)
+
+    {
+      abs_dS_sum[i] =
+	(float) sqrt(dS_sum[i * 2] * dS_sum[i * 2] +
+		     dS_sum[i * 2 + 1] * dS_sum[i * 2 + 1]);
+
+
+    }
+
+  for (i = 0; i < Tu - k_pilot1; i++)
+
+    {
+      pilot_indicator[i] = abs_dS_sum[k_pilot1 + i];
+    }
+  for (i = 0; i < k_pilot1; i++)
+
+    {
+      pilot_indicator[i + Tu - k_pilot1] = abs_dS_sum[i];
+    }
+  for (i = 0; i < Tu - k_pilot2; i++)
+
+    {
+      pilot_indicator[i] += abs_dS_sum[k_pilot2 + i];
+    }
+  for (i = 0; i < k_pilot2; i++)
+
+    {
+      pilot_indicator[i + Tu - k_pilot2] += abs_dS_sum[i];
+    }
+  for (i = 0; i < Tu - k_pilot3; i++)
+
+    {
+      pilot_indicator[i] += abs_dS_sum[k_pilot3 + i];
+    }
+  for (i = 0; i < k_pilot3; i++)
+
+    {
+      pilot_indicator[i + Tu - k_pilot3] += abs_dS_sum[i];
+    }
+
+  /* Now find max pilot_indicator and index */
+  dummy = -1.0E20;
+  K_dc_offset = 0;
+
+
+  for (i = 0; i < Tu; i++)
+
+    {
+
+
+      if (pilot_indicator[i] > dummy)
+
+	{
+	  dummy = pilot_indicator[i];
+	  K_dc_offset = i;
+	}
+    }
+
+  K_dc_offset = ((K_dc_offset - K_dc + Tu / 2 + Tu) % Tu) - Tu / 2;
+  freq_offset_integer = (float) (2 * PI * K_dc_offset);
+  return (freq_offset_integer);
+}
diff --git a/qsstv/drmrx/getmode.cpp b/qsstv/drmrx/getmode.cpp
new file mode 100644
index 0000000..fd266bf
--- /dev/null
+++ b/qsstv/drmrx/getmode.cpp
@@ -0,0 +1,314 @@
+
+/*
+*    File getmode.c
+*
+*    Author M.Bos - PA0MBO
+*    Date Feb 21st 2009
+*
+*    routine to determine robustness mode
+*    of baseband drm signal (complex)
+*    in "in"  real and imag components
+*    stored alternatively in sequence
+*
+*    returns number of the mode A=0, B=1, etc...
+*    input param. n is number of samples in rsbuf
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <malloc.h>
+#include "structtemplates.h"
+#include "drmproto.h"
+#include "drmdefs.h"
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+
+static /*@only@ */ float *in_, *abs_in_, *abs_in_in_;
+static /*@only@ */ float *conv_in_, *conv_abs_in_in_;
+static bool initDone=false;
+
+void initGetmode(int n)
+{
+  if(initDone) return;
+  initDone=true;
+  /* create and initialize  arrays */
+  in_ =(float *) malloc((n - Tu_D) * 2 * sizeof(float));	/* complex data */
+  if (in_ == NULL)
+
+    {
+      printf("mem alloc problem in getmode \n");
+      exit(EXIT_FAILURE);
+    }
+  conv_in_ = (float *)malloc((n - Tu_D) * 2 * sizeof(float));	/* complex */
+  if (conv_in_ == NULL)
+
+    {
+      printf("mem alloc problem in getmode \n");
+      exit(EXIT_FAILURE);
+    }
+  abs_in_ = (float *)malloc(n * sizeof(float));
+  if (abs_in_ == NULL)
+
+    {
+      printf("mem alloc problem in getmode \n");
+      exit(EXIT_FAILURE);
+    }
+  abs_in_in_ = (float *)malloc((n - Tu_D) * sizeof(float));
+  if (abs_in_in_ == NULL)
+
+    {
+      printf("mem alloc problem in getmode \n");
+      exit(EXIT_FAILURE);
+    }
+  conv_abs_in_in_ = (float *)malloc((n - Tu_D) * sizeof(float));
+  if (conv_abs_in_in_ == NULL)
+
+    {
+      printf("mem alloc problem in getmode \n");
+      exit(EXIT_FAILURE);
+    }
+  return;
+}				/* end initializations */
+
+
+void getmode(float *input, int n, smode_info * result)
+{
+  float EPSILON = 1.0E-10;
+  float SNR_mode_det = (float) (exp(15.0 * log(10) / 10.0));
+  float rho;
+  int Ts_list[4] = { Ts_A, Ts_B, Ts_C, Ts_D };
+  int Tu_list[4] = { Tu_A, Tu_B, Tu_C, Tu_D };
+  float max_abs_gamma_rel_list[] = { 0.0, 0.0, 0.0, 0.0 };
+  int theta_list[] = { 0, 0, 0, 0 };
+  float epsilon_ML_list[] = { 0.0, 0.0, 0.0, 0.0 };
+  int N_symbols_mode_det, Ts, Tu, Tg, t_smp;
+
+  int mode, i, j, theta, maxOK;
+  float max_abs_gamma_rel, tmpmax, epsilon_ML;
+  //  float frequency_offset_fract;
+  int b[20], time_offset_mean;
+  float sumx, sumy, sumxx, sumxy, slope, boffs;
+  //  float a[20];
+  float gamma[2 * Ts_A], Phi[Ts_A];
+  float my_rect[Ts_D - Tu_D];
+
+  rho = SNR_mode_det / (SNR_mode_det + 1);
+  N_symbols_mode_det = ((n + 1) / Ts_A) - 1;
+  for (mode = 0; mode < 3; mode++) // ON4QZ 3 was 4
+    {
+      Ts = Ts_list[mode];
+      Tu = Tu_list[mode];
+      Tg = Ts - Tu;
+      t_smp = 0;
+
+      /* initialize arrays with zero's */
+      for (i = 0; i < Ts; i++)
+        {
+          gamma[i * 2] = 0.0;
+          gamma[i * 2 + 1] = 0.0;
+          Phi[i] = 0.0;
+        }
+      for (i = 0; i < n - Tu; i++)	/* complex mult */
+        {
+          in_[2 * i] = input[2 * i] * input[(i + Tu) * 2] + input[2 * i + 1] * input[(i + Tu) * 2 +1];
+          in_[2 * i + 1] = -input[2 * i] * input[(i + Tu) * 2 + 1] + input[2 * i + 1] * input[(i + Tu) * 2];
+        }
+//      arrayDump("gM1",input,16,true);
+      my_rect[0] = 0.5; // ON4QZ
+      for (i = 1; i < Tg; i++)
+        {
+          my_rect[i] = 1.0;
+        }
+//       my_rect[Tg-1] = 0.5; // ON4QZ
+
+      drmfilter1c(in_, conv_in_, my_rect, n - Tu, Tg);
+      for (i = 0; i < n; i++)
+        {
+          abs_in_[i] = input[i * 2] * input[i * 2] + input[i * 2 + 1] * input[i * 2 + 1];
+        }
+      for (i = 0; i < n - Tu; i++)
+        {
+          abs_in_in_[i] = abs_in_[i] + abs_in_[i + Tu];
+        }
+      drmfilter1(abs_in_in_, conv_abs_in_in_, my_rect, n - Tu, Tg);
+      for (j = 0; j < N_symbols_mode_det; j++)
+        {
+          for (i = 0; i < Ts; i++)
+            {
+              gamma[i * 2] = gamma[i * 2] + conv_in_[(t_smp + Tg + i - 1) * 2];	/* pa0mbo -1 ios nieuw */
+              gamma[i * 2 + 1] = gamma[i * 2 + 1] + conv_in_[(t_smp + Tg + i - 1) * 2 + 1];
+              Phi[i] =  Phi[i] +  (float) (0.5 * (EPSILON + conv_abs_in_in_[t_smp + Tg + i - 1]));
+            }
+          t_smp += Ts;
+        }
+      /* detmn max and index in abs(gamma .. rho*Phi) */
+      theta = 0;
+      max_abs_gamma_rel = -1.0E20;
+
+      /* debugging
+         printf("==== mode %d === gamma\n",mode);   */
+      for (i = 0; i < Ts; i++)
+        {
+          tmpmax = (float) sqrt(gamma[2 * i] * gamma[2 * i] + gamma[2 * i + 1] * gamma[2 * i + 1]);
+
+          /* printf("%g\n", tmpmax);  */
+          tmpmax -= rho * Phi[i];
+          if (tmpmax > max_abs_gamma_rel)
+            {
+              max_abs_gamma_rel = tmpmax;
+              theta = i;
+            }
+        }
+   //   arrayDump("gM2",gamma,Ts*2,true);
+
+      /*  printf("===============\n");    */
+      max_abs_gamma_rel = (float) sqrt(gamma[theta * 2] * gamma[theta * 2] + gamma[theta * 2 + 1] * gamma[theta * 2 + 1]) / (rho * Phi[theta]);
+      epsilon_ML = (float) atan2(gamma[2 * theta], gamma[2 * theta + 1]);
+      max_abs_gamma_rel_list[mode] = max_abs_gamma_rel;
+      theta_list[mode] = theta;
+      epsilon_ML_list[mode] = epsilon_ML;
+    }
+  /* debugging
+  printf("max gamma list %g %g %g %g \n", max_abs_gamma_rel_list[0],
+   max_abs_gamma_rel_list[1], max_abs_gamma_rel_list[2],
+   max_abs_gamma_rel_list[3]);
+  printf("theta list %d %d %d %d \n", theta_list[0], theta_list[1],
+   theta_list[2], theta_list[3]);
+  printf("epsilon ML  list %g %g %g %g \n", epsilon_ML_list[0], epsilon_ML_list[1], epsilon_ML_list[2], epsilon_ML_list[3]);	 end debug info */
+
+  /* now decide for particular mode */
+  max_abs_gamma_rel = -1.0E20;
+  for (i = 0; i < 4; i++)
+    {
+      if (max_abs_gamma_rel_list[i] > max_abs_gamma_rel)
+        {
+          max_abs_gamma_rel = max_abs_gamma_rel_list[i];
+          mode = i;
+        }
+    }
+//              <<max_abs_gamma_rel_list[1] << max_abs_gamma_rel_list[2] << max_abs_gamma_rel_list[3];
+  /* check if result is reliable */
+  maxOK = 1;			/* start with reliable */
+  if (max_abs_gamma_rel > 0.6) // was 0.6
+    {
+      for (i = 0; i < 3; i++)  //ON4QZ 3 was 4
+        {
+          if ((i != mode) && (max_abs_gamma_rel_list[i] > 0.6))
+            {
+              maxOK = 0;
+            }
+        }
+    }
+  else
+    {
+      maxOK = 0;
+    }
+  if (maxOK == 0)
+    {
+      result->mode_indx = 99;
+      result->time_offset = 0.0;
+      result->sample_rate_offset = 0.0;
+      result->freq_offset_fract = 0.0;
+      return;
+    }
+  else
+    {
+      addToLog("max mode ok",LOGDRMDEMOD);
+      Ts = Ts_list[mode];
+      Tu = Tu_list[mode];
+      Tg = Ts - Tu;
+      time_offset_mean = theta_list[mode];	/* pa0mbo checked  +1 removed on Dec 22nd 2006  */
+      //      frequency_offset_fract = epsilon_ML_list[mode];
+      /* now recalculate several vars with the established mode */
+      for (i = 0; i < n - Tu; i++)
+        {
+          in_[2 * i] = input[2 * i] * input[(i + Tu) * 2] + input[2 * i + 1] * input[(i + Tu) * 2 + 1];
+          in_[2 * i + 1] = -input[2 * i] * input[(i + Tu) * 2 + 1] + input[2 * i + 1] * input[(i + Tu) * 2];
+        }
+      my_rect[0] = 0.5;
+      for (i = 1; i < Tg; i++)
+        {
+          my_rect[i] = 1.0;
+        }
+      my_rect[Tg - 1] = 0.5;
+      drmfilter1c(in_, conv_in_, my_rect, n - Tu, Tg);
+      for (i = 0; i < n; i++)
+        {
+          abs_in_[i] = input[i * 2] * input[i * 2] + input[i * 2 + 1] * input[i * 2 + 1];
+        }
+      for (i = 0; i < n - Tu; i++)
+       {
+          abs_in_in_[i] = abs_in_[i] + abs_in_[i + Tu];
+        }
+      drmfilter1(abs_in_in_, conv_abs_in_in_, my_rect, n - Tu, Tg);
+      t_smp = Tg + time_offset_mean + Ts / 2;
+      for (j = 0; j < (N_symbols_mode_det - 2); j++)
+        {
+          max_abs_gamma_rel = -1.0E20;
+          for (i = 0; i < Ts; i++)
+            {
+              gamma[i * 2] = conv_in_[(t_smp + i) * 2];
+              gamma[i * 2 + 1] = conv_in_[(t_smp + i) * 2 + 1];
+              Phi[i] = (float) (0.5 * (EPSILON + conv_abs_in_in_[t_smp + i]));
+
+              /* detmn max and its indx */
+              tmpmax =
+                  (float) sqrt(gamma[2 * i] * gamma[2 * i] +
+                               gamma[2 * i + 1] * gamma[2 * i + 1]) -
+                  rho * Phi[i];
+              if (tmpmax > max_abs_gamma_rel)
+
+                {
+                  max_abs_gamma_rel = tmpmax;
+                  //                  a[j] = tmpmax;
+                  b[j] = i;
+                }
+            }
+          t_smp += Ts;
+        }
+
+      /* Now least squares to 0...N_symbols_mode_det-3 and b[0] .. */
+      sumx = 0.0;
+      sumy = 0.0;
+      sumxx = 0.0;
+      sumxy = 0.0;
+      for (i = 0; i < N_symbols_mode_det - 2; i++)
+
+        {
+          sumx += (float) i;
+          sumy += (float) b[i];
+          sumxx += (float) i *(float) i;
+          sumxy += (float) i *(float) b[i];
+        }
+      slope = (float) (((N_symbols_mode_det - 2) * sumxy - sumx * sumy) / ((N_symbols_mode_det - 2) * sumxx - sumx * sumx));
+      boffs = (float) ((sumy * sumxx - sumx * sumxy) / ((N_symbols_mode_det - 2) * sumxx - sumx * sumx));
+    }
+  /* printf("in getmode N_symbols_mode_det %d \n", N_symbols_mode_det);
+  printf("mode is %d toffs %g samplroffs %g f_offs_fract %g\n", mode, fmod((boffs + Ts / 2 + time_offset_mean - 1), (float) Ts), slope / ((float) Ts), epsilon_ML_list[mode]);	 */
+
+  /* pa0mbo todo reliability check */
+  result->mode_indx = mode;
+  result->time_offset = fmodf((boffs + Ts / 2 + time_offset_mean - 1), (float) Ts);	/* fp rest pa0mbo was -2  */
+  result->sample_rate_offset = slope / ((float) Ts);
+  result->freq_offset_fract = epsilon_ML_list[mode];
+//logfile->addToAux(QString("%1 %2 %3 %4").arg( mode).arg(result->time_offset).arg(result->sample_rate_offset).arg(result->freq_offset_fract));
+
+}
diff --git a/qsstv/drmrx/getofdm.cpp b/qsstv/drmrx/getofdm.cpp
new file mode 100644
index 0000000..ceaa25b
--- /dev/null
+++ b/qsstv/drmrx/getofdm.cpp
@@ -0,0 +1,332 @@
+
+/*
+*     File getofdm.c
+*    
+*     Author PA0MBO - M.BOS
+*     DATE Feb 21st 2009
+*
+*     implements more or less the get_ofdm_symbol 
+*     MATLAB routine from diorama-1.1.1
+*
+*     
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fftw3.h>
+#include "drmproto.h"
+#include "qsstvglobal.h"
+
+#define PI (atan(1.0)*4.0)
+int getofdm( /*@null@ */ float *rs, float time_offset_fractional_init,
+             float freq_offset_init, float delta_time_offset_I_init, int Ts,
+             int Tu, /*@null@ */ float Zi[], /*@null@ */ float *out,
+             int init, int TIME_SYNC_ENABLE, int FREQ_SYNC_ENABLE)
+{
+  float EPSILON = 1.0E-10;
+  float max_theta = 10.0;
+  //  float max_time_offset_ctrl = 10.0;
+  //  float max_delta_time_offset = 2.0;
+  float kP_small_timing_controller = 0.01;
+  float kP_large_timing_controller = 0.01;
+  float threshold_timing_small_large = 2.0;
+  float kI_timing_controller = 0.00020;
+  float kP_small_freq_controller = 0.750;
+  float threshold_freq_small_large = 0.75;
+  float kI_freq_controller = 0.0008;
+  int Tg, Tgh;
+  float phi_freq_correction_last;
+  float delta_time_offset_I;
+  float delta_time_offset_P;
+  float dfreq_offset_I;
+  float freq_offset, time_offset_fractional;
+  int i;
+  float temp1[] = { 0.0, 0.0 }, temp2, temp3, temp4;
+  float temp[] = { 0.0, 0.0 };
+  float temp5[] = { 0.0, 0.0 };
+  float temp6, temp7;
+  float temp8[] = { 0.0, 0.0 };
+  float temp9, temp10;
+  float theta_plus, theta_minus, delta_theta;
+  float time_offset_ctrl;
+  float delta_time_offset;
+  int delta_time_offset_integer;
+  float epsilon_ML, freq_offset_ctrl, dfreq_offset_P;
+  float kP_large_freq_controller, dfreq_offset;
+  float tmptheta, term1;
+  static float *exp_temp;
+  static float *out1;
+
+  static drmComplex s[288], S[288];	/* 288 = max Tu */
+  static fftwf_plan p = NULL;
+
+  if (init == 1)
+
+    {
+
+      /* malloc space for arrays */
+      if ((exp_temp = (float *)malloc(Tu * 2 * sizeof(float))) == NULL)
+
+        {
+          printf("cannot malloc space for exp_temp in get_ofdm_symbol\n");
+          exit(EXIT_FAILURE);
+        }
+      if ((out1 = (float *)malloc(Tu * 2 * sizeof(float))) == NULL)
+
+        {
+          printf("cannot malloc space for out1 in get_ofdm_symbol\n");
+          exit(EXIT_FAILURE);
+        }
+      if (p != NULL)
+
+        {
+          fftwf_destroy_plan(p);
+        }
+      p = fftwf_plan_dft_1d(Tu,(fftwf_complex *)s,(fftwf_complex *)S,FFTW_FORWARD, FFTW_ESTIMATE);
+      return (0);
+    }
+
+  else
+
+    {
+
+      /* fixed parameters */
+      EPSILON = 1.0E-10;
+      max_theta = 5.0;
+      //      max_time_offset_ctrl = 10.0;
+      //      max_delta_time_offset = 2.0;
+      kP_small_timing_controller = 0.01;
+      kP_large_timing_controller = 0.01;
+      threshold_timing_small_large = 2.0;
+      kI_timing_controller = 0.00005;
+      kP_small_freq_controller = 0.05;
+      kP_large_freq_controller = 0.75;
+      threshold_freq_small_large = 0.5;
+      kI_freq_controller = 0.0008;
+      Tg = Ts - Tu;
+      Tgh = (int) floor(Tg / 2 + 0.5);
+      if (Zi[0] < 0.0)
+
+        {
+          phi_freq_correction_last = 0.0;
+          delta_time_offset_I = delta_time_offset_I_init;
+          dfreq_offset_I = 0.0;
+          freq_offset = freq_offset_init;
+          time_offset_fractional = time_offset_fractional_init;
+        }
+
+      else
+
+        {
+          phi_freq_correction_last = Zi[1];
+          delta_time_offset_I = Zi[2];
+          dfreq_offset_I = Zi[3];
+          freq_offset = Zi[4];
+          time_offset_fractional = Zi[5];
+        }
+      if (TIME_SYNC_ENABLE == 1)
+
+        {
+          temp1[0] = 0.0;
+          temp1[1] = 0.0;
+          temp2 = 0.0;
+          temp3 = 0.0;
+          temp5[0] = 0.0;
+          temp5[1] = 0.0;
+          temp6 = 0.0;
+          temp7 = 0.0;
+          temp8[0] = 0.0;
+          temp8[1] = 0.0;
+          temp9 = 0.0;
+          temp10 = 0.0;
+          for (i = 0; i < Tg + 2; i++)
+
+            {
+//              logfile->addToAux(QString("tm1 %1 %2 %3 %4 %5").arg(rs[i * 2] ).arg(rs[(Tu + i) * 2]).arg(rs[i * 2 + 1]).arg(rs[(Tu + i) * 2 + 1]).arg(temp1[0]));
+              temp1[0] += rs[i * 2] * rs[(Tu + i) * 2] + rs[i * 2 + 1] * rs[(Tu + i) * 2 + 1];	/* real part */
+              temp1[1] += -rs[i * 2] * rs[(Tu + i) * 2 + 1] + rs[i * 2 + 1] * rs[(Tu + i) * 2];	/* imag part */
+              temp2 += rs[i * 2] * rs[i * 2] + rs[i * 2 + 1] * rs[i * 2 + 1];
+              temp3 += rs[(i + Tu) * 2] * rs[(i + Tu) * 2] + rs[(i + Tu) * 2 + 1] * rs[(i + Tu) * 2 + 1];
+            }
+          temp4 = (float) (EPSILON + 0.5 * (temp2 + temp3));
+//          logfile->addToAux(QString("tmp %1 %2 %3 %4 %5").arg(temp1[0]).arg(temp1[1]).arg(temp2).arg(temp3).arg(temp4));
+
+          /* time offset measurement : theta */
+          for (i = 0; i < 5; i++)
+
+            {
+              temp5[0] += rs[i * 2] * rs[(Tu + i) * 2] + rs[i * 2 + 1] * rs[(Tu + i) * 2 + 1];	/* 1-5 * Tu_1_5 */
+              temp5[1] += -rs[i * 2] * rs[(Tu + i) * 2 + 1] + rs[i * 2 +1] * rs[(Tu + i) * 2];
+              temp6 += rs[i * 2] * rs[i * 2] + rs[i * 2 + 1] * rs[i * 2 + 1];	/* 1-5 * 1-5' */
+              temp7 += rs[(i + Tu) * 2] * rs[(i + Tu) * 2] + rs[(i + Tu) * 2 + 1] * rs[(i + Tu) * 2 + 1];	/* Tu_1_5 * Tu_1_5' */ /* pa0mbo check for OK */
+              temp8[0] += rs[(i + Tg - 3) * 2] * rs[(i + Tu + Tg - 3) * 2] + rs[(i + Tg - 3) * 2 + 1] * rs[(i + Tu + Tg - 3) * 2 + 1];	/* Tg -2 +2 * Tg + Tu -2 +2 */
+              temp8[1] += -rs[(i + Tg - 3) * 2] * rs[(i + Tu + Tg - 3) * 2 + 1] + rs[(i + Tg - 3) * 2 + 1] + rs[(i + Tu + Tg - 3) * 2];
+              temp9 += rs[(i + Tg - 3) * 2] * rs[(i + Tg - 3) * 2] + rs[(i + Tg - 3) * 2 + 1] * rs[(i + Tg - 3) * 2 + 1];	/* Tg-2+2 * Tg-2+2 */
+              temp10 += rs[(i + Tg + Tu - 3) * 2] * rs[(i + Tg + Tu - 3) * 2] + rs[(i + Tg + Tu - 3) * 2 + 1] * rs[(i + Tg + Tu - 3) * 2 + 1];	/* TG+Tu-2+2 * Tg+Tu-2+2 */
+            }
+          theta_plus = (float) sqrt((temp1[0] - temp5[0]) * (temp1[0] - temp5[0]) + (temp1[1] - temp5[1]) * (temp1[1] - temp5[1]));
+          theta_plus -= (0.5 * (-temp6 - temp7));
+          theta_minus = (float) sqrt((temp1[0] - temp8[0]) * (temp1[0] - temp8[0]) + (temp1[1] - temp8[1]) * (temp1[1] - temp8[1]));
+          theta_minus -= (0.5 * (temp9 - temp10));
+          delta_theta = (theta_plus - theta_minus) * Tgh / temp4;
+
+          /* now limit the delta_theta value */
+          if (delta_theta < -max_theta)  delta_theta = -max_theta;
+          if (delta_theta > max_theta)   delta_theta = max_theta;
+
+          /* P-I controller for theta */
+          time_offset_ctrl = delta_theta - time_offset_fractional;
+          delta_time_offset_I += kI_timing_controller * time_offset_ctrl;
+          delta_time_offset_P = kP_large_timing_controller * time_offset_ctrl +
+              threshold_timing_small_large * (kP_small_timing_controller -kP_large_timing_controller) *
+              tanh(time_offset_ctrl / threshold_timing_small_large);
+          delta_time_offset = delta_time_offset_P + delta_time_offset_I + time_offset_fractional;
+          delta_time_offset_integer = (int) floor(delta_time_offset + 0.5);
+//          logfile->addToAux(QString("dt %1 %2 %3 %4 %5").arg(time_offset_ctrl).arg(delta_time_offset_I).arg(delta_time_offset_P).arg(delta_time_offset).arg(delta_time_offset_integer));
+
+          /* now limit delta_time_offset_integer */
+          if (delta_time_offset_integer < -1) delta_time_offset_integer = -1;
+          if (delta_time_offset_integer > 1)  delta_time_offset_integer = 1;
+          time_offset_fractional =  delta_time_offset - delta_time_offset_integer;
+
+          /* printf("delta_time_offset_integer as used in phi_freq_corr %d phifcl %g \n",
+       delta_time_offset_integer, phi_freq_correction_last); */
+          phi_freq_correction_last += (delta_time_offset_integer / Tu) * freq_offset;
+
+        }
+
+      else
+
+        {
+          delta_time_offset_integer = 0;
+          time_offset_fractional = 0.0;
+        }
+
+      /*  printf("in getofdm delta_time_offset_integer is %d time_offs_fract %g\n",
+         delta_time_offset_integer, time_offset_fractional);   */
+      if (FREQ_SYNC_ENABLE == 1)
+
+        {
+          temp[0] = 0.0;
+          temp[1] = 0.0;
+          for (i = 0; i < Tg; i++)
+
+            {
+              temp[0] +=
+                  rs[(i + 1 + delta_time_offset_integer) * 2] * rs[(i + 1 +
+                                                                    delta_time_offset_integer
+                                                                    + Tu) * 2] +
+                  rs[(i + 1 + delta_time_offset_integer) * 2 +
+                  1] * rs[(i + 1 + delta_time_offset_integer + Tu) * 2 + 1];
+              temp[1] +=
+                  rs[(i + 1 + delta_time_offset_integer) * 2 +
+                  1] * rs[(i + 1 + delta_time_offset_integer + Tu) * 2] -
+                  rs[(i + 1 + delta_time_offset_integer) * 2] * rs[(i + 1 +
+                                                                    delta_time_offset_integer
+                                                                    + Tu) * 2 +
+                  1];
+            }
+          epsilon_ML = (float) (atan2(temp[1], temp[0]));
+
+          /* filter epsilon_ML */
+          freq_offset_ctrl =
+              fmodf(epsilon_ML - freq_offset + PI + 100.0 * PI, 2 * PI) - PI;
+          dfreq_offset_I += kI_freq_controller * freq_offset_ctrl;
+          dfreq_offset_P =
+              kP_large_freq_controller * freq_offset_ctrl +
+              threshold_freq_small_large * (kP_small_freq_controller -
+                                            kP_large_freq_controller) *
+              tanh(freq_offset_ctrl / threshold_freq_small_large);
+          dfreq_offset = dfreq_offset_P + dfreq_offset_I;
+          freq_offset += dfreq_offset;
+        }
+
+      else
+
+        {
+
+          /* freq_offset =0.0  */
+        }
+
+      /* printf("in getofdm freq_offset = %g\n", freq_offset); */
+
+      /* get symbol and correct frequency */
+      for (i = 0; i < Tu; i++)
+
+        {
+          tmptheta = (freq_offset / Tu) * i + phi_freq_correction_last;
+          exp_temp[i * 2] = (float) cos(tmptheta);
+          exp_temp[i * 2 + 1] = (float) sin(tmptheta);
+        }
+
+      for (i = 0; i < Tu; i++)
+        {
+          (s[i]).re =
+              rs[(1 + delta_time_offset_integer + Tgh +
+                  i) * 2] * exp_temp[i * 2] - rs[(1 +
+                                                  delta_time_offset_integer +
+                                                  Tgh + i) * 2 +
+              1] * exp_temp[i * 2 + 1];
+          (s[i]).im =
+              rs[(1 + delta_time_offset_integer + Tgh +
+                  i) * 2] * exp_temp[i * 2 + 1] + rs[(1 +
+                                                      delta_time_offset_integer
+                                                      + Tgh + i) * 2 +
+              1] * exp_temp[i * 2];
+        }
+      phi_freq_correction_last = (float) fmod(phi_freq_correction_last + (float) Ts / (float) Tu * freq_offset, (float)(2.0 * PI));
+
+      /* Now do fft and output symbol */
+      fftwf_execute(p);
+      for (i = 0; i <= Tu / 2; i++)
+
+        {
+          term1 = (float) (i * 2.0 * PI * (Tgh + time_offset_fractional) / Tu);	/* Euler */
+          exp_temp[i * 2] = (float) cos(term1);
+          exp_temp[i * 2 + 1] = (float) sin(term1);
+        }
+      /* now calc out */
+      for (i = 0; i < Tu / 2; i++)
+
+        {
+          out1[i * 2] = (float) (((S[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2] + ((S[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2 + 1]);	/* real = ac+bd */
+          out1[i * 2 + 1] = (float) (((S[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2] - ((S[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2 + 1]);	/* imag bc -ad */
+        }
+      /* Now flip out1 to out */
+      for (i = 0; i < Tu / 2; i++)
+
+        {
+          out[i * 2] = out1[(Tu / 2 - 1 - i) * 2];
+          out[i * 2 + 1] = out1[(Tu / 2 - i - 1) * 2 + 1];
+          out[(Tu / 2 + i) * 2] =
+              (float) (((S[i]).re) * exp_temp[i * 2] -
+                       ((S[i]).im) * exp_temp[i * 2 + 1]);
+          out[(Tu / 2 + i) * 2 + 1] =
+              (float) (((S[i]).im) * exp_temp[i * 2] +
+                       ((S[i]).re) * exp_temp[i * 2 + 1]);
+        } Zi[0] = 1.0;
+      Zi[1] = phi_freq_correction_last;
+      Zi[2] = delta_time_offset_I;
+      Zi[3] = dfreq_offset_I;
+      Zi[4] = freq_offset;
+      Zi[5] = time_offset_fractional;
+      return (delta_time_offset_integer);
+    }
+}
diff --git a/qsstv/drmrx/getofdmsync.cpp b/qsstv/drmrx/getofdmsync.cpp
new file mode 100644
index 0000000..a78cca8
--- /dev/null
+++ b/qsstv/drmrx/getofdmsync.cpp
@@ -0,0 +1,352 @@
+
+/*
+*     File getofdmsync.c
+*    
+*     Author PA0MBO - M.BOS
+*     DATE Feb 21st 2009
+*
+*     implements more or less the get_ofdm_symbol_sync 
+*     MATLAB routine from diorama-1.1.1
+*
+*  
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fftw3.h>
+#include "drmproto.h"
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+
+extern int FREQ_SYNC_ENABLE;
+
+
+#define PI (atan(1.0)*4.0)
+void filter1(float *, float *, float *, int, int);
+int
+getofdmsync( /*@null@ */ float *rs, int Ts, int Tu, /*@null@ */ float *H,
+             int lH,
+             float delta_freq_offset, /*@null@ */ float *Zi, /*@null@ */
+             float *out, int init,
+             int TIME_SYNC_ENABLE, int FREQ_SYNC_ENABLE)
+{
+  static int Tg, Tgh;
+  static int Tc;
+  static int Tgs, Tgsh;
+  static float kP_small_timing_controller = 0.0005;
+  static float kP_large_timing_controller = 0.003;	/* pa0mbo was 0.01 */
+  static float threshold_timing_small_large = 2.0;	/* was 2.0 */
+  static float kI_timing_controller = 0.000020;	/* was 0.00005 */
+  int max_delta_theta;
+  int max_delta_time_offset_integer = 3;
+  int max_symbol_position_offset;
+
+  static drmComplex s[512], S[512];
+  static drmComplex s1[512], S1[512];
+  static fftwf_plan p1, p2;
+  float h_absq[512];		/* 2 x max el */
+  static float sinfilter[256];	/* too long */
+  float h_absq_filtered[512];
+  float dummy;
+  float delta_theta, delta_theta_tmp;
+  int dftmp, symbol_position_offset, spotmp;
+  float freq_offset_ctrl;
+  static float /*@only@ */ *exp_temp;
+  static float /*@only@ */ *out1;
+  int i;
+  float kP_freq_controller;
+//  float *pinput;
+  float time_offset_ctrl;
+  float delta_time_offset_P, delta_time_offset;
+  int delta_time_offset_integer;
+  float tmptheta, term1;
+  int indexin;
+  float time_offset_fractional, freq_offset;
+  float delta_time_offset_I, phi_freq_correction_last;
+
+
+  /* pa0mbo if-part checken 16 april 2007 */
+  if (init == 1)
+
+    {
+      Tg = Ts - Tu;
+      Tgh = (int) floor(Tg / 2 + 0.5);
+      Tc = (int) pow(2, ceil(log((double) lH) / log(2.0)));
+      Tgs = (int) floor((float) Tg / (float) Tu * (float) Tc);
+      Tgsh = (int) floor((float) Tg / (float) Tu / 2.0 * (float) Tc);
+
+      /* printf("Tg %d, Tgh %d, Tc %d,Tu %d,  Tgs %d, Tgsh %d\n",
+         Tg, Tgh, Tc, Tu, Tgs, Tgsh);   */
+      /* malloc space for arrays */
+      if ((exp_temp = (float *)malloc((Tu * 2 + 2) * sizeof(float))) == NULL)
+
+        {
+          printf("cannot malloc space for exp_temp in get_ofdm_symbol\n");
+          exit(EXIT_FAILURE);
+        }
+      if ((out1 = (float *)malloc(Tu * 2 * sizeof(float))) == NULL)
+
+        {
+          printf("cannot malloc space for out1 in get_ofdm_symbol\n");
+          exit(EXIT_FAILURE);
+        }
+      if (p1 != NULL) fftwf_destroy_plan(p1);
+      if (p2 != NULL) fftwf_destroy_plan(p2);
+      p1 = fftwf_plan_dft_1d(Tc,(fftwf_complex *)s,(fftwf_complex *)S,FFTW_FORWARD, FFTW_PATIENT);
+      p2 = fftwf_plan_dft_1d(Tu,(fftwf_complex *)s1,(fftwf_complex *)S1, FFTW_FORWARD, FFTW_PATIENT);
+
+
+      /*      printf("xxxx sinfilter\n");   */
+      for (i = 0; i < Tgs; i++)
+
+        {
+          sinfilter[i] = (float) pow(sin((float) (i + 1.0) / (float) (Tgs + 1.0) * PI), 0.001);
+        } return (0);
+    }
+
+  else
+
+    {
+
+      /* fixed parameters */
+      Tg = Ts - Tu;
+      Tgh = (int) floor(Tg / 2 + 0.5);
+      Tc = (int) pow(2, ceil(log((double) lH) / log(2.0)));
+      Tgs = (int) floor((float) Tg / (float) Tu * (float) Tc);
+      Tgsh = (int) floor((float) Tg / (float) Tu / 2.0 * (float) Tc);
+      kP_small_timing_controller = 0.001;	/* pa0mbo was 0.005 */
+      kP_large_timing_controller = 0.001;	/* pa0mbo was 0.01 */
+      threshold_timing_small_large = (float) Tgh;
+      kI_timing_controller = 2E-5;	/* pa0mbo was 0.000005 */
+      max_delta_theta = Tgh;
+      max_delta_time_offset_integer = 3;
+      max_symbol_position_offset = Tgh - max_delta_time_offset_integer;
+      kP_freq_controller = 0.01;	/* pa0mbo was 0.01 */
+      phi_freq_correction_last = Zi[1];
+      delta_time_offset_I = Zi[2];
+      freq_offset = Zi[4];
+      time_offset_fractional = Zi[5];
+
+
+
+
+      /* debugging  */
+
+      addToLog(QString("ofdmsync: dfo= %1 tof=%2 fofs=  %3 dtoI = %4 phicl = %5")
+               .arg(delta_freq_offset).arg(time_offset_fractional).arg(freq_offset)
+               .arg(delta_time_offset_I).arg(phi_freq_correction_last),LOGDRMDEMOD);
+      if (TIME_SYNC_ENABLE == 1)
+
+        {
+
+          /* estimate time offset */
+          /* first copy H data to  s buffer  that is destroyed by fft */
+//          pinput = H;
+          for (i = 0; i < lH; i++)
+            {
+              s[i].re = H[i * 2];
+              s[i].im = H[i * 2 + 1];
+            }
+
+          /* zero fill to power of 2 elements */
+          for (i = lH; i < Tc; i++)
+            {
+              s[i].re = 0.0;
+              s[i].im = 0.0;
+            }
+          fftwf_execute(p1);	/* do complex fft */
+
+
+          /*  printf("xxx h_absq\n"); */
+          for (i = 0; i < Tc; i++)
+
+            {
+              h_absq[i] = (float) (S[i].re * S[i].re + S[i].im * S[i].im);
+              h_absq[i + Tc] = (float) (S[i].re * S[i].re + S[i].im * S[i].im);	/* needed 2 times */
+
+              /*   printf(" %g\n", h_absq[i]); */
+            }
+          drmfilter1(h_absq, h_absq_filtered, sinfilter, 2 * Tc, Tgs);
+
+          /* debugging
+       printf("xxx filter h_abs \n");
+       for (i=0; i < 2*Tc ; i++)
+       printf("%g \n", h_absq_filtered[i]);
+       printf("ooooo\n");    */
+
+          /* now determine max and position */
+          dummy = -1.0E30;
+          delta_theta = 0.0;
+          for (i = 0; i < 2 * Tc; i++)
+
+            {
+              if (h_absq_filtered[i] > dummy)
+
+                {
+                  dummy = h_absq_filtered[i];
+                  delta_theta = (float) i;
+                }
+            }
+          /* debugging
+       printf("ofdmsync: dummy= %g delta_theta=%g\n", dummy, delta_theta);  */
+          delta_theta =
+              (float) (((((Tc + Tgsh - (int) delta_theta +
+                           (int) (Tc * 1.5)) % Tc) -
+                         Tc / 2) * Tu) / (float) Tc);
+
+          /* printf("delta_theta rescaled %g\n", delta_theta);   */
+          if (delta_theta >= (float) max_delta_theta)
+            delta_theta_tmp = (float) max_delta_theta;
+
+          else
+            delta_theta_tmp = delta_theta;
+          if (delta_theta_tmp > (float) -max_delta_theta)
+            delta_theta = delta_theta_tmp;
+
+          else
+            delta_theta = (float) -max_delta_theta;
+
+          /* filter theta: P-I controller */
+          time_offset_ctrl = delta_theta - time_offset_fractional;
+          delta_time_offset_I += kI_timing_controller * time_offset_ctrl;
+          delta_time_offset_P =
+              kP_large_timing_controller * time_offset_ctrl +
+              threshold_timing_small_large * (kP_small_timing_controller -
+                                              kP_large_timing_controller) *
+              tanhf(time_offset_ctrl / threshold_timing_small_large);
+          delta_time_offset =
+              delta_time_offset_P + delta_time_offset_I +
+              time_offset_fractional;
+          delta_time_offset_integer = (int) floor(delta_time_offset + 0.5);
+          if (delta_time_offset_integer > -max_delta_time_offset_integer)
+            dftmp = delta_time_offset_integer;
+
+          else
+            dftmp = -max_delta_time_offset_integer;
+          if (dftmp > max_delta_time_offset_integer)
+            delta_time_offset_integer = max_delta_time_offset_integer;
+
+          else
+            delta_time_offset_integer = dftmp;	/* only +/- one symbol */
+          time_offset_fractional =
+              delta_time_offset - delta_time_offset_integer;
+
+          /* debugging
+       printf("delta_t_offs  %g delta_t_int %d time_offs_fract %g \n",
+       delta_time_offset, delta_time_offset_integer, time_offset_fractional);  */
+
+          /* get best time window */
+          symbol_position_offset = (int) floor(delta_theta - delta_time_offset_integer + 0.5);
+
+          if (symbol_position_offset > -max_symbol_position_offset) spotmp = symbol_position_offset;
+          else           spotmp = -max_symbol_position_offset;
+          if (spotmp < max_symbol_position_offset) symbol_position_offset = spotmp;
+          else symbol_position_offset = max_symbol_position_offset;
+
+          /* do integer time offset correction and comp phase shift */
+          phi_freq_correction_last +=
+              ((float) delta_time_offset_integer / Tu) * freq_offset;
+        }
+
+      else
+
+        {
+          delta_time_offset_integer = 0;
+          time_offset_fractional = 0;
+          symbol_position_offset = 0;
+        }
+      if (FREQ_SYNC_ENABLE == 1)
+
+        {
+
+          /* frequency offset estimation */
+          freq_offset_ctrl = delta_freq_offset;
+          freq_offset += kP_freq_controller * freq_offset_ctrl;
+        }
+
+      /* get symbol and correct frequency */
+
+      for (i = 0; i < Tu; i++)
+
+        {
+          indexin = i + symbol_position_offset;
+          tmptheta = (freq_offset / Tu) * indexin + phi_freq_correction_last;
+          exp_temp[i * 2] = (float) cos(tmptheta);
+          exp_temp[i * 2 + 1] = (float) sin(tmptheta);
+        }
+
+      for (i = 0; i < Tu; i++)
+        {
+          indexin =i + 1 + delta_time_offset_integer + Tgh + symbol_position_offset;
+          s1[i].re = rs[indexin * 2] * exp_temp[i * 2] - rs[indexin * 2 + 1] * exp_temp[i * 2 + 1];
+          s1[i].im = rs[indexin * 2] * exp_temp[i * 2 + 1] + rs[indexin * 2 + 1] * exp_temp[i * 2];
+        }
+      phi_freq_correction_last =
+          fmodf(phi_freq_correction_last +
+                (float) Ts / (float) Tu * freq_offset, 2.0 * PI);
+
+      /* Now do fft and output symbol */
+      fftwf_execute(p2);
+
+      /*  printf("xxx exp_temp in getofdmsync \n"); */
+      for (i = 0; i <= Tu / 2; i++)
+
+        {
+          term1 = (float) ((i * 2.0 * PI / (float) Tu) * (Tgh + time_offset_fractional - symbol_position_offset));	/* Euler */
+          exp_temp[i * 2] = (float) cos(term1);
+          exp_temp[i * 2 + 1] = (float) sin(term1);
+
+          /*    printf(" %g %g \n", exp_temp[i*2], exp_temp[i*2+1]); */
+        }
+      /* now calc out */
+      for (i = 0; i < Tu / 2; i++)
+
+        {
+          out1[i * 2] = (float) (((S1[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2] + ((S1[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2 + 1]);	/* real = ac+bd */
+          out1[i * 2 + 1] = (float) (((S1[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2] - ((S1[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2 + 1]);	/* imag bc -ad */
+        }
+      for (i = 0; i < (Tu / 2 - 1); i++)
+        {
+          out1[(Tu / 2 + i) * 2] =
+              (float) (((S1[i]).re) * exp_temp[i * 2] -
+                       ((S1[i]).im) * exp_temp[i * 2 + 1]);
+          out1[(Tu / 2 + i) * 2 + 1] =
+              (float) (((S1[i]).im) * exp_temp[i * 2] +
+                       ((S1[i]).re) * exp_temp[i * 2 + 1]);
+        }
+      /* Now flip out1 to out */
+      for (i = 0; i < Tu / 2; i++)
+         {
+          out[i * 2] = out1[(Tu / 2 - 1 - i) * 2];
+          out[i * 2 + 1] = out1[(Tu / 2 - 1 - i) * 2 + 1];
+        }
+
+      /* now put in the rest */
+      for (i = Tu / 2; i < Tu; i++)
+         {
+          out[i * 2] = out1[i * 2];
+          out[i * 2 + 1] = out1[i * 2 + 1];
+        }
+      Zi[1] = phi_freq_correction_last;
+      Zi[2] = delta_time_offset_I;
+      Zi[4] = freq_offset;
+      Zi[5] = time_offset_fractional;
+      return (delta_time_offset_integer);
+    }
+}
diff --git a/qsstv/drmrx/getsymbolidx.cpp b/qsstv/drmrx/getsymbolidx.cpp
new file mode 100644
index 0000000..7349661
--- /dev/null
+++ b/qsstv/drmrx/getsymbolidx.cpp
@@ -0,0 +1,139 @@
+
+/*
+*
+*    file  getsymbolidx.c
+*
+*    corresponds with matlab file get_symbol_index.m
+*    from diorama-1.1.1
+*    by Andreas Dittrich
+* 
+*    translated to C
+*    by M.Bos  - PA0MBO
+*
+*    Date feb 21st 2009
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+struct cmplxnmbr
+{
+  float re;
+  float im;
+};
+
+
+#define PI  (4.0*atan(1.00))
+int
+getsymbolidx(float *symbol_buffer, int symbols_per_frame,
+	     int *time_ref_cells_k, int *time_ref_cells_theta_1024, int K_dc,
+	     int K_modulo, int n_time_ref_cells)
+{
+  struct cmplxnmbr S[288][25];	/* max 24 symbols per frame */
+  int i, j, n, symbol0;
+  float sum_real_xx[25];
+//  float sum_imag_xx[25];
+  float tmp1real, tmp1imag, tmp2real, tmp2imag;
+  int k1_index, k2_index;
+  float phi1, phi2;
+  float max_sum_xx;
+
+
+  /* reshape symbol buffer to matrix S */
+  for (i = 0; i < symbols_per_frame; i++)
+
+    {
+      for (j = 0; j < K_modulo; j++)
+
+	{
+	  (S[j][i]).re = symbol_buffer[(j + i * K_modulo) * 2];
+	  (S[j][i]).im = symbol_buffer[(j + i * K_modulo) * 2 + 1];
+	}
+    }
+
+  /* debugging 
+     printf("ooo symbols buffer in get symbolidx\n");
+     for (i=0; i < K_modulo; i++)
+     printf("%g\n", sqrt( (S[i][0]).re* (S[i][0]).re+
+     (S[i][0]).im*(S[i][0]).im));
+
+     printf("oooo\n");  */
+  for (i = 0; i < symbols_per_frame; i++)
+
+    {
+      sum_real_xx[i] = 0.0;
+//      sum_imag_xx[i] = 0.0;
+    }
+  for (n = 0; n < n_time_ref_cells - 1; n++)
+
+    {
+      if (time_ref_cells_k[n + 1] - time_ref_cells_k[n] == 1)
+
+	{
+	  k1_index = K_dc + time_ref_cells_k[n];
+	  k2_index = K_dc + time_ref_cells_k[n + 1];
+	  phi1 = (2.0 * PI * time_ref_cells_theta_1024[n]) / 1024.0;
+	  phi2 = (2.0 * PI * time_ref_cells_theta_1024[n + 1]) / 1024.0;
+
+	  /*    printf("timeref[n] is %d timeref[n+1] is %d n is %d\n", 
+	     time_ref_cells_k[n], time_ref_cells_k[n+1], n); 
+	     printf("in getsymbolidx K_dc is %d k1_index %d k2_index %d phi1 %g phi2 %g\n",
+	     K_dc, k1_index, k2_index, phi1, phi2);  */
+	  for (j = 0; j < symbols_per_frame; j++)
+
+	    {
+	      tmp1real =
+		(S[k1_index][j]).re * cos(phi1) +
+		(S[k1_index][j]).im * sin(phi1);
+	      tmp1imag =
+		(S[k1_index][j]).im * cos(phi1) -
+		(S[k1_index][j]).re * sin(phi1);
+	      tmp2real =
+		(S[k2_index][j]).re * cos(phi2) +
+		(S[k2_index][j]).im * sin(phi2);
+	      tmp2imag =
+		(S[k2_index][j]).im * cos(phi2) -
+		(S[k2_index][j]).re * sin(phi2);
+	      sum_real_xx[j] += tmp1real * tmp2real + tmp1imag * tmp2imag;
+
+
+/*           sum_imag_xx[j]  += tmp1imag*tmp2real - tmp1real*tmp2imag; */
+	    }
+	}
+    }
+
+  /* detmn of index of max in abs(sum..xx) */
+  max_sum_xx = -1.0E20;
+  symbol0 = 0;
+  for (j = 0; j < symbols_per_frame; j++)
+
+    {
+      tmp1real = fabs(sum_real_xx[j]);
+
+      /* debugging 
+         printf("abs_sum_real_xx[%d] %g\n", j, tmp1real);   */
+      if (tmp1real > max_sum_xx)
+
+	{
+	  max_sum_xx = tmp1real;
+	  symbol0 = j;
+	}
+    }
+  return (symbol0);
+}
diff --git a/qsstv/drmrx/hybridcrypt.cpp b/qsstv/drmrx/hybridcrypt.cpp
new file mode 100644
index 0000000..f94045a
--- /dev/null
+++ b/qsstv/drmrx/hybridcrypt.cpp
@@ -0,0 +1,135 @@
+#include "hybridcrypt.h"
+#include <QDebug>
+#include "configparams.h"
+#include "QResource"
+
+
+
+hybridCrypt::hybridCrypt()
+{
+  const uchar *d;
+  QResource qrc(":/icons/mgc.raw");
+  d=qrc.data();
+  key1=d[32]-0x36;
+  key2=d[33]-0x72;
+  key3=d[34]-0xd8;
+  key4=d[35]-0xFE;
+}
+
+
+bool hybridCrypt::deCrypt(QByteArray *ba, QString &result)
+{
+  int baSize;
+  bool ok;
+  int charCount=0;
+  QString tempStr="0x00";
+  short int bufI, bufI2, divzr, num1, num2, num3, num4, res1, res2, res3, res4;
+  QString bufS, hexa, hexa1, hexa2;
+  unsigned char r1,r2;
+  result="";
+  res1=res2=res3=res4=0;
+  divzr=key1*key4;
+  bufI2=key3*key2;
+  divzr-=bufI2;
+  if(divzr==0) return FALSE;
+  baSize=ba->size()-2; //drop /r/n
+  if(baSize<20)
+    {
+      hcFtpRemoteHost=hybridFtpRemoteHost;
+      hcFtpLogin=hybridFtpLogin;
+      hcFtpPassword=hybridFtpPassword;
+      hcFtpRemoteDirectory=hybridFtpRemoteDirectory2;
+      return TRUE;
+    }
+  do
+    {
+      for(bufI=0;bufI<4;bufI++,charCount+=2)
+        {
+          r1=ba->at(charCount);
+          r2=ba->at(charCount+1);
+          if(r1==0xFF)
+            {
+              r1=r2=0;
+            }
+          if(r1==0xFE)
+            {
+              r1=0;
+            }
+          if(r1==0xFD)
+            {
+              r1=r2;
+              r2=0;
+            }
+          switch(bufI)
+            {
+            case 0: res1=r1*256+r2; break;
+            case 1: res2=r1*256+r2; break;
+            case 2: res3=r1*256+r2; break;
+            case 3: res4=r1*256+r2; break;
+            }
+        }
+
+      bufI= res1 * key4;
+      bufI2= res2 * key3;
+      num1= bufI - bufI2;
+      num1= num1 / divzr;
+      bufI= res2 * key1;
+      bufI2= res1 * key2;
+      num2= bufI - bufI2;
+      num2 = num2 / divzr;
+      bufI= res3 * key4;
+      bufI2= res4 * key3;
+      num3= bufI - bufI2;
+      num3= num3 / divzr;
+      bufI= res4 * key1;
+      bufI2= res3 * key2;
+      num4= bufI - bufI2;
+      num4= num4 / divzr;
+      tempStr[2]=QChar(num1);
+      tempStr[3]=QChar(num2);
+      result.append(QChar(tempStr.toInt(&ok,16)));
+      tempStr[2]=QChar(num3);
+      tempStr[3]=QChar(num4);
+      result.append(QChar(tempStr.toInt(&ok,16)));
+    }
+  while(charCount<baSize);
+  reverseString(result);
+  getParam(result);
+  return TRUE;
+}
+
+
+void hybridCrypt::reverseString(QString & s)
+{
+  int i,j;
+  QString t;
+  for (i=0,j=s.length()-1;i<s.length();i++,j--)
+    {
+      t[j]=s[i];
+    }
+  s=t;
+}
+
+bool hybridCrypt::getParam(QString result)
+{
+  int a,b,c,d,e;
+  a=result.indexOf(QChar(63));
+  b=result.indexOf(QChar(34),a+1);
+  c=result.indexOf(QChar(60),b+1);
+  d=result.indexOf(QChar(62),c+1);
+  e=result.indexOf(QChar(58),d+1);
+  hcFtpRemoteHost=result.mid(a+1,b-a-1);
+  hcFtpLogin=result.mid(b+1,c-b-1);
+  hcFtpPassword=result.mid(c+1,d-c-1);
+  hcFtpRemoteDirectory=result.mid(d+1,e-d-1);
+  if(hcFtpRemoteDirectory.isEmpty())
+    {
+      hcFtpRemoteDirectory="HybridFiles";
+    }
+  return TRUE;
+}
+
+
+
+
+
diff --git a/qsstv/drmrx/lubksb.cpp b/qsstv/drmrx/lubksb.cpp
new file mode 100644
index 0000000..eaa76c9
--- /dev/null
+++ b/qsstv/drmrx/lubksb.cpp
@@ -0,0 +1,21 @@
+void lubksb(float **a, int n, int *indx, float b[])
+{
+	int i,ii=0,ip,j;
+	float sum;
+
+	for (i=1;i<=n;i++) {
+		ip=indx[i];
+		sum=b[ip];
+		b[ip]=b[i];
+		if (ii)
+			for (j=ii;j<=i-1;j++) sum -= a[i][j]*b[j];
+		else if (sum) ii=i;
+		b[i]=sum;
+	}
+	for (i=n;i>=1;i--) {
+		sum=b[i];
+		for (j=i+1;j<=n;j++) sum -= a[i][j]*b[j];
+		b[i]=sum/a[i][i];
+	}
+}
+/* (C) Copr. 1986-92 Numerical Recipes Software 0!-"1. */
diff --git a/qsstv/drmrx/ludcmp.cpp b/qsstv/drmrx/ludcmp.cpp
new file mode 100644
index 0000000..4153e34
--- /dev/null
+++ b/qsstv/drmrx/ludcmp.cpp
@@ -0,0 +1,58 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define TINY 1.0e-20;
+
+void ludcmp(float **a, int n, int *indx, float *d)
+{
+	int i,imax=0,j,k;
+	float big,dum,sum,temp;
+	float *vv;
+
+  vv=fvector(1,n);
+	*d=1.0;
+	for (i=1;i<=n;i++) {
+		big=0.0;
+		for (j=1;j<=n;j++)
+			if ((temp=fabs(a[i][j])) > big) big=temp;
+		if (big == 0.0) nrerror("Singular matrix in routine ludcmp");
+		vv[i]=1.0/big;
+	}
+	for (j=1;j<=n;j++) {
+		for (i=1;i<j;i++) {
+			sum=a[i][j];
+			for (k=1;k<i;k++) sum -= a[i][k]*a[k][j];
+			a[i][j]=sum;
+		}
+		big=0.0;
+		for (i=j;i<=n;i++) {
+			sum=a[i][j];
+			for (k=1;k<j;k++)
+				sum -= a[i][k]*a[k][j];
+			a[i][j]=sum;
+			if ( (dum=vv[i]*fabs(sum)) >= big) {
+				big=dum;
+				imax=i;
+			}
+		}
+		if (j != imax) {
+			for (k=1;k<=n;k++) {
+				dum=a[imax][k];
+				a[imax][k]=a[j][k];
+				a[j][k]=dum;
+			}
+			*d = -(*d);
+			vv[imax]=vv[j];
+		}
+		indx[j]=imax;
+		if (a[j][j] == 0.0) a[j][j]=TINY;
+		if (j != n) {
+			dum=1.0/(a[j][j]);
+			for (i=j+1;i<=n;i++) a[i][j] *= dum;
+		}
+	}
+  free_fvector(vv,1,n);
+}
+#undef TINY
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software 0!-"1. */
diff --git a/qsstv/drmrx/mkfacmap.cpp b/qsstv/drmrx/mkfacmap.cpp
new file mode 100644
index 0000000..44e3ce4
--- /dev/null
+++ b/qsstv/drmrx/mkfacmap.cpp
@@ -0,0 +1,259 @@
+
+/*
+*    File mkfacmap.c
+*
+*
+*    produces FAC mapping data
+*
+*    Author M.Bos
+*    Date Feb 21st 2009
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+int mkfacmap(int robustness_mode, int K_dc, int K_modulo, int /*@out@ */ *FACmap)
+{
+  int elem_cnt, i;
+
+  switch (robustness_mode)
+
+    {
+    case 0:
+      elem_cnt = 45;
+      i = 0;
+      FACmap[i++] = K_dc + K_modulo + 10;
+      FACmap[i++] = K_dc + K_modulo + 22;
+      FACmap[i++] = K_dc + K_modulo + 30;
+      FACmap[i++] = K_dc + K_modulo + 50;
+      FACmap[i++] = K_dc + 2 * K_modulo + 14;
+      FACmap[i++] = K_dc + 2 * K_modulo + 26;
+      FACmap[i++] = K_dc + 2 * K_modulo + 34;
+      FACmap[i++] = K_dc + 3 * K_modulo + 18;
+      FACmap[i++] = K_dc + 3 * K_modulo + 30;
+      FACmap[i++] = K_dc + 3 * K_modulo + 38;
+      FACmap[i++] = K_dc + 4 * K_modulo + 22;
+      FACmap[i++] = K_dc + 4 * K_modulo + 34;
+      FACmap[i++] = K_dc + 4 * K_modulo + 42;
+      FACmap[i++] = K_dc + 5 * K_modulo + 18;
+      FACmap[i++] = K_dc + 5 * K_modulo + 26;
+      FACmap[i++] = K_dc + 5 * K_modulo + 38;
+      FACmap[i++] = K_dc + 5 * K_modulo + 46;
+      FACmap[i++] = K_dc + 6 * K_modulo + 22;
+      FACmap[i++] = K_dc + 6 * K_modulo + 30;
+      FACmap[i++] = K_dc + 6 * K_modulo + 42;
+      FACmap[i++] = K_dc + 6 * K_modulo + 50;
+      FACmap[i++] = K_dc + 7 * K_modulo + 26;
+      FACmap[i++] = K_dc + 7 * K_modulo + 34;
+      FACmap[i++] = K_dc + 7 * K_modulo + 46;
+      FACmap[i++] = K_dc + 8 * K_modulo + 10;
+      FACmap[i++] = K_dc + 8 * K_modulo + 30;
+      FACmap[i++] = K_dc + 8 * K_modulo + 38;
+      FACmap[i++] = K_dc + 8 * K_modulo + 50;
+      FACmap[i++] = K_dc + 9 * K_modulo + 14;
+      FACmap[i++] = K_dc + 9 * K_modulo + 34;
+      FACmap[i++] = K_dc + 9 * K_modulo + 42;
+      FACmap[i++] = K_dc + 10 * K_modulo + 18;
+      FACmap[i++] = K_dc + 10 * K_modulo + 38;
+      FACmap[i++] = K_dc + 10 * K_modulo + 46;
+      FACmap[i++] = K_dc + 11 * K_modulo + 10;
+      FACmap[i++] = K_dc + 11 * K_modulo + 22;
+      FACmap[i++] = K_dc + 11 * K_modulo + 42;
+      FACmap[i++] = K_dc + 11 * K_modulo + 50;
+      FACmap[i++] = K_dc + 12 * K_modulo + 14;
+      FACmap[i++] = K_dc + 12 * K_modulo + 26;
+      FACmap[i++] = K_dc + 12 * K_modulo + 46;
+      FACmap[i++] = K_dc + 13 * K_modulo + 18;
+      FACmap[i++] = K_dc + 13 * K_modulo + 30;
+      FACmap[i++] = K_dc + 14 * K_modulo + 22;
+      FACmap[i++] = K_dc + 14 * K_modulo + 34;
+      break;
+    case 1:
+      elem_cnt = 45;
+      i = 0;
+      FACmap[i++] = K_dc + 21;
+      FACmap[i++] = K_dc + K_modulo + 11;
+      FACmap[i++] = K_dc + K_modulo + 23;
+      FACmap[i++] = K_dc + K_modulo + 35;
+      FACmap[i++] = K_dc + 2 * K_modulo + 13;
+      FACmap[i++] = K_dc + 2 * K_modulo + 25;
+      FACmap[i++] = K_dc + 2 * K_modulo + 37;
+      FACmap[i++] = K_dc + 3 * K_modulo + 15;
+      FACmap[i++] = K_dc + 3 * K_modulo + 27;
+      FACmap[i++] = K_dc + 3 * K_modulo + 39;
+      FACmap[i++] = K_dc + 4 * K_modulo + 5;
+      FACmap[i++] = K_dc + 4 * K_modulo + 17;
+      FACmap[i++] = K_dc + 4 * K_modulo + 29;
+      FACmap[i++] = K_dc + 4 * K_modulo + 41;
+      FACmap[i++] = K_dc + 5 * K_modulo + 7;
+      FACmap[i++] = K_dc + 5 * K_modulo + 19;
+      FACmap[i++] = K_dc + 5 * K_modulo + 31;
+      FACmap[i++] = K_dc + 6 * K_modulo + 9;
+      FACmap[i++] = K_dc + 6 * K_modulo + 21;
+      FACmap[i++] = K_dc + 6 * K_modulo + 33;
+      FACmap[i++] = K_dc + 7 * K_modulo + 11;
+      FACmap[i++] = K_dc + 7 * K_modulo + 23;
+      FACmap[i++] = K_dc + 7 * K_modulo + 35;
+      FACmap[i++] = K_dc + 8 * K_modulo + 13;
+      FACmap[i++] = K_dc + 8 * K_modulo + 25;
+      FACmap[i++] = K_dc + 8 * K_modulo + 37;
+      FACmap[i++] = K_dc + 9 * K_modulo + 15;
+      FACmap[i++] = K_dc + 9 * K_modulo + 27;
+      FACmap[i++] = K_dc + 9 * K_modulo + 39;
+      FACmap[i++] = K_dc + 10 * K_modulo + 5;
+      FACmap[i++] = K_dc + 10 * K_modulo + 17;
+      FACmap[i++] = K_dc + 10 * K_modulo + 29;
+      FACmap[i++] = K_dc + 10 * K_modulo + 41;
+      FACmap[i++] = K_dc + 11 * K_modulo + 7;
+      FACmap[i++] = K_dc + 11 * K_modulo + 19;
+      FACmap[i++] = K_dc + 11 * K_modulo + 31;
+      FACmap[i++] = K_dc + 12 * K_modulo + 9;
+      FACmap[i++] = K_dc + 12 * K_modulo + 21;
+      FACmap[i++] = K_dc + 12 * K_modulo + 33;
+      FACmap[i++] = K_dc + 13 * K_modulo + 11;
+      FACmap[i++] = K_dc + 13 * K_modulo + 23;
+      FACmap[i++] = K_dc + 13 * K_modulo + 35;
+      FACmap[i++] = K_dc + 14 * K_modulo + 13;
+      FACmap[i++] = K_dc + 14 * K_modulo + 25;
+      FACmap[i++] = K_dc + 14 * K_modulo + 37;
+      break;
+    case 2:
+      elem_cnt = 45;
+      i = 0;
+      FACmap[i++] = K_dc + K_modulo + 7;
+      FACmap[i++] = K_dc + K_modulo + 23;
+      FACmap[i++] = K_dc + 2 * K_modulo + 8;
+      FACmap[i++] = K_dc + 2 * K_modulo + 16;
+      FACmap[i++] = K_dc + 2 * K_modulo + 24;
+      FACmap[i++] = K_dc + 3 * K_modulo + 9;
+      FACmap[i++] = K_dc + 3 * K_modulo + 17;
+      FACmap[i++] = K_dc + 4 * K_modulo + 10;
+      FACmap[i++] = K_dc + 4 * K_modulo + 18;
+      FACmap[i++] = K_dc + 5 * K_modulo + 11;
+      FACmap[i++] = K_dc + 5 * K_modulo + 19;
+      FACmap[i++] = K_dc + 6 * K_modulo + 4;
+      FACmap[i++] = K_dc + 6 * K_modulo + 12;
+      FACmap[i++] = K_dc + 7 * K_modulo + 13;
+      FACmap[i++] = K_dc + 7 * K_modulo + 21;
+      FACmap[i++] = K_dc + 8 * K_modulo + 6;
+      FACmap[i++] = K_dc + 8 * K_modulo + 14;
+      FACmap[i++] = K_dc + 8 * K_modulo + 22;
+      FACmap[i++] = K_dc + 9 * K_modulo + 7;
+      FACmap[i++] = K_dc + 9 * K_modulo + 23;
+      FACmap[i++] = K_dc + 10 * K_modulo + 8;
+      FACmap[i++] = K_dc + 10 * K_modulo + 16;
+      FACmap[i++] = K_dc + 10 * K_modulo + 24;
+      FACmap[i++] = K_dc + 11 * K_modulo + 9;
+      FACmap[i++] = K_dc + 11 * K_modulo + 13;
+      FACmap[i++] = K_dc + 11 * K_modulo + 17;
+      FACmap[i++] = K_dc + 12 * K_modulo + 10;
+      FACmap[i++] = K_dc + 12 * K_modulo + 18;
+      FACmap[i++] = K_dc + 13 * K_modulo + 11;
+      FACmap[i++] = K_dc + 13 * K_modulo + 19;
+      FACmap[i++] = K_dc + 14 * K_modulo + 4;
+      FACmap[i++] = K_dc + 14 * K_modulo + 12;
+      FACmap[i++] = K_dc + 14 * K_modulo + 16;
+      FACmap[i++] = K_dc + 15 * K_modulo + 13;
+      FACmap[i++] = K_dc + 15 * K_modulo + 21;
+      FACmap[i++] = K_dc + 16 * K_modulo + 6;
+      FACmap[i++] = K_dc + 16 * K_modulo + 14;
+      FACmap[i++] = K_dc + 16 * K_modulo + 22;
+      FACmap[i++] = K_dc + 17 * K_modulo + 7;
+      FACmap[i++] = K_dc + 17 * K_modulo + 23;
+      FACmap[i++] = K_dc + 18 * K_modulo + 8;
+      FACmap[i++] = K_dc + 18 * K_modulo + 16;
+      FACmap[i++] = K_dc + 18 * K_modulo + 24;
+      FACmap[i++] = K_dc + 19 * K_modulo + 9;
+      FACmap[i++] = K_dc + 19 * K_modulo + 17;
+      break;
+    case 3:			/* does not exist in amateur mode */
+      elem_cnt = 45;
+      i = 0;
+      FACmap[i++] = K_dc + 3 * K_modulo + 9;
+      FACmap[i++] = K_dc + 3 * K_modulo + 18;
+      FACmap[i++] = K_dc + 3 * K_modulo + 27;
+      FACmap[i++] = K_dc + 4 * K_modulo + 10;
+      FACmap[i++] = K_dc + 4 * K_modulo + 19;
+      FACmap[i++] = K_dc + 5 * K_modulo + 11;
+      FACmap[i++] = K_dc + 5 * K_modulo + 20;
+      FACmap[i++] = K_dc + 5 * K_modulo + 29;
+      FACmap[i++] = K_dc + 6 * K_modulo + 12;
+      FACmap[i++] = K_dc + 6 * K_modulo + 30;
+      FACmap[i++] = K_dc + 7 * K_modulo + 13;
+      FACmap[i++] = K_dc + 7 * K_modulo + 22;
+      FACmap[i++] = K_dc + 7 * K_modulo + 31;
+      FACmap[i++] = K_dc + 8 * K_modulo + 5;
+      FACmap[i++] = K_dc + 8 * K_modulo + 14;
+      FACmap[i++] = K_dc + 8 * K_modulo + 23;
+      FACmap[i++] = K_dc + 8 * K_modulo + 32;
+      FACmap[i++] = K_dc + 9 * K_modulo + 6;
+      FACmap[i++] = K_dc + 9 * K_modulo + 15;
+      FACmap[i++] = K_dc + 9 * K_modulo + 24;
+      FACmap[i++] = K_dc + 9 * K_modulo + 33;
+      FACmap[i++] = K_dc + 10 * K_modulo + 16;
+      FACmap[i++] = K_dc + 10 * K_modulo + 25;
+      FACmap[i++] = K_dc + 10 * K_modulo + 34;
+      FACmap[i++] = K_dc + 11 * K_modulo + 8;
+      FACmap[i++] = K_dc + 11 * K_modulo + 17;
+      FACmap[i++] = K_dc + 11 * K_modulo + 26;
+      FACmap[i++] = K_dc + 11 * K_modulo + 35;
+      FACmap[i++] = K_dc + 12 * K_modulo + 9;
+      FACmap[i++] = K_dc + 12 * K_modulo + 18;
+      FACmap[i++] = K_dc + 12 * K_modulo + 27;
+      FACmap[i++] = K_dc + 12 * K_modulo + 36;
+      FACmap[i++] = K_dc + 13 * K_modulo + 10;
+      FACmap[i++] = K_dc + 13 * K_modulo + 19;
+      FACmap[i++] = K_dc + 13 * K_modulo + 37;
+      FACmap[i++] = K_dc + 14 * K_modulo + 11;
+      FACmap[i++] = K_dc + 14 * K_modulo + 20;
+      FACmap[i++] = K_dc + 14 * K_modulo + 29;
+      FACmap[i++] = K_dc + 15 * K_modulo + 12;
+      FACmap[i++] = K_dc + 15 * K_modulo + 30;
+      FACmap[i++] = K_dc + 16 * K_modulo + 13;
+      FACmap[i++] = K_dc + 16 * K_modulo + 22;
+      FACmap[i++] = K_dc + 16 * K_modulo + 31;
+      FACmap[i++] = K_dc + 17 * K_modulo + 5;
+      FACmap[i++] = K_dc + 17 * K_modulo + 14;
+      FACmap[i++] = K_dc + 17 * K_modulo + 23;
+      FACmap[i++] = K_dc + 17 * K_modulo + 32;
+      FACmap[i++] = K_dc + 18 * K_modulo + 6;
+      FACmap[i++] = K_dc + 18 * K_modulo + 15;
+      FACmap[i++] = K_dc + 18 * K_modulo + 24;
+      FACmap[i++] = K_dc + 18 * K_modulo + 33;
+      FACmap[i++] = K_dc + 19 * K_modulo + 16;
+      FACmap[i++] = K_dc + 19 * K_modulo + 25;
+      FACmap[i++] = K_dc + 19 * K_modulo + 34;
+      FACmap[i++] = K_dc + 20 * K_modulo + 8;
+      FACmap[i++] = K_dc + 20 * K_modulo + 17;
+      FACmap[i++] = K_dc + 20 * K_modulo + 26;
+      FACmap[i++] = K_dc + 20 * K_modulo + 35;
+      FACmap[i++] = K_dc + 21 * K_modulo + 9;
+      FACmap[i++] = K_dc + 21 * K_modulo + 18;
+      FACmap[i++] = K_dc + 21 * K_modulo + 27;
+      FACmap[i++] = K_dc + 21 * K_modulo + 36;
+      FACmap[i++] = K_dc + 22 * K_modulo + 10;
+      FACmap[i++] = K_dc + 22 * K_modulo + 19;
+      FACmap[i++] = K_dc + 22 * K_modulo + 37;
+      break;
+    default:
+      printf("wrong robustness mode in cal to mkfacmap\n");
+      exit(EXIT_FAILURE);
+    }
+  return elem_cnt;
+}
diff --git a/qsstv/drmrx/mkmap.h b/qsstv/drmrx/mkmap.h
new file mode 100644
index 0000000..3dace48
--- /dev/null
+++ b/qsstv/drmrx/mkmap.h
@@ -0,0 +1,4 @@
+#ifndef MKMAP_H
+#define MKMAP_H
+
+#endif // MKMAP_H
diff --git a/qsstv/drmrx/mkmscmap.cpp b/qsstv/drmrx/mkmscmap.cpp
new file mode 100644
index 0000000..a4014d1
--- /dev/null
+++ b/qsstv/drmrx/mkmscmap.cpp
@@ -0,0 +1,423 @@
+
+/*
+*   file mkmscmap.c
+*
+*   makes MSC_Demapper
+*
+*   cf create_MSC_demapper.m matlab code by
+*   Torsten Schorr 2004
+*
+*   recoded in C by PA0MBO - M.Bos
+*
+*   date Feb 21st 2009
+*
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <math.h>
+#include <malloc.h>
+#include "drmdefs.h"
+#include "structtemplates.h"
+#include "drmproto.h"
+extern int symbols_per_frame_list[4];
+extern int time_ref_cells_k_list[4][21];
+extern int y_list[4];
+extern int K_min_K_max_list[2][24];
+extern int freq_ref_cells_k_list[4][3];
+extern int x_list[4];
+extern int k0_list[4];
+extern int mode_and_occupancy_code_table[24];
+extern int time_ref_cells_cnt_list[4];
+extern int lFAC;
+extern int MSC_Demapper[6][2959];
+int
+mkmscmap(int robustness_mode, int spectrum_occupancy, int interleaver_depth,
+	 int K_dc, int K_modulo)
+{
+  int frames_per_superframe = 3;
+  int K_min;
+  int K_max;
+  int mode_and_occupancy_code;
+  int x, y, k0;
+  int s;
+//  int Tu;
+  int gain_ref_cells_k[712];
+  int unused_carriers_k[3];
+  int i, m, j;
+  int first_symbol;
+  int control_cells_k[65];
+  int pilot_cells_k[600];
+  int cnt_msc_cells = 0;
+  int rndcnt, cnt_time_ref_cells;
+  int n, k, p, p_min, p_max;
+  int contains;
+  int term;
+  int N_SFA, N_L, N_MUX, D;
+  int cnt_msc_cells_3_superframes;
+  static int *PICMSC_inv = NULL;
+  int step,  ncGIMSC;
+//  int nrGIMSC;
+  int columnv[5];
+  int rowv[1000];
+  int vproduct[5][1000];
+  int GIMSC_inv[5000];
+  int cnt_GIMSC;
+  int Cell_Deinterleaver[5000];
+  int FAC_cells_k[65];		/*   reeds global */
+//  int Tu_list[] = { Tu_A, Tu_B, Tu_C, Tu_D };
+  int symbols_per_frame;
+  int freq_ref_cells_k[3];
+  int time_ref_cells_k[21];
+//  int carrier_per_symbol;
+  int MSC_cells_k[8877];
+  int MSC_cells_3_superframes[3 * 8877];
+
+//  Tu = Tu_list[robustness_mode];
+  K_min = K_min_K_max_list[0][spectrum_occupancy + robustness_mode * 6];
+  K_max = K_min_K_max_list[1][spectrum_occupancy + robustness_mode * 6];
+  mode_and_occupancy_code =
+    mode_and_occupancy_code_table[robustness_mode * 6 + spectrum_occupancy];
+  if (mode_and_occupancy_code < 0)
+
+    {
+      printf("BAD MODE AND OCCUPANCY CODE \n");
+    }
+  symbols_per_frame = symbols_per_frame_list[robustness_mode];
+//  carrier_per_symbol = K_max - K_min + 1;
+  x = x_list[robustness_mode];
+  y = y_list[robustness_mode];
+  k0 = k0_list[robustness_mode];
+  rndcnt = 0;
+  for (s = 0; s < symbols_per_frame; s++)
+
+    {
+      n = s % y;
+      m = s / y;
+      p_min = (int) ceil((double) ((K_min - k0 - x * n) / (x * y)));
+      p_max = (K_max - k0 - x * n) / (x * y);
+      for (p = p_min; p <= p_max; p++)
+
+	{
+	  k = k0 + x * n + x * y * p;
+	  gain_ref_cells_k[rndcnt++] = k + s * K_modulo;
+
+	  /* printf("gain_ref_cells_k[%d] = %d\n", rndcnt-1, gain_ref_cells_k[rndcnt-1]);  */
+	}
+    }
+  unused_carriers_k[0] = 0;
+  if (robustness_mode == 0)
+
+    {
+      unused_carriers_k[1] = 1;
+    }
+  for (i = 0; i < 3; i++)
+
+    {
+      freq_ref_cells_k[i] = freq_ref_cells_k_list[robustness_mode][i];
+    }
+  cnt_time_ref_cells = time_ref_cells_cnt_list[robustness_mode];
+  for (i = 0; i < cnt_time_ref_cells; i++)
+
+    {
+      time_ref_cells_k[i] = time_ref_cells_k_list[robustness_mode][i];
+    }
+  lFAC = mkfacmap(robustness_mode, 0, K_modulo, FAC_cells_k);
+
+  /* MSC cells per superframe */
+  first_symbol = 0;
+  cnt_msc_cells = 0;
+
+  for (m = 0; m < frames_per_superframe; m++)
+
+    {
+      for (i = 0; i < lFAC; i++)
+
+	{
+	  control_cells_k[i] =
+	    FAC_cells_k[i] + K_modulo * symbols_per_frame * m;
+
+	  /* printf("control_cells_k[%d] = %d  FAC_cells_k %d K_modulo %d symbols_per_frame %d m %d \n",
+	     i, control_cells_k[i], FAC_cells_k[i], K_modulo, symbols_per_frame, m);   */
+	}
+      for (j = 3; j < cnt_time_ref_cells + 3; j++)
+
+	{
+	  pilot_cells_k[j] =
+	    K_modulo * symbols_per_frame * m + time_ref_cells_k[j - 3];
+	}
+      for (j = 3 + cnt_time_ref_cells; j < 3 + cnt_time_ref_cells + rndcnt;
+	   j++)
+
+	{
+	  pilot_cells_k[j] =
+	    K_modulo * symbols_per_frame * m + gain_ref_cells_k[j - 3 -
+								cnt_time_ref_cells];
+	}
+      for (s = first_symbol; s < symbols_per_frame; s++)
+
+	{
+	  for (j = 0; j < 3; j++)
+
+	    {
+	      pilot_cells_k[j] =
+		K_modulo * symbols_per_frame * m + K_modulo * s +
+		freq_ref_cells_k[j];
+	    }
+	  for (k = K_min; k <= K_max; k++)
+
+	    {
+	      contains = 0;
+	      term = k + K_modulo * symbols_per_frame * m + K_modulo * s;
+	      for (j = 0; j < 3 + cnt_time_ref_cells + rndcnt; j++)
+
+		{
+		  if (term == pilot_cells_k[j])
+
+		    {
+		      contains = 1;
+		      goto uit;
+		    }
+		}
+	      for (j = 0; j < lFAC; j++)
+
+		{
+		  if (term == control_cells_k[j])
+
+		    {
+		      contains = 1;
+		      goto uit;
+		    }
+		}
+	      if (robustness_mode == 0)
+
+		{
+		  for (j = 0; j < 1; j++)
+
+		    {
+		      if (term ==
+			  K_modulo * symbols_per_frame * m + K_modulo * s +
+			  unused_carriers_k[j])
+
+			{
+			  contains = 1;
+			  goto uit;
+			}
+		    }
+		}
+
+	      else
+
+		{
+		  if (term == K_modulo * symbols_per_frame * m + K_modulo * s)	/* pa0mbo 29-11-2007 was + unused_carriers_k[0] */
+
+		    {
+		      contains = 1;
+		      goto uit;
+		    }
+		}
+	    uit:if (contains == 0)
+
+		{
+		  MSC_cells_k[cnt_msc_cells++] = term;
+
+		  /* debugging   
+		     printf("%d  cell=%d \n", cnt_msc_cells, MSC_cells_k[cnt_msc_cells -1]);   */
+		}
+	    }			/* end k-loop */
+	}			/* end s-loop */
+      first_symbol = 0;
+    }				/* end m -loop */
+  N_SFA = cnt_msc_cells;
+  N_L = N_SFA % frames_per_superframe;
+  N_MUX = (N_SFA - N_L) / frames_per_superframe;
+
+
+  /*  cell interleaver for MSC_cells */
+  cnt_msc_cells_3_superframes = 0;
+  for (i = 0; i < N_SFA - N_L; i++)
+
+    {
+      MSC_cells_3_superframes[cnt_msc_cells_3_superframes++] = K_dc + MSC_cells_k[i];	
+    }
+  for (i = 0; i < N_SFA - N_L; i++)
+
+    {
+      MSC_cells_3_superframes[cnt_msc_cells_3_superframes++] =
+	K_dc + symbols_per_frame * frames_per_superframe * K_modulo +
+	MSC_cells_k[i];
+    }
+  for (i = 0; i < N_SFA - N_L; i++)
+
+    {
+      MSC_cells_3_superframes[cnt_msc_cells_3_superframes++] =
+	K_dc + 2 * symbols_per_frame * frames_per_superframe * K_modulo +
+	MSC_cells_k[i];
+    }
+
+  /*  for (i=0; i < cnt_msc_cells_3_superframes; i++)
+     printf("MSC_cells3..[%d] = %d\n", i, MSC_cells_3_superframes[i]);
+     printf("====\n");    */
+  if (PICMSC_inv != NULL) free(PICMSC_inv);
+  PICMSC_inv = deinterleaver(0, 1, N_MUX, 5);
+
+
+  if (interleaver_depth == 0)
+
+    {
+
+      /* convolutional deinterleaver 
+         ETSI ES 201980 / 7.6 */
+      D = 5;
+
+      /* calc of GIMSC_inv in steps */
+      /* first step [1:N_MUX+1:D*(N_MUX+1)] */
+      step = N_MUX + 1;
+//      nrGIMSC = D;
+      ncGIMSC = (int) ceil((float) N_MUX / D);
+
+      /* printf("ncGIMSC = %d\n", ncGIMSC); */
+      columnv[0] = 1;
+      rowv[0] = 0;
+      for (i = 1; i < D; i++)
+	columnv[i] = columnv[i - 1] + step;
+      for (i = 1; i < ncGIMSC; i++)
+	rowv[i] = rowv[i - 1] + D;
+      for (i = 0; i < D; i++)
+
+	{
+	  for (j = 0; j < ncGIMSC; j++)
+
+	    {
+	      vproduct[i][j] = columnv[i] + rowv[j];
+	    }
+	}
+      cnt_GIMSC = 0;
+      for (j = 0; j < ncGIMSC; j++)
+
+	{
+	  for (i = 0; i < D; i++)
+
+	    {
+	      GIMSC_inv[cnt_GIMSC++] = vproduct[i][j];
+
+	      /* printf("GIMSC_inv[%d] = %d \n", cnt_GIMSC-1,  vproduct[i][j]);  */
+	    }
+	}
+      cnt_GIMSC--;
+
+      /*  printf("xxx Cell_Deinterleaver\n");  */
+      for (i = 0; i < N_MUX; i++)
+
+	{
+	  Cell_Deinterleaver[i] = GIMSC_inv[PICMSC_inv[i]];
+
+	  /* printf("%d \n", Cell_Deinterleaver[i]);  */
+	}
+
+      /*  printf("xxx\n");   */
+      for (i = 0; i < N_MUX; i++)
+
+	{
+	  MSC_Demapper[5][i] =
+	    MSC_cells_3_superframes[Cell_Deinterleaver[i] - 1];
+	  MSC_Demapper[0][i] =
+	    ((MSC_cells_3_superframes[N_MUX + Cell_Deinterleaver[i] - 1] +
+	      1) % (2 * symbols_per_frame * frames_per_superframe *
+		    K_modulo)) - 1;
+	  MSC_Demapper[1][i] =
+	    ((MSC_cells_3_superframes[2 * N_MUX + Cell_Deinterleaver[i] - 1] +
+	      1) % (2 * symbols_per_frame * frames_per_superframe *
+		    K_modulo)) - 1;
+	  MSC_Demapper[2][i] =
+	    ((symbols_per_frame * frames_per_superframe * K_modulo +
+	      MSC_cells_3_superframes[Cell_Deinterleaver[i] - 1] +
+	      1) % (2 * symbols_per_frame * frames_per_superframe *
+		    K_modulo)) - 1;
+	  MSC_Demapper[3][i] =
+	    ((symbols_per_frame * frames_per_superframe * K_modulo +
+	      MSC_cells_3_superframes[N_MUX + Cell_Deinterleaver[i] - 1] +
+	      1) % (2 * symbols_per_frame * frames_per_superframe *
+		    K_modulo)) - 1;
+	  MSC_Demapper[4][i] =
+	    ((symbols_per_frame * frames_per_superframe * K_modulo +
+	      MSC_cells_3_superframes[2 * N_MUX + Cell_Deinterleaver[i] - 1] +
+	      1) % (2 * symbols_per_frame * frames_per_superframe *
+		    K_modulo)) - 1;
+	}
+    }
+
+  else
+
+    {
+
+      /* printf("xxx Cell_Deinterleaver\n");  */
+      for (i = 0; i < N_MUX; i++)
+
+	{
+	  Cell_Deinterleaver[i] = PICMSC_inv[i];
+
+	  /*    printf("%d \n", Cell_Deinterleaver[i]);  */
+	}
+
+      /*   printf("xxx\n");  */
+      /*  printf("xxxx MSC_Demap[1]\n");  */
+      for (i = 0; i < N_MUX; i++)
+
+	{
+	  MSC_Demapper[1][i] = MSC_cells_3_superframes[Cell_Deinterleaver[i]];	/* pa0mbo -1 binnen [] weggehaald */
+
+	  /* printf("%d \n", MSC_Demapper[1][i]);  */
+	  MSC_Demapper[2][i] =
+	    (MSC_cells_3_superframes[N_MUX + Cell_Deinterleaver[i]]) % (2 *
+									symbols_per_frame
+									*
+									frames_per_superframe
+									*
+									K_modulo);
+	  MSC_Demapper[3][i] =
+	    (MSC_cells_3_superframes[2 * N_MUX + Cell_Deinterleaver[i]]) %
+	    (2 * symbols_per_frame * frames_per_superframe * K_modulo);
+	  MSC_Demapper[4][i] =
+	    (symbols_per_frame * frames_per_superframe * K_modulo +
+	     MSC_cells_3_superframes[Cell_Deinterleaver[i]]) % (2 *
+								symbols_per_frame
+								*
+								frames_per_superframe
+								* K_modulo);
+	  MSC_Demapper[5][i] =
+	    (symbols_per_frame * frames_per_superframe * K_modulo +
+	     MSC_cells_3_superframes[N_MUX +
+				     Cell_Deinterleaver[i]]) % (2 *
+								symbols_per_frame
+								*
+								frames_per_superframe
+								* K_modulo);
+	  MSC_Demapper[0][i] =
+	    (symbols_per_frame * frames_per_superframe * K_modulo +
+	     MSC_cells_3_superframes[2 * N_MUX +
+				     Cell_Deinterleaver[i]]) % (2 *
+								symbols_per_frame
+								*
+								frames_per_superframe
+								* K_modulo);
+	}
+    }
+  return N_MUX;
+}
diff --git a/qsstv/drmrx/msd_hard.h b/qsstv/drmrx/msd_hard.h
new file mode 100644
index 0000000..dda67f7
--- /dev/null
+++ b/qsstv/drmrx/msd_hard.h
@@ -0,0 +1,47 @@
+ signed char puncturing[13][17] = { {0, 15, 0}, 
+  {-4, 15, 2, 7, 2, 7, -4}, 
+  {0, 7, 0}, 
+  {-6, 7, 2, 7, 2, 7, 2, 3, -6}, 
+  {0, 3, 0}, 
+  {-6, 3, 2, 5, 2, 3, 2, 1, -6}, 
+  {-4, 3, 2, 1, 2, 3, -4}, 
+  {-2, 3, 2, 1, -2}, 
+  {-14, 3, 2, 1, 2, 1, 2, 3, 2, 1, 2, 1, 2, 3, 2, 1, -14}, 
+  {-4, 3, 2, 1, 2, 1, -4}, 
+  {-6, 3, 2, 1, 2, 1, 2, 1, -6}, 
+  {-12, 3, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, -12}, {-14, 3, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, -14}
+};
+
+
+
+/* int RX[13] = {1, 3, 1, 4, 1, 4, 3, 2, 8, 3, 4, 7, 8}; */ 
+int RY[13] = { 4, 10, 3, 11, 2, 7, 5, 3, 11, 4, 5, 8, 9 };
+signed char tailpuncturing[12][13] = { {-10, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, -10}, 
+  {-10, 7, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, -10}, 
+  {-10, 7, 2, 3, 2, 3, 2, 7, 2, 3, 2, 3, -10}, 
+  {-10, 7, 2, 7, 2, 3, 2, 7, 2, 3, 2, 3, -10}, 
+  {-10, 7, 2, 7, 2, 3, 2, 7, 2, 7, 2, 3, -10}, 
+  {-10, 7, 2, 7, 2, 7, 2, 7, 2, 7, 2, 3, -10}, 
+  {-10, 7, 2, 7, 2, 7, 2, 7, 2, 7, 2, 7, -10}, 
+  {-10, 15, 2, 7, 2, 7, 2, 7, 2, 7, 2, 7, -10}, 
+  {-10, 15, 2, 7, 2, 7, 2, 15, 2, 7, 2, 7, -10}, 
+  {-10, 15, 2, 15, 2, 7, 2, 15, 2, 7, 2, 7, -10}, 
+  {-10, 15, 2, 15, 2, 7, 2, 15, 2, 7, 2, 15, -10}, {-10, 15, 2, 15, 2, 15, 2, 15, 2, 7, 2, 15, -10}
+};
+
+
+#define SQRT2	1.41421356237310F
+#define SQRT10	3.16227766016838F
+#define SQRT42	6.48074069840786F
+float partitioning[4][8] =
+  { {7 / SQRT42, 5 / SQRT42, 3 / SQRT42, 1 / SQRT42, -1 / SQRT42, -3 / SQRT42, -5 / SQRT42, -7 / SQRT42},
+  /* 64-QAM Ungerb�k Set Partitioning */ 
+{7 / SQRT42, -1 / SQRT42, 5 / SQRT42, -3 / SQRT42, 3 / SQRT42, -5 / SQRT42, 1 / SQRT42, -7 / SQRT42}, /* 64-QAM Block Partitioning */ 
+{3 / SQRT10, 1 / SQRT10, -1 / SQRT10, -3 / SQRT10}, /* 16-QAM Ungerb�k Set Partitioning */ 
+{1 / SQRT2, -1 / SQRT2}
+};                              /*  4-QAM Ungerb�k Set Partitioning */
+
+
+#undef SQRT2
+#undef SQRT10
+#undef SQRT42
diff --git a/qsstv/drmrx/msd_hard_sdc.h b/qsstv/drmrx/msd_hard_sdc.h
new file mode 100644
index 0000000..cf82326
--- /dev/null
+++ b/qsstv/drmrx/msd_hard_sdc.h
@@ -0,0 +1,89 @@
+
+/******************************************************************************/  
+
+/*                                                                            */ 
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */ 
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */ 
+
+/*                                                                            */ 
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */ 
+
+/*  Project start: 15.06.2004                                                 */ 
+
+/*  Last change  : 30.06.2004                                                 */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+
+/*                                                                            */ 
+
+/*  This program is free software; you can redistribute it and/or modify      */ 
+
+/*  it under the terms of the GNU General Public License as published by      */ 
+
+/*  the Free Software Foundation; either version 2 of the License, or         */ 
+
+/*  (at your option) any later version.                                       */ 
+
+/*                                                                            */ 
+
+/*  This program is distributed in the hope that it will be useful,           */ 
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */ 
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */ 
+
+/*  GNU General Public License for more details.                              */ 
+
+/*                                                                            */ 
+
+/*  You should have received a copy of the GNU General Public License         */ 
+
+/*  along with this program; if not, write to the Free Software               */ 
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+  
+
+/******************************************************************************/ 
+
+/*                                                                            */ 
+
+/*  msd_hard.h (part of msd_hard)                                             */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+
+/*  Description:                                                              */ 
+
+/*  constants and tables for msd_hard.c                                       */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+extern signed char puncturing[13][17];
+
+
+
+/*   int RX[13] = {1, 3, 1, 4, 1, 4, 3, 2, 8, 3, 4, 7, 8}; */ 
+extern int RY[13];
+extern signed char tailpuncturing[12][13];
+
+
+#define SQRT2	1.41421356237310F
+#define SQRT10	3.16227766016838F
+#define SQRT42	6.48074069840786F
+extern float partitioning[4][8];
+
+
+#undef SQRT2
+#undef SQRT10
+#undef SQRT42
diff --git a/qsstv/drmrx/msdhardfac.cpp b/qsstv/drmrx/msdhardfac.cpp
new file mode 100644
index 0000000..ec5737a
--- /dev/null
+++ b/qsstv/drmrx/msdhardfac.cpp
@@ -0,0 +1,736 @@
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */
+
+/*                                                                            */
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */
+
+/*  Project start: 15.06.2004                                                 */
+
+/*  Last change  : 22.07.2004                                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is free software; you can redistribute it and/or modify      */
+
+/*  it under the terms of the GNU General Public License as published by      */
+
+/*  the Free Software Foundation; either version 2 of the License, or         */
+
+/*  (at your option) any later version.                                       */
+
+/*                                                                            */
+
+/*  This program is distributed in the hope that it will be useful,           */
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+
+/*  GNU General Public License for more details.                              */
+
+/*                                                                            */
+
+/*  You should have received a copy of the GNU General Public License         */
+
+/*  along with this program; if not, write to the Free Software               */
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  msdhardfac.c                                                                */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*  Description:                                                              */
+
+/*  Multi-Stage-Decoder for DRM QAM signals (iterations with hard decisions)  */
+
+/*  Usage:                                                                    */
+
+/*                                                                            */
+
+/*  [LPhardout, HPhardout, VSPPhardout] =                                     */
+
+/*                      msd_hard (received, H, N1, L, Lvspp,                  */
+
+/*                      Deinterleaver,PL, maxiter, SDCorMSC);                 */
+
+/*                                                                            */
+
+/*  received: samples of an FAC, SDC or MSC frame                             */
+
+/*  H: estimated channel transfer function                                    */
+
+/*  N1: number of OFDM cells in the higher protected part (part A)            */
+
+/*  L: number of information bits for Part A/B for each level in (2xl)-matrix */
+
+/*  Lvspp: number of information bits in the very strongly protected part     */
+
+/*  Deinterleaver: deinterleavers for each levels in (Nxl)-int32-matrix       */
+
+/*  PL: Protection Levels for Part A/B for each level in (2xl)-matrix         */
+
+/*  maxiter: maximum number of decoding iterations                            */
+
+/*  SDCorMSC: 1 for SDC and MSC frames, 0 for FAC frames                      */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/* 
+*  changed filename to msdhardfac.c
+*  and added new interface to accomodate
+*  own C-language interface instead of 
+*  Matlab interface
+*
+*  Author of changes M.Bos - PA0MBO
+*  Date July 10th 2007
+*  changed memory alignment of lastiter and malloc to prevent IRIX problem
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+#include "viterbi_decode.h"
+#include "msd_hard.h"
+
+#define ITER_BREAK
+#define CONSIDERING_SNR
+
+#ifdef CONSIDERING_SNR
+
+#define ARG_INDEX_OFFSET 1
+#define NARGS_RHS_STR "9"
+#define NARGS_RHS 9
+
+#else /*  */
+
+#define ARG_INDEX_OFFSET 0
+#define NARGS_RHS_STR "8"
+#define NARGS_RHS 8
+
+#endif /*  */
+
+#define PROGNAME "msd_hard"
+
+#define PRBS_INIT(reg) reg = 511;
+#define PRBS_BIT(reg) ((reg ^ (reg >> 4)) & 0x1)
+#define PRBS_SHIFT(reg) reg = (((reg ^ (reg >> 4)) & 0x1) << 8) | (reg >> 1)
+int
+msdhardfac(double /*@out@ */ *received_real, double /*@out@ */ *received_imag,
+	   int Lrxdata, /*@out@ */ double *snr, int N1, double *L,
+	   int dimL, int Lvspp, int *Deinterleaver, int *PL, int maxiter,
+	   int SDCorMSC, double *facdata)
+{
+  double *received, *first_received, *L1, *L2, L1_real[10], *L2_real,
+    *L1_imag, *L2_imag;
+  double *PL1, *PL2, PL1_real[10], *PL2_real, *PL1_imag, *PL2_imag,
+    *output_ptr, L_dummy[3] = { 0, 0, 0 };
+  float *metric_real, *metric_imag, *metric, *first_metric, closest_one,
+    closest_zero, sample, *llr, dist;
+  char *memory_ptr, *viterbi_mem, *msd_mem, *hardpoints, *hardpoints_ptr,
+    *lastiter, *infoout[3];
+  int m, n, N, no_of_levels, iteration, diff;
+  int sample_index, rp_real[3], rp_imag[3], *rp, level, subset_point,
+    no_of_bits, error, msd_mem_size, viterbi_mem_size;
+  int PRBS_reg;
+  int HMmix = 0, HMsym = 0;
+  int i;
+
+
+#ifdef CONSIDERING_SNR
+  double *signal_to_noise_ratio;
+  float SNR;
+
+
+#endif /*  */
+
+
+/*  new interface to C-language */
+  signal_to_noise_ratio = snr;
+  no_of_levels = 1;
+  for (i = 0; i < dimL; i++)
+
+    {
+      L1_real[i] = L[i];
+
+      /* debugging 
+         printf("L[%d] = %g , L1_real[%d] = %g\n", i, L[i], i,  L1_real[i]);  */
+    }
+  L2_real = L1_real + no_of_levels;
+  L1_imag = L_dummy;
+  L2_imag = L_dummy;
+  for (i = 0; i < 2; i++)
+
+    {
+      PL1_real[i] = (double) PL[i];
+    } PL2_real = PL1_real + no_of_levels;
+  PL1_imag = PL2_real + no_of_levels;
+  PL2_imag = PL1_imag + no_of_levels;	/* pa0mbo will not be OK has to be checked !! will do for the moment */
+
+  /* debugging 
+     printf("PL1_real[0]= %g, PL1_real[1]= %g, PL2_real[0]= %g, PL2_real[1]= %g, PL1_imag[0]= %g, PL2_imag[0]= %g\n",
+     PL1_real[0], PL1_real[1], PL2_real[0], PL2_real[1],
+     PL1_imag[0], PL2_imag[0]);   */
+  SDCorMSC = ((0 - SDCorMSC) != 0);
+
+  /* debugging 
+     printf("SDCorMSC = %d\n", SDCorMSC);   */
+  if (Lrxdata < 20)
+
+    {
+      printf("msdhardfac: length rxdata should be >= 20\n");
+      exit(1);
+    }
+  N = Lrxdata;
+  if (N < 20)
+    {
+      printf("msdhardfac: N  has to be >= 20!\n");
+      exit(1);
+    }
+  if ((N1 < 0) || (N1 > N - 20))
+    {
+      printf("msdhardfac: N1 has to be >= 0!\n");
+      exit(1);
+    }
+  if (Lvspp < 0)
+    {
+      printf("msdhardfac: Lvspp has to be >= 0!\n");
+      exit(1);
+    }
+  if (maxiter < 0)
+    {
+      printf("msdhardfac: maxiter must not be negativ.");
+      exit(1);
+    }
+  if (HMmix && (Lvspp == 0))
+    {
+      printf("msdhardfac:  HMmix requires Lvspp > 0.");
+      exit(1);
+    }
+
+  /* printf("start mem alloc \n ");
+     printf("N= %d, no_of_levels= %d\n", N, no_of_levels);  */
+
+  /* memory allocation and initialization: */
+  no_of_bits = 0;
+  for (level = 0; level < no_of_levels; level++)
+    {
+      no_of_bits +=
+	(int) L1_real[level] + (int) L2_real[level] + 6 +
+	(int) L1_imag[level] + (int) L2_imag[level] + 6;
+
+      /* printf(" --- level %d L1real %d L2real %d L1imga %d L2imag %d\n",
+         level, (int)L1_real[level], (int)L2_real[level], (int)L1_imag[level], (int)L2_imag[level]);    */
+    } msd_mem_size =
+    2 * N * sizeof(float) + 2 * N * sizeof(char) + 2 * N * sizeof(char) +
+    no_of_bits * sizeof(char);
+  viterbi_mem_size =
+    STATES * sizeof(float) + STATES * sizeof(float) +
+    2 * N * STATES * sizeof(char);
+
+  /* printf("msdhardfac: viterbi_mem_size is %d STATES is %d\n", viterbi_mem_size, STATES);   */
+  if (received_imag == NULL)
+    {
+      memory_ptr =
+	(char *) malloc(viterbi_mem_size + msd_mem_size + N * sizeof(double) +
+			2);
+      received_imag =
+	(double *) (memory_ptr + viterbi_mem_size + msd_mem_size);
+      memset(received_imag, 0, N * sizeof(double));
+    }
+  else
+    {
+      memory_ptr = (char *) malloc(viterbi_mem_size + msd_mem_size + 2);
+
+      /* printf("msdhardfac: debugging memory_ptr alloc succeeded viterbi_size = %d mds_size=%d  end addr is %x\n",
+         viterbi_mem_size, msd_mem_size, memory_ptr+ viterbi_mem_size+msd_mem_size); */
+      if (memory_ptr == NULL)
+
+	{
+	  printf("msdhardfac: cannot malloc for memory_ptr\n");
+	  exit(1);
+	}
+    }
+  if (!memory_ptr)
+    {
+      printf("Failed memory request!\n");
+      exit(1);
+    }
+  viterbi_mem = memory_ptr;
+  msd_mem = memory_ptr + viterbi_mem_size;
+  llr = (float *) msd_mem;
+  hardpoints = (char *) (msd_mem + 2 * N * sizeof(float));
+  lastiter =
+    (char *) (msd_mem + 2 * N * sizeof(float) + 2 * N * sizeof(char) + 2);
+  infoout[0] =
+    (char *) (msd_mem + 2 * N * sizeof(float) + 2 * N * sizeof(char) + 2 +
+	      2 * N * sizeof(char));
+  infoout[1] = 0;
+  for (m = 1; m < no_of_levels; m++)
+    {
+      infoout[m] =
+	infoout[m - 1] + (int) L1_real[m - 1] + (int) L2_real[m - 1] + 6 +
+	(int) L1_imag[m - 1] + (int) L2_imag[m - 1] + 6;
+
+      /* debugging pa0mbo 
+         printf("infoout[%d] = %p \n", m, infoout[m]);  */
+    } memset(hardpoints, 0, 2 * N * sizeof(char));
+
+  /* choosing partitioning type: */
+  if (no_of_levels == 3)
+    {
+      if ((Lvspp != 0) && HMmix)
+	{			/* HMmix 64-QAM */
+	  metric_real = partitioning[1];
+	  metric_imag = partitioning[0];
+	  rp_real[0] =
+	    (N - 12) -
+	    RY[(int) PL2_real[0]] * ((N - 12) / RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * (((N - N1) - 12) / RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * (((N - N1) - 12) / RY[(int) PL2_real[2]]);
+	  rp_imag[0] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[0]] * (((N - N1) - 12) / RY[(int) PL2_imag[0]]);
+	  rp_imag[1] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[1]] * (((N - N1) - 12) / RY[(int) PL2_imag[1]]);
+	  rp_imag[2] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[2]] * (((N - N1) - 12) / RY[(int) PL2_imag[2]]);
+	}
+      else if (Lvspp != 0)
+	{			/* HMsym 64-QAM */
+	  HMsym = 1;
+	  metric_real = partitioning[1];
+	  metric_imag = partitioning[1];
+	  rp_real[0] =
+	    (2 * N - 12) -
+	    RY[(int) PL2_real[0]] * ((2 * N - 12) / RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[2]]);
+	}
+      else
+	{			/* SM 64-QAM */
+	  metric_real = partitioning[0];
+	  metric_imag = partitioning[0];
+	  rp_real[0] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[2]]);
+    }}
+  else if (no_of_levels == 2)
+    {				/* SM 16-QAM */
+      rp_real[0] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[0]]);
+      rp_real[1] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[1]]);
+      metric_real = partitioning[2];
+      metric_imag = partitioning[2];
+    }
+  else
+    {				/* SM 4-QAM */
+      rp_real[0] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[0]]);
+      metric_real = partitioning[3];
+      metric_imag = partitioning[3];
+  } if (!SDCorMSC)
+    {
+      rp_real[0] = -12;
+      rp_real[1] = -12;
+      rp_real[2] = -12;
+    }
+  if (Lvspp != 0)
+    {
+      L1_real[0] = 0;
+      L2_real[0] = (double) Lvspp;
+    }
+
+  /* debugging pa0mbo 
+     printf("=== voor  viterbi \n");
+     for (i=0; i < 2*N ; i++)
+     {
+     printf("hadpoints[%d] = %d \n",i,  hardpoints[i]);
+     }  */
+
+  /* Multi-Stage Decoding: */
+
+  /* first decoding: */
+  PL1 = PL1_real;
+  PL2 = PL2_real;
+  L1 = L1_real;
+  L2 = L2_real;
+  rp = rp_real;
+  first_metric = metric_real;
+  first_received = received_real;
+  hardpoints_ptr = hardpoints;
+
+  /* debugging 
+     printf("msdhardfac: at start first decoding\n");
+     printf("PL1[0] = %g, PL2[0]= %g, L1_real[0]= %g, L2_real[0]=%g, L1[0]=%g, L2[0]= %g, rp[0]= %d\n",
+     PL1[0], PL2[0], L1_real[0], L2_real[0], L1[0], L2[0], rp[0]);   */
+  for (n = 0; n <= HMmix; n++)
+
+    {
+      for (level = 0; level < no_of_levels; level++)
+
+	{
+	  metric = first_metric;
+	  received = first_received;
+	  for (m = 0; m < 2 - HMmix; m++)
+
+	    {			/* for real and imaginary part */
+	      for (sample_index = m; sample_index < (2 - HMmix) * N;
+		   sample_index += 2 - HMmix)
+
+		{
+		  sample = (float) received[sample_index >> (1 - HMmix)];	/* extract real or imaginary part respectively */
+		  closest_zero =
+		    fabs(sample - metric[(int) hardpoints_ptr[sample_index]]);
+
+		  /* printf("msdhardfac: index= %d  sample = %g metric = %g closest_zero = %g \n",
+		     sample_index, sample,  metric[hardpoints_ptr[sample_index]], closest_zero);   pa0mbo */
+		  for (subset_point = (0x1 << (level + 1));
+		       subset_point < (0x1 << no_of_levels);
+		       subset_point += (0x1 << (level + 1)))
+
+		    {
+		      dist =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] +
+				    subset_point]);
+		      if (dist < closest_zero)
+
+			{
+			  closest_zero = dist;
+			}
+		    }
+		  closest_one =
+		    fabs(sample -
+			 metric[hardpoints_ptr[sample_index] +
+				(0x1 << level)]);
+
+		  /* printf("closest_one %g\n", closest_one);  pa0mbo */
+		  for (subset_point = (0x3 << level);
+		       subset_point < (0x1 << no_of_levels);
+		       subset_point += (0x1 << (level + 1)))
+
+		    {
+		      dist =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] +
+				    subset_point]);
+		      if (dist < closest_one)
+
+			{
+			  closest_one = dist;
+			}
+		    }
+
+		  /* printf("final closest_zero=%g closest_one=%g\n", closest_zero, closest_one);  pa0mbo */
+
+#ifdef CONSIDERING_SNR
+		  SNR =
+		    (float) signal_to_noise_ratio[sample_index >>
+						  (1 - HMmix)];
+		  llr[sample_index] = (closest_zero - closest_one) * SNR;
+
+		  /* printf("llr[%d] = %g\n", sample_index, llr[sample_index]);   */
+
+		  /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one) * SNR * SNR; */
+#else /*  */
+		  llr[sample_index] = (closest_zero - closest_one);
+
+		  /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one); */
+#endif /*  */
+		}		/* end loop sample_index */
+	      metric = metric_imag;
+	      received = received_imag;
+	    }			/* end loop m */
+
+	  /* printf(" level %d HMsym %d HMmix %d N1 %d n %d\n", level, HMsym, HMmix, N1, n);  
+	     printf("msdhardfac: (level || (!HMsym ..) = %d\n", (level || (!HMsym && (n || !HMmix)))* (2 - HMmix)*N1 ); 
+	     printf("eerste viter PL1[0] %g PL2[0] %g L1[0] %g L2[0] %g L1_real[0] %g L2_real[0] %g rp[0] %d\n",
+	     PL1[0], PL2[0], L1[0], L2[0], L1_real[0], L2_real[0], rp[0]);  
+	     for (i=0; i < 17 ; i++)
+	     printf("puncturing[6][%d] = %d\n", i, puncturing[6][i]);   */
+	  error =
+	    viterbi_decode(llr, (2 - HMmix) * N,
+			   (level
+			    || (!HMsym
+				&& (n
+				    || !HMmix))) * (2 - HMmix) * N1,
+			   puncturing[(int) PL1[level]],
+			   puncturing[(int) PL2[level]],
+			   tailpuncturing[rp[level] + 12],
+			   infoout[level] + n * ((int) L1_real[level] +
+						 (int) L2_real[level] + 6),
+			   hardpoints_ptr, level,
+			   Deinterleaver + (2 - HMmix) * N * level,
+			   (int) L1[level] + (int) L2[level] + 6,
+			   rp[level] + 12, viterbi_mem);
+
+	  /* debugging pa0mbo 
+	     printf("=== na eerste viterbi \n");
+	     for (i=0; i < 2*N ; i++)
+	     {
+	     printf("infoout[0][%d] = %d \n",i,  infoout[0][i]);
+	     }   */
+	  if (error)
+
+	    {
+	      free(memory_ptr);
+	      printf("msdhardfac: Error in Viterbi decoder");
+	      return 1;
+	    }
+	}			/* end loop level */
+      PL1 = PL1_imag;
+      PL2 = PL2_imag;
+      L1 = L1_imag;
+      L2 = L2_imag;
+      rp = rp_imag;
+      first_metric = metric_imag;
+      first_received = received_imag;
+      hardpoints_ptr = hardpoints + N;
+    }				/* end loop over n */
+  diff = 1;
+  iteration = 0;
+
+  /* iterations: */
+  while (iteration < maxiter)
+
+    {
+      PL1 = PL1_real;
+      PL2 = PL2_real;
+      L1 = L1_real;
+      L2 = L2_real;
+      rp = rp_real;
+      first_metric = metric_real;
+      first_received = received_real;
+      hardpoints_ptr = hardpoints;
+
+#ifdef ITER_BREAK
+      memcpy(lastiter, hardpoints, 2 * N);
+
+#endif /*  */
+      for (n = 0; n <= HMmix; n++)
+
+	{
+	  for (level = 0; level < no_of_levels; level++)
+
+	    {
+	      metric = first_metric;
+	      received = first_received;
+	      for (m = 0; m < 2 - HMmix; m++)
+
+		{		/* for real and imaginary part */
+		  for (sample_index = m; sample_index < (2 - HMmix) * N;
+		       sample_index += 2 - HMmix)
+
+		    {
+		      sample = (float) received[sample_index >> (1 - HMmix)];	/* extract real or imaginary part respectively */
+		      closest_zero =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] &
+				    ~(0x1 << level)]);
+		      closest_one =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] |
+				    (0x1 << level)]);
+
+#ifdef CONSIDERING_SNR
+		      SNR =
+			(float) signal_to_noise_ratio[sample_index >>
+						      (1 - HMmix)];
+		      llr[sample_index] = (closest_zero - closest_one) * SNR;
+
+		      /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one) * SNR * SNR; */
+#else /*  */
+		      llr[sample_index] = (closest_zero - closest_one);
+
+		      /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one); */
+#endif /*  */
+		    }		/* end loop over sample_index */
+		  metric = metric_imag;
+		  received = received_imag;
+		}		/* end loop over m */
+
+	      /* printf("Tweede viterbi PL1[0] %g PL2[0] %g L1[0] %g L2[0] %g L1_real[0] %g L2_real[0] %g rp[0] %d\n",
+	         PL1[0], PL2[0], L1[0], L2[0], L1_real[0], L2_real[0], rp[0]);   */
+	      error =
+		viterbi_decode(llr, (2 - HMmix) * N,
+			       (level
+				|| (!HMsym
+				    && (n
+					|| !HMmix))) * (2 - HMmix) * N1,
+			       puncturing[(int) PL1[level]],
+			       puncturing[(int) PL2[level]],
+			       tailpuncturing[rp[level]],
+			       infoout[level] + n * ((int) L1_real[level] +
+						     (int) L2_real[level] +
+						     6), hardpoints_ptr,
+			       level, Deinterleaver + (2 - HMmix) * N * level,
+			       (int) L1[level] + (int) L2[level] + 6,
+			       rp[level] + 12, viterbi_mem);
+	      if (error)
+
+		{
+		  free(memory_ptr);
+		  printf("msdhardfac: Error in Viterbi decoder");
+		  return 1;
+		}
+
+#ifdef ITER_BREAK
+	      if (level == 0)
+
+		{
+		  diff = 0;
+      for (sample_index = 0;sample_index <((int)(((2 - HMmix) * N * sizeof(char)) / sizeof(int))); sample_index++)
+
+		    {
+		      diff +=
+			(((int *) hardpoints)[sample_index] ^
+			 ((int *) lastiter)[sample_index]) != 0;
+		    }
+		  /*diff = memcmp (lastiter,hardpoints,2 * N); */
+		  if (!diff)
+
+		    {
+		      break;
+		    }
+		}
+
+#endif /*  */
+	    }			/* for (level = 0; level < no_of_levels; level++) */
+	  PL1 = PL1_imag;
+	  PL2 = PL2_imag;
+	  L1 = L1_imag;
+	  L2 = L2_imag;
+	  rp = rp_imag;
+	  first_metric = metric_imag;
+	  first_received = received_imag;
+	  hardpoints_ptr = hardpoints + N;
+	}			/* for (n = 0; n <= HMmix; n++) */
+
+#ifdef ITER_BREAK
+      if (!diff)
+
+	{
+	  break;
+	}
+
+#endif /*  */
+      iteration++;
+    }				/* while (iteration < maxiter) */
+
+  /* Energy Dispersal */
+  no_of_bits = 0;
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      no_of_bits += (int) L1_real[level] + (int) L2_real[level];
+  } for (level = 0; level < no_of_levels; level++)
+    {
+      no_of_bits += (int) L1_imag[level] + (int) L2_imag[level];
+    } output_ptr = facdata;
+  PRBS_INIT(PRBS_reg);
+  n = 0;
+  if (HMmix)
+    {
+      for (m = Lvspp + 6; m < Lvspp + 6 + (int) L1_imag[0]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[0][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      for (m = 0; m < (int) L1_real[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+      } for (m = (int) L1_real[level] + (int) L2_real[level] + 6;
+	       m <
+	       (int) L1_real[level] + (int) L2_real[level] + 6 +
+	       (int) L1_imag[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+  }} if (HMmix)
+    {
+      for (m = Lvspp + 6 + (int) L1_imag[0];
+	   m < Lvspp + 6 + (int) L1_imag[0] + (int) L2_imag[0]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[0][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      for (m = (int) L1_real[level];
+	   m < (int) L1_real[level] + (int) L2_real[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+      } for (m =
+	       (int) L1_real[level] + (int) L2_real[level] + 6 +
+	       (int) L1_imag[level];
+	       m <
+	       (int) L1_real[level] + (int) L2_real[level] + 6 +
+	       (int) L1_imag[level] + (int) L2_imag[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }} PRBS_INIT(PRBS_reg);
+  if (Lvspp != 0)
+    {
+      printf
+	("msdhardfac: There is a very strongly protected part, but no variable to put it into!");
+    }
+  free(memory_ptr);
+  return 0;
+}
diff --git a/qsstv/drmrx/msdhardmsc.cpp b/qsstv/drmrx/msdhardmsc.cpp
new file mode 100644
index 0000000..efbbd66
--- /dev/null
+++ b/qsstv/drmrx/msdhardmsc.cpp
@@ -0,0 +1,700 @@
+
+/* 
+*  changed filename to msdhardmsc.c
+*  and added new interface to accomodate
+*  own C-language interface instead of 
+*  Matlab interface
+*
+*  almost fully copied  from msd_hard.c
+*  by Torsten Schorr Kaiserslautern 2004
+*
+*  Author of changes M.Bos - PA0MBO
+*  Date Feb 21st 2009
+*/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+
+/* #include "viterbi_decode.h" */
+#include "msd_hard_sdc.h"
+
+#define ITER_BREAK
+#define CONSIDERING_SNR
+
+#ifdef CONSIDERING_SNR
+
+#define ARG_INDEX_OFFSET 1
+#define NARGS_RHS_STR "9"
+#define NARGS_RHS 9
+
+#else /*  */
+
+#define ARG_INDEX_OFFSET 0
+#define NARGS_RHS_STR "8"
+#define NARGS_RHS 8
+
+#endif /*  */
+#define STATES 64
+#define PROGNAME "msd_hard"
+
+#define PRBS_INIT(reg) reg = 511;
+#define PRBS_BIT(reg) ((reg ^ (reg >> 4)) & 0x1)
+#define PRBS_SHIFT(reg) reg = (((reg ^ (reg >> 4)) & 0x1) << 8) | (reg >> 1)
+int viterbi_decode(float *, int, int, signed char *, signed char *,
+		   signed char *, char *, char *, int, int *, int, int,
+		   char *);
+int msdhardmsc(double *received_real, double *received_imag, int Lrxdata,
+	       double *snr, int N1, double *L, int rowdimL, int coldimL,
+	       int Lvspp, int *Deinterleaver, int *PL, int maxiter,
+	       int SDCorMSC, /*@out@ */ double *SPPhard,
+	       /*@out@ */ double *VSPPhard, double *iterations,
+	       double *calc_variance, double *noise_signal)
+{
+  double *received, *first_received, *L1, *L2, L1_real[10], *L2_real,
+    *L1_imag, *L2_imag;
+  double *PL1, *PL2, PL1_real[10], *PL2_real, *PL1_imag, *PL2_imag,
+    *output_ptr, L_dummy[3] = { 0, 0, 0 };
+  float *metric_real, *metric_imag, *metric, *first_metric, closest_one,
+    closest_zero, sample, *llr, dist;
+  double variance;
+  char *memory_ptr, *viterbi_mem, *msd_mem, *hardpoints, *hardpoints_ptr,
+    *lastiter, *infoout[3];
+  int m, n, N, no_of_levels, iteration, diff;
+  int sample_index, rp_real[3], rp_imag[3], *rp, level, subset_point,
+    no_of_bits, error, msd_mem_size, viterbi_mem_size;
+  int PRBS_reg;
+  int HMmix = 0, HMsym = 0;
+  int i;
+
+
+#ifdef CONSIDERING_SNR
+  double *signal_to_noise_ratio;
+  float SNR;
+
+
+#endif /*  */
+
+
+/*  new interface to C-language */
+  signal_to_noise_ratio = snr;
+  no_of_levels = rowdimL;
+  for (i = 0; i < rowdimL * coldimL; i++)
+
+    {
+      L1_real[i] = L[i];
+
+      /* debugging  
+         printf("L[%d] = %g , L1_real[%d] = %g\n", i, L[i], i,  L1_real[i]);  */
+    }
+  L2_real = L1_real + no_of_levels;
+  L1_imag = L_dummy;
+  L2_imag = L_dummy;
+  for (i = 0; i < coldimL * rowdimL; i++)	/* pa0mbo 4 niet gebruiken later */
+
+    {
+      PL1_real[i] = (double) PL[i];
+
+      /* printf("PL1_real[%d] = %g \n", i , PL1_real[i]);   */
+    } PL2_real = PL1_real + no_of_levels;
+  PL1_imag = PL2_real + no_of_levels;
+  PL2_imag = PL1_imag + no_of_levels;	/* pa0mbo will not be OK has to be checked !! will do for the moment */
+
+  /* debugging  
+     printf("PL1_real[0]= %g, PL1_real[1]= %g, PL2_real[0]= %g, PL2_real[1]= %g, PL1_imag[0]= %g, PL2_imag[0]= %g\n",
+     PL1_real[0], PL1_real[1], PL2_real[0], PL2_real[1],
+     PL1_imag[0], PL2_imag[0]);   */
+  SDCorMSC = ((0 - SDCorMSC) != 0);
+
+  /* debugging 
+     printf("SDCorMSC = %d\n", SDCorMSC);   */
+  if (Lrxdata < 20)
+
+    {
+      printf("msdhardmsc: length rxdata should be >= 20\n");
+      exit(1);
+    }
+  N = Lrxdata;
+  if (N < 20)
+    {
+      printf("msdhardmsc: N  has to be >= 20!\n");
+      exit(1);
+    }
+  if ((N1 < 0) || (N1 > N - 20))
+    {
+      printf("msdhardmsc: N1 has to be >= 0!\n");
+      exit(1);
+    }
+  if (Lvspp < 0)
+    {
+      printf("msdhardmsc: Lvspp has to be >= 0!\n");
+      exit(1);
+    }
+  if (maxiter < 0)
+    {
+      printf("msdhardmsc: maxiter must not be negativ.");
+      exit(1);
+    }
+  if (HMmix && (Lvspp == 0))
+    {
+      printf("msdhardmsc:  HMmix requires Lvspp > 0.");
+      exit(1);
+    }
+
+  /* printf("start mem alloc \n ");
+     printf("N= %d, no_of_levels= %d\n", N, no_of_levels);  */
+
+  /* memory allocation and initialization: */
+  no_of_bits = 0;
+  for (level = 0; level < no_of_levels; level++)
+    {
+      no_of_bits +=
+	(int) L1_real[level] + (int) L2_real[level] + 6 +
+	(int) L1_imag[level] + (int) L2_imag[level] + 6;
+
+      /* printf(" --- level %d L1real %d L2real %d L1imga %d L2imag %d\n",
+         level, (int)L1_real[level], (int)L2_real[level], (int)L1_imag[level], (int)L2_imag[level]);    */
+    } msd_mem_size =
+    2 * N * sizeof(float) + 2 * N * sizeof(char) + 2 * N * sizeof(char) +
+    no_of_bits * sizeof(char);
+  viterbi_mem_size =
+    STATES * sizeof(float) + STATES * sizeof(float) +
+    2 * N * STATES * sizeof(char);
+
+  /* printf("msdhardmsc: viterbi_mem_size is %d STATES is %d\n", viterbi_mem_size, STATES);   */
+  if (received_imag == NULL)
+    {
+      memory_ptr =
+	(char *) malloc(viterbi_mem_size + msd_mem_size + N * sizeof(double) +
+			2);
+      received_imag =
+	(double *) (memory_ptr + viterbi_mem_size + msd_mem_size);
+      memset(received_imag, 0, N * sizeof(double));
+    }
+  else
+    {
+      memory_ptr = (char *) malloc(viterbi_mem_size + msd_mem_size + 2);
+
+      /*        printf("msdhardmsc: debugging memory_ptr alloc succeeded viterbi_size = %d mds_size=%d  end addr is %x\n",
+         viterbi_mem_size, msd_mem_size, memory_ptr+ viterbi_mem_size+msd_mem_size);   */
+      if (memory_ptr == NULL)
+
+	{
+	  printf("msdhardmsc: cannot malloc for memory_ptr\n");
+	  exit(1);
+	}
+    }
+  if (!memory_ptr)
+    {
+      printf("Failed memory request!\n");
+      exit(1);
+    }
+  viterbi_mem = memory_ptr;
+  msd_mem = memory_ptr + viterbi_mem_size;
+  llr = (float *) msd_mem;
+  hardpoints = (char *) (msd_mem + 2 * N * sizeof(float));
+  lastiter =
+    (char *) (msd_mem + 2 * N * sizeof(float) + 2 * N * sizeof(char) + 2);
+  infoout[0] =
+    (char *) (msd_mem + 2 * N * sizeof(float) + 2 * N * sizeof(char) + 2 +
+	      2 * N * sizeof(char));
+  infoout[1] = 0;
+  for (m = 1; m < no_of_levels; m++)
+    {
+      infoout[m] =
+	infoout[m - 1] + (int) L1_real[m - 1] + (int) L2_real[m - 1] + 6 +
+	(int) L1_imag[m - 1] + (int) L2_imag[m - 1] + 6;
+
+      /* debugging pa0mbo 
+         printf("infoout[%d] = %p \n", m, infoout[m]);  */
+    } memset(hardpoints, 0, 2 * N * sizeof(char));
+
+  /* choosing partitioning type: */
+  if (no_of_levels == 3)
+    {
+      if ((Lvspp != 0) && HMmix)
+	{			/* HMmix 64-QAM */
+	  metric_real = partitioning[1];
+	  metric_imag = partitioning[0];
+	  rp_real[0] =
+	    (N - 12) -
+	    RY[(int) PL2_real[0]] * ((N - 12) / RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * (((N - N1) - 12) / RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * (((N - N1) - 12) / RY[(int) PL2_real[2]]);
+	  rp_imag[0] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[0]] * (((N - N1) - 12) / RY[(int) PL2_imag[0]]);
+	  rp_imag[1] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[1]] * (((N - N1) - 12) / RY[(int) PL2_imag[1]]);
+	  rp_imag[2] =
+	    ((N - N1) - 12) -
+	    RY[(int) PL2_imag[2]] * (((N - N1) - 12) / RY[(int) PL2_imag[2]]);
+	}
+      else if (Lvspp != 0)
+	{			/* HMsym 64-QAM */
+	  HMsym = 1;
+	  metric_real = partitioning[1];
+	  metric_imag = partitioning[1];
+	  rp_real[0] =
+	    (2 * N - 12) -
+	    RY[(int) PL2_real[0]] * ((2 * N - 12) / RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[2]]);
+	}
+      else
+	{			/* SM 64-QAM */
+	  metric_real = partitioning[0];
+	  metric_imag = partitioning[0];
+	  rp_real[0] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[0]]);
+	  rp_real[1] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[1]]);
+	  rp_real[2] =
+	    (2 * (N - N1) - 12) -
+	    RY[(int) PL2_real[2]] * ((2 * (N - N1) - 12) /
+				     RY[(int) PL2_real[2]]);
+    }}
+  else if (no_of_levels == 2)
+    {				/* SM 16-QAM */
+      rp_real[0] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[0]]);
+      rp_real[1] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[1]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[1]]);
+      metric_real = partitioning[2];
+      metric_imag = partitioning[2];
+
+      /* printf("SM 16 QAM\n"); */
+    }
+  else
+    {				/* SM 4-QAM */
+      rp_real[0] =
+	(2 * (N - N1) - 12) -
+	RY[(int) PL2_real[0]] * ((2 * (N - N1) - 12) / RY[(int) PL2_real[0]]);
+      metric_real = partitioning[3];
+      metric_imag = partitioning[3];
+  } if (!SDCorMSC)
+    {
+      rp_real[0] = -12;
+      rp_real[1] = -12;
+      rp_real[2] = -12;
+    }
+  if (Lvspp != 0)
+    {
+      L1_real[0] = 0;
+      L2_real[0] = (double) Lvspp;
+    }
+
+  /* debugging pa0mbo  
+     printf("=== voor  viterbi \n");
+     for (i=0; i < 2*N ; i++)
+     {
+     printf("hardpoints[%d] = %d \n",i,  hardpoints[i]);
+     }  */
+
+  /* Multi-Stage Decoding: */
+
+  /* first decoding: */
+  PL1 = PL1_real;
+  PL2 = PL2_real;
+  L1 = L1_real;
+  L2 = L2_real;
+  rp = rp_real;
+  first_metric = metric_real;
+  first_received = received_real;
+  hardpoints_ptr = hardpoints;
+
+  /* debugging  
+     printf("msdhardmsc: at start first decoding\n");
+     printf("PL1[0] = %g, PL2[0]= %g, L1_real[0]= %g, L2_real[0]=%g, L1[0]=%g, L2[0]= %g, rp[0]= %d\n",
+     PL1[0], PL2[0], L1_real[0], L2_real[0], L1[0], L2[0], rp[0]);   */
+  for (n = 0; n <= HMmix; n++)
+
+    {
+      for (level = 0; level < no_of_levels; level++)
+
+	{
+	  metric = first_metric;
+	  received = first_received;
+	  for (m = 0; m < 2 - HMmix; m++)
+
+	    {			/* for real and imaginary part */
+	      for (sample_index = m; sample_index < (2 - HMmix) * N;
+		   sample_index += 2 - HMmix)
+
+		{
+		  sample = (float) received[sample_index >> (1 - HMmix)];	/* extract real or imaginary part respectively */
+		  closest_zero =
+		    fabs(sample - metric[(int) hardpoints_ptr[sample_index]]);
+
+		  /* printf("msdhardmsc: index= %d  sample = %g metric = %g closest_zero = %g \n",
+		     sample_index, sample,  metric[hardpoints_ptr[sample_index]], closest_zero);   pa0mbo */
+		  for (subset_point = (0x1 << (level + 1));
+		       subset_point < (0x1 << no_of_levels);
+		       subset_point += (0x1 << (level + 1)))
+
+		    {
+		      dist =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] +
+				    subset_point]);
+		      if (dist < closest_zero)
+
+			{
+			  closest_zero = dist;
+			}
+		    }
+		  closest_one =
+		    fabs(sample -
+			 metric[hardpoints_ptr[sample_index] +
+				(0x1 << level)]);
+
+		  /* printf("closest_one %g\n", closest_one);  pa0mbo */
+		  for (subset_point = (0x3 << level);
+		       subset_point < (0x1 << no_of_levels);
+		       subset_point += (0x1 << (level + 1)))
+
+		    {
+		      dist =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] +
+				    subset_point]);
+		      if (dist < closest_one)
+
+			{
+			  closest_one = dist;
+			}
+		    }
+
+		  /* printf("final closest_zero=%g closest_one=%g\n", closest_zero, closest_one);  pa0mbo */
+
+#ifdef CONSIDERING_SNR
+		  SNR =
+		    (float) signal_to_noise_ratio[sample_index >>
+						  (1 - HMmix)];
+		  llr[sample_index] = (closest_zero - closest_one) * SNR;
+
+		  /* printf("llr[%d] = %g\n", sample_index, llr[sample_index]);   */
+
+		  /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one) * SNR * SNR; */
+#else /*  */
+		  llr[sample_index] = (closest_zero - closest_one);
+
+		  /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one); */
+#endif /*  */
+		}		/* end loop sample_index */
+	      metric = metric_imag;
+	      received = received_imag;
+	    }			/* end loop m */
+
+	  /* printf(" level %d HMsym %d HMmix %d N1 %d n %d\n", level, HMsym, HMmix, N1, n);  
+	     printf("msdhardmsc: (level || (!HMsym ..) = %d\n", (level || (!HMsym && (n || !HMmix)))* (2 - HMmix)*N1 ); 
+	     printf("eerste viter PL1[0] %g PL2[0] %g L1[0] %g L2[0] %g L1_real[0] %g L2_real[0] %g rp[0] %d\n",
+	     PL1[0], PL2[0], L1[0], L2[0], L1_real[0], L2_real[0], rp[0]);  */
+	  /* for (i=0; i < 17 ; i++)
+	     printf("puncturing[6][%d] = %d\n", i, puncturing[6][i]);  
+	     printf("level %d rp[level] %d \n", level, rp[level]);
+	     printf("tailpuncturing[... ] = %p\n", tailpuncturing[rp[level]]);
+	     for (i=0; i < 13 ; i++)
+	     printf("inhoud is %d ", tailpuncturing[rp[level]][i]);  */
+	  error = viterbi_decode(llr, (2 - HMmix) * N,
+				 (level
+				  || (!HMsym
+				      && (n
+					  || !HMmix))) * (2 - HMmix) * N1,
+				 puncturing[(int) PL1[level]],
+				 puncturing[(int) PL2[level]],
+				 tailpuncturing[rp[level]],
+				 infoout[level] + n * ((int) L1_real[level] +
+						       (int) L2_real[level] +
+						       6), hardpoints_ptr,
+				 level,
+				 Deinterleaver + (2 - HMmix) * N * level,
+				 (int) L1[level] + (int) L2[level] + 6,
+				 rp[level] + 12, viterbi_mem);
+
+	  /* debugging pa0mbo  
+	     printf("=== na eerste viterbi \n");
+	     for (i=0; i < 2*N ; i++)
+	     {
+	     printf("infoout[0][%d] = %d \n",i,  infoout[0][i]);
+	     }   */
+	  if (error)
+
+	    {
+	      free(memory_ptr);
+	      printf("msdhardmsc: Error in Viterbi decoder");
+	      return 1;
+	    }
+	}			/* end loop level */
+      PL1 = PL1_imag;
+      PL2 = PL2_imag;
+      L1 = L1_imag;
+      L2 = L2_imag;
+      rp = rp_imag;
+      first_metric = metric_imag;
+      first_received = received_imag;
+      hardpoints_ptr = hardpoints + N;
+    }				/* end loop over n */
+  diff = 1;
+  iteration = 0;
+
+  /* iterations: */
+  while (iteration < maxiter)
+
+    {
+      PL1 = PL1_real;
+      PL2 = PL2_real;
+      L1 = L1_real;
+      L2 = L2_real;
+      rp = rp_real;
+      first_metric = metric_real;
+      first_received = received_real;
+      hardpoints_ptr = hardpoints;
+
+#ifdef ITER_BREAK
+      memcpy(lastiter, hardpoints, 2 * N);
+
+#endif /*  */
+      for (n = 0; n <= HMmix; n++)
+
+	{
+	  for (level = 0; level < no_of_levels; level++)
+
+	    {
+	      metric = first_metric;
+	      received = first_received;
+	      for (m = 0; m < 2 - HMmix; m++)
+
+		{		/* for real and imaginary part */
+		  for (sample_index = m; sample_index < (2 - HMmix) * N;
+		       sample_index += 2 - HMmix)
+
+		    {
+		      sample = (float) received[sample_index >> (1 - HMmix)];	/* extract real or imaginary part respectively */
+		      closest_zero =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] &
+				    ~(0x1 << level)]);
+		      closest_one =
+			fabs(sample -
+			     metric[hardpoints_ptr[sample_index] |
+				    (0x1 << level)]);
+
+#ifdef CONSIDERING_SNR
+		      SNR =
+			(float) signal_to_noise_ratio[sample_index >>
+						      (1 - HMmix)];
+		      llr[sample_index] = (closest_zero - closest_one) * SNR;
+
+		      /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one) * SNR * SNR; */
+#else /*  */
+		      llr[sample_index] = (closest_zero - closest_one);
+
+		      /* llr[sample_index] = (closest_zero*closest_zero - closest_one*closest_one); */
+#endif /*  */
+		    }		/* end loop over sample_index */
+		  metric = metric_imag;
+		  received = received_imag;
+		}		/* end loop over m */
+
+	      /* printf("Tweede viterbi PL1[0] %g PL2[0] %g L1[0] %g L2[0] %g L1_real[0] %g L2_real[0] %g rp[0] %d\n",
+	         PL1[0], PL2[0], L1[0], L2[0], L1_real[0], L2_real[0], rp[0]);   */
+	      error = viterbi_decode(llr,
+				     (2 - HMmix) * N,
+				     (level
+				      || (!HMsym
+					  && (n
+					      || !HMmix))) * (2 - HMmix) * N1,
+				     puncturing[(int) PL1[level]],
+				     puncturing[(int) PL2[level]],
+				     tailpuncturing[rp[level]],
+				     infoout[level] +
+				     n * ((int) L1_real[level] +
+					  (int) L2_real[level] + 6),
+				     hardpoints_ptr, level,
+				     Deinterleaver + (2 - HMmix) * N * level,
+				     (int) L1[level] + (int) L2[level] + 6,
+				     rp[level] + 12, viterbi_mem);
+	      if (error)
+
+		{
+		  free(memory_ptr);
+		  printf("msdhardmsc: Error in Viterbi decoder");
+		  return 1;
+		}
+
+#ifdef ITER_BREAK
+	      if (level == 0)
+
+		{
+		  diff = 0;
+      for (sample_index = 0;sample_index <(int)((2 - HMmix) * N * sizeof(char) / sizeof(int));sample_index++)
+        {
+		      diff +=
+			(((int *) hardpoints)[sample_index] ^
+			 ((int *) lastiter)[sample_index]) != 0;
+		    }
+		  /*diff = memcmp (lastiter,hardpoints,2 * N); */
+		  if (!diff)
+
+		    {
+		      break;
+		    }
+		}
+
+#endif /*  */
+	    }			/* for (level = 0; level < no_of_levels; level++) */
+	  PL1 = PL1_imag;
+	  PL2 = PL2_imag;
+	  L1 = L1_imag;
+	  L2 = L2_imag;
+	  rp = rp_imag;
+	  first_metric = metric_imag;
+	  first_received = received_imag;
+	  hardpoints_ptr = hardpoints + N;
+	}			/* for (n = 0; n <= HMmix; n++) */
+
+#ifdef ITER_BREAK
+      if (!diff)
+
+	{
+	  break;
+	}
+
+#endif /*  */
+      iteration++;
+    }				/* while (iteration < maxiter) */
+
+  /* Energy Dispersal */
+  no_of_bits = 0;
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      no_of_bits += (int) L1_real[level] + (int) L2_real[level];
+  } for (level = 0; level < no_of_levels; level++)
+    {
+      no_of_bits += (int) L1_imag[level] + (int) L2_imag[level];
+    } output_ptr = SPPhard;
+  PRBS_INIT(PRBS_reg);
+  n = 0;
+  if (HMmix)
+    {
+      for (m = Lvspp + 6; m < Lvspp + 6 + (int) L1_imag[0]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[0][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+
+  /* printf("msdhardmsc: 1e n %d\n", n); */
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      for (m = 0; m < (int) L1_real[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+	}
+      /* printf("msdhardmsc: 2a n %d\n", n); */
+      for (m = (int) L1_real[level] + (int) L2_real[level] + 6;
+	   m <
+	   (int) L1_real[level] + (int) L2_real[level] + 6 +
+	   (int) L1_imag[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+  /*  printf("msdhardmsc: 2b n %d\n", n);  */
+  if (HMmix)
+    {
+      for (m = Lvspp + 6 + (int) L1_imag[0];
+	   m < Lvspp + 6 + (int) L1_imag[0] + (int) L2_imag[0]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[0][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+
+  /* printf("msdhardmsc: 3e n %d\n", n); */
+  for (level = (Lvspp != 0); level < no_of_levels; level++)
+    {
+      for (m = (int) L1_real[level];
+	   m < (int) L1_real[level] + (int) L2_real[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+      } for (m =
+	       (int) L1_real[level] + (int) L2_real[level] + 6 +
+	       (int) L1_imag[level];
+	       m <
+	       (int) L1_real[level] + (int) L2_real[level] + 6 +
+	       (int) L1_imag[level] + (int) L2_imag[level]; m++)
+	{
+	  output_ptr[n++] = (double) (infoout[level][m] ^ PRBS_BIT(PRBS_reg));
+	  PRBS_SHIFT(PRBS_reg);
+    }}
+
+/*         printf("msdhardmsc: 4e n %d\n", n);  */
+  PRBS_INIT(PRBS_reg);
+  if (Lvspp != 0)
+    {
+      printf
+	("msdhardmsc: There is a very strongly protected part, but no variable to put it into!");
+    }
+  no_of_bits = Lvspp;
+  output_ptr = VSPPhard;
+  for (m = 0; m < Lvspp; m++)
+    {
+      output_ptr[m] = (double) (infoout[0][m] ^ PRBS_BIT(PRBS_reg));
+      PRBS_SHIFT(PRBS_reg);
+    } output_ptr = iterations;
+  output_ptr[0] = (double) iteration;
+  output_ptr = calc_variance;
+  variance = 0.0;
+  for (sample_index = 0; sample_index < N; sample_index++)
+    {
+      sample = (float) received_real[sample_index];	/* extract real part respectively */
+      dist =
+	(sample - metric_real[(int) hardpoints[(2 - HMmix) * sample_index]]);
+      variance += (double) dist *(double) dist;
+
+      sample = (float) received_imag[sample_index];	/* extract imaginary part respectively */
+      dist =
+	(sample -
+	 metric_imag[(int)
+		     hardpoints[HMmix * (N - 1) + (2 - HMmix) * sample_index +
+				1]]);
+      variance += (double) dist *(double) dist;
+    } output_ptr[0] = variance / ((double) N);
+  output_ptr = noise_signal;
+  for (sample_index = 0; sample_index < N; sample_index++)
+    {
+      sample = (float) received_real[sample_index];	/* extract real part */
+      output_ptr[sample_index * 2] =
+	(sample - metric_real[(int) hardpoints[(2 - HMmix) * sample_index]]);
+      sample = (float) received_imag[sample_index];	/* extract imaginary part */
+      output_ptr[2 * sample_index + 1] =
+	(sample -
+	 metric_imag[(int)
+		     hardpoints[HMmix * (N - 1) + (2 - HMmix) * sample_index +
+				1]]);
+    } free(memory_ptr);
+  return n;
+}
diff --git a/qsstv/drmrx/newfft.cpp b/qsstv/drmrx/newfft.cpp
new file mode 100644
index 0000000..fc6ea2d
--- /dev/null
+++ b/qsstv/drmrx/newfft.cpp
@@ -0,0 +1,206 @@
+#include <math.h>
+#include <float.h>
+
+
+/******************************************************************************
+ *
+ *  MiXViews - an X window system based sound & data editor/processor
+ *
+ *  Copyright (c) 1993, 1994 Regents of the University of California
+ *
+ *  Author:     Douglas Scott
+ *  Date:       December 13, 1994
+ *
+ *  Permission to use, copy and modify this software and its documentation
+ *  for research and/or educational purposes and without fee is hereby granted,
+ *  provided that the above copyright notice appear in all copies and that
+ *  both that copyright notice and this permission notice appear in
+ *  supporting documentation. The author reserves the right to distribute this
+ *  software and its documentation.  The University of California and the author
+ *  make no representations about the suitability of this software for any 
+ *  purpose, and in no event shall University of California be liable for any
+ *  damage, loss of data, or profits resulting from its use.
+ *  It is provided "as is" without express or implied warranty.
+ *
+ ******************************************************************************/
+void rfft(float *, int, int);
+void cfft(float *, int, int);
+static double twopi = M_PI * 2.0;
+static double pi = M_PI;
+
+
+
+/* If forward is true, rfft replaces 2*N real data points in buf with
+	 N complex values representing the positive frequency half of their
+	 Fourier spectrum, with *(buf+1) replaced with the real part of the Nyquist
+	 frequency value.	If forward is false, rfft expects buf to contain a
+	 positive frequency spectrum arranged as before, and replaces it with
+	 2*N real values.	N MUST be a power of 2. */
+void rfft(float *buf, int N2, int forward)
+{
+  float c2, h1r, h1i, h2r, h2i, temp;
+  float br, bi;
+  float theta = (float) (pi / N2);
+  float wr = 1.;
+  float wi = 0.;
+  float c1 = 0.5;
+  float wpr;
+  float wpi;
+  int N2p1;
+  int i, i1, i2, i3, i4;
+
+
+  /* debugging pa0mbo 
+     printf("N2 is %d\n",N2); */
+  if (forward == 1)
+    {
+      c2 = -0.5;
+      cfft(buf, N2, forward);
+
+      /* debugging pa0mbo 
+         printf("na cfft\n"); */
+      br = *buf;
+      bi = *(buf + 1);
+
+      /* debuging pa0mbo 
+         printf(" na br ni = \n"); */
+    }
+
+  else
+    {
+      c2 = 0.5;
+      theta = -theta;
+      br = *(buf + 1);
+      bi = 0.;
+      *(buf + 1) = 0.;
+    }
+  wpr = (float) (-2. * pow(sin(0.5 * theta), 2.));
+  wpi = (float) sin(theta);
+  N2p1 = (N2 << 1) + 1;
+
+  /* debugging pa0mbo 
+     printf(" N2p1 is %d\n",N2p1); */
+  for (i = 0; i <= N2 >> 1; i++)
+    {
+      i1 = i << 1;
+      i2 = i1 + 1;
+      i3 = N2p1 - i2;
+      i4 = i3 + 1;
+      if (i == 0)
+	{
+	  h1r = c1 * (*(buf + i1) + br);
+	  h1i = c1 * (*(buf + i2) - bi);
+	  h2r = -c2 * (*(buf + i2) + bi);
+	  h2i = c2 * (*(buf + i1) - br);
+	  *(buf + i1) = h1r + (wr * h2r) - (wi * h2i);
+	  *(buf + i2) = h1i + (wr * h2i) + (wi * h2r);
+	  br = h1r - (wr * h2r) + (wi * h2i);
+	  bi = -h1i + (wr * h2i) + (wi * h2r);
+	}
+
+      else
+	{
+	  h1r = c1 * (*(buf + i1) + *(buf + i3));
+	  h1i = c1 * (*(buf + i2) - *(buf + i4));
+	  h2r = -c2 * (*(buf + i2) + *(buf + i4));
+	  h2i = c2 * (*(buf + i1) - *(buf + i3));
+	  *(buf + i1) = h1r + wr * h2r - wi * h2i;
+	  *(buf + i2) = h1i + wr * h2i + wi * h2r;
+	  *(buf + i3) = h1r - wr * h2r + wi * h2i;
+	  *(buf + i4) = -h1i + wr * h2i + wi * h2r;
+	}
+      wr = ((temp = wr) * wpr) - (wi * wpi) + wr;
+      wi = (wi * wpr) + (temp * wpi) + wi;
+    }
+  if (forward == 1)
+    *(buf + 1) = br;
+
+  else
+    cfft(buf, N2, forward);
+}
+
+
+
+/* cfft replaces float array x containing NC complex values
+	 (2*NC float values alternating real, imagininary, etc.)
+	 by its Fourier transform if forward is true, or by its
+	 inverse Fourier transform if forward is false, using a
+	 recursive Fast Fourier transform method due to Danielson
+	 and Lanczos.	NC MUST be a power of 2. */
+void bitreverse(float *, int);
+void cfft(float *buf, int N2, int forward)
+{
+  int delta;
+  int ND = N2 << 1;
+  int mmax;
+  float theta, wpr, wpi, wr, wi;
+  float rtemp, itemp;
+  int i, j, m;
+  float scale;
+  register float *bi, *be;
+
+  bitreverse(buf, ND);
+  for (mmax = 2; mmax < ND; mmax = delta)
+    {
+      delta = mmax << 1;
+      theta = (float) (twopi / ((forward == 1) ? mmax : -mmax));
+      wpr = (float) (-2. * pow(sin(0.5 * theta), 2.));
+      wpi = (float) sin(theta);
+      wr = 1.;
+      wi = 0.;
+      for (m = 0; m < mmax; m += 2)
+	{
+	  for (i = m; i < ND; i += delta)
+	    {
+	      j = i + mmax;
+	      rtemp = (wr * *(buf + j)) - (wi * *(buf + j + 1));
+	      itemp = (wr * *(buf + j + 1)) + (wi * *(buf + j));
+	      *(buf + j) = *(buf + i) - rtemp;
+	      *(buf + j + 1) = *(buf + i + 1) - itemp;
+	      *(buf + i) += rtemp;
+	      *(buf + i + 1) += itemp;
+	    }
+	  wr = ((rtemp = wr) * wpr) - (wi * wpi) + wr;
+	  wi = (wi * wpr) + (rtemp * wpi) + wi;
+	}
+    }
+
+
+/* scale output */
+
+
+/*	scale = forward ? 1./ND : 2.;		 this is the original */
+  scale = (float) ((forward == 1) ? 1.0 : 1.0 / ND);
+  if ((fabs(scale) - 1.0) < DBL_EPSILON)
+    {
+      bi = buf;
+      be = buf + ND;
+      while (bi < be)
+	*bi++ *= scale;
+    }
+}
+
+
+
+/* bitreverse places float array x containing N/2 complex values
+	 into bit-reversed order   */
+void bitreverse(float *buf, int N)
+{
+  int i, j, m;
+
+  for (i = j = 0; i < N; i += 2, j += m)
+    {
+      if (j > i)
+	{
+	  float rtemp = *(buf + j);	/* complex exchange */
+	  float itemp = *(buf + j + 1);
+
+	  *(buf + j) = *(buf + i);
+	  *(buf + j + 1) = *(buf + i + 1);
+	  *(buf + i) = rtemp;
+	  *(buf + i + 1) = itemp;
+	}
+      for (m = N >> 1; m >= 2 && j >= m; m >>= 1)
+	j -= m;
+    }
+}
diff --git a/qsstv/drmrx/nrutil.cpp b/qsstv/drmrx/nrutil.cpp
new file mode 100644
index 0000000..7b551de
--- /dev/null
+++ b/qsstv/drmrx/nrutil.cpp
@@ -0,0 +1,702 @@
+#if defined(__STDC__) || defined(ANSI) || defined(NRANSI)	/* ANSI */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#define NR_END 1
+#define FREE_ARG char*
+
+
+
+void nrerror(const char *error_text)
+/* Numerical Recipes standard error handler */
+{
+  fprintf(stderr, "Numerical Recipes run-time error...\n");
+  fprintf(stderr, "%s\n", error_text);
+  fprintf(stderr, "...now exiting to system...\n");
+  exit(1);
+}
+
+// allocate a float vector with subscript range v[nl..nh]
+float *fvector(long nl, long nh)
+{
+  float *v;
+  v = (float *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(float)));
+  if (!v) nrerror("allocation failure in vector()");
+  return v - nl + NR_END;
+}
+
+// allocate an int vector with subscript range v[nl..nh]
+int *ivector(long nl, long nh)
+{
+  int *v;
+  v = (int *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(int)));
+  if (!v)
+    nrerror("allocation failure in ivector()");
+  return v - nl + NR_END;
+}
+
+// allocate an unsigned char vector with subscript range v[nl..nh]
+unsigned char *cvector(long nl, long nh)
+{
+  unsigned char *v;
+  v = (unsigned char *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(unsigned char)));
+  if (!v) nrerror("allocation failure in cvector()");
+  return v - nl + NR_END;
+}
+
+// allocate an unsigned long vector with subscript range v[nl..nh]
+unsigned long *lvector(long nl, long nh)
+{
+  unsigned long *v;
+  v = (unsigned long *)  malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(long)));
+  if (!v) nrerror("allocation failure in lvector()");
+  return v - nl + NR_END;
+}
+
+// allocate a double vector with subscript range v[nl..nh]
+double *dvector(long nl, long nh)
+{
+  double *v;
+  v = (double *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(double)));
+  if (!v) nrerror("allocation failure in dvector()");
+  return v - nl + NR_END;
+}
+
+// allocate a float matrix with subscript range m[nrl..nrh][ncl..nch]
+float **matrix(long nrl, long nrh, long ncl, long nch)
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  float **m;
+  /* allocate pointers to rows */
+  m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *)));
+  if (!m) nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] = (float *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(float)));
+  if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++) m[i] = m[i - 1] + ncol;
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+// allocate a double matrix with subscript range m[nrl..nrh][ncl..nch]
+double **dmatrix(long nrl, long nrh, long ncl, long nch)
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  double **m;
+  /* allocate pointers to rows */
+  m = (double **) malloc((size_t) ((nrow + NR_END) * sizeof(double *)));
+  if (!m)
+    nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] =
+    (double *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(double)));
+  if (!m[nrl])
+    nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++)
+    m[i] = m[i - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+
+// allocate a int matrix with subscript range m[nrl..nrh][ncl..nch]
+int **imatrix(long nrl, long nrh, long ncl, long nch)
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  int **m;
+
+
+  /* allocate pointers to rows */
+  m = (int **) malloc((size_t) ((nrow + NR_END) * sizeof(int *)));
+  if (!m)
+    nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] = (int *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(int)));
+  if (!m[nrl])
+    nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++)
+    m[i] = m[i - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long ,long newrl, long newcl)
+/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
+{
+  long i, j, nrow = oldrh - oldrl + 1, ncol = oldcl - newcl;
+  float **m;
+
+  /* allocate array of pointers to rows */
+  m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *)));
+  if (!m)
+    nrerror("allocation failure in submatrix()");
+  m += NR_END;
+  m -= newrl;
+
+  /* set pointers to rows */
+  for (i = oldrl, j = newrl; i <= oldrh; i++, j++)
+    m[j] = a[i] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch)
+/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
+declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
+and ncol=nch-ncl+1. The routine should be called with the address
+&a[0][0] as the first argument. */
+{
+  long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  float **m;
+
+
+  /* allocate pointers to rows */
+  m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *)));
+  if (!m)
+    nrerror("allocation failure in convert_matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* set pointers to rows */
+  m[nrl] = a - ncl;
+  for (i = 1, j = nrl + 1; i < nrow; i++, j++)
+    m[j] = m[j - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh)
+/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
+{
+  long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1, ndep = ndh - ndl + 1;
+  float ***t;
+
+
+  /* allocate pointers to pointers to rows */
+  t = (float ***) malloc((size_t) ((nrow + NR_END) * sizeof(float **)));
+  if (!t)
+    nrerror("allocation failure 1 in f3tensor()");
+  t += NR_END;
+  t -= nrl;
+
+  /* allocate pointers to rows and set pointers to them */
+  t[nrl] =
+    (float **) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(float *)));
+  if (!t[nrl])
+    nrerror("allocation failure 2 in f3tensor()");
+  t[nrl] += NR_END;
+  t[nrl] -= ncl;
+
+  /* allocate rows and set pointers to them */
+  t[nrl][ncl] =
+    (float *)
+    malloc((size_t) ((nrow * ncol * ndep + NR_END) * sizeof(float)));
+  if (!t[nrl][ncl])
+    nrerror("allocation failure 3 in f3tensor()");
+  t[nrl][ncl] += NR_END;
+  t[nrl][ncl] -= ndl;
+  for (j = ncl + 1; j <= nch; j++)
+    t[nrl][j] = t[nrl][j - 1] + ndep;
+  for (i = nrl + 1; i <= nrh; i++)
+    {
+      t[i] = t[i - 1] + ncol;
+      t[i][ncl] = t[i - 1][ncl] + ncol * ndep;
+      for (j = ncl + 1; j <= nch; j++)
+	t[i][j] = t[i][j - 1] + ndep;
+    }
+
+  /* return pointer to array of pointers to rows */
+  return t;
+}
+void free_fvector(float *v, long nl, long )
+/* free a float vector allocated with vector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+}
+
+void free_ivector(int *v, long nl, long )
+/* free an int vector allocated with ivector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+}
+void free_cvector(unsigned char *v, long nl, long )
+/* free an unsigned char vector allocated with cvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+}
+
+void free_lvector(unsigned long *v, long nl, long )
+/* free an unsigned long vector allocated with lvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+}
+
+void free_dvector(double *v, long nl, long )
+/* free a double vector allocated with dvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+}
+
+void free_matrix(float **m, long nrl, long , long ncl, long )
+/* free a float matrix allocated by matrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+}
+
+void free_dmatrix(double **m, long nrl, long , long ncl, long )
+/* free a double matrix allocated by dmatrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+}
+
+void free_imatrix(int **m, long nrl, long , long ncl, long )
+/* free an int matrix allocated by imatrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+}
+
+void free_submatrix(float **b, long nrl, long , long , long )
+/* free a submatrix allocated by submatrix() */
+{
+  free((FREE_ARG) (b + nrl - NR_END));
+}
+
+void free_convert_matrix(float **b, long nrl, long , long , long )
+/* free a matrix allocated by convert_matrix() */
+{
+  free((FREE_ARG) (b + nrl - NR_END));
+}
+
+void free_f3tensor(float ***t, long nrl, long , long ncl, long , long ndl,long )
+/* free a float f3tensor allocated by f3tensor() */
+{
+  free((FREE_ARG) (t[nrl][ncl] + ndl - NR_END));
+  free((FREE_ARG) (t[nrl] + ncl - NR_END));
+  free((FREE_ARG) (t + nrl - NR_END));
+}
+
+#else /* ANSI */
+
+/* traditional - K&R */
+
+#include <stdio.h>
+#define NR_END 1
+#define FREE_ARG char*
+void nrerror(error_text)
+     char error_text[];
+
+
+
+/* Numerical Recipes standard error handler */
+{
+  void exit();
+
+  fprintf(stderr, "Numerical Recipes run-time error...\n");
+  fprintf(stderr, "%s\n", error_text);
+  fprintf(stderr, "...now exiting to system...\n");
+  exit(1);
+} float *vector(nl, nh)
+     long nh, nl;
+
+
+
+/* allocate a float vector with subscript range v[nl..nh] */
+{
+  float *v;
+  v =
+    (float *) malloc((unsigned int) ((nh - nl + 1 + NR_END) * sizeof(float)));
+  if (!v)
+    nrerror("allocation failure in vector()");
+  return v - nl + NR_END;
+}
+
+int *ivector(nl, nh)
+     long nh, nl;
+
+
+
+/* allocate an int vector with subscript range v[nl..nh] */
+{
+  int *v;
+  v = (int *) malloc((unsigned int) ((nh - nl + 1 + NR_END) * sizeof(int)));
+  if (!v)
+    nrerror("allocation failure in ivector()");
+  return v - nl + NR_END;
+}
+unsigned char *cvector(nl, nh)
+     long nh, nl;
+
+
+
+/* allocate an unsigned char vector with subscript range v[nl..nh] */
+{
+  unsigned char *v;
+  v =
+    (unsigned char *)
+    malloc((unsigned int) ((nh - nl + 1 + NR_END) * sizeof(unsigned char)));
+  if (!v)
+    nrerror("allocation failure in cvector()");
+  return v - nl + NR_END;
+}
+unsigned long *lvector(nl, nh)
+     long nh, nl;
+
+
+
+/* allocate an unsigned long vector with subscript range v[nl..nh] */
+{
+  unsigned long *v;
+  v =
+    (unsigned long *)
+    malloc((unsigned int) ((nh - nl + 1 + NR_END) * sizeof(long)));
+  if (!v)
+    nrerror("allocation failure in lvector()");
+  return v - nl + NR_END;
+}
+
+double *dvector(nl, nh)
+     long nh, nl;
+
+
+
+/* allocate a double vector with subscript range v[nl..nh] */
+{
+  double *v;
+  v =
+    (double *)
+    malloc((unsigned int) ((nh - nl + 1 + NR_END) * sizeof(double)));
+  if (!v)
+    nrerror("allocation failure in dvector()");
+  return v - nl + NR_END;
+}
+
+float **matrix(nrl, nrh, ncl, nch)
+     long nch, ncl, nrh, nrl;
+
+
+
+/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  float **m;
+
+
+  /* allocate pointers to rows */
+  m = (float **) malloc((unsigned int) ((nrow + NR_END) * sizeof(float *)));
+  if (!m)
+    nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] =
+    (float *) malloc((unsigned int) ((nrow * ncol + NR_END) * sizeof(float)));
+  if (!m[nrl])
+    nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++)
+    m[i] = m[i - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+double **dmatrix(nrl, nrh, ncl, nch)
+     long nch, ncl, nrh, nrl;
+
+
+
+/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  double **m;
+
+
+  /* allocate pointers to rows */
+  m = (double **) malloc((unsigned int) ((nrow + NR_END) * sizeof(double *)));
+  if (!m)
+    nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] =
+    (double *)
+    malloc((unsigned int) ((nrow * ncol + NR_END) * sizeof(double)));
+  if (!m[nrl])
+    nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++)
+    m[i] = m[i - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+int **imatrix(nrl, nrh, ncl, nch)
+     long nch, ncl, nrh, nrl;
+
+
+
+/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+  long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  int **m;
+
+
+  /* allocate pointers to rows */
+  m = (int **) malloc((unsigned int) ((nrow + NR_END) * sizeof(int *)));
+  if (!m)
+    nrerror("allocation failure 1 in matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* allocate rows and set pointers to them */
+  m[nrl] =
+    (int *) malloc((unsigned int) ((nrow * ncol + NR_END) * sizeof(int)));
+  if (!m[nrl])
+    nrerror("allocation failure 2 in matrix()");
+  m[nrl] += NR_END;
+  m[nrl] -= ncl;
+  for (i = nrl + 1; i <= nrh; i++)
+    m[i] = m[i - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+float **submatrix(a, oldrl, oldrh, oldcl, oldch, newrl, newcl)
+     float **a;
+     long newcl, newrl, oldch, oldcl, oldrh, oldrl;
+
+
+
+/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
+{
+  long i, j, nrow = oldrh - oldrl + 1, ncol = oldcl - newcl;
+  float **m;
+
+
+  /* allocate array of pointers to rows */
+  m = (float **) malloc((unsigned int) ((nrow + NR_END) * sizeof(float *)));
+  if (!m)
+    nrerror("allocation failure in submatrix()");
+  m += NR_END;
+  m -= newrl;
+
+  /* set pointers to rows */
+  for (i = oldrl, j = newrl; i <= oldrh; i++, j++)
+    m[j] = a[i] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+float **convert_matrix(a, nrl, nrh, ncl, nch)
+     float *a;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
+declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
+and ncol=nch-ncl+1. The routine should be called with the address
+&a[0][0] as the first argument. */
+{
+  long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
+  float **m;
+
+
+  /* allocate pointers to rows */
+  m = (float **) malloc((unsigned int) ((nrow + NR_END) * sizeof(float *)));
+  if (!m)
+    nrerror("allocation failure in convert_matrix()");
+  m += NR_END;
+  m -= nrl;
+
+  /* set pointers to rows */
+  m[nrl] = a - ncl;
+  for (i = 1, j = nrl + 1; i < nrow; i++, j++)
+    m[j] = m[j - 1] + ncol;
+
+  /* return pointer to array of pointers to rows */
+  return m;
+}
+
+float ***f3tensor(nrl, nrh, ncl, nch, ndl, ndh)
+     long nch, ncl, ndh, ndl, nrh, nrl;
+
+
+
+/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
+{
+  long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1, ndep = ndh - ndl + 1;
+  float ***t;
+
+
+  /* allocate pointers to pointers to rows */
+  t = (float ***) malloc((unsigned int) ((nrow + NR_END) * sizeof(float **)));
+  if (!t)
+    nrerror("allocation failure 1 in f3tensor()");
+  t += NR_END;
+  t -= nrl;
+
+  /* allocate pointers to rows and set pointers to them */
+  t[nrl] =
+    (float **)
+    malloc((unsigned int) ((nrow * ncol + NR_END) * sizeof(float *)));
+  if (!t[nrl])
+    nrerror("allocation failure 2 in f3tensor()");
+  t[nrl] += NR_END;
+  t[nrl] -= ncl;
+
+  /* allocate rows and set pointers to them */
+  t[nrl][ncl] =
+    (float *)
+    malloc((unsigned int) ((nrow * ncol * ndep + NR_END) * sizeof(float)));
+  if (!t[nrl][ncl])
+    nrerror("allocation failure 3 in f3tensor()");
+  t[nrl][ncl] += NR_END;
+  t[nrl][ncl] -= ndl;
+  for (j = ncl + 1; j <= nch; j++)
+    t[nrl][j] = t[nrl][j - 1] + ndep;
+  for (i = nrl + 1; i <= nrh; i++)
+    {
+      t[i] = t[i - 1] + ncol;
+      t[i][ncl] = t[i - 1][ncl] + ncol * ndep;
+      for (j = ncl + 1; j <= nch; j++)
+	t[i][j] = t[i][j - 1] + ndep;
+    }
+
+  /* return pointer to array of pointers to rows */
+  return t;
+}
+
+void free_vector(v, nl, nh)
+     float *v;
+     long nh, nl;
+
+
+
+/* free a float vector allocated with vector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+} void free_ivector(v, nl, nh)
+     int *v;
+     long nh, nl;
+
+
+
+/* free an int vector allocated with ivector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+} void free_cvector(v, nl, nh)
+     long nh, nl;
+     unsigned char *v;
+
+
+
+/* free an unsigned char vector allocated with cvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+} void free_lvector(v, nl, nh)
+     long nh, nl;
+     unsigned long *v;
+
+
+
+/* free an unsigned long vector allocated with lvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+} void free_dvector(v, nl, nh)
+     double *v;
+     long nh, nl;
+
+
+
+/* free a double vector allocated with dvector() */
+{
+  free((FREE_ARG) (v + nl - NR_END));
+} void free_matrix(m, nrl, nrh, ncl, nch)
+     float **m;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* free a float matrix allocated by matrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+} void free_dmatrix(m, nrl, nrh, ncl, nch)
+     double **m;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* free a double matrix allocated by dmatrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+} void free_imatrix(m, nrl, nrh, ncl, nch)
+     int **m;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* free an int matrix allocated by imatrix() */
+{
+  free((FREE_ARG) (m[nrl] + ncl - NR_END));
+  free((FREE_ARG) (m + nrl - NR_END));
+} void free_submatrix(b, nrl, nrh, ncl, nch)
+     float **b;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* free a submatrix allocated by submatrix() */
+{
+  free((FREE_ARG) (b + nrl - NR_END));
+} void free_convert_matrix(b, nrl, nrh, ncl, nch)
+     float **b;
+     long nch, ncl, nrh, nrl;
+
+
+
+/* free a matrix allocated by convert_matrix() */
+{
+  free((FREE_ARG) (b + nrl - NR_END));
+} void free_f3tensor(t, nrl, nrh, ncl, nch, ndl, ndh)
+     float ***t;
+     long nch, ncl, ndh, ndl, nrh, nrl;
+
+
+
+/* free a float f3tensor allocated by f3tensor() */
+{
+  free((FREE_ARG) (t[nrl][ncl] + ndl - NR_END));
+  free((FREE_ARG) (t[nrl] + ncl - NR_END));
+  free((FREE_ARG) (t + nrl - NR_END));
+}
+#endif /* ANSI */
diff --git a/qsstv/drmrx/nrutil.h b/qsstv/drmrx/nrutil.h
new file mode 100644
index 0000000..d28b185
--- /dev/null
+++ b/qsstv/drmrx/nrutil.h
@@ -0,0 +1,103 @@
+#ifndef _NR_UTILS_H_
+#define _NR_UTILS_H_
+
+
+/*  static float sqrarg;
+#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) */ 
+  
+
+/* static double dsqrarg;
+#define DSQR(a) ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg)  */ 
+  
+
+/* static double dmaxarg1,dmaxarg2;
+#define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ?\
+        (dmaxarg1) : (dmaxarg2))
+
+static double dminarg1,dminarg2;
+#define DMIN(a,b) (dminarg1=(a),dminarg2=(b),(dminarg1) < (dminarg2) ?\
+        (dminarg1) : (dminarg2))
+
+static float maxarg1,maxarg2;
+#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
+        (maxarg1) : (maxarg2))
+
+static float minarg1,minarg2;
+#define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\
+        (minarg1) : (minarg2))
+
+static long lmaxarg1,lmaxarg2;
+#define LMAX(a,b) (lmaxarg1=(a),lmaxarg2=(b),(lmaxarg1) > (lmaxarg2) ?\
+        (lmaxarg1) : (lmaxarg2))
+
+static long lminarg1,lminarg2;
+#define LMIN(a,b) (lminarg1=(a),lminarg2=(b),(lminarg1) < (lminarg2) ?\
+        (lminarg1) : (lminarg2))
+
+static int imaxarg1,imaxarg2;
+#define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\
+        (imaxarg1) : (imaxarg2))
+
+static int iminarg1,iminarg2;
+#define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\
+        (iminarg1) : (iminarg2))  */ 
+  
+#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+  
+#if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */
+void nrerror (const char error_text[]);
+float *fvector (long nl, long nh);
+int *ivector (long nl, long nh);
+unsigned char *cvector (long nl, long nh);
+unsigned long *lvector (long nl, long nh);
+double *dvector (long nl, long nh);
+float **matrix (long nrl, long nrh, long ncl, long nch);
+double **dmatrix (long nrl, long nrh, long ncl, long nch);
+int **imatrix (long nrl, long nrh, long ncl, long nch);
+float **submatrix (float **a, long oldrl, long oldrh, long oldcl, long oldch, long newrl, long newcl);
+float **convert_matrix (float *a, long nrl, long nrh, long ncl, long nch);
+float ***f3tensor (long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
+void free_fvector (float *v, long nl, long nh);
+void free_ivector (int *v, long nl, long nh);
+void free_cvector (unsigned char *v, long nl, long nh);
+void free_lvector (unsigned long *v, long nl, long nh);
+void free_dvector (double *v, long nl, long nh);
+void free_matrix (float **m, long nrl, long nrh, long ncl, long nch);
+void free_dmatrix (double **m, long nrl, long nrh, long ncl, long nch);
+void free_imatrix (int **m, long nrl, long nrh, long ncl, long nch);
+void free_submatrix (float **b, long nrl, long nrh, long ncl, long nch);
+void free_convert_matrix (float **b, long nrl, long nrh, long ncl, long nch);
+void free_f3tensor (float ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
+
+
+#else   /* ANSI */
+
+/* traditional - K&R */ 
+void nrerror ();
+float *vector ();
+float **matrix ();
+float **submatrix ();
+float **convert_matrix ();
+float ***f3tensor ();
+double *dvector ();
+double **dmatrix ();
+int *ivector ();
+int **imatrix ();
+unsigned char *cvector ();
+unsigned long *lvector ();
+void free_vector ();
+void free_dvector ();
+void free_ivector ();
+void free_cvector ();
+void free_lvector ();
+void free_matrix ();
+void free_submatrix ();
+void free_convert_matrix ();
+void free_dmatrix ();
+void free_imatrix ();
+void free_f3tensor ();
+
+
+#endif  /* ANSI */
+  
+#endif  /* _NR_UTILS_H_ */
diff --git a/qsstv/drmrx/psdcmean.cpp b/qsstv/drmrx/psdcmean.cpp
new file mode 100644
index 0000000..bc7992b
--- /dev/null
+++ b/qsstv/drmrx/psdcmean.cpp
@@ -0,0 +1,100 @@
+
+/*
+*    File psdcmean.c
+*
+*    Calculates mean powerspectrum of complex input data
+*    using blocklength lblock and nblocks as number
+*    of blocks to be processed.
+*
+*    Author M.Bos - PA0MBO
+*    Date Feb 21st 2009
+*
+*    resulting power spectral density of complex input
+*    signal in rsbuf is stored in cpsd[]
+*    length of datablock to be processed is lblock
+*    nblocks is the nuber of blocks of input data
+*    to be processed.
+*    
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <malloc.h>
+void cfft(float *, int, int);
+void psdcmean(float *rsbuf, float *cpsd, int lblock, int nblocks)
+{
+  float *pinput;
+  int i, j;
+  float tmpinbuf[2048];
+  float result[512];
+
+
+  /*  check space */
+  if (lblock > 1024)
+
+    {
+      printf("not enough temp space\b");
+      exit(EXIT_FAILURE);
+    }
+
+  /* clear array result before accumulating new data */
+  for (i = 0; i < (lblock / 2); i++)
+
+    {
+      result[i] = 0.0;
+    }
+
+  /* loop over all blocks taking care to keep track of ptr in input */
+  pinput = rsbuf;
+  for (j = 0; j < nblocks; j++)
+
+    {
+
+      /* Now fill tmpinbuf with converted data from input */
+      for (i = 0; i < lblock; i++)
+
+        {
+          tmpinbuf[i * 2] = pinput[2 * i];
+          tmpinbuf[i * 2 + 1] = pinput[2 * i + 1];
+        }
+      cfft(tmpinbuf, lblock / 2, 1);
+      for (i = 1; i < lblock / 2; ++i)
+
+        {
+          result[i] +=
+              sqrt(tmpinbuf[i * 2] * tmpinbuf[i * 2] +
+                   tmpinbuf[i * 2 + 1] * tmpinbuf[i * 2 + 1]);
+        }
+      pinput += 2 * lblock;	/* update pointer in input data */
+    }
+  for (i = 0; i < lblock / 2; i++)
+
+    {
+      result[i] = (float) (10.0 * log(result[i] + 1.0e-8) / 2.305 - 14.0);
+    }
+  /* interchange halfs of cpsd buffer as in matlab code from
+     plot_input_spectrum in diorama */
+  for (i = 1; i < lblock / 4; i++)
+
+    {
+      cpsd[i] = result[lblock / 4 + i];
+      cpsd[i + lblock / 4 - 1] = result[i];
+    }
+}
diff --git a/qsstv/drmrx/psdmean.cpp b/qsstv/drmrx/psdmean.cpp
new file mode 100644
index 0000000..bf63c94
--- /dev/null
+++ b/qsstv/drmrx/psdmean.cpp
@@ -0,0 +1,85 @@
+
+/*
+*    File psdmean.c
+*
+*    Calculates mean powerspectrum of input data
+*    using blocklength lblock and nblocks as number
+*    of blocks to be processed.
+*
+*    Author M.Bos - PA0MBO
+*    Date Feb 21st 2009
+*
+*    resulting power spectral density of real input data
+*    vector input[]  is stored in psd[]
+*    lblock is the length of the block of data to be
+*    processed and nblock is the number of data blocks
+*    to be processed
+*   
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <malloc.h>
+void rfft(float *, int, int);
+void psdmean(float *input, float *psd, int lblock, int nblocks)
+{
+  float *pinput;
+  int i, j;
+  float tmpinbuf[1024];
+
+
+  /* check for space allocated */
+  if (lblock > 1024)
+
+    {
+      printf("lblokc param too large in call psdmean\n");
+      exit(EXIT_FAILURE);
+    }
+
+  /* clear array psd before accumulating new data */
+  for (i = 0; i < lblock / 2; i++)
+
+    {
+      psd[i] = 0.0;
+    }
+
+  /* loop over all blocks taking care to keep track of ptr in input */
+  pinput = input;
+  for (j = 0; j < nblocks; j++)
+
+    {
+
+      /* Now fill tmpinbuf with converted data from input */
+      for (i = 0; i < lblock; i++)
+
+        {
+          tmpinbuf[i] = pinput[i];
+        }
+      rfft(tmpinbuf, lblock / 2, 1);
+      for (i = 1; i < lblock / 2; ++i)
+
+        {
+          psd[i] += sqrt(tmpinbuf[i * 2] * tmpinbuf[i * 2] + tmpinbuf[2 * i + 1] * tmpinbuf[2 * i + 1]);
+        }
+      pinput += lblock;		/* update pointer in input data */
+    }
+  for (i = 1; i < lblock / 2; ++i)
+    psd[i] = (float) (10.0 * log(psd[i] + 1.0e-8) / 2.3025 - 14.0);
+}
diff --git a/qsstv/drmrx/resamplefilter.h b/qsstv/drmrx/resamplefilter.h
new file mode 100644
index 0000000..9ef2ed4
--- /dev/null
+++ b/qsstv/drmrx/resamplefilter.h
@@ -0,0 +1,49 @@
+
+/* Automatically generated file with MATLAB */  
+
+/* File name: "ResampleFilter.m" */ 
+
+/* Filter taps in time-domain */ 
+  
+#ifndef _RESAMPLEFILTER_H_
+#define _RESAMPLEFILTER_H_
+  
+#define RES_FILT_NUM_TAPS_PER_PHASE  12
+#define INTERP_DECIM_I_D             10
+  
+
+/* Filter for ratios close to 1 */ 
+static float fResTaps1To1[INTERP_DECIM_I_D][RES_FILT_NUM_TAPS_PER_PHASE] = { 
+  {-0.00129181992672801360f, 0.00561586829442904840f, -0.01349857823816511800f, 0.02541150940858524100f,
+   -0.04267869501534898200f, 0.07724474282951483700f, 0.96609875058711103000f, -0.01641812005088002400f,
+   -0.00427135103965109450f, 0.00726225824406205160f, -0.00544188094946287510f, 0.00266742068076876060f },
+  {-0.00207886551285772290f, 0.00866090598717600930f, -0.02161960909069559500f, 0.04383507935997314800f,
+   -0.08302470868585065700f, 0.18738870090358245000f, 0.93524350914423104000f, -0.09031872116141286000f,
+   0.02909509423931267600f, -0.00897188476756275060f, 0.00178311012364952820f, 0.00010586149691723067f },
+  {-0.00287519800425638110f, 0.01143197533872717000f, -0.02889142869399521600f, 0.06060641890050100900f,
+   -0.12152802242786863000f, 0.30933747340895279000f, 0.87539536840978205000f, -0.14271415809850990000f,
+   0.05516985095031713000f, -0.02205265100214613000f, 0.00761119378345958850f, -0.00187713739944610450f },
+  {-0.00354120720771153910f, 0.01351098086300389300f, -0.03433664370844288100f, 0.07367662235517660800f,
+   -0.15398027155782226000f, 0.43728178746780866000f, 0.79013921003423337000f, -0.17341770937821352000f,
+   0.07263788052016696700f, -0.03120859084480779800f, 0.01170664402374247200f, -0.00319259334815649940f },
+  {-0.00391755659664638590f, 0.01447751287549226700f, -0.03701682481313090000f, 0.08107302414568577600f,
+   -0.17606165300033697000f, 0.56464344237183917000f, 0.68451472884717957000f, -0.18369620562420094000f,
+   0.08111657494320076400f, -0.03614676421513295800f, 0.01396276906259418800f, -0.00384568128202934270f },
+  {-0.00384568128202934270f, 0.01396276906259418800f, -0.03614676421513295800f, 0.08111657494320076400f,
+   -0.18369620562420094000f, 0.68451472884717957000f, 0.56464344237183917000f, -0.17606165300033697000f,
+   0.08107302414568577600f, -0.03701682481313090000f, 0.01447751287549226700f, -0.00391755659664638590f },
+  {-0.00319259334815649940f, 0.01170664402374247200f, -0.03120859084480779800f, 0.07263788052016696700f,
+   -0.17341770937821352000f, 0.79013921003423337000f, 0.43728178746780866000f, -0.15398027155782226000f,
+   0.07367662235517660800f, -0.03433664370844288100f, 0.01351098086300389300f, -0.00354120720771153910f },
+  {-0.00187713739944610450f, 0.00761119378345958850f, -0.02205265100214613000f, 0.05516985095031713000f,
+   -0.14271415809850990000f, 0.87539536840978205000f, 0.30933747340895279000f, -0.12152802242786863000f,
+   0.06060641890050100900f, -0.02889142869399521600f, 0.01143197533872717000f, -0.00287519800425638110f },
+  {0.00010586149691723067f, 0.00178311012364952820f, -0.00897188476756275060f, 0.02909509423931267600f,
+   -0.09031872116141286000f, 0.93524350914423104000f, 0.18738870090358245000f, -0.08302470868585065700f,
+   0.04383507935997314800f, -0.02161960909069559500f, 0.00866090598717600930f, -0.00207886551285772290f },
+  {0.00266742068076876060f, -0.00544188094946287510f, 0.00726225824406205160f, -0.00427135103965109450f,
+   -0.01641812005088002400f, 0.96609875058711103000f, 0.07724474282951483700f, -0.04267869501534898200f,
+   0.02541150940858524100f, -0.01349857823816511800f, 0.00561586829442904840f, -0.00129181992672801360f }
+};
+
+#endif  /* _RESAMPLEFILTER_H_ */
diff --git a/qsstv/drmrx/sourcedecoder.cpp b/qsstv/drmrx/sourcedecoder.cpp
new file mode 100644
index 0000000..166eb60
--- /dev/null
+++ b/qsstv/drmrx/sourcedecoder.cpp
@@ -0,0 +1,803 @@
+#include "sourcedecoder.h"
+#include "drm.h"
+#include "qsstvglobal.h"
+#include "drmproto.h"
+#include <math.h>
+#include <float.h>
+#include "configparams.h"
+#include "dispatcher.h"
+#include <QApplication>
+#include <QFileInfo>
+#include "utils/reedsolomoncoder.h"
+#include <QFile>
+#include "drmrx/demodulator.h"
+#include "utils/ftp.h"
+#include "configparams.h"
+#include "utils/qjp2io.h"
+#include "utils/hybridcrypt.h"
+#include "logbook/logbook.h"
+#include "drmrx/drmstatusframe.h"
+
+sourceDecoder::sourceDecoder(QObject *parent) : QObject(parent)
+{
+
+}
+
+void sourceDecoder::init()
+{
+  lastTransportBlockPtr=NULL;
+  transportBlockPtrList.clear();
+  bodyTotalSegments=0;
+  checkIt=false;
+  erasureList.clear();
+  lastContinuityIndex=-1;
+}
+
+
+/*!
+ \brief decode of valid data block
+
+• header     8 bits.
+• data field n bytes.
+• CRC        16 bits.
+
+
+ \return bool return true if successful
+*/
+bool sourceDecoder::decode()
+{
+  double checksum;
+  int N_partB;
+  //  if(!demodulatorPtr->isTimeSync())
+
+  if (channel_decoded_data_buffer_data_valid != 1)  return false;
+  if (audio_data_flag == 0)
+    {
+      addToLog("audio decoding not implemented in qsstv !\n",LOGDRMSRC); return false;
+    }
+  addToLog("Datapacket received",LOGPERFORM);
+  N_partB = (int) (length_decoded_data/ 8);
+  addToLog(QString("N-partB lenght=%1").arg(N_partB),LOGDRMSRC);
+  if(N_partB>PACKETBUFFERLEN)
+    {
+      addToLog(QString("packet buffer length exceeded: lenght=%1").arg(N_partB),LOGDRMMOT);
+    }
+  bits2bytes (channel_decoded_data_buffer, N_partB * 8, packetBuffer);
+  crc16_bytewise(&checksum, packetBuffer,N_partB);
+  if(fabs (checksum) <= DBL_EPSILON)
+    {
+      if(!setupDataBlock(packetBuffer,true,N_partB))
+        {
+          msc_valid=INVALID;
+          return false;
+        }
+    }
+  else
+    {
+      msc_valid=INVALID;
+      return false;
+    }
+  // at this point we have a dataPacket we now check header / data and buils a transport stream
+  switch(currentDataPacket.dataGroupType)
+    {
+    case MOTDATA:  addToLog("Datasegment",LOGDRMSRC);   addDataSegment(); break;
+    case MOTHEAD:  addToLog("Headersegment",LOGDRMSRC); addHeaderSegment(); break;
+    default:
+    return false;
+    break;
+    }
+  return true;
+}
+
+
+
+bool sourceDecoder::setupDataBlock(unsigned char *buffer,bool crcIsOK,int len)
+{
+  currentDataBlock.length=len;
+  unsigned char header=buffer[0];
+  unsigned int availableBytes;
+  const char *bufPtr;
+  currentDataBlock.ba=QByteArray((char *)buffer,len);
+  currentDataBlock.firstFlag=currentDataBlock.lastFlag=false;
+  if(header & 0x80) currentDataBlock.firstFlag = true;
+  if(header & 0x40) currentDataBlock.lastFlag  = true;
+  currentDataBlock.packetID = (header & 0x30) >> 4;
+  currentDataBlock.PPI = (header & 0x8) >> 3;
+  currentDataBlock.continuityIndex = (header & 0x7);
+  currentDataBlock.log();
+  if ((currentDataBlock.PPI != 0) && (crcIsOK))
+    {
+      availableBytes=buffer[1];
+      bufPtr=(const char *)&buffer[2];
+    }
+  else
+    {
+      availableBytes=len-3;
+      bufPtr=(const char *)&buffer[1];
+    }
+  if(currentDataBlock.firstFlag)
+    {
+      holdingBuffer.clear();
+      lastContinuityIndex=currentDataBlock.continuityIndex;
+    }
+  else
+    {
+      if(lastContinuityIndex<0)
+        {
+          return false;
+        }
+      lastContinuityIndex=(lastContinuityIndex+1)%8;
+      if(currentDataBlock.continuityIndex!=lastContinuityIndex)
+        {
+          lastContinuityIndex=-1;
+          return false;
+        }
+
+    }
+  holdingBuffer.append(bufPtr,availableBytes);
+  if(currentDataBlock.lastFlag)
+    {
+      return setupDataPacket(holdingBuffer);
+    }
+  return false;
+}
+
+void dataBlock::log()
+{
+  addToLog(QString("FFlag %1,LFlag %2, PacketID %3,PPI %4,ContIdx %5, CRC %6 len %7")
+           .arg(firstFlag).arg(lastFlag).arg(packetID).arg(PPI).arg(continuityIndex).arg(crcOK).arg(length),LOGDRMMOT);
+}
+
+//MSC Header Description 2 bytes or 4 bytes if Extension field
+// First Byte
+// B7 Extension Flag    Header has 2 more bytes in the Extension field
+// B6 CRC Flag          MOT has CRC
+// B5 Session Flag      MOT last flag and segment number present.
+// B4 UserAccess Flag   Mot user access field present.
+// B3-B0 Data Group Type
+//    0 0 0 0  (0) General data;
+//    0 0 0 1  (1) CA messages (for example ECMs or EMMs: see subclause 9.3.2.1);
+//    0 0 1 0  (2) General data and CA parameters (for example, DGCA);
+//    0 0 1 1  (3) MOT header information;
+//    0 1 0 0  (4) MOT data
+//    0 1 0 1  (5) MOT data and CA parameters.
+
+//Second Byte
+// B7-B4 Continuity Index
+// B3-B0 Repetition Index
+
+// 2 more extension bytes if flag is set
+// B15-B0 Extension Field - no further specifications
+
+// Session Header (presence indicated by Session Flag
+
+// if last flag and segment number present
+// B15    last segment
+// B14-B0 segment number
+
+// user access fields
+// B7-B5  rfa (not used)
+// B4     TransportID present flag
+// B3-B0  length of user access fields
+
+// if set
+// B15-B0  TransportID
+// other bytes filled with End user address field
+
+// This is followed with a 2 bytes Segmentation header
+// B15-B13 Repetition Count (not used)
+// B12-B0  Length of the data that follows
+
+
+bool sourceDecoder::setupDataPacket(QByteArray ba)
+{
+  double checksum;
+  unsigned char lengthIndicator;
+  unsigned char header;
+
+  currentDataPacket.ba=ba; // drop crc
+  header=ba.at(0);
+  currentDataPacket.transportID =0xFFFF;
+  currentDataPacket.extFlag=false;
+  currentDataPacket.crcFlag=false;
+  currentDataPacket.sessionFlag=false;
+  currentDataPacket.userFlag=false;
+  currentDataPacket.lastSegment=false;
+  currentDataPacket.crcOK=currentDataBlock.crcOK;
+
+  if(header&0x10) currentDataPacket.userFlag=true;
+  currentDataPacket.dataGroupType=(edataGroupType)(header&0x07);
+  if(header&0x80) currentDataPacket.extFlag=true;
+  if(header&0x20) currentDataPacket.sessionFlag=true;
+
+  if(header&0x40)
+    {
+      currentDataPacket.crcFlag=true;
+      crc16_bytewise (&checksum,(unsigned char *)currentDataPacket.ba.data(),currentDataPacket.ba.count());
+      if (fabs (checksum) <= DBL_EPSILON)
+        {
+          currentDataPacket.crcOK=true;
+        }
+      else
+        {
+          currentDataPacket.crcOK=false;
+          msc_valid=INVALID;
+          return false;
+        }
+      currentDataPacket.chop(2); // drop crc
+    }
+  currentDataPacket.advance(2); //skip header and continuity bits
+  if(currentDataPacket.extFlag) currentDataPacket.advance(2); // just skip the extension bytes
+
+  if(currentDataPacket.sessionFlag)
+    {
+      currentDataPacket.segmentNumber = (((unsigned char)(currentDataPacket.ba.at(0)) & 0x7F))*256+ ((unsigned char)currentDataPacket.ba.at(1))  ;
+      if(currentDataPacket.ba.at(0)&0x80)
+        {
+          currentDataPacket.lastSegment=true;
+        }
+      currentSegmentNumber=currentDataPacket.segmentNumber;
+      currentDataPacket.advance(2);
+    }
+
+  if (currentDataPacket.userFlag)
+    {
+      currentDataPacket.userAccessField = (unsigned char)(currentDataPacket.ba.at(0));
+      currentDataPacket.advance(1);
+      lengthIndicator = (currentDataPacket.userAccessField& 0xF);
+
+      if((currentDataPacket.userAccessField & 0x10) && (lengthIndicator>=2)) currentDataPacket.transportID = (((unsigned char)(currentDataPacket.ba.at(0))))*256+ ((unsigned char)currentDataPacket.ba.at(1))  ;
+      currentDataPacket.advance(lengthIndicator);
+    }
+  currentDataPacket.segmentSize=(((unsigned char)(currentDataPacket.ba.at(0)) & 0x1F))*256+ ((unsigned char)currentDataPacket.ba.at(1));
+  currentDataPacket.advance(2);
+  currentDataPacket.lenght=currentDataPacket.ba.count();
+  currentDataPacket.log();
+  return true;
+}
+
+
+
+void dataPacket::log()
+{
+  addToLog(QString("extF %1,dType %2, sessionF %3, lastSegment %4, segment# %5, userF %6, transportId %7, len %8")
+           .arg(extFlag).arg(dataGroupType).arg(sessionFlag).arg(lastSegment).arg(segmentNumber).arg(userFlag).arg(transportID).arg(lenght),LOGDRMMOT);
+}
+
+
+
+bool sourceDecoder::addHeaderSegment()
+{
+  transportBlock *tbPtr;
+  addToLog(QString("Header segsize: %1").arg(currentDataPacket.segmentSize),LOGDRMSRC);
+  tbPtr=getTransporPtr(currentDataPacket.transportID,true);
+  if(!tbPtr->alreadyReceived) msc_valid=VALID;
+  else
+    {
+      msc_valid=ALREADYRECEIVED;
+      return true;
+    }
+
+  tbPtr->headerReceived=true;
+  unsigned char *dataPtr=(unsigned char *)currentDataPacket.ba.data();
+  unsigned char PLI;
+  unsigned char paramID;
+  unsigned char extBit;
+  unsigned short dataFieldLength;
+  tbPtr->bodySize =
+      (((unsigned int)dataPtr[0]) << 20) +
+      (((unsigned int)dataPtr[1]) << 12) +
+      (((unsigned int)dataPtr[2]) << 4) +
+      ((((unsigned int)dataPtr[3]) & 0xF0) >> 4);
+
+  tbPtr->headerSize =
+      (((unsigned int)dataPtr[3] & 0x0F) << 9) +
+      (((unsigned int)dataPtr[4]) << 1) +
+      ((((unsigned int)dataPtr[5]) & 0x80) >> 7);
+
+  tbPtr->contentType = (((unsigned int)dataPtr[5] & 0x7E) >> 1);
+  tbPtr->contentSubtype = ((((unsigned int)dataPtr[5]) & 0x1) <<8) +((unsigned int)dataPtr[6]);
+  currentDataPacket.advance(7); // size of header core
+  // The header core is followed by a number of parameter blocks
+  // the first byte of every parameter block contains a 2-bits PLI (B7 and B6) indicating the type of parameter block.
+
+  while(currentDataPacket.ba.count())  // todo
+    {
+      PLI=dataPtr[0]>>6;
+      paramID=dataPtr[0]&0x3F;
+      switch (PLI)
+        {
+        case 0:
+          currentDataPacket.advance(1);
+        break;
+        case 1:
+          loadParams(tbPtr,paramID,1);
+          currentDataPacket.advance(2);
+        break;
+        case 2:
+          loadParams(tbPtr,paramID,4);
+          currentDataPacket.advance(5);
+        break;
+        case 3:
+          extBit=dataPtr[0]&0x80;
+          if(extBit)
+            {
+              dataFieldLength=256*(dataPtr[0]&0x7F)+dataPtr[1];
+              currentDataPacket.advance(2);
+            }
+          else
+            {
+              dataFieldLength=dataPtr[0]&0x7F;
+              currentDataPacket.advance(1);
+            }
+          loadParams(tbPtr,paramID,dataFieldLength);
+          currentDataPacket.advance(dataFieldLength);
+        break;
+        }
+    }
+  return true;
+
+}
+
+void sourceDecoder::loadParams(transportBlock *tbPtr,unsigned char paramID,int len)
+{
+  rxDRMStatusEvent *stce;
+  QString tmp,t;
+  switch(paramID)
+    {
+    case 5: // expiration
+    break;
+    case 6:
+    break;
+    case 12:
+      tbPtr->fileName=QString(currentDataPacket.ba.data()+1).left(len-1);
+      stce= new rxDRMStatusEvent(QString("%1").arg(tbPtr->fileName));
+      QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+    break;
+    default:
+    break;
+    }
+}
+
+
+void sourceDecoder::addDataSegment()
+{
+  int i;
+  transportBlock *tbPtr;
+  tbPtr=getTransporPtr(currentDataPacket.transportID,true);
+  rxTransportID=currentDataPacket.transportID;
+  if(callsignValid) tbPtr->callsign=drmCallsign;
+  addToLog(QString("Data segsize: %1 segment# %2").arg(currentDataPacket.segmentSize).arg(currentDataPacket.segmentNumber),LOGDRMSRC);
+
+  bodyTotalSegments=tbPtr->totalSegments;
+  if(!tbPtr->alreadyReceived) msc_valid=VALID;
+  else
+    {
+      msc_valid=ALREADYRECEIVED;
+      //      return;
+    }
+  if(currentDataPacket.lastSegment)
+    {
+      tbPtr->totalSegments=currentDataPacket.segmentNumber+1;
+      tbPtr->lastSegmentReceived=true;
+    }
+  else
+    {
+      tbPtr->defaultSegmentSize=currentDataPacket.segmentSize;
+    }
+  if(tbPtr->defaultSegmentSize==0)
+    {
+      //      qDebug() << "def seg size not set";
+    }
+  for(i=tbPtr->dataSegmentPtrList.count();i<=currentDataPacket.segmentNumber;i++)
+    {
+      tbPtr->dataSegmentPtrList.append(new dataSegment(tbPtr->defaultSegmentSize));
+    }
+
+  if(!tbPtr->dataSegmentPtrList.at(currentDataPacket.segmentNumber)->hasData())
+    {
+      checkIt=true;
+    }
+  else
+    {
+      msc_valid=ALREADYRECEIVED;
+    }
+  if(tbPtr->alreadyReceived)
+    {
+      msc_valid=ALREADYRECEIVED;
+      checkIt=false;
+    }
+
+
+  if(tbPtr->totalSegments<currentDataPacket.segmentNumber+1) tbPtr->totalSegments=currentDataPacket.segmentNumber+1;
+  rxSegments=tbPtr->segmentsReceived;
+  //  bytesReceived=rxSegments*tbPtr->defaultSegmentSize;
+
+  tbPtr->dataSegmentPtrList.at(currentDataPacket.segmentNumber)->setData(currentDataPacket.ba,currentDataPacket.segmentNumber,true);
+
+  writeData(tbPtr);
+
+}
+
+void sourceDecoder::writeData(transportBlock *tbPtr)
+{
+  int i;
+
+  QByteArray ba;
+
+  int length=0;
+  erasureList.clear();
+  erasureList.append(tbPtr->totalSegments);
+  erasureList.append(tbPtr->defaultSegmentSize);
+  for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
+    {
+      if(!tbPtr->dataSegmentPtrList.at(i)->hasData())
+        {
+          erasureList.append(i);
+          ba.append(QByteArray(tbPtr->defaultSegmentSize,0x00));
+        }
+      else
+        {
+          ba.append(tbPtr->dataSegmentPtrList.at(i)->data);
+        }
+      length+=tbPtr->dataSegmentPtrList.at(i)->data.size();
+      //      qDebug()<< "block" << i<< "len" << tbPtr->dataSegmentPtrList.at(i)->data.size() ;
+    }
+  tbPtr->segmentsReceived=0;
+  drmBlockList.clear();
+  for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
+    {
+      if(tbPtr->dataSegmentPtrList.at(i)->hasData())
+        {
+          drmBlockList.append(i);
+          tbPtr->segmentsReceived++;
+        }
+    }
+  if(tbPtr->isAlmostComplete()<63) return ;
+
+  if(!tbPtr->lastSegmentReceived) return;
+  checkSaveImage(ba,tbPtr);
+}
+
+
+void sourceDecoder::saveImage(transportBlock *tbPtr)
+{
+  int i;
+  eftpError ftpResult;
+  QByteArray hybridBa;
+  hybridCrypt hc;
+  QImage test;
+  displayTextEvent *stce;
+  displayMBoxEvent *stmb=0;
+  QString t;
+  ftpInterface ftpIntf("Save Image FTP");
+  bool done=false;
+  bool textMode=false;
+  QString downloadF;
+  bool   saveOK=false;
+
+  if(tbPtr->alreadyReceived) return ;
+  if(tbPtr->fileName.isEmpty()) return ;
+  lastAvgSNR=avgSNR;
+  isHybrid=false;
+  if(tbPtr->fileName.left(3)=="de_")
+    {
+      isHybrid=true;
+      if(enableHybridRx)
+        {
+          downloadF=rxImagesPath+"/"+tbPtr->fileName;
+          for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
+            {
+              hybridBa+=tbPtr->dataSegmentPtrList.at(i)->data;
+            }
+          if(hc.deCrypt(&hybridBa))
+            {
+
+
+              ftpIntf.setupConnection(hc.host(),hc.port(),hc.user(),hc.passwd(),hc.dir()+"/"+hybridFtpHybridFilesDirectory);
+              ftpResult=ftpIntf.downloadFile(tbPtr->fileName,downloadF);
+              switch(ftpResult)
+                {
+                case FTPOK:
+                break;
+                case FTPERROR:
+                  stmb= new displayMBoxEvent("FTP Error",QString("Host: %1: %2").arg(ftpRemoteHost).arg(ftpIntf.getLastError()));
+                break;
+                case FTPNAMEERROR:
+                  stmb= new displayMBoxEvent("FTP Error",QString("Host: %1, Error in filename").arg(ftpRemoteHost));
+                break;
+                case FTPCANCELED:
+                  stmb= new displayMBoxEvent("FTP Error",QString("Connection to %1 Canceled").arg(ftpRemoteHost));
+                break;
+                case FTPTIMEOUT:
+                  stmb= new displayMBoxEvent("FTP Error",QString("Connection to %1 timed out").arg(ftpRemoteHost));
+                break;
+                }
+              if(ftpResult!=FTPOK)
+                {
+                  QApplication::postEvent( dispatcherPtr, stmb );  // Qt will delete it when done
+                  tbPtr->setAlreadyReceived(true);
+                  return;
+                }
+
+            }
+          else
+            {
+              stmb= new displayMBoxEvent("Hybrid Error","No file downloaded");
+              QApplication::postEvent( dispatcherPtr, stmb );  // Qt will delete it when done
+              return;
+            }
+        }
+      else
+        {
+          downloadF.clear();
+        }
+
+      tbPtr->newFileName=downloadF;
+
+    }
+
+  if(tbPtr->newFileName.isEmpty()) return ;
+  //  test=readJP2Image(tbPtr->newFileName);
+  if(!test.load(tbPtr->newFileName))
+    {
+      test=readJP2Image(tbPtr->newFileName);
+      if(test.isNull())
+        {
+          // maybe text
+          QFileInfo finfo(tbPtr->newFileName);
+          if((finfo.suffix()=="txt") || (finfo.suffix()=="chat") )
+            {
+              QFile fi(tbPtr->newFileName);
+              if(!fi.open(QIODevice::ReadOnly)) return;
+              t=fi.readAll();
+              stce= new displayTextEvent(t);
+              QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+              textMode=true;
+            }
+        }
+
+      saveOK=true;
+    }
+  else
+    {
+      saveOK=true;
+    }
+  if(saveOK)
+    {
+      QFileInfo tfi(tbPtr->newFileName);
+      QString modestr(tfi.fileName());
+      modestr+=" ";
+      if(isHybrid) modestr+="Hybrid ";
+      modestr+=compactModeToString(tbPtr->modeCode);
+      logBookPtr->logQSO(tbPtr->callsign,modestr);
+    }
+  tbPtr->setAlreadyReceived(true);
+  if(!textMode)
+    {
+      saveDRMImageEvent *ce = new saveDRMImageEvent(tbPtr->newFileName);
+      ce->waitFor(&done);
+      QApplication::postEvent(dispatcherPtr, ce);
+      while(!done)
+        {
+          usleep(10);
+        }
+      checkIt=false;
+    }
+}
+
+bool sourceDecoder::checkSaveImage(QByteArray ba,transportBlock *tbPtr)
+{
+  prepareFixEvent *pe;
+  QFile outFile;
+  reedSolomonCoder rsd;
+  QString fileName;
+  QString extension;
+  fileName=rxImagesPath+"/"+tbPtr->fileName;
+  tbPtr->newFileName=fileName;
+  QByteArray baFile;
+  QByteArray *baFilePtr;
+
+  if(!checkIt) return false;
+  QFileInfo qfinf(fileName);
+  extension=qfinf.suffix().toLower();
+  if((extension=="rs1") || (extension=="rs2") ||(extension=="rs3")||(extension=="rs4"))
+    {
+      // try to decode
+      if(tbPtr->alreadyReceived) return false;
+      if(!rsd.decode(ba,fileName,tbPtr->newFileName,baFile,extension,erasureList)) return false;
+      baFilePtr=&baFile;
+    }
+  else
+    {
+      if(!tbPtr->isComplete()) return false;
+      tbPtr->newFileName=fileName;
+      if((tbPtr->fileName=="bsr.bin")&&(!tbPtr->alreadyReceived))
+        {
+          tbPtr->setAlreadyReceived(true);
+          pe = new prepareFixEvent(ba);
+          QApplication::postEvent(dispatcherPtr, pe);
+          return false;
+        }
+      baFilePtr=&ba;
+    }
+  if(!tbPtr->alreadyReceived)
+    {
+      outFile.setFileName(tbPtr->newFileName);
+      if(outFile.open(QIODevice::WriteOnly)<=0)
+        {
+          outFile.close();
+          return false;
+        }
+      outFile.write(*baFilePtr);
+      outFile.close();
+      erasureList.clear();
+      saveImage(tbPtr);
+    }
+  return false;
+}
+
+QList <bsrBlock> *sourceDecoder::getBSR()
+{
+  int i;
+  transportBlock *tbPtr;
+  bsrList.clear();
+
+  for(i=0;i<transportBlockPtrList.count();i++)
+    {
+      tbPtr=transportBlockPtrList.at(i);
+      if(tbPtr->alreadyReceived) continue;
+      if(tbPtr->fileName=="bsr.bin") continue;
+      bsrList.append(bsrBlock(tbPtr));
+    }
+  return &bsrList;
+}
+
+bool sourceDecoder::storeBSR(transportBlock *tb, bool compat)
+{
+  int i;
+  int needsFiller;
+  int prevErasure=0;
+  // QByteArray ba;
+  erasureList.clear();
+  erasureList.append(tb->totalSegments);
+  erasureList.append(tb->defaultSegmentSize);
+  for(i=0;i<tb->dataSegmentPtrList.count();i++)
+    {
+      if(!tb->dataSegmentPtrList.at(i)->hasData())
+        {
+          erasureList.append(i);
+          //         ba.append(QByteArray(tb->defaultSegmentSize,0x00));
+        }
+      else
+        {
+          //          ba.append(tb->dataSegmentPtrList.at(i)->data);
+        }
+      //      length+=tb->dataSegmentPtrList.at(i)->data.size();
+      //      qDebug()<< "block" << i<< "len" << tbPtr->dataSegmentPtrList.at(i)->data.size() ;
+    }
+  tb->baBSR.clear();
+  if(erasureList.count()<3) return false; //erasurelist has already totalSegments and defaultSegmentSize
+  tb->baBSR.append(QString::number(tb->transportID).toLatin1().data());
+  tb->baBSR.append("\n");
+  tb->baBSR.append("H_OK\n");
+  tb->baBSR.append(QString::number(erasureList.at(1)).toLatin1().data());
+  tb->baBSR.append("\n");
+  tb->baBSR.append(QString::number(erasureList.at(2)).toLatin1().data());
+  tb->baBSR.append("\n");
+
+  prevErasure=erasureList.at(2);
+  needsFiller=false;
+  for(i=3;i<erasureList.count();i++) //skip
+    {
+      if(((prevErasure+1)==erasureList.at(i))&&(compat))
+        {
+          needsFiller=true;
+        }
+      else
+        {
+          if(needsFiller)
+            {
+              tb->baBSR.append(QString::number(-1).toLatin1().data());
+              tb->baBSR.append("\n");
+              needsFiller=false;
+            }
+          tb->baBSR.append(QString::number(erasureList.at(i)).toLatin1().data());
+          tb->baBSR.append("\n");
+        }
+      prevErasure=erasureList.at(i);
+    }
+  if(needsFiller)
+    {
+      tb->baBSR.append(QString::number(-1).toLatin1().data());
+      tb->baBSR.append("\n");
+      needsFiller=false;
+      tb->baBSR.append(QString::number(erasureList.at(erasureList.count()-1)).toLatin1().data());
+      tb->baBSR.append("\n");
+    }
+  tb->baBSR.append("-99\n");
+  if(!compat)
+    {
+      QString temp;
+      tb->baBSR.append(tb->fileName+"\n");
+      temp=QString::number(tb->modeCode);
+      while(temp.length()<5) temp.prepend("0");
+      tb->baBSR.append(temp);
+    }
+  return true;
+}
+
+
+transportBlock *sourceDecoder::getTransporPtr(unsigned short tId,bool create)
+{
+  int i;
+  rxDRMStatusEvent *stce;
+  bool found=false;
+  for(i=0;i<transportBlockPtrList.count();i++)
+    {
+      if(transportBlockPtrList.at(i)->transportID==tId)
+        {
+          found=true;
+          break;
+        }
+    }
+  if(found) lastTransportBlockPtr=transportBlockPtrList.at(i);
+  else if (create)
+    {
+      callsignValid=false;
+      stce= new rxDRMStatusEvent("");
+      QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+      if(transportBlockPtrList.count()>=MAXTRANSPORTLISTS)
+        {
+          //delete the oldest
+          transportBlockPtrList.removeFirst();
+        }
+      for(i=0;i<transportBlockPtrList.count();)
+        {
+          if(transportBlockPtrList.at(i)->fileName=="bsr.bin")
+            {
+              transportBlockPtrList.takeAt(i);
+              //              qDebug()<< "deleting bsr.bin";
+            }
+          else i++;
+        }
+      transportBlockPtrList.append(new transportBlock(tId));
+      lastTransportBlockPtr=transportBlockPtrList.last();
+      lastTransportBlockPtr->robMode=robustness_mode;
+      lastTransportBlockPtr->interLeaver=interleaver_depth_new;
+      lastTransportBlockPtr->mscMode=msc_mode_new; // qam
+      lastTransportBlockPtr->mpx=multiplex_description.PL_PartB;
+      lastTransportBlockPtr->spectrum=spectrum_occupancy_new;
+      // remap msc_new to modeCode
+      int mCode=1;    //default QAM16
+      if(msc_mode_new==3) mCode=0;
+      if(msc_mode_new==0) mCode=2;
+      int protection=0;
+      if(multiplex_description.PL_PartB==1) protection=1;
+      lastTransportBlockPtr->modeCode=robustness_mode*10000+spectrum_occupancy_new*1000+protection*100+mCode*10+interleaver_depth_new;
+    }
+  else
+    {
+      return NULL;
+    }
+  return lastTransportBlockPtr;
+}
+
+void sourceDecoder::removeTransporPtr(transportBlock * ptr)
+{
+  int i;
+  for(i=0;i<transportBlockPtrList.count();i++)
+    {
+      if(transportBlockPtrList.at(i)==ptr)
+        {
+          transportBlockPtrList.takeAt(i);
+          break;
+        }
+    }
+}
+
+
+
+
+
+
diff --git a/qsstv/drmrx/sourcedecoder.h b/qsstv/drmrx/sourcedecoder.h
new file mode 100644
index 0000000..8ff4f48
--- /dev/null
+++ b/qsstv/drmrx/sourcedecoder.h
@@ -0,0 +1,213 @@
+#ifndef SOURCEDECODER_H
+#define SOURCEDECODER_H
+//#include <QFTP>
+#include <QObject>
+#include <QList>
+#define PACKETBUFFERLEN 512
+#define NUMSERVICES 4
+#define MAXTRANSPORTLISTS 5
+
+#include <QList>
+#include <QDateTime>
+#include <QByteArray>
+#include <QVector>
+#include "qsstvdefs.h"
+
+enum edataGroupType {GENDATA,CAMESS,GENCA,MOTHEAD,MOTDATA,MOTDATACA};
+
+
+struct dataSegment
+{
+  dataSegment(int newSize)
+  {
+    crcOK=false;
+    recovered=false;
+    segmentNumber=-1;
+    data.resize(newSize);
+    data.fill(0XAA,newSize);
+  }
+
+  void setData(QByteArray ba,short int segNumber,bool crcok)
+  {
+    crcOK=crcok;
+    data=ba;
+    segmentNumber=segNumber;
+  }
+
+  void clearData()
+  {
+    data.clear();
+  }
+  bool crcOK;
+  bool recovered;
+  short int segmentNumber;
+  bool hasData() {return (crcOK||recovered);}
+  QByteArray data;
+};
+
+struct dataPacket
+{
+  void log();
+  QByteArray ba;
+  bool extFlag;
+  bool crcFlag;
+  bool sessionFlag;
+  bool userFlag;
+  bool crcOK;
+  edataGroupType dataGroupType;
+  unsigned char continuityIndex;
+  unsigned char repetitionIndex;
+  unsigned short segmentNumber;
+  bool lastSegment;
+  unsigned short transportID;
+  unsigned char userAccessField;
+  unsigned short segmentSize;
+  int offset;
+  int lenght;
+  unsigned int advance(int numBytes)
+  {
+    ba.remove(0,numBytes);
+    return ba.count();
+  }
+  unsigned int chop(int numBytes)
+  {
+    ba.chop(numBytes);
+    return ba.count();
+  }
+
+};
+
+
+struct dataBlock
+{
+  void log();
+  bool firstFlag;
+  bool lastFlag;
+  short packetID;
+  bool  PPI;
+  unsigned char continuityIndex;
+  bool crcOK;
+  unsigned int length;
+  QByteArray ba;
+};
+
+
+
+struct transportBlock
+{
+  transportBlock(unsigned short tId)
+  {
+    clear();
+    transportID=tId;
+  }
+
+  void clear()
+  {
+    totalSegments=0;
+    headerReceived=false;
+    segmentsReceived=0;
+    bodySize=0;
+    headerSize=0;
+    alreadyReceived=false;
+    //    blockList.clear();
+    lastSegmentReceived=false;
+    defaultSegmentSize=0;
+  }
+  bool isComplete()
+  {
+    if(!headerReceived) return false;
+    if(segmentsReceived<totalSegments) return false;
+    return true;
+  }
+
+  int isAlmostComplete()
+  {
+    if(!headerReceived) return 0;
+    if(totalSegments==0) return 0;
+    return (segmentsReceived*100)/totalSegments;
+  }
+
+  void setAlreadyReceived(bool aRx)
+  {
+    int i;
+    if(aRx)
+      {
+        alreadyReceived=true;
+        for(i=0;i<dataSegmentPtrList.count();i++)
+          {
+            dataSegmentPtrList.at(i)->clearData();
+          }
+      }
+  }
+
+  unsigned short transportID;
+  unsigned int bodySize;
+  unsigned int headerSize;
+  unsigned short contentType;
+  unsigned short contentSubtype;
+  unsigned int defaultSegmentSize;
+  QString fileName;
+  QString newFileName;
+  bool headerReceived;
+  bool alreadyReceived;
+  unsigned short segmentsReceived;
+  bool lastSegmentReceived;
+  int totalSegments;
+  QString callsign;
+  //  QList <short int> blockList;
+  QVector<dataSegment *> dataSegmentPtrList;
+  int robMode;
+  int interLeaver;
+  int mscMode; // qam
+  int mpx;
+  int spectrum;
+  QByteArray baBSR;
+  uint modeCode; //mode(A=0,B=1,E=2) BW(0=2.3,1=2.5) prot(High=0,LOW=1) QAM(4=0,16=1,64=2) ineterleaver
+};
+
+
+struct bsrBlock
+{
+  bsrBlock(transportBlock *tb)
+  {
+    tbPtr=tb;
+  }
+  transportBlock *tbPtr;
+};
+
+
+
+class sourceDecoder : public QObject
+{
+  Q_OBJECT
+public:
+  explicit sourceDecoder(QObject *parent = 0);
+  void init();
+  bool decode();
+  //    bool hasStarted(){return started;}
+  QList <bsrBlock> *getBSR();
+  bool checkSaveImage(QByteArray ba, transportBlock *tbPtr);
+  void saveImage(transportBlock *tbPtr);
+  bool storeBSR(transportBlock *tb,bool compat);
+private:
+  bool setupDataBlock(unsigned char *buffer,bool crcIsOK,int len);
+  bool setupDataPacket(QByteArray ba);
+  void addDataSegment();
+  bool addHeaderSegment();
+  void loadParams(transportBlock *tbPtr, unsigned char paramID, int len);
+  void writeData(transportBlock *tbPtr);
+  transportBlock *getTransporPtr(unsigned short tId,bool create);
+  void removeTransporPtr(transportBlock * ptr);
+  unsigned char packetBuffer[PACKETBUFFERLEN];
+  dataBlock currentDataBlock;
+  dataPacket currentDataPacket;
+  QList<transportBlock *> transportBlockPtrList;
+  transportBlock *lastTransportBlockPtr;
+  QList <int> erasureList;
+  QByteArray holdingBuffer;
+  short int lastContinuityIndex;
+  bool checkIt;
+  QList <bsrBlock> bsrList;
+  bool isHybrid;
+};
+#endif // SOURCEDECODER_H
diff --git a/qsstv/drmrx/structtemplates.h b/qsstv/drmrx/structtemplates.h
new file mode 100644
index 0000000..55d2a5e
--- /dev/null
+++ b/qsstv/drmrx/structtemplates.h
@@ -0,0 +1,185 @@
+/*
+*    File structtemplates.h
+*    part of package RXAMADRM
+*    M.Bos - PA0MBO
+*    Date feb 21st 2009
+*/
+
+/*************************************************************************
+*
+*                           PA0MBO
+*
+*    COPYRIGHT (C)  2009  M.Bos 
+*
+*    This file is part of the distribution package RXAMADRM
+*
+*    This package is free software and you can redistribute is
+*    and/or modify it under the terms of the GNU General Public License
+*
+*    More details can be found in the accompanying file COPYING
+*************************************************************************/
+
+
+struct mplex_desc
+{
+  int stream_lengths[2][6];
+   int PL_PartA;
+   int PL_PartB;
+   int HM_length;
+   int PL_HM;
+ };
+struct audio_info
+{
+  int ID[4];
+   int stream_ID[4];
+   int audio_coding[4];
+   int SBR_flag[4];
+   int audio_mode[4];
+   int sampling_rate[4];
+   int text_flag[4];
+   int enhancement_flag[4];
+   int coder_field[4];
+   int bytes_per_frame[4];
+ };
+struct appl_info
+{
+  int ID[4];
+   int stream_ID[4];
+   int packet_mode[4];
+   int data_unit_indicator[4];
+   int packet_ID[4];
+   int enhancement_flag[4];
+   int application_domain[4];
+   int packet_length[4];
+   int application_data[4][100];       /* pa0mbo checken for max later */
+   int appl_data_length[4];
+   int user_application_type[4];
+   int user_application_identifier[4];
+   char label[4][100];         /*  pa0mbo check max 100 */
+   char language[4][100];      /* ibid */
+   char country[4][100];       /* inid */
+   int language_code[4];
+   int programme_type_code[4];
+   int bytes_per_frame[4];
+ };
+struct stream_info
+{
+  int number_of_audio_services;
+   int number_of_data_services;
+   int number_of_streams;
+   int number_of_audio_streams;
+   int number_of_data_streams;
+   int audio_streams[4];
+   int data_streams[4];
+   int audio_services[4];
+   int data_services[4];
+ };
+struct time_info
+{
+  int day;
+   int month;
+   int year;
+   int hours;
+   int minutes;
+ };
+struct dflttmsg
+{
+  int stream_no;
+   int current_toggle;
+   int first_last_flag;
+   int command_flag;
+   double field1;
+   double field2;
+   char segments[8][256];      /* pa0mbo check max dim 100 later */
+   char string[256];
+   int CRC_error;
+   int first_seg_received;
+   char current_segment[256];  /* check dim 256 later pa0mbo */
+   int current_segment_no;
+   int current_segment_length;
+ };
+struct dfltdunitasmbly
+{
+  int ID;
+   int first_packet_received;
+   int packet_ID;
+   int continuity_index;
+   int application_domain;
+   char application_data[120000];      /* pa0mbo check max dim 1200 */
+   int CRC_error;
+   char data_unit[120000];
+   int cnt_data_unit;
+ };
+struct dfltMOTdirasmbly
+{
+  int transport_ID;
+   int continuity_index;
+   int current_segment_no;
+   int body_complete;
+   char body[120000];          /* pa0mbo check max dim 12000 */
+   int bodycnt;
+ };
+struct dfltMOTobjasmbly
+{
+  int transport_ID;
+   int header_continuity_index;
+   int current_header_segment_no;
+   int body_continuity_index;
+   int current_body_segment_no;
+   int header_complete;
+   int body_complete;
+   int doDelete;
+   char header[900];           /* pa0mbo check max 900 dim */
+   int hdrcnt;
+   //char body[120000];          /* pa0mbo check max dim 100 */
+   char  body[140000];       //joma   /* pa0mbo check max dim 100 */
+   int bodycnt;
+ };
+struct dfltMOTobjasmblyinfo
+{
+  int transport_ID[120000];    /* pa0mbo check max dim 100 */
+   int cntID;
+ };
+struct dfltMOTobj
+{
+  int ID;
+   int content_type;
+   int content_subtype;
+   int creation_time;
+   int start_validity;
+   int expire_time;
+   int trigger_time;
+   int version_number;
+   unsigned char repetition_distance[100];     /* pa0mbo check max 100 dim */
+   unsigned char group_reference[100]; /* pa0mbo check */
+   int priority;
+   char label[16];
+   char content_name[30];      /* pa0mbo check max dim 30 */
+   char content_description[100];      /* pa0mbo check later */
+   unsigned char mime_type[100];       /* pa0mbo chack 100 dim later */
+   unsigned char compression_type;
+   unsigned char header[900];
+   int hdrcnt;
+   unsigned char body[1200000];        /* chck these dims 100 later */
+   int bodycnt;
+   int error;
+ };
+
+
+struct shadowMOTobj
+{
+   int transport_ID;
+   int hdr_segment_count ;
+   int hdr_highest_segm ;
+   int hdr_segment_lengths[10];
+   int body_segment_lengths[2000];
+   char header [10][250];
+   int body_segment_count ;
+   int body_highest_segm;
+   int body_cnt;
+   int header_cnt;
+   char body[2000][250];
+   int body_complete;
+   int header_complete;
+};
+
diff --git a/qsstv/drmrx/viterbi_decode.cpp b/qsstv/drmrx/viterbi_decode.cpp
new file mode 100644
index 0000000..d012c14
--- /dev/null
+++ b/qsstv/drmrx/viterbi_decode.cpp
@@ -0,0 +1,305 @@
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */
+
+/*                                                                            */
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */
+
+/*  Project start: 15.06.2004                                                 */
+
+/*  Last change  : 01.07.2004                                                 */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  This program is free software; you can redistribute it and/or modify      */
+
+/*  it under the terms of the GNU General Public License as published by      */
+
+/*  the Free Software Foundation; either version 2 of the License, or         */
+
+/*  (at your option) any later version.                                       */
+
+/*                                                                            */
+
+/*  This program is distributed in the hope that it will be useful,           */
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+
+/*  GNU General Public License for more details.                              */
+
+/*                                                                            */
+
+/*  You should have received a copy of the GNU General Public License         */
+
+/*  along with this program; if not, write to the Free Software               */
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+
+/******************************************************************************/
+
+/*                                                                            */
+
+/*  viterbi_decode.c (part of msd_hard)                                       */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+/*  Description:                                                              */
+
+/*  Viterbi Decoder for Multi-Stage Decoding                                  */
+
+/*                                                                            */
+
+/******************************************************************************/
+
+#include <stdlib.h>
+#include <errno.h>
+#include "viterbi_decode.h"
+int
+viterbi_decode(float *llr, int N, int N_PartA, signed char *puncturing1,
+	       signed char *puncturing2, signed char *puncturing3,
+	       char *infoout, char *cwout, int bitpos, int *Deinterleaver,
+	       int L, int N_tail, char *memory_ptr)
+{
+  float *old_metrics, *new_metrics, inf = (float) 1e20;
+  char *path_reg;
+  signed char *puncture_ptr;
+  void *swap_ptr;
+  int symbol_index, j, symbol_pos, symbol_pos2, symbol_pos3;
+  float symbols_acc[QOFB];
+  float metric_path2, metric_s1, metric_s2, metric_inc;
+  char mask, codebits;
+  int part, state, N_Part, path_reg_index, butterfly, info_index;
+
+  if (!llr || !infoout || !cwout || !memory_ptr)
+    {
+      return ENOMEM;
+    }
+
+  /* debugging pa0mbo 
+     printf("viterbidecoder: N = %d, N_PartA = %d , L = %d, N_tail= %d  STATES = %d\n",
+     N, N_PartA, L, N_tail, STATES );   */
+  old_metrics = (float *) (memory_ptr + 0);
+  new_metrics = (float *) (memory_ptr + STATES * sizeof(float));
+  path_reg =
+    (char *) (memory_ptr + STATES * sizeof(float) + STATES * sizeof(float));
+  for (j = 1; j < STATES; j++)
+    {
+      old_metrics[j] = -inf;
+    }
+  old_metrics[0] = 0;
+  symbol_index = 0;
+  path_reg_index = 0;
+  puncture_ptr = puncturing1 + 1;
+  N_Part = N_PartA;
+
+  /* pa0mbo debugging  
+     printf("address puncturing1 = %p \n", puncturing1);
+     printf("inhoud :\n");
+     for (i=0; i < 10 ; i++)
+     printf("%d\n", puncturing1[i]);  
+     printf("address puncturing2 = %p \n", puncturing2);
+     printf("inhoud :\n");
+     for (i=0; i < 10 ; i++)
+     printf("%d\n", puncturing2[i]);  
+     printf("address puncturing3 = %p \n", puncturing3);
+     printf("inhoud :\n");
+     for (i=0; i < 10 ; i++)
+     printf("%d\n", puncturing3[i]);   */
+  for (part = 0; part < 3; part++)
+    {
+      while (symbol_index < N_Part)
+	{
+	  symbol_pos = Deinterleaver[symbol_index];
+	  symbols_acc[0] = -llr[symbol_pos];
+	  symbols_acc[1] = llr[symbol_pos];
+	  symbols_acc[2] = -llr[symbol_pos];
+	  symbols_acc[3] = llr[symbol_pos];
+
+	  /*  printf(" part = %d symbol_index %d symbol_pos %d puncturing switch = %d N_Part %d\n",
+	     part, symbol_index, symbol_pos,  (*puncture_ptr) , N_Part ) ;   pa0mbo */
+	  switch (*puncture_ptr)
+	    {
+	    case 3:
+	      symbol_pos = Deinterleaver[symbol_index + 1];
+	      symbols_acc[0] += -llr[symbol_pos];
+	      symbols_acc[1] += -llr[symbol_pos];
+	      symbols_acc[2] += llr[symbol_pos];
+	      symbols_acc[3] += llr[symbol_pos];
+	      symbol_index += 2;
+	      break;
+	    case 5:
+	      symbol_pos = Deinterleaver[symbol_index + 1];
+	      symbols_acc[0] += -llr[symbol_pos];
+	      symbols_acc[1] += -llr[symbol_pos];
+	      symbols_acc[2] += -llr[symbol_pos];
+	      symbols_acc[3] += -llr[symbol_pos];
+	      symbol_index += 2;
+	      break;
+	    case 7:
+	      symbol_pos = Deinterleaver[symbol_index + 1];
+	      symbol_pos2 = Deinterleaver[symbol_index + 2];
+	      symbols_acc[0] += -llr[symbol_pos] - llr[symbol_pos2];
+	      symbols_acc[1] += -llr[symbol_pos] - llr[symbol_pos2];
+	      symbols_acc[2] += llr[symbol_pos] - llr[symbol_pos2];
+	      symbols_acc[3] += llr[symbol_pos] - llr[symbol_pos2];
+	      symbol_index += 3;
+	      break;
+	    case 15:
+	      symbol_pos = Deinterleaver[symbol_index + 1];
+	      symbol_pos2 = Deinterleaver[symbol_index + 2];
+	      symbol_pos3 = Deinterleaver[symbol_index + 3];
+	      symbols_acc[0] +=
+		-llr[symbol_pos] - llr[symbol_pos2] - llr[symbol_pos3];
+	      symbols_acc[1] +=
+		-llr[symbol_pos] - llr[symbol_pos2] + llr[symbol_pos3];
+	      symbols_acc[2] +=
+		llr[symbol_pos] - llr[symbol_pos2] - llr[symbol_pos3];
+	      symbols_acc[3] +=
+		llr[symbol_pos] - llr[symbol_pos2] + llr[symbol_pos3];
+	      symbol_index += 4;
+	      break;
+	    default:
+	      symbol_index++;
+	    }
+
+	  /*                printf("puncture_ptr %p pp_increm = %d \n", puncture_ptr, *(puncture_ptr+1));   */
+	  puncture_ptr += *(puncture_ptr + 1);
+	  symbols_acc[7] = -symbols_acc[0];
+	  symbols_acc[6] = -symbols_acc[1];
+	  symbols_acc[5] = -symbols_acc[2];
+	  symbols_acc[4] = -symbols_acc[3];
+	  for (butterfly = 0; butterfly < NOOFBF; butterfly++)
+	    {
+	      metric_s1 = old_metrics[butterfly];
+	      metric_s2 = old_metrics[butterfly + NOOFBF];
+	      metric_inc = symbols_acc[(int) CODER_OUTPUT[butterfly]];
+	      new_metrics[2 * butterfly] = metric_s1 + metric_inc;
+	      path_reg[path_reg_index + 2 * butterfly] = 0;
+	      metric_path2 = metric_s2 - metric_inc;	/* Add */
+	      if (metric_path2 > new_metrics[2 * butterfly])
+		{		/* Compare */
+		  new_metrics[2 * butterfly] = metric_path2;
+		  path_reg[path_reg_index + 2 * butterfly] = 1;	/* Select */
+		}
+	      new_metrics[2 * butterfly + 1] = metric_s1 - metric_inc;
+	      path_reg[path_reg_index + 2 * butterfly + 1] = 0;
+	      metric_path2 = metric_s2 + metric_inc;	/* Add */
+	      if (metric_path2 > new_metrics[2 * butterfly + 1])
+		{		/* Compare */
+		  new_metrics[2 * butterfly + 1] = metric_path2;
+		  path_reg[path_reg_index + 2 * butterfly + 1] = 1;	/* Select */
+		}
+	    }
+
+	  /* are hard coded butterflies faster? */
+	  path_reg_index += STATES;
+	  swap_ptr = old_metrics;
+	  old_metrics = new_metrics;
+      new_metrics = (float *)swap_ptr;
+	}
+      if (part == 0)
+	{
+	  puncture_ptr = puncturing2 + 1;
+	  N_Part = N - N_tail;
+	}
+      else
+	{
+	  puncture_ptr = puncturing3 + 1;
+	  N_Part = N;
+	}
+    }
+
+  /* trace back */
+  state = 0;
+  symbol_index = N - 1;
+  info_index = L - 1;
+  N_Part = N - N_tail;
+  path_reg_index -= STATES;
+  puncture_ptr = puncturing3 + 1 - *puncturing3;
+  for (part = 2; part >= 0; part--)
+    {
+      while (symbol_index >= N_Part)
+	{
+	  infoout[info_index] = state & 0x1;	/* information bit output */
+	  info_index--;
+	  mask = 0 - ((state & 0x1) ^ path_reg[path_reg_index + state]);
+	  state = (state >> 1) | (path_reg[path_reg_index + state] * NOOFBF);
+
+	  /* code bits ouput and interleaving: */
+	  codebits = CODER_OUTPUT[state & ~(NOOFBF)] ^ mask;
+	  switch (*puncture_ptr)
+	    {
+	    case 3:
+	      cwout[Deinterleaver[symbol_index]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index]] |=
+		((codebits & 0x2) >> 1) << bitpos;
+	      symbol_index -= 2;
+	      break;
+	    case 5:
+	      cwout[Deinterleaver[symbol_index]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index]] |=
+		((codebits & 0x4) >> 2) << bitpos;
+	      symbol_index -= 2;
+	      break;
+	    case 7:
+	      cwout[Deinterleaver[symbol_index]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index]] |=
+		((codebits & 0x4) >> 2) << bitpos;
+	      cwout[Deinterleaver[symbol_index - 1]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index - 1]] |=
+		((codebits & 0x2) >> 1) << bitpos;
+	      symbol_index -= 3;
+	      break;
+	    case 15:
+	      cwout[Deinterleaver[symbol_index]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index]] |=
+		(codebits & 0x1) << bitpos;
+	      cwout[Deinterleaver[symbol_index - 1]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index - 1]] |=
+		((codebits & 0x4) >> 2) << bitpos;
+	      cwout[Deinterleaver[symbol_index - 2]] &= ~(0x1 << bitpos);
+	      cwout[Deinterleaver[symbol_index - 2]] |=
+		((codebits & 0x2) >> 1) << bitpos;
+	      symbol_index -= 4;
+	      break;
+	    default:
+	      symbol_index--;
+	    }
+	  cwout[Deinterleaver[symbol_index + 1]] &= ~(0x1 << bitpos);
+	  cwout[Deinterleaver[symbol_index + 1]] |=
+	    (codebits & 0x1) << bitpos;
+	  path_reg_index -= STATES;
+	  puncture_ptr -= *(puncture_ptr - 1);
+	}
+      if (part == 2)
+	{
+	  puncture_ptr = puncturing2 + 1 - *puncturing2;
+	  N_Part = N_PartA;
+	}
+      else
+	{
+	  puncture_ptr = puncturing1 + 1 - *puncturing1;
+	  N_Part = 0;
+	}
+    }
+  return 0;
+}
diff --git a/qsstv/drmrx/viterbi_decode.h b/qsstv/drmrx/viterbi_decode.h
new file mode 100644
index 0000000..eb4000b
--- /dev/null
+++ b/qsstv/drmrx/viterbi_decode.h
@@ -0,0 +1,92 @@
+
+/******************************************************************************/  
+
+/*                                                                            */ 
+
+/*  University of Kaiserslautern, Institute of Communications Engineering     */ 
+
+/*  Copyright (C) 2004 Torsten Schorr                                         */ 
+
+/*                                                                            */ 
+
+/*  Author(s)    : Torsten Schorr (schorr at eit.uni-kl.de)                      */ 
+
+/*  Project start: 15.06.2004                                                 */ 
+
+/*  Last change  : 30.06.2004                                                 */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+
+/*                                                                            */ 
+
+/*  This program is free software; you can redistribute it and/or modify      */ 
+
+/*  it under the terms of the GNU General Public License as published by      */ 
+
+/*  the Free Software Foundation; either version 2 of the License, or         */ 
+
+/*  (at your option) any later version.                                       */ 
+
+/*                                                                            */ 
+
+/*  This program is distributed in the hope that it will be useful,           */ 
+
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */ 
+
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */ 
+
+/*  GNU General Public License for more details.                              */ 
+
+/*                                                                            */ 
+
+/*  You should have received a copy of the GNU General Public License         */ 
+
+/*  along with this program; if not, write to the Free Software               */ 
+
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+  
+
+/******************************************************************************/ 
+
+/*                                                                            */ 
+
+/*  viterbi_decode.h (part of msd_hard)                                       */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+
+/*  Description:                                                              */ 
+
+/*  function headers and constants for viterbi_decode.c                       */ 
+
+/*                                                                            */ 
+
+/******************************************************************************/ 
+  
+#ifndef	STATES
+#define		STATES 64			/* number of states */
+#endif  /*  */
+#ifndef	NOOFBF
+#define		NOOFBF 32			/* number of butterflies */
+#endif  /*  */
+#ifndef QOFB
+#define		QOFB 8				/* number of different output patterns per input symbol */
+#endif  /*  */
+#ifndef NOPB
+#define		NOPB 4				/* number of parity bits */
+#endif  /*  */
+  
+
+/* decoder output for zero input, states 1:32  */ 
+const char CODER_OUTPUT[] = { 0, 6, 3, 5, 3, 5, 0, 6, 4, 2, 7, 1, 7, 1, 4, 2, 1, 7, 2, 4, 2, 4, 1, 7, 5, 3, 6, 0, 6, 0, 5, 3 };
+int viterbi_decode (float *llr, int N, int N_PartA, signed char *puncturing1, signed char *puncturing2,
+                       signed char *puncturing3, char *infoout, char *cwout, int bitpos, int *Deinterleaver, int L, int N_tail,
+                       char *memory_ptr);
+
diff --git a/qsstv/drmtx/bsrform.cpp b/qsstv/drmtx/bsrform.cpp
new file mode 100644
index 0000000..a9dccf9
--- /dev/null
+++ b/qsstv/drmtx/bsrform.cpp
@@ -0,0 +1,92 @@
+#include "bsrform.h"
+#include "ui_bsrform.h"
+#include "drmrx/drmstatusframe.h"
+#include "qsstvglobal.h"
+
+bsrForm::bsrForm(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::bsrForm)
+{
+  ui->setupUi(this);
+  connect(ui->bsrComboBox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotBSRSelection(int)));
+  connect(ui->cancelPushButton,SIGNAL(clicked()),this,SLOT(slotCanceled()));
+  connect(ui->easypalPushButton,SIGNAL(clicked()),this,SLOT(slotEasypal()));
+  connect(ui->compatiblePushButton,SIGNAL(clicked()),this,SLOT(slotCompatible()));
+
+}
+
+bsrForm::~bsrForm()
+{
+  delete ui;
+}
+
+void bsrForm::init()
+{
+  int i;
+  bsrPtr=srcDecoder.getBSR();
+  if(bsrPtr->count()==0)
+    {
+      ui->infoTextEdit->clear();
+      ui->infoTextEdit->appendPlainText("No BSR available");
+      return;
+    }
+  for(i=bsrPtr->count()-1;i>=0;i--) //latest first
+    {
+      ui->bsrComboBox->addItem(bsrPtr->at(i).tbPtr->fileName);
+    }
+  slotBSRSelection(0);
+}
+
+bool bsrForm::hasBSR()
+{
+  return (bsrPtr->count()>0);
+}
+
+QByteArray *bsrForm::getBA(bool compat)
+{
+  int i;
+  i=bsrPtr->count()-1-ui->bsrComboBox->currentIndex();
+  if(bsrPtr->count()>0)
+    {
+      if(srcDecoder.storeBSR(bsrPtr->at(i).tbPtr,compat))
+        {
+          drmParams.robMode=bsrPtr->at(i).tbPtr->robMode;
+          drmParams.interleaver=bsrPtr->at(i).tbPtr->interLeaver;
+          drmParams.qam=bsrPtr->at(i).tbPtr->mscMode;
+          drmParams.protection=bsrPtr->at(i).tbPtr->mpx;
+          drmParams.bandwith=bsrPtr->at(i).tbPtr->spectrum;
+          return(&bsrPtr->at(i).tbPtr->baBSR);
+        }
+    }
+  return NULL;
+}
+
+void bsrForm::slotBSRSelection(int idx)
+{
+  int i;
+  transportBlock *tbPtr;
+  i=bsrPtr->count()-1-idx;
+  ui->infoTextEdit->clear();
+  tbPtr=bsrPtr->at(i).tbPtr;
+  ui->infoTextEdit->appendPlainText(tbPtr->callsign);
+  ui->infoTextEdit->appendPlainText("Segments received: "+QString::number(tbPtr->segmentsReceived));
+  ui->infoTextEdit->appendPlainText("Total Segments: "+QString::number(tbPtr->totalSegments));
+  ui->infoTextEdit->appendPlainText(modeToString(tbPtr->modeCode));
+}
+
+void bsrForm::slotCanceled()
+{
+  done(CANCEL);
+}
+
+
+
+void bsrForm::slotEasypal()
+{
+  done(EASYPAL);
+}
+
+void bsrForm::slotCompatible()
+{
+  done (COMPAT);
+}
diff --git a/qsstv/drmtx/bsrform.h b/qsstv/drmtx/bsrform.h
new file mode 100644
index 0000000..215a100
--- /dev/null
+++ b/qsstv/drmtx/bsrform.h
@@ -0,0 +1,41 @@
+#ifndef BSRFORM_H
+#define BSRFORM_H
+
+#include <QDialog>
+#include "drmrx/sourcedecoder.h"
+#include "drmtx/drmtransmitter.h"
+
+namespace Ui {
+class bsrForm;
+}
+
+class bsrForm : public QDialog
+{
+  Q_OBJECT
+
+  
+public:
+  enum eResult {CANCEL,EASYPAL,COMPAT};
+  explicit bsrForm(QWidget *parent = 0);
+  ~bsrForm();
+  void init();
+  QByteArray *getBA(bool compat);
+  bool hasBSR();
+  drmTxParams getDRMParams() {return  drmParams;}
+public slots:
+  void slotCompatible();
+  void slotEasypal();
+  void slotCanceled();
+  
+private slots:
+ void slotBSRSelection(int);
+
+private:
+  Ui::bsrForm *ui;
+  void displayInfo(int idx);
+  QList <bsrBlock> *bsrPtr;
+  drmTxParams drmParams;
+
+};
+
+#endif // BSRFORM_H
diff --git a/qsstv/drmtx/bsrform.ui b/qsstv/drmtx/bsrform.ui
new file mode 100644
index 0000000..58aa48d
--- /dev/null
+++ b/qsstv/drmtx/bsrform.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>bsrForm</class>
+ <widget class="QDialog" name="bsrForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>514</width>
+    <height>367</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>BSR</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Send BSR (latest on top)</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QComboBox" name="bsrComboBox"/>
+   </item>
+   <item>
+    <widget class="QPlainTextEdit" name="infoTextEdit"/>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelPushButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="easypalPushButton">
+       <property name="text">
+        <string>Easypal</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="compatiblePushButton">
+       <property name="text">
+        <string>Compatible</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/drmtx/common/DRMSignalIO.cpp b/qsstv/drmtx/common/DRMSignalIO.cpp
new file mode 100644
index 0000000..db892f6
--- /dev/null
+++ b/qsstv/drmtx/common/DRMSignalIO.cpp
@@ -0,0 +1,142 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer, Cesco (HB9TLK)
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Transmit and receive data
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "DRMSignalIO.h"
+#include <iostream>
+#include "qsstvglobal.h"
+
+#define pi  4.0*atan(1.0) 
+
+/******************************************************************************\
+* Transmitter                                                                  *
+\******************************************************************************/
+void CTransmitData::ProcessDataInternal(CParameter&)
+{
+  int i;
+  const int iNs2 = iInputBlockSize * 2;
+  rNormFactor =1000.0 ; // pa0mbo (was 16000)
+  for (i = 0; i < iNs2; i += 2)
+    {
+      const int iCurIndex = iBlockCnt * iNs2 + i;
+
+      /* Imaginary, real */
+      const short sCurOutReal = (short) ((*pvecInputData)[i / 2].real() * rNormFactor);
+      const short  sCurOutImag = (short) ((*pvecInputData)[i / 2].imag() * rNormFactor);
+
+      /* Envelope, phase */
+      const short sCurOutEnv = (short) (Abs((*pvecInputData)[i / 2]) * (_REAL) 256.0);
+      const short sCurOutPhase = 	(short) (Angle((*pvecInputData)[i / 2]) * (_REAL) 5000.0);  /* 2^15 / pi / 2 -> approx. 5000 */
+
+      switch (eOutputFormat)
+        {
+        case OF_REAL_VAL:
+          vecsDataOut[iCurIndex] = vecsDataOut[iCurIndex + 1] = sCurOutReal;
+          //                  (short) 15000.0*sin(pi*1500.0*i/48000.0) ; //  pa0mbo 1500 Hz test signaal
+          //                printf("%d %d \n", i/2 , vecsDataOut[iCurIndex]);
+        break;
+
+        case OF_IQ_POS:
+          /* Send inphase and quadrature (I / Q) signal to stereo sound card
+         output. I: left channel, Q: right channel */
+          vecsDataOut[iCurIndex] = sCurOutReal;
+          vecsDataOut[iCurIndex + 1] = sCurOutImag;
+        break;
+
+        case OF_IQ_NEG:
+          /* Send inphase and quadrature (I / Q) signal to stereo sound card output. I: right channel, Q: left channel */
+          vecsDataOut[iCurIndex] = sCurOutImag;
+          vecsDataOut[iCurIndex + 1] = sCurOutReal;
+        break;
+
+        case OF_EP:
+          /* Send envelope and phase signal to stereo sound card output. Envelope: left channel, Phase: right channel */
+          vecsDataOut[iCurIndex] = sCurOutEnv;
+          vecsDataOut[iCurIndex + 1] = sCurOutPhase;
+        break;
+        }
+    }
+  iBlockCnt++;
+  if (iBlockCnt == iNumBlocks)
+    {
+      iBlockCnt = 0;
+      pSound->Write(vecsDataOut); //  printf("DRMSignalIO na pSound-> write\n");
+      addToLog(QString("writing vecsDataOut:%1").arg(vecsDataOut.size()),LOGDRMTX);
+     }
+}
+
+void CTransmitData::InitInternal(CParameter& TransmParam)
+{
+/*
+	float*	pCurFilt;
+	int		iNumTapsTransmFilt;
+	CReal	rNormCurFreqOffset;
+*/
+	const int iSymbolBlockSize = TransmParam.CellMappingTable.iSymbolBlockSize;
+
+	/* Init vector for storing a complete DRM frame number of OFDM symbols */
+	iBlockCnt = 0;
+	TransmParam.Lock(); 
+	iNumBlocks = TransmParam.CellMappingTable.iNumSymPerFrame;
+	ESpecOcc eSpecOcc = TransmParam.GetSpectrumOccup();
+	TransmParam.Unlock(); 
+	iBigBlockSize = iSymbolBlockSize * 2 * iNumBlocks;  /* stereo */
+        // printf("iBigBlockSize in init Ctransmitdata = %d\n", iBigBlockSize);
+
+	vecsDataOut.Init(iBigBlockSize);
+
+	if (pFileTransmitter != NULL)
+	{
+		fclose(pFileTransmitter);
+	}
+
+  /* Init sound interface */
+//		pSound->Init(iBigBlockSize, true);
+
+	/* Init bandpass filter object */
+  BPFilter.Init(iSymbolBlockSize, rDefCarOffset, eSpecOcc,CDRMBandpassFilt::FT_TRANSMITTER);
+     /*   printf("DRMSignalIO BPFilter init Symbolsize %d rDefCaroffset %g  eSpecooc %d \n",
+			iSymbolBlockSize, rDefCarOffset, eSpecOcc); */
+
+
+	/* All robustness modes and spectrum occupancies should have the same output
+	   power. Calculate the normaization factor based on the average power of
+	   symbol (the number 3000 was obtained through output tests) */
+	rNormFactor = (CReal) 6000.0 / Sqrt(TransmParam.CellMappingTable.rAvPowPerSymbol);  // pa0mbo was 3000.0 nu as in ham
+	/* Define block-size for input */
+	iInputBlockSize = iSymbolBlockSize;
+}
+
+CTransmitData::~CTransmitData()
+{
+	/* Close file */
+	if (pFileTransmitter != NULL)
+		fclose(pFileTransmitter);
+}
+
diff --git a/qsstv/drmtx/common/DRMSignalIO.h b/qsstv/drmtx/common/DRMSignalIO.h
new file mode 100644
index 0000000..d039eed
--- /dev/null
+++ b/qsstv/drmtx/common/DRMSignalIO.h
@@ -0,0 +1,110 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See DRMSignalIO.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DRMSIGNALIO_H__3B0BA660_CA63_4344_B_23E7A0D31912__INCLUDED_)
+#define DRMSIGNALIO_H__3B0BA660_CA63_4344_B_23E7A0D31912__INCLUDED_
+
+#include "Parameter.h"
+#include "soundinterface.h"
+#include <math.h>
+#include "matlib/Matlib.h"
+//#include "IQInputFilter.h"
+#include "util/Modul.h"
+#include "util/Utilities.h"
+
+/* Definitions ****************************************************************/
+/* Number of FFT blocks used for averaging. See next definition
+   ("NUM_SMPLS_4_INPUT_SPECTRUM") for how to set the parameters */
+#define NUM_AV_BLOCKS_PSD			16
+#define LEN_PSD_AV_EACH_BLOCK		512
+
+/* same but for the rpsd tag */
+#define NUM_AV_BLOCKS_PSD_RSI	150
+#define LEN_PSD_AV_EACH_BLOCK_RSI		256
+#define PSD_OVERLAP_RSI	128
+
+/* power gain of the Hamming window */
+#define PSD_WINDOW_GAIN 0.39638
+
+/* Length of vector for input spectrum. We use approx. 0.2 sec
+   of sampled data for spectrum calculation, this is 2^13 = 8192 to 
+   make the FFT work more efficient. Make sure that this number is not smaller
+   than the symbol lenght in 48 khz domain of longest mode (which is mode A/B:
+   1280) */
+#define NUM_SMPLS_4_INPUT_SPECTRUM (NUM_AV_BLOCKS_PSD * LEN_PSD_AV_EACH_BLOCK)
+
+/* The RSI output needs 400ms with a 50% overlap, so this needs more space 
+   I think the RSCI spec is slightly wrong - using 150 windows consumes just over 400ms, 149 would be exact */
+#define INPUT_DATA_VECTOR_SIZE (NUM_AV_BLOCKS_PSD_RSI * (LEN_PSD_AV_EACH_BLOCK_RSI-PSD_OVERLAP_RSI)+PSD_OVERLAP_RSI)
+
+#define RNIP_SEARCH_RANGE_NARROW 5100.0
+#define RNIP_SEARCH_RANGE_WIDE 15100.0
+#define RNIP_EXCLUDE_BINS 2 // either side of the peak
+
+/* Use raw 16 bit data or in text form for file format for DRM data. Defining
+   the following macro will enable the raw data option */
+#define FILE_DRM_USING_RAW_DATA
+
+
+/* Classes ********************************************************************/
+class CTransmitData : public CTransmitterModul<_COMPLEX, _COMPLEX>
+{
+public:
+	enum EOutFormat {OF_REAL_VAL /* real valued */, OF_IQ_POS,
+		OF_IQ_NEG /* I / Q */, OF_EP /* envelope / phase */};
+
+	CTransmitData(CSoundOutInterface* pNS) : pFileTransmitter(NULL), pSound(pNS), 
+    eOutputFormat(OF_REAL_VAL), rDefCarOffset((_REAL) VIRTUAL_INTERMED_FREQ)
+//  , strOutFileName("test/TransmittedData.txt")
+  {
+  }
+	virtual ~CTransmitData();
+
+	void SetIQOutput(const EOutFormat eFormat) {eOutputFormat = eFormat;}
+	EOutFormat GetIQOutput() {return eOutputFormat;}
+
+  void SetCarOffset(const CReal rNewCarOffset) {rDefCarOffset = rNewCarOffset;}
+
+
+protected:
+	FILE*				pFileTransmitter;
+	CSoundOutInterface*	pSound;
+	CVector<short>		vecsDataOut;  
+	int					iBlockCnt;
+	int					iNumBlocks;
+	EOutFormat			eOutputFormat;
+	CDRMBandpassFilt	BPFilter;
+	CReal				rDefCarOffset;
+	CReal				rNormFactor;
+	int					iBigBlockSize;
+//	string				strOutFileName;
+  virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& Parameter);
+};
+
+#endif // !defined(DRMSIGNALIO_H__3B0BA660_CA63_4344_B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/DataIO.cpp b/qsstv/drmtx/common/DataIO.cpp
new file mode 100644
index 0000000..3e4b76b
--- /dev/null
+++ b/qsstv/drmtx/common/DataIO.cpp
@@ -0,0 +1,72 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001-2006
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "DataIO.h"
+#include <iomanip>
+#include <time.h>
+
+
+
+
+/******************************************************************************\
+* FAC data																	   *
+\******************************************************************************/
+/* Transmitter */
+void CGenerateFACData::ProcessDataInternal(CParameter& TransmParam)
+{
+	FACTransmit.FACParam(pvecOutputData, TransmParam);
+}
+
+void CGenerateFACData::InitInternal(CParameter& TransmParam)
+{
+	FACTransmit.Init(TransmParam);
+
+	/* Define block-size for output */
+	iOutputBlockSize = NUM_FAC_BITS_PER_BLOCK;
+}
+
+
+
+
+/******************************************************************************\
+* SDC data																	   *
+\******************************************************************************/
+/* Transmitter */
+void CGenerateSDCData::ProcessDataInternal(CParameter& TransmParam)
+{
+	SDCTransmit.SDCParam(pvecOutputData, TransmParam);
+}
+
+void CGenerateSDCData::InitInternal(CParameter& TransmParam)
+{
+	/* Define block-size for output */
+	iOutputBlockSize = TransmParam.iNumSDCBitsPerSFrame;
+}
+
+
+
diff --git a/qsstv/drmtx/common/DataIO.h b/qsstv/drmtx/common/DataIO.h
new file mode 100644
index 0000000..7342d18
--- /dev/null
+++ b/qsstv/drmtx/common/DataIO.h
@@ -0,0 +1,129 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001-2005
+ *
+ * Author(s):
+ *	Volker Fischer, Andrew Murphy
+ *
+ * Description:
+ *	See Data.cpp
+ *
+ * 11/21/2005 Andrew Murphy, BBC Research & Development, 2005
+ *	- Addition GetSDCReceive(), Added CSplit class
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DATA_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define DATA_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+# include "soundinterface.h"
+#include "Parameter.h"
+#include "util/Modul.h"
+#include "FAC/FAC.h"
+#include "SDC/SDC.h"
+//#include "TextMessage.h"
+//#include "util/AudioFile.h"
+#include "util/Utilities.h"
+//#include "AMDemodulation.h" // For CMixer
+
+/* Definitions ****************************************************************/
+/* In case of random-noise, define number of blocks */
+#define DEFAULT_NUM_SIM_BLOCKS		50
+
+/* Length of vector for audio spectrum. We use a power-of-two length to 
+   make the FFT work more efficient */
+#define NUM_SMPLS_4_AUDIO_SPECTRUM	256
+
+/* Time span used for averaging the audio spectrum. Shall be higher than the
+   400 ms DRM audio block */
+#define TIME_AV_AUDIO_SPECT_MS		500 /* ms */		
+
+/* Number of blocks for averaging the audio spectrum */
+#define NUM_BLOCKS_AV_AUDIO_SPEC	Ceil(((_REAL) SOUNDCRD_SAMPLE_RATE * \
+	TIME_AV_AUDIO_SPECT_MS / 1000 / NUM_SMPLS_4_AUDIO_SPECTRUM))
+
+/* Normalization constant for two mixed signals. If this constant is 2, no
+   overrun of the "short" variable can happen but signal has quite much lower
+   power -> compromise */
+#define MIX_OUT_CHAN_NORM_CONST		((_REAL) 1.0 / sqrt((_REAL) 2.0))
+
+
+
+class CGenSimData : public CTransmitterModul<_BINARY, _BINARY>
+{
+public:
+	CGenSimData() : eCntType(CT_TIME), iNumSimBlocks(DEFAULT_NUM_SIM_BLOCKS),
+		iNumErrors(0), iCounter(0), strFileName("SimTime.dat"), tiStartTime(0) {}
+	virtual ~CGenSimData() {}
+
+	void SetSimTime(int iNewTi, string strNewFileName);
+	void SetNumErrors(int iNewNE, string strNewFileName);
+
+protected:
+	enum ECntType {CT_TIME, CT_ERRORS};
+	ECntType	eCntType;
+	int			iNumSimBlocks;
+	int			iNumErrors;
+	int			iCounter;
+	int			iMinNumBlocks;
+	string		strFileName;
+	time_t		tiStartTime;
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+
+/* FAC ---------------------------------------------------------------------- */
+class CGenerateFACData : public CTransmitterModul<_BINARY, _BINARY>
+{
+public:
+	CGenerateFACData() {}
+	virtual ~CGenerateFACData() {}
+
+protected:
+	CFACTransmit FACTransmit;
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+
+/* SDC ---------------------------------------------------------------------- */
+class CGenerateSDCData : public CTransmitterModul<_BINARY, _BINARY>
+{
+public:
+	CGenerateSDCData() {}
+	virtual ~CGenerateSDCData() {}
+
+protected:
+	CSDCTransmit SDCTransmit;
+		
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+
+#endif // !defined(DATA_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/DrmTransmitter.cpp b/qsstv/drmtx/common/DrmTransmitter.cpp
new file mode 100644
index 0000000..592986a
--- /dev/null
+++ b/qsstv/drmtx/common/DrmTransmitter.cpp
@@ -0,0 +1,232 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use by Ties Bos - PA0MBO
+ *
+ * Description:
+ *	DRM-transmitter
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "DrmTransmitter.h"
+#include "csoundout.h"
+#include <qsstvglobal.h>
+#include "utils/supportfunctions.h"
+
+/* Implementation *************************************************************/
+void CDRMTransmitter::Start()
+{
+
+  TransmParam.bRunThread = true; // Set run flag
+//  Init(); // Initialization of the modules
+  Run();
+}
+
+void CDRMTransmitter::Stop()
+{
+	TransmParam.bRunThread = false;
+}
+
+void CDRMTransmitter::Run()
+{
+  /*
+  The hand over of data is done via an intermediate-buffer. The calling
+  convention is always "input-buffer, output-buffer". Additional, the
+  DRM-parameters are fed to the function
+*/
+  while (TransmParam.bRunThread)
+    {
+      addToLog("AudioSourceEncoder",LOGDRMTX);
+      AudioSourceEncoder.ProcessData(TransmParam,  AudSrcBuf);
+
+      //arrayBinDump(QString("audiosrc %1").arg(runCounter++),AudSrcBuf,32,true);
+      addToLog("MSCMLCEncoder",LOGPERFORM);
+      MSCMLCEncoder.ProcessData(TransmParam, AudSrcBuf, MLCEncBuf);
+      addToLog("SymbInterleaver",LOGPERFORM);
+      SymbInterleaver.ProcessData(TransmParam, MLCEncBuf, IntlBuf);
+      addToLog("GenerateFACData",LOGPERFORM);
+      GenerateFACData.ReadData(TransmParam, GenFACDataBuf);
+      addToLog("FACMLCEncoder",LOGPERFORM);
+      FACMLCEncoder.ProcessData(TransmParam, GenFACDataBuf, FACMapBuf);
+      addToLog("OFDMCellMapping",LOGPERFORM);
+      OFDMCellMapping.ProcessData(TransmParam, IntlBuf, FACMapBuf, CarMapBuf);
+      addToLog("OFDMModulation",LOGPERFORM);
+      OFDMModulation.ProcessData(TransmParam, CarMapBuf, OFDMModBuf);
+      addToLog("TransmitData",LOGPERFORM);
+      TransmitData.WriteData(TransmParam, OFDMModBuf);
+//      arrayComplexDump(QString("cd "),OFDMModBuf.getVecBuffer(),8,true);
+
+      if (stopDRM)
+        {
+          TransmParam.bRunThread=false;
+          addToLog("stopping drm",LOGPERFORM);
+        }
+    }
+}
+
+void CDRMTransmitter::Init()
+{
+  int PacLen, nr_decoded_bits  ;   // added pa0mbo
+
+  OFDMCellMapping.Init(TransmParam, CarMapBuf); // Defines number of cells, important!
+  //	SDCMLCEncoder.Init(TransmParam, SDCMapBuf); // Defines number of SDC bits per super-frame
+  MSCMLCEncoder.Init(TransmParam, MLCEncBuf);
+  nr_decoded_bits = TransmParam.iNumDecodedBitsMSC ;
+  PacLen = (nr_decoded_bits/8) - 3 ;
+  // printf("PacLen is %d\n", PacLen);
+  TransmParam.Service[0].DataParam.iPacketLen=PacLen;
+
+  // added Oct 19th 2011 pa0mbo
+  TransmParam.iNumAudioService=0;
+  TransmParam.iNumDataService =1 ;
+  TransmParam.Service[0].eAudDataFlag = CService::SF_DATA;
+  TransmParam.Service[0].DataParam.iStreamID = 0;
+  TransmParam.Service[0].DataParam.eDataUnitInd = CDataParam::DU_DATA_UNITS;
+  TransmParam.Service[0].DataParam.eAppDomain = CDataParam::AD_DAB_SPEC_APP;
+  // end added block
+  SymbInterleaver.Init(TransmParam, IntlBuf);
+  GenerateFACData.Init(TransmParam, GenFACDataBuf);
+  FACMLCEncoder.Init(TransmParam, FACMapBuf);
+  OFDMModulation.Init(TransmParam, OFDMModBuf);
+  AudioSourceEncoder.Init(TransmParam, AudSrcBuf);
+  TransmitData.Init(TransmParam);
+}
+
+CDRMTransmitter::CDRMTransmitter() :
+  pSoundOutInterface(new CSoundOut),
+  TransmitData(pSoundOutInterface),
+
+  // UEP only works with Dream receiver, FIXME! -> disabled for now
+  bUseUEP(false)
+{
+}
+
+void CDRMTransmitter::init_base()
+{
+  TransmParam.init();
+   /* Init streams */
+  TransmParam.ResetServicesStreams();
+
+  /* Init frame ID counter (index) */
+  TransmParam.iFrameIDTransm = 0;
+
+  /* Date, time. TODO: use computer system time... */
+  TransmParam.iDay = 0;
+  TransmParam.iMonth = 0;
+  TransmParam.iYear = 0;
+  TransmParam.iUTCHour = 0;
+  TransmParam.iUTCMin = 0;
+
+
+  /**************************************************************************/
+  /* Robustness mode and spectrum occupancy. Available transmission modes:
+     RM_ROBUSTNESS_MODE_A: Gaussian channels, with minor fading,
+     RM_ROBUSTNESS_MODE_B: Time and frequency selective channels, with longer
+     delay spread,
+     RM_ROBUSTNESS_MODE_C: As robustness mode B, but with higher Doppler
+     spread,
+     RM_ROBUSTNESS_MODE_D: As robustness mode B, but with severe delay and
+     Doppler spread.
+     Available bandwidths:
+     SO_0: 4.5 kHz, SO_1: 5 kHz, SO_2: 9 kHz, SO_3: 10 kHz, SO_4: 18 kHz,
+     SO_5: 20 kHz
+
+           PA0MBO: for ham use now only modes A, B and E */
+  TransmParam.InitCellMapTable(RM_ROBUSTNESS_MODE_E, SO_1);                            // was B pa0mbo 21-10-2011
+
+  /* Protection levels for MSC. Depend on the modulation scheme. Look at
+     TableMLC.h, iCodRateCombMSC16SM, iCodRateCombMSC64SM,
+     iCodRateCombMSC64HMsym, iCodRateCombMSC64HMmix for available numbers */
+  TransmParam.MSCPrLe.iPartA = 0;
+  TransmParam.MSCPrLe.iPartB = 0;
+  TransmParam.MSCPrLe.iHierarch = 0;
+
+  /* Either one audio or one data service can be chosen */
+//  _BOOLEAN bIsAudio = false;
+
+  CService Service;
+
+  /* In the current version only one service and one stream is supported. The
+     stream IDs must be 0 in both cases */
+
+
+  /* Data Service only, no Audio*/
+  TransmParam.SetNumOfServices(0,1);
+  TransmParam.SetCurSelDataService(0);
+  TransmParam.SetAudDataFlag(0,  CService::SF_DATA);
+  CDataParam DataParam;
+  DataParam.iStreamID = 0;
+  /* Init SlideShow application */
+  DataParam.iPacketLen = 45; /* TEST */
+  DataParam.eDataUnitInd = CDataParam::DU_DATA_UNITS;
+  DataParam.eAppDomain = CDataParam::AD_DAB_SPEC_APP;
+  TransmParam.SetDataParam(0, DataParam);
+
+  /* The value 0 indicates that the application details are provided solely by SDC data entity type 5 */
+  Service.iServiceDescr = 0;
+
+
+  /* Init service parameters, 24 bit unsigned integer number */
+  Service.iServiceID = 0;
+
+  // Service label data. Up to 16 bytes defining the label using UTF-8 coding
+  Service.strLabel = "MYCALL";
+
+  /* Language (see TableFAC.h, "strTableLanguageCode[]") */
+  Service.iLanguage = 5; /* 5 -> english */
+
+  TransmParam.SetServiceParameters(0, Service);
+
+  /* Interleaver mode of MSC service. Long interleaving (2 s): SI_LONG, short interleaving (400 ms): SI_SHORT */
+  TransmParam.eSymbolInterlMode = CParameter::SI_LONG;
+
+  /* MSC modulation scheme. Available modes:
+     16-QAM standard mapping (SM): CS_2_SM,
+     64-QAM standard mapping (SM): CS_3_SM,
+     64-QAM symmetrical hierarchical mapping (HMsym): CS_3_HMSYM,
+     64-QAM mixture of the previous two mappings (HMmix): CS_3_HMMIX */
+  TransmParam.eMSCCodingScheme = CS_2_SM;      // was CS_3_SM pa0mbo 21-11-2011
+
+  /* SDC modulation scheme. Available modes:
+     4-QAM standard mapping (SM): CS_1_SM,
+     16-QAM standard mapping (SM): CS_2_SM */
+  // 	TransmParam.eSDCCodingScheme = CS_2_SM;    pa0mbo
+
+  /* Set desired intermedia frequency (IF) in Hertz */
+  SetCarOffset(350.0); /* Default: "VIRTUAL_INTERMED_FREQ" was 12000.0  pa0mbo */
+  rDefCarOffset=(_REAL) VIRTUAL_INTERMED_FREQ;
+
+  if (bUseUEP == true)
+    {
+      // TEST
+      TransmParam.SetStreamLen(0, 80, 0);
+    }
+  else
+    {
+      /* Length of part B is set automatically (equal error protection (EEP),
+       if "= 0"). Sets the number of bytes, should not exceed total number
+       of bytes available in MSC block */
+      TransmParam.SetStreamLen(0, 0, 0);
+    }
+}
+
diff --git a/qsstv/drmtx/common/DrmTransmitter.h b/qsstv/drmtx/common/DrmTransmitter.h
new file mode 100644
index 0000000..8c37b03
--- /dev/null
+++ b/qsstv/drmtx/common/DrmTransmitter.h
@@ -0,0 +1,127 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See DrmTransmitter.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DRMTRANSM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define DRMTRANSM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include <iostream>
+#include "util/Buffer.h"
+#include "Parameter.h"
+#include "DataIO.h"
+#include "mlc/MLC.h"
+#include "interleaver/SymbolInterleaver.h"
+#include "ofdmcellmapping/OFDMCellMapping.h"
+#include "OFDM.h"
+#include "DRMSignalIO.h"
+#include "sourcedecoders/AudioSourceDecoder.h"
+#include "soundinterface.h"
+
+
+
+/* Classes ********************************************************************/
+class CDRMTransmitter
+{
+public:
+  CDRMTransmitter();
+  virtual ~CDRMTransmitter() {}
+
+  void Init();
+  void init_base();
+  void Start();
+  void Stop();
+
+  /* Get pointer to internal modules */
+  //	CSoundInInterface*		GetSoundInInterface() {return pSoundInInterface;}
+  CSoundOutInterface*		GetSoundOutInterface() {return pSoundOutInterface;}
+  CAudioSourceEncoder*	GetAudSrcEnc() {return &AudioSourceEncoder;}
+  CTransmitData*			GetTransData() {return &TransmitData;}
+  //	CReadData*				GetReadData() {return &ReadData;}
+
+  CParameter*				GetParameters() {return &TransmParam;}
+
+  void SetCarOffset(const _REAL rNewCarOffset)
+  {
+    /* Has to be set in OFDM modulation and transmitter filter module */
+    OFDMModulation.SetCarOffset(rNewCarOffset);
+    TransmitData.SetCarOffset(rNewCarOffset);
+    rDefCarOffset = rNewCarOffset;
+  }
+  _REAL GetCarOffset() {return rDefCarOffset;}
+
+protected:
+  void Run();
+
+  /* Parameters */
+  CParameter				TransmParam;
+
+  /* Buffers */
+  CSingleBuffer<_SAMPLE>	DataBuf;
+  CSingleBuffer<_BINARY>	AudSrcBuf;
+
+  CSingleBuffer<_COMPLEX>	MLCEncBuf;
+  CCyclicBuffer<_COMPLEX>	IntlBuf;
+
+  CSingleBuffer<_BINARY>	GenFACDataBuf;
+  CCyclicBuffer<_COMPLEX>	FACMapBuf;
+
+  CSingleBuffer<_BINARY>	GenSDCDataBuf;
+  CCyclicBuffer<_COMPLEX>	SDCMapBuf;
+
+  CSingleBuffer<_COMPLEX>	CarMapBuf;
+  CSingleBuffer<_COMPLEX>	OFDMModBuf;
+
+  //	CSoundInInterface*		pSoundInInterface;
+  CSoundOutInterface*		pSoundOutInterface;
+
+  /* Modules */
+  //	CReadData				ReadData;
+  CAudioSourceEncoder		AudioSourceEncoder;
+  CMSCMLCEncoder			MSCMLCEncoder;
+  CSymbInterleaver		SymbInterleaver;
+  CGenerateFACData		GenerateFACData;
+  CFACMLCEncoder			FACMLCEncoder;
+  CGenerateSDCData		GenerateSDCData;
+  CSDCMLCEncoder			SDCMLCEncoder;
+  COFDMCellMapping		OFDMCellMapping;
+  COFDMModulation			OFDMModulation;
+  CTransmitData			  TransmitData;
+
+  _REAL					rDefCarOffset;
+  _BOOLEAN				bUseUEP;
+};
+
+#define BWs 2
+#define MODES 3
+#define PROTECTIONS 2
+#define QAMS 3
+extern int partTable[BWs][MODES][PROTECTIONS][QAMS];
+
+
+
+
+#endif // !defined(DRMTRANSM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/FAC/FAC.cpp b/qsstv/drmtx/common/FAC/FAC.cpp
new file mode 100644
index 0000000..13072e8
--- /dev/null
+++ b/qsstv/drmtx/common/FAC/FAC.cpp
@@ -0,0 +1,545 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos PA0MBO
+ *
+ * Description:
+ *	FAC
+ *
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "FAC.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* CFACTransmit																   *
+\******************************************************************************/
+void CFACTransmit::FACParam(CVector<_BINARY>* pbiFACData, CParameter& Parameter)
+{
+
+
+	/* Reset enqueue function */
+	(*pbiFACData).ResetBitAccess();
+
+	/* Put FAC parameters on stream */
+	/* Channel parameters --------------------------------------------------- */
+
+	/* Identity m 2 bits  */
+	/* Manage index of FAC block in super-frame */
+	switch (Parameter.iFrameIDTransm)
+	{
+	case 0:
+		/* Assuming AFS is valid (AFS not used here), if AFS is not valid, the
+		   parameter must be 3 (11) */
+		(*pbiFACData).Enqueue(3 /* 11 */, 2);
+		break;
+
+	case 1:
+		(*pbiFACData).Enqueue(1 /* 01 */, 2);
+		break;
+
+	case 2:
+		(*pbiFACData).Enqueue(2 /* 10 */, 2);
+		break;
+	}
+
+	/* Spectrum occupancy for ham use only 1 bit  */
+	switch (Parameter.GetSpectrumOccup())
+	{
+	case SO_0:
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);
+		break;
+
+	case SO_1:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+		break;
+    default:
+      (*pbiFACData).Enqueue(0 /* 0 */, 1);
+      break;
+
+
+	}
+
+	/* Interleaver depth flag ham situation 1 bit */
+	switch (Parameter.eSymbolInterlMode)
+	{
+	case CParameter::SI_LONG:
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);
+		break;
+
+	case CParameter::SI_SHORT:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+		break;
+	}
+
+	/* MSC mode */
+	switch (Parameter.eMSCCodingScheme)
+	{
+	case CS_3_SM:
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);
+		break;
+
+	case CS_1_SM:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+		break;
+
+	case CS_2_SM:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+		break;
+	default:
+		break;
+	}
+
+	/* Prot Level  1 bit */
+	switch (Parameter.MSCPrLe.iPartB)
+	{
+	case 0:
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);
+		break;
+
+	case 1:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+		break;
+	}
+
+	/* Audio/Data flag */
+	switch (Parameter.Service[0].eAudDataFlag)
+	{
+	case CService::SF_AUDIO:
+	
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);
+		(*pbiFACData).Enqueue(3 /* 11 */, 2);  // pa0mbo dummy bits SSTV
+		(*pbiFACData).Enqueue(0 /* 0 */, 1);  // pa0mbo  no text
+		break;
+
+	case CService::SF_DATA:
+		(*pbiFACData).Enqueue(1 /* 1 */, 1);
+
+		/* Packet Id */
+		(*pbiFACData).Enqueue( 
+			(uint32_t) Parameter.Service[0].DataParam.iPacketID, 2);
+		/* Extended MSC mode 1 bit */
+		if (Parameter.eMSCCodingScheme == CS_1_SM)
+			(*pbiFACData).Enqueue(1 /* 1 */, 1);  // QAM 4
+		else
+			(*pbiFACData).Enqueue(0 /* 0 */, 1);  // others
+		break;
+
+	}
+
+	{
+		int	iLenLabel;
+		int iframet = Parameter.iFrameIDTransm;
+		const int iLenLabelTmp = Parameter.Service[0].strLabel.length();
+		if (iLenLabelTmp > 9)
+			iLenLabel = 9;
+		else
+			iLenLabel = iLenLabelTmp;
+		/* Set all characters of label string */
+		for (int i = 3*iframet; i < 3*iframet+3; i++)
+		{
+			char cNewChar;
+			if (i >= iLenLabel)
+				cNewChar = 0;
+			else
+				cNewChar = Parameter.Service[0].strLabel[i];
+			cNewChar &= 127;
+			/* Set character */
+			(*pbiFACData).Enqueue((uint32_t) cNewChar, 7);
+		}
+	}
+
+
+	/* CRC ------------------------------------------------------------------ */
+	/* Calculate the CRC and put at the end of the stream */
+	CRCObject.Reset(8);
+
+	(*pbiFACData).ResetBitAccess();
+
+	for (int i = 0; i < NUM_FAC_BITS_PER_BLOCK / SIZEOF__BYTE - 1; i++)
+		CRCObject.AddByte((_BYTE) (*pbiFACData).Separate(SIZEOF__BYTE));
+
+	/* Now, pointer in "enqueue"-function is back at the same place,
+	   add CRC */
+	(*pbiFACData).Enqueue(CRCObject.GetCRC(), 8);
+}
+
+void CFACTransmit::Init(CParameter& Parameter)
+{
+	set<int>	actServ;
+
+	/* Get active services */
+	Parameter.GetActiveServices(actServ);
+	const size_t iTotNumServices = actServ.size();
+
+	/* Check how many audio and data services present */
+	vector<int>	veciAudioServ;
+	vector<int>	veciDataServ;
+	size_t		iNumAudio = 0;
+	size_t		iNumData = 0;
+
+	for (set<int>::iterator i = actServ.begin(); i!=actServ.end(); i++)
+	{
+		if (Parameter.Service[*i].eAudDataFlag == CService::SF_AUDIO)
+		{
+			veciAudioServ.push_back(*i);
+			iNumAudio++;
+		}
+		else
+		{
+			veciDataServ.push_back(*i);
+			iNumData++;
+		}
+	}
+
+	/* Now check special cases which are defined in 6.3.6-------------------- */
+	/* If we have only data or only audio services. When all services are of
+	   the same type (e.g. all audio or all data) then the services shall be
+	   signalled sequentially */
+	if ((iNumAudio == iTotNumServices) || (iNumData == iTotNumServices))
+	{
+		/* Init repetition vector */
+		FACNumRep = iTotNumServices;
+		FACRepetition.resize(0);
+
+		for (set<int>::iterator i = actServ.begin(); i!=actServ.end(); i++)
+			FACRepetition.push_back(*i);
+	}
+	else
+	{
+		/* Special cases according to Table 60 (Service parameter repetition
+		   patterns for mixtures of audio and data services) */
+		if (iNumAudio == 1)
+		{
+			if (iNumData == 1)
+			{
+				/* Init repetion vector */
+				FACNumRep = 5;
+				FACRepetition.resize(FACNumRep);
+
+				/* A1A1A1A1D1 */
+				FACRepetition[0] = veciAudioServ[0];
+				FACRepetition[1] = veciAudioServ[0];
+				FACRepetition[2] = veciAudioServ[0];
+				FACRepetition[3] = veciAudioServ[0];
+				FACRepetition[4] = veciDataServ[0];
+			}
+			else if (iNumData == 2)
+			{
+				/* Init repetion vector */
+				FACNumRep = 10;
+				FACRepetition.resize(FACNumRep);
+
+				/* A1A1A1A1D1A1A1A1A1D2 */
+				FACRepetition[0] = veciAudioServ[0];
+				FACRepetition[1] = veciAudioServ[0];
+				FACRepetition[2] = veciAudioServ[0];
+				FACRepetition[3] = veciAudioServ[0];
+				FACRepetition[4] = veciDataServ[0];
+				FACRepetition[5] = veciAudioServ[0];
+				FACRepetition[6] = veciAudioServ[0];
+				FACRepetition[7] = veciAudioServ[0];
+				FACRepetition[8] = veciAudioServ[0];
+				FACRepetition[9] = veciDataServ[1];
+			}
+			else /* iNumData == 3 */
+			{
+				/* Init repetion vector */
+				FACNumRep = 15;
+				FACRepetition.resize(FACNumRep);
+
+				/* A1A1A1A1D1A1A1A1A1D2A1A1A1A1D3 */
+				FACRepetition[0] = veciAudioServ[0];
+				FACRepetition[1] = veciAudioServ[0];
+				FACRepetition[2] = veciAudioServ[0];
+				FACRepetition[3] = veciAudioServ[0];
+				FACRepetition[4] = veciDataServ[0];
+				FACRepetition[5] = veciAudioServ[0];
+				FACRepetition[6] = veciAudioServ[0];
+				FACRepetition[7] = veciAudioServ[0];
+				FACRepetition[8] = veciAudioServ[0];
+				FACRepetition[9] = veciDataServ[1];
+				FACRepetition[10] = veciAudioServ[0];
+				FACRepetition[11] = veciAudioServ[0];
+				FACRepetition[12] = veciAudioServ[0];
+				FACRepetition[13] = veciAudioServ[0];
+				FACRepetition[14] = veciDataServ[2];
+			}
+		}
+		else if (iNumAudio == 2)
+		{
+			if (iNumData == 1)
+			{
+				/* Init repetion vector */
+				FACNumRep = 5;
+				FACRepetition.resize(FACNumRep);
+
+				/* A1A2A1A2D1 */
+				FACRepetition[0] = veciAudioServ[0];
+				FACRepetition[1] = veciAudioServ[1];
+				FACRepetition[2] = veciAudioServ[0];
+				FACRepetition[3] = veciAudioServ[1];
+				FACRepetition[4] = veciDataServ[0];
+			}
+			else /* iNumData == 2 */
+			{
+				/* Init repetion vector */
+				FACNumRep = 10;
+				FACRepetition.resize(FACNumRep);
+
+				/* A1A2A1A2D1A1A2A1A2D2 */
+				FACRepetition[0] = veciAudioServ[0];
+				FACRepetition[1] = veciAudioServ[1];
+				FACRepetition[2] = veciAudioServ[0];
+				FACRepetition[3] = veciAudioServ[1];
+				FACRepetition[4] = veciDataServ[0];
+				FACRepetition[5] = veciAudioServ[0];
+				FACRepetition[6] = veciAudioServ[1];
+				FACRepetition[7] = veciAudioServ[0];
+				FACRepetition[8] = veciAudioServ[1];
+				FACRepetition[9] = veciDataServ[1];
+			}
+		}
+		else /* iNumAudio == 3 */
+		{
+			/* Init repetion vector */
+			FACNumRep = 7;
+			FACRepetition.resize(FACNumRep);
+
+			/* A1A2A3A1A2A3D1 */
+			FACRepetition[0] = veciAudioServ[0];
+			FACRepetition[1] = veciAudioServ[1];
+			FACRepetition[2] = veciAudioServ[2];
+			FACRepetition[3] = veciAudioServ[0];
+			FACRepetition[4] = veciAudioServ[1];
+			FACRepetition[5] = veciAudioServ[2];
+			FACRepetition[6] = veciDataServ[0];
+		}
+	}
+}
+
+
+/******************************************************************************\
+* CFACReceive																   *
+\******************************************************************************/
+_BOOLEAN CFACReceive::FACParam(CVector<_BINARY>* pbiFACData,
+							   CParameter& Parameter)
+{
+/*
+	First get new data from incoming data stream, then check if the new
+	parameter differs from the old data stored in the receiver. If yes, init
+	the modules to the new parameter
+*/
+	uint32_t	iTempServiceID;
+	int			iTempShortID;
+
+	/* CRC ------------------------------------------------------------------ */
+	/* Check the CRC of this data block */
+	CRCObject.Reset(8);
+
+	(*pbiFACData).ResetBitAccess();
+
+	for (int i = 0; i < NUM_FAC_BITS_PER_BLOCK / SIZEOF__BYTE - 1; i++)
+		CRCObject.AddByte((_BYTE) (*pbiFACData).Separate(SIZEOF__BYTE));
+
+	if (CRCObject.CheckCRC((*pbiFACData).Separate(8)) == true)
+	{
+		/* CRC-check successful, extract data from FAC-stream */
+		/* Reset separation function */
+		(*pbiFACData).ResetBitAccess();
+
+		Parameter.Lock();
+
+		/* Channel parameters ----------------------------------------------- */
+		/* Base/Enhancement flag (not used) */
+		(*pbiFACData).Separate(1);
+
+		/* Identity */
+		switch ((*pbiFACData).Separate(2))
+		{
+		case 0: /* 00 */
+			Parameter.iFrameIDReceiv = 0;
+			break;
+
+		case 1: /* 01 */
+			Parameter.iFrameIDReceiv = 1;
+			break;
+
+		case 2: /* 10 */
+			Parameter.iFrameIDReceiv = 2;
+			break;
+
+		case 3: /* 11 */
+			Parameter.iFrameIDReceiv = 0;
+			break;
+		}
+
+		/* Spectrum occupancy */
+		switch ((*pbiFACData).Separate(4))
+		{
+		case 0: /* 0000 */
+			Parameter.SetSpectrumOccup(SO_0);
+			break;
+
+		case 1: /* 0001 */
+			Parameter.SetSpectrumOccup(SO_1);
+			break;
+
+		case 2: /* 0010 */
+			Parameter.SetSpectrumOccup(SO_2);
+			break;
+
+		case 3: /* 0011 */
+			Parameter.SetSpectrumOccup(SO_3);
+			break;
+
+		case 4: /* 0100 */
+			Parameter.SetSpectrumOccup(SO_4);
+			break;
+
+		case 5: /* 0101 */
+			Parameter.SetSpectrumOccup(SO_5);
+			break;
+		}
+
+		/* Interleaver depth flag */
+		switch ((*pbiFACData).Separate(1))
+		{
+		case 0: /* 0 */
+			Parameter.SetInterleaverDepth(CParameter::SI_LONG);
+			break;
+
+		case 1: /* 1 */
+			Parameter.SetInterleaverDepth(CParameter::SI_SHORT);
+			break;
+		}
+
+		/* MSC mode */
+		switch ((*pbiFACData).Separate(2))
+		{
+		case 0: /* 00 */
+			Parameter.SetMSCCodingScheme(CS_3_SM);
+			break;
+
+		case 1: /* 01 */
+			Parameter.SetMSCCodingScheme(CS_3_HMMIX);
+			break;
+
+		case 2: /* 10 */
+			Parameter.SetMSCCodingScheme(CS_3_HMSYM);
+			break;
+
+		case 3: /* 11 */
+			Parameter.SetMSCCodingScheme(CS_2_SM);
+			break;
+		}
+
+		/* SDC mode */
+		switch ((*pbiFACData).Separate(1))
+		{
+		case 0: /* 0 */
+			Parameter.SetSDCCodingScheme(CS_2_SM);
+			break;
+
+		case 1: /* 1 */
+			Parameter.SetSDCCodingScheme(CS_1_SM);
+			break;
+		}
+
+		/* Number of services */
+		/* Search table for entry */
+		int iNumServTabEntry = (*pbiFACData).Separate(4);
+		for (int i = 0; i < 5; i++)
+			for (int j = 0; j < 5; j++)
+				if (iNumServTabEntry == iTableNumOfServices[i][j])
+					Parameter.SetNumOfServices(i, j);
+
+		/* Reconfiguration index (not used, yet) */
+		(*pbiFACData).Separate(3);
+
+		/* rfu */
+		/* Do not use rfu */
+		(*pbiFACData).Separate(2);
+
+
+		/* Service parameters ----------------------------------------------- */
+		/* Service identifier */
+		iTempServiceID = (*pbiFACData).Separate(24);
+
+		/* Short ID (the short ID is the index of the service-array) */
+		iTempShortID = (*pbiFACData).Separate(2);
+
+		/* Set service identifier */
+		Parameter.SetServiceID(iTempShortID, iTempServiceID);
+
+		/* CA indication */
+		switch ((*pbiFACData).Separate(1))
+		{
+		case 0: /* 0 */
+			Parameter.Service[iTempShortID].eCAIndication =
+				CService::CA_NOT_USED;
+			break;
+
+		case 1: /* 1 */
+			Parameter.Service[iTempShortID].eCAIndication =
+				CService::CA_USED;
+			break;
+		}
+
+		/* Language */
+		Parameter.Service[iTempShortID].iLanguage = (*pbiFACData).Separate(4);
+
+		/* Audio/Data flag */
+		switch ((*pbiFACData).Separate(1))
+		{
+		case 0: /* 0 */
+			Parameter.SetAudDataFlag(iTempShortID, CService::SF_AUDIO);
+			break;
+
+		case 1: /* 1 */
+			Parameter.SetAudDataFlag(iTempShortID, CService::SF_DATA);
+			break;
+		}
+
+		/* Service descriptor */
+		Parameter.Service[iTempShortID].iServiceDescr =
+			(*pbiFACData).Separate(5);
+
+		Parameter.Unlock();
+
+		/* Rfa */
+		/* Do not use Rfa */
+		(*pbiFACData).Separate(7);
+
+		/* CRC is ok, return true */
+		return true;
+	}
+	else
+	{
+		/* Data is corrupted, do not use it. Return failure as false */
+		return false;
+	}
+}
+
diff --git a/qsstv/drmtx/common/FAC/FAC.h b/qsstv/drmtx/common/FAC/FAC.h
new file mode 100644
index 0000000..57ee5a7
--- /dev/null
+++ b/qsstv/drmtx/common/FAC/FAC.h
@@ -0,0 +1,71 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See FAC.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(FAC_H__3B0BA660_CA63VEUASDVN89LKVNE877A0D31912__INCLUDED_)
+#define FAC_H__3B0BA660_CA63VEUASDVN89LKVNE877A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../tables/TableFAC.h"
+#include "../Parameter.h"
+#include "../util/CRC.h"
+#include "utils/vector.h"
+
+
+/* Classes ********************************************************************/
+class CFACTransmit
+{
+public:
+	CFACTransmit():FACRepetitionCounter(0) {}
+	virtual ~CFACTransmit() {}
+
+	/* "pbiFACData" contains 72 bits */
+	void FACParam(CVector<_BINARY>* pbiFACData, CParameter& Parameter);
+	void Init(CParameter& Parameter);
+
+protected:
+	CCRC CRCObject;
+	vector<int>	FACRepetition; /* See 6.3.6 */
+	size_t		FACNumRep;
+	size_t		FACRepetitionCounter;
+};
+
+class CFACReceive
+{
+public:
+	CFACReceive() {}
+	virtual ~CFACReceive() {}
+
+	/* "pbiFACData" contains 72 bits */
+	_BOOLEAN FACParam(CVector<_BINARY>* pbiFACData, CParameter& Parameter);
+
+protected:
+	CCRC CRCObject;
+};
+
+
+#endif // !defined(FAC_H__3B0BA660_CA63VEUASDVN89LKVNE877A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/GlobalDefinitions.h b/qsstv/drmtx/common/GlobalDefinitions.h
new file mode 100644
index 0000000..53d084a
--- /dev/null
+++ b/qsstv/drmtx/common/GlobalDefinitions.h
@@ -0,0 +1,290 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001-2006
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBo
+ *
+ * Description:
+ *	Global definitions
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DEF_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define DEF_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+#include "qsstvglobal.h"
+#include "qsstvdefs.h"
+
+#include <string>
+#include <stdio.h>
+#include <math.h>
+#include "../config.h"
+
+
+#include "tables/TableDRMGlobal.h"
+
+
+/* Definitions ****************************************************************/
+/* When you define the following flag, a directory called
+   "test" MUST EXIST in the windows directory (or linux directory if you use
+   Linux)! */
+#define _DEBUG_
+#undef _DEBUG_
+
+
+/* Choose algorithms -------------------------------------------------------- */
+/* There are two algorithms available for frequency offset estimation for
+   tracking mode: Using frequency pilots or the guard-interval correlation. In
+   case of guard-interval correlation (which will be chosen if this macro is
+   defined), the Hilbert filter in TimeSync must be used all the time -> more
+   CPU usage. Also, the frequency tracking range is smaller */
+#undef USE_FRQOFFS_TRACK_GUARDCORR
+
+/* The sample rate offset estimation can be done using the frequency pilots or
+   the movement of the estimated impulse response. Defining this macro will
+   enable the frequency pilot based estimation. Simulations showed that this
+   method is more vulnerable to bad channel situations */
+#undef USE_SAMOFFS_TRACK_FRE_PIL
+
+/* Using max-log MAP decoder. A lot more memory and CPU is needed for this
+   method. This is just for showing the potential of an improved decoding
+   method and should not be activated for the "regular" version of Dream */
+#undef USE_MAX_LOG_MAP
+
+/* This method tries to speed up the audio output after a re-synchronization
+   when long symbol interleaver is used. We work with erasure symbols to mark
+   data which is not yet received. We hope that the channel decoder can still
+   decode audio even if not all data is yet received to fill the interleaver
+   history */
+#define USE_ERASURE_FOR_FASTER_ACQ
+
+/* If the following macro is defined, the Wiener filter for channel estimation
+   in time direction will be a Decision-Directed channel estimation ->
+   additional to the actual pilot cells, hard decisions about the data cells
+   are used as new pilots, too */
+#undef USE_DD_WIENER_FILT_TIME
+
+
+
+
+#if HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif defined(_WIN32)
+# ifndef HAVE_INT8_T
+#  define HAVE_INT8_T 1
+   typedef signed char int8_t;
+# endif
+# ifndef HAVE_INT16_T
+#  define HAVE_INT16_T 1
+   typedef signed __int16 int16_t;
+# endif
+# ifndef HAVE_INT32_T
+#  define HAVE_INT32_T 1
+   typedef signed __int32 int32_t;
+# endif
+   typedef unsigned char uint8_t;
+# ifndef HAVE_U_INT16_T
+#  define HAVE_U_INT16_T 1
+   typedef unsigned __int16 uint16_t;
+# endif
+# ifndef HAVE_U_INT32_T
+#  define HAVE_U_INT32_T 1
+   typedef unsigned __int32 uint32_t;
+# endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed int int16_t;
+typedef unsigned int uint16_t;
+typedef signed long int32_t;
+typedef unsigned long uint32_t;
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#endif
+
+/* Define type-specific information */
+#define SIZEOF__BYTE					8
+#define _MAXSHORT						32767
+#define _MAXREAL						((_REAL) 3.4e38) /* Max for float */
+
+#ifdef USE_ERASURE_FOR_FASTER_ACQ
+/* Use max-value for showing that this is an erasure */
+# define ERASURE_TAG_VALUE				_MAXREAL
+#endif
+
+
+/* MAP ---------------------------------------------------------------------- */
+#ifdef USE_MAX_LOG_MAP
+typedef _REAL							_DECISION;
+# define ML_SOFT_INF_MAX_VALUE			((_DECISION) 1e10)
+inline _BINARY ExtractBit(_DECISION dD) {return dD > 0 ? 1 : 0;}
+inline _DECISION BitToSoft(_BINARY biB) {return biB == 0 ? -1.0 : 1.0;}
+#else
+typedef _BINARY							_DECISION;
+#define ExtractBit(a)					(a)
+#define BitToSoft(a)					(a)
+#endif
+
+
+/* Definitions for window message system ------------------------------------ */
+typedef unsigned int					_MESSAGE_IDENT;
+#define MS_FAC_CRC						1	/* MS: Message */
+#define MS_SDC_CRC						2
+#define MS_MSC_CRC						3
+#define MS_FRAME_SYNC					4
+#define MS_TIME_SYNC					5
+#define MS_IOINTERFACE					6
+#define MS_RESET_ALL					7
+#define MS_MOT_OBJ_STAT					8
+
+#define GUI_CONTROL_UPDATE_TIME			500	/* Milliseconds */
+#define GUI_CONTROL_UPDATE_TIME_FAST	250	/* Milliseconds */
+
+
+/* Global enumerations ------------------------------------------------------ */
+enum ESpecOcc {SO_0, SO_1, SO_2, SO_3, SO_4, SO_5}; /* SO: Spectrum Occupancy */
+enum ERobMode {RM_ROBUSTNESS_MODE_A, RM_ROBUSTNESS_MODE_B,
+		RM_ROBUSTNESS_MODE_E, RM_ROBUSTNESS_MODE_D,    // pa0mbo was MODE_D
+		RM_NO_MODE_DETECTED}; /* RM: Robustness Mode */
+
+
+/* Constants ---------------------------------------------------------------- */
+const _REAL crPi = ((_REAL) 3.14159265358979323846);
+
+
+#define S9_DBUV 34.0 /* S9 in dBuV for converting HamLib S-meter readings to RSCI format */
+
+/* Define a number for the case: log10(0), which would lead to #inf */
+#define RET_VAL_LOG_0					((_REAL) -200.0)
+
+
+///* Standard definitions */
+//#define	true							1
+//#define false							0
+
+
+/* Classes ********************************************************************/
+/* For metric */
+class CDistance
+{
+public:
+	/* Distance towards 0 or towards 1 */
+	_REAL rTow0;
+	_REAL rTow1;
+};
+
+/* Viterbi needs information of equalized received signal and channel */
+class CEquSig
+{
+public:
+	CEquSig() : cSig(_COMPLEX((_REAL) 0.0, (_REAL) 0.0)), rChan((_REAL) 0.0) {}
+	CEquSig(const _COMPLEX cNS, const _REAL rNC) : cSig(cNS), rChan(rNC) {}
+
+	_COMPLEX	cSig; /* Actual signal */
+	_REAL		rChan; /* Channel power at this cell */
+};
+
+/* Mutex object to access data safely from different threads */
+/* QT mutex */
+#ifdef USE_QT_GUI
+# if #if (QT_VERSION >= QT_VERSION_CHECK(3, 0, 0))
+#  include <qthread.h>
+# else
+#  include <qmutex.h>
+# endif
+class CMutex
+{
+public:
+	void Lock() {Mutex.lock();}
+	void Unlock() {Mutex.unlock();}
+
+protected:
+	QMutex Mutex;
+};
+#else
+/* No GUI and no threads, we do not need mutex in this case */
+class CMutex
+{
+public:
+	void Lock() {}
+	void Unlock() {}
+};
+#endif
+
+class CGenErr
+{
+public:
+	CGenErr(string strNE) : strError(strNE) {}
+	string strError;
+};
+
+// FIXME something nicer than using "MAX_NUM_TAPS_DRM_CHAN"
+/* For simulation, data from channel simulation */
+#define MAX_NUM_TAPS_DRM_CHAN			4
+template<class T> class CChanSimData
+{
+public:
+	T					tIn; /* Channel input data */
+	T					tOut; /* Output of the channel (with noise) */
+	T					tRef; /* Channel reference signal (without noise) */
+	_COMPLEX			veccTap[MAX_NUM_TAPS_DRM_CHAN]; /* Tap gains */
+	_COMPLEX			veccTapBackw[MAX_NUM_TAPS_DRM_CHAN];
+};
+typedef CChanSimData<_REAL>		CChanSimDataMod; /* OFDM modulated signals */
+typedef CChanSimData<_COMPLEX>	CChanSimDataDemod; /* Demodulated signals */
+
+/* Path for simulation output and status files */
+#define SIM_OUT_FILES_PATH				"test/"
+
+
+/* Prototypes for global functions ********************************************/
+/* Posting a window message */
+//void PostWinMessage(const _MESSAGE_IDENT MessID, const int iMessageParam = 0);
+
+/* Debug error handling */
+void DebugError(const char* pchErDescr, const char* pchPar1Descr,
+				const double dPar1, const char* pchPar2Descr,
+				const double dPar2);
+
+void ErrorMessage(string strErrorString);
+
+
+/* Global functions ***********************************************************/
+/* Converting _REAL to _SAMPLE */
+inline _SAMPLE Real2Sample(const _REAL rInput)
+{
+	/* Lower bound */
+	if (rInput < -_MAXSHORT)
+		return -_MAXSHORT;
+
+	/* Upper bound */
+	if (rInput > _MAXSHORT)
+		return _MAXSHORT;
+
+	return (_SAMPLE) rInput;
+}
+
+
+#endif // !defined(DEF_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/OFDM.cpp b/qsstv/drmtx/common/OFDM.cpp
new file mode 100644
index 0000000..4d8139a
--- /dev/null
+++ b/qsstv/drmtx/common/OFDM.cpp
@@ -0,0 +1,133 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use  Ties Bos - PA0MBO
+ *
+ * Description:
+ *	OFDM modulation;
+ *	OFDM demodulation, SNR estimation, PSD estimation
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "OFDM.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* OFDM-modulation                                                              *
+\******************************************************************************/
+void COFDMModulation::ProcessDataInternal(CParameter&)
+{
+	int	i;
+         // printf("In COFDMModul iShiftKmin %d iEndIndex %d , iDFTSize %d iGuardSize %d\n",
+//			iShiftedKmin, iEndIndex, iDFTSize, iGuardSize); 
+	/* Copy input vector in matlib vector and place bins at the correct
+	   position */
+	for (i = iShiftedKmin; i < iEndIndex; i++) 
+	{
+//		veccFFTInput[150] = _COMPLEX((_REAL) 1.0, (_REAL) -1.0);   // test pa0mbo
+		veccFFTInput[i] = (*pvecInputData)[i - iShiftedKmin]; 
+//		veccFFTInput[iDFTSize -1  - i] = Conj((*pvecInputData)[i - iShiftedKmin]); 
+		// printf(" veccFFTInput %d  %g  %g\n", i,(veccFFTInput[i]).real(), (veccFFTInput[i]).imag());
+	}
+
+	/* Calculate inverse fast Fourier transformation */
+	veccFFTOutput = Ifft(veccFFTInput, FftPlan);
+
+	/* Copy complex FFT output in output buffer and scale */
+	for (i = 0; i < iDFTSize; i++)
+		(*pvecOutputData)[i + iGuardSize] = veccFFTOutput[i] * (CReal) iDFTSize;
+
+	/* Copy data from the end to the guard-interval (Add guard-interval) */
+	for (i = 0; i < iGuardSize; i++)
+		(*pvecOutputData)[i] = (*pvecOutputData)[iDFTSize + i];
+
+	/* tbv test printout pa0mbo  
+        printf("===========\n");
+	 for (i=0; i < iDFTSize + iGuardSize ; i++)
+		 printf("%d  %g \n",i,  ((*pvecOutputData)[i]).real()); 
+	 printf("=============\n");  */ 
+
+	/* Shift spectrum to desired IF ----------------------------------------- */
+	/* Only apply shifting if phase is not zero */
+	if (cExpStep != _COMPLEX((_REAL) 1.0, (_REAL) 0.0))
+	{
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			(*pvecOutputData)[i] = (*pvecOutputData)[i] * Conj(cCurExp);   // pa0mbo shift off
+			
+			/* Rotate exp-pointer on step further by complex multiplication
+			   with precalculated rotation vector cExpStep. This saves us from
+			   calling sin() and cos() functions all the time (iterative
+			   calculation of these functions) */
+			cCurExp *= cExpStep;
+		}
+	}
+}
+
+void COFDMModulation::InitInternal(CParameter& TransmParam)
+{
+	TransmParam.Lock(); 
+	/* Get global parameters */
+	iDFTSize = TransmParam.CellMappingTable.iFFTSizeN;
+	iGuardSize = TransmParam.CellMappingTable.iGuardSize;
+	iShiftedKmin = TransmParam.CellMappingTable.iShiftedKmin;
+
+	/* Last index */
+	iEndIndex = TransmParam.CellMappingTable.iShiftedKmax + 1;
+
+	/* Normalized offset correction factor for IF shift. Subtract the
+	   default IF frequency ("VIRTUAL_INTERMED_FREQ") */
+	const _REAL rNormCurFreqOffset = (_REAL) -2.0 * crPi *
+//		((_REAL ) -6751.0)/ SOUNDCRD_SAMPLE_RATE;  
+		(rDefCarOffset - VIRTUAL_INTERMED_FREQ) / SOUNDCRD_SAMPLE_RATE;  
+         // printf("COFDMMod rDefCarOffset %g Virt IF %g rNormCurFreqoffset %g \n", rDefCarOffset, (_REAL) VIRTUAL_INTERMED_FREQ,  rNormCurFreqOffset);
+
+	/* Rotation vector for exp() calculation */
+	cExpStep = _COMPLEX(cos(rNormCurFreqOffset), sin(rNormCurFreqOffset));
+
+         //  printf("COFDMMod cExpStep real %g imag  %g \n", cos(rNormCurFreqOffset), sin(rNormCurFreqOffset));
+	/* Start with phase null (can be arbitrarily chosen) */
+	cCurExp = (_REAL) 1.0;
+
+	/* Init plans for FFT (faster processing of Fft and Ifft commands) */
+	FftPlan.Init(iDFTSize);
+   //     printf("In FftPlan.Init  iDFTSize is %d \n", iDFTSize);
+
+	/* Allocate memory for intermediate result of fft. Zero out input vector
+	   (because only a few bins are used, the rest has to be zero) */
+	veccFFTInput.Init(iDFTSize, (CReal) 0.0);
+	veccFFTOutput.Init(iDFTSize);
+
+	/* Define block-sizes for input and output */
+	iInputBlockSize = TransmParam.CellMappingTable.iNumCarrier;
+	iOutputBlockSize = TransmParam.CellMappingTable.iSymbolBlockSize;
+
+	// printf("In COFDMMOd init DFTsize %d Guardsize %d ShiftedKmin %d \n", iDFTSize, iGuardSize, iShiftedKmin);
+	 // printf("In COFDMMOd init Inputblcksize %d Outputblocksize %d \n", iInputBlockSize, iOutputBlockSize);
+
+
+	TransmParam.Unlock(); 
+}
+
+
diff --git a/qsstv/drmtx/common/OFDM.h b/qsstv/drmtx/common/OFDM.h
new file mode 100644
index 0000000..d29a0dd
--- /dev/null
+++ b/qsstv/drmtx/common/OFDM.h
@@ -0,0 +1,74 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See OFDM.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(OFDM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define OFDM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "Parameter.h"
+#include "util/Modul.h"
+#include "matlib/Matlib.h"
+
+
+/* Definitions ****************************************************************/
+/* Time constant for IIR averaging of PSD estimation */
+#define TICONST_PSD_EST_OFDM			((CReal) 1.0) /* sec */
+
+
+/* Classes ********************************************************************/
+class COFDMModulation : public CTransmitterModul<_COMPLEX, _COMPLEX>
+{
+public:
+	COFDMModulation() : rDefCarOffset((_REAL) VIRTUAL_INTERMED_FREQ) {}
+	virtual ~COFDMModulation() {}
+
+	void SetCarOffset(const _REAL rNewCarOffset)
+		{rDefCarOffset = rNewCarOffset;}
+
+protected:
+	CFftPlans				FftPlan;
+
+	CComplexVector			veccFFTInput;
+	CComplexVector			veccFFTOutput;
+
+	int						iShiftedKmin;
+	int						iEndIndex;
+	int						iDFTSize;
+	int						iGuardSize;
+
+	_COMPLEX				cCurExp;
+	_COMPLEX				cExpStep;
+	_REAL					rDefCarOffset;
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+
+#endif // !defined(OFDM_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/Parameter.cpp b/qsstv/drmtx/common/Parameter.cpp
new file mode 100644
index 0000000..0df8903
--- /dev/null
+++ b/qsstv/drmtx/common/Parameter.cpp
@@ -0,0 +1,1362 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2007
+ *
+ * Author(s):
+ *	Volker Fischer, Andrew Murphy
+ *
+ * Adapter for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	DRM Parameters
+ *
+ ******************************************************************************
+ *
+ * This program is free software(), you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation(), either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY(), without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program(), if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "Parameter.h"
+//#include "DrmReceiver.h"
+#include <limits>
+#include <sstream>
+#include <iomanip>
+//#include "util/LogPrint.h"
+
+/* Implementation *************************************************************/
+CParameter::CParameter():
+// pDRMRec(pRx),
+
+
+
+ Stream(MAX_NUM_STREAMS), Service(MAX_NUM_SERVICES),
+
+ iNumAudioFrames(0),
+ vecbiAudioFrameStatus(0),
+ bMeasurePSD(),
+ vecrPSD(0),
+ matcReceivedPilotValues(),
+ RawSimDa(),
+ eSimType(ST_NONE),
+ iDRMChannelNum(0),
+ iSpecChDoppler(0),
+ rBitErrRate(0.0),
+ rSyncTestParam(0.0),
+ rSINR(0.0),
+ iNumBitErrors(0),
+ iChanEstDelay(0),
+ iNumTaps(0),
+ iPathDelay(MAX_NUM_TAPS_DRM_CHAN),
+ rGainCorr(0.0),
+ iOffUsfExtr(0),
+ ReceiveStatus(),
+ FrontEndParameters(),
+ AltFreqSign(),
+ rMER(0.0),
+ rWMERMSC(0.0),
+ rWMERFAC(0.0),
+ rSigmaEstimate(0.0),
+ rMinDelay(0.0),
+ rMaxDelay(0.0),
+ bMeasureDelay(),
+ vecrRdel(0),
+ vecrRdelThresholds(0),
+ vecrRdelIntervals(0),
+ bMeasureDoppler(0),
+ rRdop(0.0),
+ bMeasureInterference(false),
+ rIntFreq(0.0),
+ rINR(0.0),
+ rICR(0.0),
+ rMaxPSDwrtSig(0.0),
+ rMaxPSDFreq(0.0),
+ rSigStrengthCorrection(0.0),
+ bRunThread(false),
+ bUsingMultimedia(false),
+ CellMappingTable(),
+ rSysSimSNRdB(0.0),
+ iFrequency(0),
+ bValidSignalStrength(false),
+ rSigStr(0.0),
+ rIFSigStr(0.0),
+ iCurSelAudioService(0),
+ iCurSelDataService(0),
+ eRobustnessMode(RM_ROBUSTNESS_MODE_B),
+ eSpectOccup(SO_3),
+ LastAudioService(),
+ LastDataService(),
+ Mutex()
+{
+  init();
+	GenerateRandomSerialNumber();
+	CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup);
+}
+
+void CParameter::init()
+{
+  eSymbolInterlMode=SI_LONG;
+  eMSCCodingScheme=CS_1_SM;
+  eSDCCodingScheme=CS_1_SM;
+  iNumAudioService=0;
+  iNumDataService=0;
+  iAMSSCarrierMode=0;
+  sReceiverID="                ";
+  sSerialNumber="";
+  sDataFilesDirectory=".";
+  MSCPrLe.init();
+  iNumBitsHierarchFrameTotal=0;
+  iNumDecodedBitsMSC=0;
+  iNumSDCBitsPerSFrame=0;
+  iNumAudioDecoderBits=0;
+  iNumDataDecoderBits=0;
+  iYear=0;
+  iMonth=0;
+  iDay=0;
+  iUTCHour=0;
+  iUTCMin=0;
+  iFrameIDTransm=0;
+  iFrameIDReceiv=0;
+  rFreqOffsetAcqui=0.0;
+  rFreqOffsetTrack=0.0;
+  rResampleOffset=0.0;
+  iTimingOffsTrack=0;
+  eReceiverMode=RM_NONE;
+  eAcquiState=AS_NO_SIGNAL;
+}
+
+
+CParameter::~CParameter()
+{
+}
+
+CParameter::CParameter(const CParameter& p):
+// pDRMRec(p.pDRMRec),
+ eSymbolInterlMode(p.eSymbolInterlMode),
+ eMSCCodingScheme(p.eMSCCodingScheme),
+ eSDCCodingScheme(p.eSDCCodingScheme),
+ iNumAudioService(p.iNumAudioService),
+ iNumDataService(p.iNumDataService),
+ iAMSSCarrierMode(p.iAMSSCarrierMode),
+ sReceiverID(p.sReceiverID),
+ sSerialNumber(p.sSerialNumber),
+ sDataFilesDirectory(p.sDataFilesDirectory),
+ MSCPrLe(p.MSCPrLe),
+ Stream(p.Stream), Service(p.Service),
+ iNumBitsHierarchFrameTotal(p.iNumBitsHierarchFrameTotal),
+ iNumDecodedBitsMSC(p.iNumDecodedBitsMSC),
+ iNumSDCBitsPerSFrame(p.iNumSDCBitsPerSFrame),
+ iNumAudioDecoderBits(p.iNumAudioDecoderBits),
+ iNumDataDecoderBits(p.iNumDataDecoderBits),
+ iYear(p.iYear),
+ iMonth(p.iMonth),
+ iDay(p.iDay),
+ iUTCHour(p.iUTCHour),
+ iUTCMin(p.iUTCMin),
+ iFrameIDTransm(p.iFrameIDTransm),
+ iFrameIDReceiv(p.iFrameIDReceiv),
+ rFreqOffsetAcqui(p.rFreqOffsetAcqui),
+ rFreqOffsetTrack(p.rFreqOffsetTrack),
+ rResampleOffset(p.rResampleOffset),
+ iTimingOffsTrack(p.iTimingOffsTrack),
+ eReceiverMode(p.eReceiverMode),
+ eAcquiState(p.eAcquiState),
+ iNumAudioFrames(p.iNumAudioFrames),
+ vecbiAudioFrameStatus(p.vecbiAudioFrameStatus),
+ bMeasurePSD(p.bMeasurePSD),
+ vecrPSD(p.vecrPSD),
+ //matcReceivedPilotValues(p.matcReceivedPilotValues),
+ matcReceivedPilotValues(), // OPH says copy constructor for CMatrix not safe yet
+ RawSimDa(p.RawSimDa),
+ eSimType(p.eSimType),
+ iDRMChannelNum(p.iDRMChannelNum),
+ iSpecChDoppler(p.iSpecChDoppler),
+ rBitErrRate(p.rBitErrRate),
+ rSyncTestParam	(p.rSyncTestParam),
+ rSINR(p.rSINR),
+ iNumBitErrors(p.iNumBitErrors),
+ iChanEstDelay(p.iChanEstDelay),
+ iNumTaps(p.iNumTaps),
+ iPathDelay(p.iPathDelay),
+ rGainCorr(p.rGainCorr),
+ iOffUsfExtr(p.iOffUsfExtr),
+ ReceiveStatus(p.ReceiveStatus),
+ FrontEndParameters(p.FrontEndParameters),
+ AltFreqSign(p.AltFreqSign),
+ rMER(p.rMER),
+ rWMERMSC(p.rWMERMSC),
+ rWMERFAC(p.rWMERFAC),
+ rSigmaEstimate(p.rSigmaEstimate),
+ rMinDelay(p.rMinDelay),
+ rMaxDelay(p.rMaxDelay),
+ bMeasureDelay(p.bMeasureDelay),
+ vecrRdel(p.vecrRdel),
+ vecrRdelThresholds(p.vecrRdelThresholds),
+ vecrRdelIntervals(p.vecrRdelIntervals),
+ bMeasureDoppler(p.bMeasureDoppler),
+ rRdop(p.rRdop),
+ bMeasureInterference(p.bMeasureInterference),
+ rIntFreq(p.rIntFreq),
+ rINR(p.rINR),
+ rICR(p.rICR),
+ rMaxPSDwrtSig(p.rMaxPSDwrtSig),
+ rMaxPSDFreq(p.rMaxPSDFreq),
+ rSigStrengthCorrection(p.rSigStrengthCorrection),
+ bRunThread(p.bRunThread),
+ bUsingMultimedia(p.bUsingMultimedia),
+ CellMappingTable(), // jfbc CCellMappingTable uses a CMatrix :(
+// GPSData(p.GPSData),
+ rSysSimSNRdB(p.rSysSimSNRdB),
+ iFrequency(p.iFrequency),
+ bValidSignalStrength(p.bValidSignalStrength),
+ rSigStr(p.rSigStr),
+ rIFSigStr(p.rIFSigStr),
+ iCurSelAudioService(p.iCurSelAudioService),
+ iCurSelDataService(p.iCurSelDataService),
+ eRobustnessMode(p.eRobustnessMode),
+ eSpectOccup(p.eSpectOccup),
+ LastAudioService(p.LastAudioService),
+ LastDataService(p.LastDataService)
+ //, Mutex() // jfbc: I don't think this state should be copied
+{
+	CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup);
+	matcReceivedPilotValues = p.matcReceivedPilotValues; // TODO
+}
+
+CParameter& CParameter::operator=(const CParameter& p)
+{
+//	pDRMRec = p.pDRMRec;
+	eSymbolInterlMode = p.eSymbolInterlMode;
+	eMSCCodingScheme = p.eMSCCodingScheme;
+	eSDCCodingScheme = p.eSDCCodingScheme;
+	iNumAudioService = p.iNumAudioService;
+	iNumDataService = p.iNumDataService;
+	iAMSSCarrierMode = p.iAMSSCarrierMode;
+	sReceiverID = p.sReceiverID;
+	sSerialNumber = p.sSerialNumber;
+	sDataFilesDirectory = p.sDataFilesDirectory;
+	MSCPrLe = p.MSCPrLe;
+	Stream = p.Stream;
+	Service = p.Service;
+	iNumBitsHierarchFrameTotal = p.iNumBitsHierarchFrameTotal;
+	iNumDecodedBitsMSC = p.iNumDecodedBitsMSC;
+	iNumSDCBitsPerSFrame = p.iNumSDCBitsPerSFrame;
+	iNumAudioDecoderBits = p.iNumAudioDecoderBits;
+	iNumDataDecoderBits = p.iNumDataDecoderBits;
+	iYear = p.iYear;
+	iMonth = p.iMonth;
+	iDay = p.iDay;
+	iUTCHour = p.iUTCHour;
+	iUTCMin = p.iUTCMin;
+	iFrameIDTransm = p.iFrameIDTransm;
+	iFrameIDReceiv = p.iFrameIDReceiv;
+	rFreqOffsetAcqui = p.rFreqOffsetAcqui;
+	rFreqOffsetTrack = p.rFreqOffsetTrack;
+	rResampleOffset = p.rResampleOffset;
+	iTimingOffsTrack = p.iTimingOffsTrack;
+	eReceiverMode = p.eReceiverMode;
+	eAcquiState = p.eAcquiState;
+	iNumAudioFrames = p.iNumAudioFrames;
+	vecbiAudioFrameStatus = p.vecbiAudioFrameStatus;
+	bMeasurePSD = p.bMeasurePSD;
+	vecrPSD = p.vecrPSD;
+	matcReceivedPilotValues = p.matcReceivedPilotValues;
+	RawSimDa = p.RawSimDa;
+	eSimType = p.eSimType;
+	iDRMChannelNum = p.iDRMChannelNum;
+	iSpecChDoppler = p.iSpecChDoppler;
+	rBitErrRate = p.rBitErrRate;
+	rSyncTestParam	 = p.rSyncTestParam;
+	rSINR = p.rSINR;
+	iNumBitErrors = p.iNumBitErrors;
+	iChanEstDelay = p.iChanEstDelay;
+	iNumTaps = p.iNumTaps;
+	iPathDelay = p.iPathDelay;
+	rGainCorr = p.rGainCorr;
+	iOffUsfExtr = p.iOffUsfExtr;
+	ReceiveStatus = p.ReceiveStatus;
+	FrontEndParameters = p.FrontEndParameters;
+	AltFreqSign = p.AltFreqSign;
+	rMER = p.rMER;
+	rWMERMSC = p.rWMERMSC;
+	rWMERFAC = p.rWMERFAC;
+	rSigmaEstimate = p.rSigmaEstimate;
+	rMinDelay = p.rMinDelay;
+	rMaxDelay = p.rMaxDelay;
+	bMeasureDelay = p.bMeasureDelay;
+	vecrRdel = p.vecrRdel;
+	vecrRdelThresholds = p.vecrRdelThresholds;
+	vecrRdelIntervals = p.vecrRdelIntervals;
+	bMeasureDoppler = p.bMeasureDoppler;
+	rRdop = p.rRdop;
+	bMeasureInterference = p.bMeasureInterference;
+	rIntFreq = p.rIntFreq;
+	rINR = p.rINR;
+	rICR = p.rICR;
+	rMaxPSDwrtSig = p.rMaxPSDwrtSig;
+	rMaxPSDFreq = p.rMaxPSDFreq;
+	rSigStrengthCorrection = p.rSigStrengthCorrection;
+	bRunThread = p.bRunThread;
+	bUsingMultimedia = p.bUsingMultimedia;
+	CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup); // don't copy CMatrix
+//	GPSData = p.GPSData;
+	rSysSimSNRdB = p.rSysSimSNRdB;
+	iFrequency = p.iFrequency;
+	bValidSignalStrength = p.bValidSignalStrength;
+	rSigStr = p.rSigStr;
+	rIFSigStr = p.rIFSigStr;
+	iCurSelAudioService = p.iCurSelAudioService;
+	iCurSelDataService = p.iCurSelDataService;
+	eRobustnessMode = p.eRobustnessMode;
+	eSpectOccup = p.eSpectOccup;
+	LastAudioService = p.LastAudioService;
+	LastDataService = p.LastDataService;
+	return *this;
+}
+
+void CParameter::ResetServicesStreams()
+{
+	int i;
+	if(GetReceiverMode() == RM_DRM)
+	{
+
+		/* Store informations about last services selected
+		 * this for select current service automatically after a resync */
+
+		if (iCurSelAudioService > 0)
+			LastAudioService.Save(iCurSelAudioService, Service[iCurSelAudioService].iServiceID);
+
+		if (iCurSelDataService > 0)
+			LastDataService.Save(iCurSelDataService, Service[iCurSelDataService].iServiceID);
+
+		/* Reset everything to possible start values */
+		for (i = 0; i < MAX_NUM_SERVICES; i++)
+		{
+			Service[i].AudioParam.strTextMessage = "";
+			Service[i].AudioParam.iStreamID = STREAM_ID_NOT_USED;
+			Service[i].AudioParam.eAudioCoding = CAudioParam::AC_AAC;
+			Service[i].AudioParam.eSBRFlag = CAudioParam::SB_NOT_USED;
+			Service[i].AudioParam.eAudioSamplRate = CAudioParam::AS_24KHZ;
+			Service[i].AudioParam.bTextflag = false;
+			Service[i].AudioParam.bEnhanceFlag = false;
+			Service[i].AudioParam.eAudioMode = CAudioParam::AM_MONO;
+			Service[i].AudioParam.iCELPIndex = 0;
+			Service[i].AudioParam.bCELPCRC = false;
+			Service[i].AudioParam.eHVXCRate = CAudioParam::HR_2_KBIT;
+			Service[i].AudioParam.bHVXCCRC = false;
+
+			Service[i].DataParam.iStreamID = STREAM_ID_NOT_USED;
+			Service[i].DataParam.ePacketModInd = CDataParam::PM_PACKET_MODE;
+			Service[i].DataParam.eDataUnitInd = CDataParam::DU_SINGLE_PACKETS;
+			Service[i].DataParam.iPacketID = 0;
+			Service[i].DataParam.iPacketLen = 0;
+			Service[i].DataParam.eAppDomain = CDataParam::AD_DRM_SPEC_APP;
+			Service[i].DataParam.iUserAppIdent = 0;
+
+			Service[i].iServiceID = SERV_ID_NOT_USED;
+			Service[i].eCAIndication = CService::CA_NOT_USED;
+			Service[i].iLanguage = 0;
+			Service[i].strCountryCode = "";
+			Service[i].strLanguageCode = "";
+			Service[i].eAudDataFlag = CService::SF_AUDIO;
+			Service[i].iServiceDescr = 0;
+			Service[i].strLabel = "";
+		}
+
+		for (i = 0; i < MAX_NUM_STREAMS; i++)
+		{
+			Stream[i].iLenPartA = 0;
+			Stream[i].iLenPartB = 0;
+		}
+	}
+	else
+	{
+
+		// Set up encoded AM audio parameters
+		Service[0].AudioParam.strTextMessage = "";
+		Service[0].AudioParam.iStreamID = 0;
+		Service[0].AudioParam.eAudioCoding = CAudioParam::AC_AAC;
+		Service[0].AudioParam.eSBRFlag = CAudioParam::SB_NOT_USED;
+		Service[0].AudioParam.eAudioSamplRate = CAudioParam::AS_24KHZ;
+		Service[0].AudioParam.bTextflag = false;
+		Service[0].AudioParam.bEnhanceFlag = false;
+		Service[0].AudioParam.eAudioMode = CAudioParam::AM_MONO;
+		Service[0].AudioParam.iCELPIndex = 0;
+		Service[0].AudioParam.bCELPCRC = false;
+		Service[0].AudioParam.eHVXCRate = CAudioParam::HR_2_KBIT;
+		Service[0].AudioParam.bHVXCCRC = false;
+
+		Service[0].iServiceID = SERV_ID_NOT_USED;
+		Service[0].eCAIndication = CService::CA_NOT_USED;
+		Service[0].iLanguage = 0;
+		Service[0].strCountryCode = "";
+		Service[0].strLanguageCode = "";
+		Service[0].eAudDataFlag = CService::SF_AUDIO;
+		Service[0].iServiceDescr = 0;
+		Service[0].strLabel = "";
+
+		Stream[0].iLenPartA = 0;
+		Stream[0].iLenPartB = 1044;
+	}
+
+	/* Reset alternative frequencies */
+	AltFreqSign.Reset();
+
+	/* Date, time */
+	iDay = 0;
+	iMonth = 0;
+	iYear = 0;
+	iUTCHour = 0;
+	iUTCMin = 0;
+}
+
+void CParameter::GetActiveServices(set<int>& actServ)
+{
+	/* Init return vector */
+	actServ.clear();
+
+	/* Get active services */
+	for (int i = 0; i < MAX_NUM_SERVICES; i++)
+	{
+		if (Service[i].IsActive())
+			/* A service is active, add ID to set */
+			actServ.insert(i);
+	}
+}
+
+/* using a set ensures each stream appears only once */
+void CParameter::GetActiveStreams(set<int>& actStr)
+{
+	actStr.clear();
+
+	/* Determine which streams are active */
+	for (int i = 0; i < MAX_NUM_SERVICES; i++)
+	{
+		if (Service[i].IsActive())
+		{
+			/* Audio stream */
+			if (Service[i].AudioParam.iStreamID != STREAM_ID_NOT_USED)
+				actStr.insert(Service[i].AudioParam.iStreamID);
+
+			/* Data stream */
+			if (Service[i].DataParam.iStreamID != STREAM_ID_NOT_USED)
+				actStr.insert(Service[i].DataParam.iStreamID);
+		}
+	}
+}
+
+_REAL CParameter::GetBitRateKbps(const int iShortID, const _BOOLEAN bAudData)
+{
+	/* Init lengths to zero in case the stream is not yet assigned */
+	int iLen = 0;
+
+	/* First, check if audio or data service and get lengths */
+	if (Service[iShortID].eAudDataFlag == CService::SF_AUDIO)
+	{
+		/* Check if we want to get the data stream connected to an audio
+		   stream */
+		if (bAudData == true)
+		{
+			iLen = GetStreamLen( Service[iShortID].DataParam.iStreamID);
+		}
+		else
+		{
+			iLen = GetStreamLen( Service[iShortID].AudioParam.iStreamID);
+		}
+	}
+	else
+	{
+		iLen = GetStreamLen( Service[iShortID].DataParam.iStreamID);
+	}
+
+	/* We have 3 frames with time duration of 1.2 seconds. Bit rate should be
+	   returned in kbps (/ 1000) */
+	return (_REAL) iLen * SIZEOF__BYTE * 3 / (_REAL) 1.2 / 1000;
+}
+
+_REAL CParameter::PartABLenRatio(const int iShortID)
+{
+	int iLenA = 0;
+	int iLenB = 0;
+
+	/* Get the length of protection part A and B */
+	if (Service[iShortID].eAudDataFlag == CService::SF_AUDIO)
+	{
+		/* Audio service */
+		if (Service[iShortID].AudioParam.iStreamID != STREAM_ID_NOT_USED)
+		{
+			iLenA = Stream[Service[iShortID].AudioParam.iStreamID].iLenPartA;
+			iLenB = Stream[Service[iShortID].AudioParam.iStreamID].iLenPartB;
+		}
+	}
+	else
+	{
+		/* Data service */
+		if (Service[iShortID].DataParam.iStreamID != STREAM_ID_NOT_USED)
+		{
+			iLenA = Stream[Service[iShortID].DataParam.iStreamID].iLenPartA;
+			iLenB = Stream[Service[iShortID].DataParam.iStreamID].iLenPartB;
+		}
+	}
+
+	const int iTotLen = iLenA + iLenB;
+
+	if (iTotLen != 0)
+		return (_REAL) iLenA / iTotLen;
+	else
+		return (_REAL) 0.0;
+}
+
+void CParameter::InitCellMapTable(const ERobMode eNewWaveMode,
+								  const ESpecOcc eNewSpecOcc)
+{
+	/* Set new values and make table */
+	eRobustnessMode = eNewWaveMode;
+	eSpectOccup = eNewSpecOcc;
+	CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup);
+}
+
+_BOOLEAN CParameter::SetWaveMode(const ERobMode eNewWaveMode)
+{
+	/* First check if spectrum occupancy and robustness mode pair is defined */
+	if ((
+		(eNewWaveMode == RM_ROBUSTNESS_MODE_E) ||
+		(eNewWaveMode == RM_ROBUSTNESS_MODE_D)
+		) && !(
+		(eSpectOccup == SO_3) ||
+		(eSpectOccup == SO_5)
+		))
+	{
+		/* Set spectrum occupance to a valid parameter */
+		eSpectOccup = SO_3;
+	}
+
+	/* Apply changes only if new paramter differs from old one */
+	if (eRobustnessMode != eNewWaveMode)
+	{
+		/* Set new value */
+		eRobustnessMode = eNewWaveMode;
+
+		/* This parameter change provokes update of table */
+		CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup);
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForWaveMode();
+
+		/* Signal that parameter has changed */
+		return true;
+	}
+	else
+		return false;
+}
+
+void CParameter::SetSpectrumOccup(ESpecOcc eNewSpecOcc)
+{
+	/* First check if spectrum occupancy and robustness mode pair is defined */
+	if ((
+		(eRobustnessMode == RM_ROBUSTNESS_MODE_E) ||
+		(eRobustnessMode == RM_ROBUSTNESS_MODE_D)
+		) && !(
+		(eNewSpecOcc == SO_3) ||
+		(eNewSpecOcc == SO_5)
+		))
+	{
+		/* Set spectrum occupance to a valid parameter */
+		eNewSpecOcc = SO_3;
+	}
+
+	/* Apply changes only if new paramter differs from old one */
+	if (eSpectOccup != eNewSpecOcc)
+	{
+		/* Set new value */
+		eSpectOccup = eNewSpecOcc;
+
+		/* This parameter change provokes update of table */
+		CellMappingTable.MakeTable(eRobustnessMode, eSpectOccup);
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForSpectrumOccup();
+	}
+}
+
+void CParameter::SetStreamLen(const int iStreamID, const int iNewLenPartA,
+							  const int iNewLenPartB)
+{
+	/* Apply changes only if parameters have changed */
+	if ((Stream[iStreamID].iLenPartA != iNewLenPartA) ||
+		(Stream[iStreamID].iLenPartB != iNewLenPartB))
+	{
+		/* Use new parameters */
+		Stream[iStreamID].iLenPartA = iNewLenPartA;
+		Stream[iStreamID].iLenPartB = iNewLenPartB;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSC();
+	}
+}
+
+int CParameter::GetStreamLen(const int iStreamID)
+{
+	if(iStreamID != STREAM_ID_NOT_USED)
+		return Stream[iStreamID].iLenPartA + Stream[iStreamID].iLenPartB;
+	else
+		return 0;
+}
+
+void CParameter::SetNumDecodedBitsMSC(const int iNewNumDecodedBitsMSC)
+{
+	/* Apply changes only if parameters have changed */
+	if (iNewNumDecodedBitsMSC != iNumDecodedBitsMSC)
+	{
+		iNumDecodedBitsMSC = iNewNumDecodedBitsMSC;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSCDemux();
+	}
+}
+
+void CParameter::SetNumDecodedBitsSDC(const int iNewNumDecodedBitsSDC)
+{
+	/* Apply changes only if parameters have changed */
+	if (iNewNumDecodedBitsSDC != iNumSDCBitsPerSFrame)
+	{
+		iNumSDCBitsPerSFrame = iNewNumDecodedBitsSDC;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForNoDecBitsSDC();
+	}
+}
+
+void CParameter::SetNumBitsHieraFrTot(const int iNewNumBitsHieraFrTot)
+{
+	/* Apply changes only if parameters have changed */
+	if (iNewNumBitsHieraFrTot != iNumBitsHierarchFrameTotal)
+	{
+		iNumBitsHierarchFrameTotal = iNewNumBitsHieraFrTot;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSCDemux();
+	}
+}
+
+void CParameter::SetNumAudioDecoderBits(const int iNewNumAudioDecoderBits)
+{
+	/* Apply changes only if parameters have changed */
+	if (iNewNumAudioDecoderBits != iNumAudioDecoderBits)
+	{
+		iNumAudioDecoderBits = iNewNumAudioDecoderBits;
+	}
+}
+
+void CParameter::SetNumDataDecoderBits(const int iNewNumDataDecoderBits)
+{
+	/* Apply changes only if parameters have changed */
+	if (iNewNumDataDecoderBits != iNumDataDecoderBits)
+	{
+		iNumDataDecoderBits = iNewNumDataDecoderBits;
+	}
+}
+
+void CParameter::SetMSCProtLev(const CMSCProtLev NewMSCPrLe,
+							   const _BOOLEAN bWithHierarch)
+{
+//	_BOOLEAN bParamersHaveChanged = false;
+
+	if ((NewMSCPrLe.iPartA != MSCPrLe.iPartA) ||
+		(NewMSCPrLe.iPartB != MSCPrLe.iPartB))
+	{
+		MSCPrLe.iPartA = NewMSCPrLe.iPartA;
+		MSCPrLe.iPartB = NewMSCPrLe.iPartB;
+
+//		bParamersHaveChanged = true;
+	}
+
+	/* Apply changes only if parameters have changed */
+	if (bWithHierarch == true)
+	{
+		if (NewMSCPrLe.iHierarch != MSCPrLe.iHierarch)
+		{
+			MSCPrLe.iHierarch = NewMSCPrLe.iHierarch;
+
+//			bParamersHaveChanged = true;
+		}
+	}
+
+	/* In case parameters have changed, set init flags */
+//	if (bParamersHaveChanged == true)
+//		if(pDRMRec) pDRMRec->InitsForMSC();
+}
+
+void CParameter::SetServiceParameters(int iShortID, const CService& newService)
+{
+	Service[iShortID] = newService;
+}
+
+void CParameter::SetAudioParam(const int iShortID, const CAudioParam& NewAudParam)
+{
+	/* Apply changes only if parameters have changed */
+	if (Service[iShortID].AudioParam != NewAudParam)
+	{
+		Service[iShortID].AudioParam = NewAudParam;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForAudParam();
+	}
+}
+
+CAudioParam CParameter::GetAudioParam(const int iShortID)
+{
+	return Service[iShortID].AudioParam;
+}
+
+void CParameter::SetDataParam(const int iShortID, const CDataParam& NewDataParam)
+{
+	CDataParam& DataParam = Service[iShortID].DataParam;
+
+	/* Apply changes only if parameters have changed */
+	if (DataParam != NewDataParam)
+	{
+		DataParam = NewDataParam;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForDataParam();
+	}
+}
+
+CDataParam CParameter::GetDataParam(const int iShortID)
+{
+	return Service[iShortID].DataParam;
+}
+
+void CParameter::SetInterleaverDepth(const ESymIntMod eNewDepth)
+{
+	if (eSymbolInterlMode != eNewDepth)
+	{
+		eSymbolInterlMode = eNewDepth;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForInterlDepth();
+	}
+}
+
+void CParameter::SetMSCCodingScheme(const ECodScheme eNewScheme)
+{
+	if (eMSCCodingScheme != eNewScheme)
+	{
+		eMSCCodingScheme = eNewScheme;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSCCodSche();
+	}
+}
+
+void CParameter::SetSDCCodingScheme(const ECodScheme eNewScheme)
+{
+	if (eSDCCodingScheme != eNewScheme)
+	{
+		eSDCCodingScheme = eNewScheme;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForSDCCodSche();
+	}
+}
+
+void CParameter::SetCurSelAudioService(const int iNewService)
+{
+	/* Change the current selected audio service ID only if the new ID does
+	   contain an audio service. If not, keep the old ID. In that case it is
+	   possible to select a "data-only" service and still listen to the audio of
+	   the last selected service */
+	if ((iCurSelAudioService != iNewService) &&
+		(Service[iNewService].AudioParam.iStreamID != STREAM_ID_NOT_USED))
+	{
+		iCurSelAudioService = iNewService;
+
+		LastAudioService.Reset();
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForAudParam();
+	}
+}
+
+void CParameter::SetCurSelDataService(const int iNewService)
+{
+	/* Change the current selected data service ID only if the new ID does
+	   contain a data service. If not, keep the old ID. In that case it is
+	   possible to select a "data-only" service and click back to an audio
+	   service to be able to decode data service and listen to audio at the
+	   same time */
+	if ((iCurSelDataService != iNewService) &&
+		(Service[iNewService].DataParam.iStreamID != STREAM_ID_NOT_USED))
+	{
+		iCurSelDataService = iNewService;
+
+		LastDataService.Reset();
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForDataParam();
+	}
+}
+
+void CParameter::EnableMultimedia(const _BOOLEAN bFlag)
+{
+	if (bUsingMultimedia != bFlag)
+	{
+		bUsingMultimedia = bFlag;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSCDemux();
+	}
+}
+
+void CParameter::SetNumOfServices(const size_t iNNumAuSe, const size_t iNNumDaSe)
+{
+	/* Check whether number of activated services is not greater than the
+	   number of services signalled by the FAC because it can happen that
+	   a false CRC check (it is only a 8 bit CRC) of the FAC block
+	   initializes a wrong service */
+	set<int> actServ;
+
+	GetActiveServices(actServ);
+	if (actServ.size() > iNNumAuSe + iNNumDaSe)
+	{
+		/* Reset services and streams and set flag for init modules */
+		ResetServicesStreams();
+//		if(pDRMRec) pDRMRec->InitsForMSCDemux();
+	}
+
+	if ((iNumAudioService != iNNumAuSe) || (iNumDataService != iNNumDaSe))
+	{
+		iNumAudioService = iNNumAuSe;
+		iNumDataService = iNNumDaSe;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSCDemux();
+	}
+}
+
+void CParameter::SetAudDataFlag(const int iShortID, const CService::ETyOServ iNewADaFl)
+{
+	if (Service[iShortID].eAudDataFlag != iNewADaFl)
+	{
+		Service[iShortID].eAudDataFlag = iNewADaFl;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSC();
+	}
+}
+
+void CParameter::SetServiceID(const int iShortID, const uint32_t iNewServiceID)
+{
+	if (Service[iShortID].iServiceID != iNewServiceID)
+	{
+		/* JFBC - what is this for? */
+		if ((iShortID == 0) && (Service[0].iServiceID > 0))
+			ResetServicesStreams();
+
+		Service[iShortID].iServiceID = iNewServiceID;
+
+		/* Set init flags */
+//		if(pDRMRec) pDRMRec->InitsForMSC();
+
+
+		/* If the receiver has lost the sync automatically restore
+			last current service selected */
+
+		if ((iShortID > 0) && (iNewServiceID > 0))
+		{
+			if(LastAudioService.iServiceID == iNewServiceID)
+			{
+				/* Restore last audio service selected */
+				iCurSelAudioService = LastAudioService.iService;
+
+				LastAudioService.Reset();
+
+				/* Set init flags */
+//				if(pDRMRec) pDRMRec->InitsForAudParam();
+			}
+
+			if (LastDataService.iServiceID == iNewServiceID)
+			{
+				/* Restore last data service selected */
+				iCurSelDataService = LastDataService.iService;
+
+				LastDataService.Reset();
+
+				/* Set init flags */
+//				if(pDRMRec) pDRMRec->InitsForDataParam();
+			}
+		}
+	}
+}
+
+
+/* Implementaions for simulation -------------------------------------------- */
+void CRawSimData::Add(uint32_t iNewSRS)
+{
+	/* Attention, function does not take care of overruns, data will be
+	   lost if added to a filled shift register! */
+	if (iCurWritePos < ciMaxDelBlocks)
+		veciShRegSt[iCurWritePos++] = iNewSRS;
+}
+
+uint32_t CRawSimData::Get()
+{
+	/* We always use the first value of the array for reading and do a
+	   shift of the other data by adding a arbitrary value (0) at the
+	   end of the whole shift register */
+	uint32_t iRet = veciShRegSt[0];
+	veciShRegSt.AddEnd(0);
+	iCurWritePos--;
+
+	return iRet;
+}
+
+_REAL CParameter::GetSysSNRdBPilPos() const
+{
+/*
+	Get system SNR in dB for the pilot positions. Since the average power of
+	the pilots is higher than the data cells, the SNR is also higher at these
+	positions compared to the total SNR of the DRM signal.
+*/
+	return (_REAL) 10.0 * log10(pow((_REAL) 10.0, rSysSimSNRdB / 10) /
+		CellMappingTable.rAvPowPerSymbol * CellMappingTable.rAvScatPilPow * (_REAL) CellMappingTable.iNumCarrier);
+}
+
+void
+CParameter::SetSNR(const _REAL iNewSNR)
+{
+	SNRstat.addSample(iNewSNR);
+}
+
+_REAL
+CParameter::GetSNR()
+{
+	return SNRstat.getCurrent();
+}
+
+_REAL CParameter::GetNominalSNRdB()
+{
+	/* Convert SNR from system bandwidth to nominal bandwidth */
+	return (_REAL) 10.0 * log10(pow((_REAL) 10.0, rSysSimSNRdB / 10) *
+		GetSysToNomBWCorrFact());
+}
+
+void CParameter::SetNominalSNRdB(const _REAL rSNRdBNominal)
+{
+	/* Convert SNR from nominal bandwidth to system bandwidth */
+	rSysSimSNRdB = (_REAL) 10.0 * log10(pow((_REAL) 10.0, rSNRdBNominal / 10) /
+		GetSysToNomBWCorrFact());
+}
+
+_REAL CParameter::GetNominalBandwidth()
+{
+	_REAL rNomBW;
+
+	/* Nominal bandwidth as defined in the DRM standard */
+	switch (eSpectOccup)
+	{
+	case SO_0:
+		rNomBW = (_REAL) 4500.0; /* Hz */
+		break;
+
+	case SO_1:
+		rNomBW = (_REAL) 5000.0; /* Hz */
+		break;
+
+	case SO_2:
+		rNomBW = (_REAL) 9000.0; /* Hz */
+		break;
+
+	case SO_3:
+		rNomBW = (_REAL) 10000.0; /* Hz */
+		break;
+
+	case SO_4:
+		rNomBW = (_REAL) 18000.0; /* Hz */
+		break;
+
+	case SO_5:
+		rNomBW = (_REAL) 20000.0; /* Hz */
+		break;
+
+	default:
+		rNomBW = (_REAL) 10000.0; /* Hz */
+		break;
+	}
+
+	return rNomBW;
+}
+
+_REAL CParameter::GetSysToNomBWCorrFact()
+{
+	_REAL rNomBW = GetNominalBandwidth();
+
+	/* Calculate system bandwidth (N / T_u) */
+	const _REAL rSysBW = (_REAL) CellMappingTable.iNumCarrier / CellMappingTable.iFFTSizeN * SOUNDCRD_SAMPLE_RATE;
+
+	return rSysBW / rNomBW;
+}
+
+
+void CParameter::SetIFSignalLevel(_REAL rNewSigStr)
+{
+	rIFSigStr = rNewSigStr;
+}
+
+_REAL CParameter::GetIFSignalLevel()
+{
+	return rIFSigStr;
+}
+
+void CRxStatus::SetStatus(const ETypeRxStatus OK)
+{
+	status = OK;
+	iNum++;
+	if(OK==RX_OK)
+		iNumOK++;
+}
+
+//void CParameter::GenerateReceiverID()
+//{
+//	//Set receiver ID
+//	string sVer;
+//	unsigned int iImplementation = 0;;
+//	unsigned int iMajor = 0;
+//	unsigned int iMinor = 0;
+
+//	sReceiverID = "drea";
+
+//	sVer = dream_version;
+
+//	size_t pos;
+
+//	while((pos = sVer.find('.')) != string::npos)
+//		sVer.replace(pos, 1, " ");
+
+//	if ((pos = sVer.find("cvs")) != string::npos)
+//		sVer.replace(pos, 3, "   ");
+
+//	stringstream ssVer(sVer);
+//	ssVer >> iImplementation >> iMajor >> iMinor;
+
+//	stringstream ssInfoVer;
+//	ssInfoVer << setw(2) << setfill('0') << iImplementation << setw(2) << setfill('0') << iMajor << setw(2) << setfill('0') << iMinor;
+
+//	sReceiverID += ssInfoVer.str();
+
+//	while (sSerialNumber.length() < 6)
+//			sSerialNumber += "_";
+
+//	if (sSerialNumber.length() > 6)
+//		sSerialNumber.erase(6, pDRMRec->GetParameters()->sSerialNumber.length()-6);
+
+//	sReceiverID += pDRMRec->GetParameters()->sSerialNumber;
+//}
+
+void CParameter::GenerateRandomSerialNumber()
+{
+	//seed random number generator
+	srand((unsigned int)time(0));
+
+	char randomChars[36];
+
+	for (size_t q=0; q < 36; q++)
+	{
+		if (q < 26)
+			randomChars[q] = char(q)+97;
+		else
+			randomChars[q] = (char(q)-26)+48;
+	}
+
+	char serialNumTemp[7];
+
+	for (size_t i=0; i < 6; i++)
+		serialNumTemp[i] = randomChars[(int) 35.0*rand()/RAND_MAX];
+
+	serialNumTemp[6] = '\0';
+
+	sSerialNumber = serialNumTemp;
+}
+
+CMinMaxMean::CMinMaxMean():rSum(0.0),rCur(0.0),
+rMin(numeric_limits<_REAL>::max()),rMax(numeric_limits<_REAL>::min()),iNum(0)
+{
+}
+
+void
+CMinMaxMean::addSample(_REAL r)
+{
+	rCur = r;
+	rSum += r;
+	iNum++;
+	if(r>rMax)
+		rMax = r;
+	if(r<rMin)
+		rMin = r;
+}
+
+_REAL
+CMinMaxMean::getCurrent()
+{
+	return rCur;
+}
+
+_REAL
+CMinMaxMean::getMean()
+{
+	_REAL rMean = 0.0;
+	if(iNum>0)
+		rMean = rSum / iNum;
+	rSum = 0.0;
+	iNum = 0;
+	return rMean;
+}
+
+void
+CMinMaxMean::getMinMax(_REAL& rMinOut, _REAL& rMaxOut)
+{
+	if(rMin <= rMax)
+	{
+		rMinOut = rMin;
+		rMaxOut = rMax;
+	}
+	else
+	{
+		rMinOut = 0.0;
+		rMaxOut = 0.0;
+	}
+	rMin = numeric_limits<_REAL>::max();
+	rMax = numeric_limits<_REAL>::min();
+}
+
+string CServiceDefinition::Frequency(size_t n) const
+{
+	if(n>=veciFrequencies.size())
+		return ""; // not in the list
+
+	stringstream ss;
+	int iFrequency = veciFrequencies[n];
+
+	switch (iSystemID)
+	{
+	case 0:
+	case 1:
+	case 2:
+		/* AM or DRM */
+		ss << iFrequency;
+		break;
+
+	case 3:
+	case 4:
+	case 5:
+		/* 'FM1 frequency' - 87.5 to 107.9 MHz (100 kHz steps) */
+		ss << 87.5 + 0.1 * float(iFrequency);
+		break;
+
+	case 6:
+	case 7:
+	case 8:
+		/* 'FM2 frequency'- 76.0 to 90.0 MHz (100 kHz steps) */
+		ss << 76.0 + 0.1 * float(iFrequency);
+		break;
+
+	case 9:
+	case 10:
+	case 11:
+		if(iFrequency<=11) {
+			int chan = iFrequency / 4;
+			char subchan = 'A' + iFrequency % 4;
+			ss << "Band I channel " << (chan+2) << subchan;
+		} else if(64<= iFrequency && iFrequency <=95) {
+			int chan = iFrequency / 4;
+			char subchan = 'A' + iFrequency % 4;
+			ss << "Band III channel " << (chan-11) << subchan;
+		} else if(96<= iFrequency && iFrequency <=101) {
+			int chan = iFrequency / 6;
+			char subchan = 'A' + iFrequency % 6;
+			ss << "Band III+ channel " << (chan-3) << subchan;
+		} else if(128<= iFrequency && iFrequency <=143) {
+			char chan = iFrequency - 128;
+			double m = 1452.96+1.712*double(chan);
+			ss << "European L-Band channel L" << ('A'+chan) << ", " << m << " MHz";
+		} else if(160<= iFrequency && iFrequency <=182) {
+			int chan = iFrequency - 159;
+            double m = 1451.072+1.744*double(chan);
+			ss << "Canadian L-Band channel " << chan << ", " << m << " MHz";
+		} else {
+			ss << "unknown channel " << iFrequency;
+		}
+		break;
+	default:
+		break;
+	}
+	return ss.str();
+}
+
+string CServiceDefinition::FrequencyUnits() const
+{
+	switch (iSystemID)
+	{
+	case 0:
+	case 1:
+	case 2:
+		return "kHz";
+		break;
+
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		return "MHz";
+		break;
+
+	default:
+		return "";
+		break;
+	}
+}
+
+string CServiceDefinition::System() const
+{
+	switch (iSystemID)
+	{
+	case 0:
+		return "DRM";
+		break;
+
+	case 1:
+	case 2:
+		return "AM";
+		break;
+
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		return "FM";
+		break;
+	case 9:
+	case 10:
+	case 11:
+		return "DAB";
+		break;
+
+	default:
+		return "";
+		break;
+	}
+}
+
+string COtherService::ServiceID() const
+{
+	stringstream ss;
+	/*
+	switch (iSystemID)
+	{
+	case 0:
+	case 1:
+		ss << "ID:" << hex << setw(6) << iServiceID;
+		break;
+
+	case 3:
+	case 6:
+		ss << "ECC+PI:" << hex << setw(6) << iServiceID;
+		break;
+	case 4:
+	case 7:
+		ss << "PI:" << hex << setw(4) << iServiceID;
+		break;
+	case 9:
+		ss << "ECC+aud:" << hex << setw(6) << iServiceID;
+		break;
+	case 10:
+		ss << "AUDIO:" << hex << setw(4) << iServiceID;
+		break;
+	case 11:
+		ss << "DATA:" << hex << setw(8) << iServiceID;
+		break;
+		break;
+
+	default:
+		break;
+	}
+	*/
+	return ss.str();
+}
+
+/* See ETSI ES 201 980 v2.1.1 Annex O */
+_BOOLEAN
+CAltFreqSched::IsActive(const time_t ltime)
+{
+	int iScheduleStart;
+	int iScheduleEnd;
+	int iWeekDay;
+
+	/* Empty schedule is always active */
+	if (iDuration == 0)
+		return true;
+
+	/* Calculate time in UTC */
+	struct tm *gmtCur = gmtime(&ltime);
+
+	/* Check day
+	   tm_wday: day of week (0 - 6; Sunday = 0)
+	   I must normalize so Monday = 0   */
+
+	if (gmtCur->tm_wday == 0)
+		iWeekDay = 6;
+	else
+		iWeekDay = gmtCur->tm_wday - 1;
+
+	/* iTimeWeek minutes since last Monday 00:00 in UTC */
+	/* the value is in the range 0 <= iTimeWeek < 60 * 24 * 7)   */
+
+	const int iTimeWeek =
+		(iWeekDay * 24 * 60) + (gmtCur->tm_hour * 60) + gmtCur->tm_min;
+
+	/* Day Code: this field indicates which days the frequency schedule
+	 * (the following Start Time and Duration) applies to.
+	 * The msb indicates Monday, the lsb Sunday. Between one and seven bits may be set to 1.
+	 */
+	for (int i = 0; i < 7; i++)
+	{
+		/* Check if day is active */
+		if ((1 << (6 - i)) & iDayCode)
+		{
+			/* Tuesday -> 1 * 24 * 60 = 1440 */
+			iScheduleStart = (i * 24 * 60) + iStartTime;
+			iScheduleEnd = iScheduleStart + iDuration;
+
+			/* the normal check (are we inside start and end?) */
+			if ((iTimeWeek >= iScheduleStart) && (iTimeWeek <= iScheduleEnd))
+				return true;
+
+			/* the wrap-around check */
+			const int iMinutesPerWeek = 7 * 24 * 60;
+
+			if (iScheduleEnd > iMinutesPerWeek)
+			{
+				/* our duration wraps into next Monday (or even later) */
+				if (iTimeWeek < (iScheduleEnd - iMinutesPerWeek))
+					return true;
+			}
+		}
+	}
+	return false;
+}
diff --git a/qsstv/drmtx/common/Parameter.h b/qsstv/drmtx/common/Parameter.h
new file mode 100644
index 0000000..f69b123
--- /dev/null
+++ b/qsstv/drmtx/common/Parameter.h
@@ -0,0 +1,1189 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001-2007
+ *
+ * Author(s):
+ *	Volker Fischer, Andrew Murphy, Andrea Russo
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	See Parameter.cpp
+ *
+ * 10/01/2007 Andrew Murphy, BBC Research & Development, 2005
+ *	- Additions to include additional RSCI related fields
+ *
+ * 11/21/2005 Andrew Murphy, BBC Research & Development, 2005
+ *	- Additions to include AMSS demodulation (Added class
+ *    CAltFreqOtherServicesSign)
+ *
+ * 11/28/2005 Andrea Russo
+ *	- Added classes for store alternative frequencies schedules and regions
+ *
+ *******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(PARAMETER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define PARAMETER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "GlobalDefinitions.h"
+#include "ofdmcellmapping/CellMappingTable.h"
+#include "matlib/Matlib.h"
+#include <time.h>
+//#include "GPSData.h"
+//#include "ServiceInformation.h"
+#include <set>
+#include <map>
+#include <iostream>
+
+class CDRMReceiver;
+
+	/* CS: Coding Scheme */
+	enum ECodScheme { CS_1_SM, CS_2_SM, CS_3_SM, CS_3_HMSYM, CS_3_HMMIX };
+
+	/* CT: Channel Type */
+	enum EChanType { CT_MSC, CT_SDC, CT_FAC };
+
+enum ETypeIntFreq
+{ FLINEAR, FDFTFILTER, FWIENER };
+enum ETypeIntTime
+{ TLINEAR, TWIENER };
+enum ETypeSNREst
+{ SNR_FAC, SNR_PIL };
+enum ETypeRxStatus
+{ NOT_PRESENT, CRC_ERROR, DATA_ERROR, RX_OK };
+	/* RM: Receiver mode (analog or digital demodulation) */
+
+enum ERecMode
+{ RM_DRM, RM_AM, RM_NONE };
+
+	/* Acquisition state of receiver */
+enum EAcqStat {AS_NO_SIGNAL, AS_WITH_SIGNAL};
+
+	/* Receiver state */
+enum ERecState {RS_TRACKING, RS_ACQUISITION};
+
+/* Classes ********************************************************************/
+
+	class CAudioParam
+	{
+	  public:
+
+		/* AC: Audio Coding */
+		enum EAudCod { AC_AAC, AC_CELP, AC_HVXC };
+
+		/* SB: SBR */
+		enum ESBRFlag { SB_NOT_USED, SB_USED };
+
+		/* AM: Audio Mode */
+		enum EAudMode { AM_MONO, AM_P_STEREO, AM_STEREO };
+
+		/* HR: HVXC Rate */
+		enum EHVXCRate { HR_2_KBIT, HR_4_KBIT };
+
+		/* AS: Audio Sampling rate */
+		enum EAudSamRat { AS_8_KHZ, AS_12KHZ, AS_16KHZ, AS_24KHZ };
+
+		CAudioParam(): strTextMessage(), iStreamID(STREAM_ID_NOT_USED),
+			eAudioCoding(AC_AAC), eSBRFlag(SB_NOT_USED), eAudioSamplRate(AS_24KHZ),
+			bTextflag(false), bEnhanceFlag(false), eAudioMode(AM_MONO),
+			iCELPIndex(0), bCELPCRC(false), eHVXCRate(HR_2_KBIT), bHVXCCRC(false)
+		{
+		}
+		CAudioParam(const CAudioParam& ap):
+			strTextMessage(ap.strTextMessage),
+			iStreamID(ap.iStreamID),
+			eAudioCoding(ap.eAudioCoding),
+			eSBRFlag(ap.eSBRFlag),
+			eAudioSamplRate(ap.eAudioSamplRate),
+			bTextflag(ap.bTextflag),
+			bEnhanceFlag(ap.bEnhanceFlag),
+			eAudioMode(ap.eAudioMode),
+			iCELPIndex(ap.iCELPIndex),
+			bCELPCRC(ap.bCELPCRC),
+			eHVXCRate(ap.eHVXCRate),
+			bHVXCCRC(ap.bHVXCCRC)
+		{
+		}
+		CAudioParam& operator=(const CAudioParam& ap)
+		{
+			strTextMessage = ap.strTextMessage;
+			iStreamID = ap.iStreamID;
+			eAudioCoding = ap.eAudioCoding;
+			eSBRFlag = ap.eSBRFlag;
+			eAudioSamplRate = ap.eAudioSamplRate;
+			bTextflag =	ap.bTextflag;
+			bEnhanceFlag = ap.bEnhanceFlag;
+			eAudioMode = ap.eAudioMode;
+			iCELPIndex = ap.iCELPIndex;
+			bCELPCRC = ap.bCELPCRC;
+			eHVXCRate = ap.eHVXCRate;
+			bHVXCCRC = ap.bHVXCCRC;
+			return *this;
+		}
+
+		/* Text-message */
+		string strTextMessage;	/* Max length is (8 * 16 Bytes) */
+
+		int iStreamID;			/* Stream Id of the stream which carries the audio service */
+
+		EAudCod eAudioCoding;	/* This field indicated the source coding system */
+		ESBRFlag eSBRFlag;		/* SBR flag */
+		EAudSamRat eAudioSamplRate;	/* Audio sampling rate */
+		_BOOLEAN bTextflag;		/* Indicates whether a text message is present or not */
+		_BOOLEAN bEnhanceFlag;	/* Enhancement flag */
+
+		/* For AAC: Mono, LC Stereo, Stereo --------------------------------- */
+		EAudMode eAudioMode;	/* Audio mode */
+
+		/* For CELP --------------------------------------------------------- */
+		int iCELPIndex;			/* This field indicates the CELP bit rate index */
+		_BOOLEAN bCELPCRC;		/* This field indicates whether the CRC is used or not */
+
+		/* For HVXC --------------------------------------------------------- */
+		EHVXCRate eHVXCRate;	/* This field indicates the rate of the HVXC */
+		_BOOLEAN bHVXCCRC;		/* This field indicates whether the CRC is used or not */
+
+
+		/* This function is needed for detection changes in the class */
+		_BOOLEAN operator!=(const CAudioParam AudioParam)
+		{
+			if (iStreamID != AudioParam.iStreamID)
+				return true;
+			if (eAudioCoding != AudioParam.eAudioCoding)
+				return true;
+			if (eSBRFlag != AudioParam.eSBRFlag)
+				return true;
+			if (eAudioSamplRate != AudioParam.eAudioSamplRate)
+				return true;
+			if (bTextflag != AudioParam.bTextflag)
+				return true;
+			if (bEnhanceFlag != AudioParam.bEnhanceFlag)
+				return true;
+
+			switch (AudioParam.eAudioCoding)
+			{
+			case AC_AAC:
+				if (eAudioMode != AudioParam.eAudioMode)
+					return true;
+				break;
+
+			case AC_CELP:
+				if (bCELPCRC != AudioParam.bCELPCRC)
+					return true;
+				if (iCELPIndex != AudioParam.iCELPIndex)
+					return true;
+				break;
+
+			case AC_HVXC:
+				if (eHVXCRate != AudioParam.eHVXCRate)
+					return true;
+				if (bHVXCCRC != AudioParam.bHVXCCRC)
+					return true;
+				break;
+			}
+			return false;
+		}
+	};
+
+	class CDataParam
+	{
+	  public:
+
+		/* PM: Packet Mode */
+		enum EPackMod { PM_SYNCHRON_STR_MODE, PM_PACKET_MODE };
+
+		/* DU: Data Unit */
+		enum EDatUnit { DU_SINGLE_PACKETS, DU_DATA_UNITS };
+
+		/* AD: Application Domain */
+		enum EApplDomain { AD_DRM_SPEC_APP, AD_DAB_SPEC_APP, AD_OTHER_SPEC_APP };
+
+		int iStreamID;			/* Stream Id of the stream which carries the data service */
+
+		EPackMod ePacketModInd;	/* Packet mode indicator */
+
+		/* In case of packet mode ------------------------------------------- */
+		EDatUnit eDataUnitInd;	/* Data unit indicator */
+		int iPacketID;			/* Packet Id (2 bits) */
+		int iPacketLen;			/* Packet length */
+
+		// "DAB specified application" not yet implemented!!!
+		EApplDomain eAppDomain;	/* Application domain */
+		int iUserAppIdent;		/* User application identifier, only DAB */
+
+		CDataParam():
+			iStreamID(STREAM_ID_NOT_USED),
+			ePacketModInd(PM_PACKET_MODE),
+			eDataUnitInd(DU_DATA_UNITS),
+			iPacketID(0),
+			iPacketLen(0),
+			eAppDomain(AD_DAB_SPEC_APP),
+			iUserAppIdent(2)        // was 0 ipc 2 (AT_MOTSLISHOW) pa0mbo Nov 7th 2011
+		{
+		}
+		CDataParam(const CDataParam& DataParam):
+			iStreamID(DataParam.iStreamID),
+			ePacketModInd(DataParam.ePacketModInd),
+			eDataUnitInd(DataParam.eDataUnitInd),
+			iPacketID(DataParam.iPacketID),
+			iPacketLen(DataParam.iPacketLen),
+			eAppDomain(DataParam.eAppDomain),
+			iUserAppIdent(DataParam.iUserAppIdent)
+		{
+		}
+		CDataParam& operator=(const CDataParam& DataParam)
+		{
+			iStreamID = DataParam.iStreamID;
+			ePacketModInd = DataParam.ePacketModInd;
+			eDataUnitInd = DataParam.eDataUnitInd;
+			iPacketID = DataParam.iPacketID;
+			iPacketLen = DataParam.iPacketLen;
+			eAppDomain = DataParam.eAppDomain;
+			iUserAppIdent = DataParam.iUserAppIdent;
+			return *this;
+		}
+
+		/* This function is needed for detection changes in the class */
+		_BOOLEAN operator!=(const CDataParam DataParam)
+		{
+			if (iStreamID != DataParam.iStreamID)
+				return true;
+			if (ePacketModInd != DataParam.ePacketModInd)
+				return true;
+			if (DataParam.ePacketModInd == PM_PACKET_MODE)
+			{
+				if (eDataUnitInd != DataParam.eDataUnitInd)
+					return true;
+				if (iPacketID != DataParam.iPacketID)
+					return true;
+				if (iPacketLen != DataParam.iPacketLen)
+					return true;
+				if (eAppDomain != DataParam.eAppDomain)
+					return true;
+				if (DataParam.eAppDomain == AD_DAB_SPEC_APP)
+					if (iUserAppIdent != DataParam.iUserAppIdent)
+						return true;
+			}
+			return false;
+		}
+	};
+
+	class CService
+	{
+	  public:
+
+		/* CA: CA system */
+		enum ECACond { CA_USED, CA_NOT_USED };
+
+		/* SF: Service Flag */
+		enum ETyOServ { SF_AUDIO, SF_DATA };
+
+		CService():
+			iServiceID(SERV_ID_NOT_USED), eCAIndication(CA_NOT_USED),
+			iLanguage(0), eAudDataFlag(SF_AUDIO), iServiceDescr(0),
+			strCountryCode(), strLanguageCode(), strLabel(),
+			AudioParam(), DataParam()
+		{
+		}
+		CService(const CService& s):
+			iServiceID(s.iServiceID), eCAIndication(s.eCAIndication),
+			iLanguage(s.iLanguage), eAudDataFlag(s.eAudDataFlag),
+			iServiceDescr(s.iServiceDescr), strCountryCode(s.strCountryCode),
+			strLanguageCode(s.strLanguageCode), strLabel(s.strLabel),
+			AudioParam(s.AudioParam), DataParam(s.DataParam)
+		{
+		}
+		CService& operator=(const CService& s)
+		{
+			iServiceID = s.iServiceID;
+			eCAIndication = s.eCAIndication;
+			iLanguage = s.iLanguage;
+			eAudDataFlag = s.eAudDataFlag;
+			iServiceDescr = s.iServiceDescr;
+			strCountryCode = s.strCountryCode;
+			strLanguageCode = s.strLanguageCode;
+			strLabel = s.strLabel;
+			AudioParam = s.AudioParam;
+			DataParam = s.DataParam;
+			return *this;
+		}
+
+		_BOOLEAN IsActive() const
+		{
+			return iServiceID != SERV_ID_NOT_USED;
+		}
+
+		uint32_t iServiceID;
+		ECACond eCAIndication;
+		int iLanguage;
+		ETyOServ eAudDataFlag;
+		int iServiceDescr;
+		string strCountryCode;
+		string strLanguageCode;
+
+		/* Label of the service */
+		string strLabel;
+
+		/* Audio parameters */
+		CAudioParam AudioParam;
+
+		/* Data parameters */
+		CDataParam DataParam;
+	};
+
+	class CStream
+	{
+	  public:
+
+		CStream():iLenPartA(0), iLenPartB(0)
+		{
+		}
+		CStream(const CStream& s):iLenPartA(s.iLenPartA), iLenPartB(s.iLenPartB)
+		{
+		}
+		CStream& operator=(const CStream& Stream)
+		{
+			iLenPartA=Stream.iLenPartA; iLenPartB=Stream.iLenPartB;
+			return *this;
+		}
+
+		bool operator==(const CStream Stream)
+		{
+			if (iLenPartA != Stream.iLenPartA)
+				return false;
+			if (iLenPartB != Stream.iLenPartB)
+				return false;
+			return true;
+		}
+
+		int iLenPartA;			/* Data length for part A */
+		int iLenPartB;			/* Data length for part B */
+	};
+
+	class CMSCProtLev
+	{
+	  public:
+
+    CMSCProtLev(){init();}
+    void init() { iPartA=iPartB=iHierarch=0; };
+		CMSCProtLev(const CMSCProtLev& p):iPartA(p.iPartA),iPartB(p.iPartB),iHierarch(p.iHierarch) {}
+		CMSCProtLev& operator=(const CMSCProtLev& NewMSCProtLev)
+		{
+			iPartA = NewMSCProtLev.iPartA;
+			iPartB = NewMSCProtLev.iPartB;
+			iHierarch = NewMSCProtLev.iHierarch;
+			return *this;
+		}
+
+		int iPartA;				/* MSC protection level for part A */
+		int iPartB;				/* MSC protection level for part B */
+		int iHierarch;			/* MSC protection level for hierachical frame */
+	};
+
+	/* Alternative Frequency Signalling ************************************** */
+	/* Alternative frequency signalling Schedules informations class */
+	class CAltFreqSched
+	{
+	  public:
+		CAltFreqSched():iDayCode(0),iStartTime(0),iDuration(0)
+		{
+		}
+		CAltFreqSched(const CAltFreqSched& nAFS):
+			iDayCode(nAFS.iDayCode), iStartTime(nAFS.iStartTime),
+			iDuration(nAFS.iDuration)
+		{
+		}
+
+		CAltFreqSched& operator=(const CAltFreqSched& nAFS)
+		{
+			iDayCode = nAFS.iDayCode;
+			iStartTime = nAFS.iStartTime;
+			iDuration = nAFS.iDuration;
+
+			return *this;
+		}
+
+		_BOOLEAN operator==(const CAltFreqSched& nAFS)
+		{
+			if (iDayCode != nAFS.iDayCode)
+				return false;
+			if (iStartTime != nAFS.iStartTime)
+				return false;
+			if (iDuration != nAFS.iDuration)
+				return false;
+
+			return true;
+		}
+
+		_BOOLEAN IsActive(const time_t ltime);
+
+		int iDayCode;
+		int iStartTime;
+		int iDuration;
+	};
+
+	/* Alternative frequency signalling Regions informations class */
+	class CAltFreqRegion
+	{
+	  public:
+		CAltFreqRegion():veciCIRAFZones(),
+			iLatitude(0), iLongitude(0),
+			iLatitudeEx(0), iLongitudeEx(0)
+		{
+		}
+		CAltFreqRegion(const CAltFreqRegion& nAFR):
+			veciCIRAFZones(nAFR.veciCIRAFZones),
+			iLatitude(nAFR.iLatitude),
+			iLongitude(nAFR.iLongitude),
+			iLatitudeEx(nAFR.iLatitudeEx), iLongitudeEx(nAFR.iLongitudeEx)
+		{
+		}
+
+		CAltFreqRegion& operator=(const CAltFreqRegion& nAFR)
+		{
+			iLatitude = nAFR.iLatitude;
+			iLongitude = nAFR.iLongitude;
+			iLatitudeEx = nAFR.iLatitudeEx;
+			iLongitudeEx = nAFR.iLongitudeEx;
+
+			veciCIRAFZones = nAFR.veciCIRAFZones;
+
+			return *this;
+		}
+
+		_BOOLEAN operator==(const CAltFreqRegion& nAFR)
+		{
+			if (iLatitude != nAFR.iLatitude)
+				return false;
+			if (iLongitude != nAFR.iLongitude)
+				return false;
+			if (iLatitudeEx != nAFR.iLatitudeEx)
+				return false;
+			if (iLongitudeEx != nAFR.iLongitudeEx)
+				return false;
+
+			/* Vector sizes */
+			if (veciCIRAFZones.size() != nAFR.veciCIRAFZones.size())
+				return false;
+
+			/* Vector contents */
+			for (size_t i = 0; i < veciCIRAFZones.size(); i++)
+				if (veciCIRAFZones[i] != nAFR.veciCIRAFZones[i])
+					return false;
+
+			return true;
+		}
+
+		vector<int> veciCIRAFZones;
+		int iLatitude;
+		int iLongitude;
+		int iLatitudeEx;
+		int iLongitudeEx;
+	};
+
+	class CServiceDefinition
+	{
+ 	public:
+		CServiceDefinition():veciFrequencies(), iRegionID(0), iScheduleID(0),iSystemID(0)
+		{
+		}
+
+		CServiceDefinition(const CServiceDefinition& nAF):
+			veciFrequencies(nAF.veciFrequencies),
+			iRegionID(nAF.iRegionID), iScheduleID(nAF.iScheduleID),
+			iSystemID(nAF.iSystemID)
+		{
+		}
+
+		CServiceDefinition& operator=(const CServiceDefinition& nAF)
+		{
+			veciFrequencies = nAF.veciFrequencies;
+			iRegionID = nAF.iRegionID;
+			iScheduleID = nAF.iScheduleID;
+			iSystemID = nAF.iSystemID;
+			return *this;
+		}
+
+		bool operator==(const CServiceDefinition& sd) const
+		{
+			size_t i;
+
+			/* Vector sizes */
+			if (veciFrequencies.size() != sd.veciFrequencies.size())
+				return false;
+
+			/* Vector contents */
+			for (i = 0; i < veciFrequencies.size(); i++)
+				if (veciFrequencies[i] != sd.veciFrequencies[i])
+					return false;
+
+			if (iRegionID != sd.iRegionID)
+				return false;
+
+			if (iScheduleID != sd.iScheduleID)
+				return false;
+
+			if (iSystemID != sd.iSystemID)
+				return false;
+
+			return true;
+		}
+		bool operator!=(const CServiceDefinition& sd) const { return !(*this==sd); }
+
+		string Frequency(size_t) const;
+		string FrequencyUnits() const;
+		string System() const;
+
+		vector<int> veciFrequencies;
+		int iRegionID;
+		int iScheduleID;
+		int iSystemID;
+	};
+
+	class CMultiplexDefinition: public CServiceDefinition
+	{
+ 	public:
+		CMultiplexDefinition():CServiceDefinition(), veciServRestrict(4), bIsSyncMultplx(false)
+		{
+		}
+
+		CMultiplexDefinition(const CMultiplexDefinition& nAF):CServiceDefinition(nAF),
+			veciServRestrict(nAF.veciServRestrict),
+			bIsSyncMultplx(nAF.bIsSyncMultplx)
+		{
+		}
+
+		CMultiplexDefinition& operator=(const CMultiplexDefinition& nAF)
+		{
+			CServiceDefinition(*this) = nAF;
+			veciServRestrict = nAF.veciServRestrict;
+			bIsSyncMultplx = nAF.bIsSyncMultplx;
+			return *this;
+		}
+
+		bool operator==(const CMultiplexDefinition& md) const
+		{
+			if (CServiceDefinition(*this) != md)
+				return false;
+
+			/* Vector sizes */
+			if (veciServRestrict.size() != md.veciServRestrict.size())
+				return false;
+
+			/* Vector contents */
+			for (size_t i = 0; i < veciServRestrict.size(); i++)
+				if (veciServRestrict[i] != md.veciServRestrict[i])
+					return false;
+
+			if (bIsSyncMultplx != md.bIsSyncMultplx)
+				return false;
+
+			return true;
+		}
+
+		vector<int> veciServRestrict;
+		_BOOLEAN bIsSyncMultplx;
+	};
+
+	class COtherService: public CServiceDefinition
+	{
+	public:
+		COtherService(): CServiceDefinition(), bSameService(true),
+			iShortID(0), iServiceID(SERV_ID_NOT_USED)
+		{
+		}
+
+		COtherService(const COtherService& nAF):
+			CServiceDefinition(nAF), bSameService(nAF.bSameService),
+			iShortID(nAF.iShortID), iServiceID(nAF.iServiceID)
+		{
+		}
+
+		COtherService& operator=(const COtherService& nAF)
+		{
+			CServiceDefinition(*this) = nAF;
+
+			bSameService = nAF.bSameService;
+			iShortID = nAF.iShortID;
+			iServiceID = nAF.iServiceID;
+
+			return *this;
+		}
+
+		bool operator==(const COtherService& nAF)
+		{
+			if (CServiceDefinition(*this) != nAF)
+				return false;
+
+			if (bSameService != nAF.bSameService)
+				return false;
+
+			if (iShortID != nAF.iShortID)
+				return false;
+
+			if (iServiceID != nAF.iServiceID)
+				return false;
+
+			return true;
+		}
+
+		string ServiceID() const;
+
+		_BOOLEAN bSameService;
+		int iShortID;
+		uint32_t iServiceID;
+	};
+
+	/* Alternative frequency signalling class */
+	class CAltFreqSign
+	{
+	  public:
+
+		CAltFreqSign():vecRegions(16),vecSchedules(16),vecMultiplexes(),vecOtherServices(),
+			bRegionVersionFlag(false),bScheduleVersionFlag(false),
+			bMultiplexVersionFlag(false),bOtherServicesVersionFlag(false)
+		{
+		}
+
+		CAltFreqSign(const CAltFreqSign& a):vecRegions(a.vecRegions),
+			vecSchedules(a.vecSchedules), vecMultiplexes(a.vecMultiplexes),
+			bRegionVersionFlag(a.bRegionVersionFlag),
+			bScheduleVersionFlag(a.bScheduleVersionFlag),
+			bMultiplexVersionFlag(a.bMultiplexVersionFlag),
+			bOtherServicesVersionFlag(a.bOtherServicesVersionFlag)
+		{
+		}
+
+		CAltFreqSign& operator=(const CAltFreqSign& a)
+		{
+			vecRegions = a.vecRegions;
+			vecSchedules = a.vecSchedules;
+			vecMultiplexes = a.vecMultiplexes;
+			bRegionVersionFlag = a.bRegionVersionFlag;
+			bScheduleVersionFlag = a.bScheduleVersionFlag;
+			bMultiplexVersionFlag = a.bMultiplexVersionFlag;
+			bOtherServicesVersionFlag = a.bOtherServicesVersionFlag;
+			return *this;
+		}
+
+		void ResetRegions(_BOOLEAN b)
+		{
+			vecRegions.clear();
+			vecRegions.resize(16);
+			bRegionVersionFlag=b;
+		}
+
+		void ResetSchedules(_BOOLEAN b)
+		{
+			vecSchedules.clear();
+			vecSchedules.resize(16);
+			bScheduleVersionFlag=b;
+		}
+
+		void ResetMultiplexes(_BOOLEAN b)
+		{
+			vecMultiplexes.clear();
+			bMultiplexVersionFlag=b;
+		}
+
+		void ResetOtherServices(_BOOLEAN b)
+		{
+			vecOtherServices.clear();
+			bOtherServicesVersionFlag=b;
+		}
+
+		void Reset()
+		{
+			ResetRegions(false);
+			ResetSchedules(false);
+			ResetMultiplexes(false);
+			ResetOtherServices(false);
+		}
+
+		vector < vector<CAltFreqRegion> > vecRegions; // outer vector indexed by regionID
+		vector < vector<CAltFreqSched> > vecSchedules; // outer vector indexed by scheduleID
+		vector < CMultiplexDefinition > vecMultiplexes;
+		vector < COtherService > vecOtherServices;
+		_BOOLEAN bRegionVersionFlag;
+		_BOOLEAN bScheduleVersionFlag;
+		_BOOLEAN bMultiplexVersionFlag;
+		_BOOLEAN bOtherServicesVersionFlag;
+	};
+
+	/* Class to store information about the last service selected ------------- */
+
+	class CLastService
+	{
+	  public:
+		CLastService():iService(0), iServiceID(SERV_ID_NOT_USED)
+		{
+		}
+		CLastService(const CLastService& l):iService(l.iService), iServiceID(l.iServiceID)
+		{
+		}
+		CLastService& operator=(const CLastService& l)
+		{
+			iService = l.iService;
+			iServiceID = l.iServiceID;
+			return *this;
+		}
+
+		void Reset()
+		{
+			iService = 0;
+			iServiceID = SERV_ID_NOT_USED;
+		};
+
+		void Save(const int iCurSel, const int iCurServiceID)
+		{
+			if (iCurServiceID != SERV_ID_NOT_USED)
+			{
+				iService = iCurSel;
+				iServiceID = iCurServiceID;
+			}
+		};
+
+		/* store only fac parameters */
+		int iService;
+		uint32_t iServiceID;
+	};
+
+	/* Classes to keep track of status flags for RSCI rsta tag and log file */
+	class CRxStatus
+	{
+	public:
+		CRxStatus():status(NOT_PRESENT),iNum(0),iNumOK(0) {}
+		CRxStatus(const CRxStatus& s):status(s.status),iNum(s.iNum),iNumOK(s.iNumOK) {}
+		CRxStatus& operator=(const CRxStatus& s)
+			{ status = s.status; iNum = s.iNum; iNumOK = s.iNumOK; return *this;}
+		void SetStatus(const ETypeRxStatus);
+		ETypeRxStatus GetStatus() { return status; }
+		int GetCount() { return iNum; }
+		int GetOKCount() { return iNumOK; }
+		void ResetCounts() { iNum=0; iNumOK = 0; }
+	private:
+		ETypeRxStatus status;
+		int iNum, iNumOK;
+	};
+
+	class CReceiveStatus
+	{
+	  public:
+		CReceiveStatus():FSync(),TSync(),Interface(),
+		FAC(),SDC(),Audio(),LLAudio(),MOT()
+		{
+		}
+		CReceiveStatus(const CReceiveStatus& s):FSync(s.FSync), TSync(s.TSync),
+			Interface(s.Interface), FAC(s.FAC), SDC(s.SDC),
+			Audio(s.Audio),LLAudio(s.LLAudio),MOT(s.MOT)
+		{
+		}
+		CReceiveStatus& operator=(const CReceiveStatus& s)
+		{
+			FSync = s.FSync;
+			TSync = s.TSync;
+			Interface = s.Interface;
+			FAC = s.FAC;
+			SDC = s.SDC;
+			Audio = s.Audio;
+			LLAudio = s.LLAudio;
+			MOT = s.MOT;
+			return *this;
+		}
+
+		CRxStatus FSync;
+		CRxStatus TSync;
+		CRxStatus Interface;
+		CRxStatus FAC;
+		CRxStatus SDC;
+		CRxStatus Audio;
+		CRxStatus LLAudio;
+		CRxStatus MOT;
+	};
+
+
+	/* Simulation raw-data management. We have to implement a shift register
+	   with varying size. We do that by adding a variable for storing the
+	   current write position. */
+	class CRawSimData
+	{
+		/* We have to implement a shift register with varying size. We do that
+		   by adding a variable for storing the current write position. We use
+		   always the first value of the array for reading and do a shift of the
+		   other data by adding a arbitrary value (0) at the end of the whole
+		   shift register */
+	  public:
+		/* Here, the maximal size of the shift register is set */
+		CRawSimData():ciMaxDelBlocks(50), iCurWritePos(0)
+		{
+			veciShRegSt.Init(ciMaxDelBlocks);
+		}
+
+		void Add(uint32_t iNewSRS);
+		uint32_t Get();
+
+		void Reset()
+		{
+			iCurWritePos = 0;
+		}
+
+	  protected:
+		/* Max number of delayed blocks */
+		int ciMaxDelBlocks;
+		CShiftRegister < uint32_t > veciShRegSt;
+		int iCurWritePos;
+	};
+
+	class CFrontEndParameters
+	{
+	public:
+		enum ESMeterCorrectionType {S_METER_CORRECTION_TYPE_CAL_FACTOR_ONLY, S_METER_CORRECTION_TYPE_AGC_ONLY, S_METER_CORRECTION_TYPE_AGC_RSSI};
+
+		// Constructor
+		CFrontEndParameters():
+			eSMeterCorrectionType(S_METER_CORRECTION_TYPE_CAL_FACTOR_ONLY), rSMeterBandwidth(10000.0),
+				rDefaultMeasurementBandwidth(10000.0), bAutoMeasurementBandwidth(true), rCalFactorAM(0.0),
+				rCalFactorDRM(0.0), rIFCentreFreq(12000.0)
+			{}
+		CFrontEndParameters(const CFrontEndParameters& p):
+			eSMeterCorrectionType(p.eSMeterCorrectionType), rSMeterBandwidth(p.rSMeterBandwidth),
+			rDefaultMeasurementBandwidth(p.rDefaultMeasurementBandwidth),
+			bAutoMeasurementBandwidth(p.bAutoMeasurementBandwidth),
+			rCalFactorAM(p.rCalFactorAM), rCalFactorDRM(p.rCalFactorDRM),
+			rIFCentreFreq(p.rIFCentreFreq)
+			{}
+		CFrontEndParameters& operator=(const CFrontEndParameters& p)
+		{
+			eSMeterCorrectionType = p.eSMeterCorrectionType;
+			rSMeterBandwidth = p.rSMeterBandwidth;
+			rDefaultMeasurementBandwidth = p.rDefaultMeasurementBandwidth;
+			bAutoMeasurementBandwidth = p.bAutoMeasurementBandwidth;
+			rCalFactorAM = p.rCalFactorAM;
+			rCalFactorDRM = p.rCalFactorDRM;
+			rIFCentreFreq = p.rIFCentreFreq;
+			return *this;
+		}
+
+		ESMeterCorrectionType eSMeterCorrectionType;
+		_REAL rSMeterBandwidth; // The bandwidth the S-meter uses to do the measurement
+
+		_REAL rDefaultMeasurementBandwidth; // Bandwidth to do measurement if not synchronised
+		_BOOLEAN bAutoMeasurementBandwidth; // true: use the current FAC bandwidth if locked, false: use default bandwidth always
+		_REAL rCalFactorAM;
+		_REAL rCalFactorDRM;
+		_REAL rIFCentreFreq;
+
+	};
+
+
+class CMinMaxMean
+{
+public:
+	CMinMaxMean();
+
+	void addSample(_REAL);
+	_REAL getCurrent();
+	_REAL getMean();
+	void getMinMax(_REAL&, _REAL&);
+protected:
+	_REAL rSum, rCur, rMin, rMax;
+	int iNum;
+};
+
+class CParameter
+{
+  public:
+  CParameter();
+  void init();
+  CParameter(const CParameter& p);
+	//CParameter(CDRMReceiver *pRx, CParameter *pParameter); // OPH - just copy some of the members
+	virtual ~CParameter();
+	CParameter& operator=(const CParameter&);
+
+	/* Enumerations --------------------------------------------------------- */
+	/* AS: AFS in SDC is valid or not */
+	enum EAFSVali { AS_VALID, AS_NOT_VALID };
+
+
+	/* SI: Symbol Interleaver */
+	enum ESymIntMod { SI_LONG, SI_SHORT };
+
+	/* ST: Simulation Type */
+	enum ESimType
+	{ ST_NONE, ST_BITERROR, ST_MSECHANEST, ST_BER_IDEALCHAN,
+		ST_SYNC_PARAM, ST_SINR
+	};
+
+	/* Misc. Functions ------------------------------------------------------ */
+	void GenerateRandomSerialNumber();
+//	void GenerateReceiverID();
+	void ResetServicesStreams();
+	void GetActiveServices(set<int>& actServ);
+	void GetActiveStreams(set<int>& actStr);
+	void InitCellMapTable(const ERobMode eNewWaveMode,
+						  const ESpecOcc eNewSpecOcc);
+
+	void SetNumDecodedBitsMSC(const int iNewNumDecodedBitsMSC);
+	void SetNumDecodedBitsSDC(const int iNewNumDecodedBitsSDC);
+	void SetNumBitsHieraFrTot(const int iNewNumBitsHieraFrTot);
+	void SetNumAudioDecoderBits(const int iNewNumAudioDecoderBits);
+	void SetNumDataDecoderBits(const int iNewNumDataDecoderBits);
+
+	_BOOLEAN SetWaveMode(const ERobMode eNewWaveMode);
+	ERobMode GetWaveMode() const { return eRobustnessMode; }
+
+	void SetFrequency(int iNewFrequency) { iFrequency = iNewFrequency; }
+	int GetFrequency() { return iFrequency; }
+
+	void SetServiceParameters(int iShortID, const CService& newService);
+
+	void SetCurSelAudioService(const int iNewService);
+	int GetCurSelAudioService() const { return iCurSelAudioService; }
+	void SetCurSelDataService(const int iNewService);
+	int GetCurSelDataService() const { return iCurSelDataService; }
+
+	void ResetCurSelAudDatServ()
+	{
+		iCurSelAudioService = 0;
+		iCurSelDataService = 0;
+	}
+
+	void EnableMultimedia(const _BOOLEAN bFlag);
+	_BOOLEAN GetEnableMultimedia() const { return bUsingMultimedia; }
+
+	_REAL GetDCFrequency() const
+	{
+		return SOUNDCRD_SAMPLE_RATE * (rFreqOffsetAcqui + rFreqOffsetTrack);
+	}
+
+	_REAL GetBitRateKbps(const int iShortID, const _BOOLEAN bAudData);
+	_REAL PartABLenRatio(const int iShortID);
+
+	/* Parameters controlled by FAC ----------------------------------------- */
+	void SetInterleaverDepth(const ESymIntMod eNewDepth);
+	ESymIntMod GetInterleaverDepth()
+	{
+		return eSymbolInterlMode;
+	}
+
+	void SetMSCCodingScheme(const ECodScheme eNewScheme);
+	void SetSDCCodingScheme(const ECodScheme eNewScheme);
+
+	void SetSpectrumOccup(ESpecOcc eNewSpecOcc);
+	ESpecOcc GetSpectrumOccup() const
+	{
+		return eSpectOccup;
+	}
+
+	void SetNumOfServices(const size_t iNNumAuSe, const size_t iNNumDaSe);
+	size_t GetTotNumServices()
+	{
+		return iNumAudioService + iNumDataService;
+	}
+
+	void SetAudDataFlag(const int iShortID, const CService::ETyOServ iNewADaFl);
+	void SetServiceID(const int iShortID, const uint32_t iNewServiceID);
+
+//	CDRMReceiver* pDRMRec; joma removed rx
+
+	/* Symbol interleaver mode (long or short interleaving) */
+	ESymIntMod eSymbolInterlMode;
+
+	ECodScheme eMSCCodingScheme;	/* MSC coding scheme */
+	ECodScheme eSDCCodingScheme;	/* SDC coding scheme */
+
+	size_t iNumAudioService;
+	size_t iNumDataService;
+
+	/* AMSS */
+	int iAMSSCarrierMode;
+
+	/* Serial number and received ID */
+	string sReceiverID;
+	string sSerialNumber;
+	string sDataFilesDirectory;
+
+	/* Parameters controlled by SDC ----------------------------------------- */
+	void SetAudioParam(const int iShortID, const CAudioParam& NewAudParam);
+	CAudioParam GetAudioParam(const int iShortID);
+	CDataParam GetDataParam(const int iShortID);
+	void SetDataParam(const int iShortID, const CDataParam& NewDataParam);
+
+	void SetMSCProtLev(const CMSCProtLev NewMSCPrLe, const _BOOLEAN bWithHierarch);
+	void SetStreamLen(const int iStreamID, const int iNewLenPartA, const int iNewLenPartB);
+	void GetStreamLen(const int iStreamID, int& iLenPartA, int& iLenPartB);
+	int GetStreamLen(const int iStreamID);
+
+	/* Protection levels for MSC */
+	CMSCProtLev MSCPrLe;
+
+	vector<CStream> Stream;
+	vector<CService> Service;
+
+	/* information about services gathered from SDC, EPG and web schedules */
+//	map<uint32_t,CServiceInformation> ServiceInformation;
+
+	/* These values are used to set input and output block sizes of some modules */
+	int iNumBitsHierarchFrameTotal;
+	int iNumDecodedBitsMSC;
+	int iNumSDCBitsPerSFrame;	/* Number of SDC bits per super-frame */
+	int iNumAudioDecoderBits;	/* Number of input bits for audio module */
+	int iNumDataDecoderBits;	/* Number of input bits for data decoder module */
+
+	/* Date */
+	int iYear;
+	int iMonth;
+	int iDay;
+
+	/* UTC (hours and minutes) */
+	int iUTCHour;
+	int iUTCMin;
+
+	/* Identifies the current frame. This parameter is set by FAC */
+	int iFrameIDTransm;
+	int iFrameIDReceiv;
+
+	/* Synchronization ------------------------------------------------------ */
+	_REAL rFreqOffsetAcqui;
+	_REAL rFreqOffsetTrack;
+
+	_REAL rResampleOffset;
+
+	int iTimingOffsTrack;
+
+	ERecMode GetReceiverMode() { return eReceiverMode; }
+	ERecMode eReceiverMode;
+	EAcqStat GetAcquiState() { return eAcquiState; }
+	EAcqStat eAcquiState;
+	int iNumAudioFrames;
+
+	CVector <_BINARY> vecbiAudioFrameStatus;
+	_BOOLEAN bMeasurePSD;
+
+	/* vector to hold the PSD valued for the rpsd tag. */
+	CVector <_REAL> vecrPSD;
+
+	CMatrix <_COMPLEX> matcReceivedPilotValues;
+
+	/* Simulation ----------------------------------------------------------- */
+	CRawSimData RawSimDa;
+	ESimType eSimType;
+
+	int iDRMChannelNum;
+	int iSpecChDoppler;
+	_REAL rBitErrRate;
+	_REAL rSyncTestParam;		/* For any other simulations, used
+								   with "ST_SYNC_PARAM" type */
+	_REAL rSINR;
+	int iNumBitErrors;
+	int iChanEstDelay;
+
+	int iNumTaps;
+	vector<int> iPathDelay;
+	_REAL rGainCorr;
+	int iOffUsfExtr;
+
+	void SetSNR(const _REAL);
+	_REAL GetSNR();
+	void SetNominalSNRdB(const _REAL rSNRdBNominal);
+	_REAL GetNominalSNRdB();
+	void SetSystemSNRdB(const _REAL rSNRdBSystem)
+	{
+		rSysSimSNRdB = rSNRdBSystem;
+	}
+	_REAL GetSystemSNRdB() const
+	{
+		return rSysSimSNRdB;
+	}
+	_REAL GetSysSNRdBPilPos() const;
+
+	CReceiveStatus ReceiveStatus;
+	CFrontEndParameters FrontEndParameters;
+	CAltFreqSign AltFreqSign;
+
+	void Lock()
+	{
+		Mutex.Lock();
+	}
+	void Unlock()
+	{
+		Mutex.Unlock();
+	}
+	/* Channel Estimation */
+	_REAL rMER;
+	_REAL rWMERMSC;
+	_REAL rWMERFAC;
+	_REAL rSigmaEstimate;
+	_REAL rMinDelay;
+	_REAL rMaxDelay;
+
+	_BOOLEAN bMeasureDelay;
+	CRealVector vecrRdel;
+	CRealVector vecrRdelThresholds;
+	CRealVector vecrRdelIntervals;
+	_BOOLEAN bMeasureDoppler;
+	_REAL rRdop;
+	/* interference (constellation-based measurement rnic)*/
+	_BOOLEAN bMeasureInterference;
+	_REAL rIntFreq, rINR, rICR;
+
+	/* peak of PSD - for PSD-based interference measurement rnip */
+	_REAL rMaxPSDwrtSig;
+	_REAL rMaxPSDFreq;
+
+	/* the signal level as measured at IF by dream */
+	void SetIFSignalLevel(_REAL);
+	_REAL GetIFSignalLevel();
+	_REAL rSigStrengthCorrection;
+
+	/* General -------------------------------------------------------------- */
+	_REAL GetNominalBandwidth();
+	_REAL GetSysToNomBWCorrFact();
+	_BOOLEAN bRunThread;
+	_BOOLEAN bUsingMultimedia;
+
+	CCellMappingTable CellMappingTable;
+
+//	CGPSData GPSData;
+	CMinMaxMean SNRstat, SigStrstat;
+
+protected:
+
+	_REAL rSysSimSNRdB;
+
+	int iFrequency;
+	_BOOLEAN bValidSignalStrength;
+	_REAL rSigStr;
+	_REAL rIFSigStr;
+
+	/* Current selected audio service for processing */
+	int iCurSelAudioService;
+	int iCurSelDataService;
+
+	ERobMode eRobustnessMode;	/* E.g.: Mode A, B, C or D */
+	ESpecOcc eSpectOccup;
+
+	/* For resync to last service------------------------------------------- */
+	CLastService LastAudioService;
+	CLastService LastDataService;
+
+	CMutex Mutex;
+};
+
+#endif // !defined(PARAMETER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/SDC/SDC.h b/qsstv/drmtx/common/SDC/SDC.h
new file mode 100644
index 0000000..25e2c34
--- /dev/null
+++ b/qsstv/drmtx/common/SDC/SDC.h
@@ -0,0 +1,70 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001-2005
+ *
+ * Author(s):
+ *	Volker Fischer, Andrew Murphy
+ *
+ * Description:
+ *	See SDC.cpp
+ *
+ * 11/21/2005 Andrew Murphy, BBC Research & Development, 2005
+ *	- AMSS data entity groups (no AFS index), added eSDCType, data type 11
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(SDC_H__3B0BA660_CA63SDBOJKEWROBNER89NE877A0D312__INCLUDED_)
+#define SDC_H__3B0BA660_CA63SDBOJKEWROBNER89NE877A0D312__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../Parameter.h"
+#include "../util/CRC.h"
+#include "utils/vector.h"
+#include "../util/Utilities.h"
+
+/* Definitions ****************************************************************/
+/* Number of bits of header of SDC block */
+#define NUM_BITS_HEADER_SDC			12
+
+
+/* Classes ********************************************************************/
+class CSDCTransmit
+{
+public:
+	CSDCTransmit() {}
+	virtual ~CSDCTransmit() {}
+
+	void SDCParam(CVector<_BINARY>* pbiData, CParameter& Parameter);
+
+protected:
+	void DataEntityType0(CVector<_BINARY>& vecbiData, CParameter& Parameter);
+	void DataEntityType1(CVector<_BINARY>& vecbiData, int ServiceID,
+						 CParameter& Parameter);
+// ...
+	void DataEntityType5(CVector<_BINARY>& vecbiData, int ServiceID,
+						 CParameter& Parameter);
+// ...
+	void DataEntityType9(CVector<_BINARY>& vecbiData, int ServiceID,
+						 CParameter& Parameter);
+
+	CCRC CRCObject;
+};
+
+
+#endif // !defined(SDC_H__3B0BA660_CA63SDBOJKEWROBNER89NE877A0D312__INCLUDED_)
diff --git a/qsstv/drmtx/common/SDC/SDCTransmit.cpp b/qsstv/drmtx/common/SDC/SDCTransmit.cpp
new file mode 100644
index 0000000..0f53da4
--- /dev/null
+++ b/qsstv/drmtx/common/SDC/SDCTransmit.cpp
@@ -0,0 +1,556 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	SDC
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "SDC.h"
+
+
+/* Implementation *************************************************************/
+void CSDCTransmit::SDCParam(CVector<_BINARY>* pbiData, CParameter& Parameter)
+{
+/* 
+	Put SDC parameters on a stream 
+*/
+	int					i;
+	int					iSize;
+	CVector<_BINARY>	vecbiData;
+
+	/* Calculate length of data field in bytes 
+	   (consistant to table 61 in (6.4.1)) */
+	const int iLengthDataFieldBytes = 
+		(int) ((_REAL) (Parameter.iNumSDCBitsPerSFrame - 20) / 8);
+
+	/* 20 bits from AFS index and CRC */
+	const int iUsefulBitsSDC = 20 + iLengthDataFieldBytes * 8;
+
+	/* "- 20" for the AFS-index and CRC! */
+	const int iMaxNumBitsDataBlocks = iUsefulBitsSDC - 20;
+
+	/* Reset enqueue function */
+	(*pbiData).ResetBitAccess();
+
+
+	/* SDC Header ----------------------------------------------------------- */
+	/* AFS index (not used by this application, insert a "1" */
+	(*pbiData).Enqueue((uint32_t) 1, 4);
+
+
+	/* Data Entities -------------------------------------------------------- */
+	/* Init bit-count */
+	int iNumUsedBits = 0;
+
+// Choose types, TEST. Send only important types for this test!
+// TODO: test, if SDC block is long enough for all types!
+	/* Type 0 */
+	DataEntityType0(vecbiData, Parameter);
+
+	// TODO: nicer solution
+	iSize = vecbiData.Size();
+	if (iNumUsedBits + iSize < iMaxNumBitsDataBlocks)
+	{
+		iNumUsedBits += iSize;
+
+		vecbiData.ResetBitAccess();
+		for (i = 0; i < iSize; i++)
+			(*pbiData).Enqueue(vecbiData.Separate(1), 1);
+	}
+
+
+// Only working for either one audio or data service!
+if (Parameter.iNumAudioService == 1)
+{
+	/* Type 9 */
+	DataEntityType9(vecbiData, 0, Parameter);
+}
+else
+{
+	/* Type 5 */
+	DataEntityType5(vecbiData, 0, Parameter);
+}
+
+// TODO: nicer solution
+iSize = vecbiData.Size();
+if (iNumUsedBits + iSize < iMaxNumBitsDataBlocks)
+{
+	iNumUsedBits += iSize;
+
+	vecbiData.ResetBitAccess();
+	for (i = 0; i < iSize; i++)
+		(*pbiData).Enqueue(vecbiData.Separate(1), 1);
+}
+
+	/* Type 1 */
+	DataEntityType1(vecbiData, 0, Parameter);
+
+	// TODO: nicer solution
+	iSize = vecbiData.Size();
+	if (iNumUsedBits + iSize < iMaxNumBitsDataBlocks)
+	{
+		iNumUsedBits += iSize;
+
+		vecbiData.ResetBitAccess();
+		for (i = 0; i < iSize; i++)
+			(*pbiData).Enqueue(vecbiData.Separate(1), 1);
+	}
+
+
+	/* Zero-pad the unused bits in this SDC-block */
+	for (i = 0; i < iMaxNumBitsDataBlocks - iNumUsedBits; i++)
+		(*pbiData).Enqueue((uint32_t) 0, 1);
+
+
+	/* CRC ------------------------------------------------------------------ */
+	/* Calculate the CRC and put it at the end of the stream */
+	CRCObject.Reset(16);
+
+	(*pbiData).ResetBitAccess();
+
+	/* Special treatment of SDC data stream: The CRC (Cyclic Redundancy 
+	Check) field shall contain a 16-bit CRC calculated over the AFS 
+	index coded in an 8-bit field (4 msbs are 0) and the data field.
+	4 MSBs from AFS-index. Insert four "0" in the data-stream */
+	const _BYTE byFirstByte = (_BYTE) (*pbiData).Separate(4);
+	CRCObject.AddByte(byFirstByte);
+
+	for (i = 0; i < (iUsefulBitsSDC - 4) / SIZEOF__BYTE - 2; i++)
+		CRCObject.AddByte((_BYTE) (*pbiData).Separate(SIZEOF__BYTE));
+
+	/* Now, pointer in "enqueue"-function is back at the same place, 
+	   add CRC */
+	(*pbiData).Enqueue(CRCObject.GetCRC(), 16);
+}
+
+
+/******************************************************************************\
+* Data entity Type 0 (Multiplex description data entity)					   *
+\******************************************************************************/
+void CSDCTransmit::DataEntityType0(CVector<_BINARY>& vecbiData,
+								   CParameter& Parameter)
+{
+	/* 24 bits for each stream description + 4 bits for protection levels */
+	const int iNumBitsTotal = 4 + Parameter.GetTotNumServices() * 24;
+
+	/* Init return vector (storing this data block) */
+	vecbiData.Init(iNumBitsTotal + NUM_BITS_HEADER_SDC);
+	vecbiData.ResetBitAccess();
+
+	/* Length of the body, excluding the initial 4 bits ("- 4"), 
+	   measured in bytes ("/ 8") */
+	uint32_t iLengthInBytes = (iNumBitsTotal - 4) / 8;
+	vecbiData.Enqueue(iLengthInBytes, 7);
+
+	/* Version flag (not used in this implementation) */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Data entity type */
+	vecbiData.Enqueue((uint32_t) 00, 4); /* Type 00 */
+
+
+	/* Actual body ---------------------------------------------------------- */
+	/* Protection level for part A */
+	vecbiData.Enqueue((uint32_t) Parameter.MSCPrLe.iPartA, 2);
+
+	/* Protection level for part B */
+	vecbiData.Enqueue((uint32_t) Parameter.MSCPrLe.iPartB, 2);
+
+	for (size_t i = 0; i < Parameter.GetTotNumServices(); i++)
+	{
+		/* In case of hirachical modulation stream 0 describes the protection
+		   level and length of hirarchical data */
+		if ((i == 0) && 
+			((Parameter.eMSCCodingScheme == CS_3_HMSYM) ||
+			(Parameter.eMSCCodingScheme == CS_3_HMMIX)))
+		{
+			/* Protection level for hierarchical */
+			vecbiData.Enqueue((uint32_t) Parameter.MSCPrLe.iHierarch, 2);
+		
+			/* rfu */
+			vecbiData.Enqueue((uint32_t) 0, 10);
+	
+			/* Data length for hierarchical */
+			vecbiData.Enqueue((uint32_t) Parameter.Stream[i].iLenPartB, 12);
+		}
+		else
+		{
+			/* Data length for part A */
+			vecbiData.Enqueue((uint32_t) Parameter.Stream[i].iLenPartA, 12);
+		
+			/* Data length for part B */
+			vecbiData.Enqueue((uint32_t) Parameter.Stream[i].iLenPartB, 12);
+		}
+	}
+}
+
+
+/******************************************************************************\
+* Data entity Type 1 (Label data entity)									   *
+\******************************************************************************/
+void CSDCTransmit::DataEntityType1(CVector<_BINARY>& vecbiData, int ServiceID,
+								   CParameter& Parameter)
+{
+	int	iLenLabel;
+
+	/* Length of label. Label is a variable length field of up to 16 bytes
+	   defining the label using UTF-8 coding */
+	const int iLenLabelTmp = Parameter.Service[ServiceID].strLabel.length();
+	if (iLenLabelTmp > 16)
+		iLenLabel = 16;
+	else
+		iLenLabel = iLenLabelTmp;
+
+	/* Number in bits (* 8) plus initial 4 bits (+ 4) */
+	const int iNumBitsTotal = iLenLabel * 8 + 4;
+
+	/* Init return vector (storing this data block) */
+	vecbiData.Init(iNumBitsTotal + NUM_BITS_HEADER_SDC);
+	vecbiData.ResetBitAccess();
+
+
+	/**** Multiplex description data entity - type 1 ****/
+	/* Length of the body, excluding the initial 4 bits, 
+	   measured in bytes -> only number bytes of label */
+	vecbiData.Enqueue((uint32_t) iLenLabel, 7);
+
+	/* Version flag (not used in this implementation) */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Data entity type */
+	vecbiData.Enqueue((uint32_t) 01, 4); /* Type 01 */
+
+
+	/* Actual body ---------------------------------------------------------- */
+	/* Short Id */
+	vecbiData.Enqueue((uint32_t) ServiceID, 2);
+
+	/* rfu */
+	vecbiData.Enqueue((uint32_t) 0, 2);
+
+	/* Set all characters of label string */
+	for (int i = 0; i < iLenLabel; i++)
+	{
+		const char cNewChar = Parameter.Service[ServiceID].strLabel[i];
+
+		/* Set character */
+		vecbiData.Enqueue((uint32_t) cNewChar, 8);
+	}
+}
+
+
+/******************************************************************************\
+* Data entity Type 5 (Application information data entity)					   *
+\******************************************************************************/
+void CSDCTransmit::DataEntityType5(CVector<_BINARY>& vecbiData, int ServiceID,
+								   CParameter& Parameter)
+{
+	int	iNumBitsTotal = 0;
+
+	/* Set total number of bits */
+	switch (Parameter.Service[ServiceID].DataParam.ePacketModInd)
+	{
+	case CDataParam::PM_SYNCHRON_STR_MODE:
+		iNumBitsTotal = 12 + 16 /* TEST */ /* + application data TODO! */;
+		break;
+
+	case CDataParam::PM_PACKET_MODE:
+		iNumBitsTotal = 20 + 16 /* TEST */ /* + application data TODO! */;
+		break;
+	}
+
+	/* Init return vector (storing this data block) */
+	vecbiData.Init(iNumBitsTotal + NUM_BITS_HEADER_SDC);
+	vecbiData.ResetBitAccess();
+
+	/* Length of the body, excluding the initial 4 bits ("- 4"), 
+	   measured in bytes ("/ 8") */
+	vecbiData.Enqueue((uint32_t) (iNumBitsTotal - 4) / 8, 7);
+
+	/* Version flag (not used in this implementation) */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Data entity type */
+	vecbiData.Enqueue((uint32_t) 05, 4); /* Type 05 */
+
+
+	/* Actual body ---------------------------------------------------------- */
+	/* Short Id */
+	vecbiData.Enqueue((uint32_t) ServiceID, 2);
+
+	/* Stream Id */
+	vecbiData.Enqueue((uint32_t) Parameter.Service[ServiceID].DataParam.
+		iStreamID, 2);
+
+	/* Packet mode indicator */
+	switch (Parameter.Service[ServiceID].DataParam.ePacketModInd)
+	{
+	case CDataParam::PM_SYNCHRON_STR_MODE:
+		vecbiData.Enqueue(0 /* 0 */, 1);
+
+		/* Descriptor */
+		vecbiData.Enqueue((uint32_t) 0, 7);
+		break;
+
+	case CDataParam::PM_PACKET_MODE:
+		vecbiData.Enqueue(1 /* 1 */, 1);
+
+		/* Descriptor */
+		/* Data unit indicator */
+		switch (Parameter.Service[ServiceID].DataParam.eDataUnitInd)
+		{
+		case CDataParam::DU_SINGLE_PACKETS:
+			vecbiData.Enqueue(0 /* 0 */, 1);
+			break;
+
+		case CDataParam::DU_DATA_UNITS:
+			vecbiData.Enqueue(1 /* 1 */, 1);
+			break;
+		}
+
+		/* Packet Id */
+		vecbiData.Enqueue( 
+			(uint32_t) Parameter.Service[ServiceID].DataParam.iPacketID, 2);
+
+		/* Application domain */
+		switch (Parameter.Service[ServiceID].DataParam.eAppDomain)
+		{
+		case CDataParam::AD_DRM_SPEC_APP:
+			vecbiData.Enqueue(0 /* 0000 */, 4);
+			break;
+
+		case CDataParam::AD_DAB_SPEC_APP:
+			vecbiData.Enqueue(1 /* 0001 */, 4);
+			break;
+		default:
+			throw CGenErr("bad application domain in SDC preparation");
+		}
+
+		/* Packet length */
+		vecbiData.Enqueue( 
+			(uint32_t) Parameter.Service[ServiceID].DataParam.iPacketLen, 8);
+
+		break;
+	}
+
+	/* Application data */
+// Not used
+
+// TEST
+/* Fixed implementation for MOTSlideshow application which is the one and
+   only supported application right now. TODO */
+/* rfu */
+vecbiData.Enqueue((uint32_t) 0, 5);
+
+/* User application identifier. SlideShow = 2 */
+vecbiData.Enqueue((uint32_t) 2, 11);
+}
+
+
+/******************************************************************************\
+* Data entity Type 9 (Audio information data entity)						   *
+\******************************************************************************/
+void CSDCTransmit::DataEntityType9(CVector<_BINARY>& vecbiData, int ServiceID,
+								   CParameter& Parameter)
+{
+	/* Set total number of bits */
+	const int iNumBitsTotal = 20;
+
+	/* Init return vector (storing this data block) */
+	vecbiData.Init(iNumBitsTotal + NUM_BITS_HEADER_SDC);
+	vecbiData.ResetBitAccess();
+
+	/* Length of the body, excluding the initial 4 bits ("- 4"), 
+	   measured in bytes ("/ 8") */
+	vecbiData.Enqueue((uint32_t) (iNumBitsTotal - 4) / 8, 7);
+
+	/* Version flag (not used in this implementation) */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Data entity type */
+	vecbiData.Enqueue((uint32_t) 9, 4); /* Type 09 */
+
+
+	/* Actual body ---------------------------------------------------------- */
+	/* Short Id */
+	vecbiData.Enqueue((uint32_t) ServiceID, 2);
+
+	/* Stream Id */
+	vecbiData.Enqueue((uint32_t) Parameter.Service[ServiceID].AudioParam.
+		iStreamID, 2);
+
+	/* Audio coding */
+	switch (Parameter.Service[ServiceID].AudioParam.eAudioCoding)
+	{
+	case CAudioParam::AC_AAC:
+		vecbiData.Enqueue(0 /* 00 */, 2);
+		break;
+
+	case CAudioParam::AC_CELP:
+		vecbiData.Enqueue(1 /* 01 */, 2);
+		break;
+
+	case CAudioParam::AC_HVXC:
+		vecbiData.Enqueue(2 /* 10 */, 2);
+		break;
+	}
+
+	/* SBR flag */
+	switch (Parameter.Service[ServiceID].AudioParam.eSBRFlag)
+	{
+	case CAudioParam::SB_NOT_USED:
+		vecbiData.Enqueue(0 /* 0 */, 1);
+		break;
+
+	case CAudioParam::SB_USED:
+		vecbiData.Enqueue(1 /* 1 */, 1);
+		break;
+	}
+
+	/* Audio mode */
+	switch (Parameter.Service[ServiceID].AudioParam.eAudioCoding)
+	{
+	case CAudioParam::AC_AAC:
+		/* Channel type */
+		switch (Parameter.Service[ServiceID].AudioParam.eAudioMode)
+		{
+		case CAudioParam::AM_MONO:
+			vecbiData.Enqueue(0 /* 00 */, 2);
+			break;
+
+		case CAudioParam::AM_P_STEREO:
+			vecbiData.Enqueue(1 /* 01 */, 2);
+			break;
+
+		case CAudioParam::AM_STEREO:
+			vecbiData.Enqueue(2 /* 10 */, 2);
+			break;
+		}
+		break;
+
+	case CAudioParam::AC_CELP:
+		/* rfa */
+		vecbiData.Enqueue((uint32_t) 0, 1);
+
+		/* CELP_CRC */
+		switch (Parameter.Service[ServiceID].AudioParam.bCELPCRC)
+		{
+		case false:
+			vecbiData.Enqueue(0 /* 0 */, 1);
+			break;
+
+		case true:
+			vecbiData.Enqueue(1 /* 1 */, 1);
+			break;
+		}
+		break;
+
+	case CAudioParam::AC_HVXC:
+		/* HVXC_rate */
+		switch (Parameter.Service[ServiceID].AudioParam.eHVXCRate)
+		{
+		case CAudioParam::HR_2_KBIT:
+			vecbiData.Enqueue(0 /* 0 */, 1);
+			break;
+
+		case CAudioParam::HR_4_KBIT:
+			vecbiData.Enqueue(1 /* 1 */, 1);
+			break;
+		}
+
+		/* HVXC CRC */
+		switch (Parameter.Service[ServiceID].AudioParam.bHVXCCRC)
+		{
+		case false:
+			vecbiData.Enqueue(0 /* 0 */, 1);
+			break;
+
+		case true:
+			vecbiData.Enqueue(1 /* 1 */, 1);
+			break;
+		}
+		break;
+	}
+
+	/* Audio sampling rate */
+	switch (Parameter.Service[ServiceID].AudioParam.eAudioSamplRate)
+	{
+	case CAudioParam::AS_8_KHZ:
+		vecbiData.Enqueue(0 /* 000 */, 3);
+		break;
+
+	case CAudioParam::AS_12KHZ:
+		vecbiData.Enqueue(1 /* 001 */, 3);
+		break;
+
+	case CAudioParam::AS_16KHZ:
+		vecbiData.Enqueue(2 /* 010 */, 3);
+		break;
+
+	case CAudioParam::AS_24KHZ:
+		vecbiData.Enqueue(3 /* 011 */, 3);
+		break;
+	}
+
+	/* Text flag */
+	switch (Parameter.Service[ServiceID].AudioParam.bTextflag)
+	{
+	case false:
+		vecbiData.Enqueue(0 /* 0 */, 1);
+		break;
+
+	case true:
+		vecbiData.Enqueue(1 /* 1 */, 1);
+		break;
+	}
+
+	/* Enhancement flag */
+	switch (Parameter.Service[ServiceID].AudioParam.bEnhanceFlag)
+	{
+	case false:
+		vecbiData.Enqueue(0 /* 0 */, 1);
+		break;
+
+	case true:
+		vecbiData.Enqueue(1 /* 1 */, 1);
+		break;
+	}
+
+	/* Coder field */
+	if (Parameter.Service[ServiceID].AudioParam.
+		eAudioCoding == CAudioParam::AC_CELP)
+	{
+		/* CELP index */
+		vecbiData.Enqueue( 
+			(uint32_t) Parameter.Service[ServiceID].AudioParam.iCELPIndex, 5);
+	}
+	else
+	{
+		/* rfa 5 bit */
+		vecbiData.Enqueue((uint32_t) 0, 5);
+	}
+	
+	/* rfa 1 bit */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+}
diff --git a/qsstv/drmtx/common/csoundout.cpp b/qsstv/drmtx/common/csoundout.cpp
new file mode 100644
index 0000000..bdc8e68
--- /dev/null
+++ b/qsstv/drmtx/common/csoundout.cpp
@@ -0,0 +1,25 @@
+#include "csoundout.h"
+#include "qsstvglobal.h"
+#include "dsp/synthes.h"
+#include "utils/vector.h"
+
+CSoundOut::CSoundOut()
+{
+}
+
+CSoundOut::~CSoundOut()
+{
+}
+
+
+//void CSoundOut::Init(int iNewBufferSize, bool bNewBlocking)
+//{
+//}
+
+bool CSoundOut::Write(CVector< _SAMPLE >& psData)
+{
+//  addToLog(QString("csize start %1").arg(psData.Size()/2),LOGSOUND);
+  synthesPtr->writeBuffer((quint32 *)&psData[0],psData.Size()/2);
+//  addToLog(QString("csize end %1").arg(psData.Size()/2),LOGSOUND);
+  return false;
+}
diff --git a/qsstv/drmtx/common/csoundout.h b/qsstv/drmtx/common/csoundout.h
new file mode 100644
index 0000000..96428e2
--- /dev/null
+++ b/qsstv/drmtx/common/csoundout.h
@@ -0,0 +1,16 @@
+#ifndef CSOUNDOUT_H
+#define CSOUNDOUT_H
+#include "GlobalDefinitions.h"
+#include "soundinterface.h"
+
+class CSoundOut: public CSoundOutInterface
+{
+public:
+  CSoundOut();
+  ~CSoundOut();
+//  void Init(int iNewBufferSize, bool bNewBlocking);
+  bool Write(CVector< _SAMPLE >& psData);
+
+};
+
+#endif // CSOUNDOUT_H
diff --git a/qsstv/drmtx/common/datadecoding/DABMOT.cpp b/qsstv/drmtx/common/datadecoding/DABMOT.cpp
new file mode 100644
index 0000000..a0ed9c8
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/DABMOT.cpp
@@ -0,0 +1,1923 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer, Andrea Russo, Doyle Richard, Julian Cable
+ *
+ * Adapted for ham sstv use Ties Bos PA0MBO
+ *
+ * Description:
+ *	DAB MOT interface implementation
+ *
+ * 12/22/2003 Doyle Richard
+ *	- Header extension decoding
+ * 10/13/2005 Andrea Russo
+ *	- Broadcast WebSite application
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "DABMOT.h"
+#include "../util/Utilities.h"
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <iostream>
+#include "drmtx/drmtransmitter.h"
+
+#if HAVE_LIBZ
+#include <zlib.h>
+#else
+#ifdef HAVE_LIBFREEIMAGE
+# include <FreeImage.h>
+#endif
+#endif
+
+#define RUNINLEN 15
+#define RUNOUTLEN 10
+
+static int bsrTransportId=2;
+
+using namespace std; 
+/* Implementation *************************************************************/
+ostream & operator<<(ostream & out, CDateAndTime & d)
+{
+	d.dump(out);
+	return out;
+}
+
+ostream & operator<<(ostream & out, CMOTObject & o)
+{
+	o.dump(out);
+	return out;
+}
+
+ostream & operator<<(ostream & out, CMOTDirectory & o)
+{
+	o.dump(out);
+	return out;
+}
+
+/******************************************************************************\
+* Encoder                                                                      *
+\******************************************************************************/
+
+void CMOTDABEnc::SetMOTObject(CMOTObject & NewMOTObject,int bytesAvailable)
+{
+  size_t i;
+  size_t k;
+  CMOTObjectRaw MOTObjectRaw;
+  unsigned char xorfname , addfname ;
+
+  /* Get some necessary parameters of object */
+  const int iPicSizeBits = NewMOTObject.vecbRawData.Size();
+  const int iPicSizeBytes = iPicSizeBits / SIZEOF__BYTE;
+  const string strFileName = NewMOTObject.strName;
+
+  /* File name size is restricted (in this implementation) to 128 (2^7) bytes.
+     If file name is longer, cut it. TODO: better solution: set Ext flag in
+     "ContentName" header extension to allow larger file names */
+  size_t iFileNameSize = strFileName.size();
+  if (strFileName=="bsr.bin")
+    {
+      bsrTransportId++;
+      bsrTransportId=bsrTransportId%3;
+      txTransportID=bsrTransportId;
+    }
+  else
+    {
+      if (iFileNameSize > 80)    // was 128
+        iFileNameSize = 80;   //was 128
+
+      // printf("Binnenkomst SetMOTObject file %s \n", strFileName.c_str());
+      if (iFileNameSize ==0 )
+        return;
+
+      /* special hamcode */
+      xorfname = 0;
+      addfname = 0;
+      for (k=0; k < iFileNameSize ; k++)
+        {
+          xorfname ^= strFileName[k];
+          addfname += strFileName[k];
+          addfname ^= (unsigned char) k ;
+        }
+      txTransportID = 256 * (int)addfname + (int) xorfname ;
+      if (txTransportID <= 2) txTransportID += iFileNameSize;
+    }
+
+  /* end special hamcode */
+
+  /* Copy actual raw data of object */
+  MOTObjectRaw.Body.Init(iPicSizeBits);
+  MOTObjectRaw.Body = NewMOTObject.vecbRawData;
+
+  /* Get content type and content sub type of object. We use the format string
+     to get these informations about the object */
+  int iContentType = 2;		/* Set default value (general data)  was 0 */
+  int iContentSubType = 1;	/* Set default value (jpg)  was 0 */
+
+  /* Get ending string which declares the type of the file. Make lowercase */
+
+  string strFormat;
+#if defined(_MSC_VER) && (_MSC_VER < 1400)
+  strFormat = _strlwr(_strdup(NewMOTObject.strFormat.c_str()));
+#else
+  transform(NewMOTObject.strFormat.begin(), NewMOTObject.strFormat.end(),
+            strFormat.begin(), (int (*)(int)) tolower);
+#endif
+  // printf("DABMOT strFormat is %s \n", strFormat.c_str());
+  /* gif: 0, image: 2 */
+  if (strcmp(strFormat.c_str(), "gif") == 0)
+    {
+      iContentType = 2;
+      iContentSubType = 0;
+    }
+
+  /* jfif: 1, image: 2. Possible endings: jpg, jpeg, jfif  jp2 added sep 14 2012 */
+  if (( strcmp(strFormat.c_str(), "jpg")==0 ) ||
+      (strcmp(strFormat.c_str(), "jpeg") == 0) ||
+      (strcmp(strFormat.c_str(), "rs1") == 0) ||
+      (strcmp(strFormat.c_str(), "rs2") == 0) ||
+      (strcmp(strFormat.c_str(), "rs3") == 0) ||
+      (strcmp(strFormat.c_str(), "rs4") == 0) ||
+      (strcmp(strFormat.c_str(), "jp2") == 0) ||
+      (strcmp(strFormat.c_str(), "jfif") == 0))
+    {
+      iContentType = 2;
+      iContentSubType = 1;
+    }
+
+  /* bmp: 2, image: 2 */
+  if (strcmp(strFormat.c_str(),"bmp") == 0)
+    {
+      iContentType = 2;
+      iContentSubType = 2;
+    }
+
+  /* png: 3, image: 2 */
+  if (strcmp(strFormat.c_str(),"png") == 0)
+    {
+      iContentType = 2;
+      iContentSubType = 3;
+    }
+  // printf("na de ifs iContentType is %d iContentSubType is %d \n", iContentType, iContentSubType);
+  /* Header --------------------------------------------------------------- */
+  /* Header size (including header extension) */
+  const int iHeaderSize = 7 /* Header core  */  +
+      5 /* TriggerTime */  +
+      3 + iFileNameSize /* ContentName (header + actual name) */  +
+      2 /* VersionNumber */ ;
+
+  /* Allocate memory and reset bit access */
+  MOTObjectRaw.Header.Init(iHeaderSize * SIZEOF__BYTE);
+  MOTObjectRaw.Header.ResetBitAccess();
+
+  // printf("DABMOT  iHeaderSize %d \n", iHeaderSize);
+
+  /* BodySize: This 28-bit field, coded as an unsigned binary number,
+     indicates the total size of the body in bytes */
+  MOTObjectRaw.Header.Enqueue((uint32_t) iPicSizeBytes, 28);
+
+  /* HeaderSize: This 13-bit field, coded as an unsigned binary number,
+     indicates the total size of the header in bytes */
+  MOTObjectRaw.Header.Enqueue((uint32_t) iHeaderSize, 13);
+
+  /* ContentType: This 6-bit field indicates the main category of the body's
+     content */
+  MOTObjectRaw.Header.Enqueue((uint32_t) iContentType, 6);
+
+  /* ContentSubType: This 9-bit field indicates the exact type of the body's
+     content depending on the value of the field ContentType */
+  MOTObjectRaw.Header.Enqueue((uint32_t) iContentSubType, 9);
+
+  /* Header extension ----------------------------------------------------- */
+  /* MOT Slideshow application: Only the MOT parameter ContentName is
+     mandatory and must be used for each slide object that will be handled by
+     the MOT decoder and the memory management of the Slide Show terminal */
+
+  /* TriggerTime: This parameter specifies the time for when the presentation
+     takes place. The TriggerTime activates the object according to its
+     ContentType. The value of the parameter field is coded in the UTC
+     format */
+
+  /* PLI (Parameter Length Indicator): This 2-bit field describes the total
+     length of the associated parameter. In this case:
+     1 0 total parameter length = 5 bytes; length of DataField is 4 bytes */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 2, 2);
+
+  /* ParamId (Parameter Identifier): This 6-bit field identifies the
+     parameter. 1 0 1 (dec: 5) -> TriggerTime */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 5, 6);
+
+  /* Validity flag = 0: "Now", MJD and UTC shall be ignored and be set to 0.
+     Set MJD and UTC to zero. UTC flag is also zero -> short form */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 0, 32);
+
+  /* VersionNumber: If several versions of an object are transferred, this
+     parameter indicates its VersionNumber. The parameter value is coded as an
+     unsigned binary number, starting at 0 and being incremented by 1 modulo
+     256 each time the version changes. If the VersionNumber differs, the
+     content of the body was modified */
+  /* PLI
+     0 1 total parameter length = 2 bytes, length of DataField is 1 byte */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 1, 2);
+
+  /* ParamId (Parameter Identifier): This 6-bit field identifies the
+     parameter. 1 1 0 (dec: 6) -> VersionNumber */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 6, 6);
+
+  /* Version number data field */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 0, 8);
+
+  /* ContentName: The DataField of this parameter starts with a one byte
+     field, comprising a 4-bit character set indicator (see table 3) and a
+     4-bit Rfa field. The following character field contains a unique name or
+     identifier for the object. The total number of characters is determined
+     by the DataFieldLength indicator minus one byte */
+
+  /* PLI
+     1 1 total parameter length depends on the DataFieldLength indicator */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 3, 2);
+
+  /* ParamId (Parameter Identifier): This 6-bit field identifies the
+     parameter. 1 1 0 0 (dec: 12) -> ContentName */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 12, 6);
+
+  /* Ext (ExtensionFlag): This 1-bit field specifies the length of the
+     DataFieldLength Indicator.
+     0: the total parameter length is derived from the next 7 bits */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 0, 1);
+
+  /* DataFieldLength Indicator: This field specifies as an unsigned binary
+     number the length of the parameter's DataField in bytes. The length of
+     this field is either 7 or 15 bits, depending on the setting of the
+     ExtensionFlag */
+  MOTObjectRaw.Header.Enqueue((uint32_t) (1 /* header */  +
+                                          iFileNameSize	/* actual data */), 7);
+  // printf("iFileNameSize is %d\n", iFileNameSize);
+  // if (iFileNameSize ==0) return ;
+  /* Character set indicator (0 0 0 0 complete EBU Latin based repertoire) */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 0, 4);
+
+  /* Rfa 4 bits */
+  MOTObjectRaw.Header.Enqueue((uint32_t) 0, 4);
+
+  /* Character field */
+  for (i = 0; i < iFileNameSize; i++)
+    MOTObjectRaw.Header.Enqueue((uint32_t) strFileName[i], 8);
+
+  /* Generate segments ---------------------------------------------------- */
+  /* Header (header should not be partitioned! TODO) */
+  const int iPartiSizeHeader = 98;	/* Bytes */// TEST was 100 pa0mbo
+  // printf("Voor partitioning header \n");
+  PartitionUnits(MOTObjectRaw.Header, MOTObjSegments.vvbiHeader,iPartiSizeHeader);
+  /* Body */
+  //ON4QZ change to better fit in NumDecodeBits
+  PartitionUnits(MOTObjectRaw.Body, MOTObjSegments.vvbiBody, bytesAvailable-14);
+}
+
+
+void CMOTDABEnc::PartitionUnits(CVector < _BINARY > &vecbiSource, CVector < CVector < _BINARY > >&vecbiDest, const int iPartiSize)
+{
+  int i, j;
+  int iActSegSize;
+
+  /* Divide the generated units in partitions */
+  const int iSourceSize = vecbiSource.Size() / SIZEOF__BYTE;
+  const int iNumSeg = (int) ceil((_REAL) iSourceSize / iPartiSize);	/* Bytes */
+  iNumSegStore = iNumSeg;
+  const int iSizeLastSeg = iSourceSize -
+                           (int) floor((_REAL) iSourceSize / iPartiSize) * iPartiSize;
+
+  /*      printf("In PartitionUnits  iParti %d  iSource %d iNumSeg %d iSizlast %d\n",
+                  iPartiSize, iSourceSize, iNumSeg, iSizeLastSeg); */
+
+  /* Init memory for destination vector, reset bit access of source */
+  vecbiDest.Init(iNumSeg);
+  vecbiSource.ResetBitAccess();
+
+  for (i = 0; i < iNumSeg; i++)
+    {
+      /* All segments except the last one must have the size
+       "iPartSizeHeader". If "iSizeLastSeg" is =, the source data size is
+       a multiple of the partitions size. In this case, all units have
+       the same size (-> "|| (iSizeLastSeg == 0)") */
+      if ((i < iNumSeg - 1) || (iSizeLastSeg == 0))
+        iActSegSize = iPartiSize;
+      else
+        iActSegSize = iSizeLastSeg;
+
+      /* Add segment data ------------------------------------------------- */
+      /* Header */
+      /* Allocate memory for body data and segment header bits (16) */
+      vecbiDest[i].Init(iActSegSize * SIZEOF__BYTE + 16);
+      vecbiDest[i].ResetBitAccess();
+
+      /* Segment header */
+      /* RepetitionCount: This 3-bit field indicates, as an unsigned
+       binary number, the remaining transmission repetitions for the
+       current object.
+       In our current implementation, no repetitions used. TODO */
+      vecbiDest[i].Enqueue((uint32_t) 0, 3);
+
+      /* SegmentSize: This 13-bit field, coded as an unsigned binary
+       number, indicates the size of the segment data field in bytes */
+      vecbiDest[i].Enqueue((uint32_t) iActSegSize, 13);
+
+      /* Body */
+      for (j = 0; j < iActSegSize * SIZEOF__BYTE; j++)
+        vecbiDest[i].Enqueue(vecbiSource.Separate(1), 1);
+    }
+}
+
+
+
+void CMOTDABEnc::GenMOTObj(CVector < _BINARY > &vecbiData, CVector < _BINARY > &vecbiSeg, const _BOOLEAN bHeader,  const int iSegNum, const int iTranspID, const _BOOLEAN bLastSeg)
+{
+	int i;
+	CCRC CRCObject;
+        // printf("GenMOTObj iTranspID %d SIZEOF__BYTE %d\n", iTranspID, SIZEOF__BYTE);
+	/* Standard settings for this implementation */
+	const _BOOLEAN bCRCUsed = true;	/* CRC */
+	const _BOOLEAN bSegFieldUsed = true;	/* segment field */
+	const _BOOLEAN bUsAccFieldUsed = true;	/* user access field */
+	const _BOOLEAN bTransIDFieldUsed = true;	/* transport ID field */
+        unsigned char testdata[10000];
+        double ownchecksum;
+
+/* Total length of object in bits */
+	int iTotLenMOTObj = 16 /* group header */ ;
+	if (bSegFieldUsed == true)
+		iTotLenMOTObj += 16;
+	if (bUsAccFieldUsed == true)
+	{
+		iTotLenMOTObj += 8;
+		if (bTransIDFieldUsed == true)
+			iTotLenMOTObj += 16;
+	}
+        // printf("DABMOT vecbiSrg.size %d\n", vecbiSeg.Size());
+	iTotLenMOTObj += vecbiSeg.Size();
+	if (bCRCUsed == true)
+		iTotLenMOTObj += 16;
+
+	/* Init data vector */
+	vecbiData.Init(iTotLenMOTObj);
+	vecbiData.ResetBitAccess();
+
+	/* MSC data group header ------------------------------------------------ */
+	/* Extension flag: this 1-bit flag shall indicate whether the extension
+	   field is present, or not. Not used right now -> 0 */
+	vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* CRC flag: this 1-bit flag shall indicate whether there is a CRC at the
+	   end of the MSC data group */
+	if (bCRCUsed == true)
+		vecbiData.Enqueue((uint32_t) 1, 1);
+	else
+		vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Segment flag: this 1-bit flag shall indicate whether the segment field is
+	   present, or not */
+	if (bSegFieldUsed == true)
+		vecbiData.Enqueue((uint32_t) 1, 1);
+	else
+		vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* User access flag: this 1-bit flag shall indicate whether the user access
+	   field is present, or not. We always use this field -> 1 */
+	if (bUsAccFieldUsed == true)
+		vecbiData.Enqueue((uint32_t) 1, 1);
+	else
+		vecbiData.Enqueue((uint32_t) 0, 1);
+
+	/* Data group type: this 4-bit field shall define the type of data carried
+	   in the data group data field. Data group types:
+	   3: MOT header information
+	   4: MOT data */
+	if (bHeader == true)
+		vecbiData.Enqueue((uint32_t) 3, 4);
+	else
+		vecbiData.Enqueue((uint32_t) 4, 4);
+
+	/* Continuity index: the binary value of this 4-bit field shall be
+	   incremented each time a MSC data group of a particular type, with a
+	   content different from that of the immediately preceding data group of
+	   the same type, is transmitted */
+	if (bHeader == true)
+	{
+		vecbiData.Enqueue((uint32_t) iContIndexHeader, 4);
+
+		/* Increment modulo 16 */
+		iContIndexHeader++;
+		if (iContIndexHeader == 16)
+			iContIndexHeader = 0;
+	}
+	else
+	{
+		vecbiData.Enqueue((uint32_t) iContIndexBody, 4);
+
+		/* Increment modulo 16 */
+		iContIndexBody++;
+		if (iContIndexBody == 16)
+			iContIndexBody = 0;
+	}
+
+	/* Repetition index: the binary value of this 4-bit field shall signal the
+	   remaining number of repetitions of a MSC data group with the same data
+	   content, occurring in successive MSC data groups of the same type.
+	   No repetition used in this implementation right now -> 0, TODO */
+	vecbiData.Enqueue((uint32_t) 0, 4);
+
+	/* Extension field: this 16-bit field shall be used to carry the Data Group
+	   Conditional Access (DGCA) when general data or MOT data uses conditional
+	   access (Data group types 0010 and 0101, respectively). The DGCA contains
+	   the Initialization Modifier (IM) and additional Conditional Access (CA)
+	   information. For other Data group types, the Extension field is reserved
+	   for future additions to the Data group header.
+	   Extension field is not used in this implementation! */
+
+	/* Session header ------------------------------------------------------- */
+	/* Segment field */
+	if (bSegFieldUsed == true)
+	{
+		/* Last: this 1-bit flag shall indicate whether the segment number field
+		   is the last or whether there are more to be transmitted */
+		if (bLastSeg == true)
+			vecbiData.Enqueue((uint32_t) 1, 1);
+		else
+			vecbiData.Enqueue((uint32_t) 0, 1);
+
+		/* Segment number: this 15-bit field, coded as an unsigned binary number
+		   (in the range 0 to 32767), shall indicate the segment number.
+		   NOTE: The first segment is numbered 0 and the segment number is
+		   incremented by one at each new segment */
+                // printf("GenMOTObj blastSeg %d, iSegNum %d \n", bLastSeg, iSegNum);
+		vecbiData.Enqueue((uint32_t) iSegNum, 15);
+	}
+
+	/* User access field */
+	if (bUsAccFieldUsed == true)
+	{
+		/* Rfa (Reserved for future addition): this 3-bit field shall be
+		   reserved for future additions */
+		vecbiData.Enqueue((uint32_t) 0, 3);
+
+		/* Transport Id flag: this 1-bit flag shall indicate whether the
+		   Transport Id field is present, or not */
+		if (bTransIDFieldUsed == true)
+			vecbiData.Enqueue((uint32_t) 1, 1);
+		else
+			vecbiData.Enqueue((uint32_t) 0, 1);
+
+		/* Length indicator: this 4-bit field, coded as an unsigned binary
+		   number (in the range 0 to 15), shall indicate the length n in bytes
+		   of the Transport Id and End user address fields.
+		   We do not use end user address field, only transport ID -> 2 */
+		if (bTransIDFieldUsed == true)
+			vecbiData.Enqueue((uint32_t) 2, 4);
+		else
+			vecbiData.Enqueue((uint32_t) 0, 4);
+
+		/* Transport Id (Identifier): this 16-bit field shall uniquely identify
+		   one data object (file and header information) from a stream of such
+		   objects, It may be used to indicate the object to which the
+		   information carried in the data group belongs or relates */
+		if (bTransIDFieldUsed == true)
+			vecbiData.Enqueue((uint32_t) iTranspID, 16);
+	}
+
+	/* MSC data group data field -------------------------------------------- */
+	vecbiSeg.ResetBitAccess();
+
+	for (i = 0; i < vecbiSeg.Size(); i++)
+		vecbiData.Enqueue(vecbiSeg.Separate(1), 1);
+
+	/* MSC data group CRC --------------------------------------------------- */
+	/* The data group CRC shall be a 16-bit CRC word calculated on the data
+	   group header, the session header and the data group data field. The
+	   generation shall be based on the ITU-T Recommendation X.25.
+	   At the beginning of each CRC word calculation, all shift register stage
+	   contents shall be initialized to "1". The CRC word shall be complemented
+	   (1's complement) prior to transmission */
+	if (bCRCUsed == true)
+	{
+		/* Reset bit access */
+
+		vecbiData.ResetBitAccess();
+
+		/* Calculate the CRC and put it at the end of the segment */
+		CRCObject.Reset(16);
+                 // printf("iTotLenMOTObj = %d \n", iTotLenMOTObj);
+		/* "byLengthBody" was defined in the header */
+		for (i = 0; i < ((iTotLenMOTObj / SIZEOF__BYTE) - 2) /* CRC */ ; i++)  
+			CRCObject.AddByte((_BYTE) vecbiData.Separate(SIZEOF__BYTE));
+
+		/* Now, pointer in "enqueue"-function is back at the same place,
+		   add CRC */
+		vecbiData.Enqueue(CRCObject.GetCRC(), 16);
+                // printf("sending crc16 from DABMOT %x  iSegNum %d \n", CRCObject.GetCRC(), iSegNum);
+
+		vecbiData.ResetBitAccess();
+
+                /* extra debugging code pa0mbo */
+		vecbiData.ResetBitAccess();
+		CRCObject.Reset(16);
+		for (i = 0; i < ((iTotLenMOTObj / SIZEOF__BYTE))   ; i++)  
+                {
+		  testdata[i]  = ((_BYTE) vecbiData.Separate(SIZEOF__BYTE));
+                  CRCObject.AddByte(testdata[i]) ;
+                //  printf(" %d  %x\n", i, testdata[i]);
+                }
+                CRCObject.crc16_bytewise( &ownchecksum, testdata, iTotLenMOTObj/SIZEOF__BYTE); // ON4QZ divided by SIZEOF__BYTE
+               //  printf("Own CRC is %x \n", (int)ownchecksum);
+               //  printf("crc16 from DABMOT %x  iSegNum %d \n", CRCObject.GetCRC(), iSegNum);
+                /*    */
+                vecbiData.ResetBitAccess();
+                
+
+	}
+}
+
+
+void CMOTDABEnc::prepareSegmentList(unsigned int repetition) // ON4QZ
+{
+  int i,k,m,counter;
+  unsigned int j;
+  int numHeaderSegments=MOTObjSegments.vvbiHeader.Size();
+  int numBodySegments=MOTObjSegments.vvbiBody.Size();
+  segmentList.clear();
+  for(j=0;j<repetition;j++)
+    {
+      // Fill in segmentHeeder for RUNIN
+      for(counter=0;counter<RUNINLEN;)
+        {
+          for(k=0;k<numHeaderSegments;k++)
+            {
+              segmentList.append(-1-k);  // negative numbers indicate header
+            }
+          segmentList.append(numBodySegments-1); counter++;// add lastSegment
+          for(i=0;((i<numBodySegments) && (counter<RUNINLEN));i++)
+            {
+              segmentList.append(i); counter++;
+            }
+        }
+      //setup body
+      // fixBlocklist contains the segments to be send
+      // if count==0 then send all segments
+      if(fixBlockList.count()==0)
+        {
+          for(counter=0;counter<numBodySegments;counter++)
+            {
+
+              if(counter%50==0)
+                {
+                  for(k=0;k<numHeaderSegments;k++)
+                    {
+                      segmentList.append(-1-k);
+                    }
+                  segmentList.append(numBodySegments-1); // add lastSegment
+                }
+              segmentList.append(counter);
+            }
+        }
+      else
+        {
+          for(m=0;m<fixBlockList.count();m++)
+            {
+              if(counter%30==0)
+                {
+                  for(k=0;k<numHeaderSegments;k++)
+                    {
+                      segmentList.append(-1-k);
+                    }
+                  segmentList.append(numBodySegments-1); // add lastSegment
+                }
+              segmentList.append(fixBlockList.at(m));
+            }
+        }
+
+      for(counter=0;counter<RUNOUTLEN;)
+        {
+          for(k=0;k<numHeaderSegments;k++)
+            {
+              segmentList.append(-1-k);
+            }
+          segmentList.append(numBodySegments-1); counter++;// add lastSegment
+          for(i=0;((i<numBodySegments) && (counter<RUNOUTLEN));i++)
+            {
+              segmentList.append(i); counter++;
+            }
+        }
+    }
+  segmentListIdx=0;
+  numTxFrames=segmentList.size();
+}
+
+
+_BOOLEAN CMOTDABEnc::GetDataGroup(CVector < _BINARY > &vecbiNewData)
+{
+  bool bLastSegment;
+  int segmentNumber;
+  int headerNumber;
+
+  segmentNumber=segmentList.at(segmentListIdx);
+  if(segmentNumber<0)
+    {
+      //its a header
+      headerNumber=-1-segmentNumber;
+      if((headerNumber+1)==MOTObjSegments.vvbiHeader.Size()) bLastSegment=true;
+      else bLastSegment=false;
+      GenMOTObj(vecbiNewData, MOTObjSegments.vvbiHeader[headerNumber],true, headerNumber, txTransportID, bLastSegment);
+    }
+  else
+    {
+      //its a body segment
+      if ((segmentNumber+1) == MOTObjSegments.vvbiBody.Size()) bLastSegment = true;
+      else  bLastSegment = false;
+      GenMOTObj(vecbiNewData,MOTObjSegments.vvbiBody[segmentNumber], false,segmentNumber, txTransportID, bLastSegment);
+    }
+  segmentListIdx++;
+  if(segmentListIdx==segmentList.count())
+    {
+      segmentListIdx=0;
+      return true;
+    }
+ return false;
+}
+
+//_BOOLEAN CMOTDABEnc::GetDataGroup(CVector < _BINARY > &vecbiNewData)
+//{
+//  _BOOLEAN bLastSegment;
+
+
+//  /* Init return value. Is overwritten in case the object is ready */
+//  _BOOLEAN bObjectDone = false;
+//  if (bFirstRound == true)
+//    {
+//      if (bCurSegHeader == true)
+//        {
+//          /* Check if this is last segment */
+//          if (iSegmCntHeader == MOTObjSegments.vvbiHeader.Size() - 1) bLastSegment = true;
+//          else  bLastSegment = false;
+
+//          /* Generate MOT object for header */
+//          GenMOTObj(vecbiNewData, MOTObjSegments.vvbiHeader[iSegmCntHeader],true, iSegmCntHeader, txTransportID, bLastSegment);
+//          addToLog(QString("FirstRound iSegmCntheader %1 blastseg %2 transportID %3").arg(iSegmCntHeader).arg(bLastSegment).arg(txTransportID),LOGDRMTXMOT);
+//          iSegmCntHeader++;
+//          if (iSegmCntHeader == MOTObjSegments.vvbiHeader.Size())
+//            {
+//              iSegmCntBody = 0; // Reset counter for body
+//              bCurSegHeader = false; // Header is ready, transmit body now
+//            }
+//        }
+//      else
+//        {
+
+
+//          /* Check that body size is not zero */
+//          if (iSegmCntBody < MOTObjSegments.vvbiBody.Size())
+//            {
+//              /* Check if this is last segment */
+//              if (iSegmCntBody ==  RUNINLEN ) bLastSegment = true;
+//              else bLastSegment = false;
+
+//              if (bLastSegment)
+//                {
+//                  /* Generate MOT object for Body */
+//                  GenMOTObj(vecbiNewData,MOTObjSegments.vvbiBody[iNumSegStore-1], false,iNumSegStore-1, txTransportID, bLastSegment);
+//                  addToLog(QString("FirstRound Generated MOTObj for seg %1 pseudolast").arg(iNumSegStore-1),LOGDRMTXMOT);
+//                  iSegmCntBody= 0;
+//                  iSegmCntHeader =0;
+//                  bCurSegHeader = true;
+//                  bFirstRound = false ;
+//                }
+//              else
+//                {
+//                  /* Generate MOT object for Body */
+//                  GenMOTObj(vecbiNewData,MOTObjSegments.vvbiBody[iSegmCntBody], false,iSegmCntBody, txTransportID, bLastSegment);
+//                  addToLog(QString("FirstRound Generated MOTObj for seg %1 ").arg(iSegmCntBody),LOGDRMTXMOT);
+//                }
+//              iSegmCntBody++;
+//            }
+
+//          if (iSegmCntBody == MOTObjSegments.vvbiBody.Size())
+//            {
+//                iSegmCntHeader = 0; // Reset counter for header
+//                bCurSegHeader = true; // Body is ready, transmit header from next object
+//                bFirstRound = false ;
+//            }
+//          runInCnt++;
+//        }
+//    }
+//  else // not first run
+//    {
+//      if (bCurSegHeader == true)
+//        {
+
+//          if (iSegmCntHeader == MOTObjSegments.vvbiHeader.Size() - 1)
+//            {
+//              bLastSegment = true; // Check if this is last segment
+//            }
+//          else  bLastSegment = false;
+//           /* Generate MOT object for header */
+//          GenMOTObj(vecbiNewData, MOTObjSegments.vvbiHeader[iSegmCntHeader], true, iSegmCntHeader, txTransportID, bLastSegment);
+//          addToLog(QString("iSegmCntheader %1 blastseg %2").arg(iSegmCntHeader).arg(bLastSegment),LOGDRMTXMOT);
+//          iSegmCntHeader++;
+//          if (iSegmCntHeader == MOTObjSegments.vvbiHeader.Size())
+//            {
+//              iSegmCntBody = 0; // Reset counter for body
+//              bCurSegHeader = false;  // Header is ready, transmit body now
+//            }
+//        }
+//      else
+//        {
+
+//          if (iSegmCntBody < MOTObjSegments.vvbiBody.Size()) // Check that body size is not zero
+//            {
+
+//              if (iSegmCntBody == MOTObjSegments.vvbiBody.Size() - 1)
+//                {
+//                  bLastSegment = true; // Check if this is last segment
+//                }
+//              else bLastSegment = false;
+
+//              if (bLastSegment)
+//                {
+//                  /* Generate MOT object for Body */
+//                  GenMOTObj(vecbiNewData, MOTObjSegments.vvbiBody[iSegmCntBody], false, iNumSegStore-1, txTransportID, bLastSegment);
+//                  addToLog(QString("Generated MOTObj for seg %1 last").arg(iNumSegStore-1),LOGDRMTXMOT);
+//                }
+//              else
+//                {
+//                  /* Generate MOT object for Body */
+//                  GenMOTObj(vecbiNewData, MOTObjSegments.vvbiBody[iSegmCntBody], false, iSegmCntBody, txTransportID, bLastSegment);
+//                  addToLog(QString("Generated MOTObj for seg %1 of %2").arg(iSegmCntBody).arg( iNumSegStore),LOGDRMTXMOT);
+//                }
+////              printf("GetDataGroup body situation 2nd iSegmCntBody %d iSegNumStore %d blastseg %d\n", iSegmCntBody, iNumSegStore,  bLastSegment);
+
+//              iSegmCntBody++;
+//            }
+
+//          if (iSegmCntBody == MOTObjSegments.vvbiBody.Size())
+//            {
+//              iSegmCntHeader = 0; // Reset counter for header
+//              bCurSegHeader = true;  // Body is ready, transmit header from next object
+////              txTransportID++;
+//              /* Signal that old object is done */
+//              bObjectDone = true;
+//            }
+//        }
+//    }
+
+//  /* Return status of object transmission */
+//  return bObjectDone;
+//}
+
+
+
+_REAL CMOTDABEnc::GetProgPerc() const
+{
+// Get percentage of processed data of current object.
+        _REAL tussenresult;
+	const int
+    iTotNumSeg =	MOTObjSegments.vvbiHeader.Size() + MOTObjSegments.vvbiBody.Size();
+    tussenresult = ((_REAL) iSegmCntBody + (_REAL) iSegmCntHeader) / iTotNumSeg;
+    return tussenresult;
+}
+
+void
+CMOTDABEnc::Reset()
+{
+  bFirstRound = true ;
+  runInCnt=0;
+	/* Reset continuity indices */
+	iContIndexHeader = 0;
+	iContIndexBody = 0;
+  txTransportID = 0;
+
+	/* Init counter for segments */
+	iSegmCntHeader = 0;
+	iSegmCntBody = 0;
+
+	/* Init flag which shows what is currently generated, header or body */
+	bCurSegHeader = true;		/* Start with header */
+
+	/* Add a "null object" so that at least one valid object can be processed */
+	CMOTObject NullObject;
+  SetMOTObject(NullObject,154);
+}
+
+/******************************************************************************\
+* Decoder                                                                      *
+\******************************************************************************/
+_BOOLEAN
+CMOTDABDec::NewObjectAvailable()
+{
+	return qiNewObjects.empty() == false;
+}
+
+
+void
+CMOTDABDec::GetNextObject(CMOTObject & NewMOTObject)
+{
+	if (!qiNewObjects.empty())
+	{
+		TTransportID firstNew = qiNewObjects.front();
+		qiNewObjects.pop();
+		NewMOTObject = MOTCarousel[firstNew];
+	}
+	else
+	{
+		fprintf(stderr, "GetObject called when queue empty\n");
+		fflush(stderr);
+	}
+}
+
+void
+CMOTDABDec::DeliverIfReady(TTransportID TransportID)
+{
+	CMOTObject & o = MOTCarousel[TransportID];
+	if ((!o.bComplete) && o.bHasHeader && o.Body.Ready())
+	{
+		o.bComplete = true;
+		if (o.Body.IsZipped())
+		{
+			if (o.Body.uncompress() == false)
+				/* Can't unzip so change the filename */
+				o.strName = string(o.strName.c_str()) + ".gz";
+		}
+		//cerr << o << endl;;
+		qiNewObjects.push(TransportID);
+	}
+}
+
+void
+CMOTDABDec::AddDataUnit(CVector < _BINARY > &vecbiNewData)
+{
+	int iLenGroupDataField;
+	CCRC CRCObject;
+	_BOOLEAN bCRCOk;
+	int iSegmentNum;
+	_BINARY biLastFlag;
+  _BINARY btxTransportIDFlag = 0;
+	int iLenIndicat;
+	int iSegmentSize;
+	TTransportID TransportID = -1;
+
+	/* Get length of data unit */
+	iLenGroupDataField = vecbiNewData.Size();
+
+	/* CRC check ------------------------------------------------------------ */
+	/* We do the CRC check at the beginning no matter if it is used or not
+	   since we have to reset bit access for that */
+	/* Reset bit extraction access */
+	vecbiNewData.ResetBitAccess();
+
+	/* Check the CRC of this packet */
+	CRCObject.Reset(16);
+
+	/* "- 2": 16 bits for CRC at the end */
+	for (size_t i = 0; i < size_t(iLenGroupDataField / SIZEOF__BYTE) - 2; i++)
+		CRCObject.AddByte((_BYTE) vecbiNewData.Separate(SIZEOF__BYTE));
+
+	bCRCOk = CRCObject.CheckCRC(vecbiNewData.Separate(16));
+
+	/* MSC data group header ------------------------------------------------ */
+	/* Reset bit extraction access */
+	vecbiNewData.ResetBitAccess();
+
+	/* Extension flag */
+	const _BINARY biExtensionFlag = (_BINARY) vecbiNewData.Separate(1);
+
+	/* CRC flag */
+	const _BINARY biCRCFlag = (_BINARY) vecbiNewData.Separate(1);
+
+	/* Segment flag */
+	const _BINARY biSegmentFlag = (_BINARY) vecbiNewData.Separate(1);
+
+	/* User access flag */
+	const _BINARY biUserAccFlag = (_BINARY) vecbiNewData.Separate(1);
+
+	/* Data group type */
+	const int iDataGroupType = (int) vecbiNewData.Separate(4);
+
+	/* Continuity index (not yet used) */
+	vecbiNewData.Separate(4);
+
+	/* Repetition index (not yet used) */
+	vecbiNewData.Separate(4);
+
+	/* Extension field (not used) */
+	if (biExtensionFlag == true)
+		vecbiNewData.Separate(16);
+
+	/* Session header ------------------------------------------------------- */
+	/* Segment field */
+	if (biSegmentFlag == true)
+	{
+		/* Last */
+		biLastFlag = (_BINARY) vecbiNewData.Separate(1);
+
+		/* Segment number */
+		iSegmentNum = (int) vecbiNewData.Separate(15);
+	}
+	else
+	{
+		biLastFlag = 0;
+		iSegmentNum = 0;
+	}
+
+	/* User access field */
+	if (biUserAccFlag == true)
+	{
+		/* Rfa (Reserved for future addition) */
+		vecbiNewData.Separate(3);
+
+		/* Transport Id flag */
+    btxTransportIDFlag = (_BINARY) vecbiNewData.Separate(1);
+
+		/* Length indicator */
+		iLenIndicat = (int) vecbiNewData.Separate(4);
+
+		/* Transport Id */
+    if (btxTransportIDFlag == 1)
+			TransportID = (int) vecbiNewData.Separate(16);
+
+		/* End user address field (not used) */
+		int iLenEndUserAddress;
+
+    if (btxTransportIDFlag == 1)
+			iLenEndUserAddress = (iLenIndicat - 2) * SIZEOF__BYTE;
+		else
+			iLenEndUserAddress = iLenIndicat * SIZEOF__BYTE;
+
+		vecbiNewData.Separate(iLenEndUserAddress);
+	}
+	//cerr << "MOT: new data unit, tid " << TransportID << " CRC " << bCRCOk << " DG" << iDataGroupType << endl;
+
+	/* MSC data group data field -------------------------------------------- */
+	/* If CRC is not used enter if-block, if CRC flag is used, it must be ok to
+	   enter the if-block */
+	if ((biCRCFlag == false) || ((biCRCFlag == true) && (bCRCOk == true)))
+	{
+		/* Segmentation header ---------------------------------------------- */
+		/* Repetition count (not used) */
+		int repetition_count = vecbiNewData.Separate(3);
+
+		(void)repetition_count; /* silence warnings */
+
+		/* Segment size */
+		iSegmentSize = (int) vecbiNewData.Separate(13);
+
+		/* Get MOT data ----------------------------------------------------- */
+		/* Segment number and user access data is needed */
+		if ((biSegmentFlag == true) && (biUserAccFlag == true) &&
+      (btxTransportIDFlag == 1))
+		{
+			/* don't make any assumptions about the order or interleaving of
+			   Data Groups from the same or different objects.
+			 */
+
+			/* Distinguish between header and body */
+			if (iDataGroupType == 3)
+			{
+				/* Header information, i.e. the header core and the header
+				   extension, are transferred in Data Group type 3 */
+
+				if (MOTmode != headerMode)
+				{
+					/* mode change, throw away any directory */
+					MOTDirectory.Reset();
+					//cout << "Reset " << MOTDirectory << endl;
+				}
+				MOTmode = headerMode;
+
+				/* in theory, there can be only one header at a time, but
+				   lets be a bit more tolerant
+				 */
+				if (MOTHeaders.count(TransportID) == 0)
+				{
+					CBitReassembler o;
+					MOTHeaders[TransportID] = o;
+				}
+				CBitReassembler & o = MOTHeaders[TransportID];
+				o.AddSegment(vecbiNewData, iSegmentSize, iSegmentNum,
+							 biLastFlag);
+				/* if the header just got complete, see if the body is complete */
+				if (o.Ready())
+				{
+					MOTCarousel[TransportID].AddHeader(o.vecData);
+					DeliverIfReady(TransportID);
+				}
+			}
+			else if (iDataGroupType == 4)
+			{
+				/* Body data segments are transferred in Data Group type 4 */
+
+				/* don't worry about modes, just store everything in the carousel
+				   defer decisions until the object and either header or directory
+				   are complete
+				 */
+				map < TTransportID, CMOTObject >::iterator o =
+					MOTCarousel.find(TransportID);
+
+				if (o == MOTCarousel.end())
+				{
+					/* we never saw this before */
+					MOTCarousel[TransportID].TransportID = TransportID;
+					o = MOTCarousel.find(TransportID);
+				}
+				/* is this an old object we have completed? */
+				if (o->second.bComplete == false)
+				{
+					o->second.Body.AddSegment(vecbiNewData, iSegmentSize,
+											  iSegmentNum, biLastFlag);
+				}
+				else
+				{
+					/* discard the segment */
+					vecbiNewData.Separate(iSegmentSize * SIZEOF__BYTE);
+				}
+
+				/* if the body just got complete we can see if its ready to deliver */
+				DeliverIfReady(TransportID);
+			}
+			else if (iDataGroupType == 6)	/* MOT directory */
+			{
+				//cout << "DG6" << endl;
+				if (MOTmode != directoryMode)
+				{
+					/* mode change, throw away any headers */
+					MOTHeaders.clear();
+					MOTDirectory.TransportID = -1;	/* forced reset */
+					MOTmode = directoryMode;
+					//cout << "Reset " << MOTDirectory << endl;
+				}
+
+				/* The carousel is changing */
+				if (MOTDirectory.TransportID != TransportID)
+				{
+					/* we never got all the previous directory */
+					cout << " we never got all the previous directory " << TransportID << ", " << MOTDirectory.  TransportID << endl;
+					MOTDirectory.Reset();
+					MOTDirectory.TransportID = TransportID;
+					MOTDirectoryEntity.Reset();
+					MOTDirComprEntity.Reset();
+				}
+
+				if ((MOTDirectory.TransportID != TransportID) ||
+					((MOTDirectory.TransportID == TransportID)
+						&& (!MOTDirectoryEntity.Ready())))
+				{
+
+					/* Handle the new segment */
+
+					/* rely on the Add routine to check duplicates, set ready, etc. */
+					MOTDirectoryEntity.AddSegment(vecbiNewData,
+												  iSegmentSize,
+												  iSegmentNum, biLastFlag);
+					/* have we got the full directory ? */
+					if (MOTDirectoryEntity.Ready())
+					{
+						//cout << "Ready " << MOTDirectory << endl;
+						ProcessDirectory(MOTDirectoryEntity);
+						MOTDirectory.TransportID = TransportID;
+					}			/* END IF HAVE ALL OF THE NEW DIRECTORY */
+				}
+				else
+				{
+					vecbiNewData.Separate(iSegmentSize * SIZEOF__BYTE);
+				}
+			}					/* of DG type 6 */
+#if 0							//Commented until we can test it with a real compressed directory
+			else if (iDataGroupType == 7)	/* MOT directory compressed */
+			{
+				if (MOTmode != directoryMode)
+				{
+					/* mode change, throw away any headers */
+					MOTHeaders.clear();
+					MOTDirectory.TransportID = -1;	/* forced reset */
+					MOTmode = directoryMode;
+				}
+
+				/* The carousel is changing */
+				if (MOTDirectory.TransportID != TransportID)
+				{
+					/* we never got all the previous directory */
+					MOTDirectory.Reset();
+					MOTDirectory.TransportID = TransportID;
+					MOTDirectoryEntity.Reset();
+					MOTDirComprEntity.Reset();
+				}
+
+				if ((MOTDirectory.TransportID != TransportID) ||
+					((MOTDirectory.TransportID == TransportID)
+						&& (!MOTDirComprEntity.Ready())))
+				{
+					/* Handle the new segment */
+
+					/* rely on the Add routine to check duplicates, set ready, etc. */
+					MOTDirComprEntity.AddSegment(vecbiNewData,
+												 iSegmentSize,
+												 iSegmentNum, biLastFlag);
+					/* have we got the full directory ? */
+					if (MOTDirComprEntity.Ready())
+					{
+						/* uncompress data and extract directory */
+
+						/* Compression Flag - must be 1 */
+
+						const _BOOLEAN bCompressionFlag =
+							MOTDirComprEntity.vecData.
+							Separate(1) ? true : false;
+
+						/* rfu */
+						MOTDirComprEntity.vecData.Separate(1);
+
+						/* EntitySize */
+						MOTDirComprEntity.vecData.Separate(30);
+
+						/* CompressionID */
+						MOTDirComprEntity.vecData.Separate(8);
+
+						/* rfu */
+						MOTDirComprEntity.vecData.Separate(2);
+
+						/* Uncompressed DataLength */
+						MOTDirComprEntity.vecData.Separate(30);
+
+						CBitReassembler MOTDirectoryData;
+
+						MOTDirectoryData.vecData =
+							MOTDirComprEntity.vecData.
+							Separate(MOTDirComprEntity.vecData.Size() -
+									 (9 * SIZEOF__BYTE));
+
+						if (bCompressionFlag
+							&& MOTDirectoryData.IsZipped()
+							&& MOTDirectoryData.uncompress())
+						{
+							ProcessDirectory(MOTDirectoryData);
+
+							MOTDirectory.TransportID = TransportID;
+						}
+						else
+						{
+							/* reset */
+							MOTDirComprEntity.Reset();
+						}
+
+					}			/* END IF HAVE ALL OF THE NEW DIRECTORY */
+				}
+				else
+				{
+					vecbiNewData.Separate(iSegmentSize * SIZEOF__BYTE);
+				}
+			}					/* of DG type 7 */
+#endif
+			else
+			{
+				vecbiNewData.Separate(iSegmentSize * SIZEOF__BYTE);
+			}
+		}
+	}
+}
+
+void
+CMOTDABDec::ProcessDirectory(CBitReassembler & MOTDir)
+{
+	MOTDirectory.AddHeader(MOTDir.vecData);
+
+	/* first reset the "I'm in the carousel" flag for all objects
+	   and set the "I was in the carousel for the relevant objects
+	   then set the "I'm in the carousel" flag for all objects in the
+	   new directory.
+	   Then delete the objects from the carousel that need deleting
+	   leave objects alone that were never in a carousel, they might
+	   become valid later. */
+
+	/* save bodies of old carousel */
+	map < TTransportID, CByteReassembler > Body;
+	for (map < TTransportID,
+		 CMOTObject >::iterator m =
+		 MOTCarousel.begin(); m != MOTCarousel.end(); m++)
+	{
+		Body[m->first] = m->second.Body;
+	}
+	MOTCarousel.erase(MOTCarousel.begin(), MOTCarousel.end());
+
+	//cout << "decode directory " << MOTDirectory.  iNumberOfObjects << " === " << MOTDirectory.vecObjects.size() << " {";
+
+	MOTDirectory.vecObjects.clear();
+
+	for (size_t i = 0; i < size_t(MOTDirectory.iNumberOfObjects); i++)
+	{
+		/* add each header to the carousel */
+		TTransportID tid = (TTransportID) MOTDir.vecData.Separate(16);
+		MOTCarousel[tid].AddHeader(MOTDir.vecData);
+		/* keep any bodies or body fragments previously received */
+		map < TTransportID, CByteReassembler >::iterator b = Body.find(tid);
+		if (b != Body.end())
+			MOTCarousel[tid].Body = b->second;
+		/* mark objects which are in the new directory */
+		MOTDirectory.vecObjects.push_back(tid);
+		//cout << tid << " ";
+		DeliverIfReady(tid);
+	}
+	//cout << "}" << endl;
+
+}
+
+void
+CDateAndTime::extract_absolute(CVector < _BINARY > &vecbiData)
+{
+	vecbiData.Separate(1);		/* rfa */
+	CModJulDate ModJulDate(vecbiData.Separate(17));
+	day = ModJulDate.GetDay();
+	month = ModJulDate.GetMonth();
+	year = ModJulDate.GetYear();
+	vecbiData.Separate(1);		/* rfa */
+	lto_flag = (int) vecbiData.Separate(1);
+	utc_flag = (int) vecbiData.Separate(1);
+	hours = (int) vecbiData.Separate(5);
+	minutes = (int) vecbiData.Separate(6);
+	if (utc_flag != 0)
+	{
+		seconds = (int) vecbiData.Separate(6);
+		vecbiData.Separate(10);	/* rfa */
+	}
+	else
+	{
+		seconds = 0;
+	}
+	if (lto_flag != 0)
+	{
+		vecbiData.Separate(2);	/* rfa */
+		int sign = (int) vecbiData.Separate(1);
+		half_hours = (int) vecbiData.Separate(5);
+		if (sign == 1)
+			half_hours = 0 - half_hours;
+	}
+	else
+	{
+		half_hours = 0;
+	}
+}
+
+void
+CDateAndTime::extract_relative(CVector < _BINARY > &vecbiData)
+{
+	int granularity = (int) vecbiData.Separate(2);
+	int interval = (int) vecbiData.Separate(6);
+	time_t t = time(NULL);
+	switch (granularity)
+	{
+	case 0:
+		t += 2 * 60 * interval;
+		break;
+	case 1:
+		t += 30 * 60 * interval;
+		break;
+	case 2:
+		t += 2 * 60 * 60 * interval;
+		break;
+	case 3:
+		t += 24 * 60 * 60 * interval;
+		break;
+	}
+	struct tm *tmp = gmtime(&t);
+	year = tmp->tm_year;
+	month = tmp->tm_mon;
+	day = tmp->tm_mday;
+	hours = tmp->tm_hour;
+	minutes = tmp->tm_min;
+	seconds = tmp->tm_sec;
+}
+
+void
+CDateAndTime::dump(ostream & out)
+{
+	out << year << '/' << uint16_t(month) << '/' << uint16_t(day);
+	out << " " << hours << ':' << minutes << ':' << seconds;
+	out << " flags: " << utc_flag << ':' << lto_flag << ':' << half_hours;
+}
+
+void
+CMOTObjectBase::decodeExtHeader(_BYTE & bParamId, int &iHeaderFieldLen,
+								int &iDataFieldLen,
+								CVector < _BINARY > &vecbiHeader) const
+{
+	int iPLI = (int) vecbiHeader.Separate(2);
+	bParamId = (unsigned char) vecbiHeader.Separate(6);
+
+	iHeaderFieldLen = 1;
+
+	switch (iPLI)
+	{
+	case 0:
+		/* Total parameter length = 1 byte; no DataField
+		   available */
+		iDataFieldLen = 0;
+		break;
+
+	case 1:
+		/* Total parameter length = 2 bytes, length of DataField
+		   is 1 byte */
+		iDataFieldLen = 1;
+		break;
+
+	case 2:
+		/* Total parameter length = 5 bytes; length of DataField
+		   is 4 bytes */
+		iDataFieldLen = 4;
+		break;
+
+	case 3:
+		/* Total parameter length depends on the DataFieldLength
+		   indicator (the maximum parameter length is
+		   32770 bytes) */
+
+		/* Ext (ExtensionFlag): This 1-bit field specifies the
+		   length of the DataFieldLength Indicator and is coded
+		   as follows:
+		   - 0: the total parameter length is derived from the
+		   next 7 bits;
+		   - 1: the total parameter length is derived from the
+		   next 15 bits */
+		_BINARY biExt = (_BINARY) vecbiHeader.Separate(1);
+
+		/* Get data field length */
+		if (biExt == 0)
+		{
+			iDataFieldLen = (int) vecbiHeader.Separate(7);
+			iHeaderFieldLen++;
+		}
+		else
+		{
+			iDataFieldLen = (int) vecbiHeader.Separate(15);
+			iHeaderFieldLen += 2;
+		}
+	}
+}
+
+void
+CReassembler::cachelast(CVector < _BYTE > &vecDataIn, size_t iSegSize)
+{
+	vecLastSegment.Init(iSegSize);
+	for (size_t i = 0; i < iSegSize; i++)
+		vecLastSegment[i] = vecDataIn.Separate(8);
+}
+
+void
+CReassembler::copyin(CVector < _BYTE > &vecDataIn, size_t iSegNum,
+					 size_t bytes)
+{
+	size_t offset = iSegNum * iSegmentSize;
+	size_t iNewSize = offset + bytes;
+	if (size_t(vecData.Size()) < iNewSize)
+		vecData.Enlarge(iNewSize - vecData.Size());
+	for (size_t i = 0; i < bytes; i++)
+		vecData[offset + i] = vecDataIn.Separate(8);
+}
+
+void
+CReassembler::AddSegment(CVector < _BYTE > &vecDataIn,
+						 int iSegSize, int iSegNum, _BOOLEAN bLast)
+{
+	if (bLast)
+	{
+		if (iLastSegmentNum == -1)
+		{
+			iLastSegmentNum = iSegNum;
+			iLastSegmentSize = iSegSize;
+			/* three cases:
+			   1: single segment - easy! (actually degenerate with case 3)
+			   2: multi-segment and the last segment came first.
+			   3: normal - some segment, not the last, came first,
+			   we know the segment size
+			 */
+			if (iSegNum == 0)
+			{					/* case 1 */
+				copyin(vecDataIn, 0, iSegSize);
+			}
+			else if (iSegmentSize == 0)
+			{					/* case 2 */
+				cachelast(vecDataIn, iSegSize);
+			}
+			else
+			{					/* case 3 */
+				copyin(vecDataIn, iSegNum, iSegSize);
+			}
+		}						/* otherwise do nothing as we already have the last segment */
+	}
+	else
+	{
+		iSegmentSize = iSegSize;
+		if (Tracker.HaveSegment(iSegNum) == false)
+		{
+			copyin(vecDataIn, iSegNum, iSegSize);
+		}
+	}
+	Tracker.AddSegment(iSegNum);	/* tracking the last segment makes the Ready work! */
+
+	if ((iLastSegmentSize != -1)	/* we have the last segment */
+		&& (bReady == false)	/* we haven't already completed reassembly */
+		&& Tracker.Ready()		/* there are no gaps */
+		)
+	{
+		if (vecLastSegment.Size() > 0)
+		{
+			/* we have everything, but the last segment came first */
+			copylast();
+		}
+		bReady = true;
+	}
+}
+
+void
+CReassembler::copylast()
+{
+	size_t offset = iLastSegmentNum * iSegmentSize;
+	vecData.Enlarge(vecLastSegment.Size());
+	for (size_t i = 0; i < size_t(vecLastSegment.Size()); i++)
+		vecData[offset + i] = vecLastSegment[i];
+	vecLastSegment.Init(0);
+}
+
+void
+CBitReassembler::cachelast(CVector < _BYTE > &vecDataIn, size_t iSegSize)
+{
+	vecLastSegment.Init(8 * iSegSize);
+	vecLastSegment.ResetBitAccess();
+	for (size_t i = 0; i < 8 * iSegSize; i++)
+		vecLastSegment[i] = vecDataIn.Separate(1);
+}
+
+void
+CBitReassembler::copyin(CVector < _BYTE > &vecDataIn, size_t iSegNum,
+						size_t bytes)
+{
+	size_t offset = iSegNum * 8 * iSegmentSize;
+	size_t bits = 8 * bytes;
+	size_t iNewSize = offset + bits;
+	if (size_t(vecData.Size()) < iNewSize)
+		vecData.Enlarge(iNewSize - vecData.Size());
+	for (size_t i = 0; i < bits; i++)
+		vecData[offset + i] = vecDataIn.Separate(1);
+}
+
+void
+CBitReassembler::copylast()
+{
+	size_t offset = iLastSegmentNum * 8 * iSegmentSize;
+	vecData.Enlarge(vecLastSegment.Size());
+	for (size_t i = 0; i < size_t(vecLastSegment.Size()); i++)
+		vecData[offset + i] = vecLastSegment[i];
+	vecLastSegment.Init(0);
+	vecLastSegment.ResetBitAccess();
+}
+
+string
+CMOTObjectBase::extractString(CVector < _BINARY > &vecbiData, int iLen) const
+{
+	string strVar;
+	for (size_t i = 0; i < size_t(iLen); i++)
+	{
+		strVar += (char) vecbiData.Separate(SIZEOF__BYTE);
+	}
+	return strVar;
+}
+
+void
+CMOTDirectory::Reset ()
+{
+    CMOTObjectBase::Reset ();
+    bSortedHeaderInformation = false;
+    DirectoryIndex.clear ();
+    bCompressionFlag = false;
+    iCarouselPeriod = -1;
+    iNumberOfObjects = 0;
+    iSegmentSize = 0;
+}
+
+
+void
+CMOTDirectory::AddHeader(CVector < _BINARY > &vecbiHeader)
+{
+//	int iDirectorySize;
+
+	/* compression flag - must be zero */
+	bCompressionFlag = vecbiHeader.Separate(1) ? true : false;
+
+	/* rfu */
+	vecbiHeader.Separate(1);
+
+	/* Directory size: not used */
+//	iDirectorySize = (int) vecbiHeader.Separate(30);
+
+	/* Number of objects */
+	iNumberOfObjects = (int) vecbiHeader.Separate(16);
+
+	/* Carousel period */
+	iCarouselPeriod = (int) vecbiHeader.Separate(24);
+
+	/*  rfu */
+	vecbiHeader.Separate(1);
+
+	/* Rfa: not used */
+	vecbiHeader.Separate(2);
+
+	/* Segment size: not used */
+	iSegmentSize = (int) vecbiHeader.Separate(13);
+	/* Directory extension length */
+	int iDirectoryExtensionLength = (int) vecbiHeader.Separate(16);
+
+	/* Use all header extension data blocks */
+	int iSizeRec = iDirectoryExtensionLength;
+
+	/* Use all header extension data blocks */
+	while (iSizeRec > 0)
+	{
+		_BYTE bParamId;
+		int iHdrFieldLen, iDataFieldLen, iTmp;
+
+		decodeExtHeader(bParamId, iHdrFieldLen, iDataFieldLen, vecbiHeader);
+
+		iSizeRec -= (iHdrFieldLen + iDataFieldLen);
+
+		switch (bParamId)
+		{
+		case 0:				/* SortedHeaderInformation */
+			bSortedHeaderInformation = true;
+			break;
+		case 1:				/* DefaultPermitOutdatedVersions */
+			iTmp = (int) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			if (iTmp == 0)
+				bPermitOutdatedVersions = false;
+			else
+				bPermitOutdatedVersions = true;
+			break;
+		case 9:				/* DefaultExpiration */
+			if (iDataFieldLen == 1)
+			{
+				/* relative */
+				ExpireTime.extract_relative(vecbiHeader);
+			}
+			else
+			{
+				/* absolute */
+				ExpireTime.extract_absolute(vecbiHeader);
+			}
+			break;
+		case 34:				/* DirectoryIndex */
+		{
+			_BYTE iProfile = (_BYTE) vecbiHeader.Separate(SIZEOF__BYTE);
+			DirectoryIndex[iProfile] =
+				extractString(vecbiHeader, iDataFieldLen - 1);
+		}
+			break;
+		default:
+			if (iDataFieldLen > 0)
+				vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		}
+	}
+}
+
+_BOOLEAN
+CReassembler::IsZipped() const
+{
+/*
+	Check if the header file is a gzip header
+
+	see GZIP file format specification
+	http://www.ietf.org/rfc/rfc1952.txt
+*/
+	if(vecData.Size()<3)
+		return false;
+	/* Check for gzip header [31, 139, 8] */
+	if ((vecData[0] == 31) && (vecData[1] == 139) && (vecData[2] == 8))
+		return true;
+	else
+		return false;
+}
+
+unsigned int
+CReassembler::gzGetOriginalSize() const
+{
+/*
+	Get the original size from a gzip file
+	last 4 bytes contains the original file size (ISIZE)
+
+	see GZIP file format specification
+	http://www.ietf.org/rfc/rfc1952.txt
+*/
+	CVector < _BYTE > byHeader(4);
+
+	const int iLastByte = vecData.Size() - 1;
+
+	byHeader[0] = vecData[iLastByte - 3];
+	byHeader[1] = vecData[iLastByte - 2];
+	byHeader[2] = vecData[iLastByte - 1];
+	byHeader[3] = vecData[iLastByte];
+
+	return byHeader[0] + (byHeader[1] << 8) + (byHeader[2] << 16) +
+		(byHeader[3] << 24);
+}
+
+_BOOLEAN
+CReassembler::uncompress()
+{
+#if HAVE_LIBZ
+	CVector < _BYTE > vecbRawDataOut;
+	/* Extract the original file size */
+	unsigned long dest_len = gzGetOriginalSize();
+
+	if (dest_len < MAX_DEC_NUM_BYTES_ZIP_DATA)
+	{
+		vecbRawDataOut.Init(dest_len);
+
+		int zerr;
+		z_stream stream;
+		memset(&stream, 0, sizeof(stream));
+		if ((zerr = inflateInit2(&stream, -MAX_WBITS)) != Z_OK)
+			return false;
+
+		/* skip past minimal gzip header */
+		stream.next_in = &vecData[10];
+		stream.avail_in = vecData.Size() - 10;
+
+		stream.next_out = &vecbRawDataOut[0];
+		stream.avail_out = vecbRawDataOut.Size();
+
+		zerr = inflate(&stream, Z_NO_FLUSH);
+		dest_len = vecbRawDataOut.Size() - stream.avail_out;
+
+		if (zerr != Z_OK && zerr != Z_STREAM_END)
+			return false;
+
+		inflateEnd(&stream);
+
+		if (zerr != Z_OK && zerr != Z_STREAM_END)
+			return false;
+
+		vecData.Init(dest_len);
+		vecData = vecbRawDataOut;
+		return true;
+	}
+	else
+	{
+		vecData.Init(0);
+		return false;
+	}
+#else
+#	ifdef HAVE_LIBFREEIMAGE
+	CVector < _BYTE > vecbRawDataOut;
+	CVector < _BYTE > &vecbRawDataIn = vecData;
+	/* Extract the original file size */
+	const unsigned long dest_len = gzGetOriginalSize();
+
+	if (dest_len < MAX_DEC_NUM_BYTES_ZIP_DATA)
+	{
+		vecbRawDataOut.Init(dest_len);
+
+		/* Actual decompression call */
+		const int zerr = FreeImage_ZLibGUnzip(&vecbRawDataOut[0],
+											  dest_len, &vecbRawDataIn[0],
+											  vecbRawDataIn.Size());
+
+		if (zerr > 0)
+		{
+			/* Copy data */
+			vecData.Init(zerr);
+			vecData = vecbRawDataOut;
+			return true;
+		}
+		else
+		{
+			vecData.Init(0);
+			return false;
+		}
+	}
+	else
+	{
+		vecData.Init(0);
+		return false;
+	}
+#	else
+	return false;
+#	endif
+#endif
+}
+
+static const char *txt[] = { "txt", "txt", "html", 0 };
+static const char *img[] = { "gif", "jpeg", "bmp", "png", 0 };
+static const char *audio[] = { "mpg", "mp2", "mp3", "mpg", "mp2", "mp3",
+	"pcm", "aiff", "atrac", "atrac2", "mp4", 0
+};
+static const char *video[] = { "mpg", "mp2", "mp4", "h263", 0 };
+
+void
+CMOTObject::AddHeader(CVector < _BINARY > &vecbiHeader)
+{
+
+	/* HeaderSize and BodySize */
+	iBodySize = (int) vecbiHeader.Separate(28);
+	size_t iHeaderSize = (size_t) vecbiHeader.Separate(13);
+
+	/* Content type and content sub-type */
+	iContentType = (int) vecbiHeader.Separate(6);
+	iContentSubType = (int) vecbiHeader.Separate(9);
+	/* 7 bytes for header core */
+	int iSizeRec = iHeaderSize - 7;
+
+	/* Use all header extension data blocks */
+	while (iSizeRec > 0)
+	{
+		_BYTE bParamId;
+		int iHdrFieldLen, iDataFieldLen, iTmp;
+
+		decodeExtHeader(bParamId, iHdrFieldLen, iDataFieldLen, vecbiHeader);
+
+		iSizeRec -= (iHdrFieldLen + iDataFieldLen);
+
+		switch (bParamId)
+		{
+		case 1:				/* PermitOutdatedVersions */
+			iTmp = (int) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			if (iTmp == 0)
+				bPermitOutdatedVersions = false;
+			else
+				bPermitOutdatedVersions = true;
+			break;
+		case 5:				/* TriggerTime - OBSOLETE */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 6:				/* Version - OBSOLETE */
+			iVersion = (int) vecbiHeader.Separate(SIZEOF__BYTE);
+			break;
+		case 7:				/* RetransmissionDistance */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 9:				/* Expiration */
+			if (iDataFieldLen == 1)
+			{
+				/* relative */
+				ExpireTime.extract_relative(vecbiHeader);
+			}
+			else
+			{
+				/* absolute */
+				ExpireTime.extract_absolute(vecbiHeader);
+			}
+			break;
+		case 10:				/* Priority */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 11:				/* Label */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 12:				/* Content Name */
+			/* TODO - convert characters to UNICODE */
+			iCharacterSetForName = vecbiHeader.Separate(4);
+			/* rfa  */
+			vecbiHeader.Separate(4);
+			strName = extractString(vecbiHeader, iDataFieldLen - 1);
+			break;
+		case 13:				/* UniqueBodyVersion */
+			iUniqueBodyVersion = (int) vecbiHeader.Separate(32);
+			break;
+		case 15:				/* Content Description */
+			iCharacterSetForDescription = vecbiHeader.Separate(4);
+
+			/* rfa */
+			vecbiHeader.Separate(4);
+
+			strContentDescription =
+				extractString(vecbiHeader, iDataFieldLen - 1);
+			break;
+		case 16:				/* Mime Type */
+			strMimeType = extractString(vecbiHeader, iDataFieldLen);
+			break;
+		case 17:				/* Compression Type */
+			iCompressionType =
+				(int) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 0x21:				/* ProfileSubset */
+		{
+			vecbProfileSubset.clear();
+			for (size_t i = 0; i < size_t(iDataFieldLen); i++)
+				vecbProfileSubset.push_back((_BYTE) vecbiHeader.
+											Separate(SIZEOF__BYTE));
+		}
+			break;
+		case 0x23:				/* CAInfo */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 0x24:				/* CAReplacementObject */
+			/* not decoded - skip */
+			(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		case 0x25:				/* ScopeStart */
+			ScopeStart.extract_absolute(vecbiHeader);
+			break;
+		case 0x26:				/* ScopeEnd */
+			ScopeEnd.extract_absolute(vecbiHeader);
+			break;
+		case 0x27:				/* ScopeId */
+			iScopeId = vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		default:
+			/* not decoded - skip */
+			if (iDataFieldLen > 0)
+				(void) vecbiHeader.Separate(iDataFieldLen * SIZEOF__BYTE);
+			break;
+		}
+	}
+	/* set strFormat */
+	switch (iContentType)
+	{
+	case 1:
+		strFormat = txt[iContentSubType];
+		break;
+	case 2:
+		strFormat = img[iContentSubType];
+		break;
+	case 3:
+		strFormat = audio[iContentSubType];
+		break;
+	case 4:
+		strFormat = video[iContentSubType];
+		break;
+	}
+	bHasHeader = true;
+}
+
+void
+CMOTObject::dump(ostream & out)
+{
+	out << "MOT(" << TransportID << "):";
+	out << " Name: " << strName;
+	out << ':' << iCharacterSetForName;
+	out << " Description: " << strContentDescription;
+	out << ':' << iCharacterSetForDescription;
+	out << " mime: " << strMimeType;
+	out << " content: " << iContentType << '/' << iContentSubType;
+	out << " UniqueBodyVersion: " << iUniqueBodyVersion;
+	out << " Expires: " << ExpireTime;
+	out << " PermitOutdatedVersions: " << bPermitOutdatedVersions;
+	out << " Scope: " << hex << iScopeId << dec << " from " << ScopeStart <<
+		" to " << ScopeEnd;
+	out << " CompressionType: " << iCompressionType;
+	out << " Version: " << iVersion;
+	out << " Priority: " << iPriority;
+	out << " RetransmissionDistance: " << iRetransmissionDistance;
+	out << " format: " << strFormat;
+	out << " length: " << iBodySize;
+}
+
+void
+CMOTDirectory::dump(ostream & out)
+{
+	out << "MOTDIR(" << TransportID << "):";
+	out << " Expires: " << ExpireTime;
+	out << " PermitOutdatedVersions: " << bPermitOutdatedVersions;
+	out << " CarouselPeriod: " << iCarouselPeriod;
+	out << " SegmentSize: " << iSegmentSize;
+	out << " CompressionFlag: " << bCompressionFlag;
+	out << " SortedHeaderInformation: " << bSortedHeaderInformation;
+	out << " indices { ";
+	for (map < _BYTE, string >::iterator di = DirectoryIndex.begin();
+		 di != DirectoryIndex.end(); di++)
+    {
+		out << hex << di->first << dec << " => " << di->second;
+		out.flush();
+    }
+	out << " }";
+	out << " there are " << iNumberOfObjects << " objects {";
+	for (size_t i = 0; i < vecObjects.size(); i++)
+		out << " " << vecObjects[i];
+	out << " }";
+}
diff --git a/qsstv/drmtx/common/datadecoding/DABMOT.h b/qsstv/drmtx/common/datadecoding/DABMOT.h
new file mode 100644
index 0000000..c45e632
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/DABMOT.h
@@ -0,0 +1,615 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer, Andrea Russo, Julian Cable
+ * 
+ * adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ *
+ * Description:
+ *	See DABMOT.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DABMOT_H__3B0UBVE98732KJVEW363E7A0D31912__INCLUDED_)
+#define DABMOT_H__3B0UBVE98732KJVEW363E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "utils/vector.h"
+#include "../util/CRC.h"
+#include <time.h>
+#include <map>
+#include <queue>
+#include <iostream>
+#include "qsstvglobal.h"
+
+
+/* Definitions ****************************************************************/
+/* Invalid data segment number. Shall be a negative value since the test for
+   invalid data segment number is always "if (iDataSegNum < 0)" */
+#define INVALID_DATA_SEG_NUM			-1
+
+/* Maximum number of bytes for zip'ed files. We need to specify this number to
+   avoid segmentation faults due to erroneous zip header giving a much too high
+   number of bytes */
+#define MAX_DEC_NUM_BYTES_ZIP_DATA		1000000	/* 1 MB */
+
+/* Registrered BWS profiles (ETSI TS 101 498-1) */
+#define RESERVED_PROFILE	0x00
+#define BASIC_PROFILE		0x01
+#define TOP_NEWS_PROFILE	0x02
+#define UNRESTRICTED_PC_PROFILE 0xFF
+/* Classes ********************************************************************/
+
+
+class CMOTObjectRaw
+{
+  public:
+    CMOTObjectRaw ()
+    {
+	Reset ();
+    }
+
+    CMOTObjectRaw (const CMOTObjectRaw & nOR)
+    {
+	Header = nOR.Header;
+	Body = nOR.Body;
+    }
+
+    CMOTObjectRaw & operator= (const CMOTObjectRaw & nOR)
+    {
+	Header = nOR.Header;
+	Body = nOR.Body;
+	return *this;
+    }
+
+    void Reset ()
+    {
+	Header.Init (0);
+	Body.Init (0);
+    }
+
+    CVector < _BINARY > Header;
+    CVector < _BINARY > Body;
+};
+
+class CDateAndTime
+{
+  public:
+
+    CDateAndTime():utc_flag(0), lto_flag(0), half_hours(0),
+        year(0), month(0), day(0), hours(0), minutes(0), seconds(0)
+    {}
+
+    void extract_relative (CVector < _BINARY > &vecbiData);
+    void extract_absolute (CVector < _BINARY > &vecbiData);
+    void Reset ()
+    {
+        utc_flag = 0;
+        lto_flag = 0;
+        half_hours = 0;
+        year = 0;
+        month = 0;
+        day = 0;
+        hours = 0;
+        minutes = 0;
+        seconds = 0;
+    }
+
+	void dump(ostream& out);
+
+    int utc_flag, lto_flag, half_hours;
+    uint16_t year;
+    uint8_t month, day;
+    int hours, minutes, seconds;
+};
+
+typedef int TTransportID;
+
+class CSegmentTracker
+{
+  public:
+
+    CSegmentTracker ()
+    {
+	Reset ();
+    }
+
+    void Reset ()
+    {
+	vecbHaveSegment.clear ();
+    }
+
+    size_t size ()
+    {
+	return vecbHaveSegment.size ();
+    }
+
+    _BOOLEAN Ready ()
+    {
+	if (vecbHaveSegment.size () == 0)
+	    return false;
+	for (size_t i = 0; i < vecbHaveSegment.size (); i++)
+	  {
+	      if (vecbHaveSegment[i] == false)
+		{
+		    return false;
+		}
+	  }
+	return true;
+    }
+
+    void AddSegment (int iSegNum)
+    {
+	if ((iSegNum + 1) > int (vecbHaveSegment.size ()))
+	    vecbHaveSegment.resize (iSegNum + 1, false);
+	vecbHaveSegment[iSegNum] = true;
+    }
+
+    _BOOLEAN HaveSegment (int iSegNum)
+    {
+	if (iSegNum < int (vecbHaveSegment.size ()))
+	    return vecbHaveSegment[iSegNum];
+	return false;
+    }
+
+  protected:
+    vector < _BOOLEAN > vecbHaveSegment;
+};
+
+class CReassembler
+{
+  public:
+
+	CReassembler(): vecData(), vecLastSegment(),
+		iLastSegmentNum(-1), iLastSegmentSize(-1), iSegmentSize(0),
+		Tracker(), bReady(false)
+    {
+    }
+
+    CReassembler (const CReassembler & r):iLastSegmentNum (r.iLastSegmentNum),
+	iLastSegmentSize (r.iLastSegmentSize),
+	iSegmentSize (r.iSegmentSize), Tracker (r.Tracker), bReady (r.bReady)
+    {
+	vecData.Init (r.vecData.Size ());
+	vecData = r.vecData;
+	vecLastSegment.Init (r.vecLastSegment.Size ());
+	vecLastSegment = r.vecLastSegment;
+    }
+
+    virtual ~ CReassembler ()
+    {
+    }
+
+    inline CReassembler & operator= (const CReassembler & r)
+    {
+	iLastSegmentNum = r.iLastSegmentNum;
+	iLastSegmentSize = r.iLastSegmentSize;
+	iSegmentSize = r.iSegmentSize;
+	Tracker = r.Tracker;
+	vecData.Init (r.vecData.Size ());
+	vecData = r.vecData;
+	vecLastSegment.Init (r.vecLastSegment.Size ());
+	vecLastSegment = r.vecLastSegment;
+	bReady = r.bReady;
+
+	return *this;
+    }
+
+    void Reset ()
+    {
+	vecData.Init (0);
+	vecData.ResetBitAccess ();
+	vecLastSegment.Init (0);
+	vecLastSegment.ResetBitAccess ();
+	iLastSegmentNum = -1;
+	iLastSegmentSize = -1;
+	iSegmentSize = 0;
+	Tracker.Reset ();
+	bReady = false;
+    }
+
+    _BOOLEAN Ready ()
+    {
+	return bReady;
+    }
+
+    void AddSegment (CVector < _BYTE > &vecDataIn,
+		     int iSegSize, int iSegNum, _BOOLEAN bLast = false);
+
+    _BOOLEAN IsZipped () const;
+    _BOOLEAN uncompress();
+
+    CVector < _BYTE > vecData;
+
+  protected:
+
+    virtual void copyin (CVector < _BYTE > &vecDataIn, size_t iSegNum,
+			 size_t bytes);
+    virtual void cachelast (CVector < _BYTE > &vecDataIn, size_t iSegSize);
+    virtual void copylast ();
+
+    unsigned int gzGetOriginalSize () const;
+
+    CVector < _BYTE > vecLastSegment;
+    int iLastSegmentNum;
+    int iLastSegmentSize;
+    size_t iSegmentSize;
+    CSegmentTracker Tracker;
+    _BOOLEAN bReady;
+};
+
+class CBitReassembler:public CReassembler
+{
+  public:
+    CBitReassembler ():CReassembler ()
+    {
+    }
+    CBitReassembler (const CBitReassembler & r):CReassembler (r)
+    {
+    }
+
+  protected:
+    virtual void copyin (CVector < _BYTE > &vecDataIn, size_t iSegNum,
+			 size_t bytes);
+    virtual void cachelast (CVector < _BYTE > &vecDataIn, size_t iSegSize);
+    virtual void copylast ();
+};
+
+typedef CReassembler CByteReassembler;
+
+class CMOTObjectBase
+{
+  public:
+
+    CMOTObjectBase ():TransportID(-1),ExpireTime(),bPermitOutdatedVersions(false)
+    {
+		Reset ();
+    }
+
+    virtual ~CMOTObjectBase () { }
+
+    virtual void Reset ()
+    {
+		TransportID = -1;
+		ExpireTime.Reset ();
+		bPermitOutdatedVersions = false;
+    }
+
+    void decodeExtHeader (_BYTE & bParamId,
+			  int &iHeaderFieldLen, int &iDataFieldLen,
+			  CVector < _BINARY > &vecbiHeader) const;
+
+    string extractString (CVector < _BINARY > &vecbiData, int iLen) const;
+
+    TTransportID TransportID;
+    CDateAndTime ExpireTime;
+    _BOOLEAN bPermitOutdatedVersions;
+
+};
+
+class CMOTDirectory:public CMOTObjectBase
+{
+  public:
+
+    CMOTDirectory ():CMOTObjectBase(), iCarouselPeriod(0),
+    iNumberOfObjects(0), iSegmentSize(0),
+    bCompressionFlag(false), bSortedHeaderInformation(false),
+    DirectoryIndex(), vecObjects()
+    {
+    }
+
+    CMOTDirectory (const CMOTDirectory & nD):CMOTObjectBase (nD),
+		iCarouselPeriod (nD.iCarouselPeriod),
+		iNumberOfObjects (nD.iNumberOfObjects),
+		iSegmentSize (nD.iSegmentSize),
+		bCompressionFlag (nD.bCompressionFlag),
+		bSortedHeaderInformation (nD.bSortedHeaderInformation),
+		DirectoryIndex (nD.DirectoryIndex)
+    {
+    }
+
+    virtual ~CMOTDirectory ()
+    {
+    }
+
+    inline CMOTDirectory & operator= (const CMOTDirectory & nD)
+    {
+		TransportID = nD.TransportID;
+		ExpireTime = nD.ExpireTime;
+		bPermitOutdatedVersions = nD.bPermitOutdatedVersions;
+		bSortedHeaderInformation = nD.bSortedHeaderInformation;
+		bCompressionFlag = nD.bCompressionFlag;
+		iCarouselPeriod = nD.iCarouselPeriod;
+		DirectoryIndex = nD.DirectoryIndex;
+		iNumberOfObjects = nD.iNumberOfObjects;
+		iSegmentSize = nD.iSegmentSize;
+
+		return *this;
+    }
+
+    virtual void Reset ();
+
+    virtual void AddHeader (CVector < _BINARY > &vecbiHeader);
+
+	void dump(ostream&);
+
+    int iCarouselPeriod, iNumberOfObjects, iSegmentSize;
+    _BOOLEAN bCompressionFlag, bSortedHeaderInformation;
+    map < _BYTE, string > DirectoryIndex;
+    vector < TTransportID > vecObjects;
+};
+
+/* we define this to reduce the need for copy constructors and operator=
+   since CReassembler has a good set, the defaults will do for this, but not
+   for classes with CVector members
+*/
+
+class CMOTObject:public CMOTObjectBase
+{
+  public:
+
+    CMOTObject ():CMOTObjectBase(), vecbRawData(),
+    bComplete(false), bHasHeader(false), Body(),
+    strName(""), iBodySize(0), iCharacterSetForName(0), iCharacterSetForDescription(0),
+    strFormat(""), strMimeType(""), iCompressionType(0), strContentDescription(""),
+    iVersion(0), iUniqueBodyVersion(0), iContentType(0), iContentSubType(0),
+    iPriority(0), iRetransmissionDistance(0), vecbProfileSubset(),
+    ScopeStart(), ScopeEnd(), iScopeId(0), bReady(false)
+    {
+    }
+
+    CMOTObject (const CMOTObject & nO):CMOTObjectBase (nO),
+	bComplete (nO.bComplete),
+	bHasHeader (nO.bHasHeader),
+	strName (nO.strName),
+	iBodySize(nO.iBodySize),
+	iCharacterSetForName (nO.iCharacterSetForName),
+	iCharacterSetForDescription (nO.iCharacterSetForDescription),
+	strFormat (nO.strFormat),
+	strMimeType (nO.strMimeType),
+	iCompressionType (nO.iCompressionType),
+	strContentDescription (nO.strContentDescription),
+	iVersion (nO.iVersion),
+	iUniqueBodyVersion (nO.iUniqueBodyVersion),
+	iContentType (nO.iContentType),
+	iContentSubType (nO.iContentSubType),
+	iPriority (nO.iPriority),
+	iRetransmissionDistance (nO.iRetransmissionDistance),
+	vecbProfileSubset (nO.vecbProfileSubset),
+	ScopeStart (nO.ScopeStart),
+	ScopeEnd (nO.ScopeEnd), iScopeId (nO.iScopeId)
+    {
+        Body = nO.Body;
+        vecbRawData.Init (nO.vecbRawData.Size ());
+        vecbRawData = nO.vecbRawData;
+    }
+
+    virtual ~ CMOTObject ()
+    {
+    }
+
+    inline CMOTObject & operator= (const CMOTObject & nO)
+    {
+        TransportID = nO.TransportID;
+        ExpireTime = nO.ExpireTime;
+        bPermitOutdatedVersions = nO.bPermitOutdatedVersions;
+        bComplete = nO.bComplete;
+        bHasHeader = nO.bHasHeader;
+        strFormat = nO.strFormat;
+        strName = nO.strName;
+        iBodySize = nO.iBodySize;
+        iCharacterSetForName = nO.iCharacterSetForName;
+        iCharacterSetForDescription = nO.iCharacterSetForDescription;
+        strMimeType = nO.strMimeType;
+        iCompressionType = nO.iCompressionType;
+        strContentDescription = nO.strContentDescription;
+        iVersion = nO.iVersion;
+        iUniqueBodyVersion = nO.iUniqueBodyVersion;
+        iContentType = nO.iContentType;
+        iContentSubType = nO.iContentSubType;
+        iPriority = nO.iPriority;
+        iRetransmissionDistance = nO.iRetransmissionDistance;
+        vecbProfileSubset = nO.vecbProfileSubset;
+        ScopeStart = nO.ScopeStart;
+        ScopeEnd = nO.ScopeEnd;
+        iScopeId = nO.iScopeId;
+        Body = nO.Body;
+
+        vecbRawData.Init (nO.vecbRawData.Size ());
+        vecbRawData = nO.vecbRawData;
+
+        return *this;
+    }
+
+
+    void Reset ()
+    {
+        vecbRawData.Init (0);
+        bComplete = false;
+        bHasHeader = false;
+        Body.Reset ();
+        strFormat = "";
+        strName = "";
+        iBodySize = 0;
+        iCharacterSetForName = 0;
+        iCharacterSetForDescription = 0;
+        strMimeType = "";
+        iCompressionType = 0;
+        strContentDescription = "";
+        iVersion = 0;
+        iUniqueBodyVersion = 0;
+        iContentType = 0;
+        iContentSubType = 0;
+        iPriority = 0;
+        iRetransmissionDistance = 0;
+        vecbProfileSubset.clear ();
+        ScopeStart.Reset ();
+        ScopeEnd.Reset ();
+        iScopeId = 0;
+    }
+
+	void dump(ostream&);
+
+    void AddHeader (CVector < _BINARY > &header);
+
+    /* for encoding */
+    CVector < _BYTE > vecbRawData;
+
+    _BOOLEAN bComplete, bHasHeader;
+    CByteReassembler Body;
+    string strName;
+    int iBodySize;
+    int iCharacterSetForName;
+    int iCharacterSetForDescription;
+    string strFormat;
+    string strMimeType;
+    int iCompressionType;
+    string strContentDescription;
+    int iVersion, iUniqueBodyVersion;
+    int iContentType, iContentSubType;
+    int iPriority;
+    int iRetransmissionDistance;
+    vector < _BYTE > vecbProfileSubset;
+
+    /* the following for EPG Objects */
+    CDateAndTime ScopeStart, ScopeEnd;
+    int iScopeId;
+
+  protected:
+    _BOOLEAN bReady;
+};
+
+
+/* Encoder ------------------------------------------------------------------ */
+class CMOTDABEnc
+{
+public:
+  CMOTDABEnc ():MOTObject(), MOTObjSegments(),
+    iSegmCntHeader(0), iSegmCntBody(0), bCurSegHeader(false),
+    iContIndexHeader(0), iContIndexBody(0)
+  {
+    txTransportID=0;
+  }
+
+  virtual ~CMOTDABEnc ()
+  {
+  }
+
+  void Reset ();
+  _BOOLEAN GetDataGroup (CVector < _BINARY > &vecbiNewData);
+  void SetMOTObject (CMOTObject & NewMOTObject, int bytesAvailable);
+//  void SetMOTObjectRunIn (CMOTObject & NewMOTObject);
+  _REAL GetProgPerc () const;
+  int iNumSegStore ;
+  void prepareSegmentList(unsigned int repetition=1); // ON4QZ
+protected:
+  QList <int> segmentList;
+  int segmentListIdx;
+  class CMOTObjSegm
+  {
+  public:
+    CVector < CVector < _BINARY > >vvbiHeader;
+    CVector < CVector < _BINARY > >vvbiBody;
+  };
+
+  void GenMOTSegments (CMOTObjSegm & MOTObjSegm);
+  void PartitionUnits (CVector < _BINARY > &vecbiSource,
+                       CVector < CVector < _BINARY > >&vecbiDest,
+                       const int iPartiSize);
+//  void PartitionUnitsRunIn (CVector < _BINARY > &vecbiSource,
+//                            CVector < CVector < _BINARY > >&vecbiDest,
+//                            const int iPartiSize);
+
+  void GenMOTObj (CVector < _BINARY > &vecbiData,
+                  CVector < _BINARY > &vecbiSeg, const _BOOLEAN bHeader,
+                  const int iSegNum, const int iTranspID,
+                  const _BOOLEAN bLastSeg);
+
+  CMOTObject MOTObject;
+  CMOTObjSegm MOTObjSegments;
+
+  int iSegmCntHeader;
+  int iSegmCntBody;
+  int runInCnt;
+  _BOOLEAN bCurSegHeader;
+
+  int iContIndexHeader;
+  int iContIndexBody;
+  _BOOLEAN bFirstRound;
+};
+
+/* Decoder ------------------------------------------------------------------ */
+
+class CMOTDABDec
+{
+  public:
+
+    CMOTDABDec ():MOTmode (unknown), MOTHeaders(),
+    MOTDirectoryEntity(), MOTDirComprEntity(),
+    MOTDirectory(), MOTCarousel(), qiNewObjects()
+    {
+    }
+
+    virtual ~ CMOTDABDec ()
+    {
+    }
+
+    /* client methods */
+    void GetNextObject (CMOTObject & NewMOTObject);
+
+    void GetObject (CMOTObject & NewMOTObject, TTransportID TransportID)
+    {
+        NewMOTObject = MOTCarousel[TransportID];
+    }
+
+    void GetDirectory (CMOTDirectory & MOTDirectoryOut)
+    {
+        MOTDirectoryOut = MOTDirectory;
+    }
+
+    _BOOLEAN NewObjectAvailable ();
+
+    /* push from lower level */
+    void AddDataUnit (CVector < _BINARY > &vecbiNewData);
+
+  protected:
+
+	void ProcessDirectory (CBitReassembler &MOTDir);
+
+    void DeliverIfReady (TTransportID TransportID);
+
+    /* These fields are the in-progress carousel objects */
+    enum
+    { unknown, headerMode, directoryMode } MOTmode;
+    /* strictly, there should be only one of these! */
+    map < TTransportID, CBitReassembler > MOTHeaders;
+    CBitReassembler MOTDirectoryEntity;
+    CBitReassembler MOTDirComprEntity;
+
+    /* These fields are the cached complete carousel */
+    CMOTDirectory MOTDirectory;
+    map < TTransportID, CMOTObject > MOTCarousel;
+    queue < TTransportID > qiNewObjects;
+};
+
+#endif // !defined(DABMOT_H__3B0UBVE98732KJVEW363E7A0D31912__INCLUDED_)
+
diff --git a/qsstv/drmtx/common/datadecoding/DataDecoder.cpp b/qsstv/drmtx/common/datadecoding/DataDecoder.cpp
new file mode 100644
index 0000000..c73ca60
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/DataDecoder.cpp
@@ -0,0 +1,176 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer, Julian Cable
+ *
+ * Description:
+ *	Data module (using multimedia information carried in DRM stream)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "DataDecoder.h"
+#include <iostream>
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Encoder                                                                      *
+\******************************************************************************/
+void  CDataEncoder::GeneratePacket(CVector < _BINARY > &vecbiPacket)
+{
+	int i;
+	_BOOLEAN bLastFlag;
+
+	/* Init size for whole packet, not only body */
+	vecbiPacket.Init(iTotalPacketSize);
+	vecbiPacket.ResetBitAccess();
+
+	/* Calculate remaining data size to be transmitted */
+  const int iRemainSize = vecbiCurDataUnit.Size() - iCurDataPointer;
+
+	/* Header --------------------------------------------------------------- */
+	/* First flag */
+  if (iCurDataPointer == 0) vecbiPacket.Enqueue((uint32_t) 1, 1);
+  else vecbiPacket.Enqueue((uint32_t) 0, 1);
+
+	/* Last flag */
+	if (iRemainSize > iPacketLen)
+	{
+		vecbiPacket.Enqueue((uint32_t) 0, 1);
+		bLastFlag = false;
+	}
+	else
+	{
+		vecbiPacket.Enqueue((uint32_t) 1, 1);
+		bLastFlag = true;
+	}
+
+	/* Packet Id */
+	vecbiPacket.Enqueue((uint32_t) iPacketID, 2);
+
+	/* Padded packet indicator (PPI) */
+  if (iRemainSize < iPacketLen) vecbiPacket.Enqueue((uint32_t) 1, 1);
+  else vecbiPacket.Enqueue((uint32_t) 0, 1);
+
+	/* Continuity index (CI) */
+	vecbiPacket.Enqueue((uint32_t) iContinInd, 3);
+
+	/* Increment index modulo 8 (1 << 3) */
+	iContinInd++;
+  if (iContinInd == 8) iContinInd = 0;
+
+	/* Body ----------------------------------------------------------------- */
+	if (iRemainSize >= iPacketLen)
+	{
+		if (iRemainSize == iPacketLen)
+		{
+			/* Last packet */
+      for (i = 0; i < iPacketLen; i++) vecbiPacket.Enqueue(vecbiCurDataUnit.Separate(1), 1);
+		}
+		else
+		{
+			for (i = 0; i < iPacketLen; i++)
+			{
+				vecbiPacket.Enqueue(vecbiCurDataUnit.Separate(1), 1);
+				iCurDataPointer++;
+			}
+		}
+	}
+	else
+	{
+		/* Padded packet. If the PPI is 1 then the first byte shall indicate
+		   the number of useful bytes that follow, and the data field is
+		   completed with padding bytes of value 0x00 */
+		vecbiPacket.Enqueue((uint32_t) (iRemainSize / SIZEOF__BYTE),
+							SIZEOF__BYTE);
+
+		/* Data */
+		for (i = 0; i < iRemainSize; i++)
+			vecbiPacket.Enqueue(vecbiCurDataUnit.Separate(1), 1);
+
+		/* Padding */
+		for (i = 0; i < iPacketLen - iRemainSize; i++)
+			vecbiPacket.Enqueue(vecbiCurDataUnit.Separate(1), 1);
+	}
+
+	/* If this was the last packet, get data for next data unit */
+	if (bLastFlag == true)
+	{
+		/* Generate new data unit */
+    MOTSlideShowEncoder.GetDataUnit(vecbiCurDataUnit);
+		vecbiCurDataUnit.ResetBitAccess();
+
+		/* Reset data pointer and continuity index */
+		iCurDataPointer = 0;
+	}
+
+	/* CRC ------------------------------------------------------------------ */
+	CCRC CRCObject;
+
+	/* Reset bit access */
+	vecbiPacket.ResetBitAccess();
+
+	/* Calculate the CRC and put it at the end of the segment */
+	CRCObject.Reset(16);
+
+	/* "byLengthBody" was defined in the header */
+	for (i = 0; i < (iTotalPacketSize / SIZEOF__BYTE - 2); i++)
+		CRCObject.AddByte(_BYTE(vecbiPacket.Separate(SIZEOF__BYTE)));
+        // printf("adding crc16 to packet  iTotalPacketSize is %d\n", iTotalPacketSize);
+	/* Now, pointer in "enqueue"-function is back at the same place, add CRC */
+	vecbiPacket.Enqueue(CRCObject.GetCRC(), 16);
+}
+
+int CDataEncoder::Init(CParameter & Param)
+{
+	/* Init packet length and total packet size (the total packet length is
+	   three bytes longer as it includes the header and CRC fields) */
+
+// TODO we only use always the first service right now
+	const int iCurSelDataServ = 0;
+
+	Param.Lock();      // was in werkende afgecommentaard pa0mbo
+         // printf("In CDataEncoder na Param.Lock \n");
+  iPacketLen = Param.Service[iCurSelDataServ].DataParam.iPacketLen * SIZEOF__BYTE;
+	iTotalPacketSize = iPacketLen + 24 /* CRC + header = 24 bits */ ;
+	iPacketID = Param.Service[iCurSelDataServ].DataParam.iPacketID;
+
+	Param.Unlock();  // was in werkende afgecommentaard pa0mbo 
+
+      /*  printf("in CDataEncoder::Init  iPackectLen = %d iTotalPacketSize = %d packetID = %d\n",
+               iPacketLen, iTotalPacketSize, iPacketID ); */
+
+	/* Init DAB MOT encoder object */
+  MOTSlideShowEncoder.Init(Param);
+
+	/* Generate first data unit */
+	MOTSlideShowEncoder.GetDataUnit(vecbiCurDataUnit);
+	vecbiCurDataUnit.ResetBitAccess();
+
+	/* Reset pointer to current position in data unit and continuity index */
+	iCurDataPointer = 0;
+	iContinInd = 0;
+
+	/* Return total packet size */
+	return iTotalPacketSize;
+}
+
+
+
diff --git a/qsstv/drmtx/common/datadecoding/DataDecoder.h b/qsstv/drmtx/common/datadecoding/DataDecoder.h
new file mode 100644
index 0000000..9366bb5
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/DataDecoder.h
@@ -0,0 +1,80 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See DataDecoder.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(DATADECODER_H__3B0BA660_CA3452363E7A0D31912__INCLUDED_)
+#define DATADECODER_H__3B0BA660_CA3452363E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../Parameter.h"
+#include "../util/Modul.h"
+#include "../util/CRC.h"
+#include "utils/vector.h"
+#include "DABMOT.h"
+#include "MOTSlideShow.h"
+
+class CJournaline;
+class CNews;
+
+/* Definitions ****************************************************************/
+/* Maximum number of packets per stream */
+#define MAX_NUM_PACK_PER_STREAM					4
+
+
+/* Classes ********************************************************************/
+/* Encoder ------------------------------------------------------------------ */
+class CDataEncoder
+{
+  public:
+    CDataEncoder ()
+    {
+    }
+    virtual ~ CDataEncoder ()
+    {
+    }
+
+    int Init (CParameter & Param);
+    void GeneratePacket(CVector < _BINARY > &vecbiPacket);
+
+    CMOTSlideShowEncoder *GetSliShowEnc ()
+    {
+	return &MOTSlideShowEncoder;
+    }
+
+  protected:
+    CMOTSlideShowEncoder MOTSlideShowEncoder;
+    CVector < _BINARY > vecbiCurDataUnit;
+
+    int iPacketLen;
+    int iTotalPacketSize;
+    int iCurDataPointer;
+    int iPacketID;
+    int iContinInd;
+};
+
+
+#endif // !defined(DATADECODER_H__3B0BA660_CA3452363E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/datadecoding/MOTSlideShow.cpp b/qsstv/drmtx/common/datadecoding/MOTSlideShow.cpp
new file mode 100644
index 0000000..59189b8
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/MOTSlideShow.cpp
@@ -0,0 +1,131 @@
+ /******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	MOT applications (MOT Slideshow and Broadcast Web Site)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "MOTSlideShow.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "drmtx/drmtransmitter.h"
+//#include"utils/supportfunctions.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Encoder                                                                      *
+\******************************************************************************/
+void CMOTSlideShowEncoder::GetDataUnit (CVector < _BINARY > &vecbiNewData)
+{
+    if (allDataSend)
+      {
+        if(extraBlocks-- <=0) stopDRM=true;
+      }
+
+    /* Get new data group from MOT encoder. If the last MOT object was
+       completely transmitted, this functions returns true. In this case, put
+       a new picture to the MOT encoder object */
+    if (MOTDAB.GetDataGroup (vecbiNewData) == true)
+      {
+        allDataSend=true;
+        AddNextPicture (); //basically the same
+//        addToLog("loading raw data",LOGDRMMOT);
+
+      }
+//    arrayBinDump(QString("slice %1").arg(sliceCounter++),vecbiNewData,32,true);
+}
+
+_BOOLEAN CMOTSlideShowEncoder::GetTransStat (string & strCurPict, _REAL & rCurPerc) const
+{
+/*
+	Name and current percentage of transmitted data of current picture.
+*/
+    strCurPict = strCurObjName;
+    rCurPerc = MOTDAB.GetProgPerc ();
+
+    if (vecPicFileNames.Size () != 0)
+	return true;
+    else
+	return false;
+}
+
+void CMOTSlideShowEncoder::Init (CParameter &TParam)
+{
+    bytesToBeUsed=(TParam.iNumDecodedBitsMSC/SIZEOF__BYTE);
+    iPictureCnt = 0;
+    strCurObjName = "";
+    MOTDAB.Reset ();
+    AddNextPicture ();
+    allDataSend=false;
+    extraBlocks=5;
+    sliceCounter=0;
+    MOTDAB.prepareSegmentList(1);  // one repitition
+    addToLog("Init and loading raw data",LOGDRMMOT);
+}
+
+void CMOTSlideShowEncoder::AddNextPicture ()
+{
+  int i;
+  unsigned char byteIn;
+
+  /* Here at least one picture is in container */
+  if (vecPicFileNames.Size () > 0)
+    {
+      /* Get current file name */
+      QString tmp=vecPicFileNames[iPictureCnt].name+"."+vecPicFileNames[iPictureCnt].format;
+      strCurObjName = tmp.toLatin1().data();
+      CMOTObject MOTPicture;
+          /* Set file name and format string */
+      MOTPicture.strName = strCurObjName;
+      MOTPicture.strFormat = vecPicFileNames[iPictureCnt].format.toLatin1().data();
+      MOTPicture.vecbRawData.Init (0);
+      for(i=0;i<vecPicFileNames[iPictureCnt].arrayPtr->count();i++)
+         {
+           byteIn=vecPicFileNames[iPictureCnt].arrayPtr->at(i);
+//            byteIn=0;
+           /* Add one byte = SIZEOF__BYTE bits */
+           MOTPicture.vecbRawData.Enlarge (SIZEOF__BYTE);
+           MOTPicture.vecbRawData.Enqueue ((uint32_t)byteIn,SIZEOF__BYTE);
+         }
+       MOTDAB.SetMOTObject (MOTPicture,bytesToBeUsed);
+      /* Set file counter to next picture, test for wrap around */
+      iPictureCnt++;
+      if (iPictureCnt == vecPicFileNames.Size ()) iPictureCnt = 0;
+    }
+}
+
+void CMOTSlideShowEncoder::AddArray (QByteArray *ba,const QString name,const QString format)
+{
+    /* Only ContentSubType "JFIF" (JPEG) and ContentSubType "PNG" are allowed
+       for SlideShow application (not tested here!) */
+    /* Add file name to the list */
+    int iOldNumObj = vecPicFileNames.Size ();
+    vecPicFileNames.Enlarge (1);
+    vecPicFileNames[iOldNumObj].arrayPtr = ba;
+    vecPicFileNames[iOldNumObj].name=name;
+    vecPicFileNames[iOldNumObj].format=format;
+}
diff --git a/qsstv/drmtx/common/datadecoding/MOTSlideShow.h b/qsstv/drmtx/common/datadecoding/MOTSlideShow.h
new file mode 100644
index 0000000..3da3856
--- /dev/null
+++ b/qsstv/drmtx/common/datadecoding/MOTSlideShow.h
@@ -0,0 +1,68 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(MOTSLIDESHOW_H__3B0UBVE98732KJVEW363LIHGEW982__INCLUDED_)
+#define MOTSLIDESHOW_H__3B0UBVE98732KJVEW363LIHGEW982__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "utils/vector.h"
+#include "DABMOT.h"
+#include "../Parameter.h"
+
+
+/* Classes ********************************************************************/
+/* Encoder ------------------------------------------------------------------ */
+class CMOTSlideShowEncoder
+{
+public:
+  CMOTSlideShowEncoder ():vecPicFileNames (0) {strCurObjName="";}
+  virtual ~ CMOTSlideShowEncoder () {}
+  void Init (CParameter &TParam);
+  void GetDataUnit(CVector < _BINARY > &vecbiNewData);
+  void AddArray (QByteArray *ba, const QString name, const QString format);
+  void ClearAllFileNames () {vecPicFileNames.Init (0);}
+  _BOOLEAN GetTransStat (string & strCurPict, _REAL & rCurPerc) const;
+
+protected:
+  struct SPicDescr
+  {
+    QByteArray *arrayPtr;
+    QString name;
+    QString format;
+  };
+  void AddNextPicture ();
+  CMOTDABEnc MOTDAB;
+  CVector < SPicDescr > vecPicFileNames;
+  int iPictureCnt;
+  string strCurObjName;
+  bool allDataSend;
+  int extraBlocks;
+  int sliceCounter;
+  int bytesToBeUsed;
+};
+
+#endif // !defined(MOTSLIDESHOW_H__3B0UBVE98732KJVEW363LIHGEW982__INCLUDED_)
diff --git a/qsstv/drmtx/common/interleaver/BlockInterleaver.cpp b/qsstv/drmtx/common/interleaver/BlockInterleaver.cpp
new file mode 100644
index 0000000..108082a
--- /dev/null
+++ b/qsstv/drmtx/common/interleaver/BlockInterleaver.cpp
@@ -0,0 +1,69 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "BlockInterleaver.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Block-interleaver base class												   *
+\******************************************************************************/
+void CBlockInterleaver::MakeTable(CVector<int>& veciIntTable, int iFrameSize, 
+								  int it_0)
+{
+	int i;
+	int	iHighestOne;
+	int is, iq;
+
+	/* The following equations are taken directly from the DRM-standard paper 
+	   (7.3.3 and 7.6) */
+	iHighestOne = iFrameSize;
+	/* s = 2 ^ ceil(log2(iFrameSize)), means: find the highest "1" of 
+	   iFrameSize. The result of the equation is only one "1" at position 
+	   "highest position of "1" in iFrameSize plus one". Therefore: 
+	   "1 << (16 + 1)".
+	   This implementation: shift bits in iHighestOne to the left until a 
+	   "1" reaches position 16. Simultaneously the bit in "is" is 
+	   right-shifted */
+	is = 1 << (16 + 1);
+	while (!(iHighestOne & (1 << 16)))
+	{
+		iHighestOne <<= 1;
+		is >>= 1;
+	}
+
+	iq = is / 4 - 1;
+
+	veciIntTable[0] = 0;
+
+	for (i = 1; i < iFrameSize; i++)
+	{
+		veciIntTable[i] = (it_0 * veciIntTable[i - 1] + iq) % is;
+		while (veciIntTable[i] >= iFrameSize)
+			veciIntTable[i] = (it_0 * veciIntTable[i] + iq) % is;
+	}
+}
diff --git a/qsstv/drmtx/common/interleaver/BlockInterleaver.h b/qsstv/drmtx/common/interleaver/BlockInterleaver.h
new file mode 100644
index 0000000..afcdb78
--- /dev/null
+++ b/qsstv/drmtx/common/interleaver/BlockInterleaver.h
@@ -0,0 +1,48 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See Blockinterleaver.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(BLOCK_INTERL_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define BLOCK_INTERL_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "utils/vector.h"
+
+
+/* Classes ********************************************************************/
+class CBlockInterleaver
+{
+public:
+	CBlockInterleaver() {}
+	virtual ~CBlockInterleaver() {}
+
+protected:
+	void MakeTable(CVector<int>& veciIntTable, int iFrameSize, int it_0);
+};
+
+
+#endif // !defined(BLOCK_INTERL_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/interleaver/SymbolInterleaver.cpp b/qsstv/drmtx/common/interleaver/SymbolInterleaver.cpp
new file mode 100644
index 0000000..ffe3cfe
--- /dev/null
+++ b/qsstv/drmtx/common/interleaver/SymbolInterleaver.cpp
@@ -0,0 +1,113 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	Symbol interleaver for MSC-symbols
+ *	We have to support long and short symbol interleaving. Long interleaving
+ *	spans over iD times iN_MUX interleaver blocks. To create a "block-wise 
+ *	cycle-buffer" we shift the indices (stored in a table) each time a complete
+ *	block was written. Thus, we dont need to copy data since we only modify
+ *	the indices. 
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "SymbolInterleaver.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Symbol interleaver														   *
+\******************************************************************************/
+void CSymbInterleaver::ProcessDataInternal(CParameter&)
+{
+	int i, j;
+
+	/* Write data in interleaver-memory (always index "0") */
+	for (i = 0; i < iInputBlockSize; i++)
+		matcInterlMemory[veciCurIndex[0]][i] = (*pvecInputData)[i];
+
+	/* Interleave data according to the interleaver table. Use the
+	   the interleaver-blocks described in the DRM-standard 
+	   (Ro(i) = i (mod D)  -> "i % iD") */
+	for (i = 0; i < iInputBlockSize; i++)
+    (*pvecOutputData)[i] = matcInterlMemory[veciCurIndex[i % iD]][veciIntTable[i]];
+
+	/* Set new indices. Move blocks (virtually) forward */
+	for (j = 0; j < iD; j++)
+	{
+		veciCurIndex[j]--;
+
+		if (veciCurIndex[j] < 0)
+			veciCurIndex[j] = iD - 1;
+	}
+}
+
+void CSymbInterleaver::InitInternal(CParameter& TransmParam)
+{
+	int i;
+
+	TransmParam.Lock(); 
+
+	/* Set internal parameters */
+	iN_MUX = TransmParam.CellMappingTable.iNumUsefMSCCellsPerFrame;
+
+	/* Allocate memory for table */
+	veciIntTable.Init(iN_MUX);
+
+	/* Make interleaver table */
+	MakeTable(veciIntTable, iN_MUX, SYMB_INTERL_CONST_T_0);
+
+	/* Set interleaver depth */
+	switch (TransmParam.eSymbolInterlMode)
+	{
+	case CParameter::SI_LONG:
+		iD = D_LENGTH_LONG_INTERL;
+		break;
+
+	case CParameter::SI_SHORT:
+		iD = D_LENGTH_SHORT_INTERL;
+		break;
+	}
+
+	/* Always allocate memory for long interleaver case (interleaver memory) */
+	matcInterlMemory.Init(D_LENGTH_LONG_INTERL, iN_MUX);
+
+	/* Index for addressing the buffers */
+	veciCurIndex.Init(D_LENGTH_LONG_INTERL);
+	for (i = 0; i < D_LENGTH_LONG_INTERL; i++)
+		veciCurIndex[i] = i;
+
+	/* Define block-sizes for input and output */
+	iInputBlockSize = iN_MUX;
+	iOutputBlockSize = iN_MUX;
+
+	/* Since the MSC logical frames must not end at the end of one symbol
+	   (could be somewhere in the middle of the symbol), the output buffer must
+	   accept more cells than one logical MSC frame is long */
+	iMaxOutputBlockSize = 2 * iN_MUX;
+
+	TransmParam.Unlock(); 
+}
+
+
+
diff --git a/qsstv/drmtx/common/interleaver/SymbolInterleaver.h b/qsstv/drmtx/common/interleaver/SymbolInterleaver.h
new file mode 100644
index 0000000..fb38296
--- /dev/null
+++ b/qsstv/drmtx/common/interleaver/SymbolInterleaver.h
@@ -0,0 +1,67 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See SymbolInterleaver.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(CONVINTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define CONVINTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../Parameter.h"
+#include "../util/Modul.h"
+#include "BlockInterleaver.h"
+
+
+/* Definitions ****************************************************************/
+#define D_LENGTH_SHORT_INTERL		1
+#define D_LENGTH_LONG_INTERL		5
+
+/* This constant is defined in DRM-standard for MSC Cell Interleaving */
+#define SYMB_INTERL_CONST_T_0		5
+
+
+/* Classes ********************************************************************/
+class CSymbInterleaver : public CTransmitterModul<_COMPLEX, _COMPLEX>, 
+						 public CBlockInterleaver
+{
+public:
+	CSymbInterleaver() {}
+	virtual ~CSymbInterleaver() {}
+
+protected:
+	int					iN_MUX;
+	CMatrix<_COMPLEX>	matcInterlMemory;
+	CVector<int>		veciCurIndex;
+	CVector<int>		veciIntTable;
+	int					iD;
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+#endif // !defined(CONVINTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/matlib/Matlib.h b/qsstv/drmtx/common/matlib/Matlib.h
new file mode 100644
index 0000000..77443a6
--- /dev/null
+++ b/qsstv/drmtx/common/matlib/Matlib.h
@@ -0,0 +1,862 @@
+/******************************************************************************\
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	c++ Mathematic Library (Matlib)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#ifndef _MATLIB_H_
+#define _MATLIB_H_
+
+#include <cstdlib>
+#include <math.h>
+#include <complex>
+using namespace std;
+#include "../GlobalDefinitions.h"
+
+
+/* Definitions ****************************************************************/
+/* Two different types: constant and temporary buffer */
+enum EVecTy {VTY_CONST, VTY_TEMP};
+
+
+/* These definitions save a lot of redundant code */
+#define _VECOP(TYPE, LENGTH, FCT)	const int iL = LENGTH; \
+									CMatlibVector<TYPE> vecRet(iL, VTY_TEMP); \
+									for (int i = 0; i < iL; i++) \
+										vecRet[i] = FCT; \
+									return vecRet
+
+#define _VECOPCL(FCT)				for (int i = 0; i < iVectorLength; i++) \
+										operator[](i) FCT; \
+									return *this
+
+#define _MATOP(TYPE, LENGTHROW, LENGTHCOL, FCT) \
+									const int iRL = LENGTHROW; \
+									CMatlibMatrix<TYPE> matRet(iRL, LENGTHCOL, VTY_TEMP); \
+									for (int i = 0; i < iRL; i++) \
+										matRet[i] = FCT; \
+									return matRet
+
+#define _MATOPCL(FCT)				for (int i = 0; i < iRowSize; i++) \
+										operator[](i) FCT; \
+									return *this
+
+
+/* In debug mode, test input parameters */
+#ifdef _DEBUG_
+#define _TESTRNGR(POS)		if ((POS >= iVectorLength) || (POS < 0)) \
+								DebugError("MatLibrRead", "POS", POS, \
+								"iVectorLength", iVectorLength)
+#define _TESTRNGW(POS)		if ((POS >= iVectorLength) || (POS < 0)) \
+								DebugError("MatLibrWrite", "POS", POS, \
+								"iVectorLength", iVectorLength)
+#define _TESTSIZE(INP)		if (INP != iVectorLength) \
+								DebugError("SizeCheck", "INP", INP, \
+								"iVectorLength", iVectorLength)
+#define _TESTRNGRM(POS)		if ((POS >= iRowSize) || (POS < 0)) \
+								DebugError("MatLibrReadMatrix", "POS", POS, \
+								"iRowSize", iRowSize)
+#define _TESTRNGWM(POS)		if ((POS >= iRowSize) || (POS < 0)) \
+								DebugError("MatLibrWriteMatrix", "POS", POS, \
+								"iRowSize", iRowSize)
+#define _TESTSIZEM(INP)		if (INP != iRowSize) \
+								DebugError("MatLibOperatorMatrix=()", "INP", INP, \
+								"iRowSize", iRowSize)
+#else
+
+// On Visual c++ 2005 Express Edition there is a segmentation fault if these macros are empty
+// TODO: FIX this with a better solution
+#ifdef _MSC_VER
+#define _TESTRNGR(POS) if (POS != POS) int idummy=0
+#define _TESTRNGW(POS) if (POS != POS) int idummy=0
+#define _TESTSIZE(INP) if (INP != INP) int idummy=0
+#define _TESTRNGRM(POS) if (POS != POS) int idummy=0
+#define _TESTRNGWM(POS) if (POS != POS) int idummy=0
+#define _TESTSIZEM(INP) if (INP != INP) int idummy=0
+#else
+#define _TESTRNGR(POS)
+#define _TESTRNGW(POS)
+#define _TESTSIZE(INP)
+#define _TESTRNGRM(POS)
+#define _TESTRNGWM(POS)
+#define _TESTSIZEM(INP)
+#endif
+#endif
+
+
+/* Classes ********************************************************************/
+/* Prototypes */
+template<class T> class			CMatlibVector;
+template<class T> class			CMatlibMatrix;
+
+/* Here we can choose the precision of the Matlib calculations */
+typedef _REAL					CReal;
+typedef complex<CReal>			CComplex;
+typedef CMatlibVector<CReal>	CRealVector;
+typedef CMatlibVector<CComplex>	CComplexVector;
+typedef CMatlibMatrix<CReal>	CRealMatrix;
+typedef CMatlibMatrix<CComplex>	CComplexMatrix;
+
+
+/******************************************************************************/
+/* CMatlibVector class ********************************************************/
+/******************************************************************************/
+template<class T>
+class CMatlibVector
+{
+public:
+	/* Construction, Destruction -------------------------------------------- */
+	CMatlibVector() : eVType(VTY_CONST), iVectorLength(0), pData(NULL) {}
+	CMatlibVector(const int iNLen, const EVecTy eNTy = VTY_CONST) :
+		eVType(eNTy), iVectorLength(0), pData(NULL) {Init(iNLen);}
+	CMatlibVector(const int iNLen, const T tIniVal) :
+		eVType(VTY_CONST), iVectorLength(0), pData(NULL) {Init(iNLen, tIniVal);}
+	CMatlibVector(CMatlibVector<T>& vecI);
+	CMatlibVector(const CMatlibVector<T>& vecI);
+	virtual ~CMatlibVector() {if (pData != NULL) delete[] pData;}
+
+	CMatlibVector(const CMatlibVector<CReal>& fvReal, const CMatlibVector<CReal>& fvImag) :
+		eVType(VTY_CONST/*VTY_TEMP*/), iVectorLength(fvReal.GetSize()), pData(NULL)
+	{
+		/* Allocate data block for vector */
+		pData = new CComplex[iVectorLength];
+
+		/* Copy data from real-vectors in complex vector */
+		for (int i = 0; i < iVectorLength; i++)
+			pData[i] = CComplex(fvReal[i], fvImag[i]);
+	}
+
+	/* Operator[] (Regular indices!!!) */
+	inline T& operator[](int const iPos) const
+		{_TESTRNGR(iPos); return pData[iPos];}
+	inline T& operator[](int const iPos)
+		{_TESTRNGW(iPos); return pData[iPos];} // For use as l value
+
+	/* Operator() */
+	inline T& operator()(int const iPos) const
+		{_TESTRNGR(iPos - 1); return pData[iPos - 1];}
+	inline T& operator()(int const iPos)
+		{_TESTRNGW(iPos - 1); return pData[iPos - 1];} // For use as l value
+
+	CMatlibVector<T> operator()(const int iFrom, const int iTo) const;
+	CMatlibVector<T> operator()(const int iFrom, const int iStep, const int iTo) const;
+
+	inline int GetSize() const {return iVectorLength;}
+	void Init(const int iIniLen, const T tIniVal = 0);
+	inline CMatlibVector<T>& Reset(const T tResVal = 0) {_VECOPCL(= tResVal);}
+	CMatlibVector<T>& PutIn(const int iFrom, const int iTo, CMatlibVector<T>& fvA);
+	CMatlibVector<T>& Merge(const CMatlibVector<T>& vecA, T& tB);
+	CMatlibVector<T>& Merge(const CMatlibVector<T>& vecA, const CMatlibVector<T>& vecB);
+	CMatlibVector<T>& Merge(const CMatlibVector<T>& vecA, const CMatlibVector<T>& vecB,
+		const CMatlibVector<T>& vecC);
+
+
+	/* operator= */
+	inline CMatlibVector<T>&		operator=(const CMatlibVector<CReal>& vecI)
+	{
+		Init(vecI.GetSize());
+		for (int i = 0; i < iVectorLength; i++) 
+			operator[](i) = vecI[i]; 
+		
+		return *this;
+	}
+
+	inline CMatlibVector<CComplex>&	operator=(const CMatlibVector<CComplex>& vecI)
+	{
+		Init(vecI.GetSize());
+		for (int i = 0; i < iVectorLength; i++) 
+			operator[](i) = vecI[i]; 
+		
+		return *this;
+	}
+
+	/* operator*= */
+	inline CMatlibVector<T>&		operator*=(const CReal& rI)
+		{_VECOPCL(*= rI);}
+	inline CMatlibVector<CComplex>&	operator*=(const CComplex& cI)
+		{_VECOPCL(*= cI);}
+	inline CMatlibVector<T>&		operator*=(const CMatlibVector<CReal>& vecI)
+		{_VECOPCL(*= vecI[i]);}
+	inline CMatlibVector<CComplex>&	operator*=(const CMatlibVector<CComplex>& vecI)
+		{_VECOPCL(*= vecI[i]);}
+
+	/* operator/= */
+	inline CMatlibVector<T>&		operator/=(const CReal& rI)
+		{_VECOPCL(/= rI);}
+	inline CMatlibVector<CComplex>&	operator/=(const CComplex& cI)
+		{_VECOPCL(/= cI);}
+	inline CMatlibVector<T>&		operator/=(const CMatlibVector<CReal>& vecI)
+		{_VECOPCL(/= vecI[i]);}
+	inline CMatlibVector<CComplex>&	operator/=(const CMatlibVector<CComplex>& vecI)
+		{_VECOPCL(/= vecI[i]);}
+
+	/* operator+= */
+	inline CMatlibVector<T>&		operator+=(const CReal& rI)
+		{_VECOPCL(+= rI);}
+	inline CMatlibVector<CComplex>&	operator+=(const CComplex& cI)
+		{_VECOPCL(+= cI);}
+	inline CMatlibVector<T>&		operator+=(const CMatlibVector<CReal>& vecI)
+		{_VECOPCL(+= vecI[i]);}
+	inline CMatlibVector<CComplex>&	operator+=(const CMatlibVector<CComplex>& vecI)
+		{_VECOPCL(+= vecI[i]);}
+
+	/* operator-= */
+	inline CMatlibVector<T>&		operator-=(const CReal& rI)
+		{_VECOPCL(-= rI);}
+	inline CMatlibVector<CComplex>&	operator-=(const CComplex& cI)
+		{_VECOPCL(-= cI);}
+	inline CMatlibVector<T>&		operator-=(const CMatlibVector<CReal>& vecI)
+		{_VECOPCL(-= vecI[i]);}
+	inline CMatlibVector<CComplex>&	operator-=(const CMatlibVector<CComplex>& vecI)
+		{_VECOPCL(-= vecI[i]);}
+
+protected:
+	EVecTy	eVType;
+	int		iVectorLength;
+	T*		pData;
+};
+
+/* operator* ---------------------------------------------------------------- */
+inline CMatlibVector<CComplex> // cv, cv
+	operator*(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] * cvB[i]);}
+inline CMatlibVector<CReal> // rv, rv
+	operator*(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CReal, rvA.GetSize(), rvA[i] * rvB[i]);}
+
+inline CMatlibVector<CComplex> // cv, rv
+	operator*(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] * rvB[i]);}
+inline CMatlibVector<CComplex> // rv, cv
+	operator*(const CMatlibVector<CReal>& rvB, const CMatlibVector<CComplex>& cvA)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] * rvB[i]);}
+
+template<class T> inline
+CMatlibVector<T> // Tv, r
+	operator*(const CMatlibVector<T>& vecA, const CReal& rB)
+	{_VECOP(T, vecA.GetSize(), vecA[i] * rB);}
+template<class T> inline
+CMatlibVector<T> // r, Tv
+	operator*(const CReal& rA, const CMatlibVector<T>& vecB)
+	{_VECOP(T, vecB.GetSize(), rA * vecB[i]);}
+
+template<class T> inline
+CMatlibVector<CComplex> // Tv, c
+	operator*(const CMatlibVector<T>& vecA, const CComplex& cB)
+	{_VECOP(CComplex, vecA.GetSize(), vecA[i] * cB);}
+template<class T> inline
+CMatlibVector<CComplex> // c, Tv
+	operator*(const CComplex& cA, const CMatlibVector<T>& vecB)
+	{_VECOP(CComplex, vecB.GetSize(), cA * vecB[i]);}
+
+
+/* operator/ ---------------------------------------------------------------- */
+inline CMatlibVector<CComplex> // cv, cv
+	operator/(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] / cvB[i]);}
+inline CMatlibVector<CReal> // rv, rv
+	operator/(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CReal, rvA.GetSize(), rvA[i] / rvB[i]);}
+
+inline CMatlibVector<CComplex> // cv, rv
+	operator/(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] / rvB[i]);}
+inline CMatlibVector<CComplex> // rv, cv
+	operator/(const CMatlibVector<CReal>& rvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, rvA.GetSize(), rvA[i] / cvB[i]);}
+
+template<class T> inline
+CMatlibVector<T> // Tv, r
+	operator/(const CMatlibVector<T>& vecA, const CReal& rB)
+	{_VECOP(T, vecA.GetSize(), vecA[i] / rB);}
+template<class T> inline
+CMatlibVector<T> // r, Tv
+	operator/(const CReal& rA, const CMatlibVector<T>& vecB)
+	{_VECOP(T, vecB.GetSize(), rA / vecB[i]);}
+
+template<class T> inline
+CMatlibVector<CComplex> // Tv, c
+	operator/(const CMatlibVector<T>& vecA, const CComplex& cB)
+	{_VECOP(CComplex, vecA.GetSize(), vecA[i] / cB);}
+template<class T> inline
+CMatlibVector<CComplex> // c, Tv
+	operator/(const CComplex& cA, const CMatlibVector<T>& vecB)
+	{_VECOP(CComplex, vecB.GetSize(), cA / vecB[i]);}
+
+
+/* operator+ ---------------------------------------------------------------- */
+inline CMatlibVector<CComplex> // cv, cv
+	operator+(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] + cvB[i]);}
+inline CMatlibVector<CReal> // rv, rv
+	operator+(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CReal, rvA.GetSize(), rvA[i] + rvB[i]);}
+
+inline CMatlibVector<CComplex> // cv, rv
+	operator+(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] + rvB[i]);}
+inline CMatlibVector<CComplex> // rv, cv
+	operator+(const CMatlibVector<CReal>& rvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, rvA.GetSize(), rvA[i] + cvB[i]);}
+
+template<class T> inline
+CMatlibVector<T> // Tv, r
+	operator+(const CMatlibVector<T>& vecA, const CReal& rB)
+	{_VECOP(T, vecA.GetSize(), vecA[i] + rB);}
+template<class T> inline
+CMatlibVector<T> // r, Tv
+	operator+(const CReal& rA, const CMatlibVector<T>& vecB)
+	{_VECOP(T, vecB.GetSize(), rA + vecB[i]);}
+
+template<class T> inline
+CMatlibVector<CComplex> // Tv, c
+	operator+(const CMatlibVector<T>& vecA, const CComplex& cB)
+	{_VECOP(CComplex, vecA.GetSize(), vecA[i] + cB);}
+template<class T> inline
+CMatlibVector<CComplex> // c, Tv
+	operator+(const CComplex& cA, const CMatlibVector<T>& vecB)
+	{_VECOP(CComplex, vecB.GetSize(), cA + vecB[i]);}
+
+
+/* operator- ---------------------------------------------------------------- */
+inline CMatlibVector<CComplex> // cv, cv
+	operator-(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] - cvB[i]);}
+inline CMatlibVector<CReal> // rv, rv
+	operator-(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CReal, rvA.GetSize(), rvA[i] - rvB[i]);}
+
+inline CMatlibVector<CComplex> // cv, rv
+	operator-(const CMatlibVector<CComplex>& cvA, const CMatlibVector<CReal>& rvB)
+	{_VECOP(CComplex, cvA.GetSize(), cvA[i] - rvB[i]);}
+inline CMatlibVector<CComplex> // rv, cv
+	operator-(const CMatlibVector<CReal>& rvA, const CMatlibVector<CComplex>& cvB)
+	{_VECOP(CComplex, rvA.GetSize(), rvA[i] - cvB[i]);}
+
+template<class T> inline
+CMatlibVector<T> // Tv, r
+	operator-(const CMatlibVector<T>& vecA, const CReal& rB)
+	{_VECOP(T, vecA.GetSize(), vecA[i] - rB);}
+template<class T> inline
+CMatlibVector<T> // r, Tv
+	operator-(const CReal& rA, const CMatlibVector<T>& vecB)
+	{_VECOP(T, vecB.GetSize(), rA - vecB[i]);}
+
+template<class T> inline
+CMatlibVector<CComplex> // Tv, c
+	operator-(const CMatlibVector<T>& vecA, const CComplex& cB)
+	{_VECOP(CComplex, vecA.GetSize(), vecA[i] - cB);}
+template<class T> inline
+CMatlibVector<CComplex> // c, Tv
+	operator-(const CComplex& cA, const CMatlibVector<T>& vecB)
+	{_VECOP(CComplex, vecB.GetSize(), cA - vecB[i]);}
+
+
+/* Implementation **************************************************************
+   (the implementation of template classes must be in the header file!) */
+template<class T>
+CMatlibVector<T>::CMatlibVector(CMatlibVector<T>& vecI) :
+	 eVType(VTY_CONST/*VTY_TEMP*/), iVectorLength(vecI.GetSize()), pData(NULL)
+{
+	/* The copy constructor for the constant vector is a real copying
+	   task. But in the case of a temporary buffer only the pointer
+	   of the temporary buffer is used. The buffer of the temporary
+	   vector is then destroyed!!! Therefore the usage of "VTY_TEMP"
+	   should be done if the vector IS NOT USED IN A FUNCTION CALL,
+	   otherwise this vector will be destroyed afterwards (if the
+	   function argument is not declared with "&") */
+	if (iVectorLength > 0)
+	{
+		if (vecI.eVType == VTY_CONST)
+		{
+			/* Allocate data block for vector */
+			pData = new T[iVectorLength];
+
+			/* Copy vector */
+			for (int i = 0; i < iVectorLength; i++)
+				pData[i] = vecI[i];
+		}
+		else
+		{
+			/* We can define the copy constructor as a destroying operator of
+			   the input vector for performance reasons. This
+			   saves us from always copy the entire vector */
+			/* Take data pointer from input vector (steal it) */
+			pData = vecI.pData;
+
+			/* Destroy other vector (temporary vectors only) */
+			vecI.pData = NULL;
+		}
+	}
+}
+
+/* Copy constructor for constant Matlib vectors */
+template<class T>
+CMatlibVector<T>::CMatlibVector(const CMatlibVector<T>& vecI) :
+	eVType(VTY_CONST), iVectorLength(vecI.GetSize()), pData(NULL)
+{
+	if (iVectorLength > 0)
+	{
+		/* Allocate data block for vector */
+		pData = new T[iVectorLength];
+
+		/* Copy vector */
+		for (int i = 0; i < iVectorLength; i++)
+			pData[i] = vecI[i];
+	}
+}
+
+template<class T>
+void CMatlibVector<T>::Init(const int iIniLen, const T tIniVal)
+{
+	iVectorLength = iIniLen;
+
+	/* Allocate data block for vector */
+	if (iVectorLength > 0)
+	{
+		if (pData != NULL)
+			delete[] pData;
+
+		pData = new T[iVectorLength];
+
+		/* Init with init value */
+		for (int i = 0; i < iVectorLength; i++)
+			pData[i] = tIniVal;
+	}
+}
+
+template<class T> inline
+CMatlibVector<T> CMatlibVector<T>::operator()(const int iFrom,
+											  const int iTo) const
+{
+	/* This is also capable of "wrap around" blocks (if the value in "iFrom" is
+	   larger then the "iTo" value) */
+	int			i;
+	const int	iStartVal = iFrom - 1;
+
+	if (iFrom > iTo)
+	{
+		/* Wrap around case */
+		CMatlibVector<T> vecRet(iVectorLength - iStartVal + iTo, VTY_TEMP);
+
+		int iCurPos = 0;
+
+		for (i = iStartVal; i < iVectorLength; i++)
+			vecRet[iCurPos++] = operator[](i);
+
+		for (i = 0; i < iTo; i++)
+			vecRet[iCurPos++] = operator[](i);
+
+		return vecRet;
+	}
+	else
+	{
+		CMatlibVector<T> vecRet(iTo - iStartVal, VTY_TEMP);
+
+		for (i = iStartVal; i < iTo; i++)
+			vecRet[i - iStartVal] = operator[](i);
+
+		return vecRet;
+	}
+}
+
+template<class T> inline
+CMatlibVector<T> CMatlibVector<T>::operator()(const int iFrom,
+											  const int iStep,
+											  const int iTo) const
+{
+	CMatlibVector<T> vecRet(size_t(abs(float(iTo - iFrom)) / abs(float(iStep))) + 1, VTY_TEMP);
+	int iOutPos = 0;
+	int i;
+
+	if (iFrom > iTo)
+	{
+		const int iEnd = iTo - 2;
+		for (i = iFrom - 1; i > iEnd; i += iStep)
+		{
+			vecRet[iOutPos] = operator[](i);
+			iOutPos++;
+		}
+	}
+	else
+	{
+		for (i = iFrom - 1; i < iTo; i += iStep)
+		{
+			vecRet[iOutPos] = operator[](i);
+			iOutPos++;
+		}
+	}
+
+	return vecRet;
+}
+
+template<class T> inline
+CMatlibVector<T>& CMatlibVector<T>::PutIn(const int iFrom,
+										  const int iTo,
+										  CMatlibVector<T>& vecI)
+{
+	const int iStart = iFrom - 1;
+	const int iEnd = iTo - iStart;
+
+	for (int i = 0; i < iEnd; i++)
+		operator[](i + iStart) = vecI[i];
+
+	return *this;
+}
+
+template<class T> inline
+CMatlibVector<T>& CMatlibVector<T>::Merge(const CMatlibVector<T>& vecA, T& tB)
+{
+	const int iSizeA = vecA.GetSize();
+
+	for (int i = 0; i < iSizeA; i++)
+		operator[](i) = vecA[i];
+	
+	operator[](iSizeA) = tB;
+
+	return *this;
+}
+
+template<class T> inline
+CMatlibVector<T>& CMatlibVector<T>::Merge(const CMatlibVector<T>& vecA,
+										  const CMatlibVector<T>& vecB)
+{
+	int i;
+	const int iSizeA = vecA.GetSize();
+	const int iSizeB = vecB.GetSize();
+
+	/* Put first vector */
+	for (i = 0; i < iSizeA; i++)
+		operator[](i) = vecA[i];
+	
+	/* Put second vector behind the first one, both
+	   together must have length of *this */
+	for (i = 0; i < iSizeB; i++)
+		operator[](i + iSizeA) = vecB[i];
+
+	return *this;
+}
+
+template<class T> inline
+CMatlibVector<T>& CMatlibVector<T>::Merge(const CMatlibVector<T>& vecA,
+										  const CMatlibVector<T>& vecB,
+										  const CMatlibVector<T>& vecC)
+{
+	int i;
+	const int iSizeA = vecA.GetSize();
+	const int iSizeB = vecB.GetSize();
+	const int iSizeC = vecC.GetSize();
+	const int iSizeAB = iSizeA + iSizeB;
+
+	/* Put first vector */
+	for (i = 0; i < iSizeA; i++)
+		operator[](i) = vecA[i];
+	
+	/* Put second vector behind the first one */
+	for (i = 0; i < iSizeB; i++)
+		operator[](i + iSizeA) = vecB[i];
+
+	/* Put third vector behind previous put vectors */
+	for (i = 0; i < iSizeC; i++)
+		operator[](i + iSizeAB) = vecC[i];
+
+	return *this;
+}
+
+
+/******************************************************************************/
+/* CMatlibMatrix class ********************************************************/
+/******************************************************************************/
+/*
+	We define: Matrix[row][column]
+*/
+template<class T>
+class CMatlibMatrix
+{
+public:
+	/* Construction, Destruction -------------------------------------------- */
+	CMatlibMatrix() : eVType(VTY_CONST), iRowSize(0), ppData(NULL) {}
+	CMatlibMatrix(const int iNRowLen, const int iNColLen,
+		const EVecTy eNTy = VTY_CONST) :  eVType(eNTy),
+		iRowSize(0), ppData(NULL) {Init(iNRowLen, iNColLen);}
+	CMatlibMatrix(const int iNRowLen, const int iNColLen, const T tIniVal) :
+		eVType(VTY_CONST), iRowSize(0), ppData(NULL)
+		{Init(iNRowLen, iNColLen, tIniVal);}
+	CMatlibMatrix(const CMatlibMatrix<T>& matI);
+
+	virtual ~CMatlibMatrix() {if (ppData != NULL) delete[] ppData;}
+
+	void Init(const int iNRowLen, const int iNColLen, const T tIniVal = 0);
+	inline int GetRowSize() const {return iRowSize;}
+	inline int GetColSize() const
+		{if (iRowSize > 0) return ppData[0].GetSize(); else return 0;}
+
+	/* Operator[] (Regular indices!!!) */
+	inline CMatlibVector<T>& operator[](int const iPos) const
+		{_TESTRNGRM(iPos); return ppData[iPos];}
+	inline CMatlibVector<T>& operator[](int const iPos)
+		{_TESTRNGWM(iPos); return ppData[iPos];} // For use as l value
+
+	/* Operator() */
+	inline CMatlibVector<T>& operator()(int const iPos) const 
+		{_TESTRNGRM(iPos - 1); return ppData[iPos - 1];}
+	inline CMatlibVector<T>& operator()(int const iPos) 
+		{_TESTRNGWM(iPos - 1); return ppData[iPos - 1];} // For use as l value
+
+	CMatlibMatrix<T> operator()(const int iRowFrom, const int iRowTo,
+		const int iColFrom, const int iColTo) const;
+
+	/* operator= */
+	inline CMatlibMatrix<T>& operator=(const CMatlibMatrix<CReal>& matI)
+		{_TESTSIZEM(matI.GetRowSize()); _MATOPCL(= matI[i]);}
+	inline CMatlibMatrix<CComplex>& operator=(const CMatlibMatrix<CComplex>& matI)
+		{_TESTSIZEM(matI.GetRowSize()); _MATOPCL(= matI[i]);}
+
+	/* operator+= */
+	inline CMatlibMatrix<T>& operator+=(const CMatlibMatrix<CReal>& matI)
+		{_MATOPCL(+= matI[i]);}
+	inline CMatlibMatrix<CComplex>& operator+=(const CMatlibMatrix<CComplex>& matI)
+		{_MATOPCL(+= matI[i]);}
+
+	/* operator-= */
+	inline CMatlibMatrix<T>& operator-=(const CMatlibMatrix<CReal>& matI)
+		{_MATOPCL(-= matI[i]);}
+	inline CMatlibMatrix<CComplex>& operator-=(const CMatlibMatrix<CComplex>& matI)
+		{_MATOPCL(-= matI[i]);}
+
+	/* operator*= */
+	inline CMatlibMatrix<T>& operator*=(const CReal& rI)
+		{_MATOPCL(*= rI);}
+	inline CMatlibMatrix<CComplex>& operator*=(const CComplex& cI)
+		{_MATOPCL(*= cI);}
+
+	/* operator/= */
+	inline CMatlibMatrix<T>& operator/=(const CReal& rI)
+		{_MATOPCL(/= rI);}
+	inline CMatlibMatrix<CComplex>& operator/=(const CComplex& cI)
+		{_MATOPCL(/= cI);}
+
+protected:
+	EVecTy				eVType;
+	int					iRowSize;
+	CMatlibVector<T>*	ppData;
+};
+
+
+/* Help functions *************************************************************/
+/* operator+ */
+inline CMatlibMatrix<CComplex> // cm, cm
+operator+(const CMatlibMatrix<CComplex>& cmA, const CMatlibMatrix<CComplex>& cmB)
+{
+	const int iRowSizeA = cmA.GetRowSize();
+	const int iColSizeA = cmA.GetColSize();
+	CMatlibMatrix<CComplex> matRet(iRowSizeA, iColSizeA, VTY_TEMP);
+
+	for (int j = 0; j < iRowSizeA; j++)
+	{
+		for (int i = 0; i < iColSizeA; i++)
+			matRet[j][i] = cmA[j][i] + cmB[j][i];
+	}
+
+	return matRet;
+}
+
+/* operator- */
+inline CMatlibMatrix<CComplex> // cm, cm
+operator-(const CMatlibMatrix<CComplex>& cmA, const CMatlibMatrix<CComplex>& cmB)
+{
+	const int iRowSizeA = cmA.GetRowSize();
+	const int iColSizeA = cmA.GetColSize();
+	CMatlibMatrix<CComplex> matRet(iRowSizeA, iColSizeA, VTY_TEMP);
+
+	for (int j = 0; j < iRowSizeA; j++)
+	{
+		for (int i = 0; i < iColSizeA; i++)
+			matRet[j][i] = cmA[j][i] - cmB[j][i];
+	}
+
+	return matRet;
+}
+
+/* operator* */
+inline CMatlibVector<CComplex> // cm, cv
+operator*(const CMatlibMatrix<CComplex>& cmA, const CMatlibVector<CComplex>& cvB)
+{
+	const int iRowSizeA = cmA.GetRowSize();
+	const int iSizeB = cvB.GetSize();
+	CMatlibVector<CComplex> vecRet(iSizeB, VTY_TEMP);
+
+	for (int j = 0; j < iRowSizeA; j++)
+	{
+		vecRet[j] = (CReal) 0.0;
+
+		for (int i = 0; i < iSizeB; i++)
+			vecRet[j] += cmA[j][i] * cvB[i];
+	}
+
+	return vecRet;
+}
+
+/* operator* */
+inline CMatlibVector<CReal> // rm, rv
+operator*(const CMatlibMatrix<CReal>& rmA, const CMatlibVector<CReal>& rvB)
+{
+	const int iRowSizeA = rmA.GetRowSize();
+	const int iSizeB = rvB.GetSize();
+	CMatlibVector<CReal> vecRet(iSizeB, VTY_TEMP);
+
+	for (int j = 0; j < iRowSizeA; j++)
+	{
+		vecRet[j] = (CReal) 0.0;
+
+		for (int i = 0; i < iSizeB; i++)
+			vecRet[j] += rmA[j][i] * rvB[i];
+	}
+
+	return vecRet;
+}
+
+/* operator* */
+inline CMatlibMatrix<CComplex> // cm, cm
+operator*(const CMatlibMatrix<CComplex>& cmA, const CMatlibMatrix<CComplex>& cmB)
+{
+	const int iRowSizeA = cmA.GetRowSize();
+	const int iRowSizeB = cmB.GetRowSize();
+	const int iColSizeB = cmB.GetColSize();
+	CMatlibMatrix<CComplex> matRet(iRowSizeA, iColSizeB, VTY_TEMP);
+
+	for (int k = 0; k < iColSizeB; k++)
+	{
+		for (int j = 0; j < iRowSizeA; j++)
+		{
+			matRet[j][k] = (CReal) 0.0;
+
+			for (int i = 0; i < iRowSizeB; i++)
+				matRet[j][k] += cmA[j][i] * cmB[i][k];
+		}
+	}
+
+	return matRet;
+}
+
+/* operator* */
+inline CMatlibMatrix<CComplex> // c, cm
+operator*(const CComplex& cA, const CMatlibMatrix<CComplex>& cmB)
+{
+	const int iRowSizeB = cmB.GetRowSize();
+	const int iColSizeB = cmB.GetColSize();
+	CMatlibMatrix<CComplex> matRet(iRowSizeB, iColSizeB, VTY_TEMP);
+
+	for (int k = 0; k < iColSizeB; k++)
+	{
+		for (int j = 0; j < iRowSizeB; j++)
+			matRet[j][k] = cA * cmB[j][k];
+	}
+
+	return matRet;
+}
+
+/* operator* */
+inline CMatlibMatrix<CReal> // r, rm
+operator*(const CReal& rA, const CMatlibMatrix<CReal>& rmB)
+{
+	const int iRowSizeB = rmB.GetRowSize();
+	const int iColSizeB = rmB.GetColSize();
+	CMatlibMatrix<CReal> matRet(iRowSizeB, iColSizeB, VTY_TEMP);
+
+	for (int k = 0; k < iColSizeB; k++)
+	{
+		for (int j = 0; j < iRowSizeB; j++)
+			matRet[j][k] = rA * rmB[j][k];
+	}
+
+	return matRet;
+}
+
+
+/* Implementation **************************************************************
+   (the implementation of template classes must be in the header file!) */
+template<class T>
+CMatlibMatrix<T>::CMatlibMatrix(const CMatlibMatrix<T>& matI) :
+	eVType(VTY_CONST), iRowSize(matI.GetRowSize()), ppData(NULL)
+{
+	if (iRowSize > 0)
+	{
+		/* Allocate data block for vector */
+		ppData = new CMatlibVector<T>[iRowSize];
+
+		/* Init column vectors and copy */
+		for (int i = 0; i < iRowSize; i++)
+		{
+			ppData[i].Init(matI.GetColSize());
+
+			/* Copy entire vector */
+			ppData[i] = matI[i];
+		}
+	}
+}
+
+template<class T>
+void CMatlibMatrix<T>::Init(const int iNRowLen, const int iNColLen, const T tIniVal)
+{
+	iRowSize = iNRowLen;
+
+	/* Allocate data block for vector */
+	if (iRowSize > 0)
+	{
+		if (ppData != NULL)
+			delete[] ppData;
+
+		ppData = new CMatlibVector<T>[iRowSize];
+
+		/* Init column vectors and set to init value */
+		for (int i = 0; i < iRowSize; i++)
+			ppData[i].Init(iNColLen, tIniVal);
+	}
+}
+
+template<class T> inline
+CMatlibMatrix<T> CMatlibMatrix<T>::operator()(const int iRowFrom, const int iRowTo,
+											  const int iColFrom, const int iColTo) const
+{
+	const int iStartRow = iRowFrom - 1;
+	const int iStartCol = iColFrom - 1;
+	CMatlibMatrix<T> matRet(iRowTo - iStartRow, iColTo - iStartCol, VTY_TEMP);
+
+	for (int j = iStartRow; j < iRowTo; j++)
+	{
+		for (int i = iStartCol; i < iColTo; i++)
+			matRet[j - iStartRow][i - iStartCol] = operator[](j)[i];
+	}
+
+	return matRet;
+}
+
+
+/* Include toolboxes after all type definitions */
+#include "MatlibStdToolbox.h"
+#include "MatlibSigProToolbox.h"
+
+
+#endif /* _MATLIB_H_ */
diff --git a/qsstv/drmtx/common/matlib/MatlibSigProToolbox.cpp b/qsstv/drmtx/common/matlib/MatlibSigProToolbox.cpp
new file mode 100644
index 0000000..550791c
--- /dev/null
+++ b/qsstv/drmtx/common/matlib/MatlibSigProToolbox.cpp
@@ -0,0 +1,606 @@
+/******************************************************************************\
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	c++ Mathematic Library (Matlib), signal processing toolbox
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "MatlibSigProToolbox.h"
+
+
+/* Implementation *************************************************************/
+CMatlibVector<CReal> Hann(const int iLen)
+{
+	int iHalf, i;
+	CMatlibVector<CReal> fvRet(iLen, VTY_TEMP);
+
+	if (iLen % 2)
+	{
+		/* Odd length window */
+		iHalf = (iLen + 1) / 2;
+
+		/* Hanning window */
+		CMatlibVector<CReal> vecTemp(iHalf);
+		CMatlibVector<CReal> w(iHalf);
+		for (i = 0; i < iHalf; i++)
+			vecTemp[i] = (CReal) i;
+
+		w = (CReal) 0.5 * (1 - Cos((CReal) 2.0 * crPi * vecTemp / (iLen - 1)));
+
+		/* Make symmetric window */
+		return fvRet.Merge(w, w(iHalf - 1, -1, 1));
+	}
+	else
+	{
+		/* Even length window */
+		iHalf = iLen / 2;
+
+		/* Hanning window */
+		CMatlibVector<CReal> vecTemp(iHalf);
+		CMatlibVector<CReal> w(iHalf);
+		for (i = 0; i < iHalf; i++)
+			vecTemp[i] = (CReal) i;
+
+		w = (CReal) 0.5 * (1 - Cos((CReal) 2.0 * crPi * vecTemp / (iLen - 1)));
+
+		/* Make symmetric window */
+		return fvRet.Merge(w, w(iHalf, -1, 1));
+	}
+}
+
+CMatlibVector<CReal> Hamming(const int iLen)
+{
+	int iHalf, i;
+	CMatlibVector<CReal> fvRet(iLen, VTY_TEMP);
+
+	if (iLen % 2)
+	{
+		/* Odd length window */
+		iHalf = (iLen + 1) / 2;
+
+		/* Hanning window */
+		CMatlibVector<CReal> vecTemp(iHalf);
+		CMatlibVector<CReal> w(iHalf);
+		for (i = 0; i < iHalf; i++)
+			vecTemp[i] = (CReal) i;
+
+		w = (CReal) 0.54 - (CReal) 0.46 * 
+			Cos((CReal) 2.0 * crPi * vecTemp / (iLen - 1));
+
+		/* Make symmetric window */
+		return fvRet.Merge(w, w(iHalf - 1, -1, 1));
+	}
+	else
+	{
+		/* Even length window */
+		iHalf = iLen / 2;
+
+		/* Hanning window */
+		CMatlibVector<CReal> vecTemp(iHalf);
+		CMatlibVector<CReal> w(iHalf);
+		for (i = 0; i < iHalf; i++)
+			vecTemp[i] = (CReal) i;
+
+		w = (CReal) 0.54 - (CReal) 0.46 * 
+			Cos((CReal) 2.0 * crPi * vecTemp / (iLen - 1));
+
+		/* Make symmetric window */
+		return fvRet.Merge(w, w(iHalf, -1, 1));
+	}
+}
+
+CMatlibVector<CReal> Nuttallwin(const int iLen)
+{
+	CMatlibVector<CReal> fvRet(iLen, VTY_TEMP);
+
+	/* Nuttall coefficients */
+	const CReal rA0 = (CReal) 0.3635819;
+	const CReal rA1 = (CReal) 0.4891775;
+	const CReal rA2 = (CReal) 0.1365995;
+	const CReal rA3 = (CReal) 0.0106411;
+
+	const CReal rArg = (CReal) 2.0 * crPi / (iLen - 1);
+
+	for (int i = 0; i < iLen; i++)
+	{
+		fvRet[i] = rA0 - rA1 * Cos(rArg * i) +
+			rA2 * Cos(rArg * i * 2) - rA3 * Cos(rArg * i * 3);
+	}
+
+	return fvRet;
+}
+
+CMatlibVector<CReal> Bartlett(const int iLen)
+{
+	const int iHalf = (int) Ceil((CReal) iLen / 2);
+	CMatlibVector<CReal> fvHalfWin(iHalf);
+	CMatlibVector<CReal> fvRet(iLen, VTY_TEMP);
+
+	for (int i = 0; i < iHalf; i++)
+		fvHalfWin[i] = (CReal) 2.0 * i / (iLen - 1);
+
+	/* Build complete output vector depending on odd or even input length */
+	if (iLen % 2)
+		fvRet.Merge(fvHalfWin, fvHalfWin(iHalf - 1, -1, 1)); /* Odd */
+	else
+		fvRet.Merge(fvHalfWin, fvHalfWin(iHalf, -1, 1)); /* Even */
+
+	return fvRet;
+}
+
+CMatlibVector<CReal> Triang(const int iLen)
+{
+	const int iHalf = (int) Ceil((CReal) iLen / 2);
+	CMatlibVector<CReal> fvHalfWin(iHalf);
+	CMatlibVector<CReal> fvRet(iLen, VTY_TEMP);
+
+	/* Build complete output vector depending on odd or even input length */
+	if (iLen % 2)
+	{
+		for (int i = 0; i < iHalf; i++)
+			fvHalfWin[i] = (CReal) 2.0 * (i + 1) / (iLen + 1);
+
+		fvRet.Merge(fvHalfWin, fvHalfWin(iHalf - 1, -1, 1)); /* Odd */
+	}
+	else
+	{
+		for (int i = 0; i < iHalf; i++)
+			fvHalfWin[i] = ((CReal) 2.0 * (i + 1) - 1) / iLen;
+
+		fvRet.Merge(fvHalfWin, fvHalfWin(iHalf, -1, 1)); /* Even */
+	}
+
+	return fvRet;
+}
+
+CMatlibVector<CReal> Kaiser(const int iLen, const CReal rBeta)
+{
+	CReal		rX;
+	const int	iIsOdd = iLen % 2;
+	const int	n = (iLen + 1) / 2; /* Half vector size, round up */
+	CMatlibVector<CReal> fvRet(iLen);
+	CMatlibVector<CReal> fvW(n);
+
+	const CReal rNorm = Abs(Besseli((CReal) 0.0, rBeta));
+	const CReal rXind = (iLen - 1) * (iLen - 1);
+
+	if (iIsOdd == 0)
+		rX = (CReal) 0.5;
+	else
+		rX = (CReal) 0.0;
+
+	for (int i = 0; i < n; i++)
+	{
+		fvW[i] = Besseli((CReal) 0.0, rBeta * Sqrt((CReal) 1.0 -
+			(CReal) 4.0 * rX * rX / rXind)) / rNorm;
+		rX += (CReal) 1.0;
+	}
+
+	/* Symmetrical window */
+	fvRet.Merge(fvW(n, -1, iIsOdd + 1), fvW);
+
+	return Abs(fvRet);
+}
+
+CReal Besseli(const CReal rNu, const CReal rZ)
+{
+	const CReal	rEp = (CReal) 10e-9; /* Define accuracy */
+	const CReal	rY = rZ / (CReal) 2.0;
+	CReal		rReturn = (CReal) 1.0;
+	CReal		rD = (CReal) 1.0;
+	CReal		rS = (CReal) 1.0;
+
+	/* Only nu = 0 is supported right now! */
+	if (rNu != (CReal) 0.0)
+	{
+#ifdef _DEBUG_
+		DebugError("MatLibr: Besseli function", "The nu = ", rNu, \
+			" is not supported, only nu = ", 0);
+#endif
+	}
+
+	for (int i = 1; i <= 25 && rReturn * rEp <= rS; i++)
+	{
+		rD *= rY / i;
+		rS = rD * rD;
+		rReturn += rS;
+	}
+
+	return rReturn;
+}
+
+CMatlibVector<CReal> Randn(const int iLen)
+{
+	/* Add some constant distributed random processes together */
+	_VECOP(CReal, iLen, 
+		(CReal) ((((CReal) 
+		rand() + rand() + rand() + rand() + rand() + rand() + rand()) 
+		/ RAND_MAX - 0.5) * /* sqrt(3) * 2 / sqrt(7) */ 1.3093));
+}
+
+CMatlibVector<CReal> Filter(const CMatlibVector<CReal>& fvB,
+							const CMatlibVector<CReal>& fvA,
+							const CMatlibVector<CReal>& fvX,
+							CMatlibVector<CReal>& fvZ)
+{
+	int						m, n, iLenCoeff;
+	const int				iSizeA = fvA.GetSize();
+	const int				iSizeB = fvB.GetSize();
+	const int				iSizeX = fvX.GetSize();
+	const int				iSizeZ = fvZ.GetSize();
+	CMatlibVector<CReal>	fvY(iSizeX, VTY_TEMP);
+	CMatlibVector<CReal>	fvANew, fvBNew;
+
+	if ((iSizeA == 1) && (fvA[0] == (CReal) 1.0))
+	{
+		/* FIR filter ------------------------------------------------------- */
+		const int				iSizeXNew = iSizeX + iSizeZ;
+		CMatlibVector<CReal>	rvXNew(iSizeXNew);
+
+		/* Add old values to input vector */
+		rvXNew.Merge(fvZ, fvX);
+
+		/* Actual convolution */
+		for (m = 0; m < iSizeX; m++)
+		{
+			fvY[m] = (CReal) 0.0;
+
+			for (n = 0; n < iSizeB; n++)
+				fvY[m] += fvB[n] * rvXNew[m + iSizeB - n - 1];
+		}
+
+		/* Save last samples in state vector */
+		fvZ = rvXNew(iSizeXNew - iSizeZ + 1, iSizeXNew);
+	}
+	else
+	{
+		/* IIR filter ------------------------------------------------------- */
+		/* Length of coefficients */
+		iLenCoeff = (int) Max((CReal) iSizeB, (CReal) iSizeA);
+
+		/* Make fvB and fvA the same length (zero padding) */
+		if (iSizeB > iSizeA)
+		{
+			fvBNew.Init(iSizeB);
+			fvANew.Init(iSizeB);
+
+			fvBNew = fvB;
+			fvANew.Merge(fvA, Zeros(iSizeB - iSizeA));
+		}
+		else
+		{
+			fvBNew.Init(iSizeA);
+			fvANew.Init(iSizeA);
+
+			fvANew = fvA;
+			fvBNew.Merge(fvB, Zeros(iSizeA - iSizeB));
+		}
+
+		/* Filter is implemented as a transposed direct form II structure */
+		for (m = 0; m < iSizeX; m++)
+		{
+			/* y(m) = (b(1) x(m) + z_1(m - 1)) / a(1) */
+			fvY[m] = (fvBNew[0] * fvX[m] + fvZ[0]) / fvANew[0];
+
+			for (n = 1; n < iLenCoeff; n++)
+			{
+				/* z_{n - 2}(m) = b(n - 1) x(m) + z_{n - 1}(m - 1) -
+				   a(n - 1) y(m) */
+				fvZ[n - 1] = fvBNew[n] * fvX[m] + fvZ[n] - fvANew[n] * fvY[m];
+			}
+		}
+	}
+
+	return fvY;
+}
+
+CMatlibVector<CReal> FirLP(const CReal rNormBW,
+						   const CMatlibVector<CReal>& rvWin)
+{
+/*
+	Lowpass filter design using windowing method
+*/
+	const int				iLen = rvWin.GetSize();
+	const int				iHalfLen = (int) Floor((CReal) iLen / 2);
+	CMatlibVector<CReal>	fvRet(iLen, VTY_TEMP);
+
+	/* Generate truncuated ideal response */
+	for (int i = 0; i < iLen; i++)
+		fvRet[i] = rNormBW * Sinc(rNormBW * (i - iHalfLen));
+
+	/* Apply window */
+	fvRet *= rvWin;
+
+	return fvRet;
+}
+
+CMatlibVector<CComplex> FirFiltDec(const CMatlibVector<CComplex>& cvB,
+								   const CMatlibVector<CComplex>& cvX,
+								   CMatlibVector<CComplex>& cvZ,
+								   const int iDecFact)
+{
+	int			m, n, iCurPos;
+	const int	iSizeX = cvX.GetSize();
+	const int	iSizeZ = cvZ.GetSize();
+	const int	iSizeB = cvB.GetSize();
+	const int	iSizeXNew = iSizeX + iSizeZ;
+	const int	iSizeFiltHist = iSizeB - 1;
+
+	int iNewLenZ;
+	int iDecSizeY;
+
+	if (iSizeFiltHist >= iSizeXNew)
+	{
+		 /* Special case if no new output can be calculated */
+		iDecSizeY = 0;
+
+		iNewLenZ = iSizeXNew;
+	}
+	else
+	{
+		/* Calculate the number of output bits which can be generated from the
+		   provided input vector */
+		iDecSizeY = 
+				(int) (((CReal) iSizeXNew - iSizeFiltHist - 1) / iDecFact + 1);
+
+		/* Since the input vector length must not be a multiple of "iDecFact",
+		   some input bits will be unused. To store this number, the size of
+		   the state vector "Z" is adapted */
+		iNewLenZ = iSizeFiltHist - 
+			(iDecSizeY * iDecFact - (iSizeXNew - iSizeFiltHist));
+	}
+
+	CMatlibVector<CComplex>	cvY(iDecSizeY, VTY_TEMP);
+	CMatlibVector<CComplex>	cvXNew(iSizeXNew);
+
+	/* Add old values to input vector */
+	cvXNew.Merge(cvZ, cvX);
+
+	/* FIR filter */
+	for (m = 0; m < iDecSizeY; m++)
+	{
+		iCurPos = m * iDecFact + iSizeFiltHist;
+
+		cvY[m] = (CReal) 0.0;
+
+		for (n = 0; n < iSizeB; n++)
+			cvY[m] += cvB[n] * cvXNew[iCurPos - n];
+	}
+
+	/* Save last samples in state vector */
+	cvZ.Init(iNewLenZ);
+	cvZ = cvXNew(iSizeXNew - iNewLenZ + 1, iSizeXNew);
+
+	return cvY;
+}
+
+CMatlibVector<CReal> Levinson(const CMatlibVector<CReal>& vecrRx,
+							  const CMatlibVector<CReal>& vecrB)
+{
+/* 
+	The levinson recursion [S. Haykin]
+
+	This algorithm solves the following equations:
+	Rp ap = ep u1,
+	Rp Xp = b, where Rp is a Toepliz-matrix of vector prRx and b = prB 
+	is an arbitrary correlation-vector. The Result is ap = prA.
+
+	Parts of the following code are taken from Ptolemy
+	(http://ptolemy.eecs.berkeley.edu/)
+*/
+	const int	iLength = vecrRx.GetSize();
+	CRealVector vecrX(iLength, VTY_TEMP);
+
+	CReal		rGamma;
+	CReal		rGammaCap;
+	CReal		rDelta;
+	CReal		rE;
+	CReal		rQ;
+	int			i, j;
+	CRealVector vecraP(iLength);
+	CRealVector vecrA(iLength);
+
+	/* Initialize the recursion --------------------------------------------- */
+	// (a) First coefficient is always unity
+	vecrA[0] = (CReal) 1.0;
+	vecraP[0] = (CReal) 1.0;
+
+	// (b) 
+	vecrX[0] = vecrB[0] / vecrRx[0];
+
+	// (c) Initial prediction error is simply the zero-lag of
+	// of the autocorrelation, or the signal power estimate.
+	rE = vecrRx[0];
+
+
+	/* Main loop ------------------------------------------------------------ */
+	// The order recurrence
+	for (j = 0; j < iLength - 1; j++)
+	{
+		const int iNextInd = j + 1;
+
+		// (a) Compute the new gamma
+		rGamma = vecrRx[iNextInd];
+		for (i = 1; i < iNextInd; i++) 
+			rGamma += vecrA[i] * vecrRx[iNextInd - i];
+
+		// (b), (d) Compute and output the reflection coefficient
+		// (which is also equal to the last AR parameter)
+		vecrA[j + 1] = rGammaCap = - rGamma / rE;
+
+		// (c)
+		for (i = 1; i < iNextInd; i++) 
+			vecraP[i] = vecrA[i] + rGammaCap * vecrA[iNextInd - i];
+
+		// Swap a and aP for next order recurrence
+		for (i = 1; i < iNextInd; i++)
+			vecrA[i] = vecraP[i];
+
+		// (e) Update the prediction error power
+		rE = rE * ((CReal) 1.0 - rGammaCap * rGammaCap);
+
+		// (f)
+		rDelta = (CReal) 0.0;
+		for (i = 0; i < iNextInd; i++) 
+			rDelta += vecrX[i] * vecrRx[iNextInd - i];
+
+		// (g), (i) 
+		vecrX[iNextInd] = rQ = (vecrB[iNextInd] - rDelta) / rE;
+
+		// (h)
+		for (i = 0; i < iNextInd; i++) 
+			vecrX[i] = vecrX[i] + rQ * vecrA[iNextInd - i];
+	}
+
+	return vecrX;
+}
+
+CMatlibVector<CComplex> Levinson(const CMatlibVector<CComplex>& veccRx,
+								 const CMatlibVector<CComplex>& veccB)
+{
+/* 
+	The levinson recursion [S. Haykin]
+	COMPLEX version!
+
+	This algorithm solves the following equations:
+	Rp ap = ep u1,
+	Rp Xp = b, where Rp is a Toepliz-matrix of vector prRx and b = prB 
+	is an arbitrary correlation-vector. The Result is ap = prA.
+
+	Parts of the following code are taken from Ptolemy
+	(http://ptolemy.eecs.berkeley.edu/)
+*/
+	const int		iLength = veccRx.GetSize();
+	CComplexVector	veccX(iLength, VTY_TEMP);
+
+	CComplex		cGamma;
+	CComplex		cGammaCap;
+	CComplex		cDelta;
+	CReal			rE;
+	CComplex		cQ;
+	int				i, j;
+	CComplexVector	veccaP(iLength);
+	CComplexVector	veccA(iLength);
+
+	/* Initialize the recursion --------------------------------------------- */
+	// (a) First coefficient is always unity
+	veccA[0] = (CReal) 1.0;
+	veccaP[0] = (CReal) 1.0;
+
+	// (b) 
+	veccX[0] = veccB[0] / veccRx[0];
+
+	// (c) Initial prediction error is simply the zero-lag of
+	// of the autocorrelation, or the signal power estimate.
+	rE = Real(veccRx[0]);
+
+
+	/* Main loop ------------------------------------------------------------ */
+	// The order recurrence
+	for (j = 0; j < iLength - 1; j++)
+	{
+		const int iNextInd = j + 1;
+
+		// (a) Compute the new gamma
+		cGamma = veccRx[iNextInd];
+		for (i = 1; i < iNextInd; i++) 
+			cGamma += veccA[i] * veccRx[iNextInd - i];
+
+		// (b), (d) Compute and output the reflection coefficient
+		// (which is also equal to the last AR parameter)
+		veccA[iNextInd] = cGammaCap = - cGamma / rE;
+
+		// (c)
+		for (i = 1; i < iNextInd; i++) 
+			veccaP[i] = veccA[i] + cGammaCap * Conj(veccA[iNextInd - i]);
+
+		// Swap a and aP for next order recurrence
+		for (i = 1; i < iNextInd; i++)
+			veccA[i] = veccaP[i];
+
+		// (e) Update the prediction error power
+		rE = rE * ((CReal) 1.0 - SqMag(cGammaCap));
+
+		// (f)
+		cDelta = (CReal) 0.0;
+		for (i = 0; i < iNextInd; i++) 
+			cDelta += veccX[i] * veccRx[iNextInd - i];
+
+		// (g), (i) 
+		veccX[iNextInd] = cQ = (veccB[iNextInd] - cDelta) / rE;
+
+		// (h)
+		for (i = 0; i < iNextInd; i++) 
+			veccX[i] = veccX[i] + cQ * Conj(veccA[iNextInd - i]);
+	}
+
+	return veccX;
+}
+
+CMatlibVector<CReal> DomEig(const CMatlibMatrix<CReal>& rmI,
+							const CReal rEpsilon)
+{
+	const int				iMaxNumIt = 150; /* Maximum number of iterations */
+	const int				iSize = rmI.GetColSize();
+	CMatlibVector<CReal>	vecrV(iSize, VTY_TEMP);
+	CMatlibVector<CReal>	vecrVold(iSize);
+	CMatlibVector<CReal>	vecrY(iSize);
+	CReal					rLambda, rLambdaold, rError;
+
+	/* Implementing the power method for getting the dominant eigenvector */
+	/* Start value for eigenvector */
+	vecrV = Ones(iSize);
+	rLambda = rLambdaold = (CReal) 1.0;
+	rError = _MAXREAL;
+	int iItCnt = iMaxNumIt;
+
+	while ((iItCnt > 0) && (rError > rEpsilon))
+	{
+		/* Save old values needed error calculation */
+		vecrVold = vecrV;
+		rLambdaold = rLambda;
+
+		/* Actual power method calculations */
+		rLambda = Max(Abs(vecrV));
+		vecrV = (CReal) 1.0 / rLambda * rmI * vecrV;
+
+		/* Take care of number of iterations and error calculations */
+		iItCnt--;
+		rError =
+			Max(Abs(rLambda - rLambdaold), Max(Abs(vecrV - vecrVold)));
+	}
+
+	return vecrV;
+}
+
+CReal LinRegr(const CMatlibVector<CReal>& rvX, const CMatlibVector<CReal>& rvY)
+{
+	/* Linear regression */
+	CReal Xm(Mean(rvX));
+	CReal Ym(Mean(rvY));
+
+	CRealVector Xmrem(rvX - Xm); /* Remove mean of W */
+
+	/* Return only the gradient, we do not calculate and return the offset */
+	return Sum(Xmrem * (rvY - Ym)) / Sum(Xmrem * Xmrem);
+}
diff --git a/qsstv/drmtx/common/matlib/MatlibSigProToolbox.h b/qsstv/drmtx/common/matlib/MatlibSigProToolbox.h
new file mode 100644
index 0000000..cc27032
--- /dev/null
+++ b/qsstv/drmtx/common/matlib/MatlibSigProToolbox.h
@@ -0,0 +1,136 @@
+/******************************************************************************\
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	c++ Mathematic Library (Matlib), signal processing toolbox
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#ifndef _MATLIB_SIGNAL_PROC_TOOLBOX_H_
+#define _MATLIB_SIGNAL_PROC_TOOLBOX_H_
+
+#include "Matlib.h"
+#include "MatlibStdToolbox.h"
+#include <cstdlib>
+
+
+/* Helpfunctions **************************************************************/
+/* Randomize functions */
+CMatlibVector<CReal>	Randn(const int iLen);
+inline
+CMatlibVector<CReal>	Rand(const int iLen)
+							{_VECOP(CReal, iLen, (CReal) rand() / RAND_MAX);}
+
+
+/* Window functions */
+CMatlibVector<CReal>	Hann(const int iLen);
+CMatlibVector<CReal>	Hamming(const int iLen);
+CMatlibVector<CReal>	Nuttallwin(const int iLen);
+CMatlibVector<CReal>	Bartlett(const int iLen);
+CMatlibVector<CReal>	Triang(const int iLen);
+CMatlibVector<CReal>	Kaiser(const int iLen, const CReal rBeta);
+
+
+/* Bessel function */
+CReal					Besseli(const CReal rNu, const CReal rZ);
+
+
+/* Filter data with a recursive (IIR) or nonrecursive (FIR) filter */
+CMatlibVector<CReal>	Filter(const CMatlibVector<CReal>& fvB, 
+							   const CMatlibVector<CReal>& fvA, 
+							   const CMatlibVector<CReal>& fvX, 
+							   CMatlibVector<CReal>& fvZ);
+
+
+/* Levinson durbin recursion */
+CMatlibVector<CReal>	Levinson(const CMatlibVector<CReal>& vecrRx, 
+								 const CMatlibVector<CReal>& vecrB);
+CMatlibVector<CComplex>	Levinson(const CMatlibVector<CComplex>& veccRx, 
+								 const CMatlibVector<CComplex>& veccB);
+
+
+/* Sinc-function */
+inline CReal			Sinc(const CReal& rI)
+							{return rI == (CReal) 0.0 ? (CReal) 1.0 : sin(crPi * rI) / (crPi * rI);}
+inline
+CMatlibVector<CReal>	Sinc(const CMatlibVector<CReal>& fvI)
+							{_VECOP(CReal, fvI.GetSize(), Sinc(fvI[i]));}
+
+
+/* My own functions --------------------------------------------------------- */
+/* Dominant eigenvector */
+CMatlibVector<CReal>	DomEig(const CMatlibMatrix<CReal>& rmI, const CReal rEpsilon = 1e-5);
+
+/* Linear regression */
+CReal					LinRegr(const CMatlibVector<CReal>& rvX, const CMatlibVector<CReal>& rvY);
+
+/* Lowpass filter design using windowing method */
+CMatlibVector<CReal>	FirLP(const CReal rNormBW,
+							  const CMatlibVector<CReal>& rvWin);
+
+/* Complex FIR filter with decimation */
+CMatlibVector<CComplex>	FirFiltDec(const CMatlibVector<CComplex>& cvB,
+								   const CMatlibVector<CComplex>& cvX,
+								   CMatlibVector<CComplex>& cvZ,
+								   const int iDecFact);
+
+/* Squared magnitude */
+inline CReal			SqMag(const CComplex& cI)
+							{return cI.real() * cI.real() + cI.imag() * cI.imag();}
+inline CReal			SqMag(const CReal& rI)
+							{return rI * rI;}
+inline
+CMatlibVector<CReal>	SqMag(const CMatlibVector<CComplex>& veccI)
+							{_VECOP(CReal, veccI.GetSize(), SqMag(veccI[i]));}
+inline
+CMatlibVector<CReal>	SqMag(const CMatlibVector<CReal>& vecrI)
+							{_VECOP(CReal, vecrI.GetSize(), SqMag(vecrI[i]));}
+
+/* One pole recursion (first order IIR)
+   y_n = lambda * y_{n - 1} + (1 - lambda) * x_n */
+inline void				IIR1(CReal& rY, const CReal& rX, const CReal rLambda)
+							{rY = rLambda * (rY - rX) + rX;}
+
+inline void				IIR1(CComplex& cY, const CComplex& cX, const CReal rLambda)
+							{cY = rLambda * (cY - cX) + cX;}
+
+inline void				IIR1(CMatlibVector<CReal>& rY,
+							 const CMatlibVector<CReal>& rX,
+							 const CReal rLambda)
+{
+	const int iSize = rY.GetSize();
+
+	for (int i = 0; i < iSize; i++)
+		IIR1(rY[i], rX[i], rLambda);
+}
+
+/* Two-sided one pole recursion */
+inline void				IIR1TwoSided(CReal& rY, const CReal& rX,
+									 const CReal rLamUp, const CReal rLamDown)
+							{rX > rY ? IIR1(rY, rX, rLamUp) : IIR1(rY, rX, rLamDown);}
+
+/* Get lambda for one-pole recursion from time constant */
+inline CReal			IIR1Lam(const CReal& rTau, const CReal& rFs)
+							{return exp((CReal) -1.0 / (rTau * rFs));}
+
+
+#endif	/* _MATLIB_SIGNAL_PROC_TOOLBOX_H_ */
diff --git a/qsstv/drmtx/common/matlib/MatlibStdToolbox.cpp b/qsstv/drmtx/common/matlib/MatlibStdToolbox.cpp
new file mode 100644
index 0000000..7949bc1
--- /dev/null
+++ b/qsstv/drmtx/common/matlib/MatlibStdToolbox.cpp
@@ -0,0 +1,777 @@
+/******************************************************************************\
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	c++ Mathematic Library (Matlib)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "MatlibStdToolbox.h"
+
+
+/* Implementation *************************************************************/
+CReal Min(const CMatlibVector<CReal>& rvI)
+{
+	const int iSize = rvI.GetSize();
+	CReal rMinRet = rvI[0];
+	for (int i = 1; i < iSize; i++)
+	{
+		if (rvI[i] < rMinRet)
+			rMinRet = rvI[i];
+	}
+
+	return rMinRet;
+}
+
+void Min(CReal& rMinVal, int& iMinInd, const CMatlibVector<CReal>& rvI)
+{
+	const int iSize = rvI.GetSize();
+	rMinVal = rvI[0]; /* Init actual minimum value */
+	iMinInd = 0; /* Init index of minimum */
+	for (int i = 1; i < iSize; i++)
+	{
+		if (rvI[i] < rMinVal)
+		{
+			rMinVal = rvI[i];
+			iMinInd = i;
+		}
+	}
+}
+
+CReal Max(const CMatlibVector<CReal>& rvI)
+{
+	CReal rMaxRet;
+	int iMaxInd; /* Not used by this function */
+	Max(rMaxRet, iMaxInd, rvI);
+
+	return rMaxRet;
+}
+
+void Max(CReal& rMaxVal, int& iMaxInd, const CMatlibVector<CReal>& rvI)
+{
+	const int iSize = rvI.GetSize();
+	rMaxVal = rvI[0]; /* Init actual maximum value */
+	iMaxInd = 0; /* Init index of maximum */
+	for (int i = 1; i < iSize; i++)
+	{
+		if (rvI[i] > rMaxVal)
+		{
+			rMaxVal = rvI[i];
+			iMaxInd = i;
+		}
+	}
+}
+
+CMatlibVector<CReal> Sort(const CMatlibVector<CReal>& rvI)
+{
+	const int iSize = rvI.GetSize();
+	const int iEnd = iSize - 1;
+	CMatlibVector<CReal> fvRet(iSize, VTY_TEMP);
+
+	/* Copy input vector in output vector */
+	fvRet = rvI;
+
+	/* Loop through the array one less than its total cell count */
+	for (int i = 0; i < iEnd; i++)
+	{
+		/* Loop through every cell (value) in array */
+		for (int j = 0; j < iEnd; j++)
+		{
+			/* Compare the values and switch if necessary */
+			if (fvRet[j] > fvRet[j + 1])
+			{
+				const CReal rSwap = fvRet[j];
+				fvRet[j] = fvRet[j + 1];
+				fvRet[j + 1] = rSwap;
+			}
+		}
+	}
+
+	return fvRet;
+}
+
+CMatlibMatrix<CReal> Eye(const int iLen)
+{
+	CMatlibMatrix<CReal> matrRet(iLen, iLen, VTY_TEMP);
+
+	/* Set all values except of the diagonal to zero, diagonal entries = 1 */
+	for (int i = 0; i < iLen; i++)
+	{
+		for (int j = 0; j < iLen; j++)
+		{
+			if (i == j)
+				matrRet[i][j] = (CReal) 1.0;
+			else
+				matrRet[i][j] = (CReal) 0.0;
+		}
+	}
+
+	return matrRet;
+}
+
+CMatlibMatrix<CComplex> Diag(const CMatlibVector<CComplex>& cvI)
+{
+	const int iSize = cvI.GetSize();
+	CMatlibMatrix<CComplex> matcRet(iSize, iSize, VTY_TEMP);
+
+	/* Set the diagonal to the values of the input vector */
+	for (int i = 0; i < iSize; i++)
+	{
+		for (int j = 0; j < iSize; j++)
+		{
+			if (i == j)
+				matcRet[i][j] = cvI[i];
+			else
+				matcRet[i][j] = (CReal) 0.0;
+		}
+	}
+
+	return matcRet;
+}
+
+CReal Trace(const CMatlibMatrix<CReal>& rmI)
+{
+	const int iSize = rmI.GetRowSize(); /* matrix must be square */
+	CReal rReturn = (CReal) 0.0;
+
+	for (int i = 0; i < iSize; i++)
+		rReturn += rmI[i][i];
+
+	return rReturn;
+}
+
+CMatlibMatrix<CComplex> Toeplitz(const CMatlibVector<CComplex>& cvI)
+{
+	const int				iSize = cvI.GetSize();
+	CMatlibMatrix<CComplex>	matcRet(iSize, iSize, VTY_TEMP);
+
+	/* Create Toeplitz matrix */
+	for (int i = 0; i < iSize; i++)
+	{
+		for (int j = 0; j < iSize; j++)
+		{
+			if (i < j)
+				matcRet[i][j] = cvI[j - i];
+			else
+				matcRet[i][j] = Conj(cvI[i - j]);
+		}
+	}
+
+	return matcRet;
+}
+
+CMatlibMatrix<CComplex> Transp(const CMatlibMatrix<CComplex>& cmI)
+{
+	const int iRowSize = cmI.GetRowSize();
+	const int iColSize = cmI.GetColSize();
+
+	/* Swaped row and column size due to transpose operation */
+	CMatlibMatrix<CComplex> matcRet(iColSize, iRowSize, VTY_TEMP);
+
+	/* Transpose matrix */
+	for (int i = 0; i < iRowSize; i++)
+	{
+		for (int j = 0; j < iColSize; j++)
+			matcRet[j][i] = cmI[i][j];
+	}
+
+	return matcRet;
+}
+
+CMatlibMatrix<CComplex> Inv(const CMatlibMatrix<CComplex>& matrI)
+{
+/*
+	Parts of the following code are taken from Ptolemy
+	(http://ptolemy.eecs.berkeley.edu/)
+
+	The input matrix must be square, this is NOT checked here!
+*/
+	_COMPLEX	temp;
+	int			row, col, i;
+
+	const int iSize = matrI.GetColSize();
+	CMatlibMatrix<CComplex> matrRet(iSize, iSize, VTY_TEMP);
+
+	/* Make a working copy of input matrix */
+	CMatlibMatrix<CComplex> work(matrI);
+
+	/* Set result to be the identity matrix */
+	matrRet = Eye(iSize);
+
+	for (i = 0; i < iSize; i++) 
+	{
+		/* Check that the element in (i,i) is not zero */
+		if ((Real(work[i][i]) == 0) && (Imag(work[i][i]) == 0))
+		{
+			/* Swap with a row below this one that has a non-zero element
+			   in the same column */
+			for (row = i + 1; row < iSize; row++)
+			{
+				if ((Real(work[i][i]) != 0) || (Imag(work[i][i]) != 0))
+					break;
+			}
+
+// TEST
+if (row == iSize)
+{
+printf("couldn't invert matrix, possibly singular.\n");
+	matrRet = Eye(iSize);
+	return matrRet;
+}
+
+			/* Swap rows */
+			for (col = 0; col < iSize; col++)
+			{
+				temp = work[i][col];
+				work[i][col] = work[row][col];
+				work[row][col] = temp;
+				temp = matrRet[i][col];
+				matrRet[i][col] = matrRet[row][col];
+				matrRet[row][col] = temp;
+			}
+		}
+
+		/* Divide every element in the row by element (i,i) */
+		temp = work[i][i];
+		for (col = 0; col < iSize; col++)
+		{
+			work[i][col] /= temp;
+			matrRet[i][col] /= temp;
+		}
+
+		/* Zero out the rest of column i */
+		for (row = 0; row < iSize; row++)
+		{
+			if (row != i)
+			{
+				temp = work[row][i];
+				for (col = iSize - 1; col >= 0; col--)
+				{
+					work[row][col] -= (temp * work[i][col]);
+					matrRet[row][col] -= (temp * matrRet[i][col]);
+				}
+			}
+		}
+	}
+
+	return matrRet;
+}
+
+/* This function is not listed in the header file. It shall be used only for
+   Matlib internal calculations */
+CComplex _integral(MATLIB_CALLBACK_QAUD f, const CReal a, const CReal b,
+				   const CReal errorBound, CReal& integralBound,
+				   _BOOLEAN& integralError, const CReal ru)
+{
+/*
+	The following code (inclusive the actual Quad() function) is based on a
+	JavaScript Example written by Lucio Tavernini. The code is hosted at
+	http://tavernini.com/integral.shtml.
+
+	Description: Adaptive Simpson's Quadrature
+
+	_integral(f, a, b, errorBound) attempts to integrate f from
+	a to b while keeping the asymptotic error estimate below
+	errorBound using an adaptive implementation of Simpson's rule.
+
+	Notes: Instead of NaN we use _MAXREAL. Infinite bounds are not allowed! The
+	lower bound must always be smaller than the higher bound!
+*/
+
+	CReal		left, right, h, h6, bound;
+	CComplex	fa, fb, v1, v2, error, value;
+	int			m1, jend, mstart, j;
+
+	if (integralError)
+		return _MAXREAL; /* NaN */
+
+	/* Integrate over [a,b]. Initialize */
+	const int max = 1024;
+	CRealVector x(max);
+	CComplexVector f1(max);
+	CComplexVector f2(max);
+	CComplexVector f3(max);
+	CComplexVector v(max);
+
+	int step = 1;
+	int m = 1;
+	bound = errorBound;
+	value = 0;
+	h = b - a;
+	x[0] = a;
+	f1[0] = f(a);
+	f2[0] = f((CReal) 0.5 * (a + b));
+	f3[0] = f(b);
+	v[0] = h * (f1[0] + (CReal) 4.0 * f2[0] + f3[0]) / (CReal) 6.0;
+
+	do
+	{
+		/* Are we going to go forward or backward? */
+		if (step == -1)
+		{
+			/* Forward: j = m,...,max */
+			step = 1;
+			j = m + 1;
+			jend = max;
+			m = 0;
+			mstart = 0;
+		}
+		else
+		{
+			/* Backward: j = m,...,1 */
+			step = -1;
+			j = m - 1;
+			jend = -1;
+			m = max - 1;
+			mstart = max - 1;
+		}
+		
+		h = (CReal) 0.5 * h;
+		h6 = h / 6;
+		bound = (CReal) 0.5 * bound;
+		
+		do
+		{
+			left = x[j];
+			right = x[j] + (CReal) 0.5 * h;
+
+			/* Complete loss of significance? */
+			if (left >= right)
+			{
+				printf("integral: Error 1");
+				return value;
+			}
+
+			fa = f(x[j] + (CReal) 0.5 * h);
+			fb = f(x[j] + (CReal) 1.5 * h);
+			v1 = h6 * (f1[j] + (CReal) 4.0 * fa + f2[j]);
+			v2 = h6 * (f2[j] + (CReal) 4.0 * fb + f3[j]);
+			error = (v[j] - v1 - v2) / (CReal) 15.0;
+
+			if ((Abs(error) <= bound) || (Abs(v1 + v2) < Abs(value) * ru))
+				value = ((v1 + v2) + value) - error;
+			else
+			{
+				if (integralError)
+					return _MAXREAL; /* NaN */
+				
+				/* Are we out of memory? */
+				if (m == j)
+				{
+					left = x[j];
+					right = x[j] + (CReal) 0.5 * h;
+
+					/* Complete loss of significance? */
+					if (left >= right)
+					{
+						printf("integral: Error 2");
+						return value;
+					}
+
+					value += _integral(f, left, x[j] + 2 * h, bound,
+						integralBound, integralError, ru);
+				}
+				else 
+				{
+					/* No, we are not */
+					left = x[j];
+					right = x[j] + (CReal) 0.125 * h;
+
+					if (left >= right)
+					{
+						/* The error bound specified is too small! */
+						integralError = true;
+						return _MAXREAL; /* NaN */
+					}
+
+					m1 = m + step;
+					x[m] = x[j];
+					x[m1] = x[j] + h;
+					v[m] = v1;
+					v[m1] = v2;
+					f1[m] = f1[j];
+					f2[m] = fa;
+					f3[m] = f2[j];
+					f1[m1] = f2[j];
+					f2[m1] = fb;
+					f3[m1] = f3[j];
+					m += 2 * step;
+				}
+			}
+			j += step;
+		}
+		while (j != jend);
+	}
+	while (m != mstart);
+
+	if (integralError)
+		return _MAXREAL; /* NaN */
+	else
+		return value;
+}
+
+CComplex Quad(MATLIB_CALLBACK_QAUD f, const CReal a, const CReal b,
+			  const CReal errorBound)
+{
+	/* Set globals */
+	/* Generate rounding unit */
+	CReal value;
+	CReal ru = (CReal) 1.0;
+	do
+	{
+		ru = (CReal) 0.5 * ru;
+		value = (CReal) 1.0 + ru;
+	}
+	while (value != (CReal) 1.0);
+
+	ru *= 2;
+
+	CReal integralBound = errorBound;
+	_BOOLEAN integralError = false;
+
+	/* Compute */
+	return _integral(f, a, b, errorBound, integralBound, integralError, ru);
+}
+
+CMatlibVector<CComplex> Fft(const CMatlibVector<CComplex>& cvI,
+							const CFftPlans& FftPlans)
+{
+	int				i;
+	CFftPlans*		pCurPlan;
+	fftw_complex*	pFftwComplexIn;
+	fftw_complex*	pFftwComplexOut;
+
+	const int				n(cvI.GetSize());
+
+	CMatlibVector<CComplex>	cvReturn(n, VTY_TEMP);
+
+	/* If input vector has zero length, return */
+	if (n == 0)
+		return cvReturn;
+
+	/* Check, if plans are already created, else: create it */
+	if (!FftPlans.IsInitialized())
+	{
+		pCurPlan = new CFftPlans;
+		pCurPlan->Init(n);
+	}
+	else
+	{
+		/* Ugly, but ok: We transform "const" object in "non constant" object
+		   since we KNOW that the original object is not constant since it
+		   was already initialized! */
+		pCurPlan = (CFftPlans*) &FftPlans;
+	}
+
+	pFftwComplexIn = pCurPlan->pFftwComplexIn;
+	pFftwComplexOut = pCurPlan->pFftwComplexOut;
+
+	/* fftw (Homepage: http://www.fftw.org/) */
+	for (i = 0; i < n; i++)
+	{
+		pFftwComplexIn[i][0] = cvI[i].real();
+		pFftwComplexIn[i][1] = cvI[i].imag();     /* pa0mbo [0] [1] was .re .im */
+
+	}
+
+	/* Actual fftw call */
+	fftw_execute(pCurPlan->FFTPlForw);
+
+	for (i = 0; i < n; i++)
+		cvReturn[i] = CComplex(pFftwComplexOut[i][0], pFftwComplexOut[i][1]);
+
+	if (!FftPlans.IsInitialized())
+		delete pCurPlan;
+
+	return cvReturn;
+}
+
+CMatlibVector<CComplex> Ifft(const CMatlibVector<CComplex>& cvI,
+							 const CFftPlans& FftPlans)
+{
+	int				i;
+	CFftPlans*		pCurPlan;
+	fftw_complex*	pFftwComplexIn;
+	fftw_complex*	pFftwComplexOut;
+
+	const int		n(cvI.GetSize());
+	CMatlibVector<CComplex>	cvReturn(n, VTY_TEMP);
+
+	/* If input vector has zero length, return */
+	if (n == 0)
+		return cvReturn;
+
+	/* Check, if plans are already created, else: create it */
+	if (!FftPlans.IsInitialized())
+	{
+		pCurPlan = new CFftPlans;
+		pCurPlan->Init(n);
+	}
+	else
+	{
+		/* Ugly, but ok: We transform "const" object in "non constant" object
+		   since we KNOW that the original object is not constant since it
+		   was already initialized! */
+		pCurPlan = (CFftPlans*) &FftPlans;
+	}
+
+	pFftwComplexIn = pCurPlan->pFftwComplexIn;
+	pFftwComplexOut = pCurPlan->pFftwComplexOut;
+
+	/* fftw (Homepage: http://www.fftw.org/) */
+	for (i = 0; i < n; i++)
+	{
+		pFftwComplexIn[i][0] = cvI[i].real();
+		pFftwComplexIn[i][1] = cvI[i].imag();
+        //        printf("vullen pFftwComplexIn %d %14.10f %14.10f \n",i, cvI[i].real(), cvI[i].imag());
+	}
+     //   printf("na vullen pFftwComplexIn \n");
+
+	/* Actual fftw call */
+	fftw_execute(pCurPlan->FFTPlBackw);
+      //  printf("Na execute plan in Ifft n is %d \n", n);	
+	const CReal scale = (CReal) 1.0 / n;
+	for (i = 0; i < n; i++)
+	{
+		cvReturn[i] = CComplex(pFftwComplexOut[i][0] * scale,
+			pFftwComplexOut[i][1] * scale);
+       //         printf("returnvalue %d is %g %g \n", i, pFftwComplexOut[i][0], pFftwComplexOut[i][1]);
+	}
+
+	if (!FftPlans.IsInitialized())
+		 delete pCurPlan;
+
+	return cvReturn;
+}
+
+CMatlibVector<CComplex> rfft(const CMatlibVector<CReal>& fvI,
+							 const CFftPlans& FftPlans)
+{
+	int			i;
+	CFftPlans*	pCurPlan;
+	double *	pFftwRealIn;
+	double *	pFftwRealOut;
+
+	const int	iSizeI = fvI.GetSize();
+	const int	iLongLength(iSizeI);
+	const int	iShortLength(iLongLength / 2);
+	const int	iUpRoundShortLength((iLongLength + 1) / 2);
+	
+	CMatlibVector<CComplex>	cvReturn(iShortLength
+		/* Include Nyquist frequency in case of even N */ + 1, VTY_TEMP);
+
+	/* If input vector has zero length, return */
+	if (iLongLength == 0)
+		return cvReturn;
+
+	/* Check, if plans are already created, else: create it */
+	if (!FftPlans.IsInitialized())
+	{
+		pCurPlan = new CFftPlans;
+		pCurPlan->Init(iLongLength);
+	}
+	else
+	{
+		/* Ugly, but ok: We transform "const" object in "non constant" object
+		   since we KNOW that the original object is not constant since it
+		   was already initialized! */
+		pCurPlan = (CFftPlans*) &FftPlans;
+	}
+
+	pFftwRealIn = pCurPlan->pFftwRealIn;
+	pFftwRealOut = pCurPlan->pFftwRealOut;
+
+	/* fftw (Homepage: http://www.fftw.org/) */
+	for (i = 0; i < iSizeI; i++)
+		pFftwRealIn[i] = fvI[i];
+
+	/* Actual fftw call */
+	fftw_execute(pCurPlan->RFFTPlForw);
+
+	/* Now build complex output vector */
+	/* Zero frequency */
+	cvReturn[0] = pFftwRealOut[0];
+	for (i = 1; i < iUpRoundShortLength; i++)
+		cvReturn[i] = CComplex(pFftwRealOut[i], pFftwRealOut[iLongLength - i]);
+
+	/* If N is even, include Nyquist frequency */
+	if (iLongLength % 2 == 0)
+		cvReturn[iShortLength] = pFftwRealOut[iShortLength];
+
+	if (!FftPlans.IsInitialized())
+		delete pCurPlan;
+
+	return cvReturn;
+}
+
+
+CMatlibVector<CReal> rifft(const CMatlibVector<CComplex>& cvI,
+						   const CFftPlans& FftPlans)
+{
+/*
+	This function only works with EVEN N!
+*/
+	int			i;
+	CFftPlans*	pCurPlan;
+	double*	pFftwRealIn;
+	double*	pFftwRealOut;
+
+	const int	iShortLength(cvI.GetSize() - 1); /* Nyquist frequency! */
+	const int	iLongLength(iShortLength * 2);
+
+	CMatlibVector<CReal> fvReturn(iLongLength, VTY_TEMP);
+
+	/* If input vector is too short, return */
+	if (iShortLength <= 0)
+		return fvReturn;
+
+	/* Check, if plans are already created, else: create it */
+	if (!FftPlans.IsInitialized())
+	{
+		pCurPlan = new CFftPlans;
+		pCurPlan->Init(iLongLength);
+	}
+	else
+	{
+		/* Ugly, but ok: We transform "const" object in "non constant" object
+		   since we KNOW that the original object is not constant since it
+		   was already initialized! */
+		pCurPlan = (CFftPlans*) &FftPlans;
+	}
+
+	pFftwRealIn = pCurPlan->pFftwRealIn;
+	pFftwRealOut = pCurPlan->pFftwRealOut;
+
+	/* Now build half-complex-vector */
+	pFftwRealIn[0] = cvI[0].real();
+	for (i = 1; i < iShortLength; i++)
+	{
+		pFftwRealIn[i] = cvI[i].real();
+		pFftwRealIn[iLongLength - i] = cvI[i].imag();
+	}
+	/* Nyquist frequency */
+	pFftwRealIn[iShortLength] = cvI[iShortLength].real(); 
+
+	/* Actual fftw call */
+	fftw_execute(pCurPlan->RFFTPlBackw);
+
+	/* Scale output vector */
+	const CReal scale = (CReal) 1.0 / iLongLength;
+	for (i = 0; i < iLongLength; i++) 
+		fvReturn[i] = pFftwRealOut[i] * scale;
+
+	if (!FftPlans.IsInitialized())
+		delete pCurPlan;
+
+	return fvReturn;
+}
+
+CMatlibVector<CReal> FftFilt(const CMatlibVector<CComplex>& rvH,
+							 const CMatlibVector<CReal>& rvI,
+							 CMatlibVector<CReal>& rvZ,
+							 const CFftPlans& FftPlans)
+{
+/*
+	This function only works with EVEN N!
+*/
+	CFftPlans*				pCurPlan;
+	const int				iL(rvH.GetSize() - 1); /* Nyquist frequency! */
+	const int				iL2(2 * iL);
+	CMatlibVector<CReal>	rvINew(iL2);
+	CMatlibVector<CReal>	rvOutTMP(iL2);
+
+	/* Check, if plans are already created, else: create it */
+	if (!FftPlans.IsInitialized())
+	{
+		pCurPlan = new CFftPlans;
+		pCurPlan->Init(iL2);
+	}
+	else
+	{
+		/* Ugly, but ok: We transform "const" object in "non constant" object
+		   since we KNOW that the original object is not constant since it
+		   was already initialized! */
+		pCurPlan = (CFftPlans*) &FftPlans;
+	}
+
+	/* Update history of input signal */
+	rvINew.Merge(rvZ, rvI);
+
+	rvOutTMP = rifft(rfft(rvINew, FftPlans) * rvH, FftPlans);
+
+	/* Save old input signal vector for next block */
+	rvZ = rvI;
+
+	/* Cut out correct samples (to get from cyclic convolution to linear
+	   convolution) */
+	return rvOutTMP(iL + 1, iL2);
+}
+
+
+/* FftPlans implementation -------------------------------------------------- */
+CFftPlans::~CFftPlans()
+{
+	if (bInitialized)
+	{
+		/* Delete old plans and intermediate buffers */
+		fftw_destroy_plan(RFFTPlForw);
+		fftw_destroy_plan(RFFTPlBackw);
+		fftw_destroy_plan(FFTPlForw);
+		fftw_destroy_plan(FFTPlBackw);
+
+		fftw_free(pFftwRealIn);
+		fftw_free(pFftwRealOut);
+		fftw_free(pFftwComplexIn);
+		fftw_free(pFftwComplexOut);
+	}
+}
+
+void CFftPlans::Init(const int iFSi)
+{
+	if (bInitialized)
+	{
+		/* Delete old plans and intermediate buffers */
+		fftw_destroy_plan(RFFTPlForw);
+		fftw_destroy_plan(RFFTPlBackw);
+		fftw_destroy_plan(FFTPlForw);
+		fftw_destroy_plan(FFTPlBackw);
+
+		fftw_free(pFftwRealIn);
+		fftw_free(pFftwRealOut);
+		fftw_free(pFftwComplexIn);
+		fftw_free(pFftwComplexOut);
+	}
+
+	/* Create new plans and intermediate buffers */
+	pFftwRealIn = (double *) fftw_malloc(sizeof(double)*2*iFSi);
+	pFftwRealOut = (double *) fftw_malloc(sizeof(double)*2*iFSi);
+	pFftwComplexIn = (fftw_complex *) fftw_malloc(sizeof(fftw_complex)*iFSi);
+	pFftwComplexOut = (fftw_complex *) fftw_malloc(sizeof(fftw_complex)*iFSi);
+
+	RFFTPlForw = fftw_plan_r2r_1d(iFSi, pFftwRealIn, pFftwRealOut,  FFTW_R2HC, FFTW_ESTIMATE);
+	RFFTPlBackw = fftw_plan_r2r_1d(iFSi, pFftwRealIn,pFftwRealOut,  FFTW_HC2R, FFTW_ESTIMATE);
+	FFTPlForw = fftw_plan_dft_1d(iFSi, pFftwComplexIn, pFftwComplexOut, FFTW_FORWARD, FFTW_ESTIMATE);
+	FFTPlBackw = fftw_plan_dft_1d(iFSi, pFftwComplexIn, pFftwComplexOut, FFTW_BACKWARD, FFTW_ESTIMATE);
+
+	bInitialized = true;
+}
+
diff --git a/qsstv/drmtx/common/matlib/MatlibStdToolbox.h b/qsstv/drmtx/common/matlib/MatlibStdToolbox.h
new file mode 100644
index 0000000..ddcb32d
--- /dev/null
+++ b/qsstv/drmtx/common/matlib/MatlibStdToolbox.h
@@ -0,0 +1,291 @@
+/******************************************************************************\
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	c++ Mathematic Library (Matlib), standard toolbox
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#ifndef _MATLIB_STD_TOOLBOX_H_
+#define _MATLIB_STD_TOOLBOX_H_
+
+#include "Matlib.h"
+
+/* fftw (Homepage: http://www.fftw.org) */
+#include <fftw3.h>
+
+
+/* Classes ********************************************************************/
+class CFftPlans
+{
+public:
+	CFftPlans() : RFFTPlForw(NULL), RFFTPlBackw(NULL), bInitialized(false) {}
+	CFftPlans(const int iFftSize) : RFFTPlForw(NULL), RFFTPlBackw(NULL), bInitialized(false) {Init(iFftSize);}
+	virtual ~CFftPlans();
+
+	void Init(const int iFSi);
+	inline bool IsInitialized() const {return bInitialized;}
+
+	fftw_plan		RFFTPlForw;
+	fftw_plan		RFFTPlBackw;
+	fftw_plan		FFTPlForw;
+	fftw_plan		FFTPlBackw;
+
+	double *		pFftwRealIn;
+	double *		pFftwRealOut;
+	fftw_complex*	pFftwComplexIn;
+	fftw_complex*	pFftwComplexOut;
+
+protected:
+	void Clean();
+	bool bInitialized;
+};
+
+
+/* Helpfunctions **************************************************************/
+inline CReal				Min(const CReal& rA, const CReal& rB)
+								{return rA < rB ? rA : rB;}
+inline CMatlibVector<CReal>	Min(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+								{_VECOP(CReal, rvA.GetSize(), Min(rvA[i], rvB[i]));}
+CReal						Min(const CMatlibVector<CReal>& rvI);
+void						Min(CReal& rMinVal /* out */, int& iMinInd /* out */,
+								const CMatlibVector<CReal>& rvI /* in */);
+
+inline CReal				Min(const CReal& r1, const CReal& r2, const CReal& r3, const CReal& r4)
+								{return Min(Min(Min(r1, r2), r3), r4);}
+inline CReal				Min(const CReal& r1, const CReal& r2, const CReal& r3, const CReal& r4,
+								const CReal& r5, const CReal& r6, const CReal& r7, const CReal& r8)
+								{return Min(Min(Min(Min(Min(Min(Min(r1, r2), r3), r4), r5), r6), r7), r8);}
+
+
+inline CReal				Max(const CReal& rA, const CReal& rB)
+								{return rA > rB ? rA : rB;}
+inline CMatlibVector<CReal>	Max(const CMatlibVector<CReal>& rvA, const CMatlibVector<CReal>& rvB)
+								{_VECOP(CReal, rvA.GetSize(), Max(rvA[i], rvB[i]));}
+CReal						Max(const CMatlibVector<CReal>& rvI);
+void						Max(CReal& rMaxVal /* out */, int& iMaxInd /* out */,
+								const CMatlibVector<CReal>& rvI /* in */);
+
+inline CReal				Max(const CReal& r1, const CReal& r2, const CReal& r3)
+								{return Max(Max(r1, r2), r3);}
+inline CReal				Max(const CReal& r1, const CReal& r2, const CReal& r3, const CReal& r4,
+								const CReal& r5, const CReal& r6, const CReal& r7)
+								{return Max(Max(Max(Max(Max(Max(r1, r2), r3), r4), r5), r6), r7);}
+
+
+inline CMatlibVector<CReal>	Ones(const int iLen)
+								{_VECOP(CReal, iLen, (CReal) 1.0);}
+inline CMatlibVector<CReal>	Zeros(const int iLen)
+								{_VECOP(CReal, iLen, (CReal) 0.0);}
+
+
+inline CReal				Real(const CComplex& cI) {return cI.real();}
+inline CMatlibVector<CReal>	Real(const CMatlibVector<CComplex>& cvI)
+								{_VECOP(CReal, cvI.GetSize(), Real(cvI[i]));}
+
+inline CReal				Imag(const CComplex& cI) {return cI.imag();}
+inline CMatlibVector<CReal>	Imag(const CMatlibVector<CComplex>& cvI)
+								{_VECOP(CReal, cvI.GetSize(), Imag(cvI[i]));}
+
+inline CComplex					Conj(const CComplex& cI) {return conj(cI);}
+inline CMatlibVector<CComplex>	Conj(const CMatlibVector<CComplex>& cvI)
+									{_VECOP(CComplex, cvI.GetSize(), Conj(cvI[i]));}
+inline CMatlibMatrix<CComplex>	Conj(const CMatlibMatrix<CComplex>& cmI)
+									{_MATOP(CComplex, cmI.GetRowSize(), cmI.GetColSize(), Conj(cmI[i]));}
+
+
+/* Absolute and angle (argument) functions */
+inline CReal				Abs(const CReal& rI) {return fabs(rI);}
+inline CMatlibVector<CReal>	Abs(const CMatlibVector<CReal>& fvI)
+								{_VECOP(CReal, fvI.GetSize(), Abs(fvI[i]));}
+
+inline CReal				Abs(const CComplex& cI) {return abs(cI);}
+inline CMatlibVector<CReal>	Abs(const CMatlibVector<CComplex>& cvI)
+								{_VECOP(CReal, cvI.GetSize(), Abs(cvI[i]));}
+
+inline CReal				Angle(const CComplex& cI) {return arg(cI);}
+inline CMatlibVector<CReal>	Angle(const CMatlibVector<CComplex>& cvI)
+								{_VECOP(CReal, cvI.GetSize(), Angle(cvI[i]));}
+
+
+/* Trigonometric functions */
+inline CReal				Sin(const CReal& fI) {return sin(fI);}
+template<class T> inline
+CMatlibVector<T>			Sin(const CMatlibVector<T>& vecI) 
+								{_VECOP(T, vecI.GetSize(), sin(vecI[i]));}
+
+inline CReal				Cos(const CReal& fI) {return cos(fI);}
+template<class T> inline
+CMatlibVector<T>			Cos(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), cos(vecI[i]));}
+
+inline CReal				Tan(const CReal& fI) {return tan(fI);}
+template<class T> inline
+CMatlibVector<T>			Tan(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), tan(vecI[i]));}
+
+inline CReal				Sinh(const CReal& fI) {return sinh(fI);}
+template<class T> inline
+CMatlibVector<T>			Sinh(const CMatlibVector<T>& vecI) 
+								{_VECOP(T, vecI.GetSize(), sinh(vecI[i]));}
+
+inline CReal				Cosh(const CReal& fI) {return cosh(fI);}
+template<class T> inline
+CMatlibVector<T>			Cosh(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), cosh(vecI[i]));}
+
+inline CReal				Tanh(const CReal& fI) {return tanh(fI);}
+template<class T> inline
+CMatlibVector<T>			Tanh(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), tanh(vecI[i]));}
+
+
+/* Square root */
+inline CReal				Sqrt(const CReal& fI) {return sqrt(fI);}
+template<class T> inline
+CMatlibVector<T>			Sqrt(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), sqrt(vecI[i]));}
+
+
+/* Exponential function */
+inline CReal				Exp(const CReal& fI) {return exp(fI);}
+template<class T> inline
+CMatlibVector<T>			Exp(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), exp(vecI[i]));}
+
+
+/* Logarithm */
+inline CReal				Log(const CReal& fI) {return log(fI);}
+template<class T> inline
+CMatlibVector<T>			Log(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), log(vecI[i]));}
+
+inline CReal				Log10(const CReal& fI) {return log10(fI);}
+template<class T> inline
+CMatlibVector<T>			Log10(const CMatlibVector<T>& vecI)
+								{_VECOP(T, vecI.GetSize(), log10(vecI[i]));}
+
+
+/* Mean, variance and standard deviation */
+template<class T> inline T	Mean(const CMatlibVector<T>& vecI)
+								{return Sum(vecI) / vecI.GetSize();}
+template<class T> inline T	Std(CMatlibVector<T>& vecI) 
+								{return Sqrt(Var(vecI));}
+template<class T> T			Var(const CMatlibVector<T>& vecI);
+
+
+/* Rounding functions */
+inline CReal				Fix(const CReal& fI) {return (int) fI;}
+inline CMatlibVector<CReal>	Fix(const CMatlibVector<CReal>& fvI)
+								{_VECOP(CReal, fvI.GetSize(), Fix(fvI[i]));}
+
+inline CReal				Floor(const CReal& fI) {return floor(fI);}
+inline CMatlibVector<CReal>	Floor(const CMatlibVector<CReal>& fvI)
+								{_VECOP(CReal, fvI.GetSize(), Floor(fvI[i]));}
+
+inline CReal				Ceil(const CReal& fI) {return ceil(fI);}
+inline CMatlibVector<CReal>	Ceil(const CMatlibVector<CReal>& fvI)
+								{_VECOP(CReal, fvI.GetSize(), Ceil(fvI[i]));}
+
+inline CReal				Round(const CReal& fI)
+								{return Floor(fI + (CReal) 0.5);}
+inline CMatlibVector<CReal>	Round(const CMatlibVector<CReal>& fvI)
+								{_VECOP(CReal, fvI.GetSize(), Round(fvI[i]));}
+
+inline CReal				Sign(const CReal& rI)
+								{return rI == 0 ? 0 : rI > 0 ? 1 : -1;}
+
+inline int					Mod(const int ix, const int iy)
+								{return ix < 0 ? (ix % iy + iy) % iy : ix % iy;}
+
+template<class T> T			Sum(const CMatlibVector<T>& vecI);
+
+CMatlibVector<CReal>		Sort(const CMatlibVector<CReal>& rvI);
+
+
+/* Matrix inverse */
+CMatlibMatrix<CComplex>		Inv(const CMatlibMatrix<CComplex>& matrI);
+
+/* Identity matrix */
+CMatlibMatrix<CReal>		Eye(const int iLen);
+
+CMatlibMatrix<CComplex>		Diag(const CMatlibVector<CComplex>& cvI);
+
+CReal						Trace(const CMatlibMatrix<CReal>& rmI);
+
+CMatlibMatrix<CComplex>		Toeplitz(const CMatlibVector<CComplex>& cvI);
+
+/* Matrix transpose */
+CMatlibMatrix<CComplex>		Transp(const CMatlibMatrix<CComplex>& cmI);
+inline
+CMatlibMatrix<CComplex>		TranspH(const CMatlibMatrix<CComplex>& cmI)
+								{return Conj(Transp(cmI));} /* With conjugate complex */
+
+/* Fourier transformations (also included: real FFT) */
+CMatlibVector<CComplex>		Fft(const CMatlibVector<CComplex>& cvI, const CFftPlans& FftPlans = CFftPlans());
+CMatlibVector<CComplex>		Ifft(const CMatlibVector<CComplex>& cvI, const CFftPlans& FftPlans = CFftPlans());
+CMatlibVector<CComplex>		rfft(const CMatlibVector<CReal>& fvI, const CFftPlans& FftPlans = CFftPlans());
+CMatlibVector<CReal>		rifft(const CMatlibVector<CComplex>& cvI, const CFftPlans& FftPlans = CFftPlans());
+
+CMatlibVector<CReal>		FftFilt(const CMatlibVector<CComplex>& rvH,
+									const CMatlibVector<CReal>& rvI,
+									CMatlibVector<CReal>& rvZ,
+									const CFftPlans& FftPlans = CFftPlans());
+
+
+/* Numerical integration */
+typedef CComplex(MATLIB_CALLBACK_QAUD)(CReal rX); /* Callback function definition */
+CComplex					Quad(MATLIB_CALLBACK_QAUD f, const CReal a,
+								 const CReal b, const CReal errorBound = 1.e-6);
+
+
+/* Implementation **************************************************************
+   (the implementation of template classes must be in the header file!) */
+template<class T> inline
+T Sum(const CMatlibVector<T>& vecI)
+{
+	const int iSize = vecI.GetSize();
+	T SumRet = 0;
+	for (int i = 0; i < iSize; i++)
+		SumRet += vecI[i];
+
+	return SumRet;
+}
+
+template<class T> inline
+T Var(const CMatlibVector<T>& vecI)
+{
+	const int iSize = vecI.GetSize();
+
+	/* First calculate mean */
+	T tMean = Mean(vecI);
+
+	/* Now variance (sum formula) */
+	T tRet = 0;
+	for (int i = 0; i < iSize; i++)
+		tRet += (vecI[i] - tMean) * (vecI[i] - tMean);
+
+	return tRet / (iSize - 1); /* Normalizing */
+}
+
+
+#endif	/* _MATLIB_STD_TOOLBOX_H_ */
diff --git a/qsstv/drmtx/common/mlc/BitInterleaver.cpp b/qsstv/drmtx/common/mlc/BitInterleaver.cpp
new file mode 100644
index 0000000..9bbb3e0
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/BitInterleaver.cpp
@@ -0,0 +1,145 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ * The two parts with different protection levels shall not overlap in the 
+ * interleaving process. Therefore the interleaved lower protected part shall 
+ * be appended to the interleaved higher protected part.
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "BitInterleaver.h"
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Bit interleaver															   *
+\******************************************************************************/
+void CBitInterleaver::Interleave(CVector<_DECISION>& InputData)
+{
+	int i;
+
+	/* Block 1 -------------------------------------------------------------- */
+	/* Interleave data according the interleaver table */
+	for (i = 0; i < ix_in1; i++)
+		vecInterlMemory1[i] = InputData[veciIntTable1[i]];
+
+	/* Copy result in input-vector */
+	for (i = 0; i < ix_in1; i++)
+		InputData[i] = vecInterlMemory1[i];
+
+	/* Block 2 -------------------------------------------------------------- */
+	/* Interleave data according the interleaver table */
+	for (i = 0; i < ix_in2; i++)
+		vecInterlMemory2[i] = InputData[veciIntTable2[i] + ix_in1];
+
+	/* Copy result in input-vector */
+	for (i = 0; i < ix_in2; i++)
+		InputData[i + ix_in1] = vecInterlMemory2[i];
+}
+
+void CBitInterleaver::Init(int iNewx_in1, int iNewx_in2, int it_0)
+{
+	/* Set internal parameters */
+	ix_in1 = iNewx_in1;
+	ix_in2 = iNewx_in2;
+
+	/* ix_in1 can be 0 but ix_in2 is always greater than "0" */
+	if (ix_in1 > 0)
+	{
+		/* Allocate memory for table */
+		veciIntTable1.Init(ix_in1);
+	
+		/* Make interleaver table */
+		MakeTable(veciIntTable1, ix_in1, it_0);
+	
+		/* Allocate memory for interleaver */
+		vecInterlMemory1.Init(ix_in1);
+	}
+	
+	/* Allocate memory for table */
+	veciIntTable2.Init(ix_in2);
+
+	/* Make interleaver table */
+	MakeTable(veciIntTable2, ix_in2, it_0);
+
+	/* Allocate memory for interleaver */
+	vecInterlMemory2.Init(ix_in2);
+}
+
+
+/******************************************************************************\
+* Bit deinterleaver															   *
+\******************************************************************************/
+void CBitDeinterleaver::Deinterleave(CVector<CDistance>& vecInput)
+{
+	int i;
+
+	/* Block 1 -------------------------------------------------------------- */
+	/* Deinterleave data according the deinterleaver table */
+	for (i = 0; i < ix_in1; i++)
+		vecDeinterlMemory1[veciIntTable1[i]] = vecInput[i];
+
+	/* Copy result in input-vector */
+	for (i = 0; i < ix_in1; i++)
+		vecInput[i] = vecDeinterlMemory1[i];
+
+	/* Block 2 -------------------------------------------------------------- */
+	/* Deinterleave data according the deinterleaver table */
+	for (i = 0; i < ix_in2; i++)
+		vecDeinterlMemory2[veciIntTable2[i]] = vecInput[i + ix_in1];
+
+	/* Copy result in input-vector */
+	for (i = 0; i < ix_in2; i++)
+		vecInput[i + ix_in1] = vecDeinterlMemory2[i];
+}
+
+void CBitDeinterleaver::Init(int iNewx_in1, int iNewx_in2, int it_0)
+{
+	/* Set internal parameters */
+	ix_in1 = iNewx_in1;
+	ix_in2 = iNewx_in2;
+
+	/* ix_in1 can be 0 but ix_in2 is always greater than "0" */
+	if (ix_in1 > 0)
+	{
+		/* Allocate memory for table */
+		veciIntTable1.Init(ix_in1);
+	
+		/* Make interleaver table */
+		MakeTable(veciIntTable1, ix_in1, it_0);
+	
+		/* Allocate memory for interleaver */
+		vecDeinterlMemory1.Init(ix_in1);
+	}
+	
+	/* Allocate memory for table */
+	veciIntTable2.Init(ix_in2);
+
+	/* Make interleaver table */
+	MakeTable(veciIntTable2, ix_in2, it_0);
+
+	/* Allocate memory for interleaver */
+	vecDeinterlMemory2.Init(ix_in2);
+}
diff --git a/qsstv/drmtx/common/mlc/BitInterleaver.h b/qsstv/drmtx/common/mlc/BitInterleaver.h
new file mode 100644
index 0000000..aa58612
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/BitInterleaver.h
@@ -0,0 +1,75 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(BIT_INTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define BIT_INTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../interleaver/BlockInterleaver.h"
+#include "utils/vector.h"
+
+
+/* Classes ********************************************************************/
+class CBitInterleaver: public CBlockInterleaver
+{
+public:
+	CBitInterleaver() {}
+	virtual ~CBitInterleaver() {}
+
+	void Init(int iNewx_in1, int iNewx_in2, int it_0);
+	void Interleave(CVector<_DECISION>& InputData);
+
+protected:
+	int					ix_in1;
+	int					ix_in2;
+	CVector<int>		veciIntTable1;
+	CVector<int>		veciIntTable2;
+	CVector<_DECISION>	vecInterlMemory1;
+	CVector<_DECISION>	vecInterlMemory2;
+};
+
+class CBitDeinterleaver: public CBlockInterleaver
+{
+public:
+	CBitDeinterleaver() {}
+	virtual ~CBitDeinterleaver() {}
+
+	void Init(int iNewx_in1, int iNewx_in2, int it_0);
+	void Deinterleave(CVector<CDistance>& vecInput);
+
+protected:
+	int					ix_in1;
+	int					ix_in2;
+	CVector<int>		veciIntTable1;
+	CVector<int>		veciIntTable2;
+	CVector<CDistance>	vecDeinterlMemory1;
+	CVector<CDistance>	vecDeinterlMemory2;
+};
+
+
+#endif // !defined(BIT_INTERLEAVER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/mlc/ChannelCode.cpp b/qsstv/drmtx/common/mlc/ChannelCode.cpp
new file mode 100644
index 0000000..7530318
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/ChannelCode.cpp
@@ -0,0 +1,189 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "ChannelCode.h"
+
+
+/* Implementation *************************************************************/
+CVector<int> CChannelCode::GenPuncPatTable(ECodScheme eNewCodingScheme,
+										   EChanType eNewChannelType,
+										   int iN1, int iN2,
+										   int iNewNumOutBitsPartA,
+										   int iNewNumOutBitsPartB,
+										   int iPunctPatPartA, int iPunctPatPartB,
+										   int iLevel)
+{
+	int				i;
+	int				iNumOutBits;
+	int				iNumOutBitsWithMemory;
+	int				iTailbitPattern;
+	int				iTailbitParamL0;
+	int				iTailbitParamL1;
+	int				iPartAPatLen;
+	int				iPartBPatLen;
+	int				iPunctCounter;
+	CVector<int>	veciPuncPatPartA;
+	CVector<int>	veciPuncPatPartB;
+	CVector<int>	veciTailBitPat;
+	CVector<int>	veciReturn;
+
+	/* Number of bits out is the sum of all protection levels */
+	iNumOutBits = iNewNumOutBitsPartA + iNewNumOutBitsPartB;
+
+	/* Number of out bits including the state memory */
+	iNumOutBitsWithMemory = iNumOutBits + MC_CONSTRAINT_LENGTH - 1;
+
+	/* Init vector, storing table for puncturing pattern */
+	veciReturn.Init(iNumOutBitsWithMemory);
+
+
+	/* Set tail-bit pattern ------------------------------------------------- */
+	/* We have to consider two cases because in HSYM "N1 + N2" is used
+	   instead of only "N2" to calculate the tailbit pattern */
+	switch (eNewCodingScheme)
+	{
+	case CS_3_HMMIX:
+		iTailbitParamL0 = iN1 + iN2;
+		iTailbitParamL1 = iN2;
+		break;
+
+	case CS_3_HMSYM:
+		iTailbitParamL0 = 2 * (iN1 + iN2);
+		iTailbitParamL1 = 2 * iN2;
+		break;
+
+	default:
+		iTailbitParamL0 = 2 * iN2;
+		iTailbitParamL1 = 2 * iN2;
+	}
+
+	/* Tailbit pattern calculated according DRM-standard. We have to consider
+	   two cases because in HSYM "N1 + N2" is used instead of only "N2" */
+	if (iLevel == 0)
+		iTailbitPattern =
+			iTailbitParamL0 - 12 - iPuncturingPatterns[iPunctPatPartB][1] *
+			(int) ((iTailbitParamL0 - 12) /
+			iPuncturingPatterns[iPunctPatPartB][1]);
+	else
+		iTailbitPattern =
+			iTailbitParamL1 - 12 - iPuncturingPatterns[iPunctPatPartB][1] *
+			(int) ((iTailbitParamL1 - 12) /
+			iPuncturingPatterns[iPunctPatPartB][1]);
+
+
+	/* Set puncturing bit patterns and lengths ------------------------------ */
+	/* Lengths */
+	iPartAPatLen = iPuncturingPatterns[iPunctPatPartA][0];
+	iPartBPatLen = iPuncturingPatterns[iPunctPatPartB][0];
+
+	/* Vector, storing patterns for part A. Patterns begin at [][2 + x] */
+	veciPuncPatPartA.Init(iPartAPatLen);
+	for (i = 0; i < iPartAPatLen; i++)
+		veciPuncPatPartA[i] = iPuncturingPatterns[iPunctPatPartA][2 + i];
+
+	/* Vector, storing patterns for part B. Patterns begin at [][2 + x] */
+	veciPuncPatPartB.Init(iPartBPatLen);
+	for (i = 0; i < iPartBPatLen; i++)
+		veciPuncPatPartB[i] = iPuncturingPatterns[iPunctPatPartB][2 + i];
+
+	/* Vector, storing patterns for tailbit pattern */
+	veciTailBitPat.Init(LENGTH_TAIL_BIT_PAT);
+	for (i = 0; i < LENGTH_TAIL_BIT_PAT; i++)
+		veciTailBitPat[i] = iPunctPatTailbits[iTailbitPattern][i];
+
+
+	/* Generate actual table for puncturing pattern ------------------------- */
+	/* Reset counter for puncturing */
+	iPunctCounter = 0;
+
+	for (i = 0; i < iNumOutBitsWithMemory; i++)
+	{
+		if (i < iNewNumOutBitsPartA)
+		{
+			/* Puncturing patterns part A */
+			/* Get current pattern */
+			veciReturn[i] = veciPuncPatPartA[iPunctCounter];
+
+			/* Increment index and take care of wrap around */
+			iPunctCounter++;
+			if (iPunctCounter == iPartAPatLen)
+				iPunctCounter = 0;
+		}
+		else
+		{
+			/* In case of FAC do not use special tailbit-pattern! */
+			if ((i < iNumOutBits) || (eNewChannelType == CT_FAC))
+			{
+				/* Puncturing patterns part B */
+				/* Reset counter when beginning of part B is reached */
+				if (i == iNewNumOutBitsPartA)
+					iPunctCounter = 0;
+
+				/* Get current pattern */
+				veciReturn[i] = veciPuncPatPartB[iPunctCounter];
+
+				/* Increment index and take care of wrap around */
+				iPunctCounter++;
+				if (iPunctCounter == iPartBPatLen)
+					iPunctCounter = 0;
+			}
+			else
+			{
+				/* Tailbits */
+				/* Check when tailbit pattern starts */
+				if (i == iNumOutBits)
+					iPunctCounter = 0;
+
+				/* Set tailbit pattern */
+				veciReturn[i] = veciTailBitPat[iPunctCounter];
+
+				/* No test for wrap around needed, since there ist only one
+				   cycle of this pattern */
+				iPunctCounter++;
+			}
+		}
+	}
+
+	return veciReturn;
+}
+
+CChannelCode::CChannelCode()
+{
+	/* Create table for parity bit */
+	for (int j = 0; j < 1 << SIZEOF__BYTE; j++)
+	{
+		/* XOR all bits in byResult.
+		   We observe always the LSB by masking using operator "& 1". To get
+		   access to all bits in "byResult" we shift the current bit so long
+		   until it reaches the mask (at zero) by using operator ">> i". The
+		   actual XOR operation is done by "^=" */
+		vecbiParity[j] = 0;
+		for (int i = 0; i < MC_CONSTRAINT_LENGTH; i++)
+			vecbiParity[j] ^= (j >> i) & 1;
+	}
+}
diff --git a/qsstv/drmtx/common/mlc/ChannelCode.h b/qsstv/drmtx/common/mlc/ChannelCode.h
new file mode 100644
index 0000000..7e5fc59
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/ChannelCode.h
@@ -0,0 +1,70 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(CHANNEL_CODE_H__3B0BA660_CA63345347A0D31912__INCLUDED_)
+#define CHANNEL_CODE_H__3B0BA660_CA63345347A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../tables/TableMLC.h"
+#include "utils/vector.h"
+#include "../Parameter.h"
+
+
+/* Classes ********************************************************************/
+class CChannelCode
+{
+public:
+	CChannelCode();
+	virtual ~CChannelCode() {}
+
+	inline _BINARY Convolution(const _BYTE byNewStateShiftReg,
+							   const int iGenPolyn) const
+	{
+		/* Mask bits with generator polynomial and get convolution result from
+		   pre-calculated table (speed optimization). Since we have a AND
+		   operation on the "byGeneratorMatrix", the index of the convolution
+		   table cannot exceed the size of the table (although the value in
+		   "byNewStateShiftReg" can be larger) */
+		return vecbiParity[byNewStateShiftReg & byGeneratorMatrix[iGenPolyn]];
+	}
+
+	CVector<int> GenPuncPatTable(ECodScheme eNewCodingScheme,
+								 EChanType eNewChannelType,
+								 int iN1, int iN2,
+								 int iNewNumOutBitsPartA,
+								 int iNewNumOutBitsPartB,
+								 int iPunctPatPartA, int iPunctPatPartB,
+								 int iLevel);
+
+
+private:
+	_BINARY vecbiParity[1 << SIZEOF__BYTE];
+};
+
+
+#endif // !defined(CHANNEL_CODE_H__3B0BA660_CA63345347A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/mlc/ConvEncoder.cpp b/qsstv/drmtx/common/mlc/ConvEncoder.cpp
new file mode 100644
index 0000000..4c1eb95
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/ConvEncoder.cpp
@@ -0,0 +1,236 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *	Note: We always shift the bits towards the MSB 
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "ConvEncoder.h"
+
+
+/* Implementation *************************************************************/
+int CConvEncoder::Encode(CVector<_DECISION>& vecInputData, 
+						 CVector<_DECISION>& vecOutputData)
+{
+	/* Set output size to zero, increment it each time a new bit is encoded */
+	int iOutputCnt = 0;
+
+	/* Reset counter for puncturing and state-register */
+	_BYTE byStateShiftReg = 0;
+#ifdef USE_MAX_LOG_MAP
+	/* We know the initial state of the shift registers, therefore a very
+	   high soft information value */
+	vecStateMem.Reset(ML_SOFT_INF_MAX_VALUE);
+#endif
+
+	for (int i = 0; i < iNumInBitsWithMemory; i++)
+	{
+		/* Update shift-register (state information) ------------------------ */
+		/* Shift bits in state-shift-register */
+		byStateShiftReg <<= 1;
+
+		/* Tailbits are calculated in this loop. Check when end of vector is
+		   reached and no more bits must be added */
+		if (i < iNumInBits)
+		{
+			/* Add new bit at the beginning */
+			if (ExtractBit(vecInputData[i]) != 0)
+				byStateShiftReg |= 1;
+
+#ifdef USE_MAX_LOG_MAP
+			/* Update shift register for soft information. We assume here that
+			   the decision type is some floating point type -> we use
+			   fabs() function */
+			vecStateMem.AddBegin(fabs(vecInputData[i]));
+#endif
+		}
+
+
+		/* Puncturing ------------------------------------------------------- */
+		/* Depending on the current puncturing pattern, different numbers of
+		   output bits are generated. The state shift register "byStateShiftReg"
+		   is convoluted with the respective patterns for this bit (is done
+		   inside the convolution function) */
+#ifdef USE_MAX_LOG_MAP
+		switch (veciTablePuncPat[i])
+		{
+		case PP_TYPE_0001:
+			/* Pattern 0001 */
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 0);
+			break;
+
+		case PP_TYPE_0101:
+			/* Pattern 0101 */
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 0);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 2);
+			break;
+
+		case PP_TYPE_0011:
+			/* Pattern 0011 */
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 0);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 1);
+			break;
+
+		case PP_TYPE_0111:
+			/* Pattern 0111 */
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 0);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 1);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 2);
+			break;
+
+		case PP_TYPE_1111:
+			/* Pattern 1111 */
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 0);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 1);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 2);
+
+			vecOutputData[iOutputCnt++] =
+				SoftConvolution(byStateShiftReg, vecStateMem, 3);
+			break;
+		}
+#else
+		switch (veciTablePuncPat[i])
+		{
+		case PP_TYPE_0001:
+			/* Pattern 0001 */
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 0);
+			break;
+
+		case PP_TYPE_0101:
+			/* Pattern 0101 */
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 0);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 2);
+			break;
+
+		case PP_TYPE_0011:
+			/* Pattern 0011 */
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 0);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 1);
+			break;
+
+		case PP_TYPE_0111:
+			/* Pattern 0111 */
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 0);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 1);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 2);
+			break;
+
+		case PP_TYPE_1111:
+			/* Pattern 1111 */
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 0);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 1);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 2);
+			vecOutputData[iOutputCnt++] = Convolution(byStateShiftReg, 3);
+			break;
+		}
+#endif
+	}
+
+	/* Return number of encoded bits */
+	return iOutputCnt;
+}
+
+#ifdef USE_MAX_LOG_MAP
+_DECISION CConvEncoder::SoftConvolution(const _BYTE byNewStateShiftReg,
+										CShiftRegister<_DECISION>& vecStateMem,
+										const int iGenPolyn)
+{
+	_DECISION decSoftOut;
+
+	/* Search for minimum norm value of input soft-informations.
+	   Here we implement the convolution of the soft information independent of
+	   the poylnoms stored in "byGeneratorMatrix[]"! When changing the polynoms,
+	   it has to be changed here, too */
+	switch (iGenPolyn)
+	{
+	case 0:
+	case 3:
+		/* oct: 0155 -> 1101101 */
+		decSoftOut = Min(Min(Min(Min(vecStateMem[0], vecStateMem[2]),
+			vecStateMem[3]), vecStateMem[5]), vecStateMem[6]);
+		break;
+
+	case 1:
+		/* oct: 0117 -> 1001111 */
+		decSoftOut = Min(Min(Min(Min(vecStateMem[0], vecStateMem[1]),
+			vecStateMem[2]), vecStateMem[3]), vecStateMem[6]);
+		break;
+
+	case 2:
+		/* oct: 0123 -> 1010011 */
+		decSoftOut = Min(Min(Min(vecStateMem[0], vecStateMem[1]),
+			vecStateMem[4]), vecStateMem[6]);
+		break;
+	}
+
+	/* Hard decision defines the sign, the norm is defined by the minimum of
+	   input norms of soft informations using max-log approximation */
+	if (Convolution(byNewStateShiftReg, iGenPolyn) == 0)
+		return -decSoftOut;
+	else
+		return decSoftOut;
+}
+#endif
+
+void CConvEncoder::Init(ECodScheme eNewCodingScheme,
+						EChanType eNewChannelType, int iN1, 
+						int iN2, int iNewNumInBitsPartA,
+						int iNewNumInBitsPartB, int iPunctPatPartA,
+						int iPunctPatPartB, int iLevel)
+{
+	/* Number of bits out is the sum of all protection levels */
+	iNumInBits = iNewNumInBitsPartA + iNewNumInBitsPartB;
+
+	/* Number of out bits including the state memory */
+	iNumInBitsWithMemory = iNumInBits + MC_CONSTRAINT_LENGTH - 1;
+
+	/* Init vector, storing table for puncturing pattern and generate pattern */
+	veciTablePuncPat.Init(iNumInBitsWithMemory);
+
+	veciTablePuncPat = GenPuncPatTable(eNewCodingScheme, eNewChannelType, iN1,
+		iN2, iNewNumInBitsPartA, iNewNumInBitsPartB, iPunctPatPartA,
+		iPunctPatPartB, iLevel);
+
+#ifdef USE_MAX_LOG_MAP
+	vecStateMem.Init(MC_CONSTRAINT_LENGTH);
+#endif
+}
diff --git a/qsstv/drmtx/common/mlc/ConvEncoder.h b/qsstv/drmtx/common/mlc/ConvEncoder.h
new file mode 100644
index 0000000..7ceca36
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/ConvEncoder.h
@@ -0,0 +1,71 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(CONNVOL_ENC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define CONNVOL_ENC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../tables/TableMLC.h"
+#include "utils/vector.h"
+#include "../Parameter.h"
+#include "ChannelCode.h"
+
+
+/* Classes ********************************************************************/
+class CConvEncoder : public CChannelCode
+{
+public:
+	CConvEncoder() {}
+	virtual ~CConvEncoder() {}
+
+	int		Encode(CVector<_DECISION>& vecInputData, 
+				   CVector<_DECISION>& vecOutputData);
+
+	void	Init(ECodScheme eNewCodingScheme, EChanType eNewChannelType,
+				 int iN1, int iN2, int iNewNumInBitsPartA,
+				 int iNewNumInBitsPartB, int iPunctPatPartA, int iPunctPatPartB,
+				 int iLevel);
+
+protected:
+	int						iNumInBits;
+	int						iNumInBitsWithMemory;
+
+	CVector<int>			veciTablePuncPat;
+
+	EChanType	eChannelType;
+
+#ifdef USE_MAX_LOG_MAP
+	CShiftRegister<_DECISION>	vecStateMem;
+	_DECISION SoftConvolution(const _BYTE byNewStateShiftReg,
+							  CShiftRegister<_DECISION>& vecStateMem,
+							  const int iGenPolyn);
+#endif
+};
+
+
+#endif // !defined(CONNVOL_ENC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/mlc/EnergyDispersal.cpp b/qsstv/drmtx/common/mlc/EnergyDispersal.cpp
new file mode 100644
index 0000000..ad540cc
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/EnergyDispersal.cpp
@@ -0,0 +1,90 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *
+ * Note:
+ * Input data stream is divided into a regluar stream and the VSPP stream. 
+ * Both stream are treated independently. The respective positions of the
+ * two streams are requested in the init-routine.
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "EnergyDispersal.h"
+
+
+/* Implementation *************************************************************/
+void CEngergyDispersal::ProcessData(CVector<_BINARY>* pbiData)
+{
+	int			i;
+	uint32_t	iTempShiftRegister;
+	_BINARY		biPRBSbit;
+
+	/* Init shift register and set all registers to "1" with bit-wise
+	   not-operation */
+	iShiftRegisterSPP = ~uint32_t(0);
+	iShiftRegisterVSPP = ~uint32_t(0);
+
+	/* Main routine */
+	for (i = 0; i < iNumInBits; i++)
+	{
+		if (i < iEndIndVSPP)
+		{
+			/* Calculate new PRBS bit */
+			iTempShiftRegister = iShiftRegisterVSPP;
+
+			/* P(X) = X^9 + X^5 + 1, 
+			   in this implementation we have to shift n-1! */
+			biPRBSbit = _BINARY(((iTempShiftRegister >> 4) & 1) ^
+				((iTempShiftRegister >> 8) & 1));
+
+			/* Shift bits in shift register and add new bit */
+			iShiftRegisterVSPP <<= 1;
+			iShiftRegisterVSPP |= (biPRBSbit & 1);
+		}
+		else
+		{
+			/* Calculate new PRBS bit */
+			iTempShiftRegister = iShiftRegisterSPP;
+	
+			/* P(X) = X^9 + X^5 + 1, 
+			   in this implementation we have to shift n-1! */
+			biPRBSbit = _BINARY(((iTempShiftRegister >> 4) & 1) ^
+				((iTempShiftRegister >> 8) & 1));
+
+			/* Shift bits in shift register and add new bit */
+			iShiftRegisterSPP <<= 1;
+			iShiftRegisterSPP |= (biPRBSbit & 1);
+		}
+
+		/* Apply PRBS to the data-stream */
+		(*pbiData)[i] ^= biPRBSbit;
+	}
+}
+
+void CEngergyDispersal::Init(int iNewNumInBits, int iNewLengthVSPP)
+{
+	/* Set the internal parameters */
+	iNumInBits = iNewNumInBits;
+	iEndIndVSPP = iNewLengthVSPP;
+}
diff --git a/qsstv/drmtx/common/mlc/EnergyDispersal.h b/qsstv/drmtx/common/mlc/EnergyDispersal.h
new file mode 100644
index 0000000..1aa4c50
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/EnergyDispersal.h
@@ -0,0 +1,55 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(ENERGYDISPERSAL_H__3B0BA660_CA63_4344_2B_23453E7A0D31912__INCLUDED_)
+#define ENERGYDISPERSAL_H__3B0BA660_CA63_4344_2B_23453E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "utils/vector.h"
+
+
+/* Classes ********************************************************************/
+class CEngergyDispersal
+{
+public:
+	CEngergyDispersal() {}
+	virtual ~CEngergyDispersal() {}
+
+	void ProcessData(CVector<_BINARY>* pbiData);		
+	void Init(int iNewNumInBits, int iNewLengthVSPP);
+
+protected:
+	int			iNumInBits;
+	int			iStartIndVSPP;
+	int			iEndIndVSPP;
+	uint32_t	iShiftRegisterSPP;
+	uint32_t	iShiftRegisterVSPP;
+};
+
+
+#endif // !defined(ENERGYDISPERSAL_H__3B0BA660_CA63_4344_2B_23453E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/mlc/MLC.cpp b/qsstv/drmtx/common/mlc/MLC.cpp
new file mode 100644
index 0000000..a19676e
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/MLC.cpp
@@ -0,0 +1,492 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ * 
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ *
+ * Description:
+ *	Multi-level-channel (de)coder (MLC)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "MLC.h"
+
+#define NUM_FAC_CELLS 45
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* MLC-encoder                                                                  *
+\******************************************************************************/
+void CMLCEncoder::ProcessDataInternal(CParameter&)
+{
+	int	i, j;
+	int iElementCounter;
+
+	/* Energy dispersal ----------------------------------------------------- */
+	/* VSPP is treated as a separate part for energy dispersal */
+	EnergyDisp.ProcessData(pvecInputData);
+
+
+	/* Partitioning of input-stream ----------------------------------------- */
+	iElementCounter = 0;
+
+	if (iL[2] == 0)
+	{
+		/* Standard departitioning */
+		/* Protection level A */
+		for (j = 0; j < iLevels; j++)
+		{
+			/* Bits */
+			for (i = 0; i < iM[j][0]; i++)
+			{
+				vecEncInBuffer[j][i] =
+					// BitToSoft((*pvecInputData)[iElementCounter]);
+					(*pvecInputData)[iElementCounter];
+
+				iElementCounter++;
+			}
+		}
+
+		/* Protection level B */
+		for (j = 0; j < iLevels; j++)
+		{
+			/* Bits */
+			for (i = 0; i < iM[j][1]; i++)
+			{
+				vecEncInBuffer[j][iM[j][0] + i] =
+					// BitToSoft((*pvecInputData)[iElementCounter]);
+					(*pvecInputData)[iElementCounter];
+
+				iElementCounter++;
+			}
+		}
+	}
+	else
+	{
+		/* Special partitioning with hierarchical modulation. First set
+		   hierarchical bits at the beginning, then append the rest */
+		/* Hierarchical frame (always "iM[0][1]"). "iM[0][0]" is always "0" in
+		   this case */
+		for (i = 0; i < iM[0][1]; i++)
+		{
+			vecEncInBuffer[0][i] =
+				// BitToSoft((*pvecInputData)[iElementCounter]);
+				(*pvecInputData)[iElementCounter];
+
+			iElementCounter++;
+		}
+
+
+		/* Protection level A (higher protected part) */
+		for (j = 1; j < iLevels; j++)
+		{
+			/* Bits */
+			for (i = 0; i < iM[j][0]; i++)
+			{
+				vecEncInBuffer[j][i] =
+				//	BitToSoft((*pvecInputData)[iElementCounter]);
+					(*pvecInputData)[iElementCounter];
+
+				iElementCounter++;
+			}
+		}
+
+		/* Protection level B  (lower protected part) */
+		for (j = 1; j < iLevels; j++)
+		{
+			/* Bits */
+			for (i = 0; i < iM[j][1]; i++)
+			{
+				vecEncInBuffer[j][iM[j][0] + i] =
+					// BitToSoft((*pvecInputData)[iElementCounter]);
+					(*pvecInputData)[iElementCounter];
+
+				iElementCounter++;
+			}
+		}
+	}
+
+
+	/* Convolutional encoder ------------------------------------------------ */
+	for (j = 0; j < iLevels; j++)
+		ConvEncoder[j].Encode(vecEncInBuffer[j], vecEncOutBuffer[j]);
+
+
+	/* Bit interleaver ------------------------------------------------------ */
+	for (j = 0; j < iLevels; j++)
+		if (piInterlSequ[j] != -1)
+			BitInterleaver[piInterlSequ[j]].Interleave(vecEncOutBuffer[j]);
+
+
+	/* QAM mapping ---------------------------------------------------------- */
+	QAMMapping.Map(vecEncOutBuffer[0],
+				   vecEncOutBuffer[1],
+				   vecEncOutBuffer[2],
+				   vecEncOutBuffer[3],
+				   vecEncOutBuffer[4],
+				   vecEncOutBuffer[5], pvecOutputData);
+}
+
+void CMLCEncoder::InitInternal(CParameter& TransmParam)
+{
+	int i;
+	int	iNumInBits;
+
+	TransmParam.Lock(); 
+	CalculateParam(TransmParam, eChannelType);
+	TransmParam.Unlock(); 
+	
+	iNumInBits = iL[0] + iL[1] + iL[2];
+
+
+	/* Init modules --------------------------------------------------------- */
+	/* Energy dispersal */
+	EnergyDisp.Init(iNumInBits, iL[2]);
+
+	/* Encoder */
+	for (i = 0; i < iLevels; i++)
+		ConvEncoder[i].Init(eCodingScheme, eChannelType, iN[0], iN[1],
+			iM[i][0], iM[i][1], iCodeRate[i][0], iCodeRate[i][1], i);
+
+	/* Bit interleaver */
+	/* First init all possible interleaver (According table "TableMLC.h" ->
+	   "Interleaver sequence") */
+	if (eCodingScheme == CS_3_HMMIX)
+	{
+		BitInterleaver[0].Init(iN[0], iN[1], 13);
+		BitInterleaver[1].Init(iN[0], iN[1], 21);
+	}
+	else
+	{
+		BitInterleaver[0].Init(2 * iN[0], 2 * iN[1], 13);
+		BitInterleaver[1].Init(2 * iN[0], 2 * iN[1], 21);
+	}
+
+	/* QAM-mapping */
+	QAMMapping.Init(iN_mux, eCodingScheme);
+
+
+	/* Allocate memory for internal bit-buffers ----------------------------- */
+	for (i = 0; i < iLevels; i++)
+	{
+		/* Buffers for each encoder on all different levels */
+		/* Add bits from higher protected and lower protected part */
+		vecEncInBuffer[i].Init(iM[i][0] + iM[i][1]);
+	
+		/* Encoder output buffers for all levels. Must have the same length */
+		vecEncOutBuffer[i].Init(iNumEncBits);
+	}
+
+	/* Define block-size for input and output */
+	iInputBlockSize = iNumInBits;
+	iOutputBlockSize = iN_mux;
+	// printf("In init MSCMLCEnc inputblk = %d outpublk = %d\n",
+			 //  iInputBlockSize, iOutputBlockSize);
+
+}
+
+
+
+
+/******************************************************************************\
+* MLC base class                                                               *
+\******************************************************************************/
+void CMLC::CalculateParam(CParameter& Parameter, int iNewChannelType)
+{
+	int i;
+//	int iMSCDataLenPartA;
+
+	switch (iNewChannelType)
+	{
+	/* FAC ********************************************************************/
+	case CT_FAC:
+		eCodingScheme = CS_1_SM;
+		iN_mux = NUM_FAC_CELLS;
+
+		iNumEncBits = NUM_FAC_CELLS * 2;
+
+		iLevels = 1;
+
+		/* Code rates for prot.-Level A and B for each level */
+		/* Protection Level A */
+		iCodeRate[0][0] = 0;
+
+		/* Protection Level B */
+		iCodeRate[0][1] = iCodRateCombFDC4SM;
+
+		/* Define interleaver sequence for all levels */
+		piInterlSequ = iInterlSequ4SM;
+
+
+		/* iN: Number of OFDM-cells of each protection level ---------------- */
+		iN[0] = 0;
+		iN[1] = iN_mux;
+
+
+		/* iM: Number of bits each level ------------------------------------ */
+		iM[0][0] = 0;
+		iM[0][1] = NUM_FAC_BITS_PER_BLOCK;
+
+
+		/* iL: Number of bits each protection level ------------------------- */
+		/* Higher protected part */
+		iL[0] = 0;
+
+		/* Lower protected part */
+		iL[1] = iM[0][1];
+
+		/* Very strong protected part (VSPP) */
+		iL[2] = 0;
+		break;
+
+
+
+	/* MSC ********************************************************************/
+	case CT_MSC:
+		eCodingScheme = Parameter.eMSCCodingScheme;
+		iN_mux = Parameter.CellMappingTable.iNumUsefMSCCellsPerFrame;
+                // printf("iN_mux %d \n", iN_mux);
+
+		/* Data length for part A is the sum of all lengths of the streams */
+		
+//		iMSCDataLenPartA = 0; // for hamversion
+
+/*		iMSCDataLenPartA = Parameter.Stream[0].iLenPartA +
+						   Parameter.Stream[1].iLenPartA +
+						   Parameter.Stream[2].iLenPartA +
+						   Parameter.Stream[3].iLenPartA;  */
+
+		switch (eCodingScheme)
+		{
+		case CS_1_SM:
+			iLevels = 1;
+
+			/* Code rates for prot.-Level A and B for each level */
+				/* Protection Level A */
+			iCodeRate[0][0] = 0;
+
+			/* Protection Level B */
+			iCodeRate[0][1] = iCodRateCombMSC4SM;
+			
+			/* Define interleaver sequence for all levels */
+			piInterlSequ = iInterlSequ4SM;
+			iNumEncBits = iN_mux * 2;
+
+			/* iN: Number of OFDM-cells of each protection level ---------------- */
+			iN[0] = 0;
+			iN[1] = iN_mux;
+
+			/* iM: Number of bits each level ------------------------------------ */
+			iM[0][0] = 0;
+
+			/* M_p,2 = RX_p * floor((2 * N_2 - 12) / RY_p) */
+			iM[0][1] = iPuncturingPatterns[iCodRateCombMSC4SM][0] *
+				(int) ((_REAL) (2 * iN_mux - 12) /
+				iPuncturingPatterns[iCodRateCombMSC4SM][1]);
+
+			/* iL: Number of bits each protection level ------------------------- */
+			/* Higher protected part */
+			iL[0] = 0;
+
+			/* Lower protected part */
+			iL[1] = iM[0][1];
+
+			/* Very strong protected part (VSPP) */
+			iL[2] = 0;
+                      /*   printf("in CalcuPar CS_1_SM iN[0]= %d iN[1]= %d iM[0][0] = %d iM[0][1] = %d\n",
+					 iN[0], iN[1], iM[0][0], iM[0][1] );
+			 printf("in CalcuPar iL[o]= %d iL[1]= %d iL[2] = %d\n",
+					 iL[0], iL[1], iL[1]);  */
+			break;
+
+		case CS_2_SM:
+			iLevels = 2;
+
+			/* Code rates for prot.-Level A and B for each level */
+			for (i = 0; i < 2; i++)
+			{
+				/* Protection Level A */
+				iCodeRate[i][0] = 0 ;  // hamversion
+
+               /*				iCodeRate[i][0] =
+					iCodRateCombMSC16SM[Parameter.MSCPrLe.iPartA][i]; */
+
+				/* Protection Level B */
+				iCodeRate[i][1] =
+					iCodRateCombMSC16SM[Parameter.MSCPrLe.iPartB][i];
+			}
+
+			/* Define interleaver sequence for all levels */
+			piInterlSequ = iInterlSequ16SM;
+
+			iNumEncBits = iN_mux * 2;
+
+
+			/* iN: Number of OFDM-cells of each protection level ------------ */
+			/* N_1 = ceil(8 * X / (2 * RY_Icm * sum(R_p)) * RY_Icm */
+			iN[0] = 0;  // hamversion
+
+/*			iN[0] = (int) ceil(8 * (_REAL) iMSCDataLenPartA / (2 *
+				(_REAL) iCodRateCombMSC16SM[Parameter.MSCPrLe.iPartA][2] *
+				(
+				(_REAL) iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][0]][0] /
+					iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][0]][1] +
+				(_REAL) iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][1]][0] /
+					iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][1]][1]))) *
+				iCodRateCombMSC16SM[Parameter.MSCPrLe.iPartA][2];
+*/
+			/* Check if result can be possible, if not -> correct. This can
+			   happen, if a wrong number is in "Param.Stream[x].iLenPartA" */
+			if (iN[0] > iN_mux)
+				iN[0] = 0;
+
+			iN[1] = iN_mux - iN[0];
+
+
+			/* iM: Number of bits each level -------------------------------- */
+			for (i = 0; i < 2; i++)
+			{
+				/* M_p,1 = 2 * N_1 * R_p */
+				iM[i][0] = 0;
+	/*			iM[i][0] = (int) (2 * iN[0] *
+					(_REAL) iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][i]][0] /
+					iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartA][i]][1]);   */
+
+				/* M_p,2 = RX_p * floor((2 * N_2 - 12) / RY_p) */
+				iM[i][1] = 
+					iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartB][i]][0] *
+					(int) ((_REAL) (2 * iN[1] - 12) /
+					iPuncturingPatterns[iCodRateCombMSC16SM[
+					Parameter.MSCPrLe.iPartB][i]][1]);
+				// printf("In calcparam iM[%d][0] = %d  iM[%d][1] = %d \n", i, iM[i][0], i, iM[i][1]);
+			}
+
+
+			/* iL: Number of bits each protection level --------------------- */
+			/* Higher protected part */
+			iL[0] = iM[0][0] + iM[1][0];
+
+			/* Lower protected part */
+			iL[1] = iM[0][1] + iM[1][1];
+
+			/* Very strong protected part (VSPP) */
+			iL[2] = 0;
+                         /* printf("in CalcuPar CS_2_SM iN[0]= %d iN[1]= %d iM[0][0] = %d iM[0][1] = %d\n",
+					 iN[0], iN[1], iM[0][0], iM[0][1] );
+			 printf("in CalcuPar iL[o]= %d iL[1]= %d iL[2] = %d\n",
+					 iL[0], iL[1], iL[1]);  */
+			break;
+
+		case CS_3_SM:
+			iLevels = 3;
+
+			/* Code rates for prot.-Level A and B for each level */
+			for (i = 0; i < 3; i++)
+			{
+				/* Protection Level A */
+				iCodeRate[i][0] = 0;
+/*				iCodeRate[i][0] =
+					iCodRateCombMSC64SM[Parameter.MSCPrLe.iPartA][i]; */
+
+				/* Protection Level B */
+				iCodeRate[i][1] =
+					iCodRateCombMSC64SM[Parameter.MSCPrLe.iPartB][i];
+			}
+
+			/* Define interleaver sequence for all levels */
+			piInterlSequ = iInterlSequ64SM;
+
+			iNumEncBits = iN_mux * 2;
+
+
+			/* iN: Number of OFDM-cells of each protection level ------------ */
+			/* N_1 = ceil(8 * X / (2 * RY_Icm * sum(R_p)) * RY_Icm */
+			iN[0] = 0;
+
+			/* Check if result can be possible, if not -> correct. This can
+			   happen, if a wrong number is in "Param.Stream[x].iLenPartA" */
+			if (iN[0] > iN_mux)
+				iN[0] = 0;
+
+			iN[1] = iN_mux - iN[0];
+
+
+			/* iM: Number of bits each level -------------------------------- */
+			for (i = 0; i < 3; i++)
+			{
+				/* M_p,1 = 2 * N_1 * R_p */
+				iM[i][0] = 0;
+/*				iM[i][0] = (int) (2 * iN[0] *
+					(_REAL) iPuncturingPatterns[iCodRateCombMSC64SM[
+					Parameter.MSCPrLe.iPartA][i]][0] /
+					iPuncturingPatterns[iCodRateCombMSC64SM[
+					Parameter.MSCPrLe.iPartA][i]][1]); */
+
+				/* M_p,2 = RX_p * floor((2 * N_2 - 12) / RY_p) */
+				iM[i][1] = 
+					iPuncturingPatterns[iCodRateCombMSC64SM[
+					Parameter.MSCPrLe.iPartB][i]][0] *
+					(int) ((_REAL) (2 * iN[1] - 12) /
+					iPuncturingPatterns[iCodRateCombMSC64SM[
+					Parameter.MSCPrLe.iPartB][i]][1]); 
+			}
+
+
+			/* iL: Number of bits each protection level --------------------- */
+			/* Higher protected part */
+			iL[0] = iM[0][0] + iM[1][0] + iM[2][0];
+
+			/* Lower protected part */
+			iL[1] = iM[0][1] + iM[1][1] + iM[2][1];
+
+			/* Very strong protected part (VSPP) */
+			iL[2] = 0;
+                         /* printf("in CalcuPar CS_3_SM iN[0]= %d iN[1]= %d iM[0][0] = %d iM[0][1] = %d\n",
+					 iN[0], iN[1], iM[0][0], iM[0][1] );
+			 printf("in CalcuPar iL[o]= %d iL[1]= %d iL[2] = %d\n",
+					 iL[0], iL[1], iL[2]); */
+			break;
+
+
+		default:
+			break;
+		}
+
+		/* Set number of output bits for next module */
+		Parameter.SetNumDecodedBitsMSC(iL[0] + iL[1] + iL[2]);
+
+		/* Set total number of bits for hiearchical frame (needed for MSC
+		   demultiplexer module) */
+		Parameter.SetNumBitsHieraFrTot(iL[2]);
+              //  Parameter.DataParam.iPacketLen = 1234;
+		break;
+	}
+}
diff --git a/qsstv/drmtx/common/mlc/MLC.h b/qsstv/drmtx/common/mlc/MLC.h
new file mode 100644
index 0000000..7f5ed31
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/MLC.h
@@ -0,0 +1,142 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	See MLC.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(MLC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define MLC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../util/Modul.h"
+#include "../Parameter.h"
+#include "../tables/TableMLC.h"
+#include "../tables/TableCarMap.h"
+#include "ConvEncoder.h"
+//#include "ViterbiDecoder.h"
+//#include "Metric.h"
+#include "BitInterleaver.h"
+#include "QAMMapping.h"
+#include "EnergyDispersal.h"
+
+
+/* Classes ********************************************************************/
+class CMLC
+{
+public:
+	CMLC() : iN_mux(0), eChannelType(CT_MSC) {}
+	virtual ~CMLC() {}
+
+	void CalculateParam(CParameter& Parameter, int iNewChannelType);
+
+protected:
+	int	iLevels;
+	/* No input bits for each level. First index: Level, second index:
+	   Protection level.
+	   For three levels: [M_0,l  M_1,l  M2,l]
+	   For six levels: [M_0,lRe  M_0,lIm  M_1,lRe  M_1,lIm  M_2,lRe  ...  ] */
+	int	iM[MC_MAX_NUM_LEVELS][2];
+	int iN[2];
+	int iL[3];
+	int iN_mux;
+	int iCodeRate[MC_MAX_NUM_LEVELS][2];
+
+	const int* piInterlSequ;
+
+	int	iNumEncBits;
+
+	EChanType	eChannelType;
+	ECodScheme	eCodingScheme;
+};
+
+class CMLCEncoder : public CTransmitterModul<_BINARY, _COMPLEX>, 
+					public CMLC
+{
+public:
+	CMLCEncoder() {}
+	virtual ~CMLCEncoder() {}
+
+protected:
+	CConvEncoder		ConvEncoder[MC_MAX_NUM_LEVELS];
+	/* Two different types of interleaver table */
+	CBitInterleaver		BitInterleaver[2];
+	CQAMMapping			QAMMapping;
+	CEngergyDispersal	EnergyDisp;
+
+	/* Internal buffers */
+	CVector<_DECISION>	vecEncInBuffer[MC_MAX_NUM_LEVELS];
+	CVector<_DECISION>	vecEncOutBuffer[MC_MAX_NUM_LEVELS];
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& Parameter);
+};
+
+
+/******************************************************************************\
+* Customized channel (de-)coders											   *
+\******************************************************************************/
+class CMSCMLCEncoder : public CMLCEncoder
+{
+protected:
+	virtual void InitInternal(CParameter& TransmParam)
+	{
+		/* Set corresponding type */
+		eChannelType = CT_MSC;
+
+		/* Call init in encoder */
+		CMLCEncoder::InitInternal(TransmParam);
+	};
+};
+
+class CSDCMLCEncoder : public CMLCEncoder
+{
+protected:
+	virtual void InitInternal(CParameter& TransmParam)
+	{
+		/* Set corresponding type */
+		eChannelType = CT_SDC;
+
+		/* Call init in encoder */
+		CMLCEncoder::InitInternal(TransmParam);
+	};
+};
+
+class CFACMLCEncoder : public CMLCEncoder
+{
+protected:
+	virtual void InitInternal(CParameter& TransmParam)
+	{
+		/* Set corresponding type */
+		eChannelType = CT_FAC;
+
+		/* Call init in encoder */
+		CMLCEncoder::InitInternal(TransmParam);
+	};
+};
+
+
+
+
+#endif // !defined(MLC_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/mlc/QAMMapping.cpp b/qsstv/drmtx/common/mlc/QAMMapping.cpp
new file mode 100644
index 0000000..706eac8
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/QAMMapping.cpp
@@ -0,0 +1,175 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "QAMMapping.h"
+
+
+/* Implementation *************************************************************/
+void CQAMMapping::Map(CVector<_DECISION>& vecInputData1,
+					  CVector<_DECISION>& vecInputData2,
+					  CVector<_DECISION>& vecInputData3,
+					  CVector<_DECISION>& vecInputData4,
+					  CVector<_DECISION>& vecInputData5,
+					  CVector<_DECISION>& vecInputData6,
+					  CVector<_COMPLEX>* pcOutputData)
+{
+/* 
+	We always use "& 1" when we combine binary values with logical operators
+	for safety reasons.
+*/
+	int i;
+	int iIndexReal;
+	int iIndexImag;
+
+	switch (eMapType)
+	{
+	case CS_1_SM:
+		/* 4QAM ------------------------------------------------------------- */
+		/* Mapping according DRM-standard: 
+		   {i_0  q_0} = (y'_0  y'_1) = (y_0,0  y_0,1) */
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			(*pcOutputData)[i] = _COMPLEX(
+				/* Odd entries (second column in "rTableQAM4") */
+				rTableQAM4[ExtractBit(vecInputData1[2 * i]) & 1][0],
+				/* Even entries in input-vector */
+				rTableQAM4[ExtractBit(vecInputData1[2 * i + 1]) & 1][1]);
+		}
+		break;
+
+	case CS_2_SM:
+		/* 16QAM ------------------------------------------------------------ */
+		/* Mapping according DRM-standard: 
+		   {i_0  i_1  q_0  q_1} = (y_0,0  y_1,0  y_0,1  y_1,1) */
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			const int i2i = 2 * i;
+			const int i2ip1 = 2 * i + 1;
+
+			/* Filling indices [y_0,0, y_1,0]. Incoming bits are shifted to
+			   their desired positions in the integer variables "iIndexImag"
+			   and "iIndexReal" and combined */
+			iIndexReal = ((ExtractBit(vecInputData1[i2i]) & 1) << 1) |
+						  (ExtractBit(vecInputData2[i2i]) & 1);
+			iIndexImag = ((ExtractBit(vecInputData1[i2ip1]) & 1) << 1) |
+						  (ExtractBit(vecInputData2[i2ip1]) & 1);
+
+			(*pcOutputData)[i] =
+						 /* Odd entries (second column in "rTableQAM16") */
+				_COMPLEX(rTableQAM16[iIndexReal][0],
+						 /* Even entries in input-vector */
+						 rTableQAM16[iIndexImag][1]);
+		}
+		break;
+
+	case CS_3_SM:
+		/* 64QAM SM --------------------------------------------------------- */
+		/* Mapping according DRM-standard: {i_0  i_1  i_2  q_0  q_1  q_2} = 
+		   (y_0,0  y_1,0  y_2,0  y_0,1  y_1,1  y_2,1) */
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			const int i2i = 2 * i;
+			const int i2ip1 = 2 * i + 1;
+
+			/* Filling indices [y_0,0, y_1,0, y_2,0]. Incoming bits 
+			   are shifted to their desired positions in the integer variables 
+			   "iIndexImag" and "iIndexReal" and combined */
+			iIndexReal = ((ExtractBit(vecInputData1[i2i]) & 1) << 2) |
+						 ((ExtractBit(vecInputData2[i2i]) & 1) << 1) |
+						  (ExtractBit(vecInputData3[i2i]) & 1);
+			iIndexImag = ((ExtractBit(vecInputData1[i2ip1]) & 1) << 2) |
+						 ((ExtractBit(vecInputData2[i2ip1]) & 1) << 1) |
+						  (ExtractBit(vecInputData3[i2ip1]) & 1);
+
+			(*pcOutputData)[i] =
+						 /* Odd entries (second column in "rTableQAM64SM") */
+				_COMPLEX(rTableQAM64SM[iIndexReal][0],
+						 /* Even entries in input-vector */
+						 rTableQAM64SM[iIndexImag][1]);
+		}
+		break;
+
+	case CS_3_HMSYM:
+		/* 64QAM HMsym ------------------------------------------------------ */
+		/* Mapping according DRM-standard: {i_0  i_1  i_2  q_0  q_1  q_2} =
+		   (y_0,0  y_1,0  y_2,0  y_0,1  y_1,1  y_2,1) */
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			const int i2i = 2 * i;
+			const int i2ip1 = 2 * i + 1;
+
+			/* Filling indices [y_0,0, y_1,0, y_2,0]. Incoming bits
+			   are shifted to their desired positions in the integer variables
+			   "iIndexImag" and "iIndexReal" and combined */
+			iIndexReal = ((ExtractBit(vecInputData1[i2i]) & 1) << 2) |
+						 ((ExtractBit(vecInputData2[i2i]) & 1) << 1) |
+						  (ExtractBit(vecInputData3[i2i]) & 1);
+			iIndexImag = ((ExtractBit(vecInputData1[i2ip1]) & 1) << 2) |
+						 ((ExtractBit(vecInputData2[i2ip1]) & 1) << 1) |
+						  (ExtractBit(vecInputData3[i2ip1]) & 1);
+
+			(*pcOutputData)[i] =
+						 /* Odd entries (second column in "rTableQAM64HMsym") */
+				_COMPLEX(rTableQAM64HMsym[iIndexReal][0],
+						 /* Even entries in input-vector */
+						 rTableQAM64HMsym[iIndexImag][1]);
+		}
+		break;
+
+	case CS_3_HMMIX:
+		/* 64QAM HMmix------------------------------------------------------- */
+		/* Mapping according DRM-standard: {i_0  i_1  i_2  q_0  q_1  q_2} = 
+		   (y_0,0Re  y_1,0Re  y_2,0Re  y_0,0Im  y_1,0Im  y_2,0Im) */
+		for (i = 0; i < iOutputBlockSize; i++)
+		{
+			/* Filling indices [y_0,0, y_1,0, y_2,0] (Re, Im). Incoming bits
+			   are shifted to their desired positions in the integer variables
+			   "iIndexImag" and "iIndexReal" and combined */
+			iIndexReal = ((ExtractBit(vecInputData1[i]) & 1) << 2) |
+						 ((ExtractBit(vecInputData3[i]) & 1) << 1) |
+						  (ExtractBit(vecInputData5[i]) & 1);
+			iIndexImag = ((ExtractBit(vecInputData2[i]) & 1) << 2) |
+						 ((ExtractBit(vecInputData4[i]) & 1) << 1) |
+						  (ExtractBit(vecInputData6[i]) & 1);
+
+			(*pcOutputData)[i] =
+						 /* Odd entries (second column in "rTableQAM64HMmix") */
+				_COMPLEX(rTableQAM64HMmix[iIndexReal][0],
+						 /* Even entries in input-vector */
+						 rTableQAM64HMmix[iIndexImag][1]);
+		}
+		break;
+	}
+}
+
+void CQAMMapping::Init(int iNewOutputBlockSize, ECodScheme eNewCodingScheme)
+{
+	/* Set the two internal parameters */
+	iOutputBlockSize = iNewOutputBlockSize;
+	eMapType = eNewCodingScheme;
+}
diff --git a/qsstv/drmtx/common/mlc/QAMMapping.h b/qsstv/drmtx/common/mlc/QAMMapping.h
new file mode 100644
index 0000000..fbdff59
--- /dev/null
+++ b/qsstv/drmtx/common/mlc/QAMMapping.h
@@ -0,0 +1,60 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(QAM_MAPPING_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define QAM_MAPPING_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../tables/TableQAMMapping.h"
+#include "utils/vector.h"
+#include "../Parameter.h"
+
+
+/* Classes ********************************************************************/
+class CQAMMapping
+{
+public:
+	CQAMMapping() {}
+	virtual ~CQAMMapping() {}
+
+	void Map(CVector<_DECISION>& vecInputData1, 
+			 CVector<_DECISION>& vecInputData2, 
+			 CVector<_DECISION>& vecInputData3, 
+			 CVector<_DECISION>& vecInputData4, 
+			 CVector<_DECISION>& vecInputData5, 
+			 CVector<_DECISION>& vecInputData6,
+			 CVector<_COMPLEX>* pcOutputData);
+	void Init(int iNewOutputBlockSize, ECodScheme eNewCodingScheme);
+
+protected:
+	int						iOutputBlockSize;
+	ECodScheme	eMapType;
+};
+
+
+#endif // !defined(QAM_MAPPING_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.cpp b/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.cpp
new file mode 100644
index 0000000..f5a51b8
--- /dev/null
+++ b/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.cpp
@@ -0,0 +1,621 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use  Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Table of the mapping of OFDM cells.
+ *	We build a table of one super-frame where we set flags for each cell to
+ *	identify the symbol for this place. E.g. if the flag "CM_MSC" is set for
+ *	one table entry this is the cell for a MSC-symbol. The name of the table
+ *	is matiMapTab.
+ *  We use the table "matcPilotCells" for storing the complex values for the
+ *  pilots. For simplicity we allocate memory for all blocks but only the
+ *  pilot positions are used.
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+#include "../GlobalDefinitions.h"
+#include "../tables/TableCarrier.h"
+#include "CellMappingTable.h"
+
+//#define _DEBUG_ 1
+
+/* Implementation *************************************************************/
+void CCellMappingTable::MakeTable(ERobMode eNewRobustnessMode, 
+                                  ESpecOcc eNewSpectOccup)
+{
+  int				iNoMSCDummyCells; /* Number of MSC dummy cells */
+  int				iNumTimePilots; /* Number of time pilots per frame */
+  CScatPilots		ScatPilots;
+  int				iSym;
+  int				iFrameSym;
+  int				iCar;
+  int				iTimePilotsCounter;
+  int				iFreqPilotsCounter;
+  int				iScatPilotsCounter;
+  int				iMSCCounter;
+  int				iFACCounter=0;
+  int				iScatPilPhase;
+  int				iCarArrInd;
+  int				iSpecOccArrayIndex;
+  /* Tables */
+  const int*		piTableFAC;
+  const int*		piTableTimePilots;
+  const int*		piTableFreqPilots;
+
+
+  /* Set Parameters and pointers to the tables ******************************/
+  switch (eNewSpectOccup)
+    {
+    case SO_0:
+      iSpecOccArrayIndex = 0;
+    break;
+
+    case SO_1:
+      iSpecOccArrayIndex = 1;
+    break;
+
+    default:
+      iSpecOccArrayIndex = 1;
+    break;
+    }
+
+  // initialize defaults to avoid unitialized warning
+  piTableTimePilots = &iTableTimePilRobModA[0][0];
+  piTableFreqPilots = &iTableFreqPilRobModA[0][0];
+  piTableFAC = &iTableFACRobModA[0][0];
+  iNumTimePilots = RMA_NUM_TIME_PIL;
+
+  /* The robust mode defines all other parameters */
+  switch (eNewRobustnessMode)
+    {
+    case RM_ROBUSTNESS_MODE_A:
+      iCarrierKmin = iTableCarrierKmin[iSpecOccArrayIndex][0];
+      iCarrierKmax = iTableCarrierKmax[iSpecOccArrayIndex][0];
+
+      iFFTSizeN = RMA_FFT_SIZE_N;
+      RatioTgTu.iEnum = RMA_ENUM_TG_TU;
+      RatioTgTu.iDenom = RMA_DENOM_TG_TU;
+
+      iNumSymPerFrame = RMA_NUM_SYM_PER_FRAME;
+      iNumSymbolsPerSuperframe = iNumSymPerFrame * NUM_FRAMES_IN_SUPERFRAME;
+      piTableFAC = &iTableFACRobModA[0][0];
+      iNumTimePilots = RMA_NUM_TIME_PIL;
+      piTableTimePilots = &iTableTimePilRobModA[0][0];
+      piTableFreqPilots = &iTableFreqPilRobModA[0][0];
+      iScatPilTimeInt = RMA_SCAT_PIL_TIME_INT;
+      iScatPilFreqInt = RMA_SCAT_PIL_FREQ_INT;
+
+      /* Scattered pilots phase definition */
+      ScatPilots.piConst = iTableScatPilConstRobModA;
+      ScatPilots.iColSizeWZ = SIZE_COL_WZ_ROB_MOD_A;
+      ScatPilots.piW = &iScatPilWRobModA[0][0];
+      ScatPilots.piZ = &iScatPilZRobModA[0][0];
+      ScatPilots.iQ = iScatPilQRobModA;
+
+      ScatPilots.piGainTable = &iScatPilGainRobModA[iSpecOccArrayIndex][0];
+    break;
+
+    case RM_ROBUSTNESS_MODE_B:
+      iCarrierKmin = iTableCarrierKmin[iSpecOccArrayIndex][1];
+      iCarrierKmax = iTableCarrierKmax[iSpecOccArrayIndex][1];
+
+      iFFTSizeN = RMB_FFT_SIZE_N;
+      RatioTgTu.iEnum = RMB_ENUM_TG_TU;
+      RatioTgTu.iDenom = RMB_DENOM_TG_TU;
+
+      iNumSymPerFrame = RMB_NUM_SYM_PER_FRAME;
+      iNumSymbolsPerSuperframe = iNumSymPerFrame * NUM_FRAMES_IN_SUPERFRAME;
+      piTableFAC = &iTableFACRobModB[0][0];
+      iNumTimePilots = RMB_NUM_TIME_PIL;
+      piTableTimePilots = &iTableTimePilRobModB[0][0];
+      piTableFreqPilots = &iTableFreqPilRobModB[0][0];
+      iScatPilTimeInt = RMB_SCAT_PIL_TIME_INT;
+      iScatPilFreqInt = RMB_SCAT_PIL_FREQ_INT;
+
+      /* Scattered pilots phase definition */
+      ScatPilots.piConst = iTableScatPilConstRobModB;
+      ScatPilots.iColSizeWZ = SIZE_COL_WZ_ROB_MOD_B;
+      ScatPilots.piW = &iScatPilWRobModB[0][0];
+      ScatPilots.piZ = &iScatPilZRobModB[0][0];
+      ScatPilots.iQ = iScatPilQRobModB;
+
+      ScatPilots.piGainTable = &iScatPilGainRobModB[iSpecOccArrayIndex][0];
+    break;
+    case RM_NO_MODE_DETECTED:
+    case RM_ROBUSTNESS_MODE_D: // not used in HAM default to E joma
+    case RM_ROBUSTNESS_MODE_E:
+      iCarrierKmin = iTableCarrierKmin[iSpecOccArrayIndex][2];
+      iCarrierKmax = iTableCarrierKmax[iSpecOccArrayIndex][2];
+
+      iFFTSizeN = RME_FFT_SIZE_N;
+      RatioTgTu.iEnum = RME_ENUM_TG_TU;
+      RatioTgTu.iDenom = RME_DENOM_TG_TU;
+
+      iNumSymPerFrame = RME_NUM_SYM_PER_FRAME;
+      iNumSymbolsPerSuperframe = iNumSymPerFrame * NUM_FRAMES_IN_SUPERFRAME;
+      piTableFAC = &iTableFACRobModE[0][0];
+      iNumTimePilots = RME_NUM_TIME_PIL;
+      piTableTimePilots = &iTableTimePilRobModE[0][0];
+      piTableFreqPilots = &iTableFreqPilRobModE[0][0];
+      iScatPilTimeInt = RME_SCAT_PIL_TIME_INT;
+      iScatPilFreqInt = RME_SCAT_PIL_FREQ_INT;
+
+      /* Scattered pilots phase definition */
+      ScatPilots.piConst = iTableScatPilConstRobModE;
+      ScatPilots.iColSizeWZ = SIZE_COL_WZ_ROB_MOD_E;
+      ScatPilots.piW = &iScatPilWRobModE[0][0];
+      ScatPilots.piZ = &iScatPilZRobModE[0][0];
+      ScatPilots.iQ = iScatPilQRobModE;
+      ScatPilots.piGainTable = &iScatPilGainRobModE[iSpecOccArrayIndex][0];
+    break;
+    }
+
+  /* Get number of carriers with DC */
+  iNumCarrier = iCarrierKmax - iCarrierKmin + 1;
+
+  /* Length of guard-interval measured in "time-bins". We do the calculation
+     with integer variables -> "/ RatioTgTu.iDenom" MUST be the last
+     operation! */
+  iGuardSize = iFFTSizeN * RatioTgTu.iEnum / RatioTgTu.iDenom;
+
+  /* Symbol block size is the guard-interval plus the useful part */
+  iSymbolBlockSize = iFFTSizeN + iGuardSize;
+
+  /* Calculate the index of the DC carrier in the shifted spectrum */
+  iIndexDCFreq = (int) ((_REAL) VIRTUAL_INTERMED_FREQ *
+                        iFFTSizeN / SOUNDCRD_SAMPLE_RATE);
+
+  /* Index of minimum useful carrier (shifted) */
+  iShiftedKmin = iIndexDCFreq + iCarrierKmin;
+
+  /* Index. of maximum useful carrier (shifted) */
+  iShiftedKmax = iIndexDCFreq + iCarrierKmax;
+
+  /* Calculate number of time-interploated frequency pilots. Special case
+     with robustness mode D: pilots in all carriers! BUT: DC carrier is
+     counted as a pilot in that case!!! Be aware of that! */
+  if (iScatPilFreqInt > 1)
+    iNumIntpFreqPil = (int) ((_REAL) iNumCarrier / iScatPilFreqInt + 1);
+  else
+    iNumIntpFreqPil = iNumCarrier;
+
+  // printf("CellMappingTable Maktable iNumCarrier %d iShiftedKmin %d iIndexDCFreq %d iShiftedKmax %d fftsize %d\n",
+  //		iNumCarrier, iShiftedKmin, iIndexDCFreq, iShiftedKmax, iFFTSizeN);
+
+  /* Allocate memory for vectors and matrices ----------------------------- */
+  /* Allocate memory for mapping table (Matrix) */
+  matiMapTab.Init(iNumSymbolsPerSuperframe, iNumCarrier);
+
+  /* Allocate memory for pilot cells definition and set it to zero */
+  matcPilotCells.Init(iNumSymbolsPerSuperframe, iNumCarrier,
+                      _COMPLEX((_REAL) 0.0, (_REAL) 0.0));
+
+  /* Allocate memory for vectors with number of certain cells */
+  veciNumMSCSym.Init(iNumSymbolsPerSuperframe);
+  veciNumFACSym.Init(iNumSymbolsPerSuperframe);
+
+
+  /* Build table ************************************************************/
+  /* Some of the definitions at the beginning are overwritten by successive
+     definitions! E.g., first define all carriers as MSC cells */
+  iFreqPilotsCounter = 0;
+  iTimePilotsCounter = 0;
+  for (iSym = 0; iSym < iNumSymbolsPerSuperframe; iSym++)
+    {
+      /* Frame symbol: Counts symbols in one frame, not super frame! */
+      iFrameSym = iSym % iNumSymPerFrame;
+
+      /* Reset FAC counter at the beginning of each new frame */
+      if (iFrameSym == 0)  iFACCounter = 0;
+
+      /* Calculate the start value of "p" in equation for gain reference
+       cells in Table 90 (8.4.4.1) */
+      iScatPilotsCounter = (int) ((_REAL) (iCarrierKmin -
+                                           (int) ((_REAL) iScatPilFreqInt / 2 + .5) -
+                                           iScatPilFreqInt * mod(iFrameSym, iScatPilTimeInt)
+                                           ) / (iScatPilFreqInt * iScatPilTimeInt));
+
+      for (iCar = iCarrierKmin; iCar < iCarrierKmax + 1; iCar++)
+        {
+          /* Set carrier array index (since we do not have negative indices
+         in c++) */
+          iCarArrInd = iCar - iCarrierKmin;
+
+
+          /* MSC ---------------------------------------------------------- */
+          /* First set all cells to MSC-cells */
+          matiMapTab[iSym][iCarArrInd] = CM_MSC;
+
+
+          /* FAC ---------------------------------------------------------- */
+          /* FAC positions are defined in a table */
+          if (iFACCounter <= NUM_FAC_CELLS)
+            {
+              /* piTableFAC[x * 2]: first column; piTableFAC[x * 2 + 1]:
+           second column */
+              if (piTableFAC[iFACCounter * 2] * iNumCarrier +
+                  piTableFAC[iFACCounter * 2 + 1] == iFrameSym *
+                  iNumCarrier + iCar)
+                {
+                  iFACCounter++;
+                  matiMapTab[iSym][iCarArrInd] = CM_FAC;
+                }
+            }
+
+
+          /* Scattered pilots --------------------------------------------- */
+          /* Standard: 8.4.4.3:
+         "In some cases gain references fall in locations which coincide
+         with those already defined for either frequency or time
+         references. In these cases, the phase definitions given in
+         clauses 8.4.2 and 8.4.3 take precedence."
+         Therefore, Scattered pilots must be definded FIRST here! */
+
+          /* The rule for calculating the scattered pilots is defined in the
+         specification in the following form:
+         e.g.: k = 2 + 4 * (s mod 5) + 20 * p
+         We define a "frequency-" (FreqInt) and "time-interpolation"
+         (TimeInt). In this example, "4" is the FreqInt and "5" is the
+         TimeInt. The first term "2" is the half of the FreqInt, rounded
+         towards infinity. The parameter "20" is FreqInt * TimeInt */
+          if (iCar == (int) ((_REAL) iScatPilFreqInt / 2 + .5) +
+              iScatPilFreqInt * mod(iFrameSym, iScatPilTimeInt) +
+              iScatPilFreqInt * iScatPilTimeInt * iScatPilotsCounter)
+            {
+              iScatPilotsCounter++;
+
+              /* Set flag in mapping table */
+              matiMapTab[iSym][iCarArrInd] = CM_SCAT_PI;
+
+              /* Set complex value for this pilot */
+              /* Phase calculation ---------------------------------------- */
+              int in, im, ip, i;
+
+              /* Calculations as in drm-standard (8.4.4.3.1) */
+              /* "in" is ROW No and "im" is COLUMN No! */
+              in = mod(iFrameSym, ScatPilots.piConst[1] /* "y" */);
+              im = (int)
+                  ((_REAL) iFrameSym / ScatPilots.piConst[1] /* "y" */);
+              ip = (int) ((_REAL) (iCar - ScatPilots.piConst[2] /* "k_0" */ -
+                                   in * ScatPilots.piConst[0] /* "x" */) / (
+                            ScatPilots.piConst[0] /* "x" */ *
+                            ScatPilots.piConst[1] /* "y" */));
+
+              /* Phase_1024[s,k] =
+               (4Z_256[n,m]pW_1024[n,m] + p^2(1 + s)Q_1024) mod 1024 */
+              iScatPilPhase = mod(4 * ScatPilots.piZ[in *
+                                  ScatPilots.iColSizeWZ + im] + ip *
+                                  ScatPilots.piW[in *
+                                  ScatPilots.iColSizeWZ + im] +
+                                  ip * ip * (1 + iFrameSym) * ScatPilots.iQ, 1024);
+
+
+              /* Gain calculation and applying of complex value ----------- */
+              /* Test, if current carrier-index is one of the "boosted pilots"
+           position */
+              _BOOLEAN bIsBoostedPilot = false;
+              for (i = 0; i < NUM_BOOSTED_SCAT_PILOTS; i++)
+                {
+                  /* In case of match set flag */
+                  if (ScatPilots.piGainTable[i] == iCar)
+                    bIsBoostedPilot = true;
+                }
+
+              /* Boosted pilot: Gain = 2, Regular pilot: Gain = sqrt(2) */
+              if (bIsBoostedPilot)
+                {
+                  matcPilotCells[iSym][iCarArrInd] =
+                      Polar2Cart(2, iScatPilPhase);
+
+                  /* Add flag for boosted pilot */
+                  matiMapTab[iSym][iCarArrInd] |= CM_BOOSTED_PI;
+                }
+              else
+                {
+                  matcPilotCells[iSym][iCarArrInd] =
+                      Polar2Cart(sqrt((_REAL) 2.0), iScatPilPhase);
+                }
+            }
+
+
+          /* Time-reference pilots ---------------------------------------- */
+          /* Time refs at the beginning of each frame, we use a table */
+          if (iFrameSym == 0)
+            {
+              /* Use only the first column in piTableTimePilots */
+              if (piTableTimePilots[iTimePilotsCounter * 2] == iCar)
+                {
+                  /* Set flag in mapping table, consider case of both,
+             scattered pilot and time pilot at same position */
+                  if (_IsScatPil(matiMapTab[iSym][iCarArrInd]))
+                    matiMapTab[iSym][iCarArrInd] |= CM_TI_PI;
+                  else
+                    matiMapTab[iSym][iCarArrInd] = CM_TI_PI;
+
+                  /* Set complex value for this pilot */
+                  matcPilotCells[iSym][iCarArrInd] =
+                      Polar2Cart(sqrt((_REAL) 2.0),
+                                 piTableTimePilots[iTimePilotsCounter * 2 + 1]);
+
+                  if (iTimePilotsCounter == iNumTimePilots - 1)
+                    iTimePilotsCounter = 0;
+                  else
+                    iTimePilotsCounter++;
+                }
+            }
+
+
+          /* Frequency-reference pilots ----------------------------------- */
+          /* These pilots are in all symbols, the positions are stored in
+         a table */
+          /* piTableFreqPilots[x * 2]: first column;
+         piTableFreqPilots[x * 2 + 1]: second column */
+          if (piTableFreqPilots[iFreqPilotsCounter * 2] == iCar)
+            {
+              /* Set flag in mapping table, consider case of multiple
+           definitions of pilot-mapping */
+              if (_IsTiPil(matiMapTab[iSym][iCarArrInd]) ||
+                  _IsScatPil(matiMapTab[iSym][iCarArrInd]))
+                {
+                  matiMapTab[iSym][iCarArrInd] |= CM_FRE_PI;
+                }
+              else
+                matiMapTab[iSym][iCarArrInd] = CM_FRE_PI;
+
+              /* Set complex value for this pilot */
+              /* Test for "special case" defined in drm-standard */
+              _BOOLEAN bIsFreqPilSpeciCase = false;
+              if (eNewRobustnessMode == RM_ROBUSTNESS_MODE_E)
+                {
+                  /* For robustness mode D, carriers 7 and 21 (Means: first
+             and second pilot, not No. 28 (NUM_FREQ_PILOTS - 1) */
+                  if (iFreqPilotsCounter != NUM_FREQ_PILOTS - 1)
+                    {
+                      /* Test for odd values of "s" (iSym) */
+                      if ((iFrameSym % 2) == 1)
+                        bIsFreqPilSpeciCase = true;
+                    }
+                }
+
+              /* Apply complex value */
+              if (bIsFreqPilSpeciCase) matcPilotCells[iSym][iCarArrInd] = Polar2Cart(sqrt((_REAL) 2.0), mod(piTableFreqPilots[iFreqPilotsCounter * 2 + 1] + 512, 1024));
+              else matcPilotCells[iSym][iCarArrInd] = Polar2Cart(sqrt((_REAL) 2.0), piTableFreqPilots[iFreqPilotsCounter * 2 + 1]);
+
+              /* Increase counter and wrap if needed */
+              if (iFreqPilotsCounter == NUM_FREQ_PILOTS - 1) iFreqPilotsCounter = 0;
+              else iFreqPilotsCounter++;
+            }
+
+
+          /* DC-carrier (not used by DRM) --------------------------------- */
+          /* Mark DC-carrier. Must be marked after scattered pilots, because
+         in one case (Robustness Mode D) some pilots must be overwritten!
+         */
+          if (iCar == 0) matiMapTab[iSym][iCarArrInd] = CM_DC;
+
+          /* In Robustness Mode A there are three "not used carriers" */
+          if (eNewRobustnessMode == RM_ROBUSTNESS_MODE_A)
+            {
+              if ((iCar == -1) || (iCar == 1)) matiMapTab[iSym][iCarArrInd] = CM_DC;
+            }
+        }
+    }
+
+
+  /* Count individual cells *************************************************/
+  /* We need to count the cells in a symbol for defining how many values from
+     each source is needed to generate one symbol in carrier-mapping */
+  /* Init all counters */
+  iMaxNumMSCSym = 0;
+  iMSCCounter = 0;
+
+  rAvPowPerSymbol = (_REAL) 0.0;
+  rAvPilPowPerSym = (_REAL) 0.0;
+
+  for (iSym = 0; iSym < iNumSymbolsPerSuperframe; iSym++)
+    {
+      /* Init all counters */
+      veciNumMSCSym[iSym] = 0;
+      veciNumFACSym[iSym] = 0;
+
+      for (iCar = 0; iCar < iNumCarrier; iCar++)
+        {
+          /* MSC */
+          if (_IsMSC(matiMapTab[iSym][iCar]))
+            {
+              veciNumMSCSym[iSym]++;
+
+              /* Count ALL MSC cells per super-frame */
+              iMSCCounter++;
+            }
+
+          /* FAC */
+          if (_IsFAC(matiMapTab[iSym][iCar]))
+            veciNumFACSym[iSym]++;
+
+          /* Calculations for average power per symbol (needed for SNR
+         estimation and simulation). DC carrier is zero (contributes not
+         to the average power) */
+          if (!_IsDC(matiMapTab[iSym][iCar]))
+            {
+              if (_IsData(matiMapTab[iSym][iCar]))
+                {
+                  /* Data cells have average power of 1 */
+                  rAvPowPerSymbol += (_REAL) 1.0;
+                }
+              else
+                {
+                  /* All pilots have power of 2 except of the boosted pilots
+             at the edges of the spectrum (they have power of 4) */
+                  if (_IsBoosPil(matiMapTab[iSym][iCar]))
+                    {
+                      rAvPowPerSymbol += (_REAL) 4.0;
+                      rAvPilPowPerSym += (_REAL) 4.0;
+                    }
+                  else
+                    {
+                      /* Regular pilot has power of 2 */
+                      rAvPowPerSymbol += (_REAL) 2.0;
+                      rAvPilPowPerSym += (_REAL) 2.0;
+                    }
+                }
+            }
+        }
+
+      /* Set maximum for symbol */
+      /* MSC */
+      if (iMaxNumMSCSym < veciNumMSCSym[iSym])
+        iMaxNumMSCSym = veciNumMSCSym[iSym];
+    }
+
+  /* Set number of useful MSC cells */
+  iNumUsefMSCCellsPerFrame =
+      (int) (iMSCCounter / NUM_FRAMES_IN_SUPERFRAME);
+
+  /* Calculate dummy cells for MSC */
+  iNoMSCDummyCells = iMSCCounter - iNumUsefMSCCellsPerFrame  *
+      NUM_FRAMES_IN_SUPERFRAME;
+
+  /* Correct last MSC count (because of dummy cells) */
+  veciNumMSCSym[iNumSymbolsPerSuperframe - 1] -= iNoMSCDummyCells;
+
+  /* Normalize the average powers */
+  rAvPowPerSymbol /= iNumSymbolsPerSuperframe;
+  rAvPilPowPerSym /= iNumSymbolsPerSuperframe;
+
+
+  /* ########################################################################## */
+#ifdef _DEBUG_
+  /* Save table in file */
+  FILE* pFile = fopen("CarMapTable.txt", "w");
+
+  /* Title */
+  fprintf(pFile, "Robustness mode ");
+  switch (eNewRobustnessMode)
+    {
+    case RM_ROBUSTNESS_MODE_A:
+      fprintf(pFile, "A");
+    break;
+    case RM_ROBUSTNESS_MODE_B:
+      fprintf(pFile, "B");
+    break;
+    case RM_ROBUSTNESS_MODE_E:
+      fprintf(pFile, "E");
+    break;
+    }
+  fprintf(pFile, " / Spectrum occupancy %d\n\n", iSpecOccArrayIndex);
+
+  /* Actual table */
+  for (int i = 0; i < iNumSymbolsPerSuperframe; i++)
+    {
+      for (int j = 0; j < iNumCarrier; j++)
+        {
+          if (_IsDC(matiMapTab[i][j]))
+            {
+              fprintf(pFile, ":");
+              continue;
+            }
+          if (_IsMSC(matiMapTab[i][j]))
+            {
+              fprintf(pFile, ".");
+              continue;
+            }
+          if (_IsFAC(matiMapTab[i][j]))
+            {
+              fprintf(pFile, "X");
+              continue;
+            }
+          if (_IsTiPil(matiMapTab[i][j]))
+            {
+              fprintf(pFile, "T");
+              continue;
+            }
+          if (_IsFreqPil(matiMapTab[i][j]))
+            {
+              fprintf(pFile, "f");
+              continue;
+            }
+          if (_IsScatPil(matiMapTab[i][j]))
+            {
+              /* Special mark for boosted pilots */
+              if (_IsBoosPil(matiMapTab[i][j]))
+                fprintf(pFile, "*");
+              else
+                fprintf(pFile, "0");
+              continue;
+            }
+        }
+      fprintf(pFile, "\n");
+    }
+
+  /* Legend */
+  fprintf(pFile, "\n\nLegend:\n\t: DC-carrier\n\t. MCS cells");
+  fprintf(pFile, "\n\tX FAC cells\n\tT time pilots\n\tf frequency pilots");
+  fprintf(pFile, "\n\t0 scattered pilots\n\t* boosted scattered pilots\n");
+
+  fclose(pFile);
+
+  //#ifdef _DEBUG_
+
+  /* Save pilot values in file */
+  /* Use following command to plot pilot complex values in Matlab:
+
+  clear all;close all;load PilotCells.dat;subplot(211),mesh(abs(complex(PilotCells(:,1:2:end), PilotCells(:,2:2:end))));subplot(212),mesh(angle(complex(PilotCells(:,1:2:end), PilotCells(:,2:2:end))))
+
+(It plots the absolute of the pilots in the upper plot and angle in 
+the lower plot.)
+*/
+  pFile = fopen("test/PilotCells.dat", "w");
+  for (int z = 0; z < iNumSymbolsPerSuperframe; z++)
+    {
+      for (int v = 0; v < iNumCarrier; v++)
+        fprintf(pFile, "%e %e ", matcPilotCells[z][v].real(),
+                matcPilotCells[z][v].imag());
+
+      fprintf(pFile, "\n");
+    }
+  fclose(pFile);
+#endif
+  /* ########################################################################## */
+}
+
+_COMPLEX CCellMappingTable::Polar2Cart(const _REAL rAbsolute, 
+                                       const int iPhase) const
+{
+  /*
+  This function takes phases normalized to 1024 as defined in the drm-
+  standard.
+*/
+  return _COMPLEX(rAbsolute * cos((_REAL) 2 * crPi * iPhase / 1024),
+                  rAbsolute * sin((_REAL) 2 * crPi * iPhase / 1024));
+}
+
+int CCellMappingTable::mod(const int ix, const int iy) const
+{
+  /* Modulus definition for integer numbers */
+  if (ix < 0)
+    return ix % iy + iy;
+  else
+    return ix % iy;
+}
diff --git a/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.h b/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.h
new file mode 100644
index 0000000..d30a801
--- /dev/null
+++ b/qsstv/drmtx/common/ofdmcellmapping/CellMappingTable.h
@@ -0,0 +1,150 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(CELLMAPPINGTABLE_H__3B0BA660_CA63_4347A0D31912__INCLUDED_)
+#define CELLMAPPINGTABLE_H__3B0BA660_CA63_4347A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../tables/TableCarMap.h"
+#include "../tables/TableFAC.h"
+#include "utils/vector.h"
+
+
+/* Definitions ****************************************************************/
+/* Power definitions for pilots, boosted pilots and data cells (average) */
+#define AV_DATA_CELLS_POWER		((_REAL) 1.0)
+#define AV_PILOT_POWER			((_REAL) 2.0)
+#define AV_BOOSTED_PIL_POWER	((_REAL) 4.0)
+
+
+/* We define a bit for each flag to allow multiple assignments */
+#define	CM_DC					1	/* Bit 0 */ // CM: Carrier Mapping
+#define	CM_MSC					2	/* Bit 1 */
+#define	CM_SDC					4	/* Bit 2 */
+#define	CM_FAC					8	/* Bit 3 */
+#define	CM_TI_PI				16	/* Bit 4 */
+#define	CM_FRE_PI				32	/* Bit 5 */
+#define	CM_SCAT_PI				64	/* Bit 6 */
+#define	CM_BOOSTED_PI			128	/* Bit 7 */
+
+/* Definitions for checking the cells */
+#define _IsDC(a)				((a) & CM_DC)
+
+#define _IsMSC(a)				((a) & CM_MSC)
+#define _IsSDC(a)				((a) & CM_SDC)
+#define _IsFAC(a)				((a) & CM_FAC)
+
+#define _IsData(a)				(((a) & CM_MSC) || ((a) & CM_SDC) || ((a) & CM_FAC))
+
+
+#define _IsTiPil(a)				((a) & CM_TI_PI)
+#define _IsFreqPil(a)			((a) & CM_FRE_PI)
+#define _IsScatPil(a)			((a) & CM_SCAT_PI)
+
+#define _IsPilot(a)				(((a) & CM_TI_PI) || ((a) & CM_FRE_PI) || ((a) & CM_SCAT_PI))
+#define _IsBoosPil(a)			((a) & CM_BOOSTED_PI)
+
+
+/* Classes ********************************************************************/
+class CCellMappingTable
+{
+public:
+	CCellMappingTable() : iNumSymbolsPerSuperframe(0) {}
+	virtual ~CCellMappingTable() {}
+
+	void MakeTable(ERobMode eNewRobustnessMode, ESpecOcc eNewSpectOccup);
+
+	struct CRatio {int iEnum; int iDenom;};
+
+	/* Mapping table and pilot cell matrix */
+	CMatrix<int>		matiMapTab; 
+	CMatrix<_COMPLEX>	matcPilotCells;
+
+	int					iNumSymbolsPerSuperframe;
+	int					iNumSymPerFrame; /* Number of symbols per frame */
+	int					iNumCarrier;
+	int					iScatPilTimeInt; /* Time interpolation */
+	int					iScatPilFreqInt; /* Frequency interpolation */
+
+	int					iMaxNumMSCSym; /* Max number of MSC cells in a symbol */
+
+	/* Number of MSC cells in a symbol */
+	CVector<int>		veciNumMSCSym; 
+
+	/* Number of FAC cells in a symbol */
+	CVector<int>		veciNumFACSym; 
+
+	/* Number of SDC cells in a symbol */
+	CVector<int>		veciNumSDCSym;
+
+	int					iFFTSizeN; /* FFT size of the OFDM modulation */
+	int					iCarrierKmin; /* Carrier index of carrier with lowest frequency */
+	int					iCarrierKmax; /* Carrier index of carrier with highest frequency */
+	int					iIndexDCFreq; /* Index of DC carrier */
+	int					iShiftedKmin; /* Shifted carrier min ("sound card pass-band") */
+	int					iShiftedKmax; /* Shifted carrier max ("sound card pass-band") */
+	CRatio				RatioTgTu; /* Ratio between guard-interval and useful part */
+	int					iGuardSize; /* Length of guard-interval measured in "time-bins" */
+	int					iSymbolBlockSize; /* Useful part plus guard-interval in "time-bins" */
+	int					iNumIntpFreqPil; /* Number of time-interploated frequency pilots */
+
+	int					iNumUsefMSCCellsPerFrame; /* Number of MSC cells per multiplex frame N_{MUX} */
+	int					iNumSDCCellsPerSFrame; /* Number of SDC cells per super-frame */
+
+	/* Needed for SNR estimation and simulation */
+	_REAL				rAvPowPerSymbol; /* Total average power per symbol */
+	_REAL				rAvScatPilPow; /* Average power of scattered pilots per cell */
+        _REAL				rAvPilPowPerSym ;  /* added pa0mbo */
+
+protected:
+	/* Internal parameters for MakeTable function --------------------------- */
+	struct CScatPilots
+	{	
+		CScatPilots(): piConst(NULL), iColSizeWZ(0), piW(NULL), piZ(NULL),
+		iQ(0),piGainTable(NULL) {}
+
+		/* For the pase */
+		const int*  piConst;
+		int			iColSizeWZ;
+		const int*	piW;
+		const int*	piZ;
+		int			iQ;
+
+		/* For the gain */
+		const int*	piGainTable;
+	};
+
+
+private:
+	_COMPLEX	Polar2Cart(const _REAL rAbsolute, const int iPhase) const;
+	int			mod(const int ix, const int iy) const;
+};
+
+
+#endif // !defined(CELLMAPPINGTABLE_H__3B0BA660_CA63_4347A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.cpp b/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.cpp
new file mode 100644
index 0000000..6a020da
--- /dev/null
+++ b/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.cpp
@@ -0,0 +1,152 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	Mapping of the symbols on the carriers
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+#include "OFDMCellMapping.h"
+#include "../tables/TableCarrier.h"
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* OFDM cells mapping														   *
+\******************************************************************************/
+void COFDMCellMapping::ProcessDataInternal(CParameter& TransmParam)
+{
+	
+        const CCellMappingTable& Param = TransmParam.CellMappingTable;  /*  pa0mbo */
+	int iCar;
+	int iMSCCounter;
+	int iFACCounter;
+	int iDummyCellCounter;
+	int iSymbolCounterAbs;
+
+
+
+	/* Mapping of the data and pilot cells on the OFDM symbol --------------- */
+	/* Set absolute symbol position */
+	iSymbolCounterAbs =
+		TransmParam.iFrameIDTransm * iNumSymPerFrame + iSymbolCounter;
+
+	/* Init temporary counter */
+	iDummyCellCounter = 0;
+	iMSCCounter = 0;
+	iFACCounter = 0;
+
+	for (iCar = 0; iCar < iNumCarrier; iCar++)
+	{
+
+		/* MSC */
+
+		if (_IsMSC(Param.matiMapTab[iSymbolCounterAbs][iCar]))   /* pa0mbo was TransmParam. */
+		{
+			if (iMSCCounter >= Param.veciNumMSCSym[iSymbolCounterAbs])
+			{
+				/* Insert dummy cells */
+				(*pvecOutputData)[iCar] = pcDummyCells[iDummyCellCounter];
+
+				iDummyCellCounter++;
+
+			}
+			else
+				(*pvecOutputData)[iCar] = (*pvecInputData)[iMSCCounter];
+				
+			iMSCCounter++;
+		}
+
+		/* FAC */
+		if (_IsFAC(Param.matiMapTab[iSymbolCounterAbs][iCar]))
+		{
+			(*pvecOutputData)[iCar] = (*pvecInputData2)[iFACCounter];
+				
+			iFACCounter++;
+		}
+
+		/* DC carrier */
+		if (_IsDC(Param.matiMapTab[iSymbolCounterAbs][iCar]))
+			(*pvecOutputData)[iCar] = _COMPLEX((_REAL) 0.0, (_REAL) 0.0);
+
+		/* Pilots */
+		if (_IsPilot(Param.matiMapTab[iSymbolCounterAbs][iCar]))
+			(*pvecOutputData)[iCar] = 
+				Param.matcPilotCells[iSymbolCounterAbs][iCar];
+	}
+
+	/* Increase symbol-counter and wrap if needed */
+	iSymbolCounter++;
+	if (iSymbolCounter == iNumSymPerFrame)
+	{
+		iSymbolCounter = 0;
+
+		/* Increase frame-counter (ID) (Used also in FAC.cpp) */
+		TransmParam.iFrameIDTransm++;
+		if (TransmParam.iFrameIDTransm == NUM_FRAMES_IN_SUPERFRAME)
+			TransmParam.iFrameIDTransm = 0;
+	}
+
+	/* Set absolute symbol position (for updated relative positions) */
+	iSymbolCounterAbs =
+		TransmParam.iFrameIDTransm * iNumSymPerFrame + iSymbolCounter;
+
+	/* Set input block-sizes for next symbol */
+	iInputBlockSize = Param.veciNumMSCSym[iSymbolCounterAbs];
+	iInputBlockSize2 = Param.veciNumFACSym[iSymbolCounterAbs];
+	/* printf("OFDMCellmapping proces updated iInpblk %d iInp2 %d iSymbcntrabs %d \n",
+			iInputBlockSize, iInputBlockSize2, iSymbolCounterAbs); */
+}
+
+void COFDMCellMapping::InitInternal(CParameter& TransmParam)
+{
+        const CCellMappingTable& Param = TransmParam.CellMappingTable;  /*  pa0mbo */
+	iNumSymPerFrame = Param.iNumSymPerFrame;
+	iNumCarrier = Param.iNumCarrier;
+
+	/* Init symbol-counter */
+	iSymbolCounter = 0;
+
+	/* Init frame ID */
+	TransmParam.iFrameIDTransm = 0;
+
+	/* Choose right dummy cells for MSC QAM scheme */
+	switch (TransmParam.eMSCCodingScheme)
+	{
+    case CS_3_HMSYM: // not use so default to case CS_2_SM
+    case CS_3_HMMIX:
+    case CS_1_SM:
+		case CS_2_SM:                // pa0mbo was CParameter::CS_2_SM
+		pcDummyCells = (_COMPLEX*) &cDummyCells16QAM[0];
+		break;
+
+	case CS_3_SM:
+		pcDummyCells = (_COMPLEX*) &cDummyCells64QAM[0];  // pa0mbo was CParameter::CS_#_SM
+		break;
+	}
+
+	/* Define block-sizes for input and output of the module ---------------- */
+	iInputBlockSize = Param.veciNumMSCSym[0]; /* MSC */
+	iInputBlockSize2 = Param.veciNumFACSym[0]; /* FAC */
+	iOutputBlockSize = Param.iNumCarrier; /* Output */
+	// printf("OFDM CELLMAP : Inputblksiz %d Inpublksiz2 %d OutputBlkSiz %d\n",
+			// iInputBlockSize, iInputBlockSize2, iOutputBlockSize);
+}
diff --git a/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.h b/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.h
new file mode 100644
index 0000000..9dc2774
--- /dev/null
+++ b/qsstv/drmtx/common/ofdmcellmapping/OFDMCellMapping.h
@@ -0,0 +1,59 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(OFDMCELLMAPPING_H__3B0BA660_CA63_4344_BB2BE7A0D31912__INCLUDED_)
+#define OFDMCELLMAPPING_H__3B0BA660_CA63_4344_BB2BE7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../Parameter.h"
+#include "../util/Modul.h"
+#include "../tables/TableCarMap.h"
+#include "../tables/TableFAC.h"
+
+
+/* Classes ********************************************************************/
+class COFDMCellMapping : public CTransmitterModul<_COMPLEX, _COMPLEX>
+{
+public:
+	COFDMCellMapping() {}
+	virtual ~COFDMCellMapping() {}
+
+protected:
+	int			iNumSymPerFrame;
+	int			iNumCarrier;
+	int			iSymbolCounter;
+	_COMPLEX*	pcDummyCells;
+
+	virtual void InitInternal(CParameter& TransmParam);
+	virtual void ProcessDataInternal(CParameter& TransmParam);
+};
+
+
+
+
+#endif // !defined(OFDMCELLMAPPING_H__3B0BA660_CA63_4344_BB2BE7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/soundinterface.h b/qsstv/drmtx/common/soundinterface.h
new file mode 100644
index 0000000..43e3709
--- /dev/null
+++ b/qsstv/drmtx/common/soundinterface.h
@@ -0,0 +1,56 @@
+/******************************************************************************\
+ * British Broadcasting Corporation
+ * Copyright (c) 2007
+ *
+ * Author(s):
+ *	Julian Cable
+ * 
+ * Decription:
+ * sound interfaces
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#ifndef _SOUNDINTERFACE_H
+#define _SOUNDINTERFACE_H
+#include "qsstvglobal.h"
+#include "utils/vector.h"
+
+//class CSoundInInterface : public CSelectionInterface
+//{
+//public:
+//	virtual 			~CSoundInInterface() {}
+
+//	/* sound card interface - used by ReadData */
+//	virtual void		Init(int iNewBufferSize, _BOOLEAN bNewBlocking = true)=0;
+//	virtual _BOOLEAN	Read(CVector<short>& psData)=0;
+//	virtual void		Close()=0;
+
+//};
+
+class CSoundOutInterface
+{
+public:
+	virtual 			~CSoundOutInterface() {}
+
+	/* sound card interface - used by WriteData */
+//	virtual void		Init(int iNewBufferSize, _BOOLEAN bNewBlocking = true)=0;
+	virtual _BOOLEAN	Write(CVector<short>& psData)=0;
+};
+
+#endif
diff --git a/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.cpp b/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.cpp
new file mode 100644
index 0000000..600c199
--- /dev/null
+++ b/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.cpp
@@ -0,0 +1,138 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	Audio source encoder/decoder
+ *
+ * adapted to ham sstv use PA0MBO - Ties Bos
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 1111
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "AudioSourceDecoder.h"
+#include <iostream>
+#include <time.h>
+#define AUD_DEC_TRANSFROM_LENGTH 960
+
+/******************************************************************************\
+* Encoder                                                                      *
+\******************************************************************************/
+
+void CAudioSourceEncoderImplementation::ProcessDataInternal( CVectorEx < _BINARY >  *pvecOutputData, int &iOutputBlockSize)
+{
+	int i;
+
+	/* Reset data to zero. This is important since usually not all data is used
+	   and this data has to be set to zero as defined in the DRM standard */
+  for (i = 0; i < iOutputBlockSize; i++) (*pvecOutputData)[i] = 0;
+	/* Data service and text message application ---------------------------- */
+	if (bIsDataService == true)
+	{
+    // TODO: make a separate modul for data encoding
+		/* Write data packets in stream */
+		CVector < _BINARY > vecbiData;
+		const int iNumPack = iOutputBlockSize / iTotPacketSize;
+
+	//	printf("In process data AudioSourceEnc iOutputBlcksi %d iTotPacketSize = %d \n", iOutputBlockSize, iTotPacketSize);
+		int iPos = 0;
+
+		for (int j = 0; j < iNumPack; j++)
+		{
+			/* Get new packet */
+			DataEncoder.GeneratePacket(vecbiData);
+
+			/* Put it on stream */
+			for (i = 0; i < iTotPacketSize; i++)
+			{
+				(*pvecOutputData)[iPos] = vecbiData[i];
+				iPos++;
+			}
+		}
+	}
+}
+
+
+
+void CAudioSourceEncoderImplementation::InitInternalTx(CParameter & TransmParam, int &iOutputBlockSize)
+{
+	int iCurStreamID;
+	int iCurSelServ = 0;		// TEST
+	TransmParam.Lock(); 
+
+  // Calculate number of input samples in mono. Audio block are always 400 ms long
+//  const int iNumInSamplesMono = (int) ((_REAL) SOUNDCRD_SAMPLE_RATE * (_REAL) 0.4 /* 400 ms */ );
+
+	/* Set the total available number of bits, byte aligned */
+  iTotNumBitsForUsage = (TransmParam.iNumDecodedBitsMSC / SIZEOF__BYTE) * SIZEOF__BYTE;
+
+	/* Total number of bytes which can be used for data and audio */
+	const int iTotNumBytesForUsage = iTotNumBitsForUsage / SIZEOF__BYTE;
+
+        /* printf("in audiosourceEncoder init smplmono = %d  iTotNumBitsForUsage = %d bytefor usage = %d \n", 
+			iNumInSamplesMono,  iTotNumBitsForUsage, iTotNumBytesForUsage ); */
+
+	if (TransmParam.iNumDataService == 1)
+	{
+		/* Data service ----------------------------------------------------- */
+		bIsDataService = true;
+		// printf("Data servic is true \n");
+		iTotPacketSize = DataEncoder.Init(TransmParam);
+                // printf("na DataEncoder Init in AudioSourceEncoder init iTotPacketSize = %d\n", iTotPacketSize);
+
+		/* Get stream ID for data service */
+		iCurStreamID = TransmParam.Service[iCurSelServ].DataParam.iStreamID;
+                /* printf(" in AudioSourceEncoder dataservice is true iTotPacketSize = %d, custreamId = %d \n",
+				iTotPacketSize, iCurStreamID ); */
+
+	}
+	else
+	{
+          printf("Not implemented \n");
+          exit(1);
+	}
+
+	/* Adjust part B length for SDC stream. Notice, that the
+	   "TransmParam.iNumDecodedBitsMSC" parameter depends on these settings.
+	   Thus, length part A and B have to be set before, preferably in the
+	   DRMTransmitter initialization */
+  if ((TransmParam.Stream[iCurStreamID].iLenPartA == 0) || (iTotNumBytesForUsage < TransmParam.Stream[iCurStreamID].iLenPartA))
+	{
+    /* Equal error protection was chosen or protection part A was chosen too high, set to equal error protection! */
+		TransmParam.Stream[iCurStreamID].iLenPartA = 0;
+		TransmParam.Stream[iCurStreamID].iLenPartB = iTotNumBytesForUsage;
+	}
+	else
+    TransmParam.Stream[iCurStreamID].iLenPartB = iTotNumBytesForUsage - TransmParam.Stream[iCurStreamID].iLenPartA;
+
+	/* Define input and output block size */
+	iOutputBlockSize = TransmParam.iNumDecodedBitsMSC;
+	TransmParam.Unlock(); 
+}
+
+
+CAudioSourceEncoderImplementation::~CAudioSourceEncoderImplementation()
+{
+
+}
+
+
+
diff --git a/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.h b/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.h
new file mode 100644
index 0000000..df35a1f
--- /dev/null
+++ b/qsstv/drmtx/common/sourcedecoders/AudioSourceDecoder.h
@@ -0,0 +1,120 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer, Ollie Haffenden
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(AUIDOSOURCEDECODER_H__3B0BA660_CABB2B_23E7A0D31912__INCLUDED_)
+#define AUIDOSOURCEDECODER_H__3B0BA660_CABB2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../Parameter.h"
+#include "../util/Modul.h"
+#include "../util/CRC.h"
+//#include "../TextMessage.h"
+#include "../datadecoding/DataDecoder.h"
+#include "../util/Utilities.h"
+
+
+
+
+/* Classes ********************************************************************/
+class CAudioSourceEncoderImplementation
+{
+public:
+  CAudioSourceEncoderImplementation() : bUsingTextMessage(false)
+  {}
+  virtual ~CAudioSourceEncoderImplementation();
+
+//  void SetTextMessage(const string& strText);
+//  void ClearTextMessage();
+
+  void SetPicFileName(QByteArray *ba, const QString name,const QString format)
+  {
+    DataEncoder.GetSliShowEnc()->AddArray(ba,name,format);
+  }
+  void ClearPicFileNames()
+  {DataEncoder.GetSliShowEnc()->ClearAllFileNames();}
+  _BOOLEAN GetTransStat(string& strCPi, _REAL& rCPe)
+  {return DataEncoder.GetSliShowEnc()->GetTransStat(strCPi, rCPe);}
+
+protected:
+//  CTextMessageEncoder		TextMessage;
+  _BOOLEAN				bUsingTextMessage;
+  CDataEncoder			DataEncoder;
+  int						iTotPacketSize;
+  _BOOLEAN				bIsDataService;
+  int						iTotNumBitsForUsage;
+
+
+public:
+  virtual void InitInternalTx(CParameter &TransmParam, int &iOutputBlockSize);
+  //		virtual void InitInternalRx(CParameter& Param, int &iInputBlockSize, int &iOutputBlockSize);
+  virtual void ProcessDataInternal( CVectorEx<_BINARY>* pvecOutputData, int &iOutputBlockSize);
+  //		virtual void ProcessDataInternalRx( CVectorEx<_SAMPLE>* pvecInputData,
+  //                                                 CVectorEx<_BINARY>* pvecOutputData, int &InputBlockSize, int &iOutputBlockSize);
+//  virtual void ProcessDataInternal(CVectorEx<_SAMPLE>*,CVectorEx<_BINARY>* pvecOutputData, int &, int &iOutputBlockSize);
+};
+
+
+class CAudioSourceEncoder : public CTransmitterModul<_SAMPLE, _BINARY>
+{
+public:
+	CAudioSourceEncoder() {}
+	virtual ~CAudioSourceEncoder() {}
+
+//	void SetTextMessage(const string& strText) {AudioSourceEncoderImpl.SetTextMessage(strText);}
+//	void ClearTextMessage() {AudioSourceEncoderImpl.ClearTextMessage();}
+	
+  void SetPicFileName(QByteArray *ba, const QString name,const QString format)
+      {
+         AudioSourceEncoderImpl.SetPicFileName(ba,name,format);
+     }
+
+	void ClearPicFileNames() {AudioSourceEncoderImpl.ClearPicFileNames();}
+
+	_BOOLEAN GetTransStat(string& strCPi, _REAL& rCPe)
+			{return AudioSourceEncoderImpl.GetTransStat(strCPi, rCPe);}
+
+protected:
+	CAudioSourceEncoderImplementation AudioSourceEncoderImpl;
+
+	virtual void InitInternal(CParameter& TransmParam)
+	{
+		AudioSourceEncoderImpl.InitInternalTx(TransmParam, iOutputBlockSize);
+	}
+	
+	virtual void ProcessDataInternal(CParameter& )
+	{
+		AudioSourceEncoderImpl.ProcessDataInternal(pvecOutputData,  iOutputBlockSize);
+	}
+
+//  virtual void ProcessDataInternalRx(CParameter& )
+//  {
+//    AudioSourceEncoderImpl.ProcessDataInternalRx(pvecInputData, pvecOutputData, iInputBlockSize,   iOutputBlockSize);
+//  }
+
+};
+
+
+
+#endif // !defined(AUIDOSOURCEDECODER_H__3B0BA660_CABB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableAMSS.h b/qsstv/drmtx/common/tables/TableAMSS.h
new file mode 100644
index 0000000..7d178e6
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableAMSS.h
@@ -0,0 +1,52 @@
+/******************************************************************************\
+ * BBC Research & Development
+ * Copyright (c) 2005
+ *
+ * Author(s):
+ *	Andrew Murphy
+ *
+ * Description:
+ *	Tables for AMSS
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(__TABLE_AMSS_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_)
+#define __TABLE_AMSS_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_
+
+#include <string>
+#include "../GlobalDefinitions.h"
+
+
+/* Definitions ****************************************************************/
+#define LEN_TABLE_AMSS_CARRIER_MODE		8
+
+const string strTableAMSSCarrierMode[LEN_TABLE_AMSS_CARRIER_MODE] =
+{
+	"No Carrier Control",	// 0 0 0
+	"reserved",				// 0 0 1
+	"AMC Mode 1 (-3dB)",	// 0 1 0
+	"AMC Mode 2 (-6dB)",	// 0 1 1
+	"DAM Mode 1 (+3dB)",	// 1 0 0
+	"DAM Mode 2 (+6dB)",	// 1 0 1
+	"reserved",				// 1 1 0
+	"reserved"				// 1 1 1
+};
+
+
+#endif // !defined(__TABLE_AMSS_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableCarMap.h b/qsstv/drmtx/common/tables/TableCarMap.h
new file mode 100644
index 0000000..53c0197
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableCarMap.h
@@ -0,0 +1,242 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sttv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Tables for carrier mapping
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(TABLE_CAR_MAP_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
+#define TABLE_CAR_MAP_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+#include "../matlib/Matlib.h"
+#include "TableQAMMapping.h"
+
+
+/* Global functions ***********************************************************/
+/*
+	----------------------------------------------------------------------------
+	Implementation of distance to nearest constellation point (symbol) for all
+	QAM types
+*/
+
+
+inline CComplex MinDist4QAM(const CComplex cI)
+{
+	/* Return vector pointing to nearest signal point of this constellation.
+	   2 possible constellation points for real and imaginary axis */
+	return CComplex(
+		/* Real axis minimum distance */
+		Min(Abs(rTableQAM4[0][0] - Real(cI)), Abs(rTableQAM4[1][0] - Real(cI))),
+		/* Imaginary axis minimum distance */
+		Min(Abs(rTableQAM4[0][1] - Imag(cI)), Abs(rTableQAM4[1][1] - Imag(cI))));
+}
+
+inline CComplex MinDist16QAM(const CComplex cI)
+{
+	/* Return vector pointing to nearest signal point of this constellation.
+	   4 possible constellation points for real and imaginary axis */
+	return CComplex(
+		/* Real axis minimum distance */
+		Min(Abs(rTableQAM16[0][0] - Real(cI)), Abs(rTableQAM16[1][0] - Real(cI)),
+			Abs(rTableQAM16[2][0] - Real(cI)), Abs(rTableQAM16[3][0] - Real(cI))),
+		/* Imaginary axis minimum distance */
+		Min(Abs(rTableQAM16[0][1] - Imag(cI)), Abs(rTableQAM16[1][1] - Imag(cI)),
+			Abs(rTableQAM16[2][1] - Imag(cI)), Abs(rTableQAM16[3][1] - Imag(cI))));
+}
+
+inline CComplex MinDist64QAM(const CComplex cI)
+{
+	/* Return vector pointing to nearest signal point of this constellation.
+	   8 possible constellation points for real and imaginary axis */
+	return CComplex(
+		/* Real axis minimum distance */
+		Min(Abs(rTableQAM64SM[0][0] - Real(cI)), Abs(rTableQAM64SM[1][0] - Real(cI)),
+			Abs(rTableQAM64SM[2][0] - Real(cI)), Abs(rTableQAM64SM[3][0] - Real(cI)),
+			Abs(rTableQAM64SM[4][0] - Real(cI)), Abs(rTableQAM64SM[5][0] - Real(cI)),
+			Abs(rTableQAM64SM[6][0] - Real(cI)), Abs(rTableQAM64SM[7][0] - Real(cI))),
+		/* Imaginary axis minimum distance */
+		Min(Abs(rTableQAM64SM[0][1] - Imag(cI)), Abs(rTableQAM64SM[1][1] - Imag(cI)),
+			Abs(rTableQAM64SM[2][1] - Imag(cI)), Abs(rTableQAM64SM[3][1] - Imag(cI)),
+			Abs(rTableQAM64SM[4][1] - Imag(cI)), Abs(rTableQAM64SM[5][1] - Imag(cI)),
+			Abs(rTableQAM64SM[6][1] - Imag(cI)), Abs(rTableQAM64SM[7][1] - Imag(cI))));
+}
+
+
+
+
+/* Time pilots ****************************************************************/
+/* The two numbers are: {carrier no, phase} (Phases are normalized to 1024) */
+#define RMA_NUM_TIME_PIL	16
+const int iTableTimePilRobModA[RMA_NUM_TIME_PIL][2] = {
+	{ 6, 973},
+	{ 7, 205},
+	{11, 717},
+	{12, 264},
+	{15, 357},
+	{16, 357},
+	{23, 952},
+	{29, 440},
+	{30, 856},
+	{33,  88},
+	{34,  88},
+	{38,  68},
+	{39, 836},
+	{41, 836},
+	{45, 836},
+	{46, 1008},
+};
+
+#define RMB_NUM_TIME_PIL	15
+
+const int iTableTimePilRobModB[RMB_NUM_TIME_PIL][2] = {
+	{ 6, 304},
+	{10, 331},
+	{11, 108},
+	{14, 620},
+	{17, 192},
+	{18, 704},
+	{27,  44},
+	{28, 432},
+	{30, 588},
+	{33, 844},
+	{34, 651},
+	{38, 651},
+	{40, 651},
+	{41, 460},
+	{44, 944},
+};
+
+#define RME_NUM_TIME_PIL	8
+const int iTableTimePilRobModE[RME_NUM_TIME_PIL][2] = {
+	{ 7, 432},
+	{ 8, 331},
+	{13, 108},
+	{14, 620},
+	{21, 192},
+	{22, 704},
+	{26,  44},
+	{27, 304},
+};
+
+
+/* Scattered pilots ***********************************************************/
+/* Definitions for the positions of scattered pilots */
+#define RMA_SCAT_PIL_FREQ_INT	4
+#define RMA_SCAT_PIL_TIME_INT	5
+
+#define RMB_SCAT_PIL_FREQ_INT	2
+#define RMB_SCAT_PIL_TIME_INT	3
+
+#define RME_SCAT_PIL_FREQ_INT	1
+#define RME_SCAT_PIL_TIME_INT	4
+
+/* Phase definitions of scattered pilots ------------------------------------ */
+const int iTableScatPilConstRobModA[3] = {4, 5, 2};
+
+const int iTableScatPilConstRobModB[3] = {2, 3, 1};
+
+const int iTableScatPilConstRobModE[3] = {1, 4, 1};
+
+#define SIZE_ROW_WZ_ROB_MOD_A	5
+#define SIZE_COL_WZ_ROB_MOD_A	3
+const int iScatPilWRobModA[SIZE_ROW_WZ_ROB_MOD_A][SIZE_COL_WZ_ROB_MOD_A] = {
+	{228, 341, 455},
+	{455, 569, 683},
+	{683, 796, 910},
+	{910,   0, 114},
+	{114, 228, 341}
+};
+const int iScatPilZRobModA[SIZE_ROW_WZ_ROB_MOD_A][SIZE_COL_WZ_ROB_MOD_A] = {
+	{0,    81, 248},
+	{18,  106, 106},
+	{122, 116,  31},
+	{129, 129,  39},
+	{33,   32, 111}
+};
+const int iScatPilQRobModA = 36;
+
+#define SIZE_ROW_WZ_ROB_MOD_B	3
+#define SIZE_COL_WZ_ROB_MOD_B	5
+const int iScatPilWRobModB[SIZE_ROW_WZ_ROB_MOD_B][SIZE_COL_WZ_ROB_MOD_B] = {
+	{512,   0, 512,   0, 512},
+	{0,   512,   0, 512,   0},
+	{512,   0, 512,   0, 512}
+};
+const int iScatPilZRobModB[SIZE_ROW_WZ_ROB_MOD_B][SIZE_COL_WZ_ROB_MOD_B] = {
+	{0,    57, 164,  64,  12},
+	{168, 255, 161, 106, 118},
+	{25,  232, 132, 233,  38}
+};
+const int iScatPilQRobModB = 12;
+
+#define SIZE_ROW_WZ_ROB_MOD_E	4
+#define SIZE_COL_WZ_ROB_MOD_E	5
+const int iScatPilWRobModE[SIZE_ROW_WZ_ROB_MOD_E][SIZE_COL_WZ_ROB_MOD_E] = {
+	{512,   0, 512,   0, 512},
+	{0,   512,   0, 512,   0},
+	{512,   0, 512,   0, 512},
+	{0,   512,   0, 512,   0}
+};
+const int iScatPilZRobModE[SIZE_ROW_WZ_ROB_MOD_E][SIZE_COL_WZ_ROB_MOD_E] = {
+	{0,    57, 164,  64,  12},
+	{168, 255, 161, 106, 118},
+	{25,  232, 132, 233,  38},
+	{168, 255, 161, 106, 118}
+};
+const int iScatPilQRobModE = 10;
+
+/* Gain definitions of scattered pilots ------------------------------------- */
+#define NUM_BOOSTED_SCAT_PILOTS		4
+const int iScatPilGainRobModA[2][NUM_BOOSTED_SCAT_PILOTS] = {
+	{2, 4, 50, 54},
+	{2, 6, 54, 58}
+};
+
+const int iScatPilGainRobModB[2][NUM_BOOSTED_SCAT_PILOTS] = {
+	{1, 3, 43, 45},
+	{1, 3, 49, 51}
+};
+
+
+/* Dummy cells for the MSC ****************************************************/
+/* Already normalized */
+const _COMPLEX cDummyCells64QAM[2] = {
+  _COMPLEX(0.1543033499f,  0.1543033499f),
+  _COMPLEX(0.1543033499f, -0.1543033499f)
+};
+
+const _COMPLEX cDummyCells16QAM[2] = {
+  _COMPLEX(0.3162277660f,  0.3162277660f),
+  _COMPLEX(0.3162277660f, -0.3162277660f)
+};
+
+const int iScatPilGainRobModE[2][NUM_BOOSTED_SCAT_PILOTS] = {
+	{1,29, 0, 0},
+	{1,31, 0, 0}
+};
+
+#endif // !defined(TABLE_CAR_MAP_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableCarrier.h b/qsstv/drmtx/common/tables/TableCarrier.h
new file mode 100644
index 0000000..30b7182
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableCarrier.h
@@ -0,0 +1,97 @@
+
+/* Definitions ****************************************************************/
+/* FAC ************************************************************************/
+#define NUM_FAC_CELLS			45 
+
+/* FAC positions. The two numbers are {symbol no, carrier no} */
+
+
+const int iTableFACRobModA[NUM_FAC_CELLS][2] = {  
+  { 1, 10},	{ 1, 22}, { 1, 30}, { 1, 50},
+  { 2, 14},	{ 2, 26}, { 2, 34},
+  { 3, 18},	{ 3, 30}, { 3, 38},
+  { 4, 22},	{ 4, 34}, { 4, 42},
+  { 5, 18},	{ 5, 26},	{ 5, 38}, { 5, 46},
+  { 6, 22},	{ 6, 30},	{ 6, 42}, { 6, 50},
+  { 7, 26},	{ 7, 34},	{ 7, 46},
+  { 8, 10},	{ 8, 30},	{ 8, 38},	{ 8, 50},
+  { 9, 14},	{ 9, 34},	{ 9, 42},
+  {10, 18},	{10, 38},	{10, 46},
+  {11, 10},	{11, 22},	{11, 42},	{11, 50},
+  {12, 14},	{12, 26},	{12, 46},
+  {13, 18},	{13, 30},
+  {14, 22},	{14, 34},
+};
+
+const int iTableFACRobModB[NUM_FAC_CELLS][2] = {  
+  { 0, 21},
+  { 1, 11},	{ 1, 23}, { 1, 35},
+  { 2, 13},	{ 2, 25}, { 2, 37},
+  { 3, 15},	{ 3, 27}, { 3, 39},
+  { 4,  5},	{ 4, 17},	{ 4, 29}, { 4, 41},
+  { 5,  7},	{ 5, 19},	{ 5, 31},
+  { 6,  9},	{ 6, 21},	{ 6, 33},
+  { 7, 11},	{ 7, 23},	{ 7, 35},
+  { 8, 13},	{ 8, 25},	{ 8, 37},
+  { 9, 15},	{ 9, 27},	{ 9, 39},
+  {10,  5},	{10, 17},	{10, 29},	{10, 41},
+  {11,  7},	{11, 19},	{11, 31},
+  {12,  9},	{12, 21},	{12, 33},
+  {13, 11},	{13, 23},	{13, 35},
+  {14, 13},	{14, 25},	{14, 37},
+};
+
+const int iTableFACRobModE[NUM_FAC_CELLS][2] = {  
+  { 1, 7},				{ 1,23},
+  { 2, 8}, 	{ 2,16},	{ 2,24},
+  { 3, 9},	{ 3,17},
+  { 4,10},	{ 4,18},
+  { 5,11},	{ 5,19},
+  { 6, 4},				{ 6,12},
+  { 7,13},	{ 7,21},
+  { 8, 6},				{ 8,14},	{ 8,22},
+  { 9, 7},							{ 9,23},
+  {10, 8},				{10,16},	{10,24},
+  {11, 9},	{11,13},	{11,17},
+  {12,10},				{12,18},
+  {13,11},				{13,19},
+  {14, 4},	{14,12},	{14,16},
+  {15,13},				{15,21},
+  {16, 6},	{16,14},				{16,22},
+  {17, 7},							{17,23},
+  {18, 8},	{18,16},				{18,24},
+  {19, 9},	{19,17},
+};
+
+/* Frequency pilots ***********************************************************/
+#define NUM_FREQ_PILOTS			3
+const int iTableFreqPilRobModA[NUM_FREQ_PILOTS][2] = {
+  { 9, 205},
+  {27, 836},
+  {36, 215}
+};
+
+const int iTableFreqPilRobModB[NUM_FREQ_PILOTS][2] = {
+  { 8, 331},
+  {24, 651},
+  {32, 555}
+};
+
+const int iTableFreqPilRobModE[NUM_FREQ_PILOTS][2] = {
+  { 5, 788},
+  {15, 1014},
+  {20, 332}
+};
+
+/* Spectrum occupancy, carrier numbers for each mode **************************/
+const int iTableCarrierKmin[2][3] = {
+  {2, 1, 1},
+  {2, 1, 1}
+};
+
+const int iTableCarrierKmax[2][3] = {
+  {54, 45, 29},
+  {58, 51, 31}
+};
+
+
diff --git a/qsstv/drmtx/common/tables/TableDRMGlobal.h b/qsstv/drmtx/common/tables/TableDRMGlobal.h
new file mode 100644
index 0000000..1e53ceb
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableDRMGlobal.h
@@ -0,0 +1,88 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBo
+ *
+ * Description:
+ *	DRM global definitions
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(TABLE_DRM_GLOB_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
+#define TABLE_DRM_GLOB_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_
+
+
+/* Definitions ****************************************************************/
+/* We define a "virtual" intermedia frequency for the DC carrier in the range
+   of the FFT-size. This IF is independent of the "real" IF defined by the 
+   frequency estimation acquisition unit. Here, we are constrained to certain
+   numbers to get continuous signals like they are defined in the DRM-standard,
+   i.e. the frequency pilots which have to be continuous. Our IF must be a
+   multiple of 1500 Hz and must also be chosen so that the largest mode (20 kHz)
+   must fit into the range of the FFT-size. Therefore 6000 Hz was chosen */
+#define VIRTUAL_INTERMED_FREQ			6000	// Hz
+
+#define SOUNDCRD_SAMPLE_RATE			48000	// Hz  pa0mbo was 48000
+
+#define AUD_DEC_TRANSFORM_LENGTH             960
+
+/* DRM parameters */
+#define NUM_FRAMES_IN_SUPERFRAME		3
+
+#define RMA_FFT_SIZE_N					1152	// RMB: Robustness Mode A
+#define RMA_NUM_SYM_PER_FRAME			15
+#define RMA_ENUM_TG_TU					1
+#define RMA_DENOM_TG_TU					9
+
+#define RMB_FFT_SIZE_N					1024	// RMA: Robustness Mode B
+#define RMB_NUM_SYM_PER_FRAME			15
+#define RMB_ENUM_TG_TU					1
+#define RMB_DENOM_TG_TU					4
+
+#define RME_FFT_SIZE_N					640		// RME: Robustness Mode E
+#define RME_NUM_SYM_PER_FRAME			20
+#define RME_ENUM_TG_TU					1
+#define RME_DENOM_TG_TU					2
+
+#define MAX_NUM_STREAMS					4
+#define MAX_NUM_SERVICES				4
+
+#define NUM_ROBUSTNESS_MODES			3
+
+
+/* Service ID has 24 bits, define a number which cannot be an ID and fits into
+   the 32 bits of the length of the variable (e.g.: 1 << 25) */
+#define SERV_ID_NOT_USED				(1 << 25)
+
+/* Define a stream ID which is not valid to show that this service is not
+   attached to a stream */
+#define STREAM_ID_NOT_USED				(MAX_NUM_STREAMS + 1)
+
+
+/* Audio stream definitions ------------------------------------------------- */
+/* The text message (when present) shall occupy the last four bytes of the 
+   lower protected part of each logical frame carrying an audio stream 
+   (6.5.1) */
+#define NUM_BYTES_TEXT_MESS_IN_AUD_STR	4
+
+#endif // !defined(TABLE_DRM_GLOB_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableFAC.cpp b/qsstv/drmtx/common/tables/TableFAC.cpp
new file mode 100644
index 0000000..8f9f8a9
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableFAC.cpp
@@ -0,0 +1,992 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Tables for FAC
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "TableFAC.h"
+
+/* Definitions ****************************************************************/
+/* ETSI ES201980V2.1.1: page 115, 7.5.3: ...FAC shall use 4-QAM mapping. A
+   fixed code rate shall be applied...R_all=0.6...
+   6 tailbits are used for the encoder to get in zero state ->
+   65 [number of cells] * 2 [4-QAM] * 0.6 [code-rate] - 6 [tailbits] = 72 */
+// #define NUM_FAC_BITS_PER_BLOCK			72
+
+/* iTableNumOfServices[a][b]
+   a: Number of audio services
+   b: Number of data services 
+   (6.3.4) */
+const int iTableNumOfServices[5][5] = {
+	/* -> Data */
+	{-1,  1,  2,  3, 15},
+	{ 4,  5,  6,  7, -1},
+	{ 8,  9, 10, -1, -1},
+	{12, 13, -1, -1, -1},
+	{ 0, -1, -1, -1, -1}
+};
+
+/* Language code */
+#define LEN_TABLE_LANGUAGE_CODE			16
+
+const string strTableLanguageCode[LEN_TABLE_LANGUAGE_CODE] = {
+	"No language specified", 
+	"Arabic", 
+	"Bengali", 
+	"Chinese (Mandarin)", 
+	"Dutch", 
+	"English", 
+	"French", 
+	"German", 
+	"Hindi", 
+	"Japanese", 
+	"Javanese", 
+	"Korean", 
+	"Portuguese", 
+	"Russian", 
+	"Spanish", 
+	"Other language"
+};
+
+/* Programme Type codes */
+#define LEN_TABLE_PROG_TYPE_CODE_TOT	32
+#define LEN_TABLE_PROG_TYPE_CODE		30
+
+const string strTableProgTypCod[LEN_TABLE_PROG_TYPE_CODE_TOT] = {
+	"No programme type",
+	"News",
+	"Current Affairs",
+	"Information",
+	"Sport",
+	"Education",
+	"Drama",
+	"Culture",
+	"Science",
+	"Varied",
+	"Pop Music",
+	"Rock Music",
+	"Easy Listening Music",
+	"Light Classical",
+	"Serious Classical",
+	"Other Music",
+	"Weather/meteorology",
+	"Finance/Business",
+	"Children's programmes",
+	"Social Affairs",
+	"Religion",
+	"Phone In",
+	"Travel",
+	"Leisure",
+	"Jazz Music",
+	"Country Music",
+	"National Music",
+	"Oldies Music",
+	"Folk Music",
+	"Documentary",
+	"Not used",
+	"Not used"
+};
+
+
+/* Country code table according to ISO 3166 */
+
+const struct elCountry TableCountryCode[LEN_TABLE_COUNTRY_CODE] = {
+	{"af", "Afghanistan"},
+	{"ax", "Aland Islands"},
+	{"al", "Albania"},
+	{"dz", "Algeria"},
+	{"as", "American Samoa"},
+	{"ad", "Andorra"},
+	{"ao", "Angola"},
+	{"ai", "Anguilla"},
+	{"aq", "Antarctica"},
+	{"ag", "Antigua and barbuda"},
+	{"ar", "Argentina"},
+	{"am", "Armenia"},
+	{"aw", "Aruba"},
+	{"au", "Australia"},
+	{"at", "Austria"},
+	{"az", "Azerbaijan"},
+	{"bs", "Bahamas"},
+	{"bh", "Bahrain"},
+	{"bd", "Bangladesh"},
+	{"bb", "Barbados"},
+	{"by", "Belarus"},
+	{"be", "Belgium"},
+	{"bz", "Belize"},
+	{"bj", "Benin"},
+	{"bm", "Bermuda"},
+	{"bt", "Bhutan"},
+	{"bo", "Bolivia"},
+	{"ba", "Bosnia and Herzegovina"},
+	{"bw", "Botswana"},
+	{"bv", "Bouvet Island"},
+	{"br", "Brazil"},
+	{"io", "British Indian Ocean Ter."},
+	{"bn", "Brunei Darussalam"},
+	{"bg", "Bulgaria"},
+	{"bf", "Burkina Faso"},
+	{"bi", "Burundi"},
+	{"kh", "Cambodia"},
+	{"cm", "Cameroon"},
+	{"ca", "Canada"},
+	{"cv", "Cape Verde"},
+	{"ky", "Cayman Islands"},
+	{"cf", "Central African Republic"},
+	{"td", "Chad"},
+	{"cl", "Chile"},
+	{"cn", "China"},
+	{"cx", "Christmas Island"},
+	{"cc", "Cocos (Keeling) Islands"},
+	{"co", "Colombia"},
+	{"km", "Comoros"},
+	{"cg", "Congo Democratic Rep."},
+	{"cd", "Congo"},
+	{"ck", "Cook Islands"},
+	{"cr", "Costa Rica"},
+	{"ci", "C�te d'Ivoire"},
+	{"hr", "Croatia"},
+	{"cu", "Cuba"},
+	{"cy", "Cyprus"},
+	{"cz", "Czech Republic"},
+	{"dk", "Denmark"},
+	{"dj", "Djibouti"},
+	{"dm", "Dominica"},
+	{"do", "Dominican Republic"},
+	{"ec", "Ecuador"},
+	{"eg", "Egypt"},
+	{"sv", "El Salvador"},
+	{"gq", "Equatorial Guinea"},
+	{"er", "Eritrea"},
+	{"ee", "Estonia"},
+	{"et", "Ethiopia"},
+	{"fk", "Falkland Islands"},
+	{"fo", "Faroe Islands"},
+	{"fj", "Fiji"},
+	{"fi", "Finland"},
+	{"fr", "France"},
+	{"gf", "French Guiana"},
+	{"pf", "French Polynesia"},
+	{"tf", "French Southern Ter."},
+	{"ga", "Gabon"},
+	{"gm", "Gambia"},
+	{"ge", "Georgia"},
+	{"de", "Germany"},
+	{"gh", "Ghana"},
+	{"gi", "Gibraltar"},
+	{"gr", "Greece"},
+	{"gl", "Greenland"},
+	{"gd", "Grenada"},
+	{"gp", "Guadeloupe"},
+	{"gu", "Guam"},
+	{"gt", "Guatemala"},
+	{"gg", "Guernsey"},
+	{"gn", "Guinea"},
+	{"gw", "Guinea-Bissau"},
+	{"gy", "Guyana"},
+	{"ht", "Haiti"},
+	{"hm", "Heard Is. Mcdonald Is."},
+	{"va", "Vatican City State"},
+	{"hn", "Honduras"},
+	{"hk", "Hong Kong"},
+	{"hu", "Hungary"},
+	{"is", "Iceland"},
+	{"in", "India"},
+	{"id", "Indonesia"},
+	{"ir", "Iran"},
+	{"iq", "Iraq"},
+	{"im", "Isle of Man"},
+	{"ie", "Ireland"},
+	{"il", "Israel"},
+	{"it", "Italy"},
+	{"jm", "Jamaica"},
+	{"jp", "Japan"},
+	{"je", "Jersey"},
+	{"jo", "Jordan"},
+	{"kz", "Kazakhstan"},
+	{"ke", "Kenya"},
+	{"ki", "Kiribati"},
+	{"kp", "Korea Democratic Rep."},
+	{"kr", "Korea, Republic of"},
+	{"kw", "Kuwait"},
+	{"kg", "Kyrgyzstan"},
+	{"la", "Lao People's Democratic Rep."},
+	{"lv", "Latvia"},
+	{"lb", "Lebanon"},
+	{"ls", "Lesotho"},
+	{"lr", "Liberia"},
+	{"ly", "Libyan Arab Jamahiriya"},
+	{"li", "Liechtenstein"},
+	{"lt", "Lithuania"},
+	{"lu", "Luxembourg"},
+	{"mo", "Macao"},
+	{"mk", "Macedonia"},
+	{"mg", "Madagascar"},
+	{"mw", "Malawi"},
+	{"my", "Malaysia"},
+	{"mv", "Maldives"},
+	{"ml", "Mali"},
+	{"mt", "Malta"},
+	{"mh", "Marshall Islands"},
+	{"mq", "Martinique"},
+	{"mr", "Mauritania"},
+	{"mu", "Mauritius"},
+	{"yt", "Mayotte"},
+	{"mx", "Mexico"},
+	{"fm", "Micronesia"},
+	{"md", "Moldova"},
+	{"mc", "Monaco"},
+	{"mn", "Mongolia"},
+	{"me", "Montenegro"},
+	{"ms", "Montserrat"},
+	{"ma", "Morocco"},
+	{"mz", "Mozambique"},
+	{"mm", "Myanmar"},
+	{"na", "Namibia"},
+	{"nr", "Nauru"},
+	{"np", "Nepal"},
+	{"nl", "Netherlands"},
+	{"an", "Netherlands Antilles"},
+	{"nc", "New Caledonia"},
+	{"nz", "New Zealand"},
+	{"ni", "Nicaragua"},
+	{"ne", "Niger"},
+	{"ng", "Nigeria"},
+	{"nu", "Niue"},
+	{"nf", "Norfolk Island"},
+	{"mp", "Northern Mariana Is."},
+	{"no", "Norway"},
+	{"om", "Oman"},
+	{"pk", "Pakistan"},
+	{"pw", "Palau"},
+	{"ps", "Palestinian Territory"},
+	{"pa", "Panama"},
+	{"pg", "Papua New Guinea"},
+	{"py", "Paraguay"},
+	{"pe", "Peru"},
+	{"ph", "Philippines"},
+	{"pn", "Pitcairn"},
+	{"pl", "Poland"},
+	{"pt", "Portugal"},
+	{"pr", "Puerto Rico"},
+	{"qa", "Qatar"},
+	{"re", "R�union"},
+	{"ro", "Romania"},
+	{"ru", "Russian Federation"},
+	{"rw", "Rwanda"},
+	{"sh", "Saint Helena"},
+	{"kn", "Saint Kitts and Nevis"},
+	{"lc", "Saint Lucia"},
+	{"pm", "Saint Pierre and Miquelon"},
+	{"vc", "Saint Vincent and the Grenadines"},
+	{"ws", "Samoa"},
+	{"sm", "San Marino"},
+	{"st", "Sao Tome and Principe"},
+	{"sa", "Saudi arabia"},
+	{"sn", "Senegal"},
+	{"rs", "Serbia"},
+	{"sc", "Seychelles"},
+	{"sl", "Sierra Leone"},
+	{"sg", "Singapore"},
+	{"sk", "Slovakia"},
+	{"si", "Slovenia"},
+	{"sb", "Solomon Islands"},
+	{"so", "Somalia"},
+	{"za", "South Africa"},
+	{"gs", "South Georgia South Sandwich Is."},
+	{"es", "Spain"},
+	{"lk", "Sri Lanka"},
+	{"sd", "Sudan"},
+	{"sr", "Suriname"},
+	{"sj", "Svalbard and Jan Mayen"},
+	{"sz", "Swaziland"},
+	{"se", "Sweden"},
+	{"ch", "Switzerland"},
+	{"sy", "Syrian Arab Republic"},
+	{"tw", "Taiwan"},
+	{"tj", "Tajikistan"},
+	{"tz", "Tanzania"},
+	{"th", "Thailand"},
+	{"tl", "Timor-Leste"},
+	{"tg", "Togo"},
+	{"tk", "Tokelau"},
+	{"to", "Tonga"},
+	{"tt", "Trinidad and Tobago"},
+	{"tn", "Tunisia"},
+	{"tr", "Turkey"},
+	{"tm", "Turkmenistan"},
+	{"tc", "Turks and Caicos Islands"},
+	{"tv", "Tuvalu"},
+	{"ug", "Uganda"},
+	{"ua", "Ukraine"},
+	{"ae", "United Arab Emirates"},
+	{"gb", "United Kingdom"},
+	{"us", "United States"},
+	{"um", "United States Is."},
+	{"uy", "Uruguay"},
+	{"uz", "Uzbekistan"},
+	{"vu", "Vanuatu"},
+	{"ve", "Venezuela"},
+	{"vn", "Vietnam"},
+	{"vg", "Virgin Islands, British"},
+	{"vi", "Virgin Islands, U.S."},
+	{"wf", "Wallis and Futuna"},
+	{"eh", "Western Sahara"},
+	{"ye", "Yemen"},
+	{"zm", "Zambia"},
+	{"zw", "Zimbabwe"}
+};
+
+/* Get country name from ISO 3166 A2 */
+
+string GetISOCountryName(const string strA2)
+{
+	for (int i = 0; i < LEN_TABLE_COUNTRY_CODE; i++)
+	{
+		if (!strA2.compare(TableCountryCode[i].strcode))
+			return TableCountryCode[i].strDesc;
+	}
+
+	return "";
+}
+
+
+/* Language code table according to ISO 639-2 */
+/* All Alpha 3 codes: "bibliographic" (B code) and "terminological" (T code) */
+
+const struct elLanguage TableISOLanguageCode[LEN_TABLE_ISO_LANGUAGE_CODE] = {
+     {"aar", "Afar"},
+     {"abk", "Abkhazian"},
+     {"ace", "Achinese"},
+     {"ach", "Acoli"},
+     {"ada", "Adangme"},
+     {"ady", "Adyghe; Adygei"},
+     {"afa", "Afro-Asiatic (Other)"},
+     {"afh", "Afrihili"},
+     {"afr", "Afrikaans"},
+     {"ain", "Ainu"},
+     {"aka", "Akan"},
+     {"akk", "Akkadian"},
+     {"alb", "Albanian"},
+     {"sqi", "Albanian"},
+     {"ale", "Aleut"},
+     {"alg", "Algonquian languages"},
+     {"alt", "Southern Altai"},
+     {"amh", "Amharic"},
+     {"ang", "English, Old (ca.450-1100)"},
+     {"anp", "Angika"},
+     {"apa", "Apache languages"},
+     {"ara", "Arabic"},
+     {"arc", "Aramaic"},
+     {"arg", "Aragonese"},
+     {"arm", "Armenian"},
+     {"hye", "Armenian"},
+     {"arn", "Araucanian"},
+     {"arp", "Arapaho"},
+     {"art", "Artificial (Other)"},
+     {"arw", "Arawak"},
+     {"asm", "Assamese"},
+     {"ast", "Asturian; Bable"},
+     {"ath", "Athapascan languages"},
+     {"aus", "Australian languages"},
+     {"ava", "Avaric"},
+     {"ave", "Avestan"},
+     {"awa", "Awadhi"},
+     {"aym", "Aymara"},
+     {"aze", "Azerbaijani"},
+     {"bad", "Banda"},
+     {"bai", "Bamileke languages"},
+     {"bak", "Bashkir"},
+     {"bal", "Baluchi"},
+     {"bam", "Bambara"},
+     {"ban", "Balinese"},
+     {"baq", "Basque"},
+     {"eus", "Basque"},
+     {"bas", "Basa"},
+     {"bat", "Baltic (Other)"},
+     {"bej", "Beja"},
+     {"bel", "Belarusian"},
+     {"bem", "Bemba"},
+     {"ben", "Bengali"},
+     {"ber", "Berber (Other)"},
+     {"bho", "Bhojpuri"},
+     {"bih", "Bihari"},
+     {"bik", "Bikol"},
+     {"bin", "Bini"},
+     {"bis", "Bislama"},
+     {"bla", "Siksika"},
+     {"bnt", "Bantu (Other)"},
+     {"bos", "Bosnian"},
+     {"bra", "Braj"},
+     {"bre", "Breton"},
+     {"btk", "Batak (Indonesia)"},
+     {"bua", "Buriat"},
+     {"bug", "Buginese"},
+     {"bul", "Bulgarian"},
+     {"bur", "Burmese"},
+     {"mya", "Burmese"},
+     {"byn", "Blin; Bilin"},
+     {"cad", "Caddo"},
+     {"cai", "Central American Indian (Other)"},
+     {"car", "Carib"},
+     {"cat", "Catalan; Valencian"},
+     {"cau", "Caucasian (Other)"},
+     {"ceb", "Cebuano"},
+     {"cel", "Celtic (Other)"},
+     {"cha", "Chamorro"},
+     {"chb", "Chibcha"},
+     {"che", "Chechen"},
+     {"chg", "Chagatai"},
+     {"chi", "Chinese"},
+     {"zho", "Chinese"},
+     {"chk", "Chuukese"},
+     {"chm", "Mari"},
+     {"chn", "Chinook jargon"},
+     {"cho", "Choctaw"},
+     {"chp", "Chipewyan"},
+     {"chr", "Cherokee"},
+     {"chu", "Church Slavic - Slavonic..."},
+     {"chv", "Chuvash"},
+     {"chy", "Cheyenne"},
+     {"cmc", "Chamic languages"},
+     {"cop", "Coptic"},
+     {"cor", "Cornish"},
+     {"cos", "Corsican"},
+     {"cpe", "Creoles and pidgins, English based"},
+     {"cpf", "Creoles and pidgins, French-based"},
+     {"cpp", "Creoles and pidgins, Portuguese-based"},
+     {"cre", "Cree"},
+     {"crh", "Crimean Tatar; Crimean Turkish"},
+     {"crp", "Creoles and pidgins (Other)"},
+     {"csb", "Kashubian"},
+     {"cus", "Cushitic (Other)"},
+     {"cze", "Czech"},
+     {"ces", "Czech"},
+     {"dak", "Dakota"},
+     {"dan", "Danish"},
+     {"dar", "Dargwa"},
+     {"day", "Dayak"},
+     {"del", "Delaware"},
+     {"den", "Slave (Athapascan)"},
+     {"dgr", "Dogrib"},
+     {"din", "Dinka"},
+     {"div", "Divehi; Dhivehi; Maldivian"},
+     {"doi", "Dogri"},
+     {"dra", "Dravidian (Other)"},
+     {"dsb", "Lower Sorbian"},
+     {"dua", "Duala"},
+     {"dum", "Dutch, Middle (ca.1050-1350)"},
+     {"dut", "Dutch; Flemish"},
+     {"nld", "Dutch; Flemish"},
+     {"dyu", "Dyula"},
+     {"dzo", "Dzongkha"},
+     {"efi", "Efik"},
+     {"egy", "Egyptian (Ancient)"},
+     {"eka", "Ekajuk"},
+     {"elx", "Elamite"},
+     {"eng", "English"},
+     {"enm", "English, Middle (1100-1500)"},
+     {"epo", "Esperanto"},
+     {"est", "Estonian"},
+     {"ewe", "Ewe"},
+     {"ewo", "Ewondo"},
+     {"fan", "Fang"},
+     {"fao", "Faroese"},
+     {"fat", "Fanti"},
+     {"fij", "Fijian"},
+     {"fil", "Filipino; Pilipino"},
+     {"fin", "Finnish"},
+     {"fiu", "Finno-Ugrian (Other)"},
+     {"fon", "Fon"},
+     {"fre", "French"},
+     {"fra", "French"},
+     {"frm", "French, Middle (ca.1400-1600)"},
+     {"fro", "French, Old (842-ca.1400)"},
+     {"frr", "Northern Frisian"},
+     {"frs", "Eastern Frisian"},
+     {"fry", "Western Frisian"},
+     {"ful", "Fulah"},
+     {"fur", "Friulian"},
+     {"gaa", "Ga"},
+     {"gay", "Gayo"},
+     {"gba", "Gbaya"},
+     {"gem", "Germanic (Other)"},
+     {"geo", "Georgian"},
+     {"kat", "Georgian"},
+     {"ger", "German"},
+     {"deu", "German"},
+     {"gez", "Geez"},
+     {"gil", "Gilbertese"},
+     {"gla", "Gaelic; Scottish Gaelic"},
+     {"gle", "Irish"},
+     {"glg", "Galician"},
+     {"glv", "Manx"},
+     {"gmh", "German, Middle High (ca.1050-1500)"},
+     {"goh", "German, Old High (ca.750-1050)"},
+     {"gon", "Gondi"},
+     {"gor", "Gorontalo"},
+     {"got", "Gothic"},
+     {"grb", "Grebo"},
+     {"grc", "Greek, Ancient (to 1453)"},
+     {"gre", "Greek, Modern (1453-)"},
+     {"ell", "Greek, Modern (1453-)"},
+     {"grn", "Guarani"},
+     {"gsw", "Alemani; Swiss German"},
+     {"guj", "Gujarati"},
+     {"gwi", "Gwich�in"},
+     {"hai", "Haida"},
+     {"hat", "Haitian; Haitian Creole"},
+     {"hau", "Hausa"},
+     {"haw", "Hawaiian"},
+     {"heb", "Hebrew"},
+     {"her", "Herero"},
+     {"hil", "Hiligaynon"},
+     {"him", "Himachali"},
+     {"hin", "Hindi"},
+     {"hit", "Hittite"},
+     {"hmn", "Hmong"},
+     {"hmo", "Hiri Motu"},
+     {"hsb", "Upper Sorbian"},
+     {"hun", "Hungarian"},
+     {"hup", "Hupa"},
+     {"iba", "Iban"},
+     {"ibo", "Igbo"},
+     {"ice", "Icelandic"},
+     {"isl", "Icelandic"},
+     {"ido", "Ido"},
+     {"iii", "Sichuan Yi"},
+     {"ijo", "Ijo"},
+     {"iku", "Inuktitut"},
+     {"ile", "Interlingue"},
+     {"ilo", "Iloko"},
+     {"ina", "Interlingua"},
+     {"inc", "Indic (Other)"},
+     {"ind", "Indonesian"},
+     {"ine", "Indo-European (Other)"},
+     {"inh", "Ingush"},
+     {"ipk", "Inupiaq"},
+     {"ira", "Iranian (Other)"},
+     {"iro", "Iroquoian languages"},
+     {"ita", "Italian"},
+     {"jav", "Javanese"},
+     {"jbo", "Lojban"},
+     {"jpn", "Japanese"},
+     {"jpr", "Judeo-Persian"},
+     {"jrb", "Judeo-Arabic"},
+     {"kaa", "Kara-Kalpak"},
+     {"kab", "Kabyle"},
+     {"kac", "Kachin"},
+     {"kal", "Kalaallisut; Greenlandic"},
+     {"kam", "Kamba"},
+     {"kan", "Kannada"},
+     {"kar", "Karen"},
+     {"kas", "Kashmiri"},
+     {"kau", "Kanuri"},
+     {"kaw", "Kawi"},
+     {"kaz", "Kazakh"},
+     {"kbd", "Kabardian"},
+     {"kha", "Khasi"},
+     {"khi", "Khoisan (Other)"},
+     {"khm", "Khmer"},
+     {"kho", "Khotanese"},
+     {"kik", "Kikuyu; Gikuyu"},
+     {"kin", "Kinyarwanda"},
+     {"kir", "Kirghiz"},
+     {"kmb", "Kimbundu"},
+     {"kok", "Konkani"},
+     {"kom", "Komi"},
+     {"kon", "Kongo"},
+     {"kor", "Korean"},
+     {"kos", "Kosraean"},
+     {"kpe", "Kpelle"},
+     {"krc", "Karachay-Balkar"},
+     {"krl", "Karelian"},
+     {"kro", "Kru"},
+     {"kru", "Kurukh"},
+     {"kua", "Kuanyama; Kwanyama"},
+     {"kum", "Kumyk"},
+     {"kur", "Kurdish"},
+     {"kut", "Kutenai"},
+     {"lad", "Ladino"},
+     {"lah", "Lahnda"},
+     {"lam", "Lamba"},
+     {"lao", "Lao"},
+     {"lat", "Latin"},
+     {"lav", "Latvian"},
+     {"lez", "Lezghian"},
+     {"lim", "Limburgan; Limburger; Limburgish"},
+     {"lin", "Lingala"},
+     {"lit", "Lithuanian"},
+     {"lol", "Mongo"},
+     {"loz", "Lozi"},
+     {"ltz", "Luxembourgish; Letzeburgesch"},
+     {"lua", "Luba-Lulua"},
+     {"lub", "Luba-Katanga"},
+     {"lug", "Ganda"},
+     {"lui", "Luiseno"},
+     {"lun", "Lunda"},
+     {"luo", "Luo (Kenya and Tanzania)"},
+     {"lus", "lushai"},
+     {"mac", "Macedonian"},
+     {"mkd", "Macedonian"},
+     {"mad", "Madurese"},
+     {"mag", "Magahi"},
+     {"mah", "Marshallese"},
+     {"mai", "Maithili"},
+     {"mak", "Makasar"},
+     {"mal", "Malayalam"},
+     {"man", "Mandingo"},
+     {"mao", "Maori"},
+     {"mri", "Maori"},
+     {"map", "Austronesian (Other)"},
+     {"mar", "Marathi"},
+     {"mas", "Masai"},
+     {"may", "Malay"},
+     {"msa", "Malay"},
+     {"mdf", "Moksha"},
+     {"mdr", "Mandar"},
+     {"men", "Mende"},
+     {"mga", "Irish, Middle (900-1200)"},
+     {"mic", "Mi'kmaq; Micmac"},
+     {"min", "Minangkabau"},
+     {"mis", "Miscellaneous languages"},
+     {"mkh", "Mon-Khmer (Other)"},
+     {"mlg", "Malagasy"},
+     {"mlt", "Maltese"},
+     {"mnc", "Manchu"},
+     {"mni", "Manipuri"},
+     {"mno", "Manobo languages"},
+     {"moh", "Mohawk"},
+     {"mol", "Moldavian"},
+     {"mon", "Mongolian"},
+     {"mos", "Mossi"},
+     {"mul", "Multiple languages"},
+     {"mun", "Munda languages"},
+     {"mus", "Creek"},
+     {"mwl", "Mirandese"},
+     {"mwr", "Marwari"},
+     {"myn", "Mayan languages"},
+     {"myv", "Erzya"},
+     {"nah", "Nahuatl"},
+     {"nai", "North American Indian"},
+     {"nap", "Neapolitan"},
+     {"nau", "Nauru"},
+     {"nav", "Navajo; Navaho"},
+     {"nbl", "Ndebele, South; South Ndebele"},
+     {"nde", "Ndebele, North; North Ndebele"},
+     {"ndo", "Ndonga"},
+     {"nds", "Low German; Low Saxon..."},
+     {"nep", "Nepali"},
+     {"new", "Nepal Bhasa; Newari"},
+     {"nia", "Nias"},
+     {"nic", "Niger-Kordofanian (Other)"},
+     {"niu", "Niuean"},
+     {"nno", "Norwegian Nynorsk"},
+     {"nob", "Norwegian Bokm�l"},
+     {"nog", "Nogai"},
+     {"non", "Norse, Old"},
+     {"nor", "Norwegian"},
+     {"nqo", "N'ko"},
+     {"nso", "Northern Sotho; Pedi; Sepedi"},
+     {"nub", "Nubian languages"},
+     {"nwc", "Classical Newari; Classical Nepal Bhasa"},
+     {"nya", "Chichewa; Chewa; Nyanja"},
+     {"nym", "Nyamwezi"},
+     {"nyn", "Nyankole"},
+     {"nyo", "Nyoro"},
+     {"nzi", "Nzima"},
+     {"oci", "Occitan (post 1500); Proven�al"},
+     {"oji", "Ojibwa"},
+     {"ori", "Oriya"},
+     {"orm", "Oromo"},
+     {"osa", "Osage"},
+     {"oss", "Ossetian; Ossetic"},
+     {"ota", "Turkish, Ottoman (1500-1928)"},
+     {"oto", "Otomian languages"},
+     {"paa", "Papuan (Other)"},
+     {"pag", "Pangasinan"},
+     {"pal", "Pahlavi"},
+     {"pam", "Pampanga"},
+     {"pan", "Panjabi; Punjabi"},
+     {"pap", "Papiamento"},
+     {"pau", "Palauan"},
+     {"peo", "Persian, Old (ca.600-400 B.C.)"},
+     {"per", "Persian"},
+     {"fas", "Persian"},
+     {"phi", "Philippine (Other)"},
+     {"phn", "Phoenician"},
+     {"pli", "Pali"},
+     {"pol", "Polish"},
+     {"pon", "Pohnpeian"},
+     {"por", "Portuguese"},
+     {"pra", "Prakrit languages"},
+     {"pro", "Proven�al, Old (to 1500)"},
+     {"pus", "Pushto"},
+     {"que", "Quechua"},
+     {"raj", "Rajasthani"},
+     {"rap", "Rapanui"},
+     {"rar", "Rarotongan"},
+     {"roa", "Romance (Other)"},
+     {"roh", "Raeto-Romance"},
+     {"rom", "Romany"},
+     {"rum", "Romanian"},
+     {"ron", "Romanian"},
+     {"run", "Rundi"},
+     {"rup", "Aromanian; Arumanian; Macedo-Romanian"},
+     {"rus", "Russian"},
+     {"sad", "Sandawe"},
+     {"sag", "Sango"},
+     {"sah", "Yakut"},
+     {"sai", "South American Indian (Other)"},
+     {"sal", "Salishan languages"},
+     {"sam", "Samaritan Aramaic"},
+     {"san", "Sanskrit"},
+     {"sas", "Sasak"},
+     {"sat", "Santali"},
+     {"scc", "Serbian"},
+     {"srp", "Serbian"},
+     {"scn", "Sicilian"},
+     {"sco", "Scots"},
+     {"scr", "Croatian"},
+     {"hrv", "Croatian"},
+     {"sel", "Selkup"},
+     {"sem", "Semitic (Other)"},
+     {"sga", "Irish, Old (to 900)"},
+     {"sgn", "Sign Languages"},
+     {"shn", "Shan"},
+     {"sid", "Sidamo"},
+     {"sin", "Sinhala; Sinhalese"},
+     {"sio", "Siouan languages"},
+     {"sit", "Sino-Tibetan (Other)"},
+     {"sla", "Slavic (Other)"},
+     {"slo", "Slovak"},
+     {"slk", "Slovak"},
+     {"slv", "Slovenian"},
+     {"sma", "Southern Sami"},
+     {"sme", "Northern Sami"},
+     {"smi", "Sami languages (Other)"},
+     {"smj", "Lule Sami"},
+     {"smn", "Inari Sami"},
+     {"smo", "Samoan"},
+     {"sms", "Skolt Sami"},
+     {"sna", "Shona"},
+     {"snd", "Sindhi"},
+     {"snk", "Soninke"},
+     {"sog", "Sogdian"},
+     {"som", "Somali"},
+     {"son", "Songhai"},
+     {"sot", "Sotho, Southern"},
+     {"spa", "Spanish; Castilian"},
+     {"srd", "Sardinian"},
+     {"srn", "Sranan Togo"},
+     {"srr", "Serer"},
+     {"ssa", "Nilo-Saharan (Other)"},
+     {"ssw", "Swati"},
+     {"suk", "Sukuma"},
+     {"sun", "Sundanese"},
+     {"sus", "Susu"},
+     {"sux", "Sumerian"},
+     {"swa", "Swahili"},
+     {"swe", "Swedish"},
+     {"syr", "Syriac"},
+     {"tah", "Tahitian"},
+     {"tai", "Tai (Other)"},
+     {"tam", "Tamil"},
+     {"tat", "Tatar"},
+     {"tel", "Telugu"},
+     {"tem", "Timne"},
+     {"ter", "Tereno"},
+     {"tet", "Tetum"},
+     {"tgk", "Tajik"},
+     {"tgl", "Tagalog"},
+     {"tha", "Thai"},
+     {"tib", "Tibetan"},
+     {"bod", "Tibetan"},
+     {"tig", "Tigre"},
+     {"tir", "Tigrinya"},
+     {"tiv", "Tiv"},
+     {"tkl", "Tokelau"},
+     {"tlh", "Klingon; tlhIngan-Hol"},
+     {"tli", "Tlingit"},
+     {"tmh", "Tamashek"},
+     {"tog", "Tonga (Nyasa)"},
+     {"ton", "Tonga (Tonga Islands)"},
+     {"tpi", "Tok Pisin"},
+     {"tsi", "Tsimshian"},
+     {"tsn", "Tswana"},
+     {"tso", "Tsonga"},
+     {"tuk", "Turkmen"},
+     {"tum", "Tumbuka"},
+     {"tup", "Tupi languages"},
+     {"tur", "Turkish"},
+     {"tut", "Altaic (Other)"},
+     {"tvl", "Tuvalu"},
+     {"twi", "Twi"},
+     {"tyv", "Tuvinian"},
+     {"udm", "Udmurt"},
+     {"uga", "Ugaritic"},
+     {"uig", "Uighur; Uyghur"},
+     {"ukr", "Ukrainian"},
+     {"umb", "Umbundu"},
+     {"und", "Undetermined"},
+     {"urd", "Urdu"},
+     {"uzb", "Uzbek"},
+     {"vai", "Vai"},
+     {"ven", "Venda"},
+     {"vie", "Vietnamese"},
+     {"vol", "Volap�k"},
+     {"vot", "Votic"},
+     {"wak", "Wakashan languages"},
+     {"wal", "Walamo"},
+     {"war", "Waray"},
+     {"was", "Washo"},
+     {"wel", "Welsh"},
+     {"cym", "Welsh"},
+     {"wen", "Sorbian languages"},
+     {"wln", "Walloon"},
+     {"wol", "Wolof"},
+     {"xal", "Kalmyk; Oirat"},
+     {"xho", "Xhosa"},
+     {"yao", "Yao"},
+     {"yap", "Yapese"},
+     {"yid", "Yiddish"},
+     {"yor", "Yoruba"},
+     {"ypk", "Yupik languages"},
+     {"zap", "Zapotec"},
+     {"zen", "Zenaga"},
+     {"zha", "Zhuang; Chuang"},
+     {"znd", "Zande"},
+     {"zul", "Zulu"},
+     {"zun", "Zuni"},
+     {"zxx", "No linguistic content"},
+     {"zza", "Zaza; Dimili; Dimli; Kirdki..."}
+};
+
+/* Get language name from ISO */
+
+string GetISOLanguageName(const string strA3)
+{
+	for (int i = 0; i < LEN_TABLE_ISO_LANGUAGE_CODE; i++)
+	{
+		if (!strA3.compare(TableISOLanguageCode[i].strISOCode))
+			return TableISOLanguageCode[i].strDesc;
+	}
+
+	return "";
+}
+
+
+/* CIRAF zones */
+
+const string strTableCIRAFzones[LEN_TABLE_CIRAF_ZONES] = {
+	"", /* 0 undefined */
+	"Alaska", /* 1 */
+	"west Canada", /* 2 */
+	"central Canada - west", /* 3 */
+	"central Canada - east, Baffin Island", /* 4 */
+	"Greenland", /* 5 */
+	"west USA", /* 6 */
+	"central USA", /* 7 */
+	"east USA", /* 8 */
+	"east Canada", /* 9 */
+	"Belize, Guatemala, Mexico", /* 10 */
+	"Caribbean, central America",  /* 11 */
+	"northwestern south America", /* 12 */
+	"northeast Brazil", /* 13 */
+	"southwestern south America", /* 14 */
+	"southeast Brazil",  /* 15 */
+	"south Argentina, south Chile, Falkland Islands", /* 16 */
+	"Iceland", /* 17 */
+	"Scandinavia", /* 18 */
+	"west Russia northwest", /* 19 */
+	"west Russia north", /* 20 */
+	"central Russia northwest", /* 21 */
+	"central Russia north", /* 22 */
+	"central Russia east", /* 23 */
+	"east Russia northwest", /* 24 */
+	"east Russia north", /* 25 */
+	"east Russia northeast", /* 26 */
+	"northwest Europe", /* 27 */
+	"central east south Europe", /* 28 */
+	"Baltics and west Russia", /* 29 */
+	"central Asia, west Russia southeast", /* 30 */
+	"central Russia southwest, east Kazakhstan, east Kyrgyzstan", /* 31 */
+	"central Russia south, west Mongolia", /* 32 */
+	"central Russia southeast, east Mongolia", /* 33 */
+	"east Russia southwest: Sakhalin, Sikhote Alin", /* 34 */
+	"east Russia east: Kamchatka", /* 35 */
+	"Azores, Canary Island, Madeira", /* 36 */
+	"southwest Europe, northwest Africa", /* 37 */
+	"Egypt, Libya", /* 38 */
+	"Middle East", /* 39 */
+	"Afghanistan, Iran", /* 40 */
+	"Bangladesh, Bhutan, India, Nepal, Pakistan", /* 41 */
+	"west China", /* 42 */
+	"central China", /* 43 */
+	"east China, Macao, Hong Kong, North Korea, South Korea, Taiwan", /* 44 */
+	"Japan", /* 45 */
+	"west Africa", /* 46 */
+	"west Sudan", /* 47 */
+	"Horn of Africa", /* 48 */
+	"Kampuchea, Laos, Myanmar, Vietnam", /* 49 */
+	"Philippines", /* 50 */
+	"Malaysia, Papua New Guinea, west Indonesia", /* 51 */
+	"Angola, Burundi, Congo, Gabon, Zaire", /* 52 */
+	"Madagascar, Malawi, Mozambique, Seychelles, Zambia, Zimbabwe", /* 53 */
+	"Malaysia, Singapore, west Indonesia", /* 54 */
+	"northeast Australia", /* 55 */
+	"Caledonia, Fiji/Vanuatu", /* 56 */
+	"Botswana, Lesotho, Namibia, Swaziland, South African Republic", /* 57 */
+	"west Australia", /* 58 */
+	"southeast Australia", /* 59 */
+	"New Zealand", /* 60 */
+	"Hawaii", /* 61 */
+	"Phoenix Islands, Samoa", /* 62 */
+	"Cook Islands, Polynesia", /* 63 */
+	"Guam/Palau, Saipan", /* 64 */
+	"Kiribati, Marshall", /* 65 */
+	"central Atlantic - south: Ascension, St. Helena", /* 66 */
+	"Antarctica", /* 67 */
+	"southwest Indian Ocean: Kerguelen", /* 68 */
+	"Antarctica", /* 69 */
+	"Antarctica", /* 70 */
+	"Antarctica", /* 71 */
+	"Antarctica", /* 72 */
+	"Antarctica", /* 73 */
+	"South Pole", /* 74 */
+	"North Pole", /* 75 */
+	"northeast Pacific", /* 76 */
+	"central Pacific - northeast", /* 77 */
+	"central Pacific - southeast", /* 78 */
+	"central Indian Ocean", /* 79 */
+	"northern Atlantic", /* 80 */
+	"central Atlantic", /* 81 */
+	"northwest Pacific", /* 82 */
+	"south Pacific", /* 83 */
+	"south Atlantic", /* 84 */
+	"southeast Indian Ocean" /* 85 */
+};
diff --git a/qsstv/drmtx/common/tables/TableFAC.h b/qsstv/drmtx/common/tables/TableFAC.h
new file mode 100644
index 0000000..d0e2279
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableFAC.h
@@ -0,0 +1,102 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use  Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Tables for FAC
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(TABLE_FAC_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_)
+#define TABLE_FAC_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_
+
+#include <string>
+#include "../GlobalDefinitions.h"
+
+/* Definitions ****************************************************************/
+/* ETSI ES201980V2.1.1: page 115, 7.5.3: ...FAC shall use 4-QAM mapping. A
+   fixed code rate shall be applied...R_all=0.6...
+   6 tailbits are used for the encoder to get in zero state ->
+   65 [number of cells] * 2 [4-QAM] * 0.6 [code-rate] - 6 [tailbits] = 72 */
+#define NUM_FAC_BITS_PER_BLOCK			48
+
+/* iTableNumOfServices[a][b]
+   a: Number of audio services
+   b: Number of data services 
+   (6.3.4) */
+extern const int iTableNumOfServices[5][5];
+
+/* Language code */
+#define LEN_TABLE_LANGUAGE_CODE			16
+
+extern const string strTableLanguageCode[LEN_TABLE_LANGUAGE_CODE];
+
+/* Programme Type codes */
+#define LEN_TABLE_PROG_TYPE_CODE_TOT	32
+#define LEN_TABLE_PROG_TYPE_CODE		30
+
+extern const string strTableProgTypCod[LEN_TABLE_PROG_TYPE_CODE_TOT];
+
+/* Country code table according to ISO 3166 */
+
+#define LEN_TABLE_COUNTRY_CODE			244
+
+#define LEN_COUNTRY_CODE				2
+#define MAX_LEN_DESC_COUNTRY_CODE		44
+
+struct elCountry {
+	char	strcode [LEN_COUNTRY_CODE+1];
+	char	strDesc [MAX_LEN_DESC_COUNTRY_CODE+1];
+	};
+ 
+extern const struct elCountry TableCountryCode[LEN_TABLE_COUNTRY_CODE];
+
+/* Get country name from ISO 3166 A2 */
+
+string GetISOCountryName(const string strA2);
+
+/* Language code table according to ISO/IEC 639-2 */
+
+#define LEN_TABLE_ISO_LANGUAGE_CODE			505
+
+#define LEN_ISO_LANGUAGE_CODE				3
+#define MAX_LEN_DESC_ISO_LANGUAGE_CODE		44
+
+struct elLanguage {
+	char	strISOCode [LEN_ISO_LANGUAGE_CODE+1];
+	char	strDesc [MAX_LEN_DESC_ISO_LANGUAGE_CODE+1];
+	};
+ 
+extern const struct elLanguage TableISOLanguageCode[LEN_TABLE_ISO_LANGUAGE_CODE];
+
+/* Get language name from ISO 3166 */
+
+string GetISOLanguageName(const string strA3);
+
+/* CIRAF zones */
+#define LEN_TABLE_CIRAF_ZONES			86
+
+extern const string strTableCIRAFzones[LEN_TABLE_CIRAF_ZONES];
+
+#endif // !defined(TABLE_FAC_H__3B0_CA63_4344_BGDEB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableMLC.h b/qsstv/drmtx/common/tables/TableMLC.h
new file mode 100644
index 0000000..f69d405
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableMLC.h
@@ -0,0 +1,488 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Tables for MLC
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(_TABLE_MLC_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
+#define _TABLE_MLC_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+
+/* Definitions ****************************************************************/
+/* Default number of iterations at application startup */
+#define MC_NUM_ITERATIONS				1
+
+/* Generator polynomials used for channel coding (octal form, defined by 
+   a leading "0"!). We must bit-reverse the octal-forms given in the standard 
+   since we shift bits from right to the left! */
+/* In this implementation we shift bits from right to left, therefore the order
+   of the code-words are: [..., b_(0, i), b_(1, i), b(2, i), b(3, i), ...] */
+#define MC_NUM_OUTPUT_BITS_PER_STEP		4	/* MC: Multi-level Coder */
+const _BYTE byGeneratorMatrix[MC_NUM_OUTPUT_BITS_PER_STEP] = {
+	0155,	/* (133) x_{0, i} */
+	0117,	/* (171) x_{1, i} */
+	0123,	/* (145) x_{2, i} */
+	0155	/* (133) x_{3, i} */
+};
+
+#define MC_CONSTRAINT_LENGTH			7
+
+/* Since we have a periodical structure in the trellis it
+   is enough to build one step. 2^(MC_CONSTRAINT_LENGTH - 1) states have
+   to be considered. ("- 1": since one bit is the transition to the next 
+   state) */
+#define MC_NUM_STATES					(1 << (MC_CONSTRAINT_LENGTH - 1))
+#define MC_NUM_OUTPUT_COMBINATIONS		(1 << MC_NUM_OUTPUT_BITS_PER_STEP)
+
+/* Maximum number of levels (Its in case of HMmix) */
+#define MC_MAX_NUM_LEVELS				6
+
+
+/* Puncturing --------------------------------------------------------------- */
+/* Only these types of patterns are used in DRM */
+#define PP_TYPE_0000					0 /* not used, dummy */
+#define PP_TYPE_1111					1
+#define PP_TYPE_0111					2
+#define PP_TYPE_0011					3
+#define PP_TYPE_0001					4
+#define PP_TYPE_0101					5
+
+/* {a, b, c ...}: a = Number of groups, b = Number of "1"s, c = Patterns */
+const uint32_t iPuncturingPatterns[13][10] = {
+/*
+B0: 1
+B1: 1
+B2: 1
+B3: 1
+*/
+	{1, 4,
+	 PP_TYPE_1111,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1
+B1: 1 1 1
+B2: 1 1 1
+B3: 1 0 0
+*/
+	{3, 10,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1
+B1: 1
+B2: 1
+B3: 0
+*/
+	{1, 3,
+	 PP_TYPE_0111,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1
+B1: 1 1 1 1
+B2: 1 1 1 0
+B3: 0 0 0 0
+*/
+	{4, 11,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1
+B1: 1
+B2: 0
+B3: 0
+*/
+	{1, 2,
+	 PP_TYPE_0011,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1
+B1: 1 0 1 0
+B2: 0 1 0 0
+B3: 0 0 0 0
+*/
+	{4, 7,
+	 PP_TYPE_0011,
+	 PP_TYPE_0101,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1
+B1: 1 0 1
+B2: 0 0 0
+B3: 0 0 0
+*/
+	{3, 5,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0011,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1
+B1: 1 0
+B2: 0 0
+B3: 0 0
+*/
+	{2, 3,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1 1 1 1 1
+B1: 1 0 0 1 0 0 1 0
+B2: 0 0 0 0 0 0 0 0
+B3: 0 0 0 0 0 0 0 0
+*/
+	{8, 11,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001},
+
+/*
+B0: 1 1 1
+B1: 1 0 0
+B2: 0 0 0
+B3: 0 0 0
+*/
+	{3, 4,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1
+B1: 1 0 0 0
+B2: 0 0 0 0
+B3: 0 0 0 0
+*/
+	{4, 5,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1 1 1 1
+B1: 1 0 0 0 0 0 0
+B2: 0 0 0 0 0 0 0
+B3: 0 0 0 0 0 0 0
+*/
+	{7, 8,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0000},
+
+/*
+B0: 1 1 1 1 1 1 1 1
+B1: 1 0 0 0 0 0 0 0
+B2: 0 0 0 0 0 0 0 0
+B3: 0 0 0 0 0 0 0 0
+*/
+	{8, 9,
+	 PP_TYPE_0011,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001,
+	 PP_TYPE_0001}
+};
+
+/* Puncturing patterns for tailbits */
+#define LENGTH_TAIL_BIT_PAT				6
+const uint32_t iPunctPatTailbits[12][LENGTH_TAIL_BIT_PAT] = {
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 0 0 0 0 0 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 0 0 0 0 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 0 0 1 0 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 0 1 0 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 0 1 1 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 0
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0011},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 0 0 0 0 0 0
+*/
+	{PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 1 0 0 0 0 0
+*/
+	{PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 1 0 0 1 0 0
+*/
+	{PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 1 1 0 1 0 0
+*/
+	{PP_TYPE_1111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_0111},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 1 1 0 1 0 1
+*/
+	{PP_TYPE_1111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_1111},
+
+/*
+B0: 1 1 1 1 1 1
+B1: 1 1 1 1 1 1
+B2: 1 1 1 1 1 1
+B3: 1 1 1 1 0 1
+*/
+	{PP_TYPE_1111,
+	 PP_TYPE_1111,
+	 PP_TYPE_1111,
+	 PP_TYPE_1111,
+	 PP_TYPE_0111,
+	 PP_TYPE_1111},
+};
+
+
+/* Code rate combinations --------------------------------------------------- */
+/* row-index: protection level */
+/* {a, b, c}: a = R_0, b = R_1, c = RY_Icm */
+/* {a}: a = R_0 */
+const int iCodRateCombMSC4SM = {
+	6
+};
+
+const int iCodRateCombMSC16SM[2][3] = {
+	{2, 7, 3}, 
+	{4, 9, 4}
+};
+
+/* {a, b, c, d}: a = R_0, b = R_1, c = R_2, d = RY_Icm */
+const int iCodRateCombMSC64SM[4][4] = {
+	{0,  4,  9,  4},
+	{2,  7, 10, 15},
+	{4,  9, 11,  8},
+	{7, 10, 12, 45}
+};
+
+/* {a}: a = R_0 */
+const int iCodRateCombFDC4SM = {
+	6
+};
+
+
+/* Interleaver sequence ----------------------------------------------------- */
+/* The different coding modes in DRM use bit-interleavers in certain paths. We
+   define the following vectors to store the position and type of the 
+   interleaver as described in the DRM-standard
+
+   Interleaver modules have to be initialized in the way:
+   [0]: t_0 = 13;
+   [1]: t_0 = 21;
+   "-1": no interleaver in this level */
+const int iInterlSequ4SM[MC_MAX_NUM_LEVELS] =     { 1, -1, -1, -1, -1, -1};
+const int iInterlSequ16SM[MC_MAX_NUM_LEVELS] =    { 0,  1, -1, -1, -1, -1};
+const int iInterlSequ64SM[MC_MAX_NUM_LEVELS] =    {-1,  0,  1, -1, -1, -1};
+
+
+#endif // !defined(_TABLE_MLC_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/tables/TableQAMMapping.h b/qsstv/drmtx/common/tables/TableQAMMapping.h
new file mode 100644
index 0000000..e427c8a
--- /dev/null
+++ b/qsstv/drmtx/common/tables/TableQAMMapping.h
@@ -0,0 +1,87 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	Tables for QAM mapping (Mapping is already normalized)
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(QAM_MAPPING_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
+#define QAM_MAPPING_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+
+
+/* Definitions ****************************************************************/
+/* Input bits are collected in bytes separately for imaginary and real part. 
+   The order is: [i_0, i_1, i_2] and [q_0, q_1, q_2] -> {i, q} 
+   All entries are normalized according to the DRM-standard */
+const _REAL rTableQAM64SM[8][2] = {
+	{ 1.0801234497f,  1.0801234497f},
+	{-0.1543033499f, -0.1543033499f},
+	{ 0.4629100498f,  0.4629100498f},
+	{-0.7715167498f, -0.7715167498f},
+	{ 0.7715167498f,  0.7715167498f},
+	{-0.4629100498f, -0.4629100498f},
+	{ 0.1543033499f,  0.1543033499f},
+	{-1.0801234497f, -1.0801234497f}
+};
+
+const _REAL rTableQAM64HMsym[8][2] = {
+	{ 1.0801234497f,  1.0801234497f},
+	{ 0.4629100498f,  0.4629100498f},
+	{ 0.7715167498f,  0.7715167498f},
+	{ 0.1543033499f,  0.1543033499f},
+	{-0.1543033499f, -0.1543033499f},
+	{-0.7715167498f, -0.7715167498f},
+	{-0.4629100498f, -0.4629100498f},
+	{-1.0801234497f, -1.0801234497f}
+};
+
+const _REAL rTableQAM64HMmix[8][2] = {
+	{ 1.0801234497f,  1.0801234497f},
+	{ 0.4629100498f, -0.1543033499f},
+	{ 0.7715167498f,  0.4629100498f},
+	{ 0.1543033499f, -0.7715167498f},
+	{-0.1543033499f,  0.7715167498f},
+	{-0.7715167498f, -0.4629100498f},
+	{-0.4629100498f,  0.1543033499f},
+	{-1.0801234497f, -1.0801234497f}
+};
+
+const _REAL rTableQAM16[4][2] = {
+	{ 0.9486832980f,  0.9486832980f},
+	{-0.3162277660f, -0.3162277660f},
+	{ 0.3162277660f,  0.3162277660f},
+	{-0.9486832980f, -0.9486832980f}
+};
+
+const _REAL rTableQAM4[2][2] = {
+	{ 0.7071067811f,  0.7071067811f},
+	{-0.7071067811f, -0.7071067811f}
+};
+
+
+#endif // !defined(QAM_MAPPING_H__3B0_CA63_4344_BB2B_23E7912__INCLUDED_)
diff --git a/qsstv/drmtx/common/util/Buffer.h b/qsstv/drmtx/common/util/Buffer.h
new file mode 100644
index 0000000..20b4787
--- /dev/null
+++ b/qsstv/drmtx/common/util/Buffer.h
@@ -0,0 +1,344 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ * Transfer buffer between different modules
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(PUFFER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
+#define PUFFER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_
+#include <qsstvglobal.h>
+#include "../GlobalDefinitions.h"
+#include "utils/vector.h"
+
+
+/* Classes ********************************************************************/
+/* Buffer base class */
+template<class TData> class CBuffer
+{
+public:
+  CBuffer() : iBufferSize(0), bRequestFlag(false) , bNoMoreDataFlag(false){}
+	virtual	~CBuffer() {}
+
+  void SetRequestFlag(const _BOOLEAN bNewRequestFlag)
+    {
+      bRequestFlag = bNewRequestFlag;
+    }
+  void SetNoMoreDataFlag(const _BOOLEAN bNewNoMoreDataFlag)
+    {
+      bNoMoreDataFlag = bNewNoMoreDataFlag;
+    }
+	_BOOLEAN		GetRequestFlag() const {return bRequestFlag;}
+  _BOOLEAN		GetNoMoreDataFlag() const {return bNoMoreDataFlag;}
+	/* Virtual function to be declared by the derived object */
+	virtual void				Init(const int iNewBufferSize);
+	virtual CVectorEx<TData>*	Get(const int iRequestedSize) = 0;
+	virtual CVectorEx<TData>*	QueryWriteBuffer() = 0;
+	virtual void				Put(const int iOfferedSize) = 0;
+	virtual void				Clear() = 0;
+	virtual int					GetFillLevel() const = 0;
+  CVectorEx<TData> &getVecBuffer() {return vecBuffer;}
+
+protected:
+	CVectorEx<TData>	vecBuffer;
+	int					iBufferSize;
+	_BOOLEAN			bRequestFlag;
+  _BOOLEAN			bNoMoreDataFlag;
+};
+
+/* Single block buffer */
+template<class TData> class CSingleBuffer : public CBuffer<TData>
+{
+public:
+	CSingleBuffer() : iFillLevel(0) {}
+	CSingleBuffer(const int iNBufSize) {Init(iNBufSize);}
+	virtual	~CSingleBuffer() {}
+
+	virtual void				Init(const int iNewBufferSize);
+	virtual CVectorEx<TData>*	Get(const int iRequestedSize);
+	virtual CVectorEx<TData>*	QueryWriteBuffer() {return &(this->vecBuffer);}
+	virtual void				Put(const int iOfferedSize);
+	virtual void				Clear() {iFillLevel = 0;}
+	virtual int					GetFillLevel() const {return iFillLevel;}
+
+
+protected:
+	int iFillLevel;
+};
+
+/* Cyclic buffer */
+template<class TData> class CCyclicBuffer : public CBuffer<TData>
+{
+public:
+	enum EBufferState {BS_FULL, BS_EMPTY}; // BS: Buffer Status
+
+	CCyclicBuffer() {Clear();}
+	CCyclicBuffer(const int iNBufSize) {Init(iNBufSize);}
+	virtual	~CCyclicBuffer() {}
+
+	virtual void				Init(const int iNewBufferSize);
+	virtual CVectorEx<TData>*	Get(const int iRequestedSize) ; 
+	virtual CVectorEx<TData>*	QueryWriteBuffer() { return &vecInOutBuffer;}
+	virtual void				Put(const int iOfferedSize);
+	virtual void				Clear();
+	virtual int					GetFillLevel() const;
+
+protected:
+	CVectorEx<TData>	vecInOutBuffer;
+
+	int					iPut;
+	int					iGet;
+	EBufferState		iBufferState;
+};
+
+
+/* Implementation *************************************************************/
+template<class TData> void CBuffer<TData>::Init(const int iNewBufferSize)
+{
+        // printf("init van CBuffer size %d \n", iNewBufferSize);
+	/* Assign buffer size */
+	iBufferSize = iNewBufferSize;
+
+	/* Allocate memory for data field */
+	vecBuffer.Init(iBufferSize);
+}
+
+
+/******************************************************************************\
+* Single block buffer														   *
+\******************************************************************************/
+template<class TData> void CSingleBuffer<TData>::Init(const int iNewBufferSize)
+{
+	/* Only initialize buffer when size has changed, otherwise preserve data */
+	if (iNewBufferSize != this->iBufferSize)
+	{
+		CBuffer<TData>::Init(iNewBufferSize);
+
+		/* Reset buffer parameters (empty buffer) */
+		iFillLevel = 0;
+	}
+}
+
+template<class TData> CVectorEx<TData>* CSingleBuffer<TData>::Get(const int iRequestedSize)
+{
+	/* Get data */
+#ifdef _DEBUG_
+	if (iRequestedSize > iFillLevel)
+	{
+		DebugError("SingleBuffer Get()", "FillLevel",
+			iFillLevel, "Requested size", iRequestedSize);
+	}
+#endif
+
+	/* Block is read, buffer is now empty again */
+	iFillLevel -= iRequestedSize;
+
+	return &(this->vecBuffer);		
+}
+
+template<class TData> void CSingleBuffer<TData>::Put(const int iOfferedSize)
+{
+	/* New Block was added, set new fill level */
+	iFillLevel += iOfferedSize;
+
+#ifdef _DEBUG_
+	if (iFillLevel > this->iBufferSize)
+	{
+		DebugError("SingleBuffer Put()", "FillLevel",
+			iFillLevel, "Buffer size", this->iBufferSize);
+	}
+#endif
+}
+
+
+/******************************************************************************\
+* Cyclic buffer																   *
+\******************************************************************************/
+template<class TData> void CCyclicBuffer<TData>::Init(const int iNewBufferSize)
+{
+        // printf("In Init CCcyclic Buffer size is %d\n", iNewBufferSize);
+	/* Only initialize buffer when size has changed, otherwise preserve data */
+	if (iNewBufferSize != this->iBufferSize)
+	{
+		CBuffer<TData>::Init(iNewBufferSize);
+
+		/* Make in- and output buffer the same size as the main buffer to
+		   make sure that the worst-case is no problem */
+		vecInOutBuffer.Init(iNewBufferSize);
+
+		/* Reset buffer parameters (empty buffer) */
+		iPut = 0;
+		iGet = 0;
+		iBufferState = BS_EMPTY;
+	}
+}
+
+template<class TData> void CCyclicBuffer<TData>::Clear()
+{
+	/* Clear buffer by resetting the pointer */
+	iPut = 0;
+	iGet = 0;
+	iBufferState = BS_EMPTY;
+	this->bRequestFlag = false;
+}
+
+template<class TData> CVectorEx<TData>* CCyclicBuffer<TData>::Get(const int iRequestedSize)
+{
+	int	i;
+	int	iAvailSpace;
+	int iElementCount;
+
+	/* Test if enough data is available for reading */
+        // printf("Ccyclicbuffer get iPut %d iGet %d \n", iPut, iGet); 
+	iAvailSpace = iPut - iGet;
+	/* Test if wrap is needed */
+	if ((iAvailSpace < 0) || ((iAvailSpace == 0) && (iBufferState == BS_FULL)))
+		iAvailSpace += this->iBufferSize;
+
+#ifdef _DEBUG_
+	if (iAvailSpace < iRequestedSize)
+	{
+		DebugError("CyclicBuffer Get()", "Availabe space",
+			iAvailSpace, "Requested size", iAvailSpace);
+	}
+#endif
+
+
+	/* Get data ------------------------------------------------------------- */
+	iElementCount = 0;
+
+	/* Test if data can be read in one block */
+	if (this->iBufferSize - iGet < iRequestedSize)
+	{
+		/* Data must be read in two portions */
+		for (i = iGet; i < this->iBufferSize; i++)
+		{
+			vecInOutBuffer[iElementCount] = this->vecBuffer[i];
+			iElementCount++;
+		}
+		for (i = 0; i < iRequestedSize - this->iBufferSize + iGet; i++)
+		{
+			vecInOutBuffer[iElementCount] = this->vecBuffer[i];
+			iElementCount++;
+		}
+	}
+	else
+	{
+		/* Data can be read in one block */
+		for (i = iGet; i < iGet + iRequestedSize; i++)
+		{
+			vecInOutBuffer[iElementCount] = this->vecBuffer[i];
+			iElementCount++;
+		}
+	}
+
+	/* Adjust iGet pointer */
+	iGet += iRequestedSize;
+	if (iGet >= this->iBufferSize)
+		iGet -= this->iBufferSize;
+
+	/* Test if buffer is empty. If yes, set empty-flag */
+	if ((iGet == iPut) && (iRequestedSize > 0))
+		iBufferState = BS_EMPTY;
+
+	return &vecInOutBuffer;		
+}
+
+template<class TData> void CCyclicBuffer<TData>::Put(const int iOfferedSize)
+{
+	int	iAvailSpace;
+	int	i;
+	int iElementCount;
+
+	/* Test if enough data is available for writing */
+	iAvailSpace = iGet - iPut;
+	/* Test if wrap is needed */
+	if ((iAvailSpace < 0) || ((iAvailSpace == 0) && (iBufferState == BS_EMPTY)))
+		iAvailSpace += this->iBufferSize;
+
+#ifdef _DEBUG_
+	if (iAvailSpace < iOfferedSize)
+	{
+		DebugError("CyclicBuffer Put()", "Available space",
+			iAvailSpace, "Offered size", iOfferedSize);
+	}
+#endif
+
+
+	/* Put data ------------------------------------------------------------- */
+	iElementCount = 0;
+
+	/* Test if data can be written in one block */
+	if (this->iBufferSize - iPut < iOfferedSize)
+	{
+		/* Data must be written in two steps */
+		for (i = iPut; i < this->iBufferSize; i++)
+		{
+			this->vecBuffer[i] = vecInOutBuffer[iElementCount];
+			iElementCount++;
+		}
+		for (i = 0; i < iOfferedSize - this->iBufferSize + iPut; i++)
+		{
+			this->vecBuffer[i] = vecInOutBuffer[iElementCount];
+			iElementCount++;
+		}
+	}
+	else
+	{
+		/* Data can be written in one block */
+		for (i = iPut; i < iPut + iOfferedSize; i++)
+		{
+			this->vecBuffer[i] = vecInOutBuffer[iElementCount];
+			iElementCount++;
+		}
+	}
+
+	/* Adjust iPut pointer */
+	iPut += iOfferedSize;
+	if (iPut >= this->iBufferSize)
+		iPut -= this->iBufferSize;
+
+	/* Test if buffer is full. If yes, set full-flag */
+	if ((iGet == iPut) && (iOfferedSize > 0))
+		iBufferState = BS_FULL;
+}
+
+template<class TData> int CCyclicBuffer<TData>::GetFillLevel() const
+{
+	int iFillLevel;
+
+	/* Calculate the available data in the buffer. Test if wrap is needed!
+	   Take into account the flag-information (full or empty buffer) */
+	iFillLevel = iPut - iGet;
+	if ((iFillLevel == 0) && (iBufferState == BS_FULL))
+		iFillLevel = this->iBufferSize;
+	if (iFillLevel < 0)
+		iFillLevel += this->iBufferSize;	/* Wrap around */
+
+	return iFillLevel;
+}
+
+
+#endif // !defined(PUFFER_H__3B0BA660_CA63_4344_BB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/util/CRC.cpp b/qsstv/drmtx/common/util/CRC.cpp
new file mode 100644
index 0000000..bcf9356
--- /dev/null
+++ b/qsstv/drmtx/common/util/CRC.cpp
@@ -0,0 +1,219 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * adapted for ham sstv use Ties Bos - PA0MBO
+ * Description:
+
+ NOTE:
+ This code is NOT speed optimized! We should calculate the CRC byte-wise and
+ precalculate the results in a table (TODO!)
+
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "CRC.h"
+
+
+/* Implementation *************************************************************/
+void CCRC::Reset(const int iNewDegree)
+{
+	/* Build mask of bit, which was shifted out of the shift register */
+	iBitOutPosMask = 1 << iNewDegree;
+
+	/* Index of vector storing the polynominals for CRC calculation */
+	iDegIndex = iNewDegree - 1;
+
+	/* Init state shift-register with ones. Set all registers to "1" with
+	   bit-wise not operation */
+	iStateShiftReg = ~uint32_t(0);
+}
+
+void CCRC::AddByte(const _BYTE byNewInput)
+{
+	for (int i = 0; i < SIZEOF__BYTE; i++)
+	{
+		/* Shift bits in shift-register for transistion */
+		iStateShiftReg <<= 1;
+
+		/* Take bit, which was shifted out of the register-size and place it
+		   at the beginning (LSB)
+		   (If condition is not satisfied, implicitely a "0" is added) */
+		if ((iStateShiftReg & iBitOutPosMask) > 0)
+			iStateShiftReg |= 1;
+
+		/* Add new data bit to the LSB */
+		if ((byNewInput & (1 << (SIZEOF__BYTE - i - 1))) > 0)
+			iStateShiftReg ^= 1;
+
+		/* Add mask to shift-register if first bit is true */
+		if (iStateShiftReg & 1)
+			iStateShiftReg ^= iPolynMask[iDegIndex];
+	}
+}
+
+void CCRC::AddBit(const _BINARY biNewInput)
+{
+	/* Shift bits in shift-register for transistion */
+	iStateShiftReg <<= 1;
+
+	/* Take bit, which was shifted out of the register-size and place it
+	   at the beginning (LSB)
+	   (If condition is not satisfied, implicitely a "0" is added) */
+	if ((iStateShiftReg & iBitOutPosMask) > 0)
+		iStateShiftReg |= 1;
+
+	/* Add new data bit to the LSB */
+	if (biNewInput > 0)
+		iStateShiftReg ^= 1;
+
+	/* Add mask to shift-register if first bit is true */
+	if (iStateShiftReg & 1)
+		iStateShiftReg ^= iPolynMask[iDegIndex];
+}
+
+uint32_t CCRC::GetCRC()
+{
+	/* Return inverted shift-register (1's complement) */
+	iStateShiftReg = ~iStateShiftReg;
+
+	/* Remove bit which where shifted out of the shift-register frame */
+	return iStateShiftReg & (iBitOutPosMask - 1);
+}
+
+_BOOLEAN CCRC::CheckCRC(const uint32_t iCRC)
+{
+	if (iCRC == GetCRC())
+		return true;
+	else
+		return false;
+}
+
+CCRC::CCRC()
+{
+	/* These polynominals are used in the DRM-standard */
+	iPolynMask[0] = 0;
+	iPolynMask[1] = 1 << 1;
+	iPolynMask[2] = 1 << 1;
+	iPolynMask[4] = (1 << 1) | (1 << 2) | (1 << 4);
+	iPolynMask[5] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 5);
+	iPolynMask[7] = (1 << 2) | (1 << 3) | (1 << 4);
+	iPolynMask[15] = (1 << 5) | (1 << 12);
+}
+
+
+
+/******************************************************************************/
+/*                                                                            */
+/*  University of Kaiserslautern, Institute of Communications Engineering     */
+/*  Copyright (C) 2004 Andreas Dittrich, Torsten Schorr                       */
+/*                                                                            */
+/*  Author(s)    : Andreas Dittrich (dittrich at eit.uni-kl.de),                 */
+/*                 Torsten Schorr (schorr at eit.uni-kl.de)                      */
+/*  Project start: 27.07.2004                                                 */
+/*  Last change  : 27.07.2004                                                 */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/*  This program is free software; you can redistribute it and/or modify      */
+/*  it under the terms of the GNU General Public License as published by      */
+/*  the Free Software Foundation; either version 2 of the License, or         */
+/*  (at your option) any later version.                                       */
+/*                                                                            */
+/*  This program is distributed in the hope that it will be useful,           */
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+/*  GNU General Public License for more details.                              */
+/*                                                                            */
+/*  You should have received a copy of the GNU General Public License         */
+/*  along with this program; if not, write to the Free Software               */
+/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+/*                                                                            */
+/******************************************************************************/
+
+
+/******************************************************************************/
+/*                                                                            */
+/*  crc16_bytewise.c                                                          */
+/*                                                                            */
+/******************************************************************************/
+/*  Description:                                                              */
+/*  CRC-16 checksum calculation of a byte stream                              */
+/*  Usage:                                                                    */
+/*                                                                            */
+/*  crc16_bytewise(double *checksum, unsigned char *in, long N);                                         */
+/*                                                                            */
+/*  calculates double checksum of uint8 bytes                                 */
+/*                                                                            */
+/******************************************************************************/
+
+
+/*************
+*
+*   adjusted for use in own plain C programa
+*   by M.Bos - PA0MBO
+*
+*   Date Dec 9th 2007
+*/
+
+
+/******************************************************************************/
+/* function                                                                   */
+/******************************************************************************/
+void CCRC::crc16_bytewise(double  checksum[],
+                          unsigned char in[], long N)
+{
+  long int i;
+  int j;
+  unsigned int b = 0xFFFF;
+  unsigned int x = 0x1021;	/* (1) 0001000000100001 */
+  unsigned int y=0;
+
+  for (i = 0; i < N - 2; i++)
+
+    {
+      for (j = 7; j >= 0; j--)
+        {
+          y = (((b >> 15) + (unsigned int) (in[i] >> j)) & 0x01) & 0x01;	/* extra parenth pa0mbo */
+          if (y == 1)
+            b = ((b << 1) ^ x);
+
+          else
+            b = (b << 1);
+        }
+    }
+  for (i = N - 2; i < N; i++)
+
+    {
+      for (j = 7; j >= 0; j--)
+        {
+          y = (((b >> 15) + (unsigned int) ((in[i] >> j) & 0x01)) ^ 0x01) & 0x01;	/* extra parent pa0mbo */
+          if (y == 1)
+            b = ((b << 1) ^ x);
+
+          else
+            b = (b << 1);
+        }
+    }
+  *checksum = (double) (b & 0xFFFF);
+}
+
diff --git a/qsstv/drmtx/common/util/CRC.h b/qsstv/drmtx/common/util/CRC.h
new file mode 100644
index 0000000..89947c3
--- /dev/null
+++ b/qsstv/drmtx/common/util/CRC.h
@@ -0,0 +1,60 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * extended for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	See CRC.cpp
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(CRC_H__3B0BA660_CA63_4VASDGLJNAJ2B_23E7A0D31912__INCLUDED_)
+#define CRC_H__3B0BA660_CA63_4VASDGLJNAJ2B_23E7A0D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+
+
+/* Classes ********************************************************************/
+class CCRC
+{
+public:
+	CCRC();
+	virtual ~CCRC() {}
+
+	void Reset(const int iNewDegree);
+	void AddByte(const _BYTE byNewInput);
+	void AddBit(const _BINARY biNewInput);
+	_BOOLEAN CheckCRC(const uint32_t iCRC);
+	uint32_t GetCRC();
+        void crc16_bytewise(double checksum[], unsigned char in[], long N);
+
+protected:
+	int			iDegIndex;
+	uint32_t	iBitOutPosMask;
+
+	uint32_t	iPolynMask[16];
+	uint32_t	iStateShiftReg;
+};
+
+
+#endif // !defined(CRC_H__3B0BA660_CA63_4VASDGLJNAJ2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/drmtx/common/util/Modul.h b/qsstv/drmtx/common/util/Modul.h
new file mode 100644
index 0000000..b520c0d
--- /dev/null
+++ b/qsstv/drmtx/common/util/Modul.h
@@ -0,0 +1,411 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Adapted for ham sstv use Ties Bos - PA0MBO
+ *
+ * Description:
+ *	High level class for all modules. The common functionality for reading
+ *	and writing the transfer-buffers are implemented here.
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(AFX_MODUL_H__41E39CD3_2AEC_400E_907B_148C0EC17A43__INCLUDED_)
+#define AFX_MODUL_H__41E39CD3_2AEC_400E_907B_148C0EC17A43__INCLUDED_
+
+#include "Buffer.h"
+#include "utils/vector.h"
+#include "../Parameter.h"
+#include <iostream>
+
+
+/* Classes ********************************************************************/
+/* CModul ------------------------------------------------------------------- */
+template<class TInput, class TOutput>
+class CModul  
+{
+public:
+	CModul();
+	virtual ~CModul() {}
+
+	virtual void Init(CParameter& Parameter);
+	virtual void Init(CParameter& Parameter, CBuffer<TOutput>& OutputBuffer);
+
+protected:
+	CVectorEx<TInput>*	pvecInputData;
+	CVectorEx<TOutput>*	pvecOutputData;
+
+	/* Max block-size are used to determine the size of the requiered buffer */
+	int					iMaxOutputBlockSize;
+	/* Actual read (or written) size of the data */
+	int					iInputBlockSize;
+	int					iOutputBlockSize;
+
+	void				Lock() {Mutex.Lock();}
+	void				Unlock() {Mutex.Unlock();}
+
+	void				InitThreadSave(CParameter& Parameter);
+	virtual void		InitInternal(CParameter& Parameter) = 0;
+	void				ProcessDataThreadSave(CParameter& Parameter);
+	virtual void		ProcessDataInternal(CParameter& Parameter) = 0;
+
+private:
+	CMutex				Mutex;
+};
+
+
+/* CTransmitterModul -------------------------------------------------------- */
+template<class TInput, class TOutput>
+class CTransmitterModul : public CModul<TInput, TOutput>
+{
+public:
+	CTransmitterModul();
+	virtual ~CTransmitterModul() {}
+
+	virtual void		Init(CParameter& Parameter);
+	virtual void		Init(CParameter& Parameter, 
+							 CBuffer<TOutput>& OutputBuffer);
+	virtual void		ReadData(CParameter& Parameter, 
+								 CBuffer<TOutput>& OutputBuffer);
+	virtual void		ProcessData(CParameter& Parameter, 
+								 CBuffer<TOutput>& OutputBuffer);
+	virtual _BOOLEAN	ProcessData(CParameter& Parameter, 
+									CBuffer<TInput>& InputBuffer, 
+									CBuffer<TOutput>& OutputBuffer);
+	virtual void		ProcessData(CParameter& Parameter, 
+									CBuffer<TInput>& InputBuffer,
+									CBuffer<TInput>& InputBuffer2, 
+				//					CBuffer<TInput>& InputBuffer3,   pa0mbo 
+									CBuffer<TOutput>& OutputBuffer);
+	virtual _BOOLEAN	WriteData(CParameter& Parameter, 
+								  CBuffer<TInput>& InputBuffer);
+
+protected:
+	/* Additional buffers if the derived class has multiple input streams */
+	CVectorEx<TInput>*	pvecInputData2;
+	CVectorEx<TInput>*	pvecInputData3;
+
+	/* Actual read (or written) size of the data */
+	int					iInputBlockSize2;
+	int					iInputBlockSize3;
+};
+
+
+
+
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* CModul                                                                       *
+\******************************************************************************/
+template<class TInput, class TOutput>
+CModul<TInput, TOutput>::CModul()
+{
+	/* Initialize everything with zeros */
+	iMaxOutputBlockSize = 0;
+	iInputBlockSize = 0;
+	iOutputBlockSize = 0;
+	pvecInputData = NULL;
+	pvecOutputData = NULL;
+}
+
+template<class TInput, class TOutput>
+void CModul<TInput, TOutput>::ProcessDataThreadSave(CParameter& Parameter)
+{
+	/* Get a lock for the resources */
+	Lock();
+
+	/* Call processing routine of derived modul */
+	ProcessDataInternal(Parameter);
+
+	/* Unlock resources */
+	Unlock();
+}
+
+template<class TInput, class TOutput>
+void CModul<TInput, TOutput>::InitThreadSave(CParameter& Parameter)
+{
+	/* Get a lock for the resources */
+	Lock();
+
+	try
+	{
+		/* Call init of derived modul */
+		InitInternal(Parameter);
+
+		/* Unlock resources */
+		Unlock();
+	}
+
+	catch (CGenErr)
+	{
+		/* Unlock resources */
+		Unlock();
+
+		/* Throws the same error again which was send by the function */
+		throw;
+	}
+}
+
+template<class TInput, class TOutput>
+void CModul<TInput, TOutput>::Init(CParameter& Parameter)
+{
+	/* Init some internal variables */
+	iInputBlockSize = 0;
+
+	/* Call init of derived modul */
+	InitThreadSave(Parameter);
+}
+
+template<class TInput, class TOutput>
+void CModul<TInput, TOutput>::Init(CParameter& Parameter, 
+								   CBuffer<TOutput>& OutputBuffer)
+{
+	/* Init some internal variables */
+	iMaxOutputBlockSize = 0;
+	iInputBlockSize = 0;
+	iOutputBlockSize = 0;
+
+	/* Call init of derived modul */
+	InitThreadSave(Parameter);
+
+	/* Init output transfer buffer */
+	if (iMaxOutputBlockSize != 0)
+		OutputBuffer.Init(iMaxOutputBlockSize);
+	else
+	{
+		if (iOutputBlockSize != 0)
+			OutputBuffer.Init(iOutputBlockSize);
+	}
+}
+
+
+/******************************************************************************\
+* Transmitter modul (CTransmitterModul)                                        *
+\******************************************************************************/
+template<class TInput, class TOutput>
+CTransmitterModul<TInput, TOutput>::CTransmitterModul()
+{
+	/* Initialize all member variables with zeros */
+	iInputBlockSize2 = 0;
+	iInputBlockSize3 = 0;
+	pvecInputData2 = NULL;
+	pvecInputData3 = NULL;
+}
+
+template<class TInput, class TOutput>
+void CTransmitterModul<TInput, TOutput>::Init(CParameter& Parameter)
+{
+	/* Init some internal variables */
+	iInputBlockSize2 = 0;
+	iInputBlockSize3 = 0;
+
+	/* Init base-class */
+	CModul<TInput, TOutput>::Init(Parameter);
+}
+
+template<class TInput, class TOutput>
+void CTransmitterModul<TInput, TOutput>::Init(CParameter& Parameter, 
+											  CBuffer<TOutput>& OutputBuffer)
+{
+	/* Init some internal variables */
+	iInputBlockSize2 = 0;
+	iInputBlockSize3 = 0;
+
+	/* Init base-class */
+	CModul<TInput, TOutput>::Init(Parameter, OutputBuffer);
+}
+
+template<class TInput, class TOutput>
+_BOOLEAN CTransmitterModul<TInput, TOutput>::
+	ProcessData(CParameter& Parameter, CBuffer<TInput>& InputBuffer,
+				CBuffer<TOutput>& OutputBuffer)
+{
+        // printf("Tx Par Inp Outp request is %d\n", OutputBuffer.GetRequestFlag());
+	/* OUTPUT-DRIVEN modul implementation in the transmitter ---------------- */
+	/* Look in output buffer if data is requested */
+	if (OutputBuffer.GetRequestFlag() == true)
+	{
+                // printf("CTransmitterModul Getrequest is true\n");
+		/* Check, if enough input data is available */
+		if (InputBuffer.GetFillLevel() < this->iInputBlockSize)
+		{
+			/* Set request flag */
+			InputBuffer.SetRequestFlag(true);
+
+			return false;
+		}
+
+		/* Get vector from transfer-buffer */
+		this->pvecInputData = InputBuffer.Get(this->iInputBlockSize);
+
+		/* Query vector from output transfer-buffer for writing */
+		this->pvecOutputData = OutputBuffer.QueryWriteBuffer();
+
+		/* Copy extended data from vectors */
+    (*(this->pvecOutputData)).SetExData((*(this->pvecInputData)).GetExData());
+
+    /* Call the underlying processing-routine */
+		this->ProcessDataInternal(Parameter);
+	
+		/* Write processed data from internal memory in transfer-buffer */
+		OutputBuffer.Put(this->iOutputBlockSize);
+
+		/* Data was provided, clear data request */
+		OutputBuffer.SetRequestFlag(false);
+	}
+
+	return true;
+}
+
+template<class TInput, class TOutput>
+void CTransmitterModul<TInput, TOutput>::
+	ProcessData(CParameter& Parameter, CBuffer<TInput>& InputBuffer,
+				CBuffer<TInput>& InputBuffer2,
+	//			CBuffer<TInput>& InputBuffer3,    pa0mbo
+				CBuffer<TOutput>& OutputBuffer)
+{
+	/* OUTPUT-DRIVEN modul implementation in the transmitter ---------------- */
+	/* Look in output buffer if data is requested */
+        // printf("Tx Par Inp1 Inp2 Outp request is %d  inp1size %d inp2size %d \n",
+           //   OutputBuffer.GetRequestFlag(), this->iInputBlockSize, iInputBlockSize2);
+	if (OutputBuffer.GetRequestFlag() == true)
+	{
+		/* Check, if enough input data is available from all sources */
+    if (InputBuffer.GetFillLevel() < this->iInputBlockSize)
+		{
+			/* Set request flag */
+                        // printf("tx modul 2 inputs setting request on buf1 \n");
+			InputBuffer.SetRequestFlag(true);
+
+			return;
+		}
+		if (InputBuffer2.GetFillLevel() < iInputBlockSize2)
+		{
+			/* Set request flag */
+                        // printf("tx modul 2 inputs setting request on buf2 \n");
+			InputBuffer2.SetRequestFlag(true);
+
+			return;
+		}
+/*		if (InputBuffer3.GetFillLevel() < iInputBlockSize3)
+		{
+			InputBuffer3.SetRequestFlag(true);
+
+			return;
+		}
+		*/
+	
+		/* Get vectors from transfer-buffers */
+    addToLog(QString("ProcesData get %1").arg(this->iInputBlockSize),LOGDRMTX);
+		this->pvecInputData = InputBuffer.Get(this->iInputBlockSize);
+		pvecInputData2 = InputBuffer2.Get(iInputBlockSize2);
+//		pvecInputData3 = InputBuffer3.Get(iInputBlockSize3);   pa0mbo
+
+		/* Query vector from output transfer-buffer for writing */
+		this->pvecOutputData = OutputBuffer.QueryWriteBuffer();
+
+		/* Call the underlying processing-routine */
+		this->ProcessDataInternal(Parameter);
+	
+		/* Write processed data from internal memory in transfer-buffer */
+		OutputBuffer.Put(this->iOutputBlockSize);
+
+		/* Data was provided, clear data request */
+		OutputBuffer.SetRequestFlag(false);
+	}
+}
+
+template<class TInput, class TOutput> void CTransmitterModul<TInput, TOutput>::ProcessData(CParameter& Parameter, CBuffer<TOutput>& OutputBuffer)
+{
+  // printf("Tx par Outp request  flag is %d\n", OutputBuffer.GetRequestFlag());
+	/* OUTPUT-DRIVEN modul implementation in the transmitter ---------------- */
+	/* Look in output buffer if data is requested */
+	if (OutputBuffer.GetRequestFlag() == true)
+	{
+		/* Read data and write it in the transfer-buffer.
+		   Query vector from output transfer-buffer for writing */
+
+		this->pvecOutputData = OutputBuffer.QueryWriteBuffer();
+
+		/* Call the underlying processing-routine */
+		this->ProcessDataInternal(Parameter);
+		
+		/* Write processed data from internal memory in transfer-buffer */
+		OutputBuffer.Put(this->iOutputBlockSize);
+
+		/* Data was provided, clear data request */
+		OutputBuffer.SetRequestFlag(false);
+	}
+}
+
+template<class TInput, class TOutput>
+void CTransmitterModul<TInput, TOutput>::
+	ReadData(CParameter& Parameter, CBuffer<TOutput>& OutputBuffer)
+{
+        // printf("CTransmitterModul entry flag is %d\n", OutputBuffer.GetRequestFlag());
+	/* OUTPUT-DRIVEN modul implementation in the transmitter ---------------- */
+	/* Look in output buffer if data is requested */
+        // printf("Tx ReadData request flag is %d \n", OutputBuffer.GetRequestFlag());
+	if (OutputBuffer.GetRequestFlag() == true)
+	{
+		/* Read data and write it in the transfer-buffer.
+		   Query vector from output transfer-buffer for writing */
+		this->pvecOutputData = OutputBuffer.QueryWriteBuffer();
+
+		/* Call the underlying processing-routine */
+		this->ProcessDataInternal(Parameter);
+		
+		/* Write processed data from internal memory in transfer-buffer */
+		OutputBuffer.Put(this->iOutputBlockSize);
+
+		/* Data was provided, clear data request */
+		OutputBuffer.SetRequestFlag(false);
+	}
+}
+
+template<class TInput, class TOutput> _BOOLEAN CTransmitterModul<TInput, TOutput>:: 	WriteData(CParameter& Parameter, CBuffer<TInput>& InputBuffer)
+{
+	 // printf("WriteData fill %d Inpblk size %d \n",
+	//		InputBuffer.GetFillLevel(), this->iInputBlockSize);  
+
+	/* OUTPUT-DRIVEN modul implementation in the transmitter */
+	/* Check, if enough input data is available */
+	if (InputBuffer.GetFillLevel() < this->iInputBlockSize)
+	{
+		 // printf("set request flag transmitter module WriteData \n"); 
+		/* Set request flag */
+		InputBuffer.SetRequestFlag(true);
+
+		return false;
+	}
+        // printf("Getting the data in TXModul from input buffer\n");
+	/* Get vector from transfer-buffer */
+	this->pvecInputData = InputBuffer.Get(this->iInputBlockSize);
+
+	/* Call the underlying processing-routine */
+	this->ProcessDataInternal(Parameter);
+
+	return true;
+}
+
+#endif // !defined(AFX_MODUL_H__41E39CD3_2AEC_400E_907B_148C0EC17A43__INCLUDED_)
diff --git a/qsstv/drmtx/common/util/Utilities.cpp b/qsstv/drmtx/common/util/Utilities.cpp
new file mode 100644
index 0000000..7765553
--- /dev/null
+++ b/qsstv/drmtx/common/util/Utilities.cpp
@@ -0,0 +1,310 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2004
+ *
+ * Author(s):
+ *	Volker Fischer
+ * 
+ * Description:
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "Utilities.h"
+#include <sstream>
+#include <cstring>
+#if defined(_WIN32)
+# ifdef HAVE_SETUPAPI
+#  ifndef INITGUID
+#   define INITGUID 1
+#  endif
+#  include <windows.h>
+#  include <setupapi.h>
+#  if defined(_MSC_VER) && (_MSC_VER < 1400) || defined(__MINGW32__)
+    DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 
+    0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);
+#  endif
+# endif
+#elif defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#endif
+
+/* Implementation *************************************************************/
+/******************************************************************************\
+* Signal level meter                                                           *
+\******************************************************************************/
+//void
+//CSignalLevelMeter::Update(const _REAL rVal)
+//{
+//	/* Search for maximum. Decrease this max with time */
+//	/* Decrease max with time */
+//	if (rCurLevel >= METER_FLY_BACK)
+//		rCurLevel -= METER_FLY_BACK;
+//	else
+//	{
+//		if ((rCurLevel <= METER_FLY_BACK) && (rCurLevel > 1))
+//			rCurLevel -= 2;
+//	}
+
+//	/* Search for max */
+//	const _REAL rCurAbsVal = Abs(rVal);
+//	if (rCurAbsVal > rCurLevel)
+//		rCurLevel = rCurAbsVal;
+//}
+
+//void
+//CSignalLevelMeter::Update(const CVector < _REAL > vecrVal)
+//{
+//	/* Do the update for entire vector */
+//	const int iVecSize = vecrVal.Size();
+//	for (int i = 0; i < iVecSize; i++)
+//		Update(vecrVal[i]);
+//}
+
+//void
+//CSignalLevelMeter::Update(const CVector < _SAMPLE > vecsVal)
+//{
+//	/* Do the update for entire vector, convert to real */
+//	const int iVecSize = vecsVal.Size();
+//	for (int i = 0; i < iVecSize; i++)
+//		Update((_REAL) vecsVal[i]);
+//}
+
+//_REAL CSignalLevelMeter::Level()
+//{
+//	const _REAL
+//		rNormMicLevel = rCurLevel / _MAXSHORT;
+
+//	/* Logarithmic measure */
+//	if (rNormMicLevel > 0)
+//		return 20.0 * log10(rNormMicLevel);
+//	else
+//		return RET_VAL_LOG_0;
+//}
+
+/******************************************************************************\
+* Bandpass filter                                                              *
+\******************************************************************************/
+void
+CDRMBandpassFilt::Process(CVector < _COMPLEX > &veccData)
+{
+	int i;
+
+	/* Copy CVector data in CMatlibVector */
+	for (i = 0; i < iBlockSize; i++)
+		cvecDataTmp[i] = veccData[i];
+
+	/* Apply FFT filter */
+	cvecDataTmp =
+		CComplexVector(FftFilt
+					   (cvecB, Real(cvecDataTmp), rvecZReal, FftPlanBP),
+					   FftFilt(cvecB, Imag(cvecDataTmp), rvecZImag,
+							   FftPlanBP));
+
+	/* Copy CVector data in CMatlibVector */
+	for (i = 0; i < iBlockSize; i++)
+		veccData[i] = cvecDataTmp[i];
+}
+
+void
+CDRMBandpassFilt::Init(const int iNewBlockSize, const _REAL rOffsetHz,
+					   const ESpecOcc eSpecOcc, const EFiltType eNFiTy)
+{
+	CReal rMargin = 0.0;
+
+	/* Set internal parameter */
+	iBlockSize = iNewBlockSize;
+
+	/* Init temporary vector */
+	cvecDataTmp.Init(iBlockSize);
+
+	/* Choose correct filter for chosen DRM bandwidth. Also, adjust offset
+	   frequency for different modes. E.g., 5 kHz mode is on the right side
+	   of the DC frequency */
+	CReal rNormCurFreqOffset = rOffsetHz / SOUNDCRD_SAMPLE_RATE;
+	/* Band-pass filter bandwidth */
+	CReal rBPFiltBW = ((CReal) 10000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;
+
+	/* Negative margin for receiver filter for better interferer rejection */
+	if (eNFiTy == FT_TRANSMITTER)
+		rMargin = (CReal) 0.0;	/* Hz was 300 */
+	else
+		rMargin = (CReal) 0.0;	/* Hz */
+
+	switch (eSpecOcc)
+	{
+	case SO_0:
+		rBPFiltBW = ((CReal) 2000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;   // pa0mbo was 4500.0 moet zijn 2250?
+
+		/* Completely on the right side of DC */
+		rNormCurFreqOffset =
+			(rOffsetHz + (CReal) 1100.0) / SOUNDCRD_SAMPLE_RATE;    // pa0mbo
+//			( (CReal) 7100.0  ) / SOUNDCRD_SAMPLE_RATE;    // pa0mbo
+		         // printf("util Bandpass filt init SO_0  BW %g rNormOffs %g\n", rBPFiltBW, rNormCurFreqOffset);
+		break;
+
+	case SO_1:
+		rBPFiltBW = ((CReal) 2500.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;  // pa0mbo was 5000.0 moet zijn 2500  ?
+
+		/* Completely on the right side of DC */
+		rNormCurFreqOffset =
+			(rOffsetHz + (CReal) 1250.0) / SOUNDCRD_SAMPLE_RATE;   // pa0mbo
+//			( (CReal) 7240.0 )  / SOUNDCRD_SAMPLE_RATE;   // pa0mbo
+		// printf("util drmbandpass SO_1   BW = %g , Offset = %g \n", rBPFiltBW,  rNormCurFreqOffset);
+		break;
+
+	case SO_2:
+		rBPFiltBW = ((CReal) 9000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;
+
+		/* Centered */
+		rNormCurFreqOffset = rOffsetHz / SOUNDCRD_SAMPLE_RATE;
+		break;
+
+	case SO_3:
+		rBPFiltBW = ((CReal) 10000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;
+
+		/* Centered */
+		rNormCurFreqOffset = rOffsetHz / SOUNDCRD_SAMPLE_RATE;
+		break;
+
+	case SO_4:
+		rBPFiltBW = ((CReal) 18000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;
+
+		/* Main part on the right side of DC */
+		rNormCurFreqOffset =
+			(rOffsetHz + (CReal) 4500.0) / SOUNDCRD_SAMPLE_RATE;
+		break;
+
+	case SO_5:
+		rBPFiltBW = ((CReal) 20000.0 + rMargin) / SOUNDCRD_SAMPLE_RATE;
+
+		/* Main part on the right side of DC */
+		rNormCurFreqOffset =
+			(rOffsetHz + (CReal) 5000.0) / SOUNDCRD_SAMPLE_RATE;
+		break;
+	}
+         // printf("Init bandpass BW = %g rNorm %g \n", rBPFiltBW, rNormCurFreqOffset); 
+	/* FFT plan is initialized with the long length */
+	FftPlanBP.Init(iBlockSize * 2);
+
+	/* State memory (init with zeros) and data vector */
+	rvecZReal.Init(iBlockSize, (CReal) 0.0);
+	rvecZImag.Init(iBlockSize, (CReal) 0.0);
+	rvecDataReal.Init(iBlockSize);
+	rvecDataImag.Init(iBlockSize);
+
+	/* "+ 1" because of the Nyquist frequency (filter in frequency domain) */
+	cvecB.Init(iBlockSize + 1);
+
+	/* Actual filter design */
+	CRealVector vecrFilter(iBlockSize);
+	vecrFilter = FirLP(rBPFiltBW, Nuttallwin(iBlockSize));
+
+	/* Copy actual filter coefficients. It is important to initialize the
+	   vectors with zeros because we also do a zero-padding */
+	CRealVector rvecB(2 * iBlockSize, (CReal) 0.0);
+
+	/* Modulate filter to shift it to the correct IF frequency */
+	for (int i = 0; i < iBlockSize; i++)
+	{
+		rvecB[i] =
+			vecrFilter[i] * Cos((CReal) 2.0 * crPi * rNormCurFreqOffset * i);
+	}
+
+	/* Transformation in frequency domain for fft filter */
+	cvecB = rfft(rvecB, FftPlanBP);
+        /* debugging pa0mbo 
+        for (i=0; i < iBlockSize; i++)
+        {
+           printf(" vecrFilter[%d] is  %g cvecB[%d] real is %g \n", i, vecrFilter[i], i, cvecB[i].real());
+        }
+        */
+
+	// printf("end of init BPfilt, iBlockSize %d rBPFBW %g rOffsetHz %g i rNormFreqoff %g\n",
+			// iBlockSize, rBPFiltBW, rOffsetHz, rNormCurFreqOffset ) ;
+	// printf("Soundcard sample rate is %d \n", SOUNDCRD_SAMPLE_RATE);
+}
+
+/******************************************************************************\
+* Modified Julian Date                                                         *
+\******************************************************************************/
+void
+CModJulDate::Set(const uint32_t iModJulDate)
+{
+	uint32_t iZ, iA, iAlpha, iB, iC, iD, iE;
+  _REAL rJulDate;
+
+	/* Definition of the Modified Julian Date */
+	rJulDate = (_REAL) iModJulDate + 2400000.5;
+
+	/* Get "real" date out of Julian Date
+	   (Taken from "http://mathforum.org/library/drmath/view/51907.html") */
+	// 1. Add .5 to the JD and let Z = integer part of (JD+.5) and F the
+	// fractional part F = (JD+.5)-Z
+	iZ = (uint32_t) (rJulDate + (_REAL) 0.5);
+//	rF = (rJulDate + (_REAL) 0.5) - iZ;
+
+	// 2. If Z < 2299161, take A = Z
+	// If Z >= 2299161, calculate alpha = INT((Z-1867216.25)/36524.25)
+	// and A = Z + 1 + alpha - INT(alpha/4).
+	if (iZ < 2299161)
+		iA = iZ;
+	else
+	{
+		iAlpha = (int) (((_REAL) iZ - (_REAL) 1867216.25) / (_REAL) 36524.25);
+		iA = iZ + 1 + iAlpha - (int) ((_REAL) iAlpha / (_REAL) 4.0);
+	}
+
+	// 3. Then calculate:
+	// B = A + 1524
+	// C = INT( (B-122.1)/365.25)
+	// D = INT( 365.25*C )
+	// E = INT( (B-D)/30.6001 )
+	iB = iA + 1524;
+	iC = (int) (((_REAL) iB - (_REAL) 122.1) / (_REAL) 365.25);
+	iD = (int) ((_REAL) 365.25 * iC);
+	iE = (int) (((_REAL) iB - iD) / (_REAL) 30.6001);
+
+	// The day of the month dd (with decimals) is:
+	// dd = B - D - INT(30.6001*E) + F
+	iDay = iB - iD - (int) ((_REAL) 30.6001 * iE);	// + rF;
+
+	// The month number mm is:
+	// mm = E - 1, if E < 13.5
+	// or
+	// mm = E - 13, if E > 13.5
+	if ((_REAL) iE < 13.5)
+		iMonth = iE - 1;
+	else
+		iMonth = iE - 13;
+
+	// The year yyyy is:
+	// yyyy = C - 4716   if m > 2.5
+	// or
+	// yyyy = C - 4715   if m < 2.5
+	if ((_REAL) iMonth > 2.5)
+		iYear = iC - 4716;
+	else
+		iYear = iC - 4715;
+}
+
+
+
+
diff --git a/qsstv/drmtx/common/util/Utilities.h b/qsstv/drmtx/common/util/Utilities.h
new file mode 100644
index 0000000..d081ab2
--- /dev/null
+++ b/qsstv/drmtx/common/util/Utilities.h
@@ -0,0 +1,123 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2004
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	Implements:
+ *	- Signal level meter
+ *	- Bandpass filter
+ *	- Modified Julian Date
+ *	- Reverberation effect
+ *	- Hamlib interface
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(UTILITIES_H__3B0BA660_CA63_4344_B3452345D31912__INCLUDED_)
+#define UTILITIES_H__3B0BA660_CA63_4344_B3452345D31912__INCLUDED_
+
+#include "../GlobalDefinitions.h"
+//#include "Settings.h"
+#include "utils/vector.h"
+#include "../matlib/Matlib.h"
+#include <map>
+#include <iostream>
+
+#ifdef HAVE_LIBHAMLIB
+# include <hamlib/rig.h>
+#endif
+
+
+/* Definitions ****************************************************************/
+#define	METER_FLY_BACK					15
+
+/* Classes ********************************************************************/
+/* Signal level meter ------------------------------------------------------- */
+//class CSignalLevelMeter
+//{
+//public:
+//	CSignalLevelMeter() : rCurLevel((_REAL) 0.0) {}
+//	virtual ~CSignalLevelMeter() {}
+
+//	void Init(_REAL rStartVal) {rCurLevel = Abs(rStartVal);}
+//	void Update(const _REAL rVal);
+//	void Update(const CVector<_REAL> vecrVal);
+//	void Update(const CVector<_SAMPLE> vecsVal);
+//	_REAL Level();
+
+//protected:
+//	_REAL rCurLevel;
+//};
+
+
+/* Bandpass filter ---------------------------------------------------------- */
+class CDRMBandpassFilt
+{
+public:
+	enum EFiltType {FT_TRANSMITTER, FT_RECEIVER};
+
+	void Init(const int iNewBlockSize, const _REAL rOffsetHz,
+		const ESpecOcc eSpecOcc, const EFiltType eNFiTy);
+	void Process(CVector<_COMPLEX>& veccData);
+
+protected:
+	int				iBlockSize;
+
+	CComplexVector	cvecDataTmp;
+
+	CRealVector		rvecZReal; /* State memory real part */
+	CRealVector		rvecZImag; /* State memory imaginary part */
+	CRealVector		rvecDataReal;
+	CRealVector		rvecDataImag;
+	CFftPlans		FftPlanBP;
+	CComplexVector	cvecB;
+};
+
+
+/* Modified Julian Date ----------------------------------------------------- */
+class CModJulDate
+{
+public:
+	CModJulDate() : iYear(0), iDay(0), iMonth(0) {}
+	CModJulDate(const uint32_t iModJulDate) {Set(iModJulDate);}
+
+	void Set(const uint32_t iModJulDate);
+
+	int GetYear() {return iYear;}
+	int GetDay() {return iDay;}
+	int GetMonth() {return iMonth;}
+
+protected:
+	int iYear, iDay, iMonth;
+};
+
+
+
+
+struct CHamlib
+{
+	enum ESMeterState {SS_VALID, SS_NOTVALID, SS_TIMEOUT};
+};
+
+
+
+
+#endif // !defined(UTILITIES_H__3B0BA660_CA63_4344_B3452345D31912__INCLUDED_)
diff --git a/qsstv/drmtx/config.h b/qsstv/drmtx/config.h
new file mode 100644
index 0000000..cdec284
--- /dev/null
+++ b/qsstv/drmtx/config.h
@@ -0,0 +1,114 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <fftw3.h> header file. */
+#define HAVE_FFTW3_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `faac' library (-lfaac). */
+/* #undef HAVE_LIBFAAC */
+
+/* Define to 1 if you have the `faad' library (-lfaad). */
+/* #undef HAVE_LIBFAAD */
+
+/* Define if you have Hamlib */
+/* #undef HAVE_LIBHAMLIB */
+
+/* Define to 1 if you have the `pcap' library (-lpcap). */
+/* #undef HAVE_LIBPCAP */
+
+/* Define to 1 if you have the `qwt' library (-lqwt). */
+/* #undef HAVE_LIBQWT */
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+#define HAVE_LIBRT 1
+
+/* Define if you have libsndfile */
+/* #undef HAVE_LIBSNDFILE */
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 0
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have Hamlib >= 1.2.1 */
+/* #undef HAVE_RIG_PARSE_MODE */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "drm"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you want to use the ALSA audio lib */
+#define USE_ALSA 1
+
+/* Define if you want to use the faac library */
+/* #undef USE_FAAC_LIBRARY */
+
+/* Define if you want to use the faad2 library */
+/* #undef USE_FAAD2_LIBRARY */
+
+/* Define if you want to use the jack audio lib */
+/* #undef USE_JACK */
+
+/* Define if you want to use the OSS audio lib */
+/* #undef USE_OSS */
+
+/* Define if you want to use the portaudio audio lib */
+/* #undef USE_PORTAUDIO */
+
+/* Define if you want to use the QT GUI */
+/* #undef USE_QT_GUI */
+
+/* Version number of package */
+#define VERSION "1.12b"
diff --git a/qsstv/drmtx/drmtransmitter.cpp b/qsstv/drmtx/drmtransmitter.cpp
new file mode 100644
index 0000000..751e37b
--- /dev/null
+++ b/qsstv/drmtx/drmtransmitter.cpp
@@ -0,0 +1,158 @@
+#include "drmtransmitter.h"
+#include "configparams.h"
+
+
+// timing table;
+int numTxFrames;
+
+int partTable[BWs][MODES][PROTECTIONS][QAMS]=
+{
+  {
+    {{96,160,240},{67,140,201}},
+    {{49,103,149},{96,160,240}},
+    {{67,112,168},{49,83,124}}
+  },
+  {
+    {{104,174,261},{78,163,235}},
+    {{54,113,163},{104,174,261}},
+    {{78,130,196},{54,90,135}}
+  }
+};
+
+
+drmTransmitter::drmTransmitter()
+{
+  DRMTransmitter=NULL;
+}
+
+drmTransmitter::~drmTransmitter()
+{
+
+}
+
+
+void drmTransmitter::init(QByteArray *ba, QString name, QString format, drmTxParams params)
+{
+  if(DRMTransmitter) delete DRMTransmitter;
+  dataLength=ba->count();
+  DRMTransmitter=new CDRMTransmitter;
+//  int picSize;
+  ERobMode RobMod;
+  ESpecOcc BW ;
+  CParameter::ESymIntMod eNewDepth;
+  ECodScheme eNewScheme;
+  CMSCProtLev eNewMSCPre;
+  CService Service;
+  DRMTransmitter->init_base();
+//  DRMTransmitter.GetSoundOutInterface()->SetDev(nr_devout);
+  DRMTransmitter->GetAudSrcEnc()->ClearPicFileNames();
+  TransmParam = DRMTransmitter->GetParameters();
+//  picSize=ba->size();
+  DRMTransmitter->GetAudSrcEnc()->SetPicFileName(ba,name,format);
+  switch (params.robMode)
+    {
+    case 0 :
+      RobMod = RM_ROBUSTNESS_MODE_A;
+    break;
+    case 1 :
+      RobMod = RM_ROBUSTNESS_MODE_B ;
+    break ;
+    default:
+      RobMod = RM_ROBUSTNESS_MODE_E ;
+    break;
+    }
+  switch (params.bandwith)
+    {
+    case 0 :
+      BW = SO_0;
+    break;
+    default :
+      BW = SO_1 ;
+    break ;
+    }
+  TransmParam->InitCellMapTable(RobMod , BW);
+
+  switch (params.interleaver)
+    {
+    case 0 :
+      eNewDepth = CParameter::SI_LONG;
+    break ;
+    default :
+      eNewDepth = CParameter::SI_SHORT;
+    break;
+    }
+  TransmParam->SetInterleaverDepth(eNewDepth);
+  switch (params.qam)
+    {
+    case 0 :
+      eNewScheme = CS_1_SM ; //4Bit QAM
+    break;
+    case 1 :
+      eNewScheme = CS_2_SM ; //16Bit QAM
+    break ;
+    default :
+      eNewScheme = CS_3_SM; // 64Bit QAM
+    break;
+    }
+  TransmParam->SetMSCCodingScheme(eNewScheme);
+  switch (params.protection)
+    {
+    case 1 :
+      eNewMSCPre.iPartB = 1;  //Norm
+    break;
+    default:
+      eNewMSCPre.iPartB = 0 ; //High
+    break ;
+    }
+  TransmParam->SetMSCProtLev(eNewMSCPre, false);
+  Service.iServiceDescr=0;
+  Service.iServiceID=0;
+  Service.iLanguage=5;
+  Service.strLabel=params.callsign.toLatin1().data();
+  TransmParam->SetServiceParameters(0,Service);
+  DRMTransmitter->Init();
+  // calculate transmision time
+  duration=(double)(numTxFrames)*0.4*1.005; // 1.005 ->some extra time for buffers
+}
+
+void drmTransmitter::start(bool startTx)
+{
+
+  if(startTx)
+    {
+      stopDRM=false;
+      DRMTransmitter->Start();
+    }
+  else
+    {
+      stopDRM=true;
+    }
+}
+
+drmTxParams modeToParams(uint mode)
+{
+  drmTxParams prm;
+  prm.robMode=mode/10000;
+  mode-=(mode/10000)*10000;
+  prm.bandwith=mode/1000;
+  mode-=(mode/1000)*1000;
+  prm.protection=mode/100;
+  mode-=(mode/100)*100;
+  prm.qam=mode/10;
+  prm.interleaver=0;
+  prm.callsign=myCallsign;
+  return prm;
+}
+
+uint paramsToMode(drmTxParams prm)
+{
+  uint mode=1;
+  mode+=prm.robMode*10000;
+  mode+=prm.bandwith*1000;
+  mode+=prm.protection*100;
+  mode+=prm.qam*10;
+  return mode;
+}
+
+
+
diff --git a/qsstv/drmtx/drmtransmitter.h b/qsstv/drmtx/drmtransmitter.h
new file mode 100644
index 0000000..dff7236
--- /dev/null
+++ b/qsstv/drmtx/drmtransmitter.h
@@ -0,0 +1,39 @@
+#ifndef DRMTRANSMITTER_H
+#define DRMTRANSMITTER_H
+
+#include "common/GlobalDefinitions.h"
+#include "common/DrmTransmitter.h"
+
+struct drmTxParams
+{
+  int robMode;
+  int qam;
+  int bandwith;
+  int interleaver;
+  int protection;
+  QString callsign;
+  int reedSolomon;
+};
+
+
+class drmTransmitter
+{
+public:
+  drmTransmitter();
+  ~drmTransmitter();
+  void init(QByteArray *ba, QString name, QString format, drmTxParams params);
+  void start(bool startTx);
+  double getDuration() {return duration;} // expressed in seconds
+  double transmissionTime;
+private:
+  CDRMTransmitter *DRMTransmitter;
+  CParameter* TransmParam ;
+  int dataLength;
+  double duration;
+};
+
+drmTxParams modeToParams(uint mode);
+uint paramsToMode(drmTxParams prm);
+extern int numTxFrames;
+
+#endif // DRMTRANSMITTER_H
diff --git a/qsstv/dsp/downsamplefilter.cpp b/qsstv/dsp/downsamplefilter.cpp
new file mode 100644
index 0000000..d953aa9
--- /dev/null
+++ b/qsstv/dsp/downsamplefilter.cpp
@@ -0,0 +1,146 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "downsamplefilter.h"
+#include "configparams.h"
+#include "utils/supportfunctions.h"
+
+#define VOLINTEGRATOR 0.0001
+
+downsampleFilter::downsampleFilter()
+{
+	filteredDataBuffer=0;
+	filterParams=0;
+	samplesI=0;
+	samplesQ=0;
+	filterLength=0;
+}
+
+downsampleFilter::~downsampleFilter()
+{
+  if(filteredDataBuffer) delete [] filteredDataBuffer;
+  if(filterParams) delete [] filterParams;
+}
+
+downsampleFilter::downsampleFilter(unsigned int len, const FILTERPARAMTYPE *fparam, unsigned int filterLen, bool scaled)
+{
+  filteredDataBuffer=0;
+	filterParams=0;
+	samplesI=0;
+	samplesQ=0;
+	filterLength=0;
+  setFilterParams(fparam,filterLen,scaled);
+  allocate(len);
+  init();
+}
+
+void downsampleFilter::allocate(unsigned int len)
+{
+	length=len;
+	if(filteredDataBuffer) delete [] filteredDataBuffer;
+  filteredDataBuffer=new DSPFLOAT [len];
+}
+
+
+void downsampleFilter::init()
+{
+  unsigned int i;
+  for(i=0;i<filterLength;i++)
+    {
+      samplesI[i]=0;
+      samplesQ[i]=0;
+    }
+  for(i=0;i<length;i++)
+    {
+      filteredDataBuffer[i]=0;
+    }
+  avgVolume=0;
+}
+
+void downsampleFilter::setFilterParams(const FILTERPARAMTYPE *fparam,unsigned filterLen,bool scaled)
+{
+	unsigned int i;
+  if(filterLen!=filterLength)
+    {
+      filterLength=filterLen;
+      if(filterParams) delete [] filterParams;
+      filterParams=new FILTERPARAMTYPE [filterLength];
+      if(samplesI) delete [] samplesI;
+      if(samplesQ) delete [] samplesQ;
+      samplesI=new DSPFLOAT[filterLength];
+      samplesQ=new DSPFLOAT[filterLength];
+    }
+  zeroes=filterLength-1;
+  ssize=(zeroes)*sizeof(DSPFLOAT);
+
+
+  DSPFLOAT gain=0;
+	for(i=0;i<filterLength;i++)
+		{
+      gain+=fparam[i];
+		}
+  for(i=0;i<filterLength;i++)
+    {
+      if(scaled) filterParams[i]=fparam[i]/gain;
+      else filterParams[i]=fparam[i];
+    }
+  addToLog(QString("filtergain:=%1").arg(gain),LOGPERFORM);
+}
+
+
+
+void downsampleFilter::downSample4(short int *data)
+{
+  unsigned int i,k;
+  FILTERPARAMTYPE res;
+  const FILTERPARAMTYPE *cf1;
+  DSPFLOAT *fp1;
+  FILTERPARAMTYPE res0,res1,res2,res3;
+//  int zeroes=filterLength-1;
+  for (k=0;k<length;k+=4)
+    {
+      res0=res1=res2=res3=0;
+      cf1 = filterParams;
+      fp1 = samplesI;
+      memmove(samplesI+4, samplesI, (filterLength-4)*sizeof(DSPFLOAT));
+      samplesI[3]=data[k];
+      samplesI[2]= data[k+1];
+      samplesI[1]= data[k+2];
+      samplesI[0]= data[k+3];
+      for(i=0;i<filterLength;i+=4,fp1+=4,cf1+=4)
+        {
+          res0+=(*fp1)*(*cf1);
+          res1+=(*(fp1+1))*(*(cf1+1));
+          res2+=(*(fp1+2))*(*(cf1+2));
+          res3+=(*(fp1+3))*(*(cf1+3));
+        }
+      res=res0+res1+res2+res3;
+      filteredDataBuffer[k/4]=(DSPFLOAT)res;
+//        filteredDataBuffer[k/4]=samplesI[filterLength/2];
+      avgVolume=avgVolume*(1-VOLINTEGRATOR)+(10*log(res*res)-145)*VOLINTEGRATOR;
+
+    }
+
+}
+
+
+
+
+
diff --git a/qsstv/dsp/downsamplefilter.h b/qsstv/dsp/downsamplefilter.h
new file mode 100644
index 0000000..d4b4bc8
--- /dev/null
+++ b/qsstv/dsp/downsamplefilter.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef DOWNSAMPLEFILTER_H
+#define DOWNSAMPLEFILTER_H
+#include "nco.h"
+#include "filterparam.h"
+
+class downsampleFilter
+{
+public:
+  downsampleFilter();
+  downsampleFilter(unsigned int len, const FILTERPARAMTYPE *fparam, unsigned int filterLen, bool scaled);
+  ~downsampleFilter();
+	void allocate(unsigned int dataLength);
+  void setFilterParams(const FILTERPARAMTYPE *fparam,unsigned int filterLen,bool scaled);
+  void downSample4(short int *data);
+//  void downSample4(DSPFLOAT *data, DSPFLOAT *filteredData, unsigned int len);
+  DSPFLOAT *filteredDataPtr() {return filteredDataBuffer;}
+  void init();
+  DSPFLOAT avgVolume;
+
+private:
+	void normalizeGain();
+  DSPFLOAT *filteredDataBuffer;
+  FILTERPARAMTYPE *filterParams;
+	unsigned int filterLength;
+	DSPFLOAT *samplesI;
+	DSPFLOAT *samplesQ;
+	unsigned int length;
+  int zeroes;
+  int ssize;
+
+};
+
+#endif
diff --git a/qsstv/dsp/filter.cpp b/qsstv/dsp/filter.cpp
new file mode 100644
index 0000000..9acf12a
--- /dev/null
+++ b/qsstv/dsp/filter.cpp
@@ -0,0 +1,241 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "filter.h"
+#include "configparams.h"
+
+
+
+
+
+filter::filter()
+{
+	filteredDataBuffer=0;
+	volumeBuffer=0;
+	filterParams=0;
+	samplesI=0;
+	samplesQ=0;
+	filterLength=0;
+}
+
+filter::~filter()
+{
+  if(filteredDataBuffer) delete [] filteredDataBuffer;
+  if(volumeBuffer) delete [] volumeBuffer;
+  if(filterParams) delete [] filterParams;
+}
+
+filter::filter(unsigned int len, const FILTERPARAMTYPE *fparam, unsigned int filterLen, DSPFLOAT centerFrequency, DSPFLOAT clock, bool scaled, DSPFLOAT volumeIntegrator)
+{
+  filteredDataBuffer=0;
+  volumeBuffer=0;
+	filterParams=0;
+	samplesI=0;
+	samplesQ=0;
+	filterLength=0;
+	allocate(len);
+  setFilterParams(fparam,filterLen,centerFrequency,clock,scaled,volumeIntegrator);
+  resIprev=0;
+  resQprev=0;
+}
+
+void filter::allocate(unsigned int len)
+{
+	length=len;
+	if(filteredDataBuffer) delete [] filteredDataBuffer;
+	if(volumeBuffer) delete [] volumeBuffer;
+  filteredDataBuffer=new int [2*len]; // double the length for complex output in IQ filtering -output complex format
+  volumeBuffer=new int [len];
+}
+
+void filter::setFilterParams(const FILTERPARAMTYPE *fparam,unsigned filterLen,DSPFLOAT centerFrequency,DSPFLOAT clock,bool scaled,DSPFLOAT volumeIntegrator)
+{
+	unsigned int i;
+  volIntegrator=volumeIntegrator;
+  if(filterLen!=filterLength)
+    {
+      filterLength=filterLen;
+      if(filterParams) delete [] filterParams;
+      filterParams=new FILTERPARAMTYPE [filterLength];
+      if(samplesI) delete [] samplesI;
+      if(samplesQ) delete [] samplesQ;
+      samplesI=new DSPFLOAT[filterLength];
+      samplesQ=new DSPFLOAT[filterLength];
+    }
+  zeroes=filterLength-1;
+  ssize=(zeroes)*sizeof(DSPFLOAT);
+  frCenter=centerFrequency;
+  nco.init(frCenter/clock);
+  angleToFc=clock/(2*M_PI);
+
+
+	for(i=0;i<filterLength;i++)
+		{
+			samplesI[i]=0;
+			samplesQ[i]=0;
+		}
+  DSPFLOAT gain=0;
+	for(i=0;i<filterLength;i++)
+		{
+      gain+=fparam[i];
+		}
+  for(i=0;i<filterLength;i++)
+    {
+      if(scaled) filterParams[i]=fparam[i]/gain;
+      else filterParams[i]=fparam[i];
+
+    }
+  avgVolume=0;
+  addToLog(QString("filtergain:=%1").arg(gain),LOGPERFORM);
+}
+
+void filter::processFM(DSPFLOAT *data)
+{
+  FILTERPARAMTYPE resI=0, resQ=0;
+  int  temp;
+
+  const FILTERPARAMTYPE *cf1;
+  DSPFLOAT *fp1, *fp2;
+  FILTERPARAMTYPE discRe,discIm;
+	unsigned int i,k;
+
+
+	for (k=0;k<length;k++)
+		{
+			resI=0;
+			resQ=0;
+			cf1 = filterParams;
+			fp1 = samplesI;
+			fp2 = samplesQ;
+      memmove(samplesI, samplesI+1, ssize);
+      memmove(samplesQ, samplesQ+1, ssize);
+      nco.multiply(samplesI[zeroes],samplesQ[zeroes],data[k]);
+			for(i=0;i<filterLength;i++,fp1++,fp2++,cf1++)
+ 				{
+   				resI+=(*fp1)*(*cf1);
+   				resQ+=(*fp2)*(*cf1);
+ 				}
+			discRe=resI*resIprev+resQ*resQprev;
+  		discIm=-resQ*resIprev+resQprev*resI;
+  		resIprev=resI;
+  		resQprev=resQ;
+  		if(discRe==0) discRe=0.0001;
+      temp=(int)round(frCenter-atan2(discIm,discRe)*angleToFc);
+      if(temp<500) temp=prevTemp;
+      if(temp>2600) temp=prevTemp;
+      prevTemp=temp;
+			filteredDataBuffer[k]=temp;
+      avgVolume=avgVolume*(1-volIntegrator)+sqrt(resI*resI+resQ*resQ)*volIntegrator;
+      volumeBuffer[k]=(int)avgVolume;
+		}
+}
+
+void filter::processIQ(DSPFLOAT *data, DSPFLOAT *output,int len)
+{
+  FILTERPARAMTYPE resQ=0;
+
+  const FILTERPARAMTYPE *cf1;
+  DSPFLOAT *fp1;
+  unsigned int i;
+  int k;
+
+
+  for (k=0;k<len;k++)
+    {
+      resQ=0;
+      cf1 = filterParams;
+      fp1 = samplesI;
+      memmove(samplesI+1, samplesI, ssize); // newest at index 0
+      samplesI[0]=data[k];
+      for(i=0;i<filterLength;i++,fp1++,cf1++)
+        {
+          resQ+=(*fp1)*(*cf1);
+        }
+      output[2*k+1]=samplesI[filterLength/2]; // just delay
+//      output[2*k]=resQ*0.911;
+      output[2*k]=(DSPFLOAT)resQ;
+    }
+}
+
+
+void filter::processAM(DSPFLOAT *data)
+{
+  FILTERPARAMTYPE resI=0, resQ=0;
+  const FILTERPARAMTYPE *cf1;
+	DSPFLOAT *fp1, *fp2 ,amplitude;
+	unsigned int i,k;
+	int zeroes=filterLength-1;
+
+	for (k=0;k<length;k++)
+		{
+			resI=0;
+			resQ=0;
+			cf1 = filterParams;
+			fp1 = samplesI;
+			fp2 = samplesQ;
+			memmove(samplesI, samplesI+1, (zeroes)*sizeof(DSPFLOAT));
+  		memmove(samplesQ, samplesQ+1, (zeroes)*sizeof(DSPFLOAT));
+      nco.multiply(samplesI[zeroes],samplesQ[zeroes],data[k]);
+			for(i=0;i<filterLength;i++,fp1++,fp2++,cf1++)
+ 				{
+   				resI+=(*fp1)*(*cf1);
+   				resQ+=(*fp2)*(*cf1);
+ 				}
+			amplitude=sqrt(resI*resI+resQ*resQ);
+      filteredDataBuffer[k]=2*amplitude;
+		}
+}
+
+void filter::process(DSPFLOAT *data,DSPFLOAT *filteredData,int len)
+{
+  FILTERPARAMTYPE resI=0;
+  const FILTERPARAMTYPE *cf1;
+  DSPFLOAT *fp1;
+  unsigned int i;
+  int k;
+  int zeroes=filterLength-1;
+  for (k=0;k<len;k++)
+    {
+      resI=0;
+      cf1 = filterParams;
+      fp1 = samplesI;
+      memmove(samplesI+1, samplesI, (zeroes)*sizeof(DSPFLOAT));
+      samplesI[0]=data[k];
+      for(i=0;i<filterLength;i++,fp1++,cf1++)
+        {
+          resI+=(*fp1)*(*cf1);
+        };
+      filteredData[k]=(DSPFLOAT)resI;
+    }
+}
+
+
+void filter::delay(DSPFLOAT *data,DSPFLOAT *filteredData,int len)
+{
+  int k;
+  int zeroes=filterLength-1;
+  for (k=0;k<len;k++)
+    {
+      memmove(samplesI, samplesI+1, (zeroes)*sizeof(DSPFLOAT));
+      samplesI[zeroes]=data[k];
+      filteredData[k]=samplesI[filterLength/2];
+    }
+}
+
diff --git a/qsstv/dsp/filter.h b/qsstv/dsp/filter.h
new file mode 100644
index 0000000..d27421f
--- /dev/null
+++ b/qsstv/dsp/filter.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef FILTER_H
+#define FILTER_H
+#include "nco.h"
+#include "filterparam.h"
+
+
+class filter
+{
+public:
+	filter();
+  filter(unsigned int len,const FILTERPARAMTYPE *fparam,unsigned int filterLen,DSPFLOAT centerFrequency,DSPFLOAT clock,bool scaled,DSPFLOAT volumeIntegrator);
+	~filter();
+	void allocate(unsigned int dataLength);
+  void setFilterParams(const FILTERPARAMTYPE *fparam,unsigned int filterLen,DSPFLOAT centerFrequency,DSPFLOAT clock,bool scaled,DSPFLOAT volumeIntegrator);
+  void processFM(DSPFLOAT *data);
+  void processBP(DSPFLOAT *data);
+  void processAM(DSPFLOAT *data);
+  void processIQ(DSPFLOAT *data,DSPFLOAT *output, int len);
+  int *filteredDataPtr() {return filteredDataBuffer;}
+  int *volumePtr() {return volumeBuffer;}
+  void process(DSPFLOAT *data,DSPFLOAT *filteredData,int len);
+  void delay(DSPFLOAT *data,DSPFLOAT *filteredData,int len);
+
+private:
+	void normalizeGain();
+	bool demoluteFM;
+  int *filteredDataBuffer;
+  int *volumeBuffer;
+
+  FILTERPARAMTYPE *filterParams;
+	unsigned int filterLength;
+	DSPFLOAT *samplesI;
+	DSPFLOAT *samplesQ;
+  FILTERPARAMTYPE	resIprev;
+  FILTERPARAMTYPE	resQprev;
+  int prevTemp;
+	unsigned int length;
+	NCO nco;
+	DSPFLOAT frCenter;
+	DSPFLOAT angleToFc;
+  DSPFLOAT holdPrev;
+  DSPFLOAT amplitude;
+  int zeroes;
+  int ssize;
+  DSPFLOAT avgVolume;
+  DSPFLOAT volIntegrator;
+};
+
+#endif
diff --git a/qsstv/dsp/filterparam.cpp b/qsstv/dsp/filterparam.cpp
new file mode 100644
index 0000000..306b43d
--- /dev/null
+++ b/qsstv/dsp/filterparam.cpp
@@ -0,0 +1,3917 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes - ON4QZ                              *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "filterparam.h"
+
+
+
+#ifdef DWSBANDPASS
+// rFIR
+// localSamplingrate = 48000.000000
+// number of taps = 360
+// Band0 Lower=0.000000, Upper=50.000000, Desired=0.000000, Weight=1.000000
+// Band1 Lower=250.000000, Upper=2700.000000, Desired=1.000000, Weight=8.000000
+// Band2 Lower=2900.000000, Upper=24000.000000, Desired=0.000000, Weight=2.000000
+//Coefficients:
+const FILTERPARAMTYPE downSampleFilterParam[DSAMPLEFILTERLEN]=
+{
+  -0.00151718585312103,
+  0.000309665297865725,
+  0.000266770230632739,
+  0.000220107262717653,
+  0.00017069787890033,
+  0.000119558768317707,
+  6.7937235925543e-05,
+  1.73219175796899e-05,
+  -3.04504220475478e-05,
+  -7.32505140153168e-05,
+  -0.000108809984498506,
+  -0.000134931038254037,
+  -0.000149757016582148,
+  -0.000152098343880838,
+  -0.00014173987237634,
+  -0.000119585246451574,
+  -8.77350358596666e-05,
+  -5.09192816488034e-05,
+  -8.16419424981514e-06,
+  2.40226851297579e-05,
+  5.3136899043456e-05,
+  6.99684228232289e-05,
+  6.87817534604667e-05,
+  4.87441013458151e-05,
+  1.16900843058164e-05,
+  -3.89696741104e-05,
+  -9.85468390916148e-05,
+  -0.000161009867428677,
+  -0.000219206981177775,
+  -0.000265621897813199,
+  -0.000293498241691715,
+  -0.000298003921541809,
+  -0.000277186705451158,
+  -0.000232676836925126,
+  -0.000169914967971405,
+  -9.62079979300535e-05,
+  -2.51125513131256e-05,
+  3.69809600262516e-05,
+  7.42043577814656e-05,
+  7.97817373854275e-05,
+  5.02385379851292e-05,
+  -1.57991871271694e-05,
+  -0.000114437287265163,
+  -0.000235897965918837,
+  -0.000366102814052958,
+  -0.000489103613086259,
+  -0.000589210258820819,
+  -0.000652743939524249,
+  -0.000669783890970928,
+  -0.000635975263583453,
+  -0.000553949112513262,
+  -0.000433593492614785,
+  -0.000290988579590895,
+  -0.00014832630995168,
+  -2.59207759511581e-05,
+  5.09331968833574e-05,
+  6.84394379409231e-05,
+  1.57208894447818e-05,
+  -0.000109405205140087,
+  -0.000296897689887665,
+  -0.000527902720342548,
+  -0.000777067132466488,
+  -0.00101453005859221,
+  -0.00120920779134417,
+  -0.001333593463007,
+  -0.00136857191046792,
+  -0.00130679331314755,
+  -0.00115412927772127,
+  -0.000929440598646113,
+  -0.000663135426651631,
+  -0.000394378459673436,
+  -0.000163967367349531,
+  -1.40577113630792e-05,
+  2.71166292762123e-05,
+  -6.07346253858535e-05,
+  -0.000278313442438686,
+  -0.000607429273487369,
+  -0.00101549130993907,
+  -0.00145608565994414,
+  -0.00187440115136921,
+  -0.0022154989505,
+  -0.00243231081057876,
+  -0.00249210523944556,
+  -0.00238167905234111,
+  -0.00211084486700502,
+  -0.00171301174998109,
+  -0.00124206062226084,
+  -0.000765464268534193,
+  -0.000354836981285895,
+  -7.91079665324665e-05,
+  1.09315658115454e-05,
+  -0.000117905256558107,
+  -0.00046606643180625,
+  -0.00100501188496882,
+  -0.00167912001559893,
+  -0.00240829932962692,
+  -0.00310083785299637,
+  -0.00366547762099869,
+  -0.00402294472888868,
+  -0.00411761180003572,
+  -0.0039274117727721,
+  -0.00346911661557856,
+  -0.00279793021625653,
+  -0.00200180220277536,
+  -0.00119111041913143,
+  -0.000485030551997283,
+  3.81268603366552e-06,
+  0.000188967667995569,
+  1.75771088832494e-05,
+  -0.000512433845333963,
+  -0.00135691951675416,
+  -0.00242506976696957,
+  -0.0035879472609761,
+  -0.0046981084325413,
+  -0.00560605758712609,
+  -0.00618109818486136,
+  -0.00633169671974863,
+  -0.00602029294983547,
+  -0.00527087376898266,
+  -0.00416920321248404,
+  -0.00285489367017525,
+  -0.0015052886779112,
+  -0.000313107859445306,
+  0.000539650875676152,
+  0.000908453023733856,
+  0.000704485989099417,
+  -8.34445326883886e-05,
+  -0.00139001723273659,
+  -0.0030720685622835,
+  -0.00492573659293609,
+  -0.00671350908243368,
+  -0.00819040896088137,
+  -0.00913994430276909,
+  -0.0094054510698947,
+  -0.00891440529630932,
+  -0.0076938432246964,
+  -0.00587332975516594,
+  -0.00367330078239809,
+  -0.00138024590473629,
+  0.000688237354624767,
+  0.00222495069867272,
+  0.00297549107050037,
+  0.00277514985073955,
+  0.00158149008174527,
+  -0.000518003816389104,
+  -0.00330261804405544,
+  -0.00644305708217242,
+  -0.00953836573752198,
+  -0.0121613753764521,
+  -0.0139192020967903,
+  -0.014506083994603,
+  -0.01375111070042,
+  -0.0116523257225599,
+  -0.00839011738751365,
+  -0.00431760602103533,
+  7.1663663982476e-05,
+  0.00419702982579153,
+  0.00745975192802291,
+  0.00931939464036898,
+  0.00936943884289081,
+  0.00740391343534496,
+  0.00345877565753137,
+  -0.00216217557912831,
+  -0.00890269263539967,
+  -0.015993306828616,
+  -0.0225185891488447,
+  -0.0275127194995281,
+  -0.0300581664238577,
+  -0.0293928821975884,
+  -0.0250048561965734,
+  -0.016704594395921,
+  -0.00466807376363805,
+  0.0105565992041111,
+  0.0280814505817365,
+  0.0467469851736764,
+  0.0652208053169884,
+  0.0821177670264463,
+  0.0961283100667419,
+  0.106140280278628,
+  0.111351899732022,
+  0.111349938479372,
+  0.106125325195358,
+  0.0961012215899245,
+  0.082083055541495,
+  0.065184044146694,
+  0.0467140460518445,
+  0.0280577028971195,
+  0.0105461819922317,
+  -0.0046628081022343,
+  -0.016683390465914,
+  -0.0249695791797853,
+  -0.0293472523705209,
+  -0.0300072425912863,
+  -0.0274622028497765,
+  -0.022474041565934,
+  -0.0159593912922312,
+  -0.00888254418083573,
+  -0.00215697336191281,
+  0.00344995890282234,
+  0.00738397867095158,
+  0.00934286593093756,
+  0.00929162187785759,
+  0.00743644458315651,
+  0.00418330943585274,
+  7.14189972924584e-05,
+  -0.0043022374712043,
+  -0.00835902931430361,
+  -0.0116074461548097,
+  -0.0136961306275393,
+  -0.0144459510742554,
+  -0.0138594474412668,
+  -0.0121073658888651,
+  -0.00949458766305608,
+  -0.00641252452164234,
+  -0.0032864730910068,
+  -0.000515393696874395,
+  0.00157328267532868,
+  0.00276032738480899,
+  0.00295914596667793,
+  0.00221238862228927,
+  0.000684245970120732,
+  -0.00137202854072664,
+  -0.00365086284841068,
+  -0.00583653949343313,
+  -0.00764444661708083,
+  -0.00885577187143843,
+  -0.00934210271690931,
+  -0.0090769334929006,
+  -0.00813263733184636,
+  -0.00666507779776451,
+  -0.0048894076705026,
+  -0.00304891264847757,
+  -0.00137931307597921,
+  -8.27882523334948e-05,
+  0.000698828976253759,
+  0.000901007241309603,
+  0.000535137629629946,
+  -0.000310436583942355,
+  -0.0014921914829033,
+  -0.00282956735303828,
+  -0.00413150220327286,
+  -0.0052223003458195,
+  -0.00596376643057263,
+  -0.00627113762676166,
+  -0.00612088948511199,
+  -0.00555045453310577,
+  -0.00465067025277631,
+  -0.00355107205618392,
+  -0.00239970572091919,
+  -0.00134247910335008,
+  -0.00050688602184144,
+  1.73835459454169e-05,
+  0.000186851328593862,
+  3.76926627593299e-06,
+  -0.000479414621359702,
+  -0.00117709065000749,
+  -0.00197785315902698,
+  -0.00276391086548602,
+  -0.00342625426272416,
+  -0.0038781081141346,
+  -0.00406509683686583,
+  -0.0039708255367144,
+  -0.00361724368824401,
+  -0.0030593974778865,
+  -0.00237561549230207,
+  -0.00165598131126649,
+  -0.000990950721816176,
+  -0.000459446555962234,
+  -0.000116205262140052,
+  1.07715840793517e-05,
+  -7.79329491288675e-05,
+  -0.000349488231502234,
+  -0.000753755485340556,
+  -0.00122278280128855,
+  -0.00168603625864065,
+  -0.00207712192979598,
+  -0.00234307978201088,
+  -0.0024511361597515,
+  -0.00239175337853265,
+  -0.0021780316539496,
+  -0.0018422541194682,
+  -0.00143076173464824,
+  -0.000997583010841567,
+  -0.000596568116623263,
+  -0.000273268167415817,
+  -5.96184688626525e-05,
+  2.66114681243663e-05,
+  -1.37922622490626e-05,
+  -0.000160829296086387,
+  -0.000386729139522771,
+  -0.000650101333555018,
+  -0.000910929474657119,
+  -0.00113083975447844,
+  -0.00128007745727489,
+  -0.00134022893970869,
+  -0.00130561817736569,
+  -0.00118351678315544,
+  -0.000992701356832596,
+  -0.000760137188210536,
+  -0.00051625788278049,
+  -0.000290267675160951,
+  -0.000106932250570691,
+  1.53612523294125e-05,
+  6.68551362116988e-05,
+  4.97402887713845e-05,
+  -2.53066542354481e-05,
+  -0.000144772078583238,
+  -0.000283937761679862,
+  -0.00042297186175046,
+  -0.000540233180356209,
+  -0.000620062798522562,
+  -0.00065285375357642,
+  -0.000636080094916979,
+  -0.000574023057935788,
+  -0.000476379090176037,
+  -0.000356492807082752,
+  -0.000229652579895355,
+  -0.000111382801539256,
+  -1.53742280913752e-05,
+  4.8877464993403e-05,
+  7.7605767545448e-05,
+  7.216807749654e-05,
+  3.5960542057589e-05,
+  -2.44162570500983e-05,
+  -9.35294421380662e-05,
+  -0.000165168538352502,
+  -0.000226161106310923,
+  -0.00026941255178708,
+  -0.000289641461254719,
+  -0.000285266901460065,
+  -0.000258185533558317,
+  -0.000213088908986053,
+  -0.000156536278671508,
+  -9.58253134180074e-05,
+  -3.79017941126775e-05,
+  1.13728141318785e-05,
+  4.74365397903835e-05,
+  6.69621860673353e-05,
+  6.81475404150919e-05,
+  5.17802014823365e-05,
+  2.34227340762627e-05,
+  -7.96541204893464e-06,
+  -4.97150185953007e-05,
+  -8.5727741580426e-05,
+  -0.000116950564089072,
+  -0.00013874814150351,
+  -0.000149040545002216,
+  -0.000146908318755611,
+  -0.000132520947558629,
+  -0.00010700115323508,
+  -7.21290149150481e-05,
+  -3.00264108515757e-05,
+  1.71059107790105e-05,
+  6.7193203421747e-05,
+  0.000118437876786714,
+  0.000169375448704034,
+  0.000218770083443505,
+  0.000265605181906194,
+  0.000308850143176507,
+  -0.0189481367078439
+};
+#else
+// rFIR
+// localSamplingrate = 48000.000000
+// number of taps = 181
+// Band0 Lower=0.000000, Upper=2800.000000, Desired=2.000000, Weight=1.000000
+// Band1 Lower=3500.000000, Upper=24000.000000, Desired=0.000000, Weight=10.000000
+//Coefficients:
+ const FILTERPARAMTYPE downSampleFilterParam[DSAMPLEFILTERLEN]=
+{
+   -0.000726805371518309,
+   0.00212924341308074,
+   0.00209607515518649,
+   0.00258616863859503,
+   0.00312451941666017,
+   0.00351845737963003,
+   0.00365705393269608,
+   0.00347249738871369,
+   0.00294091277303658,
+   0.0020876784816062,
+   0.000989968644394825,
+   -0.000229536963193281,
+   -0.00141602838562286,
+   -0.00240355817861836,
+   -0.00303972686902264,
+   -0.00321109351395581,
+   -0.00286490657890866,
+   -0.00202335724664101,
+   -0.000787353876891696,
+   0.000672176418542286,
+   0.00213509217135987,
+   0.00336342008182743,
+   0.00413819652648567,
+   0.00429713243771618,
+   0.00376510708747164,
+   0.00257360789092004,
+   0.000863764615466942,
+   -0.001127792766604,
+   -0.00309663047609688,
+   -0.00472159091592147,
+   -0.0056999360876337,
+   -0.00583031339786136,
+   -0.00501710522705826,
+   -0.00331719263251476,
+   -0.000939525687925027,
+   0.00178104445626093,
+   0.00442648054106535,
+   0.00655781811862848,
+   0.00778470951795523,
+   0.00783457506289861,
+   0.00660582869791214,
+   0.00419854005559208,
+   0.000913611071336271,
+   -0.00278058458658644,
+   -0.00631166219807734,
+   -0.00908863549508422,
+   -0.0105968380602393,
+   -0.0104880827011458,
+   -0.00865059863609764,
+   -0.00524633117469452,
+   -0.000706169042697737,
+   0.00431853795460012,
+   0.00904473602072652,
+   0.0126740048689635,
+   0.0145210513202929,
+   0.0141339163209834,
+   0.0113860335748772,
+   0.00652568850180913,
+   0.000164013828194059,
+   -0.00678721495092159,
+   -0.0132450608783663,
+   -0.0181056223037124,
+   -0.0204244364948377,
+   -0.0195817855693473,
+   -0.0154088583976291,
+   -0.00825851456253797,
+   0.00100265183405794,
+   0.0110798178134256,
+   0.0204127669048817,
+   0.0273919133377699,
+   0.0306039642231267,
+   0.0290707628785015,
+   0.0224466971136873,
+   0.0111424375360727,
+   -0.00364878906373264,
+   -0.0200329664559621,
+   -0.0356023553816112,
+   -0.0477088371694707,
+   -0.053792701556172,
+   -0.0517250893361419,
+   -0.0401198095407566,
+   -0.0185705541637443,
+   0.0122205143308298,
+   0.0504455099076038,
+   0.093330253141237,
+   0.13739759113622,
+   0.178828272483524,
+   0.213871703505128,
+   0.239265440205684,
+   0.252600612879519,
+   0.252600612879519,
+   0.239265440205684,
+   0.213871703505128,
+   0.178828272483524,
+   0.13739759113622,
+   0.093330253141237,
+   0.0504455099076038,
+   0.0122205143308298,
+   -0.0185705541637443,
+   -0.0401198095407566,
+   -0.0517250893361419,
+   -0.053792701556172,
+   -0.0477088371694707,
+   -0.0356023553816112,
+   -0.0200329664559621,
+   -0.00364878906373264,
+   0.0111424375360727,
+   0.0224466971136873,
+   0.0290707628785015,
+   0.0306039642231267,
+   0.0273919133377699,
+   0.0204127669048817,
+   0.0110798178134256,
+   0.00100265183405794,
+   -0.00825851456253797,
+   -0.0154088583976291,
+   -0.0195817855693473,
+   -0.0204244364948377,
+   -0.0181056223037124,
+   -0.0132450608783663,
+   -0.00678721495092159,
+   0.000164013828194059,
+   0.00652568850180913,
+   0.0113860335748772,
+   0.0141339163209834,
+   0.0145210513202929,
+   0.0126740048689635,
+   0.00904473602072652,
+   0.00431853795460012,
+   -0.000706169042697737,
+   -0.00524633117469452,
+   -0.00865059863609764,
+   -0.0104880827011458,
+   -0.0105968380602393,
+   -0.00908863549508422,
+   -0.00631166219807734,
+   -0.00278058458658644,
+   0.000913611071336271,
+   0.00419854005559208,
+   0.00660582869791214,
+   0.00783457506289861,
+   0.00778470951795523,
+   0.00655781811862848,
+   0.00442648054106535,
+   0.00178104445626093,
+   -0.000939525687925027,
+   -0.00331719263251476,
+   -0.00501710522705826,
+   -0.00583031339786136,
+   -0.0056999360876337,
+   -0.00472159091592147,
+   -0.00309663047609688,
+   -0.001127792766604,
+   0.000863764615466942,
+   0.00257360789092004,
+   0.00376510708747164,
+   0.00429713243771618,
+   0.00413819652648567,
+   0.00336342008182743,
+   0.00213509217135987,
+   0.000672176418542286,
+   -0.000787353876891696,
+   -0.00202335724664101,
+   -0.00286490657890866,
+   -0.00321109351395581,
+   -0.00303972686902264,
+   -0.00240355817861836,
+   -0.00141602838562286,
+   -0.000229536963193281,
+   0.000989968644394825,
+   0.0020876784816062,
+   0.00294091277303658,
+   0.00347249738871369,
+   0.00365705393269608,
+   0.00351845737963003,
+   0.00312451941666017,
+   0.00258616863859503,
+   0.00209607515518649,
+   0.00212924341308074,
+   -0.000726805371518309
+};
+
+
+#endif
+
+
+/*
+
+FIR filter designed with
+http://t-filter.appspot.com
+
+sampling frequency: 48000 Hz
+
+* 0 Hz - 12000 Hz
+  gain = 1
+  desired ripple = 5 dB
+  actual ripple = 0.010064305090401647 dB
+
+* 14000 Hz - 24000 Hz
+  gain = 0
+  desired attenuation = -40 dB
+  actual attenuation = -82.0306697301112 dB
+
+*/
+
+//#define FILTER_TAP_NUM 120
+
+//const FILTERPARAMTYPE downSampleFilterParam[DSFILTERLEN]=
+//{
+//  -0.00001434671700912554,
+//  -0.000020562134651605815,
+//  0.00001876751754297483,
+//  0.000026445175446795552,
+//  -0.00006030588399278963,
+//  -0.00005828746852741699,
+//  0.00009853296830508344,
+//  0.00005931095017311163,
+//  -0.00019046483652338048,
+//  -0.0000701660667147503,
+//  0.0002856339556184428,
+//  0.000019827822393580995,
+//  -0.0004370075921714892,
+//  0.00005539702740416332,
+//  0.0005788569954831205,
+//  -0.00023331653626705516,
+//  -0.0007478536043794683,
+//  0.00047687246116682845,
+//  0.000857539467749795,
+//  -0.0008559863070902277,
+//  -0.0009222988328700723,
+//  0.001316670320186741,
+//  0.0008349709365650639,
+//  -0.0019017785484652815,
+//  -0.0005943042771593648,
+//  0.002521346756232543,
+//  0.00008790552871839043,
+//  -0.0031717280255955595,
+//  0.0006802888305853671,
+//  0.0037148625674325287,
+//  -0.0017975574392839607,
+//  -0.004094961253668418,
+//  0.003219684850878581,
+//  0.004129426138899979,
+//  -0.004972984552710972,
+//  -0.0037259328148558325,
+//  0.006937428069747955,
+//  0.002682000096803343,
+//  -0.009048195500263277,
+//  -0.0009009328077472075,
+//  0.011087098217581621,
+//  -0.0018057790712661997,
+//  -0.012884014811956659,
+//  0.005512716439276536,
+//  0.014111394052560285,
+//  -0.01038993718341453,
+//  -0.0144745570870615,
+//  0.016529612471778678,
+//  0.013477274536340088,
+//  -0.0242188112250576,
+//  -0.010528902562796134,
+//  0.03391653395396962,
+//  0.004470587562008853,
+//  -0.046963243031028244,
+//  0.007126082872329679,
+//  0.06721942081260658,
+//  -0.032209836572452265,
+//  -0.11187872022628036,
+//  0.12057492071696543,
+//  0.47614828001783693,
+//  0.47614828001783693,
+//  0.12057492071696543,
+//  -0.11187872022628036,
+//  -0.032209836572452265,
+//  0.06721942081260658,
+//  0.007126082872329679,
+//  -0.046963243031028244,
+//  0.004470587562008853,
+//  0.03391653395396962,
+//  -0.010528902562796134,
+//  -0.0242188112250576,
+//  0.013477274536340088,
+//  0.016529612471778678,
+//  -0.0144745570870615,
+//  -0.01038993718341453,
+//  0.014111394052560285,
+//  0.005512716439276536,
+//  -0.012884014811956659,
+//  -0.0018057790712661997,
+//  0.011087098217581621,
+//  -0.0009009328077472075,
+//  -0.009048195500263277,
+//  0.002682000096803343,
+//  0.006937428069747955,
+//  -0.0037259328148558325,
+//  -0.004972984552710972,
+//  0.004129426138899979,
+//  0.003219684850878581,
+//  -0.004094961253668418,
+//  -0.0017975574392839607,
+//  0.0037148625674325287,
+//  0.0006802888305853671,
+//  -0.0031717280255955595,
+//  0.00008790552871839043,
+//  0.002521346756232543,
+//  -0.0005943042771593648,
+//  -0.0019017785484652815,
+//  0.0008349709365650639,
+//  0.001316670320186741,
+//  -0.0009222988328700723,
+//  -0.0008559863070902277,
+//  0.000857539467749795,
+//  0.00047687246116682845,
+//  -0.0007478536043794683,
+//  -0.00023331653626705516,
+//  0.0005788569954831205,
+//  0.00005539702740416332,
+//  -0.0004370075921714892,
+//  0.000019827822393580995,
+//  0.0002856339556184428,
+//  -0.0000701660667147503,
+//  -0.00019046483652338048,
+//  0.00005931095017311163,
+//  0.00009853296830508344,
+//  -0.00005828746852741699,
+//  -0.00006030588399278963,
+//  0.000026445175446795552,
+//  0.00001876751754297483,
+//  -0.000020562134651605815,
+//  -0.00001434671700912554
+//};
+
+
+//// TFilter design
+//const DSPFLOAT downSampleFilterParam[DSFILTERLEN]=
+//{
+//-0.00031921241370338495,
+//0.0004949421700044819,
+//0.0013822939416138311,
+//0.0024512816581009097,
+//0.0031354286516971307,
+//0.0029158928250820424,
+//0.001661027141044636,
+//-0.00017924772122938603,
+//-0.001718399835583406,
+//-0.0021065617689579345,
+//-0.001080218804964031,
+//0.0007741897002425456,
+//0.0022803276925760932,
+//0.002370490501567989,
+//0.0008162184718294901,
+//-0.0015082994928116908,
+//-0.0030838686559974122,
+//-0.002712838485381115,
+//-0.00039730012077191765,
+//0.002503293797487666,
+//0.004036292913059631,
+//0.002948773728902641,
+//-0.0003454610920105798,
+//-0.0038302916183060387,
+//-0.005089732524335926,
+//-0.00295182534778405,
+//0.0015276238855431493,
+//0.005514356523235612,
+//0.006151496449877064,
+//0.0025611667129678915,
+//-0.0032836542253722617,
+//-0.007575527265201513,
+//-0.0071042856041813535,
+//-0.0015834913875678463,
+//0.005769176822509418,
+//0.010029690861770824,
+//0.007793764701402249,
+//-0.00023664441998894463,
+//-0.009197251927194018,
+//-0.012903337373983267,
+//-0.007999288652642442,
+//0.003296528685942004,
+//0.013945110147450536,
+//0.016304461562662808,
+//0.007384602945044693,
+//-0.008336298315850418,
+//-0.020829917188221893,
+//-0.020577775461447054,
+//-0.005292943361467393,
+//0.017139781366637633,
+//0.032108329389068505,
+//0.026911188696776438,
+//-0.0001361030874071249,
+//-0.03584756267942592,
+//-0.056825628460263636,
+//-0.04112767228986162,
+//0.01893335449923753,
+//0.11004116992869253,
+//0.2015954251233985,
+//0.25880099591048017,
+//0.25880099591048017,
+//0.2015954251233985,
+//0.11004116992869253,
+//0.01893335449923753,
+//-0.04112767228986162,
+//-0.056825628460263636,
+//-0.03584756267942592,
+//-0.0001361030874071249,
+//0.026911188696776438,
+//0.032108329389068505,
+//0.017139781366637633,
+//-0.005292943361467393,
+//-0.020577775461447054,
+//-0.020829917188221893,
+//-0.008336298315850418,
+//0.007384602945044693,
+//0.016304461562662808,
+//0.013945110147450536,
+//0.003296528685942004,
+//-0.007999288652642442,
+//-0.012903337373983267,
+//-0.009197251927194018,
+//-0.00023664441998894463,
+//0.007793764701402249,
+//0.010029690861770824,
+//0.005769176822509418,
+//-0.0015834913875678463,
+//-0.0071042856041813535,
+//-0.007575527265201513,
+//-0.0032836542253722617,
+//0.0025611667129678915,
+//0.006151496449877064,
+//0.005514356523235612,
+//0.0015276238855431493,
+//-0.00295182534778405,
+//-0.005089732524335926,
+//-0.0038302916183060387,
+//-0.0003454610920105798,
+//0.002948773728902641,
+//0.004036292913059631,
+//0.002503293797487666,
+//-0.00039730012077191765,
+//-0.002712838485381115,
+//-0.0030838686559974122,
+//-0.0015082994928116908,
+//0.0008162184718294901,
+//0.002370490501567989,
+//0.0022803276925760932,
+//0.0007741897002425456,
+//-0.001080218804964031,
+//-0.0021065617689579345,
+//-0.001718399835583406,
+//-0.00017924772122938603,
+//0.001661027141044636,
+//0.0029158928250820424,
+//0.0031354286516971307,
+//0.0024512816581009097,
+//0.0013822939416138311,
+//0.0004949421700044819,
+//-0.00031921241370338495,
+//};
+
+// blFIR  Raised cosine
+// localSamplingrate = 48000.000000
+// number of taps = 120
+// corner frequency = 8000.000000
+// beta = 0.700000
+// window applied
+//Coefficients:
+
+//const DSPFLOAT downSampleFilterParam[DSFILTERLEN]=
+//{
+//    1.10951e-06,
+//    1.68217e-07,
+//    -3.53568e-11,
+//    1.91102e-06,
+//    1.89927e-06,
+//    2.59551e-11,
+//    1.15409e-06,
+//    2.97637e-06,
+//    9.28531e-11,
+//    -2.1707e-06,
+//    1.04504e-06,
+//    9.87304e-11,
+//    -6.71334e-06,
+//    -5.23393e-06,
+//    -4.59518e-12,
+//    -7.0149e-06,
+//    -1.20569e-05,
+//    -1.65625e-10,
+//    3.36226e-06,
+//    -9.32791e-06,
+//    -2.32704e-10,
+//    2.26407e-05,
+//    1.16112e-05,
+//    -8.50774e-11,
+//    3.2786e-05,
+//    4.26511e-05,
+//    2.10748e-10,
+//    6.05424e-06,
+//    4.96652e-05,
+//    4.01764e-10,
+//    -6.63556e-05,
+//    -1.08136e-05,
+//    2.6107e-10,
+//    -0.000136682,
+//    -0.000141189,
+//    -1.63979e-10,
+//    -9.11534e-05,
+//    -0.00024146,
+//    -5.30812e-10,
+//    0.000186548,
+//    -9.29759e-05,
+//    -4.81928e-10,
+//    0.000653162,
+//    0.000539686,
+//    6.14632e-12,
+//    0.000842028,
+//    0.0015963,
+//    5.48689e-10,
+//    -0.0005741,
+//    0.00187732,
+//    6.65763e-10,
+//    -0.00703948,
+//    -0.00483539,
+//    2.2005e-10,
+//    -0.0318124,
+//    -0.0807611,
+//    -4.31271e-10,
+//    0.334861,
+//    0.785553,
+//          1,
+//    0.785553,
+//    0.334861,
+//    -4.31271e-10,
+//    -0.0807611,
+//    -0.0318124,
+//    2.20049e-10,
+//    -0.00483539,
+//    -0.00703948,
+//    6.65763e-10,
+//    0.00187732,
+//    -0.0005741,
+//    5.48689e-10,
+//    0.0015963,
+//    0.000842028,
+//    6.14623e-12,
+//    0.000539686,
+//    0.000653162,
+//    -4.81928e-10,
+//    -9.29759e-05,
+//    0.000186548,
+//    -5.30812e-10,
+//    -0.00024146,
+//    -9.11534e-05,
+//    -1.63979e-10,
+//    -0.000141189,
+//    -0.000136682,
+//    2.6107e-10,
+//    -1.08136e-05,
+//    -6.63556e-05,
+//    4.01764e-10,
+//    4.96652e-05,
+//    6.05424e-06,
+//    2.10748e-10,
+//    4.26511e-05,
+//    3.2786e-05,
+//    -8.50774e-11,
+//    1.16112e-05,
+//    2.26407e-05,
+//    -2.32704e-10,
+//    -9.32791e-06,
+//    3.36226e-06,
+//    -1.65625e-10,
+//    -1.20569e-05,
+//    -7.0149e-06,
+//    -4.59518e-12,
+//    -5.23393e-06,
+//    -6.71334e-06,
+//    9.87304e-11,
+//    1.04504e-06,
+//    -2.1707e-06,
+//    9.28531e-11,
+//    2.97637e-06,
+//    1.15409e-06,
+//    2.59551e-11,
+//    1.89927e-06,
+//    1.91102e-06,
+//    -3.53568e-11,
+//    1.68217e-07,
+//    1.10951e-06,
+//    -7.38792e-10
+//};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // downSampleFilter filter
+// rFIR
+// localSamplingrate = 48000.000000
+// number of taps = 120
+// Band0 Lower=0.000000, Upper=3000.000000, Desired=1.000000, Weight=1.000000
+// Band1 Lower=4000.000000, Upper=6500.020000, Desired=0.000000, Weight=1.000000
+// Band2 Lower=7000.000000, Upper=23999.000000, Desired=0.000000, Weight=1.000000
+//Coefficients:
+
+
+//const DSPFLOAT downSampleFilterParam[DSFILTERLEN]=
+//{
+//   0.000302984,
+//   0.00251395,
+//   0.000720718,
+//   0.000752883,
+//   0.000313521,
+//   -0.000239004,
+//   -0.00082934,
+//   -0.00132562,
+//   -0.00160754,
+//   -0.00158137,
+//   -0.00119945,
+//   -0.000480743,
+//   0.000478348,
+//   0.00149955,
+//   0.00235235,
+//   0.00279834,
+//   0.00266094,
+//   0.00188006,
+//   0.00054949,
+//   -0.00109067,
+//   -0.00270158,
+//   -0.00391293,
+//   -0.00440711,
+//   -0.00398884,
+//   -0.00263551,
+//   -0.000521475,
+//   0.00199194,
+//   0.00440711,
+//   0.00617188,
+//   0.00679615,
+//   0.00597685,
+//   0.00369732,
+//   0.000277493,
+//   -0.00365566,
+//   -0.00727899,
+//   -0.00974173,
+//   -0.0103496,
+//   -0.00873256,
+//   -0.00495604,
+//   0.000443261,
+//   0.00651177,
+//   0.0120219,
+//   0.0156842,
+//   0.0164019,
+//   0.0135302,
+//   0.00709349,
+//   -0.00211602,
+//   -0.0125803,
+//   -0.0222385,
+//   -0.0288071,
+//   -0.0301798,
+//   -0.0248006,
+//   -0.0119883,
+//   0.00786467,
+//   0.0332831,
+//   0.0618213,
+//   0.0903879,
+//   0.115644,
+//    0.1345,
+//   0.144575,
+//   0.144575,
+//    0.1345,
+//   0.115644,
+//   0.0903879,
+//   0.0618213,
+//   0.0332831,
+//   0.00786467,
+//   -0.0119883,
+//   -0.0248006,
+//   -0.0301798,
+//   -0.0288071,
+//   -0.0222385,
+//   -0.0125803,
+//   -0.00211602,
+//   0.00709349,
+//   0.0135302,
+//   0.0164019,
+//   0.0156842,
+//   0.0120219,
+//   0.00651177,
+//   0.000443261,
+//   -0.00495604,
+//   -0.00873256,
+//   -0.0103496,
+//   -0.00974173,
+//   -0.00727899,
+//   -0.00365566,
+//   0.000277493,
+//   0.00369732,
+//   0.00597685,
+//   0.00679615,
+//   0.00617188,
+//   0.00440711,
+//   0.00199194,
+//   -0.000521475,
+//   -0.00263551,
+//   -0.00398884,
+//   -0.00440711,
+//   -0.00391293,
+//   -0.00270158,
+//   -0.00109067,
+//   0.00054949,
+//   0.00188006,
+//   0.00266094,
+//   0.00279834,
+//   0.00235235,
+//   0.00149955,
+//   0.000478348,
+//   -0.000480743,
+//   -0.00119945,
+//   -0.00158137,
+//   -0.00160754,
+//   -0.00132562,
+//   -0.00082934,
+//   -0.000239004,
+//   0.000313521,
+//   0.000752883,
+//   0.000720718,
+//   0.00251395,
+//   0.000302984
+//};
+
+
+ // rFIR
+ // localSamplingrate = 12000.000000
+ // number of taps = 251
+ // Band0 Lower=0.000000, Upper=550.000000, Desired=1.000000, Weight=1.000000
+ // Band1 Lower=1050.000000, Upper=5999.000000, Desired=0.000000, Weight=1.000000
+ //Coefficients:
+ const FILTERPARAMTYPE wide600Hz[RXNUMTAPS]=
+ {
+    1.02116e-08,
+    1.86956e-08,
+    2.95997e-08,
+    3.67411e-08,
+    3.28803e-08,
+    9.86893e-09,
+    -3.85738e-08,
+    -1.13046e-07,
+    -2.04568e-07,
+    -2.91967e-07,
+    -3.42147e-07,
+    -3.14635e-07,
+    -1.71071e-07,
+    1.1124e-07,
+    5.23967e-07,
+    1.01483e-06,
+    1.48207e-06,
+    1.78127e-06,
+    1.74781e-06,
+    1.23487e-06,
+    1.62235e-07,
+    -1.43354e-06,
+    -3.3612e-06,
+    -5.2643e-06,
+    -6.65105e-06,
+    -6.97116e-06,
+    -5.73598e-06,
+    -2.66528e-06,
+    2.16834e-06,
+    8.23547e-06,
+    1.45259e-05,
+    1.96364e-05,
+    2.19833e-05,
+    2.01253e-05,
+    1.31516e-05,
+    1.0579e-06,
+    -1.4981e-05,
+    -3.25401e-05,
+    -4.81454e-05,
+    -5.77759e-05,
+    -5.76296e-05,
+    -4.5044e-05,
+    -1.93995e-05,
+    1.72081e-05,
+    5.97375e-05,
+    0.000100575,
+    0.000130573,
+    0.000140686,
+    0.000123958,
+    7.75329e-05,
+    4.21222e-06,
+    -8.68583e-05,
+    -0.000180729,
+    -0.000258554,
+    -0.000300753,
+    -0.000290988,
+    -0.000220288,
+    -9.0445e-05,
+    8.41753e-05,
+    0.000277112,
+    0.000452921,
+    0.000572765,
+    0.000601767,
+    0.000516923,
+    0.000314004,
+    1.18484e-05,
+    -0.000347327,
+    -0.000702346,
+    -0.00098214,
+    -0.00111838,
+    -0.00105967,
+    -0.000784711,
+    -0.000311538,
+    0.000299689,
+    0.000951857,
+    0.00152431,
+    0.00189303,
+    0.00195464,
+    0.00164989,
+    0.000981973,
+    2.52672e-05,
+    -0.00107873,
+    -0.0021392,
+    -0.00294598,
+    -0.00330765,
+    -0.00309123,
+    -0.00225613,
+    -0.000874798,
+    0.000865638,
+    0.00268356,
+    0.00424397,
+    0.00521526,
+    0.00533284,
+    0.00445837,
+    0.0026227,
+    4.24788e-05,
+    -0.00289622,
+    -0.00568812,
+    -0.00778773,
+    -0.00870587,
+    -0.00810812,
+    -0.00589723,
+    -0.00226255,
+    0.00231648,
+    0.00711898,
+    0.0112808,
+    0.0139308,
+    0.0143459,
+    0.0121003,
+    0.00718252,
+    5.75686e-05,
+    -0.00834188,
+    -0.0167013,
+    -0.0234799,
+    -0.0271187,
+    -0.0262714,
+    -0.0200251,
+    -0.00807634,
+    0.00916805,
+    0.0305845,
+    0.0544271,
+    0.0785142,
+    0.100484,
+    0.118085,
+    0.129463,
+    0.133397,
+    0.129463,
+    0.118085,
+    0.100484,
+    0.0785142,
+    0.0544271,
+    0.0305845,
+    0.00916805,
+    -0.00807634,
+    -0.0200251,
+    -0.0262714,
+    -0.0271187,
+    -0.0234799,
+    -0.0167013,
+    -0.00834188,
+    5.75686e-05,
+    0.00718252,
+    0.0121003,
+    0.0143459,
+    0.0139308,
+    0.0112808,
+    0.00711898,
+    0.00231648,
+    -0.00226255,
+    -0.00589723,
+    -0.00810812,
+    -0.00870587,
+    -0.00778773,
+    -0.00568812,
+    -0.00289622,
+    4.24788e-05,
+    0.0026227,
+    0.00445837,
+    0.00533284,
+    0.00521526,
+    0.00424397,
+    0.00268356,
+    0.000865638,
+    -0.000874798,
+    -0.00225613,
+    -0.00309123,
+    -0.00330765,
+    -0.00294598,
+    -0.0021392,
+    -0.00107873,
+    2.52672e-05,
+    0.000981973,
+    0.00164989,
+    0.00195464,
+    0.00189303,
+    0.00152431,
+    0.000951857,
+    0.000299689,
+    -0.000311538,
+    -0.000784711,
+    -0.00105967,
+    -0.00111838,
+    -0.00098214,
+    -0.000702346,
+    -0.000347327,
+    1.18484e-05,
+    0.000314004,
+    0.000516923,
+    0.000601767,
+    0.000572765,
+    0.000452921,
+    0.000277112,
+    8.41753e-05,
+    -9.0445e-05,
+    -0.000220288,
+    -0.000290988,
+    -0.000300753,
+    -0.000258554,
+    -0.000180729,
+    -8.68583e-05,
+    4.21222e-06,
+    7.75329e-05,
+    0.000123958,
+    0.000140686,
+    0.000130573,
+    0.000100575,
+    5.97375e-05,
+    1.72081e-05,
+    -1.93995e-05,
+    -4.5044e-05,
+    -5.76296e-05,
+    -5.77759e-05,
+    -4.81454e-05,
+    -3.25401e-05,
+    -1.4981e-05,
+    1.0579e-06,
+    1.31516e-05,
+    2.01253e-05,
+    2.19833e-05,
+    1.96364e-05,
+    1.45259e-05,
+    8.23547e-06,
+    2.16834e-06,
+    -2.66528e-06,
+    -5.73598e-06,
+    -6.97116e-06,
+    -6.65105e-06,
+    -5.2643e-06,
+    -3.3612e-06,
+    -1.43354e-06,
+    1.62235e-07,
+    1.23487e-06,
+    1.74781e-06,
+    1.78127e-06,
+    1.48207e-06,
+    1.01483e-06,
+    5.23967e-07,
+    1.1124e-07,
+    -1.71071e-07,
+    -3.14635e-07,
+    -3.42147e-07,
+    -2.91967e-07,
+    -2.04568e-07,
+    -1.13046e-07,
+    -3.85738e-08,
+    9.86893e-09,
+    3.28803e-08,
+    3.67411e-08,
+    2.95997e-08,
+    1.86956e-08,
+    1.02116e-08
+ };
+
+
+
+ // blFIR  Root Raised Cosine
+ // localSamplingrate = 12000.000000
+ // number of taps = 251
+ // corner frequency = 175.000000
+ // beta = 0.600000
+ // window applied
+ //Coefficients:
+ const  FILTERPARAMTYPE wide1200BP[RXNUMTAPS]=
+ {
+    -0.000609128,
+    -0.000552292,
+    -0.000480985,
+    -0.000395195,
+    -0.000294978,
+    -0.000180515,
+    -5.21677e-05,
+    8.9455e-05,
+    0.000243425,
+    0.000408425,
+    0.000582684,
+    0.000763914,
+    0.000949268,
+    0.0011353,
+    0.00131798,
+    0.00149267,
+    0.00165418,
+    0.00179686,
+    0.00191467,
+    0.0020013,
+    0.00205037,
+    0.00205558,
+    0.00201094,
+    0.00191099,
+    0.00175106,
+    0.00152749,
+    0.00123794,
+    0.000881561,
+    0.000459301,
+    -2.59536e-05,
+    -0.000569184,
+    -0.00116312,
+    -0.0017982,
+    -0.00246251,
+    -0.00314192,
+    -0.00382017,
+    -0.00447909,
+    -0.00509894,
+    -0.00565873,
+    -0.00613672,
+    -0.00651091,
+    -0.00675969,
+    -0.00686239,
+    -0.00680006,
+    -0.00655616,
+    -0.00611727,
+    -0.00547384,
+    -0.00462086,
+    -0.00355856,
+    -0.00229294,
+    -0.000836296,
+    0.000792401,
+    0.00256725,
+    0.00445533,
+    0.00641673,
+    0.0084048,
+    0.0103666,
+    0.0122434,
+    0.0139717,
+    0.0154841,
+    0.0167105,
+    0.0175795,
+    0.0180201,
+    0.017963,
+    0.0173428,
+    0.0160997,
+    0.0141814,
+    0.0115451,
+    0.00815967,
+    0.00400701,
+    -0.000915743,
+    -0.00659481,
+    -0.0129983,
+    -0.0200751,
+    -0.0277539,
+    -0.0359425,
+    -0.0445279,
+    -0.0533762,
+    -0.0623329,
+    -0.0712244,
+    -0.0798588,
+    -0.0880279,
+    -0.0955092,
+    -0.102069,
+    -0.107464,
+    -0.111446,
+    -0.113765,
+    -0.114172,
+    -0.112426,
+    -0.108292,
+    -0.101553,
+    -0.0920066,
+    -0.079476,
+    -0.0638084,
+    -0.0448816,
+    -0.0226067,
+    0.00306903,
+    0.0321591,
+    0.064636,
+    0.10043,
+    0.139426,
+    0.181466,
+    0.226349,
+    0.273829,
+    0.32362,
+    0.375396,
+    0.428795,
+    0.483422,
+    0.538853,
+    0.594642,
+    0.650321,
+    0.705409,
+    0.759419,
+    0.81186,
+    0.862247,
+    0.910103,
+    0.95497,
+    0.996411,
+    1.03402,
+    1.06742,
+    1.09628,
+     1.1203,
+    1.13925,
+    1.15292,
+    1.16118,
+    1.16394,
+    1.16118,
+    1.15292,
+    1.13925,
+     1.1203,
+    1.09628,
+    1.06742,
+    1.03402,
+    0.996411,
+    0.95497,
+    0.910103,
+    0.862247,
+    0.81186,
+    0.759419,
+    0.705409,
+    0.650321,
+    0.594642,
+    0.538853,
+    0.483422,
+    0.428795,
+    0.375396,
+    0.32362,
+    0.273829,
+    0.226349,
+    0.181466,
+    0.139426,
+    0.10043,
+    0.064636,
+    0.0321591,
+    0.00306903,
+    -0.0226067,
+    -0.0448816,
+    -0.0638084,
+    -0.079476,
+    -0.0920066,
+    -0.101553,
+    -0.108292,
+    -0.112426,
+    -0.114172,
+    -0.113765,
+    -0.111446,
+    -0.107464,
+    -0.102069,
+    -0.0955092,
+    -0.0880279,
+    -0.0798588,
+    -0.0712244,
+    -0.0623329,
+    -0.0533762,
+    -0.0445279,
+    -0.0359425,
+    -0.0277539,
+    -0.0200751,
+    -0.0129983,
+    -0.00659481,
+    -0.000915743,
+    0.00400701,
+    0.00815967,
+    0.0115451,
+    0.0141814,
+    0.0160997,
+    0.0173428,
+    0.017963,
+    0.0180201,
+    0.0175795,
+    0.0167105,
+    0.0154841,
+    0.0139717,
+    0.0122434,
+    0.0103666,
+    0.0084048,
+    0.00641673,
+    0.00445533,
+    0.00256725,
+    0.000792401,
+    -0.000836296,
+    -0.00229294,
+    -0.00355856,
+    -0.00462086,
+    -0.00547384,
+    -0.00611727,
+    -0.00655616,
+    -0.00680006,
+    -0.00686239,
+    -0.00675969,
+    -0.00651091,
+    -0.00613672,
+    -0.00565873,
+    -0.00509894,
+    -0.00447909,
+    -0.00382017,
+    -0.00314192,
+    -0.00246251,
+    -0.0017982,
+    -0.00116312,
+    -0.000569184,
+    -2.59536e-05,
+    0.000459301,
+    0.000881561,
+    0.00123794,
+    0.00152749,
+    0.00175106,
+    0.00191099,
+    0.00201094,
+    0.00205558,
+    0.00205037,
+    0.0020013,
+    0.00191467,
+    0.00179686,
+    0.00165418,
+    0.00149267,
+    0.00131798,
+    0.0011353,
+    0.000949268,
+    0.000763914,
+    0.000582684,
+    0.000408425,
+    0.000243425,
+    8.9455e-05,
+    -5.21677e-05,
+    -0.000180515,
+    -0.000294978,
+    -0.000395195,
+    -0.000480985,
+    -0.000552292,
+    -0.000609128
+ };
+
+
+ // rFIR
+ // localSamplingrate = 48000.000000
+ // number of taps = 121
+ // Band0 Lower=0.000000, Upper=2700.000000, Desired=1.000000, Weight=1.000000
+ // Band1 Lower=3500.000000, Upper=23999.000000, Desired=0.000000, Weight=1.000000
+ //Coefficients:
+  const  FILTERPARAMTYPE wfFilter[TXWFNUMTAPS]=
+ {
+    0.00116433,
+    -0.00494437,
+    -0.00227196,
+    -0.00162745,
+    -0.00119222,
+    -0.000602408,
+    0.000154827,
+    0.000987978,
+    0.00175857,
+    0.00231978,
+    0.00254186,
+    0.00234367,
+    0.0017027,
+    0.000677718,
+    -0.000595763,
+    -0.00192037,
+    -0.00307107,
+    -0.0038171,
+    -0.00398386,
+    -0.00346983,
+    -0.00228737,
+    -0.000567514,
+    0.00145033,
+    0.00344646,
+    0.00506842,
+    0.00599002,
+    0.00597109,
+    0.00490811,
+    0.00286844,
+    9.73674e-05,
+    -0.00300691,
+    -0.00594212,
+    -0.00817976,
+    -0.00925143,
+    -0.00883636,
+    -0.00682964,
+    -0.00339026,
+    0.00106452,
+    0.00589068,
+    0.0103035,
+    0.0134903,
+    0.0147392,
+    0.0135693,
+    0.00983817,
+    0.00380649,
+    -0.00385039,
+    -0.0120879,
+    -0.0196086,
+    -0.0250088,
+    -0.0269742,
+    -0.0244494,
+    -0.0168371,
+    -0.004072,
+    0.0132752,
+    0.034055,
+    0.0566447,
+    0.0790638,
+    0.0992571,
+    0.115295,
+    0.12561,
+    0.129165,
+    0.12561,
+    0.115295,
+    0.0992571,
+    0.0790638,
+    0.0566447,
+    0.034055,
+    0.0132752,
+    -0.004072,
+    -0.0168371,
+    -0.0244494,
+    -0.0269742,
+    -0.0250088,
+    -0.0196086,
+    -0.0120879,
+    -0.00385039,
+    0.00380649,
+    0.00983817,
+    0.0135693,
+    0.0147392,
+    0.0134903,
+    0.0103035,
+    0.00589068,
+    0.00106452,
+    -0.00339026,
+    -0.00682964,
+    -0.00883636,
+    -0.00925143,
+    -0.00817976,
+    -0.00594212,
+    -0.00300691,
+    9.73674e-05,
+    0.00286844,
+    0.00490811,
+    0.00597109,
+    0.00599002,
+    0.00506842,
+    0.00344646,
+    0.00145033,
+    -0.000567514,
+    -0.00228737,
+    -0.00346983,
+    -0.00398386,
+    -0.0038171,
+    -0.00307107,
+    -0.00192037,
+    -0.000595763,
+    0.000677718,
+    0.0017027,
+    0.00234367,
+    0.00254186,
+    0.00231978,
+    0.00175857,
+    0.000987978,
+    0.000154827,
+    -0.000602408,
+    -0.00119222,
+    -0.00162745,
+    -0.00227196,
+    -0.00494437,
+    0.00116433
+ };
+
+// // blFIR  Raised cosine
+// // localSamplingrate = 12000.000000
+// // number of taps = 251
+// // corner frequency = 750.000000
+// // beta = 0.600000
+// // window applied
+// //Coefficients:
+// const  DSPFLOAT  wideVolumeFilter[RXNUMTAPS]=
+// {
+//    -1.64544e-06,
+//    -2.80612e-06,
+//    -3.45436e-06,
+//    -3.203e-06,
+//    -1.96312e-06,
+//    2.05821e-19,
+//    2.13622e-06,
+//    3.79135e-06,
+//    4.44443e-06,
+//    3.91982e-06,
+//    2.49121e-06,
+//    8.24608e-07,
+//    -2.39152e-07,
+//          0,
+//    1.79747e-06,
+//    4.79367e-06,
+//    8.06507e-06,
+//    1.04095e-05,
+//    1.07867e-05,
+//    8.75844e-06,
+//    4.75057e-06,
+//    -4.21053e-19,
+//    -3.83858e-06,
+//    -5.31774e-06,
+//    -3.83133e-06,
+//    4.45013e-09,
+//    4.42064e-06,
+//    7.05764e-06,
+//    5.86252e-06,
+//          0,
+//    -9.60024e-06,
+//    -2.03439e-05,
+//    -2.8777e-05,
+//    -3.18678e-05,
+//    -2.83026e-05,
+//    -1.92589e-05,
+//    -8.2549e-06,
+//    -2.78137e-19,
+//    1.42117e-06,
+//    -5.6256e-06,
+//    -1.92495e-05,
+//    -3.42703e-05,
+//    -4.38597e-05,
+//    -4.21144e-05,
+//    -2.66287e-05,
+//          0,
+//    3.05108e-05,
+//    5.52904e-05,
+//    6.59811e-05,
+//    5.90786e-05,
+//    3.8027e-05,
+//    1.27283e-05,
+//    -3.70396e-06,
+//    -1.21098e-19,
+//    2.82294e-05,
+//    7.55602e-05,
+//    0.000127442,
+//    0.000164744,
+//    0.000170855,
+//    0.000138769,
+//    7.52615e-05,
+//          0,
+//    -6.07716e-05,
+//    -8.41658e-05,
+//    -6.06472e-05,
+//    1.49487e-08,
+//    6.98882e-05,
+//    0.000111706,
+//    9.29209e-05,
+//    -3.02042e-19,
+//    -0.000152851,
+//    -0.000324975,
+//    -0.000461565,
+//    -0.000513668,
+//    -0.000458879,
+//    -0.000314394,
+//    -0.000135826,
+//          0,
+//    2.38653e-05,
+//    -9.53061e-05,
+//    -0.000330391,
+//    -0.000596491,
+//    -0.000775191,
+//    -0.00075694,
+//    -0.000487457,
+//    3.07131e-18,
+//    0.000582257,
+//    0.00108022,
+//    0.00132227,
+//    0.00121691,
+//    0.000806873,
+//    0.000278913,
+//    -8.38671e-05,
+//          0,
+//    0.000690779,
+//    0.00192988,
+//    0.00340841,
+//    0.00462998,
+//    0.00506502,
+//    0.00435752,
+//    0.00251474,
+//    1.4594e-18,
+//    -0.00233525,
+//    -0.00349956,
+//    -0.00274763,
+//    2.46719e-08,
+//    0.00384959,
+//    0.0068861,
+//    0.00648597,
+//          0,
+//    -0.0142972,
+//    -0.0362032,
+//    -0.0628097,
+//    -0.0882278,
+//    -0.10402,
+//    -0.100387,
+//    -0.0679779,
+//    -3.55038e-18,
+//    0.105805,
+//    0.245799,
+//    0.41008,
+//    0.583323,
+//    0.74676,
+//    0.881013,
+//    0.96924,
+//          1,
+//    0.96924,
+//    0.881013,
+//    0.74676,
+//    0.583323,
+//    0.41008,
+//    0.245799,
+//    0.105805,
+//    -3.55038e-18,
+//    -0.0679779,
+//    -0.100387,
+//    -0.10402,
+//    -0.0882278,
+//    -0.0628097,
+//    -0.0362032,
+//    -0.0142972,
+//          0,
+//    0.00648597,
+//    0.0068861,
+//    0.00384959,
+//    2.46719e-08,
+//    -0.00274763,
+//    -0.00349956,
+//    -0.00233525,
+//    1.4594e-18,
+//    0.00251474,
+//    0.00435752,
+//    0.00506502,
+//    0.00462998,
+//    0.00340841,
+//    0.00192988,
+//    0.000690779,
+//          0,
+//    -8.38671e-05,
+//    0.000278913,
+//    0.000806873,
+//    0.00121691,
+//    0.00132227,
+//    0.00108022,
+//    0.000582257,
+//    3.07131e-18,
+//    -0.000487457,
+//    -0.00075694,
+//    -0.000775191,
+//    -0.000596491,
+//    -0.000330391,
+//    -9.53061e-05,
+//    2.38653e-05,
+//          0,
+//    -0.000135826,
+//    -0.000314394,
+//    -0.000458879,
+//    -0.000513668,
+//    -0.000461565,
+//    -0.000324975,
+//    -0.000152851,
+//    -3.02042e-19,
+//    9.29209e-05,
+//    0.000111706,
+//    6.98882e-05,
+//    1.49487e-08,
+//    -6.06472e-05,
+//    -8.41658e-05,
+//    -6.07716e-05,
+//          0,
+//    7.52615e-05,
+//    0.000138769,
+//    0.000170855,
+//    0.000164744,
+//    0.000127442,
+//    7.55602e-05,
+//    2.82294e-05,
+//    -1.21098e-19,
+//    -3.70396e-06,
+//    1.27283e-05,
+//    3.8027e-05,
+//    5.90786e-05,
+//    6.59811e-05,
+//    5.52904e-05,
+//    3.05108e-05,
+//          0,
+//    -2.66287e-05,
+//    -4.21144e-05,
+//    -4.38597e-05,
+//    -3.42703e-05,
+//    -1.92495e-05,
+//    -5.6256e-06,
+//    1.42117e-06,
+//    -2.78137e-19,
+//    -8.2549e-06,
+//    -1.92589e-05,
+//    -2.83026e-05,
+//    -3.18678e-05,
+//    -2.8777e-05,
+//    -2.03439e-05,
+//    -9.60024e-06,
+//          0,
+//    5.86252e-06,
+//    7.05764e-06,
+//    4.42064e-06,
+//    4.45013e-09,
+//    -3.83133e-06,
+//    -5.31774e-06,
+//    -3.83858e-06,
+//    -4.21053e-19,
+//    4.75057e-06,
+//    8.75844e-06,
+//    1.07867e-05,
+//    1.04095e-05,
+//    8.06507e-06,
+//    4.79367e-06,
+//    1.79747e-06,
+//          0,
+//    -2.39152e-07,
+//    8.24608e-07,
+//    2.49121e-06,
+//    3.91982e-06,
+//    4.44443e-06,
+//    3.79135e-06,
+//    2.13622e-06,
+//    2.05821e-19,
+//    -1.96312e-06,
+//    -3.203e-06,
+//    -3.45436e-06,
+//    -2.80612e-06,
+//    -1.64544e-06
+// };
+
+
+//// rFIR
+//// localSamplingrate = 11025.000000
+//// number of taps = 251
+//// Band0 Lower=0.000000, Upper=400.000000, Desired=1.000000, Weight=1.000000
+//// Band1 Lower=500.001000, Upper=5512.000000, Desired=0.000000, Weight=10.000000
+////Coefficients:
+
+
+//const  DSPFLOAT  narrowRX[RXNUMTAPS]=
+//{
+//	 -0.00111256,
+//	 -0.000482835,
+//	 -0.000555539,
+//	 -0.000610215,
+//	 -0.000640108,
+//	 -0.000639526,
+//	 -0.000603611,
+//	 -0.000529568,
+//	 -0.000416333,
+//	 -0.000265628,
+//	 -8.12867e-05,
+//	 0.00013,
+//	 0.000359701,
+//	 0.000596908,
+//	 0.000829677,
+//	 0.00104457,
+//	 0.00122841,
+//	 0.00136826,
+//	 0.00145366,
+//	 0.00147624,
+//	 0.00143148,
+//	 0.00131749,
+//	 0.00113691,
+//	 0.000896014,
+//	 0.000607194,
+//	 0.000285308,
+//	 -5.19563e-05,
+//	 -0.000387275,
+//	 -0.000695131,
+//	 -0.000958842,
+//	 -0.00115846,
+//	 -0.00127739,
+//	 -0.00130528,
+//	 -0.00123462,
+//	 -0.00106591,
+//	 -0.000804428,
+//	 -0.000463007,
+//	 -5.92664e-05,
+//	 0.000383167,
+//	 0.000837539,
+//	 0.00127394,
+//	 0.0016627,
+//	 0.001975,
+//	 0.00218602,
+//	 0.00227539,
+//	 0.00223003,
+//	 0.00204437,
+//	 0.00172242,
+//	 0.00127682,
+//	 0.000729308,
+//	 0.000108946,
+//	 -0.000547891,
+//	 -0.00120085,
+//	 -0.00180748,
+//	 -0.00232648,
+//	 -0.00271827,
+//	 -0.00295119,
+//	 -0.00300112,
+//	 -0.00285435,
+//	 -0.00251011,
+//	 -0.0019793,
+//	 -0.00128641,
+//	 -0.000467246,
+//	 0.000431672,
+//	 0.00135632,
+//	 0.00224783,
+//	 0.00304629,
+//	 0.00369428,
+//	 0.0041409,
+//	 0.00434539,
+//	 0.0042804,
+//	 0.00393443,
+//	 0.00331349,
+//	 0.00244167,
+//	 0.00136081,
+//	 0.000128749,
+//	 -0.00118363,
+//	 -0.00249613,
+//	 -0.00372356,
+//	 -0.00478096,
+//	 -0.00558948,
+//	 -0.00608132,
+//	 -0.00620458,
+//	 -0.0059281,
+//	 -0.00524423,
+//	 -0.00417056,
+//	 -0.00275136,
+//	 -0.00105514,
+//	 0.000826848,
+//	 0.00278589,
+//	 0.00470061,
+//	 0.00644423,
+//	 0.00789173,
+//	 0.00892786,
+//	 0.00945467,
+//	 0.00939881,
+//	 0.00871756,
+//	 0.0074038,
+//	 0.0054889,
+//	 0.00304369,
+//	 0.000177241,
+//	 -0.00296638,
+//	 -0.00621362,
+//	 -0.00936894,
+//	 -0.012224,
+//	 -0.0145674,
+//	 -0.0161961,
+//	 -0.0169261,
+//	 -0.0166028,
+//	 -0.015111,
+//	 -0.0123822,
+//	 -0.0084009,
+//	 -0.00320751,
+//	 0.00310037,
+//	 0.0103705,
+//	 0.0184005,
+//	 0.0269456,
+//	 0.0357282,
+//	 0.044449,
+//	 0.0528009,
+//	 0.0604811,
+//	 0.0672059,
+//	 0.0727227,
+//	 0.0768219,
+//	 0.0793463,
+//	 0.0801988,
+//	 0.0793463,
+//	 0.0768219,
+//	 0.0727227,
+//	 0.0672059,
+//	 0.0604811,
+//	 0.0528009,
+//	 0.044449,
+//	 0.0357282,
+//	 0.0269456,
+//	 0.0184005,
+//	 0.0103705,
+//	 0.00310037,
+//	 -0.00320751,
+//	 -0.0084009,
+//	 -0.0123822,
+//	 -0.015111,
+//	 -0.0166028,
+//	 -0.0169261,
+//	 -0.0161961,
+//	 -0.0145674,
+//	 -0.012224,
+//	 -0.00936894,
+//	 -0.00621362,
+//	 -0.00296638,
+//	 0.000177241,
+//	 0.00304369,
+//	 0.0054889,
+//	 0.0074038,
+//	 0.00871756,
+//	 0.00939881,
+//	 0.00945467,
+//	 0.00892786,
+//	 0.00789173,
+//	 0.00644423,
+//	 0.00470061,
+//	 0.00278589,
+//	 0.000826848,
+//	 -0.00105514,
+//	 -0.00275136,
+//	 -0.00417056,
+//	 -0.00524423,
+//	 -0.0059281,
+//	 -0.00620458,
+//	 -0.00608132,
+//	 -0.00558948,
+//	 -0.00478096,
+//	 -0.00372356,
+//	 -0.00249613,
+//	 -0.00118363,
+//	 0.000128749,
+//	 0.00136081,
+//	 0.00244167,
+//	 0.00331349,
+//	 0.00393443,
+//	 0.0042804,
+//	 0.00434539,
+//	 0.0041409,
+//	 0.00369428,
+//	 0.00304629,
+//	 0.00224783,
+//	 0.00135632,
+//	 0.000431672,
+//	 -0.000467246,
+//	 -0.00128641,
+//	 -0.0019793,
+//	 -0.00251011,
+//	 -0.00285435,
+//	 -0.00300112,
+//	 -0.00295119,
+//	 -0.00271827,
+//	 -0.00232648,
+//	 -0.00180748,
+//	 -0.00120085,
+//	 -0.000547891,
+//	 0.000108946,
+//	 0.000729308,
+//	 0.00127682,
+//	 0.00172242,
+//	 0.00204437,
+//	 0.00223003,
+//	 0.00227539,
+//	 0.00218602,
+//	 0.001975,
+//	 0.0016627,
+//	 0.00127394,
+//	 0.000837539,
+//	 0.000383167,
+//	 -5.92664e-05,
+//	 -0.000463007,
+//	 -0.000804428,
+//	 -0.00106591,
+//	 -0.00123462,
+//	 -0.00130528,
+//	 -0.00127739,
+//	 -0.00115846,
+//	 -0.000958842,
+//	 -0.000695131,
+//	 -0.000387275,
+//	 -5.19563e-05,
+//	 0.000285308,
+//	 0.000607194,
+//	 0.000896014,
+//	 0.00113691,
+//	 0.00131749,
+//	 0.00143148,
+//	 0.00147624,
+//	 0.00145366,
+//	 0.00136826,
+//	 0.00122841,
+//	 0.00104457,
+//	 0.000829677,
+//	 0.000596908,
+//	 0.000359701,
+//	 0.00013,
+//	 -8.12867e-05,
+//	 -0.000265628,
+//	 -0.000416333,
+//	 -0.000529568,
+//	 -0.000603611,
+//	 -0.000639526,
+//	 -0.000640108,
+//	 -0.000610215,
+//	 -0.000555539,
+//	 -0.000482835,
+//	 -0.00111256
+//};
+
+
+// // rFIR
+// // localSamplingrate = 11025.000000
+// // number of taps = 251
+// // Band0 Lower=0.000000, Upper=500.000000, Desired=1.000000, Weight=1.000000
+// // Band1 Lower=630.000000, Upper=5512.000000, Desired=0.000000, Weight=10.000000
+// //Coefficients:
+//  const  DSPFLOAT  wideRX[RXNUMTAPS]=
+// {
+//    0.000300231,
+//    -8.20897e-06,
+//    -4.11537e-05,
+//    -9.46388e-05,
+//    -0.000165678,
+//    -0.000249422,
+//    -0.000339104,
+//    -0.000426459,
+//    -0.00050217,
+//    -0.000556782,
+//    -0.000581556,
+//    -0.000569598,
+//    -0.000516739,
+//    -0.000422463,
+//    -0.000290329,
+//    -0.000128235,
+//    5.20287e-05,
+//    0.000235434,
+//    0.0004051,
+//    0.000543697,
+//    0.000635391,
+//    0.000667432,
+//    0.000631989,
+//    0.000527203,
+//    0.00035825,
+//    0.000137199,
+//    -0.000117217,
+//    -0.000381393,
+//    -0.000628444,
+//    -0.000831458,
+//    -0.000965562,
+//    -0.00101162,
+//    -0.000955948,
+//    -0.000797507,
+//    -0.00054387,
+//    -0.000213404,
+//    0.000165331,
+//    0.000557083,
+//    0.000921971,
+//    0.00122013,
+//    0.00141525,
+//    0.00147924,
+//    0.00139533,
+//    0.00116112,
+//    0.000789527,
+//    0.000308943,
+//    -0.000238925,
+//    -0.000802116,
+//    -0.00132339,
+//    -0.00174556,
+//    -0.00201774,
+//    -0.00210088,
+//    -0.00197294,
+//    -0.0016322,
+//    -0.00109903,
+//    -0.000415164,
+//    0.000359026,
+//    0.00114961,
+//    0.00187576,
+//    0.00245778,
+//    0.00282554,
+//    0.00292636,
+//    0.00273164,
+//    0.00224121,
+//    0.00148608,
+//    0.000526493,
+//    -0.000552233,
+//    -0.00164623,
+//    -0.00264352,
+//    -0.00343428,
+//    -0.00392291,
+//    -0.00403842,
+//    -0.00374381,
+//    -0.00304178,
+//    -0.00197746,
+//    -0.000636259,
+//    0.000861911,
+//    0.00237298,
+//    0.00374158,
+//    0.00481658,
+//    0.00546654,
+//    0.00559482,
+//    0.00515159,
+//    0.00414242,
+//    0.00263122,
+//    0.000738094,
+//    -0.00136926,
+//    -0.00348897,
+//    -0.00540332,
+//    -0.00689939,
+//    -0.00779119,
+//    -0.00794022,
+//    -0.00727319,
+//    -0.00579387,
+//    -0.00358894,
+//    -0.000825621,
+//    0.00225865,
+//    0.00537444,
+//    0.00820404,
+//    0.0104312,
+//    0.0117713,
+//    0.0120024,
+//    0.0109917,
+//    0.00871646,
+//    0.00527566,
+//    0.000892733,
+//    -0.00409393,
+//    -0.00925022,
+//    -0.014076,
+//    -0.0180419,
+//    -0.0206315,
+//    -0.0213845,
+//    -0.0199384,
+//    -0.0160644,
+//    -0.009694,
+//    -0.000934707,
+//    0.00992761,
+//    0.0224406,
+//    0.0360101,
+//    0.0499371,
+//    0.0634635,
+//    0.0758241,
+//     0.0863,
+//    0.0942695,
+//    0.0992533,
+//    0.100949,
+//    0.0992533,
+//    0.0942695,
+//     0.0863,
+//    0.0758241,
+//    0.0634635,
+//    0.0499371,
+//    0.0360101,
+//    0.0224406,
+//    0.00992761,
+//    -0.000934707,
+//    -0.009694,
+//    -0.0160644,
+//    -0.0199384,
+//    -0.0213845,
+//    -0.0206315,
+//    -0.0180419,
+//    -0.014076,
+//    -0.00925022,
+//    -0.00409393,
+//    0.000892733,
+//    0.00527566,
+//    0.00871646,
+//    0.0109917,
+//    0.0120024,
+//    0.0117713,
+//    0.0104312,
+//    0.00820404,
+//    0.00537444,
+//    0.00225865,
+//    -0.000825621,
+//    -0.00358894,
+//    -0.00579387,
+//    -0.00727319,
+//    -0.00794022,
+//    -0.00779119,
+//    -0.00689939,
+//    -0.00540332,
+//    -0.00348897,
+//    -0.00136926,
+//    0.000738094,
+//    0.00263122,
+//    0.00414242,
+//    0.00515159,
+//    0.00559482,
+//    0.00546654,
+//    0.00481658,
+//    0.00374158,
+//    0.00237298,
+//    0.000861911,
+//    -0.000636259,
+//    -0.00197746,
+//    -0.00304178,
+//    -0.00374381,
+//    -0.00403842,
+//    -0.00392291,
+//    -0.00343428,
+//    -0.00264352,
+//    -0.00164623,
+//    -0.000552233,
+//    0.000526493,
+//    0.00148608,
+//    0.00224121,
+//    0.00273164,
+//    0.00292636,
+//    0.00282554,
+//    0.00245778,
+//    0.00187576,
+//    0.00114961,
+//    0.000359026,
+//    -0.000415164,
+//    -0.00109903,
+//    -0.0016322,
+//    -0.00197294,
+//    -0.00210088,
+//    -0.00201774,
+//    -0.00174556,
+//    -0.00132339,
+//    -0.000802116,
+//    -0.000238925,
+//    0.000308943,
+//    0.000789527,
+//    0.00116112,
+//    0.00139533,
+//    0.00147924,
+//    0.00141525,
+//    0.00122013,
+//    0.000921971,
+//    0.000557083,
+//    0.000165331,
+//    -0.000213404,
+//    -0.00054387,
+//    -0.000797507,
+//    -0.000955948,
+//    -0.00101162,
+//    -0.000965562,
+//    -0.000831458,
+//    -0.000628444,
+//    -0.000381393,
+//    -0.000117217,
+//    0.000137199,
+//    0.00035825,
+//    0.000527203,
+//    0.000631989,
+//    0.000667432,
+//    0.000635391,
+//    0.000543697,
+//    0.0004051,
+//    0.000235434,
+//    5.20287e-05,
+//    -0.000128235,
+//    -0.000290329,
+//    -0.000422463,
+//    -0.000516739,
+//    -0.000569598,
+//    -0.000581556,
+//    -0.000556782,
+//    -0.00050217,
+//    -0.000426459,
+//    -0.000339104,
+//    -0.000249422,
+//    -0.000165678,
+//    -9.46388e-05,
+//    -4.11537e-05,
+//    -8.20897e-06,
+//    0.000300231
+// };
+
+//// rFIR
+//// localSamplingrate = 11025.000000
+//// number of taps = 251
+//// Band0 Lower=0.000000, Upper=999.999000, Desired=0.000000, Weight=10.000000
+//// Band1 Lower=1110.000000, Upper=1290.000000, Desired=1.000000, Weight=1.000000
+//// Band2 Lower=1400.000000, Upper=5512.000000, Desired=0.000000, Weight=10.000000
+////Coefficients:
+// const  DSPFLOAT sharp1200BP[RXNUMTAPS]=
+//{
+//	 -0.000181001,
+//	 0.000251417,
+//	 -0.000407553,
+//	 -0.000113892,
+//	 0.000360061,
+//	 0.000633657,
+//	 0.000641274,
+//	 0.000395862,
+//	 -5.12681e-05,
+//	 -0.000553689,
+//	 -0.000879439,
+//	 -0.000820056,
+//	 -0.00032433,
+//	 0.000435928,
+//	 0.00111676,
+//	 0.00135737,
+//	 0.000967803,
+//	 5.29504e-05,
+//	 -0.00100229,
+//	 -0.00169484,
+//	 -0.00164687,
+//	 -0.000809972,
+//	 0.000478952,
+//	 0.0016339,
+//	 0.00209334,
+//	 0.00160099,
+//	 0.000349433,
+//	 -0.00109484,
+//	 -0.00205749,
+//	 -0.00208466,
+//	 -0.00116866,
+//	 0.000247443,
+//	 0.00149138,
+//	 0.00199728,
+//	 0.00158125,
+//	 0.000514645,
+//	 -0.000639633,
+//	 -0.00133383,
+//	 -0.00131654,
+//	 -0.000735591,
+//	 -1.20504e-05,
+//	 0.000432654,
+//	 0.000430428,
+//	 0.000148421,
+//	 -5.08362e-05,
+//	 0.000116712,
+//	 0.000625307,
+//	 0.00110932,
+//	 0.00107711,
+//	 0.000255083,
+//	 -0.00114975,
+//	 -0.0024561,
+//	 -0.00282831,
+//	 -0.00176363,
+//	 0.00052394,
+//	 0.00307314,
+//	 0.00457652,
+//	 0.00404712,
+//	 0.00141358,
+//	 -0.00230461,
+//	 -0.00538379,
+//	 -0.00621703,
+//	 -0.00415245,
+//	 6.94677e-05,
+//	 0.00457672,
+//	 0.00721896,
+//	 0.00662798,
+//	 0.00294486,
+//	 -0.00218784,
+//	 -0.00638383,
+//	 -0.0076732,
+//	 -0.00547554,
+//	 -0.00088151,
+//	 0.00389921,
+//	 0.00665079,
+//	 0.00624499,
+//	 0.00314513,
+//	 -0.00092486,
+//	 -0.00394617,
+//	 -0.00467243,
+//	 -0.00320729,
+//	 -0.000793654,
+//	 0.00102892,
+//	 0.00138505,
+//	 0.00056018,
+//	 -0.000275589,
+//	 5.06196e-05,
+//	 0.00178544,
+//	 0.00389119,
+//	 0.00456457,
+//	 0.00240679,
+//	 -0.00243394,
+//	 -0.00790812,
+//	 -0.0108727,
+//	 -0.00877531,
+//	 -0.00135218,
+//	 0.00867904,
+//	 0.0165229,
+//	 0.0175639,
+//	 0.00992846,
+//	 -0.00402369,
+//	 -0.0182552,
+//	 -0.0256507,
+//	 -0.0215475,
+//	 -0.00645296,
+//	 0.013569,
+//	 0.0293071,
+//	 0.0326681,
+//	 0.0208103,
+//	 -0.00195836,
+//	 -0.0256691,
+//	 -0.0391028,
+//	 -0.0351489,
+//	 -0.01453,
+//	 0.0140466,
+//	 0.0376031,
+//	 0.044869,
+//	 0.0317345,
+//	 0.00350871,
+//	 -0.027234,
+//	 -0.0463481,
+//	 -0.0447258,
+//	 -0.0226958,
+//	 0.00994727,
+//	 0.0383706,
+//	 0.0495577,
+//	 0.0383706,
+//	 0.00994727,
+//	 -0.0226958,
+//	 -0.0447258,
+//	 -0.0463481,
+//	 -0.027234,
+//	 0.00350871,
+//	 0.0317345,
+//	 0.044869,
+//	 0.0376031,
+//	 0.0140466,
+//	 -0.01453,
+//	 -0.0351489,
+//	 -0.0391028,
+//	 -0.0256691,
+//	 -0.00195836,
+//	 0.0208103,
+//	 0.0326681,
+//	 0.0293071,
+//	 0.013569,
+//	 -0.00645296,
+//	 -0.0215475,
+//	 -0.0256507,
+//	 -0.0182552,
+//	 -0.00402369,
+//	 0.00992846,
+//	 0.0175639,
+//	 0.0165229,
+//	 0.00867904,
+//	 -0.00135218,
+//	 -0.00877531,
+//	 -0.0108727,
+//	 -0.00790812,
+//	 -0.00243394,
+//	 0.00240679,
+//	 0.00456457,
+//	 0.00389119,
+//	 0.00178544,
+//	 5.06196e-05,
+//	 -0.000275589,
+//	 0.00056018,
+//	 0.00138505,
+//	 0.00102892,
+//	 -0.000793654,
+//	 -0.00320729,
+//	 -0.00467243,
+//	 -0.00394617,
+//	 -0.00092486,
+//	 0.00314513,
+//	 0.00624499,
+//	 0.00665079,
+//	 0.00389921,
+//	 -0.00088151,
+//	 -0.00547554,
+//	 -0.0076732,
+//	 -0.00638383,
+//	 -0.00218784,
+//	 0.00294486,
+//	 0.00662798,
+//	 0.00721896,
+//	 0.00457672,
+//	 6.94677e-05,
+//	 -0.00415245,
+//	 -0.00621703,
+//	 -0.00538379,
+//	 -0.00230461,
+//	 0.00141358,
+//	 0.00404712,
+//	 0.00457652,
+//	 0.00307314,
+//	 0.00052394,
+//	 -0.00176363,
+//	 -0.00282831,
+//	 -0.0024561,
+//	 -0.00114975,
+//	 0.000255083,
+//	 0.00107711,
+//	 0.00110932,
+//	 0.000625307,
+//	 0.000116712,
+//	 -5.08362e-05,
+//	 0.000148421,
+//	 0.000430428,
+//	 0.000432654,
+//	 -1.20504e-05,
+//	 -0.000735591,
+//	 -0.00131654,
+//	 -0.00133383,
+//	 -0.000639633,
+//	 0.000514645,
+//	 0.00158125,
+//	 0.00199728,
+//	 0.00149138,
+//	 0.000247443,
+//	 -0.00116866,
+//	 -0.00208466,
+//	 -0.00205749,
+//	 -0.00109484,
+//	 0.000349433,
+//	 0.00160099,
+//	 0.00209334,
+//	 0.0016339,
+//	 0.000478952,
+//	 -0.000809972,
+//	 -0.00164687,
+//	 -0.00169484,
+//	 -0.00100229,
+//	 5.29504e-05,
+//	 0.000967803,
+//	 0.00135737,
+//	 0.00111676,
+//	 0.000435928,
+//	 -0.00032433,
+//	 -0.000820056,
+//	 -0.000879439,
+//	 -0.000553689,
+//	 -5.12681e-05,
+//	 0.000395862,
+//	 0.000641274,
+//	 0.000633657,
+//	 0.000360061,
+//	 -0.000113892,
+//	 -0.000407553,
+//	 0.000251417,
+//	 -0.000181001
+//};
+
+
+
+//// rFIR
+//// localSamplingrate = 11025.000000
+//// number of taps = 251
+//// Band0 Lower=0.000000, Upper=550.000000, Desired=1.000000, Weight=1.000000
+//// Band1 Lower=850.000000, Upper=5512.000000, Desired=0.000000, Weight=10.000000
+//// demodulator frequency = 1750.000000
+////Coefficients:
+// const  DSPFLOAT wideVolume[RXNUMTAPS]=
+//{
+//	 -8.12983e-07,
+//	 -6.20807e-07,
+//	 -5.71262e-07,
+//	 -2.08463e-07,
+//	 5.61841e-07,
+//	 1.76677e-06,
+//	 3.32807e-06,
+//	 5.03565e-06,
+//	 6.54698e-06,
+//	 7.4188e-06,
+//	 7.17643e-06,
+//	 5.41296e-06,
+//	 1.90859e-06,
+//	 -3.25452e-06,
+//	 -9.60746e-06,
+//	 -1.62721e-05,
+//	 -2.20201e-05,
+//	 -2.54248e-05,
+//	 -2.50993e-05,
+//	 -1.99967e-05,
+//	 -9.71913e-06,
+//	 5.22566e-06,
+//	 2.32837e-05,
+//	 4.18857e-05,
+//	 5.77019e-05,
+//	 6.70942e-05,
+//	 6.67504e-05,
+//	 5.43975e-05,
+//	 2.94745e-05,
+//	 -6.37004e-06,
+//	 -4.91004e-05,
+//	 -9.25291e-05,
+//	 -0.000129015,
+//	 -0.000150574,
+//	 -0.000150291,
+//	 -0.000123799,
+//	 -7.05844e-05,
+//	 5.16902e-06,
+//	 9.44505e-05,
+//	 0.000184149,
+//	 0.000258662,
+//	 0.000302252,
+//	 0.000301838,
+//	 0.000249793,
+//	 0.000146241,
+//	 3.42773e-07,
+//	 -0.000169823,
+//	 -0.000338982,
+//	 -0.000477957,
+//	 -0.000558161,
+//	 -0.000556692,
+//	 -0.00046121,
+//	 -0.000273687,
+//	 -1.22083e-05,
+//	 0.000289784,
+//	 0.000587038,
+//	 0.000828607,
+//	 0.000965818,
+//	 0.000960953,
+//	 0.000795233,
+//	 0.000474608,
+//	 3.2073e-05,
+//	 -0.000474377,
+//	 -0.000968335,
+//	 -0.00136559,
+//	 -0.00158739,
+//	 -0.00157453,
+//	 -0.00129973,
+//	 -0.000776269,
+//	 -6.06608e-05,
+//	 0.000751585,
+//	 0.00153735,
+//	 0.00216326,
+//	 0.00250687,
+//	 0.00247833,
+//	 0.00203924,
+//	 0.00121491,
+//	 9.73382e-05,
+//	 -0.0011625,
+//	 -0.00237319,
+//	 -0.00333006,
+//	 -0.00384765,
+//	 -0.00379243,
+//	 -0.0031105,
+//	 -0.00184508,
+//	 -0.000139903,
+//	 0.00177363,
+//	 0.00360499,
+//	 0.00504579,
+//	 0.00581827,
+//	 0.00572385,
+//	 0.0046839,
+//	 0.00276522,
+//	 0.000184684,
+//	 -0.00270984,
+//	 -0.00548173,
+//	 -0.00766633,
+//	 -0.00884263,
+//	 -0.00870541,
+//	 -0.00712658,
+//	 -0.00419509,
+//	 -0.000226991,
+//	 0.00425896,
+//	 0.00859967,
+//	 0.0120746,
+//	 0.0140108,
+//	 0.0138925,
+//	 0.0114587,
+//	 0.00677437,
+//	 0.000261833,
+//	 -0.00731507,
+//	 -0.0149163,
+//	 -0.0213353,
+//	 -0.0253404,
+//	 -0.0258336,
+//	 -0.0220032,
+//	 -0.0134525,
+//	 -0.000284783,
+//	 0.0168686,
+//	 0.0368837,
+//	 0.058241,
+//	 0.079175,
+//	 0.0978591,
+//	 0.112604,
+//	 0.122044,
+//	 0.125293,
+//	 0.122044,
+//	 0.112604,
+//	 0.0978591,
+//	 0.079175,
+//	 0.058241,
+//	 0.0368837,
+//	 0.0168686,
+//	 -0.000284783,
+//	 -0.0134525,
+//	 -0.0220032,
+//	 -0.0258336,
+//	 -0.0253404,
+//	 -0.0213353,
+//	 -0.0149163,
+//	 -0.00731507,
+//	 0.000261833,
+//	 0.00677437,
+//	 0.0114587,
+//	 0.0138925,
+//	 0.0140108,
+//	 0.0120746,
+//	 0.00859967,
+//	 0.00425896,
+//	 -0.000226991,
+//	 -0.00419509,
+//	 -0.00712658,
+//	 -0.00870541,
+//	 -0.00884263,
+//	 -0.00766633,
+//	 -0.00548173,
+//	 -0.00270984,
+//	 0.000184684,
+//	 0.00276522,
+//	 0.0046839,
+//	 0.00572385,
+//	 0.00581827,
+//	 0.00504579,
+//	 0.00360499,
+//	 0.00177363,
+//	 -0.000139903,
+//	 -0.00184508,
+//	 -0.0031105,
+//	 -0.00379243,
+//	 -0.00384765,
+//	 -0.00333006,
+//	 -0.00237319,
+//	 -0.0011625,
+//	 9.73382e-05,
+//	 0.00121491,
+//	 0.00203924,
+//	 0.00247833,
+//	 0.00250687,
+//	 0.00216326,
+//	 0.00153735,
+//	 0.000751585,
+//	 -6.06608e-05,
+//	 -0.000776269,
+//	 -0.00129973,
+//	 -0.00157453,
+//	 -0.00158739,
+//	 -0.00136559,
+//	 -0.000968335,
+//	 -0.000474377,
+//	 3.2073e-05,
+//	 0.000474608,
+//	 0.000795233,
+//	 0.000960953,
+//	 0.000965818,
+//	 0.000828607,
+//	 0.000587038,
+//	 0.000289784,
+//	 -1.22083e-05,
+//	 -0.000273687,
+//	 -0.00046121,
+//	 -0.000556692,
+//	 -0.000558161,
+//	 -0.000477957,
+//	 -0.000338982,
+//	 -0.000169823,
+//	 3.42773e-07,
+//	 0.000146241,
+//	 0.000249793,
+//	 0.000301838,
+//	 0.000302252,
+//	 0.000258662,
+//	 0.000184149,
+//	 9.44505e-05,
+//	 5.16902e-06,
+//	 -7.05844e-05,
+//	 -0.000123799,
+//	 -0.000150291,
+//	 -0.000150574,
+//	 -0.000129015,
+//	 -9.25291e-05,
+//	 -4.91004e-05,
+//	 -6.37004e-06,
+//	 2.94745e-05,
+//	 5.43975e-05,
+//	 6.67504e-05,
+//	 6.70942e-05,
+//	 5.77019e-05,
+//	 4.18857e-05,
+//	 2.32837e-05,
+//	 5.22566e-06,
+//	 -9.71913e-06,
+//	 -1.99967e-05,
+//	 -2.50993e-05,
+//	 -2.54248e-05,
+//	 -2.20201e-05,
+//	 -1.62721e-05,
+//	 -9.60746e-06,
+//	 -3.25452e-06,
+//	 1.90859e-06,
+//	 5.41296e-06,
+//	 7.17643e-06,
+//	 7.4188e-06,
+//	 6.54698e-06,
+//	 5.03565e-06,
+//	 3.32807e-06,
+//	 1.76677e-06,
+//	 5.61841e-07,
+//	 -2.08463e-07,
+//	 -5.71262e-07,
+//	 -6.20807e-07,
+//	 -8.12983e-07
+//};
+// const DSPFLOAT veryWideVolume[RXNUMTAPS]=
+//{
+//	 1.64357e-05,
+//	 -4.23193e-05,
+//	 -4.14901e-05,
+//	 -4.74921e-05,
+//	 -4.96931e-05,
+//	 -4.33244e-05,
+//	 -2.66581e-05,
+//	 -7.16336e-07,
+//	 3.07848e-05,
+//	 6.18288e-05,
+//	 8.51506e-05,
+//	 9.37278e-05,
+//	 8.25922e-05,
+//	 5.04016e-05,
+//	 5.18827e-07,
+//	 -5.8932e-05,
+//	 -0.000116071,
+//	 -0.000157418,
+//	 -0.000170751,
+//	 -0.0001482,
+//	 -8.87869e-05,
+//	 1.59863e-07,
+//	 0.000103401,
+//	 0.000199998,
+//	 0.00026727,
+//	 0.000285688,
+//	 0.000243843,
+//	 0.0001422,
+//	 -5.26495e-06,
+//	 -0.000172346,
+//	 -0.000324768,
+//	 -0.000426854,
+//	 -0.000449283,
+//	 -0.000376571,
+//	 -0.000212291,
+//	 1.92053e-05,
+//	 0.000275638,
+//	 0.000503917,
+//	 0.000650668,
+//	 0.000673945,
+//	 0.000553971,
+//	 0.000299992,
+//	 -4.82102e-05,
+//	 -0.000425738,
+//	 -0.000753809,
+//	 -0.000955647,
+//	 -0.000973745,
+//	 -0.000783958,
+//	 -0.000405082,
+//	 0.000101089,
+//	 0.000638559,
+//	 0.00109458,
+//	 0.00136236,
+//	 0.00136503,
+//	 0.00107529,
+//	 0.000526322,
+//	 -0.000189418,
+//	 -0.000934401,
+//	 -0.00155146,
+//	 -0.00189586,
+//	 -0.00186775,
+//	 -0.00143799,
+//	 -0.00066103,
+//	 0.000329022,
+//	 0.00133993,
+//	 0.0021574,
+//	 0.00258906,
+//	 0.00250785,
+//	 0.00188506,
+//	 0.000805333,
+//	 -0.000541519,
+//	 -0.00189182,
+//	 -0.00295802,
+//	 -0.00348805,
+//	 -0.00332229,
+//	 -0.00243558,
+//	 -0.000954212,
+//	 0.000857982,
+//	 0.00264405,
+//	 0.00402195,
+//	 0.00466364,
+//	 0.00436938,
+//	 0.00312112,
+//	 0.00110175,
+//	 -0.00132647,
+//	 -0.00368338,
+//	 -0.00546249,
+//	 -0.00623577,
+//	 -0.00575096,
+//	 -0.00399989,
+//	 -0.00124154,
+//	 0.0020292,
+//	 0.00516496,
+//	 0.00748838,
+//	 0.00843175,
+//	 0.00766589,
+//	 0.005191,
+//	 0.00136708,
+//	 -0.0031274,
+//	 -0.0074065,
+//	 -0.010541,
+//	 -0.0117467,
+//	 -0.0105615,
+//	 -0.00697387,
+//	 -0.00147219,
+//	 0.00500105,
+//	 0.0111914,
+//	 0.0157512,
+//	 0.0175002,
+//	 0.0156816,
+//	 0.0101648,
+//	 0.0015515,
+//	 -0.0088417,
+//	 -0.0191334,
+//	 -0.0271425,
+//	 -0.0307308,
+//	 -0.0281698,
+//	 -0.0184676,
+//	 -0.00160082,
+//	 0.0213961,
+//	 0.0485054,
+//	 0.0769597,
+//	 0.103604,
+//	 0.125334,
+//	 0.139537,
+//	 0.144475,
+//	 0.139537,
+//	 0.125334,
+//	 0.103604,
+//	 0.0769597,
+//	 0.0485054,
+//	 0.0213961,
+//	 -0.00160082,
+//	 -0.0184676,
+//	 -0.0281698,
+//	 -0.0307308,
+//	 -0.0271425,
+//	 -0.0191334,
+//	 -0.0088417,
+//	 0.0015515,
+//	 0.0101648,
+//	 0.0156816,
+//	 0.0175002,
+//	 0.0157512,
+//	 0.0111914,
+//	 0.00500105,
+//	 -0.00147219,
+//	 -0.00697387,
+//	 -0.0105615,
+//	 -0.0117467,
+//	 -0.010541,
+//	 -0.0074065,
+//	 -0.0031274,
+//	 0.00136708,
+//	 0.005191,
+//	 0.00766589,
+//	 0.00843175,
+//	 0.00748838,
+//	 0.00516496,
+//	 0.0020292,
+//	 -0.00124154,
+//	 -0.00399989,
+//	 -0.00575096,
+//	 -0.00623577,
+//	 -0.00546249,
+//	 -0.00368338,
+//	 -0.00132647,
+//	 0.00110175,
+//	 0.00312112,
+//	 0.00436938,
+//	 0.00466364,
+//	 0.00402195,
+//	 0.00264405,
+//	 0.000857982,
+//	 -0.000954212,
+//	 -0.00243558,
+//	 -0.00332229,
+//	 -0.00348805,
+//	 -0.00295802,
+//	 -0.00189182,
+//	 -0.000541519,
+//	 0.000805333,
+//	 0.00188506,
+//	 0.00250785,
+//	 0.00258906,
+//	 0.0021574,
+//	 0.00133993,
+//	 0.000329022,
+//	 -0.00066103,
+//	 -0.00143799,
+//	 -0.00186775,
+//	 -0.00189586,
+//	 -0.00155146,
+//	 -0.000934401,
+//	 -0.000189418,
+//	 0.000526322,
+//	 0.00107529,
+//	 0.00136503,
+//	 0.00136236,
+//	 0.00109458,
+//	 0.000638559,
+//	 0.000101089,
+//	 -0.000405082,
+//	 -0.000783958,
+//	 -0.000973745,
+//	 -0.000955647,
+//	 -0.000753809,
+//	 -0.000425738,
+//	 -4.82102e-05,
+//	 0.000299992,
+//	 0.000553971,
+//	 0.000673945,
+//	 0.000650668,
+//	 0.000503917,
+//	 0.000275638,
+//	 1.92053e-05,
+//	 -0.000212291,
+//	 -0.000376571,
+//	 -0.000449283,
+//	 -0.000426854,
+//	 -0.000324768,
+//	 -0.000172346,
+//	 -5.26495e-06,
+//	 0.0001422,
+//	 0.000243843,
+//	 0.000285688,
+//	 0.00026727,
+//	 0.000199998,
+//	 0.000103401,
+//	 1.59863e-07,
+//	 -8.87869e-05,
+//	 -0.0001482,
+//	 -0.000170751,
+//	 -0.000157418,
+//	 -0.000116071,
+//	 -5.8932e-05,
+//	 5.18827e-07,
+//	 5.04016e-05,
+//	 8.25922e-05,
+//	 9.37278e-05,
+//	 8.51506e-05,
+//	 6.18288e-05,
+//	 3.07848e-05,
+//	 -7.16336e-07,
+//	 -2.66581e-05,
+//	 -4.33244e-05,
+//	 -4.96931e-05,
+//	 -4.74921e-05,
+//	 -4.14901e-05,
+//	 -4.23193e-05,
+//	 1.64357e-05
+//};
+
+// // blFIR  Raised cosine
+// // localSamplingrate = 11025.000000
+// // number of taps = 251
+// // corner frequency = 400.000000
+// // beta = 0.600000
+// // window applied
+// // demodulator frequency = 1900.000000
+// //Coefficients:
+//  const DSPFLOAT narrowRXBLFIR[RXNUMTAPS]=
+// {
+//    -9.43009e-07,
+//    5.86068e-08,
+//    2.59295e-06,
+//    6.51998e-06,
+//    1.15194e-05,
+//    1.71102e-05,
+//    2.26892e-05,
+//    2.75867e-05,
+//    3.11375e-05,
+//    3.27613e-05,
+//    3.20439e-05,
+//    2.88109e-05,
+//    2.31824e-05,
+//    1.55969e-05,
+//    6.79757e-06,
+//    -2.2262e-06,
+//    -1.03411e-05,
+//    -1.64097e-05,
+//    -1.94538e-05,
+//    -1.88235e-05,
+//    -1.43458e-05,
+//    -6.42942e-06,
+//    3.89847e-06,
+//    1.50404e-05,
+//    2.49686e-05,
+//    3.14515e-05,
+//    3.23475e-05,
+//    2.59321e-05,
+//    1.12168e-05,
+//    -1.17913e-05,
+//    -4.19352e-05,
+//    -7.69e-05,
+//    -0.000113373,
+//    -0.000147376,
+//    -0.000174744,
+//    -0.000191702,
+//    -0.000195469,
+//    -0.000184804,
+//    -0.000160398,
+//    -0.000125046,
+//    -8.35194e-05,
+//    -4.21352e-05,
+//    -8.02811e-06,
+//    1.18057e-05,
+//    1.15857e-05,
+//    -1.21957e-05,
+//    -5.98941e-05,
+//    -0.000128166,
+//    -0.00020988,
+//    -0.000294572,
+//    -0.000369439,
+//    -0.00042083,
+//    -0.000436057,
+//    -0.000405344,
+//    -0.000323618,
+//    -0.00019189,
+//    -1.79507e-05,
+//    0.000183806,
+//    0.000393536,
+//    0.000588041,
+//    0.000743654,
+//    0.000839602,
+//    0.000861438,
+//    0.000804074,
+//    0.000673905,
+//    0.00048961,
+//    0.000281309,
+//    8.79499e-05,
+//    -4.69626e-05,
+//    -8.09498e-05,
+//    2.11247e-05,
+//    0.000280781,
+//    0.000700568,
+//    0.0012605,
+//    0.0019171,
+//    0.00260546,
+//    0.00324473,
+//    0.00374659,
+//    0.00402614,
+//    0.00401415,
+//    0.00366917,
+//    0.00298774,
+//    0.00201128,
+//    0.000827827,
+//    -0.000432323,
+//    -0.00160758,
+//    -0.00252251,
+//    -0.00301058,
+//    -0.00294038,
+//    -0.00224255,
+//    -0.000934317,
+//    0.000861952,
+//    0.00290941,
+//    0.00485956,
+//    0.00626926,
+//    0.00663259,
+//    0.00542685,
+//    0.00217056,
+//    -0.00351075,
+//    -0.011816,
+//    -0.0227036,
+//    -0.0358372,
+//    -0.0505484,
+//    -0.0658229,
+//    -0.0803145,
+//    -0.0923896,
+//    -0.100202,
+//    -0.101798,
+//    -0.0952396,
+//    -0.078749,
+//    -0.0508547,
+//    -0.0105335,
+//    0.0426651,
+//    0.108522,
+//    0.186092,
+//    0.273685,
+//    0.368909,
+//    0.46875,
+//    0.569709,
+//    0.667969,
+//    0.759603,
+//    0.840785,
+//    0.908018,
+//    0.95833,
+//    0.989462,
+//          1,
+//    0.989462,
+//    0.95833,
+//    0.908018,
+//    0.840785,
+//    0.759603,
+//    0.667969,
+//    0.569709,
+//    0.46875,
+//    0.368909,
+//    0.273685,
+//    0.186092,
+//    0.108522,
+//    0.0426651,
+//    -0.0105335,
+//    -0.0508547,
+//    -0.078749,
+//    -0.0952396,
+//    -0.101798,
+//    -0.100202,
+//    -0.0923896,
+//    -0.0803145,
+//    -0.0658229,
+//    -0.0505484,
+//    -0.0358372,
+//    -0.0227036,
+//    -0.011816,
+//    -0.00351075,
+//    0.00217056,
+//    0.00542685,
+//    0.00663259,
+//    0.00626926,
+//    0.00485956,
+//    0.00290941,
+//    0.000861952,
+//    -0.000934317,
+//    -0.00224255,
+//    -0.00294038,
+//    -0.00301058,
+//    -0.00252251,
+//    -0.00160758,
+//    -0.000432323,
+//    0.000827827,
+//    0.00201128,
+//    0.00298774,
+//    0.00366917,
+//    0.00401415,
+//    0.00402614,
+//    0.00374659,
+//    0.00324473,
+//    0.00260546,
+//    0.0019171,
+//    0.0012605,
+//    0.000700568,
+//    0.000280781,
+//    2.11247e-05,
+//    -8.09498e-05,
+//    -4.69626e-05,
+//    8.79499e-05,
+//    0.000281309,
+//    0.00048961,
+//    0.000673905,
+//    0.000804074,
+//    0.000861438,
+//    0.000839602,
+//    0.000743654,
+//    0.000588041,
+//    0.000393536,
+//    0.000183806,
+//    -1.79507e-05,
+//    -0.00019189,
+//    -0.000323618,
+//    -0.000405344,
+//    -0.000436057,
+//    -0.00042083,
+//    -0.000369439,
+//    -0.000294572,
+//    -0.00020988,
+//    -0.000128166,
+//    -5.98941e-05,
+//    -1.21957e-05,
+//    1.15857e-05,
+//    1.18057e-05,
+//    -8.02811e-06,
+//    -4.21352e-05,
+//    -8.35194e-05,
+//    -0.000125046,
+//    -0.000160398,
+//    -0.000184804,
+//    -0.000195469,
+//    -0.000191702,
+//    -0.000174744,
+//    -0.000147376,
+//    -0.000113373,
+//    -7.69e-05,
+//    -4.19352e-05,
+//    -1.17913e-05,
+//    1.12168e-05,
+//    2.59321e-05,
+//    3.23475e-05,
+//    3.14515e-05,
+//    2.49686e-05,
+//    1.50404e-05,
+//    3.89847e-06,
+//    -6.42942e-06,
+//    -1.43458e-05,
+//    -1.88235e-05,
+//    -1.94538e-05,
+//    -1.64097e-05,
+//    -1.03411e-05,
+//    -2.2262e-06,
+//    6.79757e-06,
+//    1.55969e-05,
+//    2.31824e-05,
+//    2.88109e-05,
+//    3.20439e-05,
+//    3.27613e-05,
+//    3.11375e-05,
+//    2.75867e-05,
+//    2.26892e-05,
+//    1.71102e-05,
+//    1.15194e-05,
+//    6.51998e-06,
+//    2.59295e-06,
+//    5.86068e-08,
+//    -9.43009e-07
+// };
+
+//  // blFIR  Raised cosine
+//  // localSamplingrate = 12000.000000
+//  // number of taps = 251
+//  // corner frequency = 500.000000
+//  // beta = 0.700000
+//  // window applied
+//  const DSPFLOAT wideRXBLFIR[RXNUMTAPS]=
+//  {
+//     6.78868e-06,
+//     7.62652e-06,
+//     7.3611e-06,
+//     5.89395e-06,
+//     3.33524e-06,
+//     1.60138e-10,
+//     -3.63027e-06,
+//     -6.98132e-06,
+//     -9.48175e-06,
+//     -1.06723e-05,
+//     -1.03077e-05,
+//     -8.43229e-06,
+//     -5.41094e-06,
+//     -1.90134e-06,
+//     1.23557e-06,
+//     3.08018e-06,
+//     2.83336e-06,
+//     -7.55651e-10,
+//     -5.46557e-06,
+//     -1.30733e-05,
+//     -2.18248e-05,
+//     -3.03406e-05,
+//     -3.70925e-05,
+//     -4.0704e-05,
+//     -4.02644e-05,
+//     -3.5593e-05,
+//     -2.73867e-05,
+//     -1.7202e-05,
+//     -7.24703e-06,
+//     -1.63e-09,
+//     2.28199e-06,
+//     -1.89402e-06,
+//     -1.28279e-05,
+//     -2.93639e-05,
+//     -4.89128e-05,
+//     -6.77847e-05,
+//     -8.18086e-05,
+//     -8.71484e-05,
+//     -8.11663e-05,
+//     -6.31571e-05,
+//     -3.47725e-05,
+//     -1.22628e-09,
+//     3.5357e-05,
+//     6.46837e-05,
+//     8.18541e-05,
+//     8.26343e-05,
+//     6.58754e-05,
+//     3.42194e-05,
+//     -5.90498e-06,
+//     -4.5097e-05,
+//     -7.25517e-05,
+//     -7.81545e-05,
+//     -5.47315e-05,
+//     8.4255e-10,
+//     8.22444e-05,
+//     0.000182046,
+//     0.000284521,
+//     0.00037229,
+//     0.000428806,
+//     0.000441971,
+//     0.000407341,
+//     0.000330171,
+//     0.000225707,
+//     0.000117409,
+//     3.31634e-05,
+//     2.85785e-09,
+//     3.82104e-05,
+//     0.000155948,
+//     0.000345618,
+//     0.000582967,
+//     0.000829548,
+//     0.00103851,
+//     0.00116306,
+//     0.00116619,
+//     0.00102993,
+//     0.000762039,
+//     0.00039841,
+//     2.79295e-09,
+//     -0.000356014,
+//     -0.000589978,
+//     -0.000636324,
+//     -0.000460149,
+//     -7.0336e-05,
+//     0.000473995,
+//     0.00106621,
+//     0.00156515,
+//     0.00181749,
+//     0.00168694,
+//     0.00108532,
+//     7.96117e-10,
+//     -0.00148873,
+//     -0.00320568,
+//     -0.00489779,
+//     -0.00627071,
+//     -0.00704213,
+//     -0.00700509,
+//     -0.00609177,
+//     -0.00442574,
+//     -0.00235059,
+//     -0.00042436,
+//     0.000627163,
+//     -1.18464e-09,
+//     -0.00306066,
+//     -0.00910697,
+//     -0.0183266,
+//     -0.0303951,
+//     -0.0443657,
+//     -0.0586203,
+//     -0.0708981,
+//     -0.0784119,
+//     -0.0780524,
+//     -0.0666671,
+//     -0.0413915,
+//     -1.94497e-09,
+//     0.0587624,
+//     0.134907,
+//     0.227025,
+//     0.332237,
+//     0.44628,
+//     0.563746,
+//     0.678442,
+//     0.783855,
+//     0.873667,
+//     0.942288,
+//     0.985332,
+//           1,
+//     0.985332,
+//     0.942288,
+//     0.873667,
+//     0.783855,
+//     0.678442,
+//     0.563746,
+//     0.44628,
+//     0.332237,
+//     0.227025,
+//     0.134907,
+//     0.0587624,
+//     -1.94497e-09,
+//     -0.0413915,
+//     -0.0666671,
+//     -0.0780524,
+//     -0.0784119,
+//     -0.0708981,
+//     -0.0586203,
+//     -0.0443657,
+//     -0.0303951,
+//     -0.0183266,
+//     -0.00910697,
+//     -0.00306066,
+//     -1.18464e-09,
+//     0.000627163,
+//     -0.00042436,
+//     -0.00235059,
+//     -0.00442574,
+//     -0.00609177,
+//     -0.00700509,
+//     -0.00704213,
+//     -0.00627071,
+//     -0.00489779,
+//     -0.00320568,
+//     -0.00148873,
+//     7.96117e-10,
+//     0.00108532,
+//     0.00168694,
+//     0.00181749,
+//     0.00156515,
+//     0.00106621,
+//     0.000473995,
+//     -7.0336e-05,
+//     -0.000460149,
+//     -0.000636324,
+//     -0.000589978,
+//     -0.000356014,
+//     2.79295e-09,
+//     0.00039841,
+//     0.000762039,
+//     0.00102993,
+//     0.00116619,
+//     0.00116306,
+//     0.00103851,
+//     0.000829548,
+//     0.000582967,
+//     0.000345618,
+//     0.000155948,
+//     3.82104e-05,
+//     2.85785e-09,
+//     3.31634e-05,
+//     0.000117409,
+//     0.000225707,
+//     0.000330171,
+//     0.000407341,
+//     0.000441971,
+//     0.000428806,
+//     0.00037229,
+//     0.000284521,
+//     0.000182046,
+//     8.22444e-05,
+//     8.4255e-10,
+//     -5.47315e-05,
+//     -7.81545e-05,
+//     -7.25517e-05,
+//     -4.5097e-05,
+//     -5.90498e-06,
+//     3.42194e-05,
+//     6.58754e-05,
+//     8.26343e-05,
+//     8.18541e-05,
+//     6.46837e-05,
+//     3.5357e-05,
+//     -1.22628e-09,
+//     -3.47725e-05,
+//     -6.31571e-05,
+//     -8.11663e-05,
+//     -8.71484e-05,
+//     -8.18086e-05,
+//     -6.77847e-05,
+//     -4.89128e-05,
+//     -2.93639e-05,
+//     -1.28279e-05,
+//     -1.89402e-06,
+//     2.28199e-06,
+//     -1.63e-09,
+//     -7.24703e-06,
+//     -1.7202e-05,
+//     -2.73867e-05,
+//     -3.5593e-05,
+//     -4.02644e-05,
+//     -4.0704e-05,
+//     -3.70925e-05,
+//     -3.03406e-05,
+//     -2.18248e-05,
+//     -1.30733e-05,
+//     -5.46557e-06,
+//     -7.55651e-10,
+//     2.83336e-06,
+//     3.08018e-06,
+//     1.23557e-06,
+//     -1.90134e-06,
+//     -5.41094e-06,
+//     -8.43229e-06,
+//     -1.03077e-05,
+//     -1.06723e-05,
+//     -9.48175e-06,
+//     -6.98132e-06,
+//     -3.63027e-06,
+//     1.60138e-10,
+//     3.33524e-06,
+//     5.89395e-06,
+//     7.3611e-06,
+//     7.62652e-06,
+//     6.78868e-06
+//  };
+
+
+
+//const DSPFLOAT f800TX[TXNUMTAPS]=
+//{
+
+//};
+
+DSPFLOAT calculateGain(const DSPFLOAT *fp,unsigned int len)
+{
+	unsigned int i;
+	DSPFLOAT fs=0.;
+	for(i=0;i<len;i++)
+		{
+			fs+=fp[i];
+		}
+	return fs;
+}
+
+sfilters filterStruct[NUMRXFILTERS]=
+{
+//	{"800 Hz",narrowRX,1900},
+//	{"1000 Hz",wideRX,1900},
+   {"600Hz Video",wide600Hz,1700},
+//   {"600Hz Video",wide600Hz,1700},
+
+};
+
diff --git a/qsstv/dsp/filterparam.h b/qsstv/dsp/filterparam.h
new file mode 100644
index 0000000..e7c95fd
--- /dev/null
+++ b/qsstv/dsp/filterparam.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes - ON4QZ                              *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef FILTERPARAM_H
+#define FILTERPARAM_H
+#include "qsstvdefs.h"
+#include <QString>
+
+#define FILTERPARAMTYPE DSPFLOAT
+//#define FILTERPARAMTYPE FILTERPARAMTYPE
+ 
+#define RXNUMTAPS 251
+#define TXWFNUMTAPS 121
+#define NUMTAPSPOST 0
+#define NUMRXFILTERS 1
+//#define DWSBANDPASS
+
+#ifdef DWSBANDPASS
+#define DSAMPLEFILTERLEN 360
+#else
+#define DSAMPLEFILTERLEN 180
+#endif
+ 
+enum efilterType {FNARROW,FWIDE};
+enum epostFilterType {NONE};
+//extern const DSPFLOAT sharp1200BP[RXNUMTAPS];
+extern const FILTERPARAMTYPE wide1200BP[RXNUMTAPS];
+extern const FILTERPARAMTYPE wide600Hz[RXNUMTAPS];
+//extern const DSPFLOAT wideVolume[RXNUMTAPS];
+//extern const DSPFLOAT veryWideVolume[RXNUMTAPS];
+//extern const DSPFLOAT narrowRX[RXNUMTAPS];
+//extern const DSPFLOAT wideRX[RXNUMTAPS];
+//extern const DSPFLOAT wideRXBLFIR[RXNUMTAPS];
+//extern const DSPFLOAT narrowRXBLFIR[RXNUMTAPS];
+extern const FILTERPARAMTYPE downSampleFilterParam[DSAMPLEFILTERLEN];
+extern const  FILTERPARAMTYPE wfFilter[TXWFNUMTAPS];
+//extern const  DSPFLOAT  wideVolumeFilter[RXNUMTAPS];
+DSPFLOAT calculateGain(const DSPFLOAT *fp,unsigned int len);
+
+struct sfilters
+{
+  const QString filterName;
+  const FILTERPARAMTYPE *filterPtr;
+  DSPFLOAT centerFrequency;
+
+};
+
+extern sfilters filterStruct[NUMRXFILTERS];
+
+
+ 
+ #endif
diff --git a/qsstv/dsp/nco.h b/qsstv/dsp/nco.h
new file mode 100644
index 0000000..1817add
--- /dev/null
+++ b/qsstv/dsp/nco.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes - ON4QZ                              *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef NCO_H
+#define NCO_H
+#include <math.h>
+#include "qsstvglobal.h"
+#include <qsstvdefs.h>
+
+/**
+ at author Johan Maes
+*/
+
+/** Numerical Controlled Oscillator
+
+	The next value for the sine and/or cosine is calculated each time the function is called
+	
+*/
+
+class NCO  // numerical controlled oscillator
+{
+ public:
+	/** create an instance of the NCO with a given frequency
+
+	\warning the frequency is the normilized frequency i.e F/samplingrate;
+*/
+  NCO(DSPFLOAT freq=0.5)
+    {
+      init(freq);
+    }
+  ~NCO()
+    {
+    }
+	/** initialize the oscilator
+
+			This function is automatically called from the constructor
+			\param[in] frequency the frequency the oscillator must be running at
+			\warning the frequency is the normilized frequency i.e F/samplingrate;
+*/
+  void init(DSPFLOAT frequency)
+    {
+      w=(2*frequency*M_PI);
+      b=2.*cos(w);
+      s1=sin(-w);
+      s2=sin(-2.*w);
+      c1=sin(M_PI/2.-w);
+      c2=sin(M_PI/2.-2.*w);   
+    }
+
+	/** get the sine and cosine values
+		\param[out] sinVal  sine value
+		\param[out] cosVal  cosine value
+	*/
+  void getSinCos(DSPFLOAT &sinVal,DSPFLOAT &cosVal)
+    {
+      sinVal=b*s1-s2;
+      s2=s1;
+      s1=sinVal;
+      cosVal=b*c1-c2;
+      c2=c1;
+      c1=cosVal;
+    }
+
+	/** get the sine value
+		\return sine value
+	*/
+  DSPFLOAT getSine()
+    {
+      float sinVal=b*s1-s2;
+      s2=s1;
+      return(s1=sinVal);
+    }
+	/** produce the I & Q values
+	\param[out] I the I component (val multiplied by a sine)
+	\param[out] Q the Q component (val multiplied by a cosine)
+	\param[in] val the real value of the sample
+ */
+  void multiply(DSPFLOAT &i, DSPFLOAT &q,DSPFLOAT val)
+		{
+			DSPFLOAT t=b*s1-s2;
+      i=val*t;
+			s2=s1;
+			s1=t;
+			t=b*c1-c2;
+			c2=c1;
+      q=val*t;
+			c1=t;
+  	}
+
+ private:
+  DSPFLOAT w;
+  DSPFLOAT b;
+  DSPFLOAT s1,s2;
+  DSPFLOAT c1,c2;
+};
+
+
+
+
+#endif
diff --git a/qsstv/dsp/synthes.cpp b/qsstv/dsp/synthes.cpp
new file mode 100644
index 0000000..c9acd59
--- /dev/null
+++ b/qsstv/dsp/synthes.cpp
@@ -0,0 +1,263 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes - ON4QZ                              *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "synthes.h"
+#include "qsstvglobal.h"
+#include "sound/soundio.h"
+#include "sound/waterfalltext.h"
+#include "utils/supportfunctions.h"
+
+/*
+  To generate the frequency, we have to calculate the instant phase jump of the signal
+  by dividing the frequency by the SR.
+  We multiply this by the number of entries in the sine lookup table.
+  This value is added to the old index  of the sine table.
+*/
+
+synthesizer *synthesPtr;
+
+synthesizer::synthesizer(double txSmpClock)
+{
+	// generate the table
+	int i;
+	txSamplingClock=txSmpClock;
+  addToLog(QString("synthes: tx sampling clock=%1").arg(txSamplingClock),LOGSOUND);
+  for (i=0;i<SINTABLEN;i++)
+    {
+      sineTable[i]=(sin(((double)i*M_PI*2.)/SINTABLEN)*8000.+0.5);
+    }
+	oldAngle=0.;
+	adjust=0.;
+}
+
+synthesizer::~synthesizer()
+{
+}
+
+void synthesizer::sendTone(double duration,double lowerFrequency,double upperFrequency,bool concat)
+{
+//  fillBuffer();
+  if(upperFrequency!=0)
+  {
+    sendSweep(duration,lowerFrequency,upperFrequency);
+    return;
+  }
+  if(!concat) adjust=0.;
+// convert duration to number of samples
+  unsigned int ns=(unsigned int)((duration+adjust)*txSamplingClock+0.5);
+  adjust+=duration-((double)ns)/txSamplingClock;
+  sendSamples(ns,lowerFrequency);
+}
+
+void synthesizer::sendWFText()
+{
+  DSPFLOAT *dataPtr;
+  int len;
+  int i;
+  len=waterfallPtr->getLength();
+  while ((dataPtr=waterfallPtr->nextLine())!=NULL)
+    {
+      addToLog(QString("sending id len=%1").arg(len),LOGSYNTHES);
+      for (i=0;i<len;i++)
+        {
+          write((double)dataPtr[i]);
+        }
+//       arrayDump(QString("wf"),dataPtr,32,true);
+    }
+  addToLog("end of id",LOGSYNTHES);
+}
+
+void synthesizer::sendSamples(unsigned int numSamples,double frequency)
+{
+	unsigned int i;
+	for(i=0;i<numSamples;i++)
+		{
+			sendSample(frequency);
+		}
+}
+
+void synthesizer::sendSweep(unsigned int duration,double lowerFrequency, double upperFrequency)
+{
+	unsigned int i;
+	unsigned int numSamples=duration*txSamplingClock;
+	double deltaFreq=(upperFrequency-lowerFrequency)/numSamples;
+	for(i=0;i<numSamples;i++)
+	{
+		sendSample(lowerFrequency+deltaFreq*i);
+	}
+}
+
+void synthesizer::sendSilence(double duration)
+{
+	unsigned int i;
+	// convert duration to number of samples
+	unsigned int ns=(uint)(duration*txSamplingClock+0.5);
+	for(i=0;i<ns;i++)
+		{
+			write(filter(0.));
+		}
+}
+
+void synthesizer::sendSample(double freq)
+{
+	sample=nextSample(freq);
+	write(sample);
+}
+
+
+SOUNDFRAME synthesizer::filter(double sample)
+{
+ quint32 tst;
+ tst=(quint32) round(sample);
+ tst+=tst<<16;
+ return tst;
+}
+
+void synthesizer::write(double sample)
+{
+  while(!soundIOPtr->txBuffer.put(filter(sample)))
+    {
+      usleep(2000);
+    }
+
+}
+
+
+
+// buffer must already contain correct stereo information
+void synthesizer::writeBuffer(quint32 *buffer, int len)
+{
+  while((!soundIOPtr->txBuffer.put(buffer,len)) && (!soundIOPtr->stoppedPlaying()))
+    {
+      usleep(2000);
+    }
+}
+
+
+
+
+void synthesizer::setFilter(efilterType txFilterType)
+{
+
+//  filterLength=TXNUMTAPS;
+  switch (txFilterType)
+    {
+//      case F400:
+//      case F600:
+//      case F1000:
+//      case F800:
+      //filterI=f800TX;
+      default:
+      break;
+    }
+}
+
+//void synthesizer::fillBuffer()
+//{
+//  unsigned int i;
+//  unsigned int sz;
+//  sz=soundIOPtr->txBuffer.getBufferSize();
+//  for(i=0;i<50000;i++)
+//    {
+//      soundIOPtr->txBuffer.put((SOUNDFRAME)(round(nextSample(2300))));
+//    }
+//  for(;i<(sz);i++)
+//    {
+//      soundIOPtr->txBuffer.put((SOUNDFRAME)(round(nextSample(1500))));
+//    }
+//  for(;i<sz-1;i++)
+//    {
+//      soundIOPtr->txBuffer.put((SOUNDFRAME)(round(0)));
+//    }
+//  addToLog(QString("buffercount %1").arg(soundIOPtr->txBuffer.getWriteIndex()),LOGSYNTHES);
+//}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qsstv/dsp/synthes.h b/qsstv/dsp/synthes.h
new file mode 100644
index 0000000..0e060f9
--- /dev/null
+++ b/qsstv/dsp/synthes.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes - ON4QZ                              *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef SYNTHES_H
+#define SYNTHES_H
+
+#include <math.h>
+#include "filterparam.h"
+
+#define SINTABLEN 2048
+
+class synthesizer
+{
+public:
+	synthesizer(double txSmpClock);
+	~synthesizer();
+	double nextSample(double freq)
+		{
+  		double temp;
+  		int t;
+  		temp=(freq/txSamplingClock)*(double)SINTABLEN+oldAngle;
+  		oldAngle=fmod(temp,SINTABLEN);
+  		t=(int)(oldAngle+0.5);
+      return sineTable[t%SINTABLEN];
+		}
+	void sendTone(double duration,double lowerFrequency,double upperFrequency, bool concat);
+	void sendSamples(unsigned int numSamples,double frequency);
+	void sendSweep(unsigned int duration,double lowerFrequency, double upperFrequency);
+	void sendSilence(double duration);
+	void sendSample(double freq);
+	void setFilter(efilterType txFilterType);
+  void sendWFText();
+  void writeBuffer(quint32 *buffer,int len);
+private:
+	double txSamplingClock;
+	double oldAngle;
+	double sineTable[SINTABLEN];
+	const float *filterI;
+	unsigned int filterLength;
+  SOUNDFRAME filter(double sample);
+  void write(double sample);
+	double sample;
+	double adjust;
+//  void fillBuffer();  //only for test
+};
+
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/qsstv/editor/editor.cpp b/qsstv/editor/editor.cpp
new file mode 100644
index 0000000..766ed18
--- /dev/null
+++ b/qsstv/editor/editor.cpp
@@ -0,0 +1,311 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "editor.h"
+
+#include <QtGui>
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+#include "editorview.h"
+#include "configparams.h"
+#include "dispatcher.h"
+
+/*! 
+  constructor
+*/
+editor::editor(QWidget *parent,Qt::WindowFlags flags): QMainWindow(parent,flags)
+{
+  ev=new editorView(this);
+  setCentralWidget (ev);
+  initActions();
+  initMenubar();
+  statusBar()->showMessage("Select a tool");
+  setMinimumSize(640,480);
+  resize(640,480);
+  addToLog (QString(" editor create: %1")
+            .arg(QString::number((ulong)this,16)),LOGEDIT);
+  setWindowTitle("QSSTV Editor");
+}
+
+/*! 
+  destructor (saves settings on deletion)
+*/
+
+editor::~editor()
+{
+  addToLog (QString(" editor delete: %1")
+            .arg(QString::number((ulong)this,16)),LOGEDIT);
+  writeSettings();
+}
+
+/*! 
+  reads the settings (saved images for tx,rx,templates)
+*/
+
+void editor::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup ("Editor" );
+  int windowWidth = qSettings.value( "windowWidth", 640 ).toInt();
+  int windowHeight = qSettings.value( "windowHeight", 480 ).toInt();
+  int windowX = qSettings.value( "windowX", -1 ).toInt();
+  int windowY = qSettings.value( "windowY", -1 ).toInt();
+  if ( windowX != -1 || windowY != -1 ) 	move ( windowX, windowY );
+  resize ( windowWidth, windowHeight );
+  qSettings.endGroup();
+}
+
+/*! 
+  writes the settings (saved images for tx,rx,templates)
+*/
+void editor::writeSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup ("Editor" );
+  qSettings.setValue ( "windowWidth", width() );
+  qSettings.setValue ( "windowHeight", height() );
+  qSettings.setValue ( "windowX", x() );
+  qSettings.setValue ( "windowY", y() );
+  qSettings.endGroup();
+}
+
+
+void editor::initActions()
+{
+  fileNew = new QAction(QIcon(":/icons/filenew.png"),tr("&New"),this);
+  fileNew->setShortcut(tr("Ctrl+N"));
+  fileNew->setStatusTip(tr("Create a new image"));
+  connect(fileNew, SIGNAL(triggered()), this, SLOT(slotFileNew()));
+
+  fileOpen = new QAction(QIcon(":/icons/fileopen.png"),tr("&Open"),this);
+  fileOpen->setShortcut(tr("Ctrl+O"));
+  fileOpen->setStatusTip(tr("Open an image file"));
+  connect(fileOpen, SIGNAL(triggered()), this, SLOT(slotFileOpen()));
+
+  fileSave = new QAction(QIcon(":/icons/filesave.png"),tr("&Save file .."),this);
+  fileSave->setStatusTip(tr("Save the file under the same name and format"));
+  connect(fileSave, SIGNAL(triggered()), this, SLOT(slotFileSave()));
+
+  fileSaveImage = new QAction(tr("Save &Image file .."),this);
+  fileSaveImage->setStatusTip(tr("Save the file in PNG format"));
+  connect(fileSaveImage, SIGNAL(triggered()), this, SLOT(slotFileSaveImage()));
+
+  fileSaveTemplate = new QAction(("Save &Template .."),this);
+  fileSaveTemplate->setStatusTip(tr("Save template file "));
+  connect(fileSaveTemplate, SIGNAL(triggered()), this, SLOT(slotFileSaveTemplate()));
+
+  fileQuit = new QAction(tr("Quit"),this);
+  fileQuit->setShortcut(tr("Ctrl+Q"));
+  fileQuit->setStatusTip(tr("Quits the editor"));
+  connect(fileQuit, SIGNAL(triggered()), this, SLOT(slotFileQuit()));
+
+  clearAll= new QAction(QIcon(":/icons/eraser.png"),tr("Clear &All"),this);
+  clearAll->setShortcut(tr("Ctrl+A"));
+  clearAll->setStatusTip(tr("Delete all objects and fill the background with the background color"));
+  connect(clearAll, SIGNAL(triggered()), ev, SLOT(slotClearAll()));
+
+  copy= new QAction(tr("Copy"),this);
+  copy->setShortcut(tr("Ctrl+C"));
+  connect(copy, SIGNAL(triggered()), ev->getScene(), SLOT(slotCopy()));
+
+  paste= new QAction(tr("Paste"),this);
+  paste->setShortcut(tr("Ctrl+V"));
+  connect(paste, SIGNAL(triggered()), ev->getScene(), SLOT(slotPaste()));
+
+  deleteAction=new QAction(tr("&Delete"),this);
+  deleteAction->setShortcut(tr("Del"));
+  connect(deleteAction, SIGNAL(triggered()), ev->getScene(), SLOT(slotDeleteItem()));
+
+  dump= new QAction(tr("dump"),this);
+  connect(dump, SIGNAL(triggered()), ev, SLOT(slotDump()));
+}
+
+
+
+void editor::initMenubar()
+{
+  fileMenu=menuBar()->addMenu(tr("&File"));
+  editMenu=menuBar()->addMenu(tr("&Edit"));
+  fileMenu->addAction(fileNew);
+  fileMenu->addAction(fileOpen);
+  fileMenu->addAction(fileSave);
+  fileMenu->addAction(fileSaveImage);
+  fileMenu->addAction(fileSaveTemplate);
+  fileMenu->addAction(fileQuit);
+  editMenu->addAction(deleteAction);
+  editMenu->addAction(copy);
+  editMenu->addAction(paste);
+  editMenu->addAction(clearAll);
+  editMenu->addAction(dump);
+}
+
+
+void editor::slotFileNew()
+{
+  if(ev->isModified())
+    {
+      switch( QMessageBox::information( this, "Editor",
+                                        "The document has not been saved as a template\n",
+                                        "&Continue Anyway","Cancel",NULL,
+                                        -1,      // Enter == button 0
+                                        1 ) )
+        { // Escape == button 2
+        case 0: // Continu clicked
+        break;
+        case 1: // Cancel clicked
+        return;
+        break;
+        }
+    }
+  ev->slotClearAll();
+  localFile.close();
+  localFile.setFileName("");
+}
+
+void editor::slotFileOpen()
+{
+  /*	QFileDialog *fd = new QFileDialog(this,0,true);
+  fd->show();*/
+  dirDialog d(this,0);
+  QString s=d.openFileName(txImagesPath,"*.png *.gif *.jpg *.templ");
+  if (s==QString::null) return ;
+  if (s.isEmpty()) return ;
+  localFile.setFileName(s);
+  ev->open(localFile);
+  addToLog("localfile after open = " + localFile.fileName(),LOGEDIT);
+}
+
+/*!
+    \fn editor::slotFileSave()
+    \brief save file under same name and same type
+*/
+
+void editor::slotFileSave()
+{
+  if(localFile.fileName().isEmpty())
+    {
+      slotFileSaveTemplate();
+      return;
+    }
+  if(ev->getScene()->getImageType()==editorScene::FLATIMAGE)
+    {
+      addToLog("localfile to save = " + localFile.fileName(),LOGEDIT);
+      ev->save(localFile,false);
+    }
+  else
+    {
+      ev->save(localFile,true);
+    }
+}
+
+void editor::slotFileSaveImage()
+{
+  dirDialog d((QWidget *)this,"Editor");
+  QString s(localFile.fileName());
+  if(s.isEmpty())
+    {
+      s=txImagesPath;
+    }
+  s=d.saveFileName(s,"*.png","png");
+  if (s==QString::null) return ;
+  if (s.isEmpty()) return ;
+  localFile.setFileName(s);
+  ev->save(localFile,false);
+}
+
+void editor::slotFileSaveTemplate()
+{
+  dirDialog d((QWidget *)this,"Browse");
+  QString s(localFile.fileName());
+  if(s.isEmpty())
+    {
+      s=templatesPath;
+    }
+  s=d.saveFileName(s,"*.templ","templ");
+  if (s==QString::null) return ;
+  if (s.isEmpty()) return ;
+  localFile.setFileName(s);
+  ev->save(localFile,true);
+}
+
+
+void editor::slotFileQuit()
+{
+  close();
+}
+
+
+
+void editor::closeEvent(QCloseEvent *e)
+{
+
+  if(ev->isModified())
+    {
+      QMessageBox msgBox;
+      msgBox.setText("The document has been modified.");
+      msgBox.setInformativeText("Do you want to save your changes?");
+      msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
+      msgBox.setDefaultButton(QMessageBox::Save);
+      int ret = msgBox.exec();
+      switch (ret)
+        {
+        case QMessageBox::Save:
+          slotFileSave();
+        break;
+        case QMessageBox::Discard:
+          // Don't Save was clicked
+        break;
+        case QMessageBox::Cancel:
+        return;
+        break;
+        default:
+          // should never be reached
+        break;
+        }
+    }
+
+  editorFinishedEvent *ce = new editorFinishedEvent(true,localFile.fileName());
+  QApplication::postEvent(dispatcherPtr, ce );  // Qt will delete it when done	emit imageAvailable(ev->getImage());
+  writeSettings();
+  e->accept();
+}
+
+
+//bool editor::render(QImage **im,QString fn)
+//{
+//	if(!openFile(fn)) return false;
+//  *im=ev->getScene()->renderImage();
+//  addToLog(QString("editor: render size: %1 x %2").arg((*im)->size().width()).arg((*im)->size().height()),LOGEDIT);
+//  return true;
+//}
+
+bool editor::setImage(QImage *im)
+{
+  ev->setImage(im);
+  return true;
+}
+
+bool editor::openFile(QString fn)
+{
+  QFile f(fn);
+  localFile.setFileName(fn);
+  return ev->open(f);
+}
+
diff --git a/qsstv/editor/editor.h b/qsstv/editor/editor.h
new file mode 100644
index 0000000..891c261
--- /dev/null
+++ b/qsstv/editor/editor.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef EDITOR_H
+#define EDITOR_H
+
+#include <QMainWindow>
+#include <QFile>
+#include <QComboBox>
+
+
+
+class editorView;
+
+/*!
+Mainwindow for the image gallery
+
+This editor allows the creation of images and templates.
+*/
+
+/*!
+ at author Johan Maes - ON4QZ
+*/
+class editor : public QMainWindow
+{
+	Q_OBJECT
+
+public:
+	editor(QWidget *parent=0,Qt::WindowFlags flags = 0);
+	~editor();
+//	void editImage(QImage *ima);
+	bool openFile(QString fn);
+	void readSettings();
+//  bool render(QImage **im,QString fn);
+  bool setImage(QImage *im);
+
+public slots:
+	void slotFileNew();
+	void slotFileOpen();
+	void slotFileSave();
+	void slotFileSaveImage();
+	void slotFileSaveTemplate();
+	void slotFileQuit();
+
+
+private:
+	editorView *ev;
+	void closeEvent(QCloseEvent *);
+	void initActions();
+	void initMenubar();
+
+	void writeSettings();
+	QAction *fileNew;
+	QAction *fileOpen;
+	QAction *fileSave;
+	QAction *fileSaveImage;
+	QAction *fileSaveTemplate;
+	QAction *fileQuit;
+	QAction *clearAll;
+	QAction *copy;
+	QAction *paste;
+	QAction *deleteAction;
+	QAction *dump;
+	QMenu *fileMenu;
+	QMenu *editMenu;
+	QFile localFile;
+	QFile externalFile;
+};
+
+#endif
diff --git a/qsstv/editor/editorform.ui b/qsstv/editor/editorform.ui
new file mode 100644
index 0000000..dd299fd
--- /dev/null
+++ b/qsstv/editor/editorform.ui
@@ -0,0 +1,1661 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>editorForm</class>
+ <widget class="QWidget" name="editorForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>808</width>
+    <height>520</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="font">
+   <font>
+    <family>Adobe Helvetica</family>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Editor</string>
+  </property>
+  <property name="autoFillBackground">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_5">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_7">
+     <item>
+      <widget class="QComboBox" name="sizeComboBox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>80</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>22</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QFontComboBox" name="fontComboBox">
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="editable">
+        <bool>false</bool>
+       </property>
+       <property name="fontFilters">
+        <set>QFontComboBox::ScalableFonts</set>
+       </property>
+       <property name="currentFont">
+        <font>
+         <family>Bitstream Charter</family>
+        </font>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="fontSizeSpinBox"/>
+     </item>
+     <item>
+      <widget class="QPushButton" name="boldButton">
+       <property name="minimumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Sans Serif</family>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>B</string>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="italicButton">
+       <property name="minimumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <weight>75</weight>
+         <italic>true</italic>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>I</string>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="underlineButton">
+       <property name="minimumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <weight>75</weight>
+         <bold>true</bold>
+         <underline>true</underline>
+        </font>
+       </property>
+       <property name="text">
+        <string>U</string>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Pen width</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="penWidthSpinBox"/>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>298</width>
+         <height>24</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Fill</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="fillToolButton">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="iconSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="popupMode">
+        <enum>QToolButton::MenuButtonPopup</enum>
+       </property>
+       <property name="arrowType">
+        <enum>Qt::NoArrow</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Line</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="lineToolButton">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="iconSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="popupMode">
+        <enum>QToolButton::MenuButtonPopup</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Gradient</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="gradientToolButton">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="iconSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="popupMode">
+        <enum>QToolButton::MenuButtonPopup</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_6">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0,1,0">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="title">
+          <string/>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_4">
+          <property name="spacing">
+           <number>2</number>
+          </property>
+          <property name="margin">
+           <number>1</number>
+          </property>
+          <item>
+           <layout class="QVBoxLayout" name="verticalLayout">
+            <property name="spacing">
+             <number>2</number>
+            </property>
+            <item>
+             <widget class="QPushButton" name="arrowPushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/arrow.png</normaloff>:/icons/arrow.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="circlePushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/fcircle.png</normaloff>:/icons/fcircle.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="rectanglePushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/frect.png</normaloff>:/icons/frect.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="linePushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/line.png</normaloff>:/icons/line.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="imagePushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/image.png</normaloff>:/icons/image.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="replayPushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/replay.png</normaloff>:/icons/replay.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="textPushButton">
+              <property name="maximumSize">
+               <size>
+                <width>30</width>
+                <height>30</height>
+               </size>
+              </property>
+              <property name="text">
+               <string/>
+              </property>
+              <property name="icon">
+               <iconset resource="../qsstv.qrc">
+                <normaloff>:/icons/text.png</normaloff>:/icons/text.png</iconset>
+              </property>
+              <property name="iconSize">
+               <size>
+                <width>22</width>
+                <height>22</height>
+               </size>
+              </property>
+              <property name="autoExclusive">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>45</width>
+           <height>88</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_6" stretch="0,2,0">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <item>
+        <spacer name="verticalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>2</width>
+           <height>4</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QGraphicsView" name="canvas">
+         <property name="frameShape">
+          <enum>QFrame::Box</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Plain</enum>
+         </property>
+         <property name="lineWidth">
+          <number>4</number>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>2</width>
+           <height>4</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,2,0,0">
+       <item>
+        <widget class="QLabel" name="vshearLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>V-Shear</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1,0">
+         <item>
+          <spacer name="horizontalSpacer_4">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>2</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QSlider" name="vshearSlider">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="minimumSize">
+            <size>
+             <width>0</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>40</width>
+             <height>16777215</height>
+            </size>
+           </property>
+           <property name="minimum">
+            <number>-25</number>
+           </property>
+           <property name="maximum">
+            <number>25</number>
+           </property>
+           <property name="pageStep">
+            <number>5</number>
+           </property>
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="tickPosition">
+            <enum>QSlider::TicksAbove</enum>
+           </property>
+           <property name="tickInterval">
+            <number>5</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_5">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>2</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QLCDNumber" name="vshearLCD">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>80</width>
+           <height>30</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="Base">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>170</green>
+               <blue>0</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Window">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>0</green>
+               <blue>255</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="Base">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>170</green>
+               <blue>0</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Window">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>0</green>
+               <blue>255</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="Base">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>0</green>
+               <blue>255</blue>
+              </color>
+             </brush>
+            </colorrole>
+            <colorrole role="Window">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>0</red>
+               <green>0</green>
+               <blue>255</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Panel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="smallDecimalPoint">
+          <bool>true</bool>
+         </property>
+         <property name="numDigits">
+          <number>4</number>
+         </property>
+         <property name="mode">
+          <enum>QLCDNumber::Dec</enum>
+         </property>
+         <property name="segmentStyle">
+          <enum>QLCDNumber::Filled</enum>
+         </property>
+         <property name="value" stdset="0">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="intValue" stdset="0">
+          <number>0</number>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="hshearLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>60</width>
+         <height>25</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>60</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>H-Shear</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" colspan="3">
+      <widget class="QSlider" name="hshearSlider">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="minimum">
+        <number>-25</number>
+       </property>
+       <property name="maximum">
+        <number>25</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>5</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TicksBelow</enum>
+       </property>
+       <property name="tickInterval">
+        <number>5</number>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="4">
+      <widget class="QLCDNumber" name="hshearLCD">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>80</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>100</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>170</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>170</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::Panel</enum>
+       </property>
+       <property name="frameShadow">
+        <enum>QFrame::Sunken</enum>
+       </property>
+       <property name="lineWidth">
+        <number>3</number>
+       </property>
+       <property name="smallDecimalPoint">
+        <bool>true</bool>
+       </property>
+       <property name="numDigits">
+        <number>4</number>
+       </property>
+       <property name="mode">
+        <enum>QLCDNumber::Dec</enum>
+       </property>
+       <property name="segmentStyle">
+        <enum>QLCDNumber::Filled</enum>
+       </property>
+       <property name="value" stdset="0">
+        <double>0.000000000000000</double>
+       </property>
+       <property name="intValue" stdset="0">
+        <number>0</number>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="rotateLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>60</width>
+         <height>25</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>60</width>
+         <height>50</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Rotate</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="1" column="2">
+      <widget class="QDial" name="rotateDial">
+       <property name="minimumSize">
+        <size>
+         <width>80</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>80</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>192</red>
+             <green>192</green>
+             <blue>192</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>160</red>
+             <green>160</green>
+             <blue>160</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>85</green>
+             <blue>85</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="AlternateBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>220</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>192</red>
+             <green>192</green>
+             <blue>192</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>160</red>
+             <green>160</green>
+             <blue>160</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>85</green>
+             <blue>85</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="AlternateBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>220</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>192</red>
+             <green>192</green>
+             <blue>192</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>160</red>
+             <green>160</green>
+             <blue>160</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>85</green>
+             <blue>85</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>64</red>
+             <green>64</green>
+             <blue>64</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="AlternateBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipBase">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>220</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ToolTipText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="minimum">
+        <number>0</number>
+       </property>
+       <property name="maximum">
+        <number>359</number>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="invertedAppearance">
+        <bool>false</bool>
+       </property>
+       <property name="invertedControls">
+        <bool>false</bool>
+       </property>
+       <property name="wrapping">
+        <bool>true</bool>
+       </property>
+       <property name="notchTarget">
+        <double>10.000000000000000</double>
+       </property>
+       <property name="notchesVisible">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="3">
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="1" column="4">
+      <widget class="QLCDNumber" name="rotateLCD">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>80</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>80</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>170</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>170</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::Panel</enum>
+       </property>
+       <property name="frameShadow">
+        <enum>QFrame::Sunken</enum>
+       </property>
+       <property name="lineWidth">
+        <number>3</number>
+       </property>
+       <property name="smallDecimalPoint">
+        <bool>true</bool>
+       </property>
+       <property name="numDigits">
+        <number>4</number>
+       </property>
+       <property name="mode">
+        <enum>QLCDNumber::Dec</enum>
+       </property>
+       <property name="segmentStyle">
+        <enum>QLCDNumber::Filled</enum>
+       </property>
+       <property name="value" stdset="0">
+        <double>0.000000000000000</double>
+       </property>
+       <property name="intValue" stdset="0">
+        <number>0</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+  <include location="../qsstv.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/qsstv/editor/editorscene.cpp b/qsstv/editor/editorscene.cpp
new file mode 100644
index 0000000..9bc94d0
--- /dev/null
+++ b/qsstv/editor/editorscene.cpp
@@ -0,0 +1,751 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include <QtGui>
+
+#include "editorscene.h"
+#include "gradientdialog.h"
+#include "qsstvglobal.h"
+#include "ui_textform.h"
+#include "gallerywidget.h"
+#include "utils/qjp2io.h"
+
+editorScene::editorScene(QGraphicsView *parent)  : QGraphicsScene(parent)
+{
+	contextMenu=new QMenu();
+	arrange = new QMenu( "Arrange");
+	arrange->setTearOffEnabled(true);
+	arrange->addAction("Forward",this,SLOT(slotSendForward()));
+	arrange->addAction("Backward",this,SLOT(slotSendBackward()));
+	arrange->addAction("Bring to front",this,SLOT(slotBringToFront()));
+	arrange->addAction("Send to back",this,SLOT(slotSendToBack()));
+	contextMenu->addMenu(arrange);
+	contextMenu->addSeparator();
+	contextMenu->addAction("Change Text",this,SLOT(slotChangeText()));
+//	contextMenu->addAction("Copy",this,SLOT(slotCopy()));
+//	contextMenu->addAction("Paste",this,SLOT(slotPaste()));
+	contextMenu->addSeparator();
+	contextMenu->addAction("Delete",this,SLOT(slotDeleteItem()));
+	contextMenu->addAction("Expand",this,SLOT(slotExpand()));
+	contextMenu->addAction("Lock",this,SLOT(slotLock()));
+	contextMenu->addAction("Unlock",this,SLOT(slotUnlock()));
+	zMax=0;
+	pasted=false;
+	copyItem=NULL;
+	mode=MOVE;
+	imageType=NONE;
+  localImage=NULL;
+  rotate=0;
+  vShear=0;
+  hShear=0;
+  border=QRectF(0,0,0,0);
+  borderItemPtr=NULL;
+  penWidth=1;
+}
+
+editorScene::~editorScene()
+{
+  if(localImage!=NULL) delete localImage;
+	if((!pasted) &&(copyItem!=NULL)) delete copyItem;
+  delete arrange;
+  delete contextMenu;
+}
+
+
+bool editorScene::load(QFile &f)
+{
+  bool borderSet=false;
+	QImage im;
+	itemBase *item;
+	quint32 magic;
+	QString version;
+	quint16 streamVersion;
+	int type;
+	if(!f.open(QIODevice::ReadOnly)) return false;
+	QDataStream str(&f);
+  str >> magic;
+	
+  if (magic != MAGICNUMBER)
+		{
+			//try to load an image
+			f.reset();
+			if(im.load(&f,0))
+				{
+          addToLog("image loaded",LOGEDIT);
+					imageType=FLATIMAGE;
+					setImage(&im);
+          border=QRect(0,0,im.width(),im.height());
+          borderSet=true;
+					f.close();
+					return true;
+				}
+			else
+				{
+          addToLog("image failed to load",LOGEDIT);
+					f.close();
+					return false;
+				}
+		}
+	imageType=TEMPLATE;
+	str >> version;  // at this moment we do not use the version
+	str >> streamVersion;
+	str.setVersion(streamVersion);
+	while (!str.atEnd())
+		{
+			str >> type;
+			switch (type)
+				{
+					case itemBase::RECTANGLE:
+						item=new itemRectangle(contextMenu);
+					break;
+					case itemBase::ELLIPSE:
+						item=new itemEllipse(contextMenu);
+					break;
+					case itemBase::LINE:
+						item=new itemLine(contextMenu);
+					break;
+					case itemBase::TEXT:
+						item=new itemText(contextMenu);
+					break;
+					case itemBase::IMAGE:
+						item=new itemImage(contextMenu);
+					break;
+          case itemBase::REPLAY:
+            item=new itemReplayImage(contextMenu);
+          break;
+        case itemBase::SBORDER:
+            borderSet=true;
+            item=new itemImage(contextMenu);
+            item->load(str);
+            border=item->rect();
+            delete item;
+            continue;
+        break;
+					default:
+            addToLog("Error in datastream",LOGEDIT);
+						f.close();
+						return false;
+					break;
+				}
+			item->load(str);
+      addItem(item);
+
+//      item->setTransform();
+      //itemSetup(item);
+		}
+  //border=sceneRect();
+  optimizeDepth();
+  if(!borderSet) border=QRectF(0,0,320,256);
+  addToLog(QString("border position %1,%2 size: %3 x %4 border set=%5")
+               .arg(border.topLeft().x()).arg(border.topLeft().y())
+               .arg(border.width()).arg(border.height()).arg(borderSet),LOGEDIT);
+  f.close();
+  setSceneRect(border);
+	return true;
+}
+
+QImage *editorScene::renderImage(int w,int h)
+{
+  clearSelection();
+  if (localImage!=NULL) delete localImage;
+  //border=sceneRect();
+  if(w==0)
+    {
+      localImage=new QImage(border.width(),border.height(),QImage::Format_ARGB32_Premultiplied);
+    }
+  else
+    {
+      localImage=new QImage(w,h,QImage::Format_ARGB32_Premultiplied);
+    }
+  addToLog(QString("editorScene: pre-render size: %1 x %2").arg(localImage->size().width()).arg(localImage->size().height()),LOGEDIT);
+  QPainter painter(localImage);
+  painter.setRenderHint(QPainter::Antialiasing);
+  //setSceneRect(0,0,localImage->width(),localImage->height());
+  localImage->fill(0);
+  render(&painter);
+  addToLog(QString("editor: post-render size: %1 x %2").arg(localImage->size().width()).arg(localImage->size().height()),LOGEDIT);
+  return localImage;
+}
+
+void editorScene::flattenImage(int w,int h)
+{
+  if (localImage!=NULL) delete localImage;
+  setSceneRect(border);
+//  border=sceneRect();
+  localImage=new QImage(w,h,QImage::Format_ARGB32_Premultiplied);
+  convertText();
+  convertReplayImage();
+  QPainter painter(localImage);
+  painter.setRenderHint(QPainter::Antialiasing);
+//  setSceneRect(0,0,localImage->width(),localImage->height());
+  localImage->fill(0);
+  render(&painter);
+}
+
+
+void editorScene::convertReplayImage()
+{
+    QString fn;
+    QImage im;
+    fn=galleryWidgetPtr->getLastRxImage();
+    if(fn.right(4).toUpper()==".JP2")
+    {
+        im=readJP2Image(fn);
+    }
+    else
+    {
+        im.load(fn);
+    }
+   if(im.isNull()) return;
+  //itemBase *it;
+  foreach(QGraphicsItem *t,items())
+    {
+      //it=qgraphicsitem_cast<itemBase *>(t);
+      if(t->type()==itemBase::REPLAY)
+        {
+          itemReplayImage *itt=qgraphicsitem_cast<itemReplayImage *>(t);
+          itt->setImage(im);
+        }
+    }
+}
+
+void editorScene::convertText()
+{
+  //itemBase *it;
+  foreach(QGraphicsItem *t,items())
+    {
+    //  it=qgraphicsitem_cast<itemBase *>(t);
+      if(t->type()==itemBase::TEXT)
+        {
+          itemText *itt=qgraphicsitem_cast<itemText *>(t);
+//          itt->setText(textConversion(itt->text()));
+          itt->setText(mexp.convert(itt->text()));
+        }
+    }
+}
+
+//QString editorScene::textConversion(QString str)
+//{
+//  int i,j;
+//  QChar c;
+//  convertedText.clear();
+//  bool special=false;
+//  for (i=0;i<str.length();i++)
+//  {
+//    if (special)
+//      {
+//        special=false;
+//        c=str.at(i);
+//        if(c=='%')
+//        {
+//          convertedText.append('%');
+//          continue;
+//        }
+//        for (j=0;j<convertList.count();j++)
+//          {
+//            if(c==convertList.at(j).tag)
+//            {
+//              convertedText.append(convertList.at(j).replacement);
+//            }
+//          }
+//      }
+//    else
+//      {
+//        if(str.at(i)!='%') convertedText.append(str.at(i));
+//        else special=true;
+//      }
+//  }
+//  addToLog("converted text: "+convertedText,LOGEDIT);
+//  return convertedText;
+//}
+
+
+
+bool editorScene::save(QFile &f,bool templ)
+{
+    QImage im(border.width(),border.height(),QImage::Format_ARGB32_Premultiplied);
+    setSceneRect(border);
+    addToLog(QString("editorscene:save %1 x %2").arg(sceneRect().width()).arg(sceneRect().height()),LOGEDIT);
+    if(!templ)
+    {
+        QPainter painter(&im);
+        painter.setRenderHint(QPainter::Antialiasing);
+        render(&painter);
+        im.save(&f,"PNG");
+        return true;
+    }
+    if(!f.open(QIODevice::WriteOnly)) return false;
+    QDataStream str(&f);
+    str.setVersion(QDataStream::Qt_4_4);
+    // Header with a "magic number" and a version
+    str << (quint32) MAGICNUMBER;
+    str <<  CONFIGVERSION;
+    str << (quint16) QDataStream::Qt_4_4;
+    itemBase *it;
+    foreach(QGraphicsItem *t,items())
+    {
+        it=qgraphicsitem_cast<itemBase *>(t);
+        if(t->type()>itemBase::BASE)
+        {
+            it->save(str);
+        }
+    }
+    f.close();
+    return true;
+}
+
+void editorScene::setMode(eMode m)
+{
+    mode = m;
+		if(mode==INSERT) clearSelection () ;
+}
+
+void editorScene::setItemType(itemBase::egraphType tp)
+{
+    itemType = tp;
+}
+
+void editorScene::apply(changeFlags cf)
+{
+	QPen p;
+	itemBase *it;
+	if(selectedItems().isEmpty()) return; // nothing to do 
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+				it=qgraphicsitem_cast<itemBase *>(t);
+      if(cf & DFILLCOLOR)
+				{
+				 	it->setBrush(fillColor);
+				}
+      if(cf & DLINECOLOR)
+				{
+					p=it->pen();
+					p.setColor(lineColor);
+				 it->setPen(p);
+				}
+      if(cf & DPEN)
+				{
+					p=it->pen();
+					p.setWidth(penWidth);
+					it->setPen(p);
+				}
+      if(cf & DGRADIENT)
+				{
+					gradientDialog gd;
+					sgradientParam tmp;
+					tmp=gd.param();
+					it->setGradient(tmp);
+					it->update();
+				}
+      if(cf & DTRANSFORM)
+				{
+					it->setTransform(rotate,hShear,vShear);
+				}
+			if(t->type()==itemBase::TEXT)
+				{
+					itemText *itt=qgraphicsitem_cast<itemText *>(t);
+          if(cf & DFONT) itt->setFont(font);
+          if(cf & DTEXT) itt->setText(text);
+				}
+			it->update();
+		}
+}
+
+void editorScene::clearAll()
+{
+	foreach(QGraphicsItem *t,items())
+		{
+      if((t->type()>itemBase::BASE) && (t->type()!=itemBase::SBORDER))
+				{
+					removeItem(t);
+					delete t;
+				}
+		}
+}
+
+
+void editorScene::itemSetup(itemBase *item)
+{
+	QPen p;
+	gradientDialog gd;
+	sgradientParam tmp;
+	tmp=gd.param();
+	item->setGradient(tmp);
+	item->setTransform(rotate,hShear,vShear);
+	p=item->pen();
+	p.setColor(lineColor);
+	p.setWidth(penWidth);
+	item->setPen(p);
+	item->setBrush(fillColor);
+	item->setZValue(zMax);
+	zMax+=1;
+	addItem(item);
+}
+
+void editorScene::setImage(QImage *im)
+{
+	itemBase *item;
+	item=new itemImage(contextMenu);
+	item->setImage(*im);
+	item->setRect(0,0,im->width(),im->height());
+	itemSetup(item);
+	item->setPos(QPointF(0,0));
+	item->setSelected(true);
+	emit changeSize(im->width(),im->height());
+}
+
+void editorScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+	itemBase *item;
+	QImage im;
+  if (mouseEvent->button() == Qt::LeftButton)
+		{
+			switch(mode)
+				{
+					case INSERT:
+						switch(itemType)
+							{
+								case itemBase::RECTANGLE:
+									item=new itemRectangle(contextMenu);
+									itemSetup(item);
+									item->setPos(mouseEvent->scenePos());
+								break;
+								case itemBase::LINE:
+									item=new itemLine(contextMenu);
+									itemSetup(item);
+									item->setPos(mouseEvent->scenePos());
+								break;
+								case itemBase::ELLIPSE:
+									item=new itemEllipse(contextMenu);
+									itemSetup(item);
+									item->setPos(mouseEvent->scenePos());
+								break;
+								case itemBase::TEXT:
+									if (!text.isEmpty())
+										{
+											item=new itemText(contextMenu);
+											item->setFont(font);
+											item->setText(text);
+											itemSetup(item);
+											item->setPos(mouseEvent->scenePos());
+										}
+								break;
+								case itemBase::IMAGE:
+									if(im.load(fl))
+										{
+											item=new itemImage(contextMenu);
+											item->setImage(im);	
+											itemSetup(item);
+											item->setPos(mouseEvent->scenePos());
+										}
+								break;
+								case itemBase::REPLAY:
+									item=new itemReplayImage(contextMenu);
+									itemSetup(item);
+									item->setPos(mouseEvent->scenePos());
+								break;
+                case itemBase::SBORDER:
+								case itemBase::BASE:
+								break;
+							}
+					break;
+					case MOVE:
+						if(!selectedItems().isEmpty())
+							{
+								item=qgraphicsitem_cast<itemBase *>(selectedItems().first());
+							}
+					break;
+          case PICK:
+          break;
+
+				}
+			}
+		QGraphicsScene::mousePressEvent(mouseEvent);
+}
+
+void editorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ QGraphicsScene::mouseMoveEvent(mouseEvent);
+
+}
+
+void editorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+	itemBase *item;
+	if(mode==MOVE)
+		{
+			if(!selectedItems().isEmpty())
+				{
+					item=qgraphicsitem_cast<itemBase *>(selectedItems().first());
+					emit itemSelected(item);
+				}
+		}
+	else if(mode==PICK)
+		{
+			emit colorSelected(mouseEvent->scenePos());
+			((QGraphicsView *)parent())->setCursor(Qt::ArrowCursor);
+			
+		}
+	mode=MOVE;
+	QGraphicsScene::mouseReleaseEvent(mouseEvent);
+}
+
+
+void editorScene::slotCopy()
+{
+	itemBase *item;
+	if((!pasted) &&(copyItem!=NULL)) delete copyItem;
+  if(selectedItems().isEmpty()) return; // nothing to do
+	item=qgraphicsitem_cast<itemBase *>(selectedItems().first());
+	makeCopy(item);
+}
+
+void editorScene::makeCopy(itemBase *it)
+{
+	itemBase *item=it;
+	itemBase::egraphType type=(itemBase::egraphType)item->type();
+	switch(type)
+		{
+			case itemBase::RECTANGLE:
+				copyItem=new itemRectangle(item->getParam().menu);
+			break;
+			case itemBase::LINE:
+				copyItem=new itemLine(item->getParam().menu);
+			break;
+			case itemBase::ELLIPSE:
+				copyItem=new itemEllipse(item->getParam().menu);
+			break;
+			case itemBase::TEXT:
+				copyItem=new itemText(item->getParam().menu);
+			break;
+			case itemBase::IMAGE:
+				copyItem=new itemImage(item->getParam().menu);
+			break;
+    case itemBase::REPLAY:
+      copyItem=new itemReplayImage(item->getParam().menu);
+    break;
+			default:
+				return;
+		}
+	copyItem->setParam(item->getParam());
+	copyItem->setPos(item->pos()+QPointF(10,10));
+	pasted=false;
+}
+
+void editorScene::slotPaste()
+{
+		clearSelection();
+		copyItem->setZValue(zMax+1);
+		zMax+=1;
+		addItem(copyItem);
+		pasted=true,
+		makeCopy(copyItem);
+		clearSelection();
+}
+
+void editorScene::slotExpand()
+{
+	itemBase *it;
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			it=qgraphicsitem_cast<itemBase *>(t);
+      if(it->type()!=itemBase::TEXT)
+				{
+					it->setRect(border);
+					it->setPos(0,0);
+				}
+		}
+}
+
+void editorScene::slotChangeText()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	itemText *item=qgraphicsitem_cast<itemText *>(selectedItems().first());
+  if(!item)
+    {
+      return;
+    }
+	if(item->type()!=itemBase::TEXT) return;
+	QDialog d(0);
+	Ui::textForm t;
+	t.setupUi(&d);
+	t.lineEdit->setText(item->text());
+	if(d.exec()==QDialog::Accepted)
+		{
+			item->setText(t.lineEdit->text());
+		}
+}
+
+
+void editorScene::slotDeleteItem()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			removeItem(t);
+			delete t;
+		}
+}
+
+
+void editorScene::slotLock()
+{
+	itemBase *it;
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			it=qgraphicsitem_cast<itemBase *>(t);
+			it->setLocked(true);
+		}
+}
+
+void editorScene::slotUnlock()
+{
+	
+	 itemBase *it;
+   if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			it=qgraphicsitem_cast<itemBase *>(t);
+			it->setLocked(false);
+		}
+}
+
+
+void editorScene::slotBringToFront()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			zMax+=1;
+			t->setZValue(zMax);
+    }
+	optimizeDepth();
+}
+
+void editorScene::slotSendToBack()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			t->setZValue(0.5);
+		}
+	optimizeDepth();
+}
+
+
+void editorScene::slotSendBackward()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			t->setZValue(t->zValue()-1.5);
+		}
+	optimizeDepth();
+}
+
+void editorScene::slotSendForward()
+{
+  if(selectedItems().isEmpty()) return; // nothing to do
+	foreach(QGraphicsItem *t,selectedItems())
+		{
+			t->setZValue(t->zValue()+1.5);
+		}
+	optimizeDepth();
+}
+
+void editorScene::optimizeDepth()
+{
+  itemBase *it;
+	zMax=items().count();
+	qreal i=0;
+	foreach(QGraphicsItem *t,items(itemsBoundingRect ()))
+		{
+      it=qgraphicsitem_cast<itemBase *>(t);
+      if(it->type()==itemBase::SBORDER)
+       {
+          it->setZValue(0.1);
+        }
+      else if(it->type()>itemBase::BASE)
+				{
+          it->setZValue(zMax-i);
+					i+=1;
+				}
+      addToLog(QString("optimize_1 type=%1 pos=%2,%3 resctPos=%4,%5").arg(it->getTypeStr()).arg(t->pos().x()).arg(t->pos().y()).arg(it->rect().x()).arg(it->rect().y()),LOGEDIT);
+      addToLog(QString("Boundingrect t=%1 %2,%3 %4,%5").arg(it->getTypeStr()).arg(it->boundingRect().x()).arg(it->boundingRect().y()).arg(it->boundingRect().width()).arg(it->boundingRect().height()),LOGEDIT);
+    }
+  //optimize position
+
+}
+
+void editorScene::addBorder(int w,int h)
+{
+  if (borderItemPtr==NULL)
+    {
+      borderItemPtr=new itemBorder(contextMenu);
+    }
+  itemSetup(borderItemPtr);
+  borderItemPtr->setPos(0,0);
+  borderItemPtr->setRect(0,0,w,h);
+  border=QRectF(0,0,w,h);
+  slotSendToBack();
+}
+
+void editorScene::overlay(QImage *ima)
+{
+  clearSelection();
+//  addToLog(QString("overlay: before number of items: %1").arg(items().count()),LOGEDIT);
+//  itemBase *item;
+//  item=new itemImage(contextMenu);
+//  item->setImage(*ima);
+//  itemSetup(item);
+//  item->setPos(0,0);
+//  item->setRect(0,0,ima->width(),ima->height());
+//  item->setSelected(true);
+//  slotSendToBack();
+//  clearSelection();
+//  addToLog(QString("overlay: after number of items: %1").arg(items().count()),LOGEDIT);
+ // flattenImage(ima->width(),ima->height());
+  setSceneRect(border);
+//  border=sceneRect();
+  if (localImage!=NULL) delete localImage;
+  localImage=new QImage(ima->copy());
+//  localImage=ima;
+  convertText();
+  convertReplayImage();
+  QPainter painter(localImage);
+  painter.setRenderHint(QPainter::Antialiasing);
+//  setSceneRect(0,0,localImage->width(),localImage->height());
+//  localImage->fill(0);
+  render(&painter,QRectF(),QRectF(),Qt::IgnoreAspectRatio);
+}
+
+
+void editorScene::addConversion(QChar tag,QString value,bool clear)
+{
+  if(clear) mexp.clear();
+  mexp.addConversion(tag,value);
+}
+
+
+
+
+
diff --git a/qsstv/editor/editorscene.h b/qsstv/editor/editorscene.h
new file mode 100644
index 0000000..51b52d8
--- /dev/null
+++ b/qsstv/editor/editorscene.h
@@ -0,0 +1,140 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef EDITORSCENE_H
+#define EDITORSCENE_H
+
+#include <QGraphicsScene>
+//#include "graphics.h"
+#include "graphicitems.h"
+#include "utils/supportfunctions.h"
+#include "utils/macroexpansion.h"
+
+class QGraphicsSceneMouseEvent;
+
+#define CHANGECOLOR
+#define CHANGE
+
+
+
+
+
+class editorScene : public QGraphicsScene
+{
+	Q_OBJECT
+
+public:
+	enum eImageType
+		{
+			NONE,				/*!< no image defined */
+			FLATIMAGE, /*!< loaded image is a simple image (png,jpeg,...) */
+			TEMPLATE    /*!< loaded image is a template file */
+		};
+	enum eMode { MOVE, INSERT,PICK};
+  enum doChange {DNOCHANGE = 0, DFILLCOLOR = 1, DLINECOLOR=2, DGRADIENT=4,DTEXT = 8,DFONT=16,DPEN=32,DTRANSFORM=64};
+	Q_DECLARE_FLAGS(changeFlags, doChange);
+  editorScene(QGraphicsView *parent=0);
+	~editorScene();
+	QColor fillColor;
+	QColor lineColor;
+	QGradient gradient;
+	QColor gradientColor;
+	QFont  font;
+	QString text;
+	QString fl;
+	double penWidth;
+	void apply(changeFlags cf);
+	void clearAll();
+	QRectF border;
+	int rotate;
+	qreal hShear;
+	qreal vShear;
+	bool load(QFile &f);
+	bool save(QFile &f,bool templ);
+  void setImage(QImage *im);
+	eMode mode;
+	eImageType getImageType(){return imageType;}
+  QImage *renderImage(int w, int h);
+  macroExpansion mexp;
+  void overlay(QImage *ima);
+  void addBorder(int w,int h);
+  QImage *getImagePtr() {return localImage;}
+  void addConversion(QChar tag,QString value,bool clear=false);
+
+// bool event(QEvent *);
+
+public slots:
+	void setMode(eMode m);
+	void setItemType(itemBase::egraphType tp);
+//    void editorLostFocus(DiagramTextItem *item);
+	void slotCopy();
+	void slotPaste();
+	void slotExpand();
+
+	void slotDeleteItem();
+	void slotLock();
+	void slotUnlock();
+	void slotBringToFront();
+	void slotSendToBack();
+	void slotSendBackward();
+	void slotSendForward();
+	void slotChangeText();
+
+signals:
+//	void itemInserted(itemBase *itm);
+//	void textInserted(itemBase *itm);
+	void changeSize(int,int);
+	void itemSelected(itemBase *itm);
+	void colorSelected( const QPointF &p);
+
+protected:
+	void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
+	void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
+	void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);
+
+
+private:
+	itemBase *copyItem;
+	itemBase::egraphType itemType;
+	bool leftButtonDown;
+
+	bool pasted;
+	qreal zMax;
+	QPointF startPoint;
+// Context menus
+	QMenu *contextMenu;
+	QMenu *arrange;
+
+	void optimizeDepth();
+	void itemSetup(itemBase *item);
+	void makeCopy(itemBase *it);
+	eImageType imageType;
+  QImage *localImage;
+  void flattenImage(int w,int h);
+  void convertText();
+//  QString textConversion(QString str);
+//  QString convertedText;
+  itemBase *borderItemPtr;
+  void convertReplayImage();
+
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(editorScene::changeFlags)
+
+#endif
diff --git a/qsstv/editor/editorview.cpp b/qsstv/editor/editorview.cpp
new file mode 100644
index 0000000..a22c51e
--- /dev/null
+++ b/qsstv/editor/editorview.cpp
@@ -0,0 +1,612 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+ 
+#include "editorview.h"
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+#include "graphicitems.h"
+#include "gradientdialog.h"
+#include "ui_textform.h"
+
+
+#define TBFILL 0
+#define TBLINE 1
+#define TBGRAD 2
+
+#define NUMCOLORSELECTORS 12
+
+static QColor defaultColors[NUMCOLORSELECTORS]= 
+{
+	qRgba(  0,  0,  0,255),
+	qRgba(255,255,255,255),
+	qRgba(255,  0,  0,255),
+	qRgba(  0,255,  0,255),
+	qRgba(  0,  0,255,255),
+	qRgba(128,128,128,255),
+	qRgba(255,  0,  0,128),
+	qRgba(  0,255,  0,128),
+	qRgba(  0,  0,255,128),
+	qRgba(255,255,  0,128),
+	qRgba(  0,255,255,128),
+	qRgba(255,  0,255,128)
+};
+
+
+
+
+struct sCanvasSize
+{
+	const QString s;
+	int width;
+	int height;
+};
+#define SIZES 7
+sCanvasSize canvasSizeArray[SIZES]=
+{
+	{"160x120",160,120},
+	{"320x240",320,240},
+	{"320x256",320,256},
+	{"500x400",500,400},
+	{"500x496",500,496},
+	{"640x496",640,496},
+	{"800x616",800,616}
+};
+
+int s;
+
+#define BORDER 4
+/** editorview */
+editorView::editorView(QWidget *parent):QWidget(parent), Ui::editorForm()
+{
+	setupUi(this);
+  scene=new editorScene(canvas);
+	canvas->setScene(scene);
+ // canvas->setFixedSize(800,700);;
+	pickMode=NOPICK;
+  //border= new QGraphicsRectItem(0,0,320,256);
+//	scene->border=border->rect();
+//	scene->addItem(border);
+
+	scene->setMode(editorScene::MOVE);
+	scene->setItemType(itemBase::BASE);
+	for (int i=0; i<SIZES;i++) sizeComboBox->addItem(canvasSizeArray[i].s);
+	
+	readSettings();
+	sizeComboBox->setCurrentIndex(canvasSizeIndex);
+  scene->setSceneRect(0,0,canvasSizeArray[canvasSizeIndex].width,canvasSizeArray[canvasSizeIndex].height);
+	connect(scene,SIGNAL(itemSelected(itemBase*)),SLOT(slotItemSelected(itemBase*)));
+    connect(sizeComboBox, SIGNAL(activated(int)), SLOT(slotChangeCanvasSize(int)));
+	connect(arrowPushButton,SIGNAL(clicked()),SLOT(slotArrow()));
+	connect(rectanglePushButton,SIGNAL(clicked()),SLOT(slotRectangle()));
+	connect(circlePushButton,SIGNAL(clicked()),SLOT(slotCircle()));
+	connect(replayPushButton,SIGNAL(clicked()),SLOT(slotReplay()));
+	connect(imagePushButton,SIGNAL(clicked()),SLOT(slotImage()));
+	connect(linePushButton,SIGNAL(clicked()),SLOT(slotLine()));
+	connect(textPushButton,SIGNAL(clicked()),SLOT(slotText()));
+	rotateLCD->display( "  0'" );
+  connect(rotateDial, SIGNAL(valueChanged(int)),SLOT(slotRotateChanged(int)) );
+  hshearLCD->display( "0.00" );
+	vshearLCD->display( "0.00" );
+	connect(hshearSlider, SIGNAL(valueChanged(int)),SLOT(slotShearChanged(int)) );
+  connect(vshearSlider, SIGNAL(valueChanged(int)),SLOT(slotShearChanged(int)) );
+  //connect (scene,SIGNAL(changeSize(int,int)),SLOT(slotChangeCanvasSize(int,int)));
+
+//	connect(textLineEdit, SIGNAL(textChanged(const QString &)),SLOT(slotTextReturnPressed(const QString &)) );
+//	slotRotateChanged(0);
+//	slotShearChanged(0);
+	fontComboBox->setCurrentIndex(currentFontIndex);
+
+	connect(fontComboBox,SIGNAL(currentFontChanged(const QFont &)),SLOT(slotFontChanged(const QFont &)));
+
+	fontSizeSpinBox->setRange(6, 180);
+	fontSizeSpinBox->setValue(currentPointSize);
+	scene->font.setPointSize(fontSizeSpinBox->value());
+	connect(fontSizeSpinBox,SIGNAL( valueChanged (int)),SLOT(slotFontSizeChanged(int)));
+	
+	penWidthSpinBox->setRange(0,99);
+	penWidthSpinBox->setValue(currentPenWidth);
+	connect(penWidthSpinBox,SIGNAL( valueChanged (double)),SLOT(slotPenWidthChanged(double)));
+
+
+	boldButton->setChecked(scene->font.bold());
+	italicButton->setChecked(scene->font.italic());
+	underlineButton->setChecked(scene->font.underline());
+	connect(boldButton,SIGNAL( clicked(bool)),SLOT(slotBold(bool)));
+	connect(italicButton,SIGNAL( clicked(bool)),SLOT(slotItalic(bool)));
+	connect(underlineButton,SIGNAL( clicked(bool)),SLOT(slotUnderline(bool)));
+	
+
+	QAction *action;
+	action = new QAction("Color Picker", this);
+	action->setData(TBFILL);
+  connect(action, SIGNAL(triggered()),this,SLOT(slotColorPicker()));
+
+	fillToolButton->setMenu(createColorMenu(SLOT(slotColorDialog()),TBFILL,"Select Color"));
+
+	fillToolButton->menu()->addAction(action);
+ // scene->fillColor=QColor(127,127,0);
+	fillToolButton->setIcon(createColorToolButtonIcon(":/icons/colorfill.png", scene->fillColor));
+	connect(fillToolButton, SIGNAL(clicked()),this, SLOT(slotButtonTriggered()));
+
+	action = new QAction("Color Picker", this);
+	action->setData(TBLINE);
+  connect(action, SIGNAL(triggered()),this,SLOT(slotColorPicker()));
+	lineToolButton->setMenu(createColorMenu(SLOT(slotColorDialog()),TBLINE,"Select Color"));
+	lineToolButton->menu()->addAction(action);
+  lineToolButton->setIcon(createColorToolButtonIcon(":/icons/colorline.png", scene->lineColor));
+	connect(lineToolButton, SIGNAL(clicked()),this, SLOT(slotButtonTriggered()));
+	connect(scene,SIGNAL(colorSelected( const QPointF &)),this,SLOT(slotColorPicked(const QPointF &)));
+	
+	gradientToolButton->setMenu(createColorMenu(SLOT(slotGradientDialog()),TBGRAD,"Select Gradient"));
+  gradientToolButton->setIcon(createColorToolButtonIcon(":/icons/gradient.png", scene->gradientColor));
+	connect(gradientToolButton, SIGNAL(clicked()),this, SLOT(slotButtonTriggered()));
+// setup the defaults
+	slotRotateChanged(0);
+	
+	setTransform();
+	slotFontChanged(fontComboBox->currentFont());
+	slotPenWidthChanged(currentPenWidth);
+	slotChangeCanvasSize(canvasSizeIndex);
+  slotDump();
+  modified=false;
+}
+
+
+editorView::~editorView()
+{
+	writeSettings();
+}
+
+/*! 
+	reads the settings (saved images for tx,rx,templates)
+*/
+
+void editorView::readSettings()
+{
+	QSettings qSettings;
+	qSettings.beginGroup ("Editor");
+	canvasSizeIndex=qSettings.value("canvassizeindex", 2 ).toInt();
+	currentFontIndex=qSettings.value("currentfontindex", 2 ).toInt();
+	currentPointSize=qSettings.value("currentpointSize", 24).toInt();
+	currentPenWidth=qSettings.value("currentpenwidth", 1).toDouble();
+	scene->font = qSettings.value("fillcolor", qApp->font()).value<QFont>();
+    scene->fillColor = qSettings.value("fillcolor", QColor(Qt::white)).value<QColor>();
+    scene->lineColor = qSettings.value("linecolor", QColor(Qt::black )).value<QColor>();
+    scene->gradientColor = qSettings.value("gradientcolor",QColor( Qt::red )).value<QColor>();
+	qSettings.endGroup();
+}
+
+/*! 
+	writes the settings (saved images for tx,rx,templates)
+*/
+void editorView::writeSettings()
+{
+	QSettings qSettings;
+	qSettings.beginGroup ("Editor" );
+	qSettings.setValue ("canvassizeindex", sizeComboBox->currentIndex());
+	qSettings.setValue ("currentfontindex", fontComboBox->currentIndex());
+	qSettings.setValue ("currentpenwidth", penWidthSpinBox->value());
+	qSettings.setValue ("currentpointsize",fontSizeSpinBox->value());
+	qSettings.setValue ("fillcolor", scene->fillColor);
+	qSettings.setValue ("linecolor", scene->lineColor);
+	qSettings.setValue ("gradientcolor", scene->gradientColor);
+	qSettings.endGroup();
+}
+
+
+
+void editorView::slotArrow()
+{
+	
+}
+
+void editorView::slotRectangle()
+{
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::RECTANGLE);
+	modified=true;
+}
+
+
+void editorView::slotCircle()
+{
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::ELLIPSE);
+	modified=true;
+}
+
+void editorView::slotText()
+{
+	QDialog d(this);
+	Ui::textForm t;
+	t.setupUi(&d);
+	t.lineEdit->setText(txt);
+	if(d.exec()==QDialog::Accepted)
+		{
+			scene->setMode(editorScene::INSERT);
+			scene->setItemType(itemBase::TEXT);
+			scene->text=t.lineEdit->text();
+			txt=t.lineEdit->text();
+      scene->apply(editorScene::DTEXT);
+		}
+	modified=true;
+}
+
+void editorView::slotLine()
+{
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::LINE);
+	modified=true;
+}
+
+void editorView::slotColorPicker()
+{
+	int tp;
+  colorPickImage=scene->renderImage(0,0);
+  addToLog(QString("colorpicker triggered size %1 x %2").arg(colorPickImage->width()).arg(colorPickImage->height()),LOGEDIT);
+	QAction *act;
+	act=qobject_cast<QAction *>(sender());
+	tp=act->data().toInt();
+	if (tp==TBFILL)
+			{
+				pickMode=PICKFILLCOLOR;
+				
+			}
+	else if (tp==TBLINE)
+			{
+				pickMode=PICKLINECOLOR;
+			}
+	scene->setMode(editorScene::PICK);
+	//setCursor(Qt::ArrowCursor);
+}
+
+void editorView::slotColorPicked(const QPointF &p)
+{
+	QRgb c=colorPickImage->pixel(p.x(),p.y());
+  addToLog(QString("Picked color r=%1,g=%2,b=%3 alpha=%4").arg(qRed(c)).arg(qGreen(c)).arg(qBlue(c)).arg(qAlpha(c)),LOGEDIT);
+	if(pickMode==PICKFILLCOLOR)
+		{
+			scene->fillColor.setRgba(c);
+			fillToolButton->setIcon(createColorToolButtonIcon(":/icons/colorfill.png",scene->fillColor));
+		}
+	else
+		{
+			scene->lineColor.setRgba(c);
+			lineToolButton->setIcon(createColorToolButtonIcon(":/icons/colorline.png",scene->lineColor));
+		}
+	canvas->setCursor(Qt::ArrowCursor);
+}
+
+
+void editorView::slotImage()
+{
+	QString fileName;
+    dirDialog dd((QWidget *)this,"editor");
+	scene->fl=dd.openFileName(QString());
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::IMAGE);
+	modified=true;
+}
+
+void editorView::slotReplay()
+{
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::REPLAY);
+	modified=true;
+}
+
+//void editorView::slotChangeCanvasSize(int w,int h)
+//{
+
+//	canvas->setSceneRect(0,0,w,h);
+//	border->setRect(0,0,w,h);
+//	scene->border=border->rect();
+//	modified=true;
+//}
+
+
+void editorView::slotChangeCanvasSize(int index)
+{
+  rotateDial->setValue(0);
+  hshearSlider->setValue(0);
+  vshearSlider->setValue(0);
+  setTransform();
+  scene->addBorder(canvasSizeArray[index].width,canvasSizeArray[index].height);
+  canvas->setSceneRect(0,0,canvasSizeArray[index].width,canvasSizeArray[index].height);
+  modified=true;
+}
+
+void editorView::slotFontChanged(const QFont &f)
+{
+	scene->font=f;
+	scene->font.setPointSize(fontSizeSpinBox->value());
+	scene->font.setBold(boldButton->isChecked());
+	scene->font.setItalic(italicButton->isChecked());
+	scene->font.setUnderline(underlineButton->isChecked());
+  scene->apply(editorScene::DFONT);
+	modified=true;
+}
+
+void editorView::slotFontSizeChanged(int sz)
+{
+	scene->font.setPointSize(sz);
+  scene->apply(editorScene::DFONT);
+	modified=true;
+}
+
+void editorView::slotPenWidthChanged(double pw)
+{
+	scene->penWidth=pw;
+  scene->apply(editorScene::DPEN);
+	modified=true;
+}
+void editorView::slotBold(bool b)
+{
+	scene->font.setBold(b);
+  scene->apply(editorScene::DFONT);
+	modified=true;
+}
+void editorView::slotItalic(bool b)
+{
+	scene->font.setItalic(b);
+  scene->apply(editorScene::DFONT);
+	modified=true;
+}
+void editorView::slotUnderline(bool b)
+{
+	scene->font.setUnderline(b);
+  scene->apply(editorScene::DFONT);
+	modified=true;
+}
+
+/*! \todo image insert
+		check if this is used 
+*/
+void editorView::setImage(QImage *)
+{
+	
+	scene->setMode(editorScene::INSERT);
+	scene->setItemType(itemBase::IMAGE);
+	modified=true;
+}
+
+
+void editorView::slotClearAll()
+{
+	scene->clearAll();
+}
+
+
+void editorView::slotButtonTriggered()
+{
+	QToolButton *act;
+	act=qobject_cast<QToolButton *>(sender());
+		if (act==fillToolButton)
+			{
+        scene->apply(editorScene::DFILLCOLOR);
+			}
+		else if (act==lineToolButton)
+			{
+        scene->apply(editorScene::DLINECOLOR);
+			}
+		else if (act==gradientToolButton)
+			{
+        scene->apply(editorScene::DGRADIENT);
+			}
+		else
+			{
+				return;
+			}
+}
+
+void editorView::slotColorDialog()
+{
+  int tp;
+	QAction *act;
+	QColor c;
+  act=qobject_cast<QAction *>(sender());
+	tp=act->data().toInt();
+	switch(tp)
+		{
+			case TBFILL:
+        c=QColorDialog::getColor(scene->fillColor,this,"",QColorDialog::ShowAlphaChannel);
+        if (c.isValid())
+          {
+            scene->fillColor=c;
+            fillToolButton->setIcon(createColorToolButtonIcon(":/icons/colorfill.png",scene->fillColor));
+					}
+				break;
+				case TBLINE:
+        c=QColorDialog::getColor(scene->lineColor,this,"",QColorDialog::ShowAlphaChannel);
+        if (c.isValid())
+          {
+            scene->lineColor=c;
+            lineToolButton->setIcon(createColorToolButtonIcon(":/icons/colorline.png",scene->lineColor));
+          }
+				break;
+				default:
+					qDebug() << "Error in slotColorDialog";
+				break;
+		}
+}
+
+void editorView::slotGradientDialog()
+{
+	gradientDialog gDiag(this);
+	gDiag.selectGradient();
+}
+
+void editorView::save(QFile &f,bool templ)
+{
+	scene->save(f,templ);
+  modified=false;
+}
+
+bool editorView::open(QFile &f)
+{
+	if(!scene->load(f)) return false;
+  slotDump();
+	return true;
+}
+
+
+void editorView::setTransform()
+{
+//	int r=450-rotateDial->value();
+//	if ( r >= 360 )	r-=360;
+	int r=rotateDial->value();
+	scene->rotate=r;
+	scene->hShear=(double)hshearSlider->value()/10.;
+	scene->vShear=(double)vshearSlider->value()/10.;
+  scene->apply(editorScene::DTRANSFORM);
+}
+
+/*! \todo  check if used
+*/
+void editorView::slotTextReturnPressed(const QString &)
+{
+	
+}
+
+
+void editorView::slotRotateChanged(int)
+{
+	QString tmp;
+//	int r=450-rotateDial->value();
+//	if ( r >= 360 )	r-=360;
+	int r=rotateDial->value();
+	tmp.sprintf( "%3i'", r );
+	rotateLCD->display( tmp );
+	setTransform();
+}
+
+void editorView::slotShearChanged(int)
+{
+	QString tmp;
+	double shearVal;
+	QSlider *sl;
+	sl=qobject_cast<QSlider *>(sender());
+	shearVal=((double)sl->value())/10;
+	tmp.sprintf( "%1.3f", shearVal  );
+	if ( shearVal >= 0 )
+	tmp.insert( 0, " " );
+	if(sl==hshearSlider)
+		{
+			hshearLCD->display( tmp );
+		}
+	else
+		{
+			vshearLCD->display( tmp );
+		}
+	setTransform();
+}
+
+QIcon editorView::createColorToolButtonIcon(const QString &imageFile, QColor color)
+{
+	QPixmap pixmap(22, 30);
+	pixmap.fill(Qt::transparent);
+	QPainter painter(&pixmap);
+	QPixmap image(imageFile);
+  QRect target(0, 0, 22, 22);
+  QRect source(0, 0, 22, 22);
+  painter.fillRect(QRect(0, 22, 22, 8), color);
+  painter.drawPixmap(target, image, source);
+  return QIcon(pixmap);
+}
+
+QMenu *editorView::createColorMenu(const char * slot,int type,QString text)
+{
+    QMenu *colorMenu = new QMenu;
+		QAction *action = new QAction(text, this);
+		action->setData(type);
+    connect(action, SIGNAL(triggered()),this,slot);
+		colorMenu->addAction(action);
+    return colorMenu;
+}
+
+void editorView::slotItemSelected(itemBase* ib)
+{
+	sitemParam p;
+	p=ib->getParam();
+	if(p.type==itemBase::TEXT)
+		{
+			fontComboBox->setCurrentFont(p.font);
+			fontSizeSpinBox->setValue(p.font.pointSize());
+			boldButton->setChecked(p.font.bold());
+			underlineButton->setChecked(p.font.underline());
+			italicButton->setChecked(p.font.italic());
+		}
+	penWidthSpinBox->setValue(p.pen.widthF());
+//	int rot=p.rotation-450;
+//	if ( rot <0)
+//	rot+= 360;
+	int rot=p.rotation;
+	rotateDial->setValue(rot);
+	hshearSlider->setValue((int)(p.hShear*10));
+	vshearSlider->setValue((int)(p.vShear*10));
+}
+
+void editorView::slotDump()
+{
+	QString t;
+	int i;
+	QList<QGraphicsItem *> l=scene->items();
+	itemBase *b;
+  addToLog(QString("dump editorView of items: %1").arg(l.count()),LOGEDIT); //exclude border
+	for(i=0;i<l.count();i++)
+		{
+//      if(l.at(i)->type()>=itemBase::BASE)
+				{
+					b=qgraphicsitem_cast<itemBase *>(l.at(i));
+					switch((int)b->type())
+						{
+							case itemBase::TEXT:
+									t="Text:";
+							break;
+							case itemBase::LINE:
+								t="Line:";
+							break;
+							case itemBase::IMAGE:
+									t="Image:";
+							break;
+							case itemBase::RECTANGLE:
+                t="Rectangle:";
+							break;
+							case itemBase::ELLIPSE:
+                t="Ellipse:";
+							break;
+              case itemBase::SBORDER:
+                t="Border:";
+              break;
+							default:
+                t=QString("Ill: %1").arg(l.at(i)->type());
+							break;
+						}
+          addToLog(QString("editorViewItems %1 pos=%2,%3 rectxy=%4,%5 size=%6x%7 depth=%8")
+                       .arg(t)
+                       .arg(b->pos().x()).arg(b->pos().y())
+                       .arg(b->rect().x()).arg(b->rect().y())
+                       .arg(b->rect().width()).arg(b->rect().height())
+                       .arg(b->zValue()),LOGEDIT);
+        }
+      }
+		
+}
diff --git a/qsstv/editor/editorview.h b/qsstv/editor/editorview.h
new file mode 100644
index 0000000..1dfaebf
--- /dev/null
+++ b/qsstv/editor/editorview.h
@@ -0,0 +1,108 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef EDITORVIEW_H
+#define EDITORVIEW_H
+
+#include <QtGui>
+#include "editorscene.h"
+#include "ui_editorform.h"
+
+
+/**
+ at author Johan Maes - ON4QZ
+*/
+class editorForm;
+enum eactionType {SELECT,COLORPICK,RECTANGLE,ELLIPSE,LINE,IMAGE,TEXT};
+enum ePickMode {NOPICK, PICKFILLCOLOR,PICKLINECOLOR};
+
+/** Widget to display the various canvasItems */
+class editorView : public QWidget,private Ui::editorForm
+{
+Q_OBJECT
+public:
+	
+	editorView(QWidget *parent = 0);
+	~editorView();
+	void readSettings();
+	void writeSettings();
+	
+	bool isModified() {return modified;}
+	bool open(QFile &f);
+	void save(QFile &f,bool templ);
+
+	QImage *getImage() { return image;}
+	void setImage(QImage *ima);
+	editorScene *getScene() {return scene;}
+
+public slots:
+	void slotChangeCanvasSize(int);
+//	void slotChangeCanvasSize(int w,int h);
+// paint actions
+	void slotArrow();
+	void slotRectangle();
+	void slotCircle();
+	void slotColorPicker();
+	void slotColorPicked(const QPointF &p);
+	void slotText();
+	void slotImage();
+	void slotReplay();
+	void slotLine();
+	void slotClearAll();
+
+//Font
+	void slotFontChanged(const QFont &);
+	void slotFontSizeChanged(int);
+	void slotPenWidthChanged(double);
+	void slotBold(bool);
+	void slotItalic(bool);
+	void slotUnderline(bool);
+
+//Color
+	void slotColorDialog();
+	void slotGradientDialog();
+	void slotButtonTriggered();
+
+//Transform
+	void slotRotateChanged(int);
+	void slotShearChanged(int);
+//	void slotShearRotateChanged(double,int);
+//item feedback
+	void slotItemSelected(itemBase*);
+//Debug
+	void slotDump();
+	void slotTextReturnPressed(const QString &);
+private:
+	editorScene *scene;
+	bool modified;
+	QImage *image;
+	void setTransform();
+  QIcon createColorToolButtonIcon(const QString &imageFile, QColor color);
+	QMenu *createColorMenu(const char *,int,QString text);
+	int canvasSizeIndex;
+	int currentPointSize;
+	int currentFontIndex;
+	double currentPenWidth;
+	QString txt;
+	QImage *colorPickImage;
+	ePickMode pickMode;
+};
+
+#endif
diff --git a/qsstv/editor/gradientdialog.cpp b/qsstv/editor/gradientdialog.cpp
new file mode 100644
index 0000000..9466d0c
--- /dev/null
+++ b/qsstv/editor/gradientdialog.cpp
@@ -0,0 +1,304 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "gradientdialog.h"
+#include "qsstvdefs.h"
+#include <QColorDialog>
+
+
+gradientDialog::gradientDialog(QWidget *parent):QDialog(parent), Ui::gradientForm()
+{
+	setupUi(this);
+	readSettings();
+	connect(color1Button,SIGNAL(clicked()),SLOT(slotColorDialog()));
+	connect(color2Button,SIGNAL(clicked()),SLOT(slotColorDialog()));
+	connect(color3Button,SIGNAL(clicked()),SLOT(slotColorDialog()));
+	connect(color4Button,SIGNAL(clicked()),SLOT(slotColorDialog()));
+	
+	previewLabel->setBackgroundRole(QPalette::Base);
+	g=NULL;
+	slotUpdate();
+	connect(pos1SpinBox,SIGNAL(valueChanged(int)),SLOT(slotUpdate()));
+	connect(pos2SpinBox,SIGNAL(valueChanged(int)),SLOT(slotUpdate()));
+	connect(pos3SpinBox,SIGNAL(valueChanged(int)),SLOT(slotUpdate()));
+	connect(pos4SpinBox,SIGNAL(valueChanged(int)),SLOT(slotUpdate()));
+	connect(dial,SIGNAL(valueChanged(int)),SLOT(slotUpdate()));
+	connect(noGradientButton,SIGNAL(clicked()),SLOT(slotUpdate()));
+	connect(linearGradientButton,SIGNAL(clicked()),SLOT(slotUpdate()));
+	connect(radialGradientButton,SIGNAL(clicked()),SLOT(slotUpdate()));
+	connect(conicalGradientButton,SIGNAL(clicked()),SLOT(slotUpdate()));
+
+}
+
+gradientDialog::~gradientDialog()
+{
+	writeSettings();
+	if(g==NULL) delete g;
+}
+
+void gradientDialog::readSettings()
+{
+	QSettings qSettings;
+	qSettings.beginGroup ("Editor");
+    gParam.color1 = qSettings.value("gradcolor1", QColor(Qt::red)).value<QColor>();
+    gParam.color2 = qSettings.value("gradcolor2", QColor(Qt::green )).value<QColor>();
+    gParam.color3 = qSettings.value("gradcolor3", QColor(Qt::yellow)).value<QColor>();
+    gParam.color4 = qSettings.value("gradcolor4", QColor(Qt::blue )).value<QColor>();
+	gParam.pos1=qSettings.value("gradpos1", 0 ).toInt();
+	gParam.pos2=qSettings.value("gradpos2", 0 ).toInt();
+	gParam.pos3=qSettings.value("gradpos3", 0 ).toInt();
+	gParam.pos4=qSettings.value("gradpos4", 0 ).toInt();
+	pos1SpinBox->setValue(gParam.pos1);
+	pos2SpinBox->setValue(gParam.pos2);
+	pos3SpinBox->setValue(gParam.pos3);
+	pos4SpinBox->setValue(gParam.pos4);
+	gParam.direction=qSettings.value("graddirection", 0 ).toInt();
+	dial->setValue((gParam.direction+90)%360);
+	if(qSettings.value("nogradbutton", 1 ).toBool())
+		{
+			noGradientButton->setChecked(true);
+			gParam.type=sgradientParam::NONE;
+		}
+	else if(qSettings.value("lineargradbutton", 0 ).toBool())
+		{
+			linearGradientButton->setChecked(true);
+			gParam.type=sgradientParam::LINEAR;;
+		}
+	else if(qSettings.value("radialgradbutton", 0 ).toBool())
+		{
+			radialGradientButton->setChecked(true);
+			gParam.type=sgradientParam::RADIAL;
+		}
+	else if(qSettings.value("conicalgradbutton", 0 ).toBool())
+		{
+			conicalGradientButton->setChecked(true);
+			gParam.type=sgradientParam::CONICAL;
+		}
+	qSettings.endGroup();
+}
+
+void gradientDialog::writeSettings()
+{
+QSettings qSettings;
+	qSettings.beginGroup ("Editor" );
+	
+	qSettings.setValue ("gradcolor1", gParam.color1);
+	qSettings.setValue ("gradcolor2", gParam.color2);
+	qSettings.setValue ("gradcolor3", gParam.color3);
+	qSettings.setValue ("gradcolor4", gParam.color4);
+	qSettings.setValue ("gradpos1", gParam.pos1);
+	qSettings.setValue ("gradpos2", gParam.pos2);
+	qSettings.setValue ("gradpos3", gParam.pos3);
+	qSettings.setValue ("gradpos4", gParam.pos4);
+	qSettings.setValue ("graddirection", gParam.direction);
+	qSettings.setValue ("nogradbutton",noGradientButton->isChecked());
+	qSettings.setValue ("lineargradbutton",linearGradientButton->isChecked());
+	qSettings.setValue ("radialgradbutton",radialGradientButton->isChecked());
+	qSettings.setValue ("conicalgradbutton",conicalGradientButton->isChecked());
+	qSettings.endGroup();
+}
+
+void sgradientParam::load(QDataStream &str)
+{
+	int t;
+	str >> color1;
+	str >> color2;
+	str >> color3;
+	str >> color4;
+	str >> pos1;
+	str >> pos2;
+	str >> pos3;
+	str >>pos4;
+	str >> t;
+	type=(gType)t;
+	str >> direction;
+}
+void sgradientParam::save(QDataStream &str)
+{
+	str << color1;
+	str << color2;
+	str << color3;
+	str << color4;
+	str << pos1;
+	str << pos2;
+	str << pos3;
+	str << pos4;
+	str << (int)type;
+	str << direction;
+}
+
+
+void gradientDialog::slotColorDialog()
+{
+	QColor c;
+  QPushButton *act=qobject_cast<QPushButton *>(sender());
+  if (act==color1Button)
+		{
+      c=QColorDialog::getColor(gParam.color1,this,"",QColorDialog::ShowAlphaChannel);
+      if (c.isValid()) gParam.color1=c;
+    }
+	else if (act==color2Button)
+    {
+      c=QColorDialog::getColor(gParam.color2,this,"",QColorDialog::ShowAlphaChannel);
+      if (c.isValid()) gParam.color2=c;
+    }
+	else if (act==color3Button)
+    {
+      c=QColorDialog::getColor(gParam.color3,this,"",QColorDialog::ShowAlphaChannel);
+      if (c.isValid()) gParam.color3=c;
+    }
+	else if (act==color4Button)
+    {
+      c=QColorDialog::getColor(gParam.color4,this,"",QColorDialog::ShowAlphaChannel);
+      if (c.isValid()) gParam.color4=c;
+    }
+	slotUpdate();
+}
+
+/*! \todo split param update from graphic creation
+*/
+void gradientDialog::slotUpdate()
+{
+   QString s;
+	QPalette palette;
+	QBrush brush;
+	gParam.direction=(270+dial->value())%360;
+	gParam.pos1=pos1SpinBox->value();
+	gParam.pos2=pos2SpinBox->value();
+	gParam.pos3=pos3SpinBox->value();
+	gParam.pos4=pos4SpinBox->value();
+
+  if(noGradientButton->isChecked()) gParam.type=sgradientParam::NONE;
+  else if (linearGradientButton->isChecked()) gParam.type=sgradientParam::LINEAR;
+  else if (radialGradientButton->isChecked()) gParam.type=sgradientParam::RADIAL;
+  else if (conicalGradientButton->isChecked()) gParam.type=sgradientParam::CONICAL;
+
+  s=gParam.color1.name();
+  color1Button->setStyleSheet("background-color: "+s+"; border-style: outset; border-width: 2px;border-radius: 10px; border-color: beige; padding: 6px");
+  s=gParam.color2.name();
+  color2Button->setStyleSheet("background-color: "+s+"; border-style: outset; border-width: 2px;border-radius: 10px; border-color: beige; padding: 6px");
+  s=gParam.color3.name();
+  color3Button->setStyleSheet("background-color: "+s+"; border-style: outset; border-width: 2px;border-radius: 10px; border-color: beige; padding: 6px");
+  s=gParam.color4.name();
+  color4Button->setStyleSheet("background-color: "+s+"; border-style: outset; border-width: 2px;border-radius: 10px; border-color: beige; padding: 6px");
+
+//	brush.setStyle(Qt::SolidPattern);
+
+//	brush.setColor(gParam.color1);
+//	palette.setBrush(QPalette::Active, QPalette::Button, brush);
+//	color1Button->setPalette(palette);
+
+//	brush.setColor(gParam.color2);11
+//	palette.setBrush(QPalette::Active, QPalette::Button, brush);
+//	color2Button->setPalette(palette);
+//	brush.setColor(gParam.color3);
+//	palette.setBrush(QPalette::Active, QPalette::Button, brush);
+//	color3Button->setPalette(palette);
+//	brush.setColor(gParam.color4);
+//	palette.setBrush(QPalette::Active, QPalette::Button, brush);
+//	color4Button->setPalette(palette);
+	brush.setStyle(Qt::SolidPattern);
+	if(gParam.type!=sgradientParam::NONE)
+		{
+			QBrush br(buildGradient(gParam,previewLabel->rect()));
+			palette.setBrush(QPalette::Active, QPalette::Base, br);
+		}
+	else
+		{
+			QBrush br(gParam.color1);
+			palette.setBrush(QPalette::Active, QPalette::Base, br);
+		}
+	previewLabel->setPalette(palette);
+}
+
+
+void gradientDialog::selectGradient()
+{
+	exec();
+}
+
+void grSetup (sgradientParam prm,QGradient &g)
+{
+	g.setColorAt(prm.pos1/100.,prm.color1);
+	if(prm.pos2<=prm.pos1) return ;
+	g.setColorAt(prm.pos2/100.,prm.color2);
+	if(prm.pos3<=prm.pos2) return ;
+	g.setColorAt(prm.pos3/100.,prm.color3);
+	if(prm.pos4<=prm.pos3) return ;
+	g.setColorAt(prm.pos4/100.,prm.color4);
+}
+
+QGradient buildGradient(sgradientParam prm, QRectF f)
+{
+	qreal w=f.width();
+	qreal h=f.height();
+	qreal d=(double)prm.direction;
+	qreal x1,y1,x2,y2;
+	qreal temp;
+	if(prm.type==sgradientParam::NONE)
+		{
+			QLinearGradient g(0,0,0,0);
+			grSetup(prm,g);
+			return g;
+		}
+	if(prm.type==sgradientParam::LINEAR)
+		{
+			if(fabs(w/2*tan(M_PI/2-d*M_PI/180))<=(w/2))
+				{
+					x1=f.x()+w/2-(w/2*tan(M_PI/2-d*M_PI/180.));
+					y1=f.y()+h;
+					x2=f.x()+w/2+(w/2*tan(M_PI/2-d*M_PI/180.));
+					y2=f.y();
+					if ((prm.direction>180) && (prm.direction<=359))
+						 {
+							temp=x1; x1=x2;x2=temp;
+							temp=y1; y1=y2;y2=temp;
+						}
+				}
+			else
+				{
+					x1=f.x();
+					y1=f.y()+h/2+(h/2*tan(d*M_PI/180.));
+					x2=f.x()+w;
+					y2=f.y()+h/2-(h/2*tan(d*M_PI/180.));
+					if ((prm.direction>90) && (prm.direction<=270))
+						{
+							temp=x1; x1=x2;x2=temp;
+							temp=y1; y1=y2;y2=temp;
+						}
+				}
+
+			QLinearGradient g(x1,y1,x2,y2);
+			grSetup(prm,g);
+			return g;
+		}
+	else if(prm.type==sgradientParam::RADIAL)
+		{
+			QRadialGradient g(QPointF(f.x()+f.width()/2,f.y()+f.height()/2),f.width()/2);
+			grSetup(prm,g);
+			return g;
+		}
+	else if(sgradientParam::CONICAL)
+		{
+			QConicalGradient g(QPointF(f.x()+f.width()/2,f.y()+f.height()/2),prm.direction);
+			grSetup(prm,g);
+			return g;
+		}
+}
diff --git a/qsstv/editor/gradientdialog.h b/qsstv/editor/gradientdialog.h
new file mode 100644
index 0000000..0473bab
--- /dev/null
+++ b/qsstv/editor/gradientdialog.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef GRADIENTDIALOG_H
+#define GRADIENTDIALOG_H
+
+#include <QtGui>
+#include "ui_gradientform.h"
+
+struct sgradientParam
+{
+	
+	enum gType {NONE,LINEAR,RADIAL,CONICAL};
+	sgradientParam()
+		{
+			type=NONE;
+		}
+	QColor color1;
+	QColor color2;
+	QColor color3;
+	QColor color4;
+	int pos1;
+	int pos2;
+	int pos3;
+	int pos4;
+	gType type;
+	int direction;
+	void load(QDataStream &str);
+	void save(QDataStream &str);
+};
+
+/**
+ at author Johan Maes - ON4QZ
+*/
+class gradientForm;
+
+/** Widget to disply the various canvasItems */
+class gradientDialog : public QDialog,private Ui::gradientForm
+{
+Q_OBJECT
+public:
+	gradientDialog(QWidget *parent = 0);
+	~gradientDialog();
+	void readSettings();
+	void writeSettings();
+	void selectGradient();
+	sgradientParam param() {return gParam;}
+
+//	QGradient *constructGradient( QRectF f);
+public slots:
+	void slotColorDialog();
+	void slotUpdate();
+private:
+	sgradientParam gParam;
+	void update();
+	QGradient *g;
+
+};
+
+QGradient buildGradient(sgradientParam prm, QRectF f);
+#endif
+
diff --git a/qsstv/editor/gradientform.ui b/qsstv/editor/gradientform.ui
new file mode 100644
index 0000000..96b2da5
--- /dev/null
+++ b/qsstv/editor/gradientform.ui
@@ -0,0 +1,438 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>gradientForm</class>
+ <widget class="QDialog" name="gradientForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>300</width>
+    <height>330</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>300</width>
+    <height>330</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>300</width>
+    <height>330</height>
+   </size>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Gradients</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="sizeConstraint">
+        <enum>QLayout::SetMinimumSize</enum>
+       </property>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label_1">
+         <property name="maximumSize">
+          <size>
+           <width>45</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Color</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Position</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QPushButton" name="color1Button">
+         <property name="autoFillBackground">
+          <bool>true</bool>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QSpinBox" name="pos1SpinBox">
+         <property name="maximumSize">
+          <size>
+           <width>45</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QPushButton" name="color2Button">
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QSpinBox" name="pos2SpinBox">
+         <property name="maximumSize">
+          <size>
+           <width>45</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QPushButton" name="color3Button">
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1">
+        <widget class="QSpinBox" name="pos3SpinBox">
+         <property name="maximumSize">
+          <size>
+           <width>45</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="0">
+        <widget class="QPushButton" name="color4Button">
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="1">
+        <widget class="QSpinBox" name="pos4SpinBox">
+         <property name="maximumSize">
+          <size>
+           <width>45</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QLabel" name="dialLabel">
+         <property name="text">
+          <string>Direction</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QDial" name="dial">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="palette">
+          <palette>
+           <active>
+            <colorrole role="Button">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>170</red>
+               <green>0</green>
+               <blue>0</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </active>
+           <inactive>
+            <colorrole role="Button">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>170</red>
+               <green>0</green>
+               <blue>0</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </inactive>
+           <disabled>
+            <colorrole role="Button">
+             <brush brushstyle="SolidPattern">
+              <color alpha="255">
+               <red>170</red>
+               <green>0</green>
+               <blue>0</blue>
+              </color>
+             </brush>
+            </colorrole>
+           </disabled>
+          </palette>
+         </property>
+         <property name="minimum">
+          <number>0</number>
+         </property>
+         <property name="maximum">
+          <number>360</number>
+         </property>
+         <property name="singleStep">
+          <number>1</number>
+         </property>
+         <property name="value">
+          <number>90</number>
+         </property>
+         <property name="sliderPosition">
+          <number>90</number>
+         </property>
+         <property name="invertedAppearance">
+          <bool>true</bool>
+         </property>
+         <property name="invertedControls">
+          <bool>false</bool>
+         </property>
+         <property name="wrapping">
+          <bool>true</bool>
+         </property>
+         <property name="notchTarget">
+          <double>10.000000000000000</double>
+         </property>
+         <property name="notchesVisible">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QGroupBox" name="gradientGroupBox">
+       <property name="minimumSize">
+        <size>
+         <width>130</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>130</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="title">
+        <string>Gradient type</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_3">
+        <item>
+         <widget class="QRadioButton" name="noGradientButton">
+          <property name="text">
+           <string>None</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="linearGradientButton">
+          <property name="text">
+           <string>Linear</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="radialGradientButton">
+          <property name="text">
+           <string>Radial</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="conicalGradientButton">
+          <property name="text">
+           <string>Conical</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="previewLabel">
+       <property name="minimumSize">
+        <size>
+         <width>130</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>130</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::Panel</enum>
+       </property>
+       <property name="frameShadow">
+        <enum>QFrame::Sunken</enum>
+       </property>
+       <property name="lineWidth">
+        <number>2</number>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+     <property name="centerButtons">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>gradientForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>gradientForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/editor/graphicitems.cpp b/qsstv/editor/graphicitems.cpp
new file mode 100644
index 0000000..01ef871
--- /dev/null
+++ b/qsstv/editor/graphicitems.cpp
@@ -0,0 +1,595 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "graphicitems.h"
+#include "qsstvglobal.h"
+#include "editorscene.h"
+
+/*!
+	
+*/
+static QPainterPath qt_graphicsItem_shapeFromPath(const QPainterPath &path, const QPen &pen)
+{
+    // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
+    // if we pass a value of 0.0 to QPainterPathStroker::setWidth()
+    const qreal penWidthZero = qreal(0.00000001);
+
+    if (path == QPainterPath()) return path;
+    QPainterPathStroker ps;
+    ps.setCapStyle(pen.capStyle());
+    if (pen.widthF() <= 0.0) ps.setWidth(penWidthZero);
+    else  ps.setWidth(pen.widthF());
+    ps.setJoinStyle(pen.joinStyle());
+    ps.setMiterLimit(pen.miterLimit());
+    QPainterPath p = ps.createStroke(path);
+    p.addPath(path);
+    return p;
+}
+
+
+itemBase::itemBase(QMenu *cntxtMenu)
+{
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+	setAcceptHoverEvents (true);
+	param.locked=false;
+	param.modified=true;
+	param.menu=cntxtMenu;
+}
+
+void itemBase::highlightSelected(QPainter *painter,const QStyleOptionGraphicsItem *option)
+{
+
+///	qreal itemPenWidth = pen().widthF();
+//		const qreal pad = itemPenWidth / 2;
+//	const qreal pad = itemPenWidth;
+	const qreal penWidth = 0; // cosmetic pen
+	const QColor fgcolor = option->palette.windowText().color();
+	const QColor bgcolor( // ensure good contrast against fgcolor
+		fgcolor.red()   > 127 ? 0 : 255,
+		fgcolor.green() > 127 ? 0 : 255,
+		fgcolor.blue()  > 127 ? 0 : 255);
+	painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));
+	painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
+	painter->setBrush(Qt::NoBrush);
+	painter->drawRect(boundingRect());
+//	painter->drawRect(boundingRect().adjusted(pad, pad, -pad, -pad));
+}
+
+QPainterPath itemBase::shape() const
+{
+	QPainterPath path;
+	path.addRect(param.rct);
+	return qt_graphicsItem_shapeFromPath(path,pen());
+}
+
+void itemBase::setBrush(QColor c)
+{
+	param.fillColor=c;
+	QAbstractGraphicsShapeItem::setBrush(param.fillColor);
+}
+
+void itemBase::load(QDataStream &str)
+{
+//	str << type(); this item is already read by the loader
+	QTransform f;
+	QPointF p;
+	QRectF r;
+	QColor c;
+	QPen pn;
+	QBrush br;
+	QString t;
+	QFont fnt;
+	qreal z;
+	str >> param.rotation;
+	str >> param.hShear,
+	str >> param.vShear;
+	str >> z;
+	setZValue(z);
+  param.zValue=z;
+	str >> p;
+	setPos(p);
+	str >> r;
+	setRect(r);
+	str >> c;
+	param.fillColor=c;
+	setBrush(c);
+	str >> pn;
+	setPen(pn);
+	str >> br;
+	QAbstractGraphicsShapeItem::setBrush(br);
+	str >> param.locked;
+	str >>param.im;
+	str >>t;
+	setText(t);
+	str >>fnt;
+	setFont(fnt);
+	str >> param.line;
+	param.gradient.load(str);
+  setTransform ();
+ }
+
+void itemBase::save(QDataStream &str)
+{
+	str << type();
+	str << param.rotation;
+	str << param.hShear,
+	str << param.vShear;
+	str	<< zValue();
+	str << pos();
+	str << rect();
+	str << param.fillColor;
+	str << pen();
+	str << brush();
+	str << param.locked;
+	str << param.im;
+	str << param.txt;
+	str << param.font;
+	str << param.line;
+	param.gradient.save(str);
+
+}
+
+void itemBase::setTransform ()
+{
+  QTransform tx;
+  tx.translate(rect().x()+rect().width()/2,rect().y()+rect().height()/2);
+  tx.shear(param.hShear,param.vShear);
+  tx.rotate(param.rotation);
+  tx.translate(-rect().x()-rect().width()/2,-rect().y()-rect().height()/2);
+  QAbstractGraphicsShapeItem::setTransform(tx,false);
+  update();
+}
+
+void itemBase::setTransform ( int rot,double hs,double vs)
+{
+  param.rotation=rot;
+  param.hShear=hs;
+	param.vShear=vs;
+  setTransform ();
+}
+
+void itemBase::hoverMoveEvent ( QGraphicsSceneHoverEvent * event )
+{
+	if(((editorScene*)scene())->mode==editorScene::PICK)
+		{
+			setCursor(*cpCursor);
+ 			return;
+		}
+	if(((editorScene*)scene())->mode==editorScene::INSERT)
+		{
+			setCursor(Qt::ArrowCursor);
+ 			return;
+		}
+
+
+	if(param.locked)
+		{
+			grab = NO;
+		 	setCursor(Qt::ForbiddenCursor);
+		}
+	else if(type()!=LINE)
+		{
+			grab = getCorner(event->pos());
+			if(type()==TEXT) grab=NO;
+			if ((grab == CUL)|| (grab == CDR)) setCursor(Qt::SizeFDiagCursor);
+			if ((grab == CUR)|| (grab == CDL)) setCursor(Qt::SizeBDiagCursor);
+			if ((grab == HU) || (grab == HD))  setCursor(Qt::SizeVerCursor);
+			if ((grab == VL) || (grab == VR))  setCursor(Qt::SizeHorCursor);
+			if (grab == NO) setCursor(Qt::OpenHandCursor);
+		}
+	else
+		{
+				grab=NO;
+				setCursor(Qt::CrossCursor);
+		}
+	//QAbstractGraphicsShapeItem::hoverEnterEvent(event);
+	QAbstractGraphicsShapeItem::hoverMoveEvent(event);
+}
+
+
+
+
+itemBase::corner itemBase::getCorner( QPointF mouse)
+{
+	double x = rect().x();
+	double y = rect().y();
+	double h = rect().height();
+	double w = rect().width();
+
+	double diff;
+	diff=w; if (diff>h) diff=h;
+	diff/=10;
+	if (diff>10) diff=10;
+	else if (diff<1) diff=1;
+
+	QRectF cul (x,y, diff,diff);
+	QRectF cur (x+w-diff,y, diff,diff);
+	QRectF cdl (x,y+h-diff,diff,diff);
+	QRectF cdr (x+w-diff,y+h-diff,diff,diff);
+
+	QRectF hu (x+diff,y,w-(2*diff),diff);
+	QRectF hd (x+diff,y+h-diff,w-(2*diff),diff);
+	QRectF vl (x,y+diff,diff,h-(2*diff));
+	QRectF vr (x+w-diff,y+diff,diff,h-(2*diff));
+
+	if ( cul.contains(mouse) ) return CUL;
+	if ( cur.contains(mouse) ) return CUR;
+	if ( cdl.contains(mouse) ) return CDL;
+	if ( cdr.contains(mouse) ) return CDR;
+	if ( hu.contains(mouse) ) return HU;
+	if ( hd.contains(mouse) ) return HD;
+	if ( vl.contains(mouse) ) return VL;
+	if ( vr.contains(mouse) ) return VR;
+
+return NO;
+}
+
+
+void itemBase::mouseMoveEvent ( QGraphicsSceneMouseEvent * event )
+{
+	if(((editorScene*)scene())->mode==editorScene::PICK) return;
+	if(((editorScene*)scene())->mode==editorScene::INSERT) return;
+	if(param.locked) return;
+	QPointF mouse = event->pos();
+//resize!
+	prepareGeometryChange();
+	param.modified=true;
+	switch ( grab)
+		{
+			case NO : QAbstractGraphicsShapeItem::mouseMoveEvent(event);
+	  	break;
+			case HD :
+				if(mouse.y()-rect().y()>1)
+				setRect(rect().x(),rect().y(),rect().width(),mouse.y()-rect().y());
+		  break;
+			case HU :
+				if(rect().height()+(rect().y()-mouse.y())>0)
+				 setRect(rect().x(),mouse.y(),rect().width(),rect().height()+(rect().y()-mouse.y()));
+			break;
+			case VR :
+				if(mouse.x()-rect().x()>1)
+				setRect(rect().x(),rect().y(),mouse.x()-rect().x(),rect().height());
+			break;
+			case VL :
+				if(rect().width()+(rect().x()-mouse.x())>1)
+ 				setRect(mouse.x(),rect().y(),rect().width()+(rect().x()-mouse.x()),rect().height());
+			break;
+			case CDR :
+				if(((mouse.x()-rect().x())>1)&&((mouse.y()-rect().y())>1))
+				setRect(rect().x(),rect().y(),mouse.x()-rect().x(),mouse.y()-rect().y());
+			break; 
+			case CUR :
+				if(((mouse.x()-rect().x())>1)&&((rect().height()+(rect().y()-mouse.y()))>1))
+				setRect(rect().x(),mouse.y(),mouse.x()-rect().x(),rect().height()+(rect().y()-mouse.y()));
+			break;
+			case CDL :
+				if(((rect().width()+(rect().x()-mouse.x()))>1)&&((mouse.y()-rect().y())>1))
+				setRect(mouse.x(),rect().y(),rect().width()+(rect().x()-mouse.x()),mouse.y()-rect().y());
+			break; 
+			case CUL :
+				if(((rect().width()+(rect().x()-mouse.x()))>1)&&((rect().height()+(rect().y()-mouse.y()))>1))
+				setRect(mouse.x(),mouse.y(),rect().width()+(rect().x()-mouse.x()),rect().height()+(rect().y()-mouse.y()));
+			break; 
+		}
+	update();
+}
+
+ QString itemBase::getTypeStr()
+ {
+   QString tp;
+   switch(type())
+    {
+      case BASE: tp="Base"; break;
+      case RECTANGLE: tp="Rectangle";  break;
+      case ELLIPSE: tp="Ellipse"; break;
+      case IMAGE: tp="Image";  break;
+      case LINE: tp="Line";  break;
+      case TEXT: tp="Text"; break;
+      case REPLAY: tp="Replay";  break;
+      case SBORDER: tp="SBorder";  break;
+    }
+   return tp;
+ }
+
+
+void itemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+	if(((editorScene*)scene())->mode==editorScene::PICK) return;
+	setSelected(true);
+	param.menu->exec(event->screenPos());
+}
+
+
+// Text graphics
+
+itemText::itemText(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+	  param.font.setFamily("Times");
+		param.font.setPointSize(24);
+    param.font.setStyleStrategy(QFont::ForceOutline);
+		setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+		setAcceptHoverEvents (true);
+}
+
+itemText::~itemText()
+{
+}
+
+
+void itemText::setText(const QString &t)
+{
+	QPainterPath tt;
+	prepareGeometryChange();
+	param.modified=true;
+	param.txt=t;
+	tt.addText(0, 0, param.font, param.txt);
+	param.rct=tt.controlPointRect();
+	update();
+}
+
+void itemText::setFont(QFont f)
+{
+	QPainterPath tt;
+	prepareGeometryChange();
+	param.modified=true;
+	param.font=f;
+	param.font.setStyleStrategy(QFont::ForceOutline);
+	tt.addText(0, 0, param.font, param.txt);
+	param.rct=tt.controlPointRect();
+	update();
+}
+
+//QRectF itemText::boundingRect() const
+//{
+//	qreal d=pen().widthF();
+//	QPainterPath tt;
+//	tt.addText(0, 0, param.font, param.txt);
+//	return tt.controlPointRect().adjusted(-d/2,-d/2,d/2,d/2);
+//}
+
+void itemText::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+	QPainterPath tt;
+	tt.addText(0, 0, param.font, param.txt);
+	if (param.modified)
+		{
+			param.modified=false;
+			if(param.gradient.type!=sgradientParam::NONE) 
+				{
+					QAbstractGraphicsShapeItem::setBrush(buildGradient(param.gradient,rect()));
+				}
+			else
+				{
+					QAbstractGraphicsShapeItem::setBrush(param.fillColor);
+				}
+		}
+	painter->setPen(pen());
+	painter->setBrush(brush());
+	painter->setFont(param.font);
+	painter->drawPath(tt);
+	if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+itemRectangle::itemRectangle(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+	setRect(0,0,100,100);
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+}
+
+
+
+void itemRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+//	if (resized ) prepareGeometryChange();
+	if (param.modified)
+		{
+			param.modified=false;
+			if(param.gradient.type!=sgradientParam::NONE) 
+				{
+					QAbstractGraphicsShapeItem::setBrush(buildGradient(param.gradient,rect()));
+				}
+			else
+				{
+					QAbstractGraphicsShapeItem::setBrush(param.fillColor);
+				}
+		}
+	painter->setPen(pen());
+	painter->setBrush(brush());
+	painter->drawRect(param.rct);
+	if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+itemEllipse::itemEllipse(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+	setRect(0,0,100,100);
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+}
+
+
+
+void itemEllipse::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+//	if(resized) prepareGeometryChange();
+		if (param.modified)
+		{
+			param.modified=false;
+			if(param.gradient.type!=sgradientParam::NONE) 
+				{
+					QAbstractGraphicsShapeItem::setBrush(buildGradient(param.gradient,rect()));
+				}
+			else
+				{
+					QAbstractGraphicsShapeItem::setBrush(param.fillColor);
+				}
+		}
+	painter->setPen(pen());
+	painter->setBrush(brush());
+	painter->drawEllipse(param.rct);
+	if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+
+
+itemImage::itemImage(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+	setRect(0,0,100,100);
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+}
+
+
+
+void itemImage::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+  QImage tim;
+  tim=param.im.scaled(param.rct.width(),param.rct.height(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
+	qreal pad=pen().widthF()/2;
+  painter->drawImage(param.rct.adjusted(pad,pad,-pad,-pad),tim);
+	painter->setBrush(Qt::NoBrush);
+	painter->setPen(pen());
+  //painter->drawRect(param.rct);
+	if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+itemLine::itemLine(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+//	setRect(0,0,100,100);
+	
+	param.line.setPoints(QPoint(0,0),QPoint(100,0));
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+}
+
+QPainterPath itemLine::shape() const
+
+{
+	QPainterPath path;
+	if (param.line.isNull()) return path;
+  path.moveTo(param.line.p1());
+  path.lineTo(param.line.p2());
+	return qt_graphicsItem_shapeFromPath(path,pen());
+}
+
+
+QRectF itemLine::boundingRect() const
+{
+	if (pen().widthF() == 0.0)
+		{
+        const qreal x1 = param.line.p1().x();
+        const qreal x2 = param.line.p2().x();
+        const qreal y1 = param.line.p1().y();
+        const qreal y2 = param.line.p2().y();
+        qreal lx = qMin(x1, x2);
+        qreal rx = qMax(x1, x2);
+        qreal ty = qMin(y1, y2);
+        qreal by = qMax(y1, y2);
+        return QRectF(lx, ty, rx - lx, by - ty);
+    }
+	return shape().controlPointRect();
+}
+
+void itemLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+//	if (resized ) prepareGeometryChange();
+	painter->setPen(pen());
+	painter->setBrush(brush());
+	painter->drawLine(param.line);
+	if (option->state & QStyle::State_Selected)
+//  highlightSelected(painter,option);
+	{
+		const qreal penWidth = 0; // cosmetic pen 
+		const QColor fgcolor = option->palette.windowText().color();
+		const QColor bgcolor( // ensure good contrast against fgcolor
+			fgcolor.red()   > 127 ? 0 : 255,
+			fgcolor.green() > 127 ? 0 : 255,
+			fgcolor.blue()  > 127 ? 0 : 255);
+		painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));
+		painter->setBrush(Qt::NoBrush);
+		painter->strokePath(shape(),QPen(bgcolor, penWidth, Qt::SolidLine));
+		painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
+		painter->setBrush(Qt::NoBrush);
+		painter->strokePath(shape(),QPen(option->palette.windowText(), 0, Qt::DashLine));
+	}
+}
+
+itemReplayImage::itemReplayImage(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+	setRect(0,0,100,100);
+	setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
+}
+
+
+
+void itemReplayImage::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+  QImage tim;
+  if (param.im.isNull())
+    {
+      QBrush b(Qt::black,Qt::Dense5Pattern);
+      painter->setPen(pen());
+      painter->setBrush(b);
+      painter->drawRect(param.rct);
+    }
+  else
+    {
+      tim=param.im.scaled(param.rct.width(),param.rct.height(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
+    qreal pad=pen().widthF()/2;
+//    painter->drawImage(param.rct.adjusted(pad,pad,-pad,-pad), param.im, param.im.rect());
+    painter->drawImage(param.rct.adjusted(pad,pad,-pad,-pad), tim);
+    painter->setBrush(Qt::NoBrush);
+    painter->setPen(pen());
+    }
+	if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+itemBorder::itemBorder(QMenu *cntxtMenu): itemBase(cntxtMenu)
+{
+  setRect(0,0,100,100);
+  setFlags(QGraphicsItem::QGraphicsItem::ItemIgnoresTransformations);
+}
+
+
+
+void itemBorder::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *)
+{
+//	if (resized ) prepareGeometryChange();
+  if (param.modified)
+    {
+      param.modified=false;
+      if(param.gradient.type!=sgradientParam::NONE)
+        {
+          QAbstractGraphicsShapeItem::setBrush(buildGradient(param.gradient,rect()));
+        }
+      else
+        {
+          QAbstractGraphicsShapeItem::setBrush(param.fillColor);
+        }
+    }
+  painter->setPen(pen());
+  painter->setBrush(Qt::NoBrush);
+  painter->drawRect(param.rct);
+  if (option->state & QStyle::State_Selected)  highlightSelected(painter,option);
+}
+
+
+
diff --git a/qsstv/editor/graphicitems.h b/qsstv/editor/graphicitems.h
new file mode 100644
index 0000000..9777504
--- /dev/null
+++ b/qsstv/editor/graphicitems.h
@@ -0,0 +1,219 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef GRAPHICITEMS_H
+#define GRAPHICITEMS_H
+#include <QtGui>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+#include <QtWidgets>
+#endif
+
+#include "gradientdialog.h"
+#include "qsstvdefs.h"
+
+
+struct sitemParam
+	{
+		// must be set before returning parameters
+		qreal zValue;
+		int type;
+		QPen pen;
+		QBrush brush;
+		QPointF position;
+// are used dynamically, no need to setup
+		QFont font;
+		QString txt;
+		int rotation;
+		double hShear;
+		double vShear;
+		QImage im;
+		sgradientParam gradient;
+		QRectF rct;
+		bool locked;
+		QLineF line;
+		bool modified;
+		QColor fillColor;
+		QMenu *menu;
+
+};
+
+class itemBase : public QAbstractGraphicsShapeItem
+{
+public:
+  enum egraphType {BASE=QGraphicsItem::UserType+1,RECTANGLE,ELLIPSE,IMAGE,LINE,TEXT,REPLAY,SBORDER};
+	itemBase(QMenu *cntxtMenu);
+	virtual QRectF rect() { return param.rct;}
+	virtual void setRect( const QRectF & rectangle )
+		{
+			param.rct=rectangle;
+			param.modified=true;
+		}
+	virtual void setRect( qreal x, qreal y, qreal width, qreal height )
+		{
+			param.rct=QRectF(x,y,width,height);
+			param.modified=true;
+		}
+	void hoverMoveEvent ( QGraphicsSceneHoverEvent * event );
+	void mouseMoveEvent ( QGraphicsSceneMouseEvent * event );
+	virtual QRectF boundingRect() const
+		{
+   		 return shape().controlPointRect();
+    }
+  QString getTypeStr();
+  void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+	void setLocked(bool b) {param.locked=b;}
+	void setGradient(sgradientParam pm) { param.modified=true; param.gradient=pm;}
+	void setBrush(QColor c);
+	void setImage(QImage ima) {param.im=ima;}
+	virtual QPainterPath shape() const;
+	virtual void setText(const QString &) {};
+	virtual void setFont(QFont) {	}
+	QString text() const { return param.txt;}
+	void load(QDataStream &str); 
+	void save(QDataStream &str);
+	void setTransform ( int rot,double hs,double vs);
+	sitemParam getParam()
+		{
+			param.zValue=zValue();
+			param.type=type();
+			param.pen=pen();
+			param.brush=brush();
+			param.position=pos();
+			return param;
+		}
+	void setParam(sitemParam sp)
+		{
+			setPen(sp.pen);
+			setBrush(sp.fillColor);
+			setFont(sp.font);
+		// are used dynamically, no need to setup
+			param.txt=sp.txt;
+			param.rotation=sp.rotation;
+			param.hShear=sp.hShear;
+			param.vShear=sp.vShear;
+			setTransform (param.rotation,param.hShear,param.vShear);
+			param.im=sp.im;
+			param.gradient=sp.gradient;
+			param.rct=sp.rct;
+			param.locked=sp.locked;
+			param.line=sp.line;
+			param.modified=true;
+			param.fillColor=sp.fillColor;
+			param.menu=sp.menu;
+		}
+		
+	
+	
+protected:
+	void highlightSelected(QPainter *painter,const QStyleOptionGraphicsItem *option);
+  sitemParam param;
+
+
+private:
+	enum corner {CUL,CUR,CDL,CDR,HU,HD,VL,VR,NO};
+  void setTransform ();
+
+	corner getCorner( QPointF mouse);
+	bool grabbed;
+	corner grab;
+};
+
+class itemText : public itemBase
+{
+public:
+	itemText(QMenu *cntxtMenu);
+	~itemText();
+	void setText(const QString &t);
+	void setFont(QFont f);
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+  //QRectF boundingRect() const;
+	int type() const {return  TEXT;}
+  void hover(){;}
+};
+
+
+class itemRectangle : public itemBase
+{
+public:
+	itemRectangle(QMenu *cntxtMenu);
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+	int type() const {return  RECTANGLE;}
+};
+
+
+class itemEllipse : public itemBase
+{
+public:
+	itemEllipse(QMenu *cntxtMenu);
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+//	QRectF boundingRect() const;
+	int type() const {return  ELLIPSE;}
+};
+
+class itemImage : public itemBase
+{
+public:
+	itemImage(QMenu *cntxtMenu);
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+//	QRectF boundingRect() const;
+	int type() const {return  IMAGE;}
+};
+
+class itemLine : public itemBase
+{
+public:
+	itemLine(QMenu *cntxtMenu);
+	void setRect( const QRectF & rectangle )
+		{
+			prepareGeometryChange();
+			param.rct=rectangle;
+			param.line.setPoints(param.rct.topLeft(),param.rct.bottomRight());
+			param.modified=true;
+		}
+	void setRect( qreal x, qreal y, qreal width, qreal height )
+		{
+			prepareGeometryChange();
+			param.rct=QRectF(x,y,width,height);
+			param.line.setLine(x,y,width,height);
+			param.modified=true;
+		}
+	QRectF boundingRect() const;
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+	QPainterPath shape() const;
+	int type() const {return  LINE;}
+};
+
+class itemReplayImage : public itemBase
+{
+public:
+	itemReplayImage(QMenu *cntxtMenu);
+	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+	int type() const {return  REPLAY;}
+};
+
+class itemBorder : public itemBase
+{
+public:
+  itemBorder(QMenu *cntxtMenu);
+  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
+  int type() const {return  SBORDER;}
+};
+
+#endif
diff --git a/qsstv/editor/qdialog_p.h b/qsstv/editor/qdialog_p.h
new file mode 100644
index 0000000..15862a6
--- /dev/null
+++ b/qsstv/editor/qdialog_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info at nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info at nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIALOG_P_H
+#define QDIALOG_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qwidget_p.h"
+#include "QtCore/qeventloop.h"
+#include "QtCore/qpointer.h"
+#include "QtGui/qdialog.h"
+#include "QtGui/qpushbutton.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSizeGrip;
+
+class QDialogPrivate : public QWidgetPrivate
+{
+    Q_DECLARE_PUBLIC(QDialog)
+public:
+
+    QDialogPrivate()
+        : mainDef(0), orientation(Qt::Horizontal),extension(0), doShowExtension(false),
+#ifndef QT_NO_SIZEGRIP
+          resizer(0),
+          sizeGripEnabled(false),
+#endif
+          rescode(0), resetModalityTo(-1), wasModalitySet(true), eventLoop(0)
+        {}
+
+    QPointer<QPushButton> mainDef;
+    Qt::Orientation orientation;
+    QWidget *extension;
+    bool doShowExtension;
+    QSize size, min, max;
+#ifndef QT_NO_SIZEGRIP
+    QSizeGrip *resizer;
+    bool sizeGripEnabled;
+#endif
+    QPoint lastRMBPress;
+
+    void setDefault(QPushButton *);
+    void setMainDefault(QPushButton *);
+    void hideDefault();
+    void resetModalitySetByOpen();
+
+#ifdef Q_WS_WINCE_WM
+    void _q_doneAction();
+#endif
+
+#ifdef Q_WS_MAC
+    virtual void mac_nativeDialogModalHelp() {}
+#endif
+
+    int rescode;
+    int resetModalityTo;
+    bool wasModalitySet;
+
+    QPointer<QEventLoop> eventLoop;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDIALOG_P_H
diff --git a/qsstv/editor/textform.ui b/qsstv/editor/textform.ui
new file mode 100644
index 0000000..958089f
--- /dev/null
+++ b/qsstv/editor/textform.ui
@@ -0,0 +1,96 @@
+<ui version="4.0" >
+ <class>textForm</class>
+ <widget class="QDialog" name="textForm" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>284</width>
+    <height>103</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Enter text</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" >
+   <item>
+    <widget class="QLineEdit" name="lineEdit" />
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout" >
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox" >
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons" >
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer" >
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer" >
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0" >
+      <size>
+       <width>20</width>
+       <height>19</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>textForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>textForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/gallerywidget.cpp b/qsstv/gallerywidget.cpp
new file mode 100644
index 0000000..7d7dac8
--- /dev/null
+++ b/qsstv/gallerywidget.cpp
@@ -0,0 +1,320 @@
+#include "gallerywidget.h"
+#include "ui_gallerywidget.h"
+#include "utils/logging.h"
+#include "qsstvglobal.h"
+#include <QSplashScreen>
+#include "dispatcher.h"
+#include <QFileInfo>
+#include <QStatusBar>
+#include "txwidget.h"
+
+galleryWidget *galleryWidgetPtr;
+
+galleryWidget::galleryWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::galleryWidget)
+{
+    ui->setupUi(this);
+    ui->tabWidget->setCurrentIndex(0);
+}
+
+galleryWidget::~galleryWidget()
+{
+  writeSettings(false);
+  addToLog ("deleting galleryWidget",LOGGALLERY);
+    delete ui;
+  addToLog ("ui deleted: galleryWidget",LOGGALLERY);
+}
+
+
+
+
+void galleryWidget::init()
+{
+  initView();
+  readSettings();
+}
+
+/*!
+  reads the settings (saved images for tx,rx,templates)
+*/
+
+void galleryWidget::readSettings()
+{
+  int i;
+  QSettings qSettings;
+  qSettings.beginGroup ("Gallery");
+  rxIndex=qSettings.value( "rxIndex",0 ).toInt();
+  qSettings.endGroup();
+  splashStr+=QString( "Loading RX images" ).rightJustified(25,' ')+"\n";
+  splashPtr->showMessage ( splashStr ,Qt::AlignLeft,Qt::white);
+  qApp->processEvents();
+  for ( i=0;i<NUMTHUMBS;i++ )
+  {
+    rxThumbsArray[i]->readThumbSettings ( &qSettings );
+  }
+  splashStr+=QString( "Loading TX images" ).rightJustified(25,' ')+"\n";
+  splashPtr->showMessage ( splashStr ,Qt::AlignLeft,Qt::white);
+  qApp->processEvents();
+  for ( i=0;i<NUMTHUMBS;i++ )
+  {
+    txThumbsArray[i]->readThumbSettings ( &qSettings );
+  }
+  splashStr+=QString( "Loading Templates" ).rightJustified(25,' ')+"\n";
+  splashPtr->showMessage ( splashStr ,Qt::AlignLeft,Qt::white);
+  qApp->processEvents();
+  for ( i=0;i<NUMTHUMBS;i++ )
+  {
+    qApp->processEvents();
+    templateThumbsArray[i]->readThumbSettings ( &qSettings );
+  }
+  slotLayoutChanged();
+}
+
+/*!
+  writes the settings (saved images for tx,rx,templates)
+*/
+void galleryWidget::writeSettings(bool onlyRx)
+{
+    int i;
+    QSettings qSettings;
+    qSettings.beginGroup ( "Gallery" );
+    qSettings.setValue ( "rxIndex",rxIndex );
+    qSettings.endGroup();
+    for ( i=0;i<NUMTHUMBS;i++ )
+    {
+        rxThumbsArray[i]->writeThumbSettings ( &qSettings );
+        if(!onlyRx)
+        {
+            txThumbsArray[i]->writeThumbSettings ( &qSettings );
+            templateThumbsArray[i]->writeThumbSettings ( &qSettings );
+        }
+    }
+}
+
+/*!
+  setup of the user interface
+*/
+
+void galleryWidget::initView()
+{
+  #define MINCOLSIZE 32
+  #define MINROWSIZE 26
+  #define MAXCOLSIZE 64
+  #define MAXROWSIZE 52
+  int i;
+  rxThumbsArray[0]= ui->rximage1;
+  rxThumbsArray[1]= ui->rximage2;
+  rxThumbsArray[2]= ui->rximage3;
+  rxThumbsArray[3]= ui->rximage4;
+  rxThumbsArray[4]= ui->rximage5;
+  rxThumbsArray[5]= ui->rximage6;
+  rxThumbsArray[6]= ui->rximage7;
+  rxThumbsArray[7]= ui->rximage8;
+  rxThumbsArray[8]= ui->rximage9;
+  rxThumbsArray[9]= ui->rximage10;
+  rxThumbsArray[10]=ui->rximage11;
+  rxThumbsArray[11]=ui->rximage12;
+
+  txThumbsArray[0]= ui->tximage1;
+  txThumbsArray[1]= ui->tximage2;
+  txThumbsArray[2]= ui->tximage3;
+  txThumbsArray[3]= ui->tximage4;
+  txThumbsArray[4]= ui->tximage5;
+  txThumbsArray[5]= ui->tximage6;
+  txThumbsArray[6]= ui->tximage7;
+  txThumbsArray[7]= ui->tximage8;
+  txThumbsArray[8]= ui->tximage9;
+  txThumbsArray[9]= ui->tximage10;
+  txThumbsArray[10]=ui->tximage11;
+  txThumbsArray[11]=ui->tximage12;
+
+  templateThumbsArray[0]= ui->tmpimage1;
+  templateThumbsArray[1]= ui->tmpimage2;
+  templateThumbsArray[2]= ui->tmpimage3;
+  templateThumbsArray[3]= ui->tmpimage4;
+  templateThumbsArray[4]= ui->tmpimage5;
+  templateThumbsArray[5]= ui->tmpimage6;
+  templateThumbsArray[6]= ui->tmpimage7;
+  templateThumbsArray[7]= ui->tmpimage8;
+  templateThumbsArray[8]= ui->tmpimage9;
+  templateThumbsArray[9]= ui->tmpimage10;
+  templateThumbsArray[10]=ui->tmpimage11;
+  templateThumbsArray[11]=ui->tmpimage12;
+  for ( i=0;i<NUMTHUMBS;i++ )
+  {
+    rxThumbsArray[i]->setType ( imageViewer::RXTHUMB );
+    txThumbsArray[i]->setType ( imageViewer::TXTHUMB );
+    templateThumbsArray[i]->setType ( imageViewer::TEMPLATETHUMB );
+    connect( rxThumbsArray[i],SIGNAL(layoutChanged()),SLOT(slotLayoutChanged()));
+    connect( txThumbsArray[i],SIGNAL(layoutChanged()),SLOT(slotLayoutChanged()));
+    connect( templateThumbsArray[i],SIGNAL(layoutChanged()),SLOT(slotLayoutChanged()));
+  }
+  for (i=0;i<4;i++)
+    {
+      ui->gridLayout1->setColumnMinimumWidth(i,MINCOLSIZE);
+      ui->gridLayout1->setColumnStretch(i,1);
+      ui->gridLayout2->setColumnMinimumWidth(i,MINCOLSIZE);
+      ui->gridLayout2->setColumnStretch(i,1);
+      ui->gridLayout3->setColumnMinimumWidth(i,MINCOLSIZE);
+      ui->gridLayout3->setColumnStretch(i,1);
+    }
+  for (i=0;i<3;i++)
+    {
+      ui->gridLayout1->setRowMinimumHeight(i,MINROWSIZE);
+      ui->gridLayout1->setRowStretch(i,0);
+      ui->gridLayout2->setRowMinimumHeight(i,MINROWSIZE);
+      ui->gridLayout2->setRowStretch(i,0);
+      ui->gridLayout3->setRowMinimumHeight(i,MINROWSIZE);
+      ui->gridLayout3->setRowStretch(i,0);
+    }
+}
+
+/*!
+  closeEvent signals the dispatcher to initiate  program exit.
+*/
+
+
+/*!
+  get the filename of a template
+
+  \param[in] tm index of template
+  \return QString containing filename, check with QString.isNull for validity
+*/
+
+QString galleryWidget::getTemplateFileName ( int tm )
+{
+  if ( tm<NUMTHUMBS )
+  {
+    return templateThumbsArray[tm]->getFilename();
+  }
+  return QString();
+}
+
+ const QStringList &galleryWidget::getFilenames()
+ {
+   QString str;
+   sl.clear();
+
+   int i;
+   for(i=0;i<NUMTHUMBS;i++)
+   {
+     str=getTemplateFileName (i);
+     if(!str.isEmpty())
+      {
+        QFileInfo fi(str);
+        sl.append(fi.baseName());
+      }
+     else
+     {
+       sl.append("Empty");
+     }
+   }
+   return sl;
+ }
+
+/*!
+  loads an image in the rximages gallery in a new position
+
+  \param[in] fn the filename
+
+*/
+
+void galleryWidget::putRxImage ( QString fn )
+{
+  statusBarPtr->showMessage ( "Saved: "+fn );
+  shuffle(rxThumbsArray,rxIndex);
+//  cleanup(rxThumbsArray,imageViewer::RXTHUMB);
+  rxThumbsArray[rxIndex]->openImage(fn,false,false);
+  addToLog (QString("adding item %1 fn: %2").arg(rxIndex).arg(fn),LOGGALLERY);
+  if(rxIndex<NUMTHUMBS-1) rxIndex++;
+  writeSettings(true);
+}
+
+/*!
+  loads an image in the rximages gallery in a new position
+
+  \return QString containing filename, check with QString.isEmpty for validity
+
+*/
+QString galleryWidget::getLastRxImage()
+{
+  //rxIndex points to the first empty location;
+  int tmpIdx;
+  tmpIdx=rxIndex;
+  if(tmpIdx!=NUMTHUMBS-1) tmpIdx-=1;
+  if(tmpIdx<0) return QString::Null();
+  return rxThumbsArray[tmpIdx]->getFilename();
+}
+
+
+void galleryWidget::cleanup(imageViewer *array[],imageViewer::thumbType tp)
+{
+  int i,j;
+  bool found;
+  for(i=0;i<NUMTHUMBS-1;i++)
+    {
+      found=false;
+      if(array[i]->getFilename().isEmpty())
+        {
+           addToLog (QString("empty item %1").arg(i),LOGGALLERY);
+          array[i]->init(tp);
+
+          for(j=i+1;j<NUMTHUMBS;j++)
+            {
+              if(!array[j]->getFilename().isEmpty())
+                {
+                  array[i]->copy(array[j]);
+                  array[j]->init(tp);
+                  found=true;
+                  break;
+                }
+            }
+          if(!found) break;
+         }
+      else
+      {
+          addToLog (QString("item %1 fn: %2").arg(i).arg(array[i]->getFilename()),LOGGALLERY);
+      }
+    }
+ }
+
+
+void galleryWidget::slotLayoutChanged()
+{
+  int i;
+  cleanup(rxThumbsArray,imageViewer::RXTHUMB);
+  cleanup(txThumbsArray,imageViewer::TXTHUMB);
+  cleanup(templateThumbsArray,imageViewer::TEMPLATETHUMB);
+  rxIndex=NUMTHUMBS-1;
+  for(i=0;i<NUMTHUMBS;i++)
+    {
+      if(rxThumbsArray[i]->getFilename().isEmpty())
+        {
+          rxIndex=i;
+          break;
+        }
+    }
+  txWidgetPtr->setupTemplatesComboBox();
+}
+
+void galleryWidget::shuffle(imageViewer *ar[],int index)
+{
+  int i,j;
+  if(ar[rxIndex]->getFilename().isEmpty()) return;
+  if(index>=NUMTHUMBS-1)
+    {
+      for(i=0,j=1;(i<(NUMTHUMBS-1))&&(j<NUMTHUMBS);j++)
+        {
+          if(!ar[j]->getFilename().isEmpty())
+          {
+            addToLog (QString("shuffling %1 fn: %2 to %3").arg(j).arg(ar[j]->getFilename()).arg(i),LOGGALLERY);
+            ar[i]->copy(ar[j]);
+            i++;
+          }
+        }
+      ar[j-1]->clear();
+    }
+
+
+}
diff --git a/qsstv/gallerywidget.h b/qsstv/gallerywidget.h
new file mode 100644
index 0000000..db1baa0
--- /dev/null
+++ b/qsstv/gallerywidget.h
@@ -0,0 +1,48 @@
+#ifndef GALLERYWIDGET_H
+#define GALLERYWIDGET_H
+
+#include <QWidget>
+#include "widgets/imageviewer.h"
+
+
+#define NUMTHUMBS 12
+
+namespace Ui {
+    class galleryWidget;
+}
+
+class galleryWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit galleryWidget(QWidget *parent = 0);
+    ~galleryWidget();
+
+  void init();
+  void writeSettings(bool onlyRx);
+  void readSettings();
+  void putRxImage(QString fn);
+  QString getTemplateFileName(int);
+  const QStringList &getFilenames();
+  QString getLastRxImage();
+
+public slots:
+    void slotLayoutChanged();
+
+
+private:
+    Ui::galleryWidget *ui;
+    void initView();
+    void cleanup(imageViewer *array[], imageViewer::thumbType tp);
+    void shuffle(imageViewer *ar[], int idx);
+    imageViewer *rxThumbsArray[NUMTHUMBS];
+    imageViewer *txThumbsArray[NUMTHUMBS];
+    imageViewer *templateThumbsArray[NUMTHUMBS];
+    int rxIndex;
+    QStringList sl;
+};
+
+extern galleryWidget *galleryWidgetPtr;
+
+#endif // GALLERYWIDGET_H
diff --git a/qsstv/gallerywidget.ui b/qsstv/gallerywidget.ui
new file mode 100644
index 0000000..418b671
--- /dev/null
+++ b/qsstv/gallerywidget.ui
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>galleryWidget</class>
+ <widget class="QWidget" name="galleryWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>562</width>
+    <height>376</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>1</number>
+   </property>
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="font">
+      <font>
+       <pointsize>8</pointsize>
+      </font>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="rxtab">
+      <attribute name="title">
+       <string>RX</string>
+      </attribute>
+      <layout class="QHBoxLayout" name="horizontalLayout_3">
+       <property name="spacing">
+        <number>1</number>
+       </property>
+       <property name="margin">
+        <number>1</number>
+       </property>
+       <item>
+        <layout class="QGridLayout" name="gridLayout1" rowstretch="1,1,1" columnstretch="1,1,1,1">
+         <property name="sizeConstraint">
+          <enum>QLayout::SetNoConstraint</enum>
+         </property>
+         <item row="0" column="0">
+          <widget class="imageViewer" name="rximage1" native="true"/>
+         </item>
+         <item row="0" column="1">
+          <widget class="imageViewer" name="rximage2" native="true"/>
+         </item>
+         <item row="0" column="2">
+          <widget class="imageViewer" name="rximage3" native="true"/>
+         </item>
+         <item row="0" column="3">
+          <widget class="imageViewer" name="rximage4" native="true"/>
+         </item>
+         <item row="1" column="0">
+          <widget class="imageViewer" name="rximage5" native="true"/>
+         </item>
+         <item row="1" column="1">
+          <widget class="imageViewer" name="rximage6" native="true"/>
+         </item>
+         <item row="1" column="2">
+          <widget class="imageViewer" name="rximage7" native="true"/>
+         </item>
+         <item row="1" column="3">
+          <widget class="imageViewer" name="rximage8" native="true"/>
+         </item>
+         <item row="2" column="0">
+          <widget class="imageViewer" name="rximage9" native="true"/>
+         </item>
+         <item row="2" column="1">
+          <widget class="imageViewer" name="rximage10" native="true"/>
+         </item>
+         <item row="2" column="2">
+          <widget class="imageViewer" name="rximage11" native="true"/>
+         </item>
+         <item row="2" column="3">
+          <widget class="imageViewer" name="rximage12" native="true"/>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="txtab">
+      <attribute name="title">
+       <string>TX</string>
+      </attribute>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <property name="spacing">
+        <number>1</number>
+       </property>
+       <property name="margin">
+        <number>1</number>
+       </property>
+       <item>
+        <layout class="QGridLayout" name="gridLayout2">
+         <item row="0" column="0">
+          <widget class="imageViewer" name="tximage1" native="true"/>
+         </item>
+         <item row="0" column="1">
+          <widget class="imageViewer" name="tximage2" native="true"/>
+         </item>
+         <item row="0" column="2">
+          <widget class="imageViewer" name="tximage3" native="true"/>
+         </item>
+         <item row="0" column="3">
+          <widget class="imageViewer" name="tximage4" native="true"/>
+         </item>
+         <item row="1" column="0">
+          <widget class="imageViewer" name="tximage5" native="true"/>
+         </item>
+         <item row="1" column="1">
+          <widget class="imageViewer" name="tximage6" native="true"/>
+         </item>
+         <item row="1" column="2">
+          <widget class="imageViewer" name="tximage7" native="true"/>
+         </item>
+         <item row="1" column="3">
+          <widget class="imageViewer" name="tximage8" native="true"/>
+         </item>
+         <item row="2" column="0">
+          <widget class="imageViewer" name="tximage9" native="true"/>
+         </item>
+         <item row="2" column="1">
+          <widget class="imageViewer" name="tximage10" native="true"/>
+         </item>
+         <item row="2" column="2">
+          <widget class="imageViewer" name="tximage11" native="true"/>
+         </item>
+         <item row="2" column="3">
+          <widget class="imageViewer" name="tximage12" native="true"/>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="templatetab">
+      <attribute name="title">
+       <string>Templates</string>
+      </attribute>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>1</number>
+       </property>
+       <property name="margin">
+        <number>1</number>
+       </property>
+       <item>
+        <layout class="QGridLayout" name="gridLayout3">
+         <item row="0" column="0">
+          <widget class="imageViewer" name="tmpimage1" native="true"/>
+         </item>
+         <item row="0" column="1">
+          <widget class="imageViewer" name="tmpimage2" native="true"/>
+         </item>
+         <item row="0" column="2">
+          <widget class="imageViewer" name="tmpimage3" native="true"/>
+         </item>
+         <item row="0" column="3">
+          <widget class="imageViewer" name="tmpimage4" native="true"/>
+         </item>
+         <item row="1" column="0">
+          <widget class="imageViewer" name="tmpimage5" native="true"/>
+         </item>
+         <item row="1" column="1">
+          <widget class="imageViewer" name="tmpimage6" native="true"/>
+         </item>
+         <item row="1" column="2">
+          <widget class="imageViewer" name="tmpimage7" native="true"/>
+         </item>
+         <item row="1" column="3">
+          <widget class="imageViewer" name="tmpimage8" native="true"/>
+         </item>
+         <item row="2" column="0">
+          <widget class="imageViewer" name="tmpimage9" native="true"/>
+         </item>
+         <item row="2" column="1">
+          <widget class="imageViewer" name="tmpimage10" native="true"/>
+         </item>
+         <item row="2" column="2">
+          <widget class="imageViewer" name="tmpimage11" native="true"/>
+         </item>
+         <item row="2" column="3">
+          <widget class="imageViewer" name="tmpimage12" native="true"/>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>imageViewer</class>
+   <extends>QWidget</extends>
+   <header>widgets/imageviewer.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/icons/arrow.png b/qsstv/icons/arrow.png
new file mode 100644
index 0000000..62229be
Binary files /dev/null and b/qsstv/icons/arrow.png differ
diff --git a/qsstv/icons/camera.png b/qsstv/icons/camera.png
new file mode 100644
index 0000000..6876fa9
Binary files /dev/null and b/qsstv/icons/camera.png differ
diff --git a/qsstv/icons/colorfill.png b/qsstv/icons/colorfill.png
new file mode 100644
index 0000000..6e8ce0c
Binary files /dev/null and b/qsstv/icons/colorfill.png differ
diff --git a/qsstv/icons/colorline.png b/qsstv/icons/colorline.png
new file mode 100644
index 0000000..23449ed
Binary files /dev/null and b/qsstv/icons/colorline.png differ
diff --git a/qsstv/icons/colorpicker.png b/qsstv/icons/colorpicker.png
new file mode 100644
index 0000000..faa5080
Binary files /dev/null and b/qsstv/icons/colorpicker.png differ
diff --git a/qsstv/icons/colorselector.png b/qsstv/icons/colorselector.png
new file mode 100644
index 0000000..f200edc
Binary files /dev/null and b/qsstv/icons/colorselector.png differ
diff --git a/qsstv/icons/doubletone.png b/qsstv/icons/doubletone.png
new file mode 100644
index 0000000..c60ca1f
Binary files /dev/null and b/qsstv/icons/doubletone.png differ
diff --git a/qsstv/icons/edit.png b/qsstv/icons/edit.png
new file mode 100644
index 0000000..526ca69
Binary files /dev/null and b/qsstv/icons/edit.png differ
diff --git a/qsstv/icons/eraser.png b/qsstv/icons/eraser.png
new file mode 100644
index 0000000..78632a5
Binary files /dev/null and b/qsstv/icons/eraser.png differ
diff --git a/qsstv/icons/fcircle.png b/qsstv/icons/fcircle.png
new file mode 100644
index 0000000..4f5fcc7
Binary files /dev/null and b/qsstv/icons/fcircle.png differ
diff --git a/qsstv/icons/filenew.png b/qsstv/icons/filenew.png
new file mode 100644
index 0000000..004ca03
Binary files /dev/null and b/qsstv/icons/filenew.png differ
diff --git a/qsstv/icons/fileopen.png b/qsstv/icons/fileopen.png
new file mode 100644
index 0000000..a79982e
Binary files /dev/null and b/qsstv/icons/fileopen.png differ
diff --git a/qsstv/icons/filesave.png b/qsstv/icons/filesave.png
new file mode 100644
index 0000000..3bc2a37
Binary files /dev/null and b/qsstv/icons/filesave.png differ
diff --git a/qsstv/icons/frect.png b/qsstv/icons/frect.png
new file mode 100644
index 0000000..2aa6af8
Binary files /dev/null and b/qsstv/icons/frect.png differ
diff --git a/qsstv/icons/gradient.png b/qsstv/icons/gradient.png
new file mode 100644
index 0000000..8d522c2
Binary files /dev/null and b/qsstv/icons/gradient.png differ
diff --git a/qsstv/icons/image.png b/qsstv/icons/image.png
new file mode 100644
index 0000000..a6f715e
Binary files /dev/null and b/qsstv/icons/image.png differ
diff --git a/qsstv/icons/line.png b/qsstv/icons/line.png
new file mode 100644
index 0000000..3b1dddd
Binary files /dev/null and b/qsstv/icons/line.png differ
diff --git a/qsstv/icons/mgc.raw b/qsstv/icons/mgc.raw
new file mode 100644
index 0000000..a56a08a
Binary files /dev/null and b/qsstv/icons/mgc.raw differ
diff --git a/qsstv/icons/mgc2.raw b/qsstv/icons/mgc2.raw
new file mode 100644
index 0000000..751ab07
--- /dev/null
+++ b/qsstv/icons/mgc2.raw
@@ -0,0 +1 @@
+s`�dF512[8#0:E/?�r
/#0�bAFKC/?83�pVI&,
/M6�_
/?4�_12*1�_T7[8#0�e
diff --git a/qsstv/icons/qsstv.png b/qsstv/icons/qsstv.png
new file mode 100644
index 0000000..e052b37
Binary files /dev/null and b/qsstv/icons/qsstv.png differ
diff --git a/qsstv/icons/qsstvsplash.png b/qsstv/icons/qsstvsplash.png
new file mode 100644
index 0000000..125811e
Binary files /dev/null and b/qsstv/icons/qsstvsplash.png differ
diff --git a/qsstv/icons/replay.png b/qsstv/icons/replay.png
new file mode 100644
index 0000000..d8a386f
Binary files /dev/null and b/qsstv/icons/replay.png differ
diff --git a/qsstv/icons/start.png b/qsstv/icons/start.png
new file mode 100644
index 0000000..bc7336c
Binary files /dev/null and b/qsstv/icons/start.png differ
diff --git a/qsstv/icons/stop.png b/qsstv/icons/stop.png
new file mode 100644
index 0000000..2a7e1bf
Binary files /dev/null and b/qsstv/icons/stop.png differ
diff --git a/qsstv/icons/sweep.png b/qsstv/icons/sweep.png
new file mode 100644
index 0000000..2ce104b
Binary files /dev/null and b/qsstv/icons/sweep.png differ
diff --git a/qsstv/icons/text.png b/qsstv/icons/text.png
new file mode 100644
index 0000000..50af422
Binary files /dev/null and b/qsstv/icons/text.png differ
diff --git a/qsstv/icons/tone.png b/qsstv/icons/tone.png
new file mode 100644
index 0000000..370775a
Binary files /dev/null and b/qsstv/icons/tone.png differ
diff --git a/qsstv/icons/transparency.png b/qsstv/icons/transparency.png
new file mode 100644
index 0000000..1c007fa
Binary files /dev/null and b/qsstv/icons/transparency.png differ
diff --git a/qsstv/icons/whatsthis.png b/qsstv/icons/whatsthis.png
new file mode 100644
index 0000000..422caed
Binary files /dev/null and b/qsstv/icons/whatsthis.png differ
diff --git a/qsstv/logbook/logbook.cpp b/qsstv/logbook/logbook.cpp
new file mode 100644
index 0000000..5c5c60f
--- /dev/null
+++ b/qsstv/logbook/logbook.cpp
@@ -0,0 +1,100 @@
+#include "logbook.h"
+#include "qsstvglobal.h"
+#include "xmlrpc/xmlinterface.h"
+#include "xmlrpc/ipcmessage.h"
+#include "configparams.h"
+#include "rig/rigcontrol.h"
+#include <QDateTime>
+
+
+
+slogParam logParamArray[NUMLOGPARAMS]=
+{
+  {"program","QSSTV 8"},
+    {"version", "1"},
+    {"date","" },
+    {"time",""  },
+    {"endTime",""  },
+    {"call","" , },
+    {"mhz",""  },
+    {"mode",""  },
+    {"tx",""  },
+    {"rx",""  },
+    {"name","" },
+    {"qth","" },
+    {"state","" },
+    {"province","" },
+    {"country","" },
+    {"locator","" },
+    {"serialout","" },
+    {"serialin","" },
+    {"free1","" },
+    {"notes","" },
+    {"power","" }
+};
+
+// to be independent from localization
+QString monthArray[12]=
+{
+  "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+
+logBook::logBook()
+{
+  ipcQueue=new ipcMessage(1238);
+
+}
+
+
+void logBook::logQSO(QString call,QString comment)
+{
+  int i;
+  QDateTime dt(QDateTime::currentDateTimeUtc());
+  QString tmp;
+  getFrequency();
+  if(frequency!=-1) setParam(LFREQ,QString::number(frequency/1000000.,'g',9));
+  setParam(LCALL,call);
+  setParam(LNOTES,comment);
+  tmp=QString::number(dt.date().day())+" "+monthArray[dt.date().month()-1]+" "+QString::number(dt.date().year());
+  setParam(LDATE,tmp);
+  tmp=QString::number(dt.time().hour()*100+dt.time().minute()).rightJustified(4,'0');
+  setParam(LTIME,tmp);
+
+ setParam(LENDTIME,tmp);
+//  setParam(LENDTIME,"");
+  setParam(LMODE,"SSTV");
+  setParam(LNOTES,comment);
+  tmp.clear();
+  for(i=0;i<NUMLOGPARAMS;i++)
+    {
+      tmp+=logParamArray[i].tag+":"+logParamArray[i].val+QChar(0x01);
+    }
+  ipcQueue->sendMessage(tmp);
+}
+
+
+
+
+
+void logBook::getFrequency()
+{
+
+  if(rigController->params()->enableXMLRPC) // we get the frequency from flrig or alike
+    {
+      frequency=xmlIntfPtr->getFrequency();
+    }
+  else if(rigController->params()->enableCAT) // we get the frequency from hamlib
+    {
+      if(!rigController->getFrequency(frequency))
+        {
+          frequency=-1;
+        }
+    }
+}
+
+void logBook::setParam(eIndex tag,QString value)
+{
+   logParamArray[tag].val=value;
+}
+
diff --git a/qsstv/logbook/logbook.h b/qsstv/logbook/logbook.h
new file mode 100644
index 0000000..eba79f1
--- /dev/null
+++ b/qsstv/logbook/logbook.h
@@ -0,0 +1,32 @@
+#ifndef LOGBOOK_H
+#define LOGBOOK_H
+
+#include <QString>
+
+class ipcMessage;
+
+#define NUMLOGPARAMS 21
+
+struct slogParam
+{
+  QString tag;
+  QString val;
+};
+
+
+
+class logBook
+{
+public:
+  enum eIndex {LPROG,LVER,LDATE,LTIME,LENDTIME,LCALL,LFREQ,LMODE,LTX,LRX,LNAME,LQTH,LSTATE,LPROV,LCNTRY,LLOC,LSO,LSI,LFREE,LNOTES,LPWR};
+  logBook();
+  void logQSO(QString call,QString comment);
+private:
+  void getFrequency();
+  double frequency;
+  void setParam(eIndex tag,QString value);
+  ipcMessage *ipcQueue;
+
+};
+
+#endif // LOGBOOK_H
diff --git a/qsstv/main.cpp b/qsstv/main.cpp
new file mode 100644
index 0000000..ace263f
--- /dev/null
+++ b/qsstv/main.cpp
@@ -0,0 +1,84 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#include <QApplication>
+
+#include <QtGui>
+//#include <QCDEStyle>
+#include "qsstvglobal.h"
+#include "mainwindow.h"
+#include "dsp/filterparam.h"
+#include "dsp/filter.h"
+#include <QPixmap>
+#include <QSplashScreen>
+#include <QTimer>
+
+QSplashScreen *splash;
+
+
+
+int main( int argc, char ** argv )
+{
+  int result=true;
+  QTimer tm;
+  tm.setSingleShot(true);
+  QApplication::setColorSpec( QApplication::ManyColor );
+  QApplication app( argc, argv );
+  QCoreApplication::setOrganizationName(ORGANIZATION);
+  QCoreApplication::setApplicationName(APPLICATION);
+  QPixmap pixmap(":/icons/qsstvsplash.png");
+  QSplashScreen splash(pixmap,Qt::WindowStaysOnTopHint);
+  splashPtr=&splash;
+
+  QFont f;
+  f.setBold(true);
+  f.setPixelSize(18);
+  splashPtr->setFont(f);
+  splash.show();
+  splashStr="\n\n\n";
+  splashStr+=QString( "Starting %1").arg(qsstvVersion).rightJustified(25,' ')+"\n";
+  splash.showMessage (splashStr,Qt::AlignLeft,Qt::white);
+  tm.start(500);
+  globalInit();
+  mainWindowPtr=new mainWindow;
+  mainWindowPtr->setWindowIcon(QPixmap(":/icons/qsstv.png"));
+
+  while(1)
+  {
+    app.processEvents();
+    if(!tm.isActive()) break;
+   }
+  mainWindowPtr->init(); // this must follow show() because window has to be drawn first to determine fftframe window size
+  mainWindowPtr->hide();
+  tm.start(3000);
+  while(1)
+  {
+    app.processEvents();
+    if(!tm.isActive()) break;
+   }
+  splash.finish(mainWindowPtr);
+  mainWindowPtr->show();
+  mainWindowPtr->startReceiving();
+  result=app.exec();
+  globalEnd();
+  return result;
+}
+
diff --git a/qsstv/mainwindow.cpp b/qsstv/mainwindow.cpp
new file mode 100644
index 0000000..c50d348
--- /dev/null
+++ b/qsstv/mainwindow.cpp
@@ -0,0 +1,380 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#include "mainwindow.h"
+#include "qsstvglobal.h"
+#include "ui_mainwindow.h"
+#include "sound/soundio.h"
+#include "configdialog.h"
+#include "configparams.h"
+#ifndef QT_NO_DEBUG
+#include "scope/scopeview.h"
+#endif
+
+#include <QFont>
+#include <QCloseEvent>
+#include <QMessageBox>
+#include "dispatcher.h"
+#include "sound/calibration.h"
+#include "dsp/filterparam.h"
+#include "dsp/filter.h"
+#include "utils/supportfunctions.h"
+#include "utils/ftp.h"
+#include "rig/rigcontrol.h"
+#include "logbook/logbook.h"
+
+
+
+/**
+ * @brief
+ *
+ * @param parent
+ */
+mainWindow::mainWindow(QWidget *parent) :
+  QMainWindow(parent),
+  ui(new Ui::MainWindow)
+{
+  soundIOPtr=new soundIO;
+  rigControllerR1=new rigControl(1);
+//  rigControllerR2=new rigControl(2);
+  rigController=rigControllerR1;
+  confDiag=new configDialog();
+#ifndef QT_NO_DEBUG
+  scopeViewerData=new scopeView("Data Scope");
+  scopeViewerSync=new scopeView("Sync Scope");
+  scopeViewerData->setAlternativeScaleMultiplier(SUBSAMPLINGRATIO/rxClock);
+  scopeViewerSync->setAlternativeScaleMultiplier(SUBSAMPLINGRATIO/rxClock);
+#endif
+
+//  drmProfileComboBox->addItem("FAX");
+  wfTextPushButton=new QPushButton("WF Text",this);
+  bsrPushButton=new QPushButton("BSR",this);
+  idPushButton=new QPushButton("ID",this);
+
+  greenPXM=new QPixmap(16,16);
+  greenPXM->fill(Qt::green);
+  redPXM=new QPixmap(16,16);
+  redPXM->fill(Qt::red);
+  pttText.setText("   PTT");
+  pttIcon=new QLabel(this);
+  pttIcon->setFixedSize(16,16);
+  pttIcon->setPixmap(*greenPXM);
+  pttIcon->setFrameShape(QFrame::Panel);
+  pttIcon->setFrameShadow(QFrame::Raised);
+  pttIcon->setLineWidth(2);
+  dispatcherPtr=NULL;
+
+  setWindowIcon(QPixmap(":/icons/qsstv.png"));
+  readSettings();
+
+  ui->setupUi(this);
+  ui->maintabWidget->setCurrentIndex(0);
+  ui->statusBar->addPermanentWidget(wfTextPushButton);
+  ui->statusBar->addPermanentWidget(bsrPushButton);
+  ui->statusBar->addPermanentWidget(idPushButton);
+  ui->statusBar->addPermanentWidget(&pttText);
+  ui->statusBar->addPermanentWidget(pttIcon);
+  statusBarPtr=statusBar(); // must be after setup UI
+
+
+  setWindowTitle(qsstvVersion);
+  logBookPtr=new logBook;
+}
+
+/**
+ * @brief
+ *
+ */
+mainWindow::~mainWindow()
+{
+#ifndef QT_NO_DEBUG
+  delete scopeViewerData;
+  delete scopeViewerSync;
+#endif
+  delete ui;
+}
+
+/**
+ * @brief initialize sound device and dispatcher
+ *
+ */
+void mainWindow::init()
+{
+  QString ret;
+  readSettings();
+  rxWidgetPtr=ui->rxWindow;
+  txWidgetPtr=ui->txWindow;
+  galleryWidgetPtr=ui->galleryWindow;
+   if(!dispatcherPtr) dispatcherPtr=new dispatcher();
+   ret=dispatcherPtr->init();
+   if(!ret.isEmpty())
+   {
+     splashStr+=ret.rightJustified(25,' ')+"\n";
+     splashPtr->showMessage(splashStr ,Qt::AlignLeft,Qt::white);
+   }
+
+
+  confDiag->readSettings();
+  connect(ui->actionExit,SIGNAL(triggered()),this, SLOT(slotExit()));
+  connect(ui->actionConfigure,SIGNAL(triggered()),this, SLOT(slotConfigure()));
+  connect(ui->actionCalibrate,SIGNAL(triggered()),this, SLOT(slotCalibrate()));
+  connect(ui->actionAboutQSSTV, SIGNAL(triggered()),SLOT(slotAboutQSSTV()));
+  connect(ui->actionAboutQt, SIGNAL(triggered()),SLOT(slotAboutQt()));
+  connect(ui->actionUsersGuide, SIGNAL(triggered()),SLOT(slotDocumentation()));
+
+
+  connect(idPushButton, SIGNAL(clicked()), this, SLOT(slotSendID()));
+  connect(bsrPushButton, SIGNAL(clicked()), this, SLOT(slotSendBSR()));
+//  connect(fixPushButton, SIGNAL(clicked()), this, SLOT(slotSendFIX()));
+  connect(wfTextPushButton, SIGNAL(clicked()), this, SLOT(slotSendWfText()));
+
+
+#ifndef QT_NO_DEBUG
+//  connect(ui->actionTest,SIGNAL(triggered()),this, SLOT(slotTest()));
+  connect(ui->actionLogSettings, SIGNAL(triggered()),SLOT(slotLogSettings()));
+  connect(ui->actionResetLog, SIGNAL(triggered()),SLOT(slotResetLog()));
+  connect(ui->actionShowDataScope, SIGNAL(triggered()),SLOT(slotShowDataScope()));
+  connect(ui->actionShowSyncScope, SIGNAL(triggered()),SLOT(slotShowSyncScope()));
+  connect(ui->actionScopeOffset,SIGNAL(triggered()),this, SLOT(slotScopeOffset()));
+#else
+  ui->menuOptions->removeAction(ui->actionTest);
+  ui->menuOptions->removeAction(ui->actionLogSettings);
+  ui->menuOptions->removeAction(ui->actionShowDataScope);
+  ui->menuOptions->removeAction(ui->actionShowSyncScope);
+  ui->menuOptions->removeAction(ui->actionLogSettings);
+  ui->menuOptions->removeAction(ui->actionResetLog);
+  ui->menuOptions->removeAction(ui->actionScopeOffset);
+
+#endif
+
+
+  if(!soundIOPtr->init())
+  {
+    splashStr+=QString("Soundcard error: %1").arg(*soundIOPtr->getLastError()).rightJustified(25,' ')+"\n";;
+    splashPtr->showMessage(splashStr ,Qt::AlignLeft,Qt::white);
+  }
+  else
+  {
+    soundIOPtr->start();
+  }
+
+#ifndef QT_NO_DEBUG
+ rxWidgetPtr->functionsPtr()->setOffset(dataScopeOffset,false);
+#endif
+
+}
+
+void mainWindow::startReceiving()
+{
+  txWidgetPtr->setSettingsTab();
+  rxWidgetPtr->setSettingsTab();
+  rxWidgetPtr->start(true);
+}
+
+void mainWindow::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("MAIN");
+  int windowWidth = qSettings.value("windowWidth", 460 ).toInt();
+  int windowHeight = qSettings.value("windowHeight", 530 ).toInt();
+  int windowX = qSettings.value( "windowX", -1 ).toInt();
+  int windowY = qSettings.value( "windowY", -1 ).toInt();
+  resize( windowWidth, windowHeight );
+  if ( windowX != -1 || windowY != -1 ) 	move( windowX, windowY );
+  transmissionModeIndex=(etransmissionMode)qSettings.value("transmissionModeIndex",0).toInt();
+  dataScopeOffset=qSettings.value("dataScopeOffset",0).toUInt();
+  logfile->readSettings(qSettings);
+  qSettings.endGroup();
+}
+
+void mainWindow::writeSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("MAIN");
+  qSettings.setValue( "windowWidth", width() );
+  qSettings.setValue( "windowHeight", height() );
+  qSettings.setValue( "windowX", x() );
+  qSettings.setValue( "windowY", y() );
+  qSettings.setValue("dataScopeOffset",dataScopeOffset);
+  qSettings.setValue("transmissionModeIndex",(int)transmissionModeIndex);
+
+  logfile->writeSettings(qSettings);
+  galleryWidgetPtr->writeSettings(false);
+  qSettings.endGroup();
+}
+
+
+/**
+ *\todo fontselection
+ */
+void mainWindow::setNewFont()
+{
+//  QFont fnt;
+//  fnt.fromString(fontString);
+//  setFont(fnt);
+//  galMW->setFont(fnt);
+//  rxMW->setFont(fnt);
+//  txMW->setFont(fnt);
+}
+
+void mainWindow::slotExit()
+{
+  int exit;
+  exit=QMessageBox::information(this, tr("Quit..."),
+                                tr("Do your really want to quit QSSTV?"),
+                                QMessageBox::Ok, QMessageBox::Cancel);
+    if(exit==QMessageBox::Ok)
+        {
+
+           dispatcherPtr->stopRXTX();
+           deleteFiles(rxImagesPath,"*.rs*");
+           deleteFiles(txImagesPath,"*.rs*");
+           soundIOPtr->stopAndWait();
+           QApplication::quit();
+      }
+   writeSettings();
+}
+
+void  mainWindow::closeEvent ( QCloseEvent *e )
+{
+  slotExit();
+  e->ignore();
+}
+
+void mainWindow::slotConfigure()
+{
+
+  if(confDiag->exec()==QDialog::Accepted)
+    {
+      if(!soundIOPtr->init())
+        {
+          QMessageBox::critical(this, tr("Soundcard error"),*soundIOPtr->getLastError());
+        }
+      else
+        {
+           soundIOPtr->start();
+        }
+     dispatcherPtr->init();
+     rxWidgetPtr->start(true);
+    }
+}
+
+void mainWindow::slotLogSettings()
+{
+  logfile->maskSelect(this);
+}
+
+void mainWindow::slotResetLog()
+{
+ logfile->reset();
+}
+
+
+void mainWindow::slotCalibrate()
+{
+  calibration calib(this);
+  if(calib.exec()==QDialog::Accepted)
+    {
+      rxClock=calib.getRXClock();
+      txClock=calib.getTXClock();
+    }
+}
+
+void mainWindow::slotAboutQSSTV()
+{
+  QString temp=tr("QSSTV\nVersion: ") + MAJORVERSION + MINORVERSION ;
+  temp += QString("\n http://users.telenet.be/on4qz \n(c) 2014-%1 -- Johan Maes - ON4QZ\n%2").arg(QDate::currentDate().toString("yyyy")).arg(shortText);
+//  temp += "\n http://users.telenet.be/on4qz \n(c) 2000-20135-- Johan Maes - ON4QZ\n HAMDRM Software based on RX/TXAMADRM\n from PA0MBO";
+
+
+  QMessageBox::about(this,tr("About..."),temp);
+
+
+//  QString temp=tr("QSSTV\nVersion: ") + MAJORVERSION + MINORVERSION;
+//  temp += "\n http://users.telenet.be/on4qz \n(c) 2000-20135-- Johan Maes - ON4QZ\n HAMDRM Software based on RX/TXAMADRM\n from PA0MBO";
+//  QMessageBox::about(this,tr("About..."),temp);
+
+}
+
+void mainWindow::slotAboutQt()
+{
+   QMessageBox::aboutQt(this,tr("About..."));
+
+}
+
+
+
+
+
+void mainWindow::setPTT(bool p)
+{
+  if(p) pttIcon->setPixmap(*redPXM);
+  else pttIcon->setPixmap(*greenPXM);
+}
+
+
+void mainWindow::slotSendBSR()
+{
+  txWidgetPtr->sendBSR();
+}
+
+
+void mainWindow::slotSendID()
+{
+  txWidgetPtr->sendID();
+}
+
+
+
+void mainWindow::slotSendWfText()
+{
+ txWidgetPtr->sendWfText();
+}
+
+void mainWindow::slotDocumentation()
+{
+    QDesktopServices::openUrl(docURL);
+
+}
+
+void mainWindow::setBSRPushButton(bool b)
+{
+  bsrPushButton->setEnabled(b);
+}
+
+#ifndef QT_NO_DEBUG
+void mainWindow::slotShowDataScope()
+{
+  scopeViewerData->show(true,true,true,true);
+}
+void mainWindow::slotShowSyncScope()
+{
+  scopeViewerSync->show(true,true,true,true);
+}
+
+void mainWindow::slotScopeOffset()
+{
+  dataScopeOffset=rxWidgetPtr->functionsPtr()->setOffset(dataScopeOffset,true);
+}
+
+
+
+#endif
diff --git a/qsstv/mainwindow.h b/qsstv/mainwindow.h
new file mode 100644
index 0000000..f862035
--- /dev/null
+++ b/qsstv/mainwindow.h
@@ -0,0 +1,59 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QComboBox>
+#include <QPushButton>
+#include <QLabel>
+
+namespace Ui {
+  class MainWindow;
+  }
+
+class mainWindow : public QMainWindow
+{
+  Q_OBJECT
+  
+public:
+  explicit mainWindow(QWidget *parent = 0);
+  ~mainWindow();
+  void init();
+  void setNewFont();
+  void setPTT(bool p);
+  void setBSRPushButton(bool b);
+  void startReceiving();
+
+private slots:
+  void slotConfigure();
+  void slotExit();
+  void slotResetLog();
+  void slotLogSettings();
+  void slotCalibrate();
+  void slotAboutQt();
+  void slotAboutQSSTV();
+  void slotDocumentation();
+  void slotSendID();
+  void slotSendBSR();
+  void slotSendWfText();
+#ifndef QT_NO_DEBUG
+  void slotShowDataScope();
+  void slotShowSyncScope();
+  void slotScopeOffset();
+
+#endif
+private:
+  Ui::MainWindow *ui;
+  void closeEvent ( QCloseEvent *e );
+  void readSettings();
+  void writeSettings();
+  QComboBox *transmissionModeComboBox;
+  QPushButton *wfTextPushButton;
+  QPushButton *fixPushButton;
+  QPushButton *bsrPushButton;
+  QPushButton *idPushButton;
+  QLabel pttText;
+  QLabel *pttIcon;
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/qsstv/mainwindow.ui b/qsstv/mainwindow.ui
new file mode 100644
index 0000000..5c2b14b
--- /dev/null
+++ b/qsstv/mainwindow.ui
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>831</width>
+    <height>566</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>8</pointsize>
+    <weight>50</weight>
+    <italic>false</italic>
+    <bold>false</bold>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <property name="tabShape">
+   <enum>QTabWidget::Rounded</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <property name="spacing">
+     <number>1</number>
+    </property>
+    <property name="margin">
+     <number>1</number>
+    </property>
+    <item>
+     <widget class="QTabWidget" name="maintabWidget">
+      <property name="font">
+       <font>
+        <weight>50</weight>
+        <italic>false</italic>
+        <bold>false</bold>
+       </font>
+      </property>
+      <property name="tabShape">
+       <enum>QTabWidget::Rounded</enum>
+      </property>
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="rxTab">
+       <property name="font">
+        <font>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <attribute name="title">
+        <string>Receive</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <property name="spacing">
+         <number>1</number>
+        </property>
+        <property name="margin">
+         <number>1</number>
+        </property>
+        <item>
+         <widget class="rxWidget" name="rxWindow" native="true"/>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="txTab">
+       <attribute name="title">
+        <string>Transmit</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_3">
+        <property name="spacing">
+         <number>1</number>
+        </property>
+        <property name="margin">
+         <number>1</number>
+        </property>
+        <item>
+         <widget class="txWidget" name="txWindow" native="true"/>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="galleryTab">
+       <attribute name="title">
+        <string>Gallery</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_4">
+        <property name="spacing">
+         <number>1</number>
+        </property>
+        <property name="margin">
+         <number>1</number>
+        </property>
+        <item>
+         <widget class="galleryWidget" name="galleryWindow" native="true"/>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>831</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <weight>50</weight>
+     <bold>false</bold>
+    </font>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionExit"/>
+   </widget>
+   <widget class="QMenu" name="menuOptions">
+    <property name="title">
+     <string>Options</string>
+    </property>
+    <addaction name="actionConfigure"/>
+    <addaction name="actionTest"/>
+    <addaction name="actionCalibrate"/>
+    <addaction name="actionShowDataScope"/>
+    <addaction name="actionShowSyncScope"/>
+    <addaction name="actionLogSettings"/>
+    <addaction name="actionResetLog"/>
+    <addaction name="actionScopeOffset"/>
+   </widget>
+   <widget class="QMenu" name="menuHelp">
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="actionAboutQSSTV"/>
+    <addaction name="actionAboutQt"/>
+    <addaction name="actionUsersGuide"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuOptions"/>
+   <addaction name="menuHelp"/>
+  </widget>
+  <widget class="QStatusBar" name="statusBar">
+   <property name="minimumSize">
+    <size>
+     <width>0</width>
+     <height>0</height>
+    </size>
+   </property>
+   <property name="maximumSize">
+    <size>
+     <width>16777215</width>
+     <height>16777215</height>
+    </size>
+   </property>
+  </widget>
+  <action name="actionExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+  </action>
+  <action name="actionConfigure">
+   <property name="text">
+    <string>Configuration</string>
+   </property>
+  </action>
+  <action name="actionTest">
+   <property name="text">
+    <string>Test</string>
+   </property>
+  </action>
+  <action name="actionCalibrate">
+   <property name="icon">
+    <iconset resource="qsstv.qrc">
+     <normaloff>:/icons/edit.png</normaloff>:/icons/edit.png</iconset>
+   </property>
+   <property name="text">
+    <string>Calibrate</string>
+   </property>
+  </action>
+  <action name="actionShowDataScope">
+   <property name="text">
+    <string>Show Data Scope</string>
+   </property>
+  </action>
+  <action name="actionLogSettings">
+   <property name="text">
+    <string>Log Settings</string>
+   </property>
+  </action>
+  <action name="actionResetLog">
+   <property name="text">
+    <string>Reset Log</string>
+   </property>
+  </action>
+  <action name="actionAboutQSSTV">
+   <property name="text">
+    <string>About QSSTV</string>
+   </property>
+  </action>
+  <action name="actionAboutQt">
+   <property name="text">
+    <string>About Qt</string>
+   </property>
+  </action>
+  <action name="actionUsersGuide">
+   <property name="text">
+    <string>Users Guide</string>
+   </property>
+  </action>
+  <action name="actionShowSyncScope">
+   <property name="text">
+    <string>Show Sync Scope</string>
+   </property>
+  </action>
+  <action name="actionScopeOffset">
+   <property name="text">
+    <string>Scope Offset</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>rxWidget</class>
+   <extends>QWidget</extends>
+   <header>rxwidget.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>txWidget</class>
+   <extends>QWidget</extends>
+   <header>txwidget.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>galleryWidget</class>
+   <extends>QWidget</extends>
+   <header>gallerywidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="qsstv.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/qsstv/qsstv.pro b/qsstv/qsstv.pro
new file mode 100644
index 0000000..f36c370
--- /dev/null
+++ b/qsstv/qsstv.pro
@@ -0,0 +1,439 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2013-01-21T00:18:40
+#
+#-------------------------------------------------
+
+QT       += core gui xml network
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = qsstv
+TEMPLATE = app
+
+
+
+QMAKE_CXXFLAGS_DEBUG += -g3 -O0
+INCLUDEPATH += src widgets ../qwt
+
+
+SOURCES += \
+    drmrx/viterbi_decode.cpp \
+    drmrx/sourcedecoder.cpp \
+    drmrx/nrutil.cpp \
+    drmrx/newfft.cpp \
+    drmrx/msdhardmsc.cpp \
+    drmrx/msdhardfac.cpp \
+    drmrx/mkmscmap.cpp \
+    drmrx/mkfacmap.cpp \
+    drmrx/ludcmp.cpp \
+    drmrx/lubksb.cpp \
+    drmrx/getsymbolidx.cpp \
+    drmrx/getofdmsync.cpp \
+    drmrx/getofdm.cpp \
+    drmrx/getmode.cpp \
+    drmrx/getfoffsint.cpp \
+    drmrx/filter1c.cpp \
+    drmrx/filter1.cpp \
+    drmrx/drmstatusframe.cpp \
+    drmrx/drmdatainput.cpp \
+    drmrx/drmconstellationframe.cpp \
+    drmrx/drm.cpp \
+    drmrx/demodulator.cpp \
+    drmrx/deinterleaver.cpp \
+    drmrx/crc16_bytewise.cpp \
+    drmrx/crc8_c.cpp \
+    drmrx/channeldecode.cpp \
+    drmrx/bits2bytes.cpp \
+    drmtx/drmtransmitter.cpp \
+    drmtx/common/Parameter.cpp \
+    drmtx/common/OFDM.cpp \
+    drmtx/common/DrmTransmitter.cpp \
+    drmtx/common/DRMSignalIO.cpp \
+    drmtx/common/DataIO.cpp \
+    drmtx/common/csoundout.cpp \
+    drmtx/common/datadecoding/MOTSlideShow.cpp \
+    drmtx/common/datadecoding/DataDecoder.cpp \
+    drmtx/common/datadecoding/DABMOT.cpp \
+    drmtx/common/FAC/FAC.cpp \
+    drmtx/common/interleaver/SymbolInterleaver.cpp \
+    drmtx/common/interleaver/BlockInterleaver.cpp \
+    drmtx/common/matlib/MatlibStdToolbox.cpp \
+    drmtx/common/matlib/MatlibSigProToolbox.cpp \
+    drmtx/common/mlc/QAMMapping.cpp \
+    drmtx/common/mlc/MLC.cpp \
+    drmtx/common/mlc/EnergyDispersal.cpp \
+    drmtx/common/mlc/ConvEncoder.cpp \
+    drmtx/common/mlc/ChannelCode.cpp \
+    drmtx/common/mlc/BitInterleaver.cpp \
+    drmtx/common/ofdmcellmapping/OFDMCellMapping.cpp \
+    drmtx/common/ofdmcellmapping/CellMappingTable.cpp \
+    drmtx/common/SDC/SDCTransmit.cpp \
+    drmtx/common/sourcedecoders/AudioSourceDecoder.cpp \
+    drmtx/common/tables/TableFAC.cpp \
+    drmtx/common/util/Utilities.cpp \
+    drmtx/common/util/CRC.cpp \
+    dsp/synthes.cpp \
+    dsp/filterparam.cpp \
+    dsp/filter.cpp \
+    dsp/downsamplefilter.cpp \
+    editor/graphicitems.cpp \
+    editor/gradientdialog.cpp \
+    editor/editorview.cpp \
+    editor/editorscene.cpp \
+    editor/editor.cpp \
+    rig/rigcontrol.cpp \
+		sound/wavio.cpp \
+    sound/waterfalltext.cpp \
+    sound/soundio.cpp \
+    sound/soundcontrol.cpp \
+    sound/resampler.cpp \
+    sound/resamplefilter.cpp \
+    sound/calibration.cpp \
+    sstv/modes/moderobot2.cpp \
+    sstv/modes/moderobot1.cpp \
+    sstv/modes/modergb.cpp \
+    sstv/modes/modepd.cpp \
+    sstv/modes/modegbr2.cpp \
+    sstv/modes/modegbr.cpp \
+    sstv/modes/modebw.cpp \
+    sstv/modes/modebase.cpp \
+    sstv/modes/modeavt.cpp \
+    sstv/syncprocessor.cpp \
+    sstv/sstvparam.cpp \
+    sstv/cw.cpp \
+    utils/supportfunctions.cpp \
+    utils/rs.cpp \
+    utils/reedsolomoncoder.cpp \
+    utils/loggingparams.cpp \
+    utils/logging.cpp \
+    utils/ftp.cpp \
+    videocapt/videocapture.cpp \
+    widgets/waterfallform.cpp \
+    widgets/vumeter.cpp \
+    widgets/spectrumwidget.cpp \
+    widgets/qscale.cpp \
+    widgets/imageviewer.cpp \
+    widgets/fftdisplay.cpp \
+    widgets/cameracontrol.cpp \
+    widgets/blockview.cpp \
+    txwidget.cpp \
+    txfunctions.cpp \
+    rxwidget.cpp \
+    rxfunctions.cpp \
+    qsstvglobal.cpp \
+    mainwindow.cpp \
+    main.cpp \
+    gallerywidget.cpp \
+    dispatcher.cpp \
+    configparams.cpp \
+    configdialog.cpp \
+    drmrx/psdmean.cpp \
+    drmrx/psdcmean.cpp \
+    drmrx/drmpsdframe.cpp \
+    drmtx/bsrform.cpp \
+    drmrx/fixform.cpp \
+    rig/rigcontrolform.cpp \
+    utils/qurlinfo.cpp \
+    utils/qftp.cpp \
+    utils/qjp2io.cpp \
+    widgets/textdisplay.cpp \
+    rig/freqdisplay.cpp \
+    widgets/markerwidget.cpp \
+    utils/hybridcrypt.cpp \
+    utils/macroexpansion.cpp \
+    xmlrpc/maiaXmlRpcClient.cpp \
+    xmlrpc/maiaFault.cpp \
+    xmlrpc/maiaXmlRpcServer.cpp \
+    xmlrpc/maiaObject.cpp \
+    xmlrpc/maiaXmlRpcServerConnection.cpp \
+    xmlrpc/xmlinterface.cpp \
+    drmprofileform.cpp \
+    xmlrpc/ipcmessage.cpp \
+    logbook/logbook.cpp
+
+HEADERS  += \
+    drmrx/viterbi_decode.h \
+    drmrx/structtemplates.h \
+    drmrx/sourcedecoder.h \
+    drmrx/resamplefilter.h \
+    drmrx/nrutil.h \
+    drmrx/msd_hard_sdc.h \
+    drmrx/msd_hard.h \
+    drmrx/drmstatusframe.h \
+    drmrx/drmproto.h \
+    drmrx/drmdefs.h \
+    drmrx/drmdatainput.h \
+    drmrx/drmconstellationframe.h \
+    drmrx/drm.h \
+    drmrx/demodulator.h \
+    drmtx/common/datadecoding/MOTSlideShow.h \
+    drmtx/common/datadecoding/DataDecoder.h \
+    drmtx/common/datadecoding/DABMOT.h \
+    drmtx/drmtransmitter.h \
+    drmtx/config.h \
+    drmtx/common/soundinterface.h \
+    drmtx/common/Parameter.h \
+    drmtx/common/OFDM.h \
+    drmtx/common/GlobalDefinitions.h \
+    drmtx/common/DrmTransmitter.h \
+    drmtx/common/DRMSignalIO.h \
+    drmtx/common/DataIO.h \
+    drmtx/common/csoundout.h \
+    drmtx/common/FAC/FAC.h \
+    drmtx/common/interleaver/SymbolInterleaver.h \
+    drmtx/common/interleaver/BlockInterleaver.h \
+    drmtx/common/matlib/MatlibStdToolbox.h \
+    drmtx/common/matlib/MatlibSigProToolbox.h \
+    drmtx/common/matlib/Matlib.h \
+    drmtx/common/mlc/QAMMapping.h \
+    drmtx/common/mlc/MLC.h \
+    drmtx/common/mlc/EnergyDispersal.h \
+    drmtx/common/mlc/ConvEncoder.h \
+    drmtx/common/mlc/ChannelCode.h \
+    drmtx/common/mlc/BitInterleaver.h \
+    drmtx/common/ofdmcellmapping/OFDMCellMapping.h \
+    drmtx/common/ofdmcellmapping/CellMappingTable.h \
+    drmtx/common/SDC/SDC.h \
+    drmtx/common/sourcedecoders/AudioSourceDecoder.h \
+    drmtx/common/tables/TableQAMMapping.h \
+    drmtx/common/tables/TableMLC.h \
+    drmtx/common/tables/TableFAC.h \
+    drmtx/common/tables/TableDRMGlobal.h \
+    drmtx/common/tables/TableCarrier.h \
+    drmtx/common/tables/TableCarMap.h \
+    drmtx/common/tables/TableAMSS.h \
+    drmtx/common/util/Utilities.h \
+    drmtx/common/util/Modul.h \
+    drmtx/common/util/CRC.h \
+    drmtx/common/util/Buffer.h \
+    dsp/synthes.h \
+    dsp/nco.h \
+    dsp/filterparam.h \
+    dsp/filter.h \
+    dsp/downsamplefilter.h \
+    editor/qdialog_p.h \
+    editor/graphicitems.h \
+    editor/gradientdialog.h \
+    editor/editorview.h \
+    editor/editorscene.h \
+    editor/editor.h \
+    rig/rigcontrol.h \
+    sound/wavio.h \
+    sound/waterfalltext.h \
+    sound/soundio.h \
+    sound/soundcontrol.h \
+    sound/resampler.h \
+    sound/resampler.cpp_new \
+    sound/resamplefilter.h \
+    sound/calibration.h \
+    sstv/modes/modes.h \
+    sstv/modes/moderobot2.h \
+    sstv/modes/moderobot1.h \
+    sstv/modes/modergb.h \
+    sstv/modes/modepd.h \
+    sstv/modes/modegbr2.h \
+    sstv/modes/modegbr.h \
+    sstv/modes/modebw.h \
+    sstv/modes/modebase.h \
+    sstv/modes/modeavt.h \
+    sstv/syncprocessor.h \
+    sstv/sstvparam.h \
+    sstv/cw.h \
+    utils/vector.h \
+    utils/supportfunctions.h \
+    utils/rs.h \
+    utils/reedsolomoncoder.h \
+    utils/loggingparams.h \
+    utils/logging.h \
+    utils/ftp.h \
+    utils/buffermanag.h \
+    videocapt/videocapture.h \
+    widgets/waterfallform.h \
+    widgets/vumeter.h \
+    widgets/spectrumwidget.h \
+    widgets/qscale.h \
+    widgets/imageviewer.h \
+    widgets/fftdisplay.h \
+    widgets/cameracontrol.h \
+    widgets/blockview.h \
+    txwidget.h \
+    txfunctions.h \
+    rxwidget.h \
+    rxfunctions.h \
+    qsstvglobal.h \
+    qsstvdefs.h \
+    mainwindow.h \
+    gallerywidget.h \
+    dispatchevents.h \
+    dispatcher.h \
+    configparams.h \
+    configdialog.h \
+    drmrx/drmpsdframe.h \
+    drmtx/bsrform.h \
+    drmrx/fixform.h \
+    rig/rigcontrolform.h \
+    drmrx/mkmap.h \
+    utils/qurlinfo.h \
+    utils/qftp.h \
+    utils/qjp2io.h \
+    widgets/textdisplay.h \
+    rig/freqdisplay.h \
+    rig/rigparams.h \
+    widgets/markerwidget.h \
+    utils/hybridcrypt.h \
+    utils/macroexpansion.h \
+    xmlrpc/maiaFault.h \
+    xmlrpc/maiaXmlRpcServer.h \
+    xmlrpc/maiaXmlRpcClient.h \
+    xmlrpc/maiaObject.h \
+    xmlrpc/maiaXmlRpcServerConnection.h \
+    xmlrpc/xmlinterface.h \
+    drmprofileform.h \
+    xmlrpc/ipcmessage.h \
+    logbook/logbook.h
+
+FORMS    += \
+    drmrx/drmstatusframe.ui \
+    drmrx/drmpsdframe.ui \
+    drmrx/drmconstellationframe.ui \
+    editor/textform.ui \
+    editor/gradientform.ui \
+    editor/editorform.ui \
+    sound/soundcontrol.ui \
+    sound/calibrationform.ui \
+    sound/calibration.ui \
+    utils/loggingform.ui \
+    widgets/waterfallform.ui \
+    widgets/sweepform.ui \
+    widgets/spectrumwidget.ui \
+    widgets/freqform.ui \
+    widgets/cameracontrol.ui \
+    widgets/blockview.ui \
+    txwidget.ui \
+    rxwidget.ui \
+    mainwindow.ui \
+    gallerywidget.ui \
+    configform.ui \
+    drmtx/bsrform.ui \
+    drmrx/fixform.ui \
+    rig/rigcontrolform.ui \
+    widgets/textdisplay.ui \
+    rig/freqdisplay.ui \
+    drmprofileform.ui
+
+RESOURCES += \
+    qsstv.qrc
+
+OTHER_FILES += \
+    icons/transparency.png \
+    icons/tone.png \
+    icons/text.png \
+    icons/sweep.png \
+    icons/stop.png \
+    icons/start.png \
+    icons/replay.png \
+    icons/qsstvsplash.png \
+    icons/qsstv.png \
+    icons/line.png \
+    icons/image.png \
+    icons/gradient.png \
+    icons/frect.png \
+    icons/filesave.png \
+    icons/fileopen.png \
+    icons/filenew.png \
+    icons/fcircle.png \
+    icons/eraser.png \
+    icons/edit.png \
+    icons/doubletone.png \
+    icons/colorselector.png \
+    icons/colorpicker.png \
+    icons/colorline.png \
+    icons/colorfill.png \
+    icons/camera.png \
+    icons/arrow.png \
+    Documentation/manual/qsstv.css \
+    Documentation/manual/manual.txt \
+    Documentation/manual/manual.doxy \
+    Doxyfile \
+    COPYING \
+    icons/mgc.raw \
+    Documentation/manual/images/Gallery_rx.png \
+    Documentation/manual/images/tx-with-template.png \
+    Documentation/manual/images/Gallery_tx.png \
+    Documentation/manual/images/receivesstv.png \
+    Documentation/manual/images/transmitsstv.png \
+    Documentation/manual/images/waterfall.png \
+    Documentation/manual/images/receivedrm.png \
+    Documentation/manual/images/transmitdrm.png \
+    Documentation/manual/images/editor_3.png \
+    Documentation/manual/images/Gallery_template.png \
+    Documentation/manual/images/config4.png \
+    Documentation/manual/images/fix.png \
+    Documentation/manual/images/config2.png \
+    Documentation/manual/images/config3.png \
+    Documentation/manual/images/config6.png \
+    Documentation/manual/images/editor_2.png \
+    Documentation/manual/images/config7.png \
+    Documentation/manual/images/config10.png \
+    Documentation/manual/images/config1.png \
+    Documentation/manual/images/bsr_select.png \
+    Documentation/manual/images/config8.png \
+    Documentation/manual/images/config9.png \
+    Documentation/manual/images/editor_1.png \
+    Documentation/manual/images/config5.png \
+    Documentation/manual/images/wftextpopup.png \
+    Documentation/manual/images/config.png \
+    Documentation/manual/images/calibration.png \
+    Documentation/manual/images/bsr_nfy.png \
+    Documentation/manual/images/statusbar.png \
+    Documentation/manual/images/statusleds.png \
+    Documentation/manual/images/hybrid_checkbox.png \
+    Documentation/manual/images/hybrid_dis_checkbox.png \
+    Documentation/manual/images/config11.png \
+    Documentation/manual/images/flrig1.png \
+    Documentation/manual/images/rxdrm_constellation.png \
+    Documentation/manual/images/rxdrm_status.png \
+    Documentation/manual/images/rxdrm_segments.png \
+    Documentation/manual/images/txdrm_compression.png \
+    Documentation/manual/images/txdrm_options.png \
+    Documentation/manual/images/txdrm_status.png \
+    Documentation/manual/images/cqrlog1.png \
+    Documentation/manual/images/cqrlog2.png
+
+
+ LIBS += -lasound \
+	-lhamlib \
+	 -lfftw3f \
+	-lfftw3 \
+ -ljasper
+
+CONFIG(debug ,debug|release){
+	message(added debugging)
+SOURCES +=     scope/scopeoffset.cpp \
+		scope/scopeview.cpp \
+		scope/scopeplot.cpp
+HEADERS  += scope/scopeoffset.h \
+		scope/scopeview.h \
+		scope/scopeplot.h
+
+FORMS   += scope/scopeoffset.ui \
+				scope/plotform.ui
+
+ INCLUDEPATH += /usr/include/qwt
+ LIBS += ../qwt/libqwt.a
+}
+
+CONFIG(debug ,debug|release){
+dox.commands = cd $$PWD/Documentation/manual ;doxygen  manual.doxy;
+dox.depends= FORCE
+PRE_TARGETDEPS       +=    dox
+message(dox will be generated)
+}
+dox.path=/usr/share/doc/$$TARGET
+dox.files= $$PWD/manual/*
+QMAKE_EXTRA_TARGETS   +=   dox
+
+target.path = /usr/bin
+INSTALLS += target dox
+
diff --git a/qsstv/qsstv.qrc b/qsstv/qsstv.qrc
new file mode 100644
index 0000000..b35049b
--- /dev/null
+++ b/qsstv/qsstv.qrc
@@ -0,0 +1,31 @@
+<RCC>
+    <qresource prefix="/">
+        <file>icons/fileopen.png</file>
+        <file>icons/filesave.png</file>
+        <file>icons/text.png</file>
+        <file>icons/image.png</file>
+        <file>icons/fcircle.png</file>
+        <file>icons/frect.png</file>
+        <file>icons/arrow.png</file>
+        <file>icons/colorpicker.png</file>
+        <file>icons/replay.png</file>
+        <file>icons/qsstv.png</file>
+        <file>icons/transparency.png</file>
+        <file>icons/start.png</file>
+        <file>icons/camera.png</file>
+        <file>icons/tone.png</file>
+        <file>icons/stop.png</file>
+        <file>icons/edit.png</file>
+        <file>icons/doubletone.png</file>
+        <file>icons/sweep.png</file>
+        <file>icons/qsstvsplash.png</file>
+        <file>icons/colorfill.png</file>
+        <file>icons/colorselector.png</file>
+        <file>icons/eraser.png</file>
+        <file>icons/filenew.png</file>
+        <file>icons/gradient.png</file>
+        <file>icons/mgc.raw</file>
+        <file>icons/mgc2.raw</file>
+        <file>icons/colorline.png</file>
+    </qresource>
+</RCC>
diff --git a/qsstv/qsstvdefs.h b/qsstv/qsstvdefs.h
new file mode 100644
index 0000000..c93b293
--- /dev/null
+++ b/qsstv/qsstvdefs.h
@@ -0,0 +1,37 @@
+#include <stdint.h>
+#include <complex>
+using namespace std; /* Because of the library: "complex" */
+#ifndef QSSTVDEFS_H
+#define QSSTVDEFS_H
+
+#define RXSTRIPE 1024
+#define TXSTRIPE 1024
+#define SUBSAMPLINGRATIO 4
+
+#define SAMPLINGSTRIPE (SUBSAMPLINGRATIO*RXSTRIPE) // 4 times oversampled
+
+#define MAXNUMCHANNELS 2
+#define SOUNDFRAME  quint32
+#define BASESAMPLERATE 48000.
+#define SAMPLERATE (BASESAMPLERATE/SUBSAMPLINGRATIO)
+
+
+//typedef double DSPFLOAT;
+typedef float DSPFLOAT;
+typedef unsigned char byte;
+enum etransmissionMode {SSTV,DRM,NOMODE};
+
+/* Define the application specific data-types ------------------------------- */
+typedef	double							_REAL;
+typedef	complex<_REAL>			_COMPLEX;
+typedef short						  	_SAMPLE;
+typedef unsigned char				_BYTE;
+typedef bool							  _BOOLEAN;
+typedef unsigned char 			_BINARY;
+
+#ifndef false
+#define false false
+#define true true
+#endif
+
+#endif // QSSTVDEFS_H
diff --git a/qsstv/qsstvglobal.cpp b/qsstv/qsstvglobal.cpp
new file mode 100644
index 0000000..dc384e2
--- /dev/null
+++ b/qsstv/qsstvglobal.cpp
@@ -0,0 +1,95 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#include "qsstvglobal.h"
+
+
+#include <QString>
+#include <QPixmap>
+
+
+
+
+const QString MAJORVERSION  = "8.2";
+const QString CONFIGVERSION = "8.1";
+const QString MINORVERSION  = ".12";
+const QString LOGVERSION = ("qsstv."+MAJORVERSION+MINORVERSION+".log");
+const QString ORGANIZATION = "ON4QZ";
+const QString APPLICATION  = ("qsstv" +CONFIGVERSION);
+const QString qsstvVersion=QString("QSSTV " + MAJORVERSION+MINORVERSION);
+const QString shortText=QString("HAMDRM Software based on RX/TXAMADRM\n from PA0MBO")
+
+;
+QByteArray *debugPtr;
+QStatusBar *statusBarPtr;
+configDialog *confDiag;
+#ifndef QT_NO_DEBUG
+scopeView *scopeViewerData;
+scopeView *scopeViewerSync;
+#endif
+
+mainWindow *mainWindowPtr;
+dispatcher *dispatcherPtr;
+waterfallText *waterfallPtr;
+QSplashScreen *splashPtr;
+QString splashStr;
+
+
+
+QCursor *cpCursor;
+logFile *logfile;
+
+
+// for DRM
+emscStatus msc_valid;
+int bodyTotalSegments;
+int rxSegments;
+int currentSegmentNumber;
+unsigned int rxTransportID;
+//unsigned int bitsPerSecond;
+QList<short unsigned int> drmBlockList;
+sourceDecoder srcDecoder;
+uint txTransportID;
+QList<short unsigned int> fixBlockList;
+bool stopDRM;
+float avgSNR;
+float lastAvgSNR;
+QPixmap *greenPXM;
+QPixmap *redPXM;
+
+logBook *logBookPtr;
+xmlInterface *xmlIntfPtr;
+
+
+void globalInit(void)
+{
+  logfile=new logFile;
+  logfile->open(LOGVERSION);
+  QPixmap pm(":/icons/colorpicker.png");
+  cpCursor=new QCursor(pm,0,pm.height()-1);
+
+}
+
+void globalEnd(void)
+{
+  logfile->close();
+}
+
diff --git a/qsstv/qsstvglobal.h b/qsstv/qsstvglobal.h
new file mode 100644
index 0000000..1c55a49
--- /dev/null
+++ b/qsstv/qsstvglobal.h
@@ -0,0 +1,88 @@
+#ifndef SSTVGLOBAL_H
+#define SSTVGLOBAL_H
+#include <QString>
+#include <QCursor>
+#include "utils/logging.h"
+#include "drmrx/sourcedecoder.h"
+#include "qsstvdefs.h"
+
+
+
+
+class QSplashScreen;
+class QStatusBar;
+class logFile;
+class galleryWidget ;
+class configDialog;
+class dispatcher;
+class rxWidget;
+class txWidget;
+class imageViewer;
+class mainWindow;
+class waterfallText;
+class logBook;
+class xmlInterface;
+
+class synthesizer;
+
+extern const QString MAJORVERSION;
+extern const QString CONFIGVERSION;
+extern const QString MINORVERSION;
+extern const QString ORGANIZATION;
+extern const QString APPLICATION;
+extern const QString qsstvVersion;
+extern const QString shortText;
+#define MAGICNUMBER   (('4'<<24)+('Q'<<16)+('Z'<<8)+'S')
+
+extern QSplashScreen    *splashPtr;
+extern configDialog     *confDiag;
+extern mainWindow *mainWindowPtr;
+extern waterfallText *waterfallPtr;
+extern QString splashStr;
+
+
+extern txWidget *txWidgetPtr;
+extern galleryWidget *galleryWidgetPtr;
+
+extern synthesizer *synthesPtr;
+extern dispatcher *dispatcherPtr;
+extern QStatusBar *statusBarPtr;
+
+// for DRM
+enum emscStatus {INVALID,VALID,ALREADYRECEIVED};
+extern emscStatus msc_valid;
+extern int bodyTotalSegments;
+extern int rxSegments;
+extern int currentSegmentNumber;
+extern unsigned int rxTransportID;
+extern unsigned int bitsPerSecond;
+extern bool stopDRM;
+extern QList<short unsigned int> drmBlockList;
+extern uint txTransportID;
+extern QList<short unsigned int> fixBlockList;
+extern float avgSNR;
+extern float lastAvgSNR;
+
+extern QCursor *cpCursor;
+void globalInit(void);
+void globalEnd(void);
+
+extern logFile *logfile;
+extern logBook *logBookPtr;
+extern xmlInterface *xmlIntfPtr;
+
+#ifndef QT_NO_DEBUG
+class scopeView;
+extern scopeView *scopeViewerData;
+extern scopeView *scopeViewerSync;
+#endif
+
+extern QByteArray *debugPtr;
+extern sourceDecoder srcDecoder;
+
+extern QPixmap *greenPXM;
+extern QPixmap *redPXM;
+
+
+
+#endif
diff --git a/qsstv/rig/freqdisplay.cpp b/qsstv/rig/freqdisplay.cpp
new file mode 100644
index 0000000..f7d75cb
--- /dev/null
+++ b/qsstv/rig/freqdisplay.cpp
@@ -0,0 +1,19 @@
+#include "freqdisplay.h"
+#include "ui_freqdisplay.h"
+
+freqDisplay::freqDisplay(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::freqDisplay)
+{
+  ui->setupUi(this);
+}
+
+freqDisplay::~freqDisplay()
+{
+  delete ui;
+}
+
+void freqDisplay::display(double freq)
+{
+  ui->frequencyLCD->display(QString("%1").arg(freq/1000,8,'f',3));
+}
diff --git a/qsstv/rig/freqdisplay.h b/qsstv/rig/freqdisplay.h
new file mode 100644
index 0000000..fa5bc71
--- /dev/null
+++ b/qsstv/rig/freqdisplay.h
@@ -0,0 +1,23 @@
+#ifndef FREQDISPLAY_H
+#define FREQDISPLAY_H
+
+#include <QWidget>
+
+namespace Ui {
+  class freqDisplay;
+  }
+
+class freqDisplay : public QWidget
+{
+  Q_OBJECT
+  
+public:
+  explicit freqDisplay(QWidget *parent = 0);
+  ~freqDisplay();
+  void display(double freq);
+  
+private:
+  Ui::freqDisplay *ui;
+};
+
+#endif // FREQDISPLAY_H
diff --git a/qsstv/rig/freqdisplay.ui b/qsstv/rig/freqdisplay.ui
new file mode 100644
index 0000000..bae6f81
--- /dev/null
+++ b/qsstv/rig/freqdisplay.ui
@@ -0,0 +1,372 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>freqDisplay</class>
+ <widget class="QWidget" name="freqDisplay">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>214</width>
+    <height>52</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="margin">
+    <number>1</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="KHzLabel">
+     <property name="font">
+      <font>
+       <weight>75</weight>
+       <bold>true</bold>
+      </font>
+     </property>
+     <property name="text">
+      <string>KHz</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLCDNumber" name="frequencyLCD">
+     <property name="minimumSize">
+      <size>
+       <width>180</width>
+       <height>50</height>
+      </size>
+     </property>
+     <property name="palette">
+      <palette>
+       <active>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>85</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>85</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </active>
+       <inactive>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>85</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>85</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </inactive>
+       <disabled>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>170</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>85</red>
+           <green>255</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>85</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </disabled>
+      </palette>
+     </property>
+     <property name="autoFillBackground">
+      <bool>true</bool>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::Box</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="numDigits">
+      <number>10</number>
+     </property>
+     <property name="segmentStyle">
+      <enum>QLCDNumber::Filled</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/rig/rigcontrol.cpp b/qsstv/rig/rigcontrol.cpp
new file mode 100644
index 0000000..954bc0f
--- /dev/null
+++ b/qsstv/rig/rigcontrol.cpp
@@ -0,0 +1,369 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "rigcontrol.h"
+#include "qsstvglobal.h"
+#include "rigparams.h"
+#include <QMessageBox>
+#include <QSplashScreen>
+#include <QApplication>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "mainwindow.h"
+#include <QMessageBox>
+#include "txwidget.h"
+
+
+#define MAXCONFLEN 128
+
+rigControl *rigControllerR1;
+rigControl *rigControllerR2;
+rigControl *rigController;
+
+QList<const rig_caps *> capsList;
+bool radiolistLoaded=false;
+
+
+
+
+int collect(const rig_caps *caps,rig_ptr_t)
+{
+  capsList.append(caps);
+  return 1;
+}
+
+rigControl::rigControl(int radioIndex)
+{
+  rigControlEnabled=false;
+  catParams.configLabel=QString("radio%1").arg(radioIndex);
+  rig_set_debug(RIG_DEBUG_NONE);
+  getRadioList();
+  serialP=0;
+  xmlIntfPtr=new xmlInterface;
+}
+
+rigControl::~rigControl()
+{
+  rig_close(my_rig); /* close port */
+  rig_cleanup(my_rig); /* if you care about memory */
+}
+
+bool rigControl::init()
+{
+  int retcode;
+  if(!catParams.enableCAT) return false;
+  myport.type.rig = RIG_PORT_SERIAL;
+  myport.parm.serial.rate = catParams.baudrate;
+  myport.parm.serial.data_bits =catParams.databits;
+  myport.parm.serial.stop_bits =catParams.stopbits;
+  if(catParams.parity=="Even") myport.parm.serial.parity = RIG_PARITY_EVEN;
+  else if (catParams.parity=="Odd") myport.parm.serial.parity = RIG_PARITY_ODD;
+  else  myport.parm.serial.parity = RIG_PARITY_NONE;
+  if(catParams.handshake=="XOn/Xoff")
+    {
+      myport.parm.serial.handshake = RIG_HANDSHAKE_XONXOFF;
+    }
+  if(catParams.handshake=="Hardware")
+    {
+      myport.parm.serial.handshake = RIG_HANDSHAKE_HARDWARE;
+    }
+
+  else
+    {
+      myport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
+    }
+  strncpy(myport.pathname, (const char *)catParams.serialPort.toLatin1().data(), FILPATHLEN);
+  //  myport.parm.serial.rts_state=RIG_SIGNAL_OFF;
+  //  myport.parm.serial.dtr_state=RIG_SIGNAL_OFF;
+  addToLog(QString("rigcontrol:init myport pathname: %1").arg(myport.pathname),LOGRIGCTRL);
+  catParams.radioModelNumber=getModelNumber(getRadioModelIndex());
+  my_rig = rig_init(catParams.radioModelNumber);
+  if(!my_rig)
+    {
+      addToLog(QString("Error in connection using radio model %1").arg(catParams.radioModel),LOGRIGCTRL);
+      initError=QString("Error in connection using radio model %1").arg(catParams.radioModel);
+      return false;
+    }
+
+  if(QString(my_rig->caps->mfg_name)=="Icom")
+    {
+      if(!catParams.civAddress.isEmpty())
+        {
+          rig_set_conf(my_rig, rig_token_lookup(my_rig, "civaddr"), catParams.civAddress.toLatin1());
+        }
+    }
+
+  strncpy(my_rig->state.rigport.pathname,(const char *)catParams.serialPort.toLatin1().data(),FILPATHLEN);
+  strncpy(my_rig->state.pttport.pathname,(const char *)catParams.serialPort.toLatin1().data(),FILPATHLEN);
+  my_rig->state.rigport.parm.serial.rate = catParams.baudrate;
+  my_rig->state.rigport.parm.serial.data_bits=catParams.databits;
+  my_rig->state.rigport.parm.serial.stop_bits=catParams.stopbits;
+  if(catParams.parity=="Even") my_rig->state.rigport.parm.serial.parity= RIG_PARITY_EVEN;
+  else if (catParams.parity=="Odd") my_rig->state.rigport.parm.serial.parity = RIG_PARITY_ODD;
+  else  my_rig->state.rigport.parm.serial.parity = RIG_PARITY_NONE;
+  if(catParams.handshake=="XOn/Xoff") my_rig->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_XONXOFF;
+  if(catParams.handshake=="Hardware") my_rig->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_HARDWARE;
+  else my_rig->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
+
+
+
+  //my_rig->state.rigport.parm.serial.rts_state=RIG_SIGNAL_OFF;
+  //my_rig->state.rigport.parm.serial.dtr_state=RIG_SIGNAL_OFF;
+  my_rig->state.pttport.type.ptt = catParams.pttType;
+
+  addToLog(QString("rigcontrol:init rigport.pathname: %1").arg(my_rig->state.rigport.pathname),LOGRIGCTRL);
+  retcode = rig_open(my_rig);
+  if (retcode != RIG_OK )
+    {
+      addToLog(QString("CAT Error: %1").arg(QString(rigerror(retcode))),LOGRIGCTRL);
+      initError=QString("CAT Error: %1").arg(QString(rigerror(retcode)));
+      return false;
+    }
+  addToLog("rigcontroller successfully opened",LOGRIGCTRL);
+  rigControlEnabled=true;
+  //	int verbose=0;
+
+  //	rig_set_debug(verbose<2 ? RIG_DEBUG_NONE: (rig_debug_level_e)verbose);
+  //rig_debug(RIG_DEBUG_VERBOSE, "rigctl, %s\n", hamlib_version);
+  // test if we can contact the tranceiver
+  double fr;
+  if(!getFrequency(fr))
+    {
+      //        rigControlEnabled=false;
+    }
+  return true;
+}
+
+bool rigControl::getFrequency(double &frequency)
+{
+  int retcode;
+  if(!rigControlEnabled) return false;
+  //  retcode = rig_set_vfo(my_rig, RIG_VFO_A);
+  //  if (retcode != RIG_OK ) {errorMessage(retcode,"setVFO"); return false; }
+  retcode = rig_get_freq(my_rig, RIG_VFO_CURR, &frequency);
+  if (retcode != RIG_OK ) {errorMessage(retcode,"getFrequency"); return false; }
+  return true;
+}
+
+bool rigControl::setFrequency(double frequency)
+{
+  int retcode;
+  if(!rigControlEnabled) return false;
+  retcode = rig_set_vfo(my_rig, RIG_VFO_CURR);
+  if (retcode != RIG_OK ) {errorMessage(retcode,"setVFO"); return false; }
+  retcode = rig_set_freq(my_rig, RIG_VFO_CURR, frequency);
+  if (retcode != RIG_OK ) {errorMessage(retcode,"setFrequency"); return false; }
+  return true;
+}
+
+void rigControl::disable()
+{
+  if(rigControlEnabled)
+    {
+      rig_close(my_rig); /* close port */
+      rig_cleanup(my_rig); /* if you care about memory */
+      rigControlEnabled=false;
+    }
+}
+
+
+
+
+bool rigControl::getMode(QString &mode)
+{
+  rmode_t rmode;
+  pbwidth_t width;
+  int retcode;
+  if(!rigControlEnabled) return false;
+  retcode = rig_get_mode(my_rig, RIG_VFO_CURR, &rmode, &width);
+  if (retcode != RIG_OK ) {errorMessage(retcode,"getMode"); return false; }
+  mode=QString(rig_strrmode(rmode));
+  return true;
+}
+
+bool rigControl::setMode(QString mode)
+{
+  rmode_t rmode=rig_parse_mode(mode.toLatin1().data());
+  int retcode;
+  if(!rigControlEnabled) return false;
+  retcode = rig_set_mode(my_rig, RIG_VFO_CURR, rmode, rig_passband_normal(my_rig,rmode));
+  if (retcode != RIG_OK ) {errorMessage(retcode,"setMode"); return false; }
+  return true;
+}
+
+
+bool rigControl::setPTT(bool on)
+{
+  int retcode;
+  ptt_t ptt;
+  if(on) ptt=RIG_PTT_ON; else ptt=RIG_PTT_OFF;
+  if(!rigControlEnabled) return false;
+  retcode = rig_set_ptt (my_rig, RIG_VFO_CURR,ptt);
+  if (retcode != RIG_OK ) {errorMessage(retcode,"setPTT"); return false; }
+  return true;
+}
+
+
+
+void  rigControl::errorMessage(int errorCode,QString command)
+{
+  QMessageBox::information(0,"Cat interface",QString("Error in connection: %1\n%2").arg(QString(rigerror(errorCode))).arg(command));
+}
+
+void rigControl::getRadioList()
+{
+  if(!radiolistLoaded)
+    {
+      capsList.clear();
+      rig_load_all_backends();
+      rig_list_foreach(collect,0);
+      qSort(capsList.begin(),capsList.end(),model_Sort);
+      radiolistLoaded=true;
+    }
+}
+
+bool rigControl::getRadioList(QComboBox *cb)
+{
+  int i;
+  if(capsList.count()==0) return false;
+  QStringList sl;
+  for (i=0;i<capsList.count();i++)
+    {
+      QString t;
+      t= QString::number(capsList.at(i)->rig_model);
+      t=t.rightJustified(5,' ')+" ";
+      t+= capsList.at(i)->mfg_name;
+      t+=",";
+      t+=capsList.at(i)->model_name;
+      sl << t;
+    }
+  cb->addItems(sl);
+  return true;
+}
+
+int rigControl::getModelNumber(int idx)
+{
+  if(idx<0) return 0;
+  return capsList.at(idx)->rig_model;
+}
+
+int rigControl::getRadioModelIndex()
+{
+  int i;
+  QString t=catParams.radioModel;
+  t=t.remove(0,5);
+  t=t.simplified();
+  QStringList sl=t.split(",");
+  if(sl.count()==1) sl.append("");
+  for(i=0;i<capsList.count();i++)
+    {
+      if((capsList.at(i)->mfg_name==sl.at(0)) && (capsList.at(i)->model_name==sl.at(1)))
+        {
+          return i;
+        }
+    }
+  return -1;
+}
+
+bool model_Sort(const rig_caps *caps1,const rig_caps *caps2)
+{
+  if(caps1->mfg_name==caps2->mfg_name)
+    {
+      if (QString::compare(caps1->model_name,caps2->model_name)<0) return true;
+      return false;
+    }
+  if (QString::compare(caps1->mfg_name,caps2->mfg_name)<0) return true;
+  return false;
+}
+
+void rigControl::activatePTT(bool b)
+{
+  int modemlines;
+  if(catParams.enableSerialPTT)
+    {
+      if (catParams.pttSerialPort.isEmpty()) return;
+      if(serialP==0)
+        {
+          serialP=::open(catParams.pttSerialPort.toLatin1().data(),O_RDWR);
+          if (serialP<=0)
+            {
+              QMessageBox::warning(txWidgetPtr,"Serial Port Error",
+                                   QString("Unable to open serial port %1\ncheck Options->Configuration\n"
+                                           "make sure that you have read/write permission\nIf you do not have a serial port,\n"
+                                           "then disable -Serial PTT- option in the configuration").arg(catParams.pttSerialPort) ,
+                                   QMessageBox::Ok,0 );
+              return;
+            }
+          else
+            {
+              ioctl(serialP,TIOCMGET,&modemlines);
+              modemlines &= ~TIOCM_DTR;
+              modemlines &= ~TIOCM_RTS;
+              if(catParams.activeDTR) modemlines &= ~TIOCM_DTR;
+              if(catParams.activeRTS)modemlines &= ~TIOCM_RTS;
+              if(catParams.nactiveDTR) modemlines |= ~TIOCM_DTR;
+              if(catParams.nactiveRTS)modemlines |= ~TIOCM_RTS;
+              ioctl(serialP,TIOCMSET,&modemlines);
+            }
+        }
+      if(serialP>0)
+        {
+          if(b)
+            {
+              ioctl(serialP,TIOCMGET,&modemlines);
+              if(catParams.activeDTR) modemlines |= TIOCM_DTR;
+              if(catParams.activeRTS)modemlines |= TIOCM_RTS;
+              if(catParams.nactiveDTR) modemlines &= ~TIOCM_DTR;
+              if(catParams.nactiveRTS)modemlines &= ~TIOCM_RTS;
+              ioctl(serialP,TIOCMSET,&modemlines);
+              //ioctl(serial,TIOCMBIS,&t);
+            }
+          else
+            {
+              ioctl(serialP,TIOCMGET,&modemlines);
+              if(catParams.activeDTR) modemlines &= ~TIOCM_DTR;
+              if(catParams.activeRTS) modemlines &= ~TIOCM_RTS;
+              if(catParams.nactiveDTR) modemlines |= ~TIOCM_DTR;
+              if(catParams.nactiveRTS)modemlines |= ~TIOCM_RTS;
+              ioctl(serialP,TIOCMSET,&modemlines);
+              //	ioctl(serial,TIOCMBIC,&t);
+
+            }
+        }
+    }
+  else if(catParams.enableXMLRPC)
+    {
+      xmlIntfPtr->activatePTT(b);
+    }
+  else rigController->setPTT(b); // does nothing if rigController is disabled
+  mainWindowPtr->setPTT(b);
+  if(b)
+    {
+      addToLog("dispatcher: PTT activated",LOGDISPAT);
+    }
+  else
+    {
+      addToLog("dispatcher: PTT deactivated",LOGDISPAT);
+    }
+}
+
+
+
diff --git a/qsstv/rig/rigcontrol.h b/qsstv/rig/rigcontrol.h
new file mode 100644
index 0000000..1cf1f78
--- /dev/null
+++ b/qsstv/rig/rigcontrol.h
@@ -0,0 +1,57 @@
+#ifndef RIGCONTROL_H
+#define RIGCONTROL_H
+#include <QObject>
+#include <QComboBox>
+#include <hamlib/rig.h>
+#include "rigparams.h"
+#include "xmlrpc/xmlinterface.h"
+
+bool model_Sort(const rig_caps *caps1,const rig_caps *caps2);
+
+
+class rigControl: public QObject
+{
+	Q_OBJECT
+public:
+  rigControl(int radioIndex);
+	~rigControl();
+	bool init();
+	bool enabled() {return rigControlEnabled;}
+	bool getFrequency(double &frequency);
+	bool setFrequency(double frequency);
+	bool getMode(QString &mode);
+  bool setMode(QString mode);
+  bool setPTT(bool On);
+  int getModelNumber(int idx);
+  int getRadioModelIndex();
+  bool getRadioList(QComboBox *cb);
+  void disable();
+  scatParams* params() {return &catParams;}
+  void activatePTT(bool b);
+  double getTxDelay() {return catParams.txOnDelay;}
+  QString initError;
+
+
+private:
+ 	hamlib_port_t myport;
+  RIG *my_rig;            // handle to rig (nstance)
+	freq_t freq;            // frequency  
+	rmode_t rmode;          // radio mode of operation 
+	pbwidth_t width;
+	vfo_t vfo;              // vfo selection 
+	int strength;           // S-Meter level 
+	int retcode;            // generic return code from functions 
+	rig_model_t myrig_model;
+	bool rigControlEnabled;
+  void errorMessage(int errorCode,QString command);
+  void getRadioList();
+  scatParams catParams;
+  int serialP;
+
+
+};
+
+extern rigControl *rigControllerR1;
+extern rigControl *rigControllerR2;
+extern rigControl *rigController;
+#endif
diff --git a/qsstv/rig/rigcontrolform.cpp b/qsstv/rig/rigcontrolform.cpp
new file mode 100644
index 0000000..ebe9989
--- /dev/null
+++ b/qsstv/rig/rigcontrolform.cpp
@@ -0,0 +1,277 @@
+#include "rigcontrolform.h"
+#include "ui_rigcontrolform.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include "utils/supportfunctions.h"
+#include "rigparams.h"
+#include "rigcontrol.h"
+#include <QSettings>
+#include <QMessageBox>
+
+rigControlForm::rigControlForm(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::rigControlForm)
+{
+  ui->setupUi(this);
+  connect(ui->enableCATCheckBox,SIGNAL(clicked()),SLOT(slotEnableCAT()));
+  connect(ui->enablePTTCheckBox,SIGNAL(clicked()),SLOT(slotEnablePTT()));
+  connect(ui->enableXMLRPCCheckBox,SIGNAL(clicked()),SLOT(slotEnableXMLRPC()));
+  connect(ui->restartPushButton,SIGNAL(clicked()),SLOT(slotRestart()));
+  connect(ui->RTSCheckBox,SIGNAL(clicked()),SLOT(slotCheckPTT0()));
+  connect(ui->DTRCheckBox,SIGNAL(clicked()),SLOT(slotCheckPTT1()));
+  connect(ui->nRTSCheckBox,SIGNAL(clicked()),SLOT(slotCheckPTT2()));
+  connect(ui->nDTRCheckBox,SIGNAL(clicked()),SLOT(slotCheckPTT3()));
+  rigController=NULL;
+}
+
+
+rigControlForm::~rigControlForm()
+{
+  delete ui;
+}
+
+void rigControlForm::attachRigController(rigControl *rigCtrl)
+{
+  rigController=rigCtrl;
+}
+
+void rigControlForm::readSettings()
+{
+  cp=rigController->params();
+  QSettings qSettings;
+  qSettings.beginGroup(cp->configLabel);
+  cp->serialPort=qSettings.value("serialPort","/dev/ttyS0").toString();
+  cp->radioModel=qSettings.value("radioModel","dummy").toString();
+  cp->civAddress=qSettings.value("civAddress","").toString();
+  cp->baudrate=qSettings.value("baudrate",9600).toInt();
+  cp->parity=qSettings.value("parity","None").toString();
+  cp->stopbits=qSettings.value("stopbits",1).toInt();
+  cp->databits=qSettings.value("databits",8).toInt();
+  cp->handshake=qSettings.value("handshake","None").toString();
+  cp->enableCAT=qSettings.value("enableCAT",0).toBool();
+  cp->enableSerialPTT=qSettings.value("enableSerialPTT",0).toBool();
+  cp->pttSerialPort=qSettings.value("pttSerialPort","/dev/ttyS0").toString();
+  cp->activeRTS=qSettings.value("activeRTS",1).toBool();
+  cp->activeDTR=qSettings.value("activeDTR",0).toBool();
+  cp->nactiveRTS=qSettings.value("nactiveRTS",1).toBool();
+  cp->nactiveDTR=qSettings.value("nactiveDTR",0).toBool();
+
+  cp->enableXMLRPC=qSettings.value("enableXMLRPC",0).toBool();
+  cp->XMLRPCPort=qSettings.value("XMLRPCPort","7362").toInt();
+
+  cp->txOnDelay=qSettings.value("txOnDelay",0.0).toDouble();
+
+  cp->pttType=(ptt_type_t)qSettings.value("pttType",(int)RIG_PTT_RIG).toInt();
+  qSettings.endGroup();
+  setParams();
+}
+
+void rigControlForm::writeSettings()
+{
+  getParams();
+  QSettings qSettings;
+  qSettings.beginGroup(cp->configLabel);
+  qSettings.setValue("serialPort",cp->serialPort);
+  qSettings.setValue("radioModel",cp->radioModel);
+  qSettings.setValue("civAddress",cp->civAddress);
+  qSettings.setValue("baudrate",cp->baudrate);
+  qSettings.setValue("parity",cp->parity);
+  qSettings.setValue("stopbits",cp->stopbits);
+  qSettings.setValue("databits",cp->databits);
+  qSettings.setValue("handshake",cp->handshake);
+  qSettings.setValue("enableCAT",cp->enableCAT);
+  qSettings.setValue("enableSerialPTT",cp->enableSerialPTT);
+  qSettings.setValue("pttSerialPort",cp->pttSerialPort);
+  qSettings.setValue("activeRTS",cp->activeRTS);
+  qSettings.setValue("activeDTR",cp->activeDTR);
+  qSettings.setValue("nactiveRTS",cp->nactiveRTS);
+  qSettings.setValue("nactiveDTR",cp->nactiveDTR);
+  qSettings.setValue("pttType",(int) cp->pttType);
+
+  qSettings.setValue("enableXMLRPC",cp->enableXMLRPC);
+  qSettings.setValue("XMLRPCPort",cp->XMLRPCPort);
+  qSettings.setValue("txOnDelay",cp->txOnDelay);
+  qSettings.endGroup();
+}
+
+void rigControlForm::setParams()
+{
+  if(rigController->getRadioList(ui->radioModelComboBox)) setValue(cp->radioModel,ui->radioModelComboBox);
+  setValue(cp->serialPort,ui->serialPortLineEdit);
+  setValue(cp->civAddress,ui->civAddressLineEdit);
+  setValue(cp->baudrate,ui->baudrateComboBox);
+  setValue(cp->parity,ui->parityComboBox);
+  setValue(cp->stopbits,ui->stopbitsComboBox);
+  setValue(cp->databits,ui->databitsComboBox);
+  setValue(cp->handshake,ui->handshakeComboBox);
+  setValue(cp->enableCAT,ui->enableCATCheckBox);
+  setValue(cp->enableSerialPTT,ui->enablePTTCheckBox);
+  setValue(cp->pttSerialPort,ui->pttSerialPortLineEdit);
+  if(cp->activeRTS) cp->nactiveRTS=false;
+  if(cp->activeDTR) cp->nactiveDTR=false;
+
+  setValue(cp->activeRTS,ui->RTSCheckBox);
+  setValue(cp->activeDTR,ui->DTRCheckBox);
+  setValue(cp->nactiveRTS,ui->nRTSCheckBox);
+  setValue(cp->nactiveDTR,ui->nDTRCheckBox);
+  switch(cp->pttType)
+    {
+    case RIG_PTT_RIG:
+      setValue(true,ui->catRadioButton);
+    break;
+    case RIG_PTT_SERIAL_RTS:
+      setValue(true,ui->rtsRadioButton);
+    break;
+    case RIG_PTT_SERIAL_DTR:
+      setValue(true,ui->dtrRadioButton);
+    break;
+    default:
+      setValue(true,ui->catRadioButton);
+    break;
+    }
+  if(cp->enableCAT && cp->enableSerialPTT)
+    {
+      if(cp->serialPort==cp->pttSerialPort)
+        {
+          cp->enableSerialPTT=false;
+        }
+    }
+  setValue(cp->enableSerialPTT,ui->enablePTTCheckBox);
+  setValue(cp->txOnDelay,ui->txOnDelayDoubleSpinBox);
+  setValue(cp->enableXMLRPC,ui->enableXMLRPCCheckBox);
+  setValue(cp->XMLRPCPort,ui->XMLRPCPortLineEdit);
+
+
+}
+
+void rigControlForm::getParams()
+{
+  getValue(cp->serialPort,ui->serialPortLineEdit);
+  if(ui->radioModelComboBox->count()!=0) getValue(cp->radioModel,ui->radioModelComboBox);
+  getValue(cp->civAddress,ui->civAddressLineEdit);
+  getValue(cp->baudrate,ui->baudrateComboBox);
+  getValue(cp->parity,ui->parityComboBox);
+  getValue(cp->stopbits,ui->stopbitsComboBox);
+  getValue(cp->databits,ui->databitsComboBox);
+  getValue(cp->handshake,ui->handshakeComboBox);
+  getValue(cp->enableCAT,ui->enableCATCheckBox);
+  getValue(cp->enableSerialPTT,ui->enablePTTCheckBox);
+  getValue(cp->pttSerialPort,ui->pttSerialPortLineEdit);
+  getValue(cp->activeRTS,ui->RTSCheckBox);
+  getValue(cp->activeDTR,ui->DTRCheckBox);
+  getValue(cp->nactiveRTS,ui->nRTSCheckBox);
+  getValue(cp->nactiveDTR,ui->nDTRCheckBox);
+  if(ui->catRadioButton->isChecked()) cp->pttType=RIG_PTT_RIG;
+  if(ui->rtsRadioButton->isChecked()) cp->pttType=RIG_PTT_SERIAL_RTS;
+  if(ui->dtrRadioButton->isChecked()) cp->pttType=RIG_PTT_SERIAL_DTR;
+  getValue(cp->txOnDelay,ui->txOnDelayDoubleSpinBox);
+  getValue(cp->enableXMLRPC,ui->enableXMLRPCCheckBox);
+  getValue(cp->XMLRPCPort,ui->XMLRPCPortLineEdit);
+}
+
+void rigControlForm::slotEnableCAT()
+{
+  if(ui->enableCATCheckBox->isChecked() && ui->enablePTTCheckBox->isChecked())
+    {
+      if(ui->pttSerialPortLineEdit->text()==ui->serialPortLineEdit->text())
+        {
+          QMessageBox::critical(this,"Configuration error",
+                                "The PTT serialport must be different from the CAT serial port if both are enabled");
+          ui->enablePTTCheckBox->setChecked(false);
+        }
+
+    }
+  if(ui->enableCATCheckBox->isChecked())
+    {
+      ui->enableXMLRPCCheckBox->setChecked(false);
+      rigController->init();
+    }
+  else
+    {
+      rigController->disable();
+    }
+  getParams();
+}
+
+
+
+void rigControlForm::slotEnablePTT()
+{
+  if(ui->enableCATCheckBox->isChecked() && ui->enablePTTCheckBox->isChecked())
+    {
+      if(ui->pttSerialPortLineEdit->text()==ui->serialPortLineEdit->text())
+        {
+          QMessageBox::critical(this,"Configuration error",
+                                "The PTT serialport must be different from the CAT serial port if both are enabled");
+          ui->enablePTTCheckBox->setChecked(false);
+          return;
+        }
+
+    }
+  if(ui->enablePTTCheckBox->isChecked())
+    {
+      ui->enableXMLRPCCheckBox->setChecked(false);
+    }
+  getParams();
+}
+
+void rigControlForm::slotEnableXMLRPC()
+{
+  ui->enableCATCheckBox->setChecked(false);
+  ui->enablePTTCheckBox->setChecked(false);
+}
+
+void rigControlForm::slotRestart()
+{
+  getParams();
+  if(ui->enableCATCheckBox->isChecked())
+    {
+      if(rigController->init())
+        {
+          ui->restartPushButton->setStyleSheet("background-color: green");
+        }
+      else
+        {
+          ui->restartPushButton->setStyleSheet("background-color: red");
+        }
+    }
+}
+
+
+void rigControlForm::slotCheckPTT0()
+{
+  checkPTT(0,ui->RTSCheckBox->isChecked());
+}
+void rigControlForm::slotCheckPTT1()
+{checkPTT(1,ui->DTRCheckBox->isChecked());
+}
+void rigControlForm::slotCheckPTT2()
+{
+  checkPTT(2,ui->nRTSCheckBox->isChecked());
+}
+void rigControlForm::slotCheckPTT3()
+{
+  checkPTT(3,ui->nDTRCheckBox->isChecked());
+}
+
+void rigControlForm::checkPTT(int p,bool b)
+{
+  if(!b) return;
+  switch (p)
+    {
+    case 0:
+      setValue(false,ui->nRTSCheckBox);
+    break;
+    case 1:
+      setValue(false,ui->nDTRCheckBox);
+    break;
+    case 2:
+      setValue(false,ui->RTSCheckBox);
+    break;
+    case 3:
+      setValue(false,ui->DTRCheckBox);
+    break;
+
+    }
+}
+
diff --git a/qsstv/rig/rigcontrolform.h b/qsstv/rig/rigcontrolform.h
new file mode 100644
index 0000000..68c93ec
--- /dev/null
+++ b/qsstv/rig/rigcontrolform.h
@@ -0,0 +1,47 @@
+#ifndef RIGCONTROLFORM_H
+#define RIGCONTROLFORM_H
+
+#include <QWidget>
+#include "rigcontrol.h"
+
+
+namespace Ui {
+class rigControlForm;
+}
+
+class rigControlForm : public QWidget
+{
+  Q_OBJECT
+  
+public:
+  explicit rigControlForm(QWidget *parent = 0);
+  ~rigControlForm();
+  void attachRigController(rigControl *rigCtrl);
+  void readSettings();
+  void writeSettings();
+
+  bool needsRestart() { return changed;}
+
+
+public slots:
+  void slotEnableCAT();
+  void slotEnablePTT();
+  void slotEnableXMLRPC();
+  void slotRestart();
+  void slotCheckPTT0();
+  void slotCheckPTT1();
+  void slotCheckPTT2();
+  void slotCheckPTT3();
+  
+private:
+
+  Ui::rigControlForm *ui;
+  bool changed;
+  void getParams();
+  void setParams();
+  scatParams *cp;
+  rigControl *rigController;
+  void checkPTT(int p,bool b);
+};
+
+#endif // RIGCONTROLFORM_H
diff --git a/qsstv/rig/rigcontrolform.ui b/qsstv/rig/rigcontrolform.ui
new file mode 100644
index 0000000..3895b35
--- /dev/null
+++ b/qsstv/rig/rigcontrolform.ui
@@ -0,0 +1,2140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>rigControlForm</class>
+ <widget class="QWidget" name="rigControlForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>673</width>
+    <height>764</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_5">
+   <property name="margin">
+    <number>1</number>
+   </property>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="palette">
+      <palette>
+       <active>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </active>
+       <inactive>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </inactive>
+       <disabled>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </disabled>
+      </palette>
+     </property>
+     <property name="autoFillBackground">
+      <bool>true</bool>
+     </property>
+     <property name="title">
+      <string>Special Serial Port</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <property name="spacing">
+       <number>2</number>
+      </property>
+      <property name="margin">
+       <number>1</number>
+      </property>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_10">
+        <item>
+         <widget class="QCheckBox" name="enablePTTCheckBox">
+          <property name="text">
+           <string>Enable PTT serial Interface</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_8">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QLabel" name="serialInterfaceTextLabel_4">
+          <property name="minimumSize">
+           <size>
+            <width>60</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="text">
+           <string>PTT Serial Port</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="wordWrap">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="pttSerialPortLineEdit">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_15">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_8"/>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_7">
+        <item>
+         <widget class="QCheckBox" name="RTSCheckBox">
+          <property name="text">
+           <string>+RTS</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="DTRCheckBox">
+          <property name="text">
+           <string>+DTR</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="nRTSCheckBox">
+          <property name="text">
+           <string>-RTS</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="nDTRCheckBox">
+          <property name="text">
+           <string>-DTR</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_11">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="palette">
+      <palette>
+       <active>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </active>
+       <inactive>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </inactive>
+       <disabled>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </disabled>
+      </palette>
+     </property>
+     <property name="autoFillBackground">
+      <bool>true</bool>
+     </property>
+     <property name="title">
+      <string>Hamlib Control</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <property name="spacing">
+       <number>2</number>
+      </property>
+      <property name="margin">
+       <number>1</number>
+      </property>
+      <item>
+       <layout class="QVBoxLayout" name="verticalLayout">
+        <property name="spacing">
+         <number>2</number>
+        </property>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_9">
+          <item>
+           <widget class="QCheckBox" name="enableCATCheckBox">
+            <property name="text">
+             <string>Enable Hamlib Cat Interface</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <layout class="QGridLayout" name="gridLayout_2">
+            <property name="spacing">
+             <number>2</number>
+            </property>
+            <item row="0" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel_2">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Radio Model</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QComboBox" name="radioModelComboBox">
+              <property name="font">
+               <font>
+                <family>Ubuntu Mono</family>
+                <weight>75</weight>
+                <italic>false</italic>
+                <bold>true</bold>
+                <kerning>false</kerning>
+               </font>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel_3">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>CIV Address</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <layout class="QHBoxLayout" name="horizontalLayout_2">
+              <item>
+               <widget class="QLineEdit" name="civAddressLineEdit">
+                <property name="maximumSize">
+                 <size>
+                  <width>50</width>
+                  <height>16777215</height>
+                 </size>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer_4">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
+            <item row="2" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel">
+              <property name="minimumSize">
+               <size>
+                <width>108</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Serial Port/Host</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QLineEdit" name="serialPortLineEdit">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel_6">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Handshake</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1">
+              <property name="spacing">
+               <number>2</number>
+              </property>
+              <item>
+               <widget class="QComboBox" name="handshakeComboBox">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+                <item>
+                 <property name="text">
+                  <string>None</string>
+                 </property>
+                </item>
+                <item>
+                 <property name="text">
+                  <string>Hardware</string>
+                 </property>
+                </item>
+                <item>
+                 <property name="text">
+                  <string>XOn/XOff</string>
+                 </property>
+                </item>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer_3">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QGridLayout" name="gridLayout">
+            <item row="0" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel_8">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Parity</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QComboBox" name="parityComboBox">
+              <property name="maximumSize">
+               <size>
+                <width>70</width>
+                <height>16777215</height>
+               </size>
+              </property>
+              <item>
+               <property name="text">
+                <string>None</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Odd</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Even</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item row="0" column="2">
+             <spacer name="horizontalSpacer_7">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="serialInterfaceTextLabel_9">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Databits</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1" colspan="2">
+             <layout class="QHBoxLayout" name="horizontalLayout_4">
+              <item>
+               <widget class="QComboBox" name="databitsComboBox">
+                <item>
+                 <property name="text">
+                  <string>7</string>
+                 </property>
+                </item>
+                <item>
+                 <property name="text">
+                  <string>8</string>
+                 </property>
+                </item>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
+            <item row="2" column="0">
+             <widget class="QLabel" name="serialPortTextLabel">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Baudrate</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QComboBox" name="baudrateComboBox">
+              <property name="maximumSize">
+               <size>
+                <width>70</width>
+                <height>16777215</height>
+               </size>
+              </property>
+              <property name="currentIndex">
+               <number>5</number>
+              </property>
+              <item>
+               <property name="text">
+                <string>300</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>600</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>1200</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>2400</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>4800</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>9600</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>19200</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>38400</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>57600</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>115200</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>230400</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>460800</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="stopbitsTextLabel">
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>StopBits</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="wordWrap">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1" colspan="2">
+             <layout class="QHBoxLayout" name="horizontalLayout_5">
+              <item>
+               <widget class="QComboBox" name="stopbitsComboBox">
+                <item>
+                 <property name="text">
+                  <string>1</string>
+                 </property>
+                </item>
+                <item>
+                 <property name="text">
+                  <string>2</string>
+                 </property>
+                </item>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer_6">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_11">
+          <item>
+           <widget class="QLabel" name="label">
+            <property name="text">
+             <string>PTT Control via:</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QRadioButton" name="catRadioButton">
+            <property name="text">
+             <string>CAT Command</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QRadioButton" name="rtsRadioButton">
+            <property name="text">
+             <string>RTS</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QRadioButton" name="dtrRadioButton">
+            <property name="text">
+             <string>DTR</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer_12">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_6">
+        <item>
+         <spacer name="horizontalSpacer_9">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="restartPushButton">
+          <property name="text">
+           <string>Restart CAT Interface</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_10">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="palette">
+      <palette>
+       <active>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </active>
+       <inactive>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </inactive>
+       <disabled>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>231</red>
+           <green>231</green>
+           <blue>231</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>139</red>
+           <green>139</green>
+           <blue>139</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>104</red>
+           <green>104</green>
+           <blue>104</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>208</red>
+           <green>208</green>
+           <blue>208</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </disabled>
+      </palette>
+     </property>
+     <property name="autoFillBackground">
+      <bool>true</bool>
+     </property>
+     <property name="title">
+      <string>XMLRPC Interface</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <property name="spacing">
+       <number>2</number>
+      </property>
+      <property name="margin">
+       <number>1</number>
+      </property>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_13">
+        <item>
+         <widget class="QCheckBox" name="enableXMLRPCCheckBox">
+          <property name="text">
+           <string>Enable XMLRPC Interface</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_14">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QLabel" name="serialInterfaceTextLabel_5">
+          <property name="minimumSize">
+           <size>
+            <width>60</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="text">
+           <string>Port</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="wordWrap">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="XMLRPCPortLineEdit">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_5">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_12">
+     <item>
+      <widget class="QLabel" name="serialPortTextLabel_2">
+       <property name="minimumSize">
+        <size>
+         <width>60</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>TX on Delay</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="txOnDelayDoubleSpinBox">
+       <property name="decimals">
+        <number>1</number>
+       </property>
+       <property name="maximum">
+        <double>5.000000000000000</double>
+       </property>
+       <property name="singleStep">
+        <double>0.100000000000000</double>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="serialPortTextLabel_3">
+       <property name="minimumSize">
+        <size>
+         <width>60</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>in seconds</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_13">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>17</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/rig/rigparams.h b/qsstv/rig/rigparams.h
new file mode 100644
index 0000000..14d0841
--- /dev/null
+++ b/qsstv/rig/rigparams.h
@@ -0,0 +1,32 @@
+#ifndef RIGPARAMS_H
+#define RIGPARAMS_H
+#include <QString>
+#include <hamlib/rig.h>
+
+
+struct scatParams
+{
+  QString configLabel;
+  QString serialPort; /**<  serial port device*/
+  QString radioModel;
+  int radioModelNumber;
+  QString civAddress;
+  int baudrate; /**<  serial port baudrate*/
+  QString parity;
+  int stopbits;
+  int databits;
+  QString handshake;
+  bool enableCAT;
+  bool enableSerialPTT;
+  QString pttSerialPort;
+  bool activeRTS;
+  bool activeDTR;
+  bool nactiveRTS;
+  bool nactiveDTR;
+  ptt_type_t pttType;
+  bool enableXMLRPC;
+  int XMLRPCPort;
+  double txOnDelay;
+};
+
+#endif // RIGPARAMS_H
diff --git a/qsstv/rxfunctions.cpp b/qsstv/rxfunctions.cpp
new file mode 100644
index 0000000..2fe8c64
--- /dev/null
+++ b/qsstv/rxfunctions.cpp
@@ -0,0 +1,466 @@
+#include "rxfunctions.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include "rxwidget.h"
+#include "sound/soundio.h"
+#include "dsp/filterparam.h"
+#include "dsp/filter.h"
+#include "dsp/downsamplefilter.h"
+#ifndef QT_NO_DEBUG
+#include "scope/scopeview.h"
+#include "scope/scopeoffset.h"
+#endif
+#include "configparams.h"
+#include "utils/supportfunctions.h"
+#include "dispatcher.h"
+#include "sstv/modes/modes.h"
+
+#include "drmrx/drm.h"
+#include "drmrx/drmproto.h"
+#include "drmrx/demodulator.h"
+
+#define DRMNEW
+
+const QString stateStr[rxFunctions::WAIT+1]=
+{
+  "Hunting",
+  "SlantAdjust",
+  "Processing",
+  "Restart",
+  "Sync Lost",
+  "End",
+  "Wait"
+};
+
+rxFunctions::rxFunctions(QObject *parent) :
+  QThread(parent)
+{
+  rxState=RXIDLE;
+  downSampleFilter=NULL;
+  rxFilter=NULL;
+  syncFilter=NULL;
+  dataInputPtr=new drmDataInput(RXSTRIPE);
+  currentMode=0;
+
+#ifndef QT_NO_DEBUG
+  scopeViewerData->setCurveName("Volume",SCDATA1);
+  scopeViewerData->setCurveName("FM Demod",SCDATA2);
+  scopeViewerData->setCurveName("raw Input",SCDATA3);
+  scopeViewerData->setCurveName("none",SCDATA4);
+  scopeViewerData->setAxisTitles("Samples","int","demod");
+#endif
+  demodulatorPtr=new demodulator;
+
+}
+
+rxFunctions::~rxFunctions()
+{
+  if(downSampleFilter==NULL) delete downSampleFilter;
+  if(rxFilter==NULL) delete rxFilter;
+  if(syncFilter==NULL) delete syncFilter;
+}
+
+
+void rxFunctions::run()
+{
+  addToLog("starting rxfunctions run",LOGRXFUNC);
+  int count;
+  bool done=false;
+  init();
+  addToLog("end init rxfunctions run",LOGRXFUNC);
+  abort=false;
+  while(!abort)
+    {
+      switch(rxState)
+        {
+        case RXIDLE:
+          msleep(200);
+        break;
+        case RXRUNNING:
+          if((count=soundIOPtr->rxBuffer.count())<SAMPLINGSTRIPE)
+            {
+              msleep((250*RXSTRIPE)/rxClock);
+              if(!soundIOPtr->isCapturing())
+                {
+                  rxState=RXINIT;
+                }
+            }
+          else
+            {
+              //              addToLog(QString("LoadTempBuf count=%1").arg(count),LOGRXFUNC);
+              soundIOPtr->rxBuffer.copyNoCheck(tempBuf,SAMPLINGSTRIPE);
+              //              addToLog("LoadTempBuf",LOGRXFUNC);
+              downSampleFilter->downSample4(tempBuf);
+              displayFFTEvent* ce = new displayFFTEvent(downSampleFilter->filteredDataPtr());
+              ce->waitFor(&done);
+              QApplication::postEvent(dispatcherPtr, ce);
+              while(!done) {usleep(10);}
+
+              switch (transmissionModeIndex)
+                {
+                case DRM:
+                  runDRM();
+                break;
+                case SSTV:
+                  rxHoldingBuffer.putNoCheck(downSampleFilter->filteredDataPtr(),RXSTRIPE);
+                  bufferCounter++;
+                  getData();
+                  processSSTV();
+                  sampleCounter+=RXSTRIPE;
+                  rxHoldingBuffer.skip(RXSTRIPE);
+                  demodBuffer.skip(RXSTRIPE);
+                break;
+                  //                case FAX:
+                  //                break;
+                case NOMODE:
+                break;
+                }
+            }
+        break;
+        case RXINIT:
+          if(transmissionModeIndex==SSTV) syncProc.init(); //reset meters
+          rxState=RXIDLE;
+        break;
+
+        }
+    }
+  abort=false;
+}
+
+void rxFunctions::init()
+{
+  //  rxMode=rxModeIndex;
+  setFilters(rxWidgetPtr->getFilterIndex()); // setup SSTV Filters
+  avgSNR=0;
+  if(transmissionModeIndex==DRM)
+    {
+      n = DRMBUFSIZE;
+      /* initialisations */
+      demodulatorPtr->init();
+      dataInputPtr->init(RXSTRIPE);
+      initGetmode( n / 4);
+      rRation = 1.000;
+      samplerate_offset_estimation = 0.0;
+      runstate = RUN_STATE_POWER_ON;		/* POWER_ON */
+      channel_decoding();
+      runstate = RUN_STATE_INIT;		/* INIT */
+      channel_decoding();
+      runstate = RUN_STATE_FIRST;			/* FIRSTRUN */
+      runstate = RUN_STATE_NORMAL;			/* NORMAL RUN */
+    }
+  else
+    {
+      SSTVState=HUNTING;
+      syncProc.init();
+    }
+  if(downSampleFilter==NULL)  downSampleFilter=new downsampleFilter(SAMPLINGSTRIPE,downSampleFilterParam,DSAMPLEFILTERLEN,false);
+  else downSampleFilter->init();
+  sampleCounter=0;
+  bufferCounter=0;
+  rxHoldingBuffer.reset();
+}
+
+void rxFunctions::getData()
+{
+  //  addToLog(QString("GetData readIndex: %1,sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(sampleCounter),LOGRXFUNC);
+  rxFilter->processFM(rxHoldingBuffer.readPointer());
+  demodBuffer.putNoCheck(rxFilter->filteredDataPtr(),RXSTRIPE);
+  syncFilter->processFM(rxHoldingBuffer.readPointer());
+  syncProc.process();
+  displaySyncEvent* ce;
+  ce = new displaySyncEvent(syncProc.syncQuality,(rxFilter->volumePtr()[0]-500)/500);
+  QApplication::postEvent(dispatcherPtr, ce);
+
+
+
+
+  //  addToLog("EndFilter",LOGPERFORM);
+
+#ifndef QT_NO_DEBUG
+  scopeViewerData->addData(SCDATA3,rxHoldingBuffer.readPointer(),sampleCounter,RXSTRIPE);
+  scopeViewerData->addData(SCDATA2,demodBuffer.readPointer(),sampleCounter,RXSTRIPE);
+  scopeViewerData->addData(SCDATA1,rxFilter->volumePtr(),sampleCounter,RXSTRIPE);
+#endif
+
+}
+
+#ifndef QT_NO_DEBUG
+
+unsigned int rxFunctions::setOffset(unsigned int offset,bool ask)
+{
+  if(ask)
+    {
+      scopeOffset so;
+      so.setOffset(offset);
+      if(so.exec()==QDialog::Accepted)
+        {
+          xOffset=so.getOffset()*1000;
+        }
+    }
+  else
+    {
+      xOffset=offset*1000;
+    }
+  syncProc.setOffset(xOffset);
+  scopeViewerData->setOffset(xOffset);
+  syncProc.setOffset(xOffset);
+  return xOffset/1000;
+}
+#endif
+
+void rxFunctions::processSSTV()
+{
+  rxSSTVStatusEvent *stce;
+  endImageRXEvent *endce;
+  bool done=false;
+  int block;
+  unsigned long sampleCounterLatch;
+  switch(SSTVState)
+    {
+    case HUNTING:
+      if(syncProc.isInSync()==0)
+        {
+          stce= new rxSSTVStatusEvent(QString("No sync"));
+          QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+          break; // no sync
+        }
+      if(!create(syncProc.getMode(),syncProc.getNewClock())) break;
+      stce= new rxSSTVStatusEvent(QString("Receiving ")+getSSTVModeNameLong(syncProc.getMode()));
+      lastUsedMode=getSSTVModeNameShort(syncProc.getMode());
+      QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+    case SLANTADJUST:
+      syncPosition=syncProc.getSyncPosition();
+      if(syncProc.isInSync()==1)
+        {
+          syncPosition=currentMode->adjustSyncPosition(syncPosition); // only execute if no retrace
+          addToLog(QString("rxFunctions: adjusted syncPosition= %1").arg(syncPosition),LOGRXFUNC);
+        }
+      sampleCounterLatch=sampleCounter; //remember where we've got
+      addToLog(QString("rxFunctions: sampleCounterLatch= %1").arg(sampleCounterLatch),LOGRXFUNC);
+      block=(syncPosition)/RXSTRIPE;
+      demodBuffer.rewind(sampleCounter-block*RXSTRIPE);
+      addToLog(QString("sc_rewind: block=%1,rewind= %2").arg(block).arg(sampleCounter-block*RXSTRIPE+RXSTRIPE),LOGRXFUNC);
+      sampleCounter=block*RXSTRIPE;
+      currentMode->setRxSampleCounter(sampleCounter);
+      currentMode->redrawFast(true);
+      currentMode->process(demodBuffer.readPointer(),syncPosition-sampleCounter,true);
+      addToLog(QString("rxFunctions: currentMode pos:=%1").arg(syncPosition-sampleCounter),LOGRXFUNC);
+      //      addToLog(QString("after Current mode set: %1,sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(sampleCounter),LOGRXFUNC);
+      while(sampleCounter<sampleCounterLatch)
+        {
+          demodBuffer.skip(RXSTRIPE);
+          sampleCounter+=RXSTRIPE;
+          //          addToLog(QString("loop readIndex: %1,sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(sampleCounter),LOGRXFUNC);
+          currentMode->process(demodBuffer.readPointer());
+        }
+      addToLog(QString("end loop readIndex: %1,sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(sampleCounter),LOGRXFUNC);
+      currentMode->redrawFast(false);
+      setState(PROCESSING);
+
+    break;
+    case PROCESSING:
+
+      if(currentMode->process(demodBuffer.readPointer())==modeBase::MBENDOFIMAGE)
+        {
+          setState(END);
+        }
+      if(syncProc.hasRetrace())
+        {
+          setState(END);
+        }
+      if(syncProc.isInSync()==0)
+        {
+          setState(END);
+        }
+      else if(syncProc.hasNewClock())
+        {
+          currentMode->init(syncProc.getNewClock());
+          setState(SLANTADJUST);
+        }
+
+    break;
+    case RESTART:
+      block=(int) ceil((rxClock*0.3)/RXSTRIPE)+1; // rewind to position before the last detected retrace, + one because we will skip 1 on return of getData()
+      addToLog(QString("rxFunction: retraceVertical: count=%1").arg(block*RXSTRIPE),LOGRXFUNC);
+      rxHoldingBuffer.rewind(block*RXSTRIPE);
+      init();
+      sampleCounter=-RXSTRIPE; // will be incremented (will be 0) when returning from this function
+    break;
+    case SYNCLOST:
+        addToLog(QString("rxFunction: synclost"),LOGRXFUNC);
+    case END:
+      addToLog("rxFunc state END",LOGRXFUNC);
+      endce = new endImageRXEvent();
+      endce->waitFor(&done);
+      QApplication::postEvent(dispatcherPtr, endce);
+      while(!done) { msleep(10);}
+      setState(RESTART);
+    break;
+    case WAIT:
+
+    break;
+    }
+}
+
+void rxFunctions::setFilters(int fIndex)
+{
+  if(rxFilter==NULL) rxFilter= new filter(RXSTRIPE,filterStruct[fIndex].filterPtr,RXNUMTAPS,filterStruct[fIndex].centerFrequency,rxClock/SUBSAMPLINGRATIO,true,0.001);
+  else rxFilter->setFilterParams(filterStruct[fIndex].filterPtr,RXNUMTAPS,filterStruct[fIndex].centerFrequency,rxClock/SUBSAMPLINGRATIO,true,0.001);
+  if(syncFilter==NULL) syncFilter= new filter(RXSTRIPE,wide1200BP,RXNUMTAPS,1200,rxClock/SUBSAMPLINGRATIO,true,1);
+  syncProc.setFilters(rxFilter,syncFilter);
+}
+
+void rxFunctions::stopAndWait()
+{
+
+  if(!isRunning()) return;
+  rxState=RXINIT;
+  while(rxState!=RXIDLE)
+    {
+      if(!isRunning()) return; // to avoid race conditions
+      qApp->processEvents();
+    }
+}
+
+bool  rxFunctions::create(esstvMode m,DSPFLOAT clock)
+{
+  bool done=false;
+  if(currentMode) delete currentMode;
+  currentMode=0;
+  switch (m)
+    {
+    case M1:
+    case M2:
+      currentMode=new modeGBR(m,RXSTRIPE,false);
+    break;
+    case S1:
+    case S2:
+    case SDX:
+      currentMode=new modeGBR2(m,RXSTRIPE,false);
+    break;
+    case R36:
+      currentMode=new modeRobot1(m,RXSTRIPE,false);
+    break;
+    case R24:
+    case R72:
+      currentMode=new modeRobot2(m,RXSTRIPE,false);
+    break;
+    case SC2_60:
+    case SC2_120:
+    case SC2_180:
+    case P3:
+    case P5:
+    case P7:
+      currentMode=new modeRGB(m,RXSTRIPE,false);
+    break;
+    case FAX480:
+    case BW8:
+    case BW12:
+      currentMode=new modeBW(m,RXSTRIPE,false);
+    break;
+    case AVT24:
+    case AVT90:
+    case AVT94:
+      currentMode=new modeAVT(m,RXSTRIPE,false);
+    break;
+    case PD50:
+    case PD90:
+    case PD120:
+    case PD160:
+    case PD180:
+    case PD240:
+    case PD290:
+    case MP73:
+    case MP115:
+    case MP140:
+    case MP175:
+      currentMode=new modePD(m,RXSTRIPE,false);
+    break;
+    default:
+      m=NOTVALID;
+    break;
+    }
+  if (m!=NOTVALID)
+    {
+      initializeSSTVParametersIndex(m,false);
+      QString s=getSSTVModeNameLong(m);
+      addToLog("rxFunction:create RX mode",LOGRXFUNC);
+      currentMode->init(clock);
+      startImageRXEvent* ce = new startImageRXEvent(QSize(currentMode->imagePixels(),currentMode->imageLines()));
+      ce->waitFor(&done);
+      QApplication::postEvent(dispatcherPtr, ce);
+      while(!done) { msleep(10);}
+      return true;
+    }
+  return false;
+}
+
+void rxFunctions::startRX()
+{
+  init();
+  rxState=RXRUNNING;
+}
+
+void rxFunctions::setState(eSSTVState st)
+{
+  addToLog(QString("rxfunc: set SSTVState: from %1 to %2").arg(stateStr[SSTVState]).arg (stateStr[st]),LOGRXFUNC);
+  SSTVState=st;
+
+}
+
+void rxFunctions::retraceVertical(void)
+{
+  setState(RESTART);
+}
+
+bool rxFunctions::saveOK()
+{
+  if(currentMode==0)
+    {
+      addToLog("saveOK called with currentMode==0",LOGRXFUNC);
+      return false;
+    }
+
+  if(currentMode->receivedLines()>(currentMode->imageLines()/3)) return true;
+  return false;
+}
+
+#define RATIONAVG 0.01
+void rxFunctions::runDRM()
+{
+  bool done=false;
+  DSPFLOAT temp;
+
+  displayDRMStatEvent *ce1;
+  displayDRMInfoEvent *ce2 ;
+  temp=WMERFAC;
+  if(temp<0) temp=0;
+  if(demodulatorPtr->isFrameSync())
+    {
+      avgSNR=(1-0.05)*avgSNR+0.05*temp;
+      ce1 = new displayDRMStatEvent(avgSNR,downSampleFilter->avgVolume);
+    }
+  else
+    {
+      ce1 = new displayDRMStatEvent(0,downSampleFilter->avgVolume);
+    }
+  QApplication::postEvent(dispatcherPtr, ce1);
+
+  while(input_samples_buffer_request ==0)
+    {
+      demodulatorPtr->demodulate(resamp_signal,0);
+    }
+  im=0;
+  im=dataInputPtr->getData(downSampleFilter->filteredDataPtr(),resamp_signal,rRation);
+  //  arrayDump("resam",resamp_signal,RXSTRIPE,true);
+  if(im==0)
+    {
+      msleep(10);
+      return;
+    }
+  demodulatorPtr->demodulate(resamp_signal,im);
+  ce2 = new displayDRMInfoEvent;
+  ce2->waitFor(&done);
+  QApplication::postEvent(dispatcherPtr, ce2);
+  while(!done) { usleep(10);}
+}
diff --git a/qsstv/rxfunctions.h b/qsstv/rxfunctions.h
new file mode 100644
index 0000000..599718a
--- /dev/null
+++ b/qsstv/rxfunctions.h
@@ -0,0 +1,74 @@
+#ifndef RXFUNCTIONS_H
+#define RXFUNCTIONS_H
+
+#include <QThread>
+#include "qsstvdefs.h"
+#include "sstv/syncprocessor.h"
+#include "utils/buffermanag.h"
+#include "drmrx/drmdefs.h"
+#include "drmrx/drmdatainput.h"
+#include "drmrx/sourcedecoder.h"
+
+
+class filter;
+class downsampleFilter;
+class modeBase;
+class demodulator;
+
+
+class rxFunctions : public QThread
+{
+  Q_OBJECT
+public:
+  enum erxState {RXIDLE,RXRUNNING,RXINIT};
+  enum eSSTVState {HUNTING,SLANTADJUST,PROCESSING,RESTART,SYNCLOST,END,WAIT};
+  explicit rxFunctions(QObject *parent = 0);
+  ~rxFunctions();
+  void init();
+  void run();
+  void stopAndWait();
+  void retraceVertical(void);
+  void startRX();
+  QString getModeString (void){ return lastUsedMode;}
+  bool saveOK();
+#ifndef QT_NO_DEBUG
+  unsigned int setOffset(unsigned int offset, bool ask);
+#endif
+signals:
+  
+public slots:
+
+private:
+  downsampleFilter *downSampleFilter;
+  filter *rxFilter;
+  filter *syncFilter;
+  unsigned int sampleCounter;
+  void setFilters(int index);
+  void setState(eSSTVState st);
+  void getData();
+  void processSSTV();
+  bool create(esstvMode m,DSPFLOAT clock);
+
+  void runDRM();
+
+  unsigned int bufferCounter;
+  syncProcessor syncProc;
+  erxState rxState;
+  eSSTVState SSTVState;
+  modeBase *currentMode;
+  bool abort;
+  QString lastUsedMode;
+  unsigned long syncPosition;
+  buffer<DSPFLOAT,22> rxHoldingBuffer;  // 2^22= 4194304, divided by samplingrate 12000 gives 349 seconds buffering
+  buffer<int,22> demodBuffer;  // 2^22= 4194304, divided by samplingrate 12000 gives 349 seconds buffering
+  unsigned int xOffset;
+  // DRM
+  float rRation;
+  float resamp_signal[2 * DRMBUFSIZE];
+  int n,im;
+  drmDataInput *dataInputPtr;
+  short int tempBuf[SAMPLINGSTRIPE];
+
+};
+
+#endif // RXFUNCTIONS_H
diff --git a/qsstv/rxwidget.cpp b/qsstv/rxwidget.cpp
new file mode 100644
index 0000000..16d9a06
--- /dev/null
+++ b/qsstv/rxwidget.cpp
@@ -0,0 +1,260 @@
+#include "rxwidget.h"
+#include "txwidget.h"
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+#include "ui_rxwidget.h"
+#include "sstv/sstvparam.h"
+#include "dsp/filterparam.h"
+#include "sound/soundio.h"
+#include "configparams.h"
+#include "dispatcher.h"
+#include "mainwindow.h"
+
+
+rxWidget *rxWidgetPtr;
+
+spectrumWidget *rxWidget::fftDisplayPtr()
+{
+  return ui->spectrumFrame;
+}
+
+vuMeter *rxWidget::vMeterPtr()
+{
+   return ui->vuWidget;
+}
+
+vuMeter *rxWidget::sMeterPtr()
+{
+   return ui->syncWidget;
+}
+
+rxWidget::rxWidget(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::rxWidget)
+{
+  int i;
+  ui->setupUi(this);
+  rxFunctionsPtr=new rxFunctions();
+  ui->syncWidget->setHorizontal(false);
+  ui->syncWidget->setColors(Qt::red,QColor(255,165,0),Qt::green);
+  ui->syncWidget->setMaximum(10.);
+  ui->syncWidget->setValue(0.);
+  ui->syncWidget->setLabelText("S");
+
+  ui->vuWidget->setHorizontal(true);
+  ui->vuWidget->setLabelText("V");
+  ui->vuWidget->setColors(QColor(255,50,0),Qt::green,Qt::red);
+  ui->vuWidget->setMaximum(10.);
+  ui->vuWidget->setValue(0.);
+  imageViewerPtr=ui->imageFrame;
+  imageViewerPtr->createImage(QSize(320,256),QColor(0,0,128));
+  imageViewerPtr->setType(imageViewer::RXIMG);
+
+  ui->sstvModeComboBox->addItem("Auto");
+  for(i=0;i<NUMSSTVMODES-1;i++)
+    {
+      ui->sstvModeComboBox->addItem(getSSTVModeNameLong((esstvMode)i));
+    }
+  for(i=0;i<NUMRXFILTERS;i++)
+    {
+      ui->filterComboBox->addItem(filterStruct[i].filterName);
+    }
+
+  for(i=0;i<NUMSENSITIVITIES;i++)
+    {
+      ui->squelchComboBox->addItem(squelchStr[i]);
+    }
+
+  connect(ui->startToolButton, SIGNAL(clicked()),SLOT(slotStart()));
+  connect(ui->stopToolButton, SIGNAL(clicked()),SLOT(slotStop()));
+  connect(ui->autoSlantAdjustCheckBox,SIGNAL(clicked()),SLOT(slotGetParams()));
+  connect(ui->autoSaveCheckBox,SIGNAL(clicked()),SLOT(slotGetParams()));
+  connect(ui->saveToolButton, SIGNAL(clicked()),SLOT(slotSaveImage()));
+  connect(ui->squelchComboBox,SIGNAL(currentIndexChanged(int)),SLOT(slotGetParams()));
+  connect(ui->settingsTableWidget,SIGNAL(currentChanged(int)),this, SLOT(slotTransmissionMode(int)));
+}
+
+rxWidget::~rxWidget()
+{
+  rxFunctionsPtr->stopAndWait();
+  delete ui;
+}
+
+void rxWidget::init()
+{
+  splashStr+=QString( "Setting up RX" ).rightJustified(25,' ')+"\n";
+  splashPtr->showMessage ( splashStr ,Qt::AlignLeft,Qt::white);
+  qApp->processEvents();
+  readSettings();
+  rxFunctionsPtr->start();
+}
+
+
+void rxWidget::slotStart()
+{
+  getParams();
+  dispatcherPtr->startRX(true);
+}
+
+void rxWidget::slotStop()
+{
+  dispatcherPtr->startRX(false);
+}
+
+void rxWidget::start(bool st)
+{
+  if(st)
+    {
+      rxFunctionsPtr->stopAndWait();
+      soundIOPtr->startCapture();
+      rxFunctionsPtr->startRX();
+      addToLog("starting rxfunction run",LOGRXMAIN);
+    }
+  else
+    {
+      soundIOPtr->idle();
+      writeSettings();
+      ui->spectrumFrame->writeSettings();
+      rxFunctionsPtr->stopAndWait();
+    }
+}
+
+
+void rxWidget::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("RX");
+  useVIS=qSettings.value("useVIS",false).toBool();
+  autoSlantAdjust=qSettings.value("autoSlantAdjust",false).toBool();
+  autoSave=qSettings.value("autoSave",true).toBool();
+  squelch=qSettings.value("squelch",1).toInt();
+  filterIndex=(qSettings.value("filterIndex",0)).toInt();
+  setParams();
+  qSettings.endGroup();
+}
+
+void rxWidget::writeSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("RX");
+  getParams();
+  qSettings.setValue( "useVIS",useVIS);
+  qSettings.setValue( "autoSlantAdjust",autoSlantAdjust);
+  qSettings.setValue( "autoSave",autoSave);
+  qSettings.setValue( "squelch",squelch);
+  qSettings.setValue( "filterIndex", filterIndex);
+  qSettings.endGroup();
+}
+
+void rxWidget::getParams()
+{
+  getValue(useVIS,ui->useVISCheckBox);
+  getValue(autoSlantAdjust,ui->autoSlantAdjustCheckBox);
+  getValue(autoSave,ui->autoSaveCheckBox);
+  getIndex(squelch,ui->squelchComboBox);
+  getIndex(filterIndex,ui->filterComboBox);
+  if(filterIndex<0) filterIndex=0;
+}
+
+void rxWidget::setParams()
+{
+  setValue(useVIS,ui->useVISCheckBox);
+  setValue(autoSlantAdjust,ui->autoSlantAdjustCheckBox);
+  setValue(autoSave,ui->autoSaveCheckBox);
+  setIndex(squelch,ui->squelchComboBox);
+  setIndex(filterIndex,ui->filterComboBox);
+}
+
+void rxWidget::setSSTVStatusText(QString txt)
+{
+  ui->sstvStatusLineEdit->setText(txt);
+}
+
+void rxWidget::setDRMStatusText(QString txt)
+{
+  ui->drmStatusLineEdit->setText(txt);
+}
+
+
+void  rxWidget::setFilterIndex(int index)
+{
+
+  ui->filterComboBox->setCurrentIndex(index);
+
+}
+
+int rxWidget::getFilterIndex()
+{
+  return ui->filterComboBox->currentIndex();
+}
+
+void  rxWidget::slotGetParams()
+{
+  getParams();
+}
+
+void rxWidget::setSettingsTab()
+{
+
+  int i;
+  if((transmissionModeIndex>=0)&&(transmissionModeIndex<NOMODE))
+    {
+      for(i=0;i<NOMODE;i++)
+        {
+          if(i!=transmissionModeIndex) ui->settingsTableWidget->widget(i)->setEnabled(false);
+        }
+      ui->settingsTableWidget->widget(transmissionModeIndex)->setEnabled(true);
+      ui->settingsTableWidget->setCurrentIndex(transmissionModeIndex);
+    }
+  if(transmissionModeIndex==DRM)
+    {
+      ui->syncWidget->setColors(QColor(0,90,0),QColor(0,190,0),Qt::green);
+      ui->syncWidget->setMaximum(25.);
+      ui->syncWidget->setMinimum(5.);
+      ui->syncWidget->setValue(0.);
+      ui->vuWidget->setColors(QColor(255,50,0),Qt::green,Qt::red);
+      ui->vuWidget->setMaximum(40.);
+      ui->vuWidget->setMinimum(18.);
+      ui->vuWidget->setValue(0.);
+      ui->spectrumFrame->displaySettings(true,true);
+    }
+  else
+    {
+      ui->syncWidget->setColors(Qt::red,QColor(255,165,0),Qt::green);
+      ui->syncWidget->setMaximum(10.);
+      ui->syncWidget->setMinimum(0.);
+      ui->syncWidget->setValue(0.);
+      ui->vuWidget->setColors(QColor(255,50,0),Qt::green,Qt::red);
+      ui->vuWidget->setMaximum(10.);
+      ui->vuWidget->setMinimum(0.);
+      ui->vuWidget->setValue(0.);
+      ui->spectrumFrame->displaySettings(false,false);
+    }
+}
+
+void rxWidget::slotTransmissionMode(int rxtxMode)
+{
+  transmissionModeIndex=(etransmissionMode)rxtxMode;
+  start(false);
+  setSettingsTab();
+  txWidgetPtr->setSettingsTab();
+  if(transmissionModeIndex==DRM)
+    {
+      mainWindowPtr->setBSRPushButton(true);
+    }
+  else
+    {
+      mainWindowPtr->setBSRPushButton(true);
+    }
+   start(true);
+}
+
+void rxWidget::slotSaveImage()
+{
+    dirDialog  dd(this);
+    QString filename;
+    filename=dd.saveFileName(rxImagesPath,"*",defaultImageFormat);
+    if(filename.isEmpty()) return;
+    imageViewerPtr->save(filename,defaultImageFormat,true);
+}
+
diff --git a/qsstv/rxwidget.h b/qsstv/rxwidget.h
new file mode 100644
index 0000000..35727f8
--- /dev/null
+++ b/qsstv/rxwidget.h
@@ -0,0 +1,62 @@
+#ifndef RXWIDGET_H
+#define RXWIDGET_H
+
+#include <QWidget>
+#include <QFrame>
+#include "rxfunctions.h"
+#include "ui_rxwidget.h"
+
+
+class imageViewer;
+class spectrumWidget;
+class vuMeter;
+
+
+namespace Ui {
+  class rxWidget;
+  }
+
+class rxWidget : public QWidget
+{
+  Q_OBJECT
+  
+public:
+  explicit rxWidget(QWidget *parent = 0);
+  ~rxWidget();
+  void init();
+  void start(bool st);
+  void readSettings();
+  void writeSettings();
+  void setFilterIndex(int index);
+  int getFilterIndex();
+  void setSettingsTab();
+  rxFunctions *functionsPtr() {return rxFunctionsPtr;}
+  imageViewer *getImageViewerPtr(){ return imageViewerPtr;}
+  spectrumWidget *fftDisplayPtr();
+  vuMeter *vMeterPtr();
+  vuMeter *sMeterPtr();
+  void setSSTVStatusText(QString txt);
+  void setDRMStatusText(QString txt);
+//  drmPSDFrame *psdWdg() {return ui->drmPSDWidget;}
+  drmConstellationFrame *mscWdg() {return ui->drmMSCWidget;}
+  drmConstellationFrame *facWdg() {return ui->drmFACWidget;}
+//  drmPSDFrame *psdWdg() {return ui->drmPSDWidget;}
+  drmStatusFrame *statusWdg() {return ui->drmStatusWidget;}
+
+private slots:
+  void slotStart();
+  void slotStop();
+  void slotGetParams();
+  void slotTransmissionMode(int rxtxMode);
+  void slotSaveImage();
+
+private:
+  Ui::rxWidget *ui;
+  void getParams();
+  void setParams();
+  rxFunctions *rxFunctionsPtr;
+  imageViewer *imageViewerPtr;
+};
+
+extern rxWidget *rxWidgetPtr;
+#endif // RXWIDGET_H
diff --git a/qsstv/rxwidget.ui b/qsstv/rxwidget.ui
new file mode 100644
index 0000000..af0beea
--- /dev/null
+++ b/qsstv/rxwidget.ui
@@ -0,0 +1,877 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>rxWidget</class>
+ <widget class="QWidget" name="rxWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>914</width>
+    <height>494</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <family>Ubuntu Mono</family>
+    <pointsize>9</pointsize>
+    <weight>50</weight>
+    <italic>false</italic>
+    <bold>false</bold>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0">
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_3">
+       <item>
+        <widget class="QToolButton" name="startToolButton">
+         <property name="toolTip">
+          <string>Start receiver</string>
+         </property>
+         <property name="statusTip">
+          <string>Status Start receiver</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/start.png</normaloff>:/icons/start.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="stopToolButton">
+         <property name="toolTip">
+          <string>Stop receiver</string>
+         </property>
+         <property name="statusTip">
+          <string>Stop receiver</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/stop.png</normaloff>:/icons/stop.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="resyncToolButton">
+         <property name="toolTip">
+          <string>Restart receiver</string>
+         </property>
+         <property name="statusTip">
+          <string>Restart receiver</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/replay.png</normaloff>:/icons/replay.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="saveToolButton">
+         <property name="toolTip">
+          <string>Save image</string>
+         </property>
+         <property name="statusTip">
+          <string>Save Image</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/filesave.png</normaloff>:/icons/filesave.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="imageViewer" name="imageFrame" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>1</horstretch>
+         <verstretch>1</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>580</width>
+         <height>360</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>1200</width>
+         <height>800</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_5">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <item>
+      <widget class="vuMeter" name="syncWidget" native="true">
+       <property name="minimumSize">
+        <size>
+         <width>18</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>18</width>
+         <height>16777215</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="vuMeter" name="vuWidget" native="true">
+       <property name="minimumSize">
+        <size>
+         <width>18</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>18</width>
+         <height>16777215</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_6" stretch="0,1">
+     <property name="spacing">
+      <number>1</number>
+     </property>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QTabWidget" name="settingsTableWidget">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="currentIndex">
+          <number>0</number>
+         </property>
+         <widget class="QWidget" name="sstvTab">
+          <attribute name="title">
+           <string>SSTV</string>
+          </attribute>
+          <layout class="QVBoxLayout" name="verticalLayout_2">
+           <property name="spacing">
+            <number>1</number>
+           </property>
+           <property name="leftMargin">
+            <number>1</number>
+           </property>
+           <property name="topMargin">
+            <number>1</number>
+           </property>
+           <property name="rightMargin">
+            <number>1</number>
+           </property>
+           <property name="bottomMargin">
+            <number>1</number>
+           </property>
+           <item>
+            <layout class="QGridLayout" name="gridLayout">
+             <property name="spacing">
+              <number>2</number>
+             </property>
+             <item row="0" column="0">
+              <widget class="QCheckBox" name="useVISCheckBox">
+               <property name="toolTip">
+                <string>Use VIS Code to start</string>
+               </property>
+               <property name="statusTip">
+                <string>Use VIS Code to start</string>
+               </property>
+               <property name="text">
+                <string>Use VIS</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="1">
+              <widget class="QLabel" name="filterLabel">
+               <property name="text">
+                <string>Filter</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="2">
+              <widget class="QComboBox" name="filterComboBox">
+               <property name="minimumSize">
+                <size>
+                 <width>110</width>
+                 <height>0</height>
+                </size>
+               </property>
+               <property name="toolTip">
+                <string>Reception filter</string>
+               </property>
+               <property name="statusTip">
+                <string>Reception filter</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QCheckBox" name="autoSlantAdjustCheckBox">
+               <property name="toolTip">
+                <string>Correct slant automatically</string>
+               </property>
+               <property name="statusTip">
+                <string>Correct slant automatically</string>
+               </property>
+               <property name="text">
+                <string>Auto Slant</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="1">
+              <widget class="QLabel" name="squelchLabel">
+               <property name="toolTip">
+                <string>Sensitivity - Higher is more sensitive</string>
+               </property>
+               <property name="statusTip">
+                <string>Sensitivity - Higher is more sensitive</string>
+               </property>
+               <property name="text">
+                <string>Sensitivity</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QCheckBox" name="autoSaveCheckBox">
+               <property name="toolTip">
+                <string>Save image when complete</string>
+               </property>
+               <property name="statusTip">
+                <string>Save image when complete</string>
+               </property>
+               <property name="text">
+                <string>Autosave</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="1">
+              <widget class="QLabel" name="label">
+               <property name="text">
+                <string>Mode</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="2">
+              <widget class="QComboBox" name="sstvModeComboBox"/>
+             </item>
+             <item row="1" column="2">
+              <widget class="QComboBox" name="squelchComboBox"/>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <widget class="QLabel" name="sstvStatusLineEdit">
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>25</height>
+              </size>
+             </property>
+             <property name="frameShape">
+              <enum>QFrame::Panel</enum>
+             </property>
+             <property name="frameShadow">
+              <enum>QFrame::Sunken</enum>
+             </property>
+             <property name="lineWidth">
+              <number>3</number>
+             </property>
+             <property name="text">
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <spacer name="verticalSpacer">
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>17</width>
+               <height>0</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+          </layout>
+         </widget>
+         <widget class="QWidget" name="drmTab">
+          <attribute name="title">
+           <string>DRM</string>
+          </attribute>
+          <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,1">
+           <property name="spacing">
+            <number>1</number>
+           </property>
+           <property name="leftMargin">
+            <number>1</number>
+           </property>
+           <property name="topMargin">
+            <number>1</number>
+           </property>
+           <property name="rightMargin">
+            <number>1</number>
+           </property>
+           <property name="bottomMargin">
+            <number>1</number>
+           </property>
+           <item>
+            <layout class="QVBoxLayout" name="drmLayout1" stretch="1">
+             <property name="spacing">
+              <number>1</number>
+             </property>
+             <item>
+              <widget class="drmStatusFrame" name="drmStatusWidget">
+               <property name="frameShape">
+                <enum>QFrame::StyledPanel</enum>
+               </property>
+               <property name="frameShadow">
+                <enum>QFrame::Raised</enum>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <widget class="QLabel" name="drmStatusLineEdit">
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>20</height>
+              </size>
+             </property>
+             <property name="font">
+              <font>
+               <pointsize>8</pointsize>
+              </font>
+             </property>
+             <property name="frameShape">
+              <enum>QFrame::Panel</enum>
+             </property>
+             <property name="frameShadow">
+              <enum>QFrame::Sunken</enum>
+             </property>
+             <property name="lineWidth">
+              <number>2</number>
+             </property>
+             <property name="text">
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_2">
+             <item>
+              <layout class="QVBoxLayout" name="drmLayout4" stretch="0,1">
+               <property name="spacing">
+                <number>1</number>
+               </property>
+               <item>
+                <widget class="QLabel" name="drmFACLabel">
+                 <property name="text">
+                  <string>FAC</string>
+                 </property>
+                 <property name="alignment">
+                  <set>Qt::AlignCenter</set>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="drmConstellationFrame" name="drmFACWidget">
+                 <property name="minimumSize">
+                  <size>
+                   <width>0</width>
+                   <height>80</height>
+                  </size>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>16777215</width>
+                   <height>120</height>
+                  </size>
+                 </property>
+                 <property name="palette">
+                  <palette>
+                   <active>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>60</red>
+                       <green>60</green>
+                       <blue>60</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </active>
+                   <inactive>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>60</red>
+                       <green>60</green>
+                       <blue>60</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </inactive>
+                   <disabled>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </disabled>
+                  </palette>
+                 </property>
+                 <property name="frameShape">
+                  <enum>QFrame::Panel</enum>
+                 </property>
+                 <property name="frameShadow">
+                  <enum>QFrame::Sunken</enum>
+                 </property>
+                 <property name="lineWidth">
+                  <number>3</number>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <layout class="QVBoxLayout" name="drmLayout3" stretch="0,1">
+               <property name="spacing">
+                <number>1</number>
+               </property>
+               <item>
+                <widget class="QLabel" name="drmMSCLabel">
+                 <property name="text">
+                  <string>MSC</string>
+                 </property>
+                 <property name="alignment">
+                  <set>Qt::AlignCenter</set>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="drmConstellationFrame" name="drmMSCWidget">
+                 <property name="minimumSize">
+                  <size>
+                   <width>0</width>
+                   <height>80</height>
+                  </size>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>16777215</width>
+                   <height>120</height>
+                  </size>
+                 </property>
+                 <property name="palette">
+                  <palette>
+                   <active>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>60</red>
+                       <green>60</green>
+                       <blue>60</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </active>
+                   <inactive>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>60</red>
+                       <green>60</green>
+                       <blue>60</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>76</red>
+                       <green>76</green>
+                       <blue>76</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </inactive>
+                   <disabled>
+                    <colorrole role="WindowText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Light">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>197</red>
+                       <green>197</green>
+                       <blue>197</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Dark">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="Text">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                    <colorrole role="ButtonText">
+                     <brush brushstyle="SolidPattern">
+                      <color alpha="255">
+                       <red>0</red>
+                       <green>0</green>
+                       <blue>0</blue>
+                      </color>
+                     </brush>
+                    </colorrole>
+                   </disabled>
+                  </palette>
+                 </property>
+                 <property name="frameShape">
+                  <enum>QFrame::Panel</enum>
+                 </property>
+                 <property name="frameShadow">
+                  <enum>QFrame::Sunken</enum>
+                 </property>
+                 <property name="lineWidth">
+                  <number>3</number>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </widget>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="spectrumWidget" name="spectrumFrame" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>1000</width>
+         <height>1000</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>imageViewer</class>
+   <extends>QWidget</extends>
+   <header>widgets/imageviewer.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>vuMeter</class>
+   <extends>QWidget</extends>
+   <header>widgets/vumeter.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>spectrumWidget</class>
+   <extends>QWidget</extends>
+   <header>widgets/spectrumwidget.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>drmStatusFrame</class>
+   <extends>QFrame</extends>
+   <header>drmrx/drmstatusframe.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>drmConstellationFrame</class>
+   <extends>QFrame</extends>
+   <header>drmrx/drmconstellationframe.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="qsstv.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/qsstv/scope/plotform.ui b/qsstv/scope/plotform.ui
new file mode 100644
index 0000000..7078b2d
--- /dev/null
+++ b/qsstv/scope/plotform.ui
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>plotForm</class>
+ <widget class="QWidget" name="plotForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>866</width>
+    <height>524</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Scope</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="margin">
+    <number>1</number>
+   </property>
+   <item>
+    <widget class="QwtPlot" name="plotWindow">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>10</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <item>
+      <layout class="QVBoxLayout">
+       <item>
+        <widget class="QLabel" name="marker1Label">
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Box</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>2</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="marker2Label">
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Box</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>2</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="marker3Label">
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::Box</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>2</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="spacer1">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>74</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="0" column="0">
+        <widget class="QPushButton" name="previousButton">
+         <property name="font">
+          <font>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string><</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QPushButton" name="nextButton">
+         <property name="font">
+          <font>
+           <weight>75</weight>
+           <bold>true</bold>
+          </font>
+         </property>
+         <property name="text">
+          <string>></string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0" colspan="2">
+        <widget class="QPushButton" name="samplesPushButton">
+         <property name="text">
+          <string>Samples</string>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="spacer1_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>73</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Position</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QwtWheel" name="offsetWheel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>1</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>150</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Zoom</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QwtWheel" name="rangeWheel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>10</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>150</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>QwtPlot</class>
+   <extends>QFrame</extends>
+   <header>qwt_plot.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>QwtWheel</class>
+   <extends>QWidget</extends>
+   <header>qwt_wheel.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/scope/scopeoffset.cpp b/qsstv/scope/scopeoffset.cpp
new file mode 100644
index 0000000..6990e46
--- /dev/null
+++ b/qsstv/scope/scopeoffset.cpp
@@ -0,0 +1,27 @@
+#include "scopeoffset.h"
+#include "ui_scopeoffset.h"
+
+scopeOffset::scopeOffset(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::scopeOffset)
+{
+  ui->setupUi(this);
+}
+
+scopeOffset::~scopeOffset()
+{
+  delete ui;
+}
+
+
+void scopeOffset::setOffset(unsigned int offset)
+{
+  ui->spinBox->setValue(offset);
+}
+
+unsigned int scopeOffset::getOffset()
+{
+  return ui->spinBox->value();
+}
+
+
diff --git a/qsstv/scope/scopeoffset.h b/qsstv/scope/scopeoffset.h
new file mode 100644
index 0000000..1321a5a
--- /dev/null
+++ b/qsstv/scope/scopeoffset.h
@@ -0,0 +1,24 @@
+#ifndef SCOPEOFFSET_H
+#define SCOPEOFFSET_H
+
+#include <QDialog>
+
+namespace Ui {
+  class scopeOffset;
+  }
+
+class scopeOffset : public QDialog
+{
+  Q_OBJECT
+  
+public:
+  explicit scopeOffset(QWidget *parent = 0);
+  ~scopeOffset();
+  void setOffset(unsigned int offset);
+  unsigned int getOffset();
+  
+private:
+  Ui::scopeOffset *ui;
+};
+
+#endif // SCOPEOFFSET_H
diff --git a/qsstv/scope/scopeoffset.ui b/qsstv/scope/scopeoffset.ui
new file mode 100644
index 0000000..b313101
--- /dev/null
+++ b/qsstv/scope/scopeoffset.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>scopeOffset</class>
+ <widget class="QDialog" name="scopeOffset">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>417</width>
+    <height>149</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Scope Sample Offset</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Select offset</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="spinBox">
+       <property name="maximum">
+        <number>10000</number>
+       </property>
+       <property name="singleStep">
+        <number>10</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>K samples</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>scopeOffset</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>scopeOffset</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/scope/scopeplot.cpp b/qsstv/scope/scopeplot.cpp
new file mode 100644
index 0000000..7115371
--- /dev/null
+++ b/qsstv/scope/scopeplot.cpp
@@ -0,0 +1,548 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Johan Maes                                      *
+ *   on4qz at telenet.be                                                      *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "scopeplot.h"
+#include "qwt_plot.h"
+#include "qwt_plot_curve.h"
+#include "qwt_plot_grid.h"
+#include "qwt_plot_marker.h"
+#include "qwt_plot_canvas.h"
+#include "qwt_plot_picker.h"
+#include "qwt_picker_machine.h"
+#include "qwt_legend_label.h"
+#include "qwt_symbol.h"
+#include <QMessageBox>
+#include <QMenu>
+#include <QMenuBar>
+
+scopePlot::scopePlot(QString title, QWidget *parent) : QMainWindow(parent)
+{
+  wd=new QWidget(this);
+  ui.setupUi(wd);
+  mrk1=0;
+  mrk2=0;
+  initActions();
+  initMenuBar();
+  initToolBar();
+  initStatusBar();
+  curve1 = new QwtPlotCurve("Curve 1");
+  curve2 = new QwtPlotCurve("Curve 2");
+  curve3 = new QwtPlotCurve("Curve 3");
+  curve4 = new QwtPlotCurve("Curve 4");
+
+  xScaleMul=1.;
+  xPrimeScaleMul=1.;
+  xAltScaleMul=1;
+  xAxisTitle="Samples";
+  xAltAxisTitle="Time (s)";
+  toggleMarker=false;
+
+  showCrv1=true;
+  showCrv2=true;
+  showCrv3=true;
+  showCrv4=true;
+  init(title);
+}
+
+scopePlot::~scopePlot()
+{
+  delete curve1;
+  delete curve2;
+  delete curve3;
+  delete curve4;
+  delete toolsMenu;
+}
+
+void scopePlot::setXScaleMultiplier(double mul)
+{
+  xPrimeScaleMul=mul;
+  xScaleMul=mul;
+
+}
+
+void scopePlot::setAlternativeScaleMultiplier(double mul)
+{
+  xAltScaleMul=mul;
+}
+
+void scopePlot::initActions()
+{
+
+
+  zoomAction = new QAction(QIcon(":/icons/viewmagplus.png"), tr("&Zoom"), this);
+  zoomAction->setCheckable(true);
+  zoomAction->setStatusTip(tr("Zoom in or out"));
+  connect(zoomAction, SIGNAL(toggled(bool)), this, SLOT(slotZoom(bool)));
+
+}
+
+
+void scopePlot::initMenuBar()
+{
+  toolsMenu=new QMenu(tr("&Zoom"));
+  toolsMenu->addAction(zoomAction);
+  menuBar()->addMenu(toolsMenu);
+}
+
+void scopePlot::initToolBar()
+{
+}
+
+void scopePlot::initStatusBar()
+{
+}
+
+
+void scopePlot::init(QString title)
+{
+  
+  setup=true;
+  setCentralWidget(wd);
+  connect(ui.offsetWheel, SIGNAL(valueChanged(double)),SLOT(slotOffsetChanged(double )));
+  connect(ui.rangeWheel, SIGNAL(valueChanged(double)), SLOT(slotRangeChanged(double )));
+  connect(ui.samplesPushButton, SIGNAL(clicked()), this, SLOT(slotSamplesButtton()));
+  plW=ui.plotWindow;
+  plW->setTitle(title);
+  plW->setCanvasBackground(Qt::darkBlue);
+  curve1->attach(plW);
+  curve2->attach(plW);
+  curve3->attach(plW);
+  curve4->attach(plW);
+  plW->setAxisTitle(QwtPlot::xBottom,xAxisTitle);
+  plW->setAxisScale(QwtPlot::xBottom, 0, 100);
+  plW->setAxisTitle(QwtPlot::yLeft, "Values");
+  plW->setAxisScale(QwtPlot::yLeft, -1.5, 1.5);
+
+  QwtPlotGrid *grid = new QwtPlotGrid;
+  grid->enableXMin(true);
+  grid->setMajorPen(QPen(Qt::white, 0, Qt::DotLine));
+  grid->setMinorPen(QPen(Qt::gray, 0 , Qt::DotLine));
+  grid->attach(plW);
+  QwtText m1("M1");
+  m1.setColor(QColor(Qt::white));
+  marker1=new QwtPlotMarker();
+  marker1->setValue(0.0, 0.0);
+  marker1->setLabel(m1);
+  marker1->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
+  marker1->setLinePen(QPen(QColor(200,150,0), 0, Qt::DashDotLine));
+  marker1->setSymbol( new QwtSymbol(QwtSymbol::Diamond,QColor(Qt::green), QColor(Qt::green), QSize(7,7)));
+  //	marker1->hide();
+  marker1->attach(plW);
+
+
+
+  QwtText m2("M2");
+  m2.setColor(QColor(Qt::white));
+  marker2=new QwtPlotMarker();
+  marker2->setValue(0.0, 0.0);
+  marker2->setLabel(m2);
+  marker2->setLabelAlignment(Qt::AlignLeft | Qt::AlignTop);
+  marker2->setLinePen(QPen(QColor(200,150,0), 0, Qt::DashDotLine));
+  marker2->setSymbol( new QwtSymbol(QwtSymbol::Diamond,QColor(Qt::yellow), QColor(Qt::yellow), QSize(7,7)));
+  //	marker2->hide();
+  marker2->attach(plW);
+
+
+
+  legend = new QwtLegend;
+  legend->setFrameStyle(QFrame::Box|QFrame::Sunken);
+  legend->setDefaultItemMode(QwtLegendData::Checkable);
+  QPalette pal(legend->palette());
+  pal.setColor(QPalette::Window,Qt::darkBlue);
+  pal.setColor(QPalette::WindowText,Qt::white);
+  pal.setColor(QPalette::Text,Qt::black);
+  legend->setPalette(pal);
+  plW->insertLegend(legend, QwtPlot::BottomLegend);
+
+  picker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,QwtPlotPicker::CrossRubberBand, QwtPicker::AlwaysOn, plW->canvas());
+  picker->setStateMachine(new QwtPickerDragPointMachine());
+  picker->setRubberBandPen(QColor(Qt::green));
+  picker->setRubberBand(QwtPicker::CrossRubberBand);
+  picker->setTrackerPen(QColor(Qt::white));
+  picker->setEnabled(true);
+  plW->replot();
+
+  QwtPlotItemList items = plW->itemList( QwtPlotItem::Rtti_PlotCurve);
+  for ( int i = 0; i < items.size(); i++ )
+    {
+      const QVariant itemInfo = plW->itemToInfo( items[i] );
+
+      QwtLegendLabel *legendLabel =qobject_cast<QwtLegendLabel *>( legend->legendWidget( itemInfo ) );
+      if (legendLabel )
+        {
+          legendLabel->setChecked( true );
+        }
+
+      items[i]->setVisible( true );
+    }
+  connect(picker, SIGNAL(moved(const QPointF  &)),SLOT(pickerMoved(const QPointF &)));
+  connect(picker, SIGNAL(selected(const QPointF  &)), SLOT(pickerSelected(const QPointF  &)));
+  connect(legend, SIGNAL(checked(const QVariant &, bool ,int)),SLOT(legendClicked(const QVariant &,bool)));
+  connect(ui.nextButton, SIGNAL(clicked()),SLOT(slotNext()));
+  connect(ui.previousButton, SIGNAL(clicked()),SLOT(slotPrevious()));
+  plW->setAxisTitle(QwtPlot::xBottom,xAxisTitle);
+  xOffset=0;
+}
+
+
+void scopePlot::setCurveOn(int i,bool b)
+{
+  QwtPlotItemList items = plW->itemList( QwtPlotItem::Rtti_PlotCurve);
+  if(i>=items.size()) return;
+  const QVariant itemInfo = plW->itemToInfo( items[i] );
+  QwtLegendLabel *legendLabel =qobject_cast<QwtLegendLabel *>( legend->legendWidget( itemInfo ) );
+  if (legendLabel )
+    {
+      legendLabel->setChecked(b);
+    }
+
+  items[i]->setVisible( b );
+
+}
+
+void scopePlot::slotSamplesButtton()
+{
+  long i;
+  if (ui.samplesPushButton->isChecked())
+    {
+      ui.samplesPushButton->setText(xAltAxisTitle);
+      plW->setAxisTitle(QwtPlot::xBottom,xAltAxisTitle);
+      xScaleMul=xAltScaleMul;
+      ui.offsetWheel->setSingleStep(10*xScaleMul);
+      ui.rangeWheel->setSingleStep(10*xScaleMul);
+    }
+  else
+    {
+      ui.samplesPushButton->setText(xAxisTitle);
+      plW->setAxisTitle(QwtPlot::xBottom,xAxisTitle);
+      xScaleMul=xPrimeScaleMul;
+      ui.offsetWheel->setSingleStep(1);
+
+      ui.rangeWheel->setSingleStep(1);
+
+    }
+  for(i=0;i<x.size();i++)
+    {
+      x[i]=(double)(i+xOffset)*xScaleMul;
+//      x[i]=(double)i*xScaleMul;
+    }
+
+  ui.offsetWheel->setPageStepCount(10);
+  ui.rangeWheel->setPageStepCount(10);
+  if(x.size()==c1.size()) curve1->setSamples(x.data(), c1.data(), x.size());
+  if(x.size()==c3.size()) curve2->setSamples(x.data(), c3.data(), x.size());
+  if(x.size()==c3.size()) curve3->setSamples(x.data(), c3.data(), x.size());
+  if(x.size()==c4.size()) curve4->setSamples(x.data(), c4.data(), x.size());
+  setupWheels(x.size());
+  plW->replot();
+}
+
+
+
+
+
+
+
+void scopePlot::plot1DUpdate(double *data)
+{
+  for (long i = 0; i < c1.size(); i++)
+    {
+      c1[i]=data[i];
+    }
+  curve1->setSamples(x.data(), c1.data(), x.size());
+  plW->replot();
+}
+
+void scopePlot::plotData(unsigned int size,
+                         short int * iData, QString curve1Name,QString yLLabel,
+                         double * dData, QString curve2Name, QString yRLabel)
+{	
+  add1(iData,size,curve1Name,yLLabel);
+  add3(dData,size,curve2Name,yRLabel);
+  show();
+}
+
+void  scopePlot::plotData(unsigned int size,
+                          double * dData1, QString curve1Name, QString yLLabel,
+                          double * dData2, QString curve2Name, QString yRLabel)
+{	
+  add1(dData1,size,curve1Name,yLLabel);
+  add3(dData2,size,curve2Name,yRLabel);
+  show();
+}
+
+
+
+void scopePlot::add1(short int *data, unsigned long len,QString name,QString yLeftLabel)
+{
+  x.resize(len);
+  c1.resize(len);
+  for (unsigned long i = 0; i < len; i++)
+    {
+
+      x[i]=(double)(i+xOffset)*xScaleMul;
+      c1[i]=(double)data[i];
+    }
+  plot1(name,yLeftLabel);
+}
+
+void scopePlot::add1(double *data, unsigned long len,QString curveName,QString yLeftLabel)
+{
+  x.resize(len);
+  c1.resize(len);
+  for (unsigned long i = 0; i < len; i++)
+    {
+      x[i]=(double)(i+xOffset)*xScaleMul;
+      c1[i]=data[i];
+    }
+  plot1(curveName,yLeftLabel);
+}
+
+
+void scopePlot::add2(double *data, unsigned long len,QString curveName)
+{
+  c2.resize(len);
+  for (unsigned long i = 0; i < len; i++)
+    {
+      c2[i]=data[i];
+    }
+  plot2(curveName);
+}
+
+void scopePlot::add3(double *data, unsigned long len,QString curveName,QString yRightLabel)
+{
+  c3.resize(len);
+  for (unsigned long i = 0; i < len; i++)
+    {
+      c3[i]=data[i];
+    }
+  plot3(curveName,yRightLabel);
+}
+
+void scopePlot::add4(double *data, unsigned long len,QString curveName)
+{
+  c4.resize(len);
+  for (unsigned long i = 0; i < len; i++)
+    {
+      c4[i]=data[i];
+    }
+  plot4(curveName);
+}
+
+void scopePlot::show()
+{
+  QMainWindow::show();
+  plW->show();
+  plW->replot();
+}
+
+void scopePlot::refresh()
+{
+
+  plW->replot();
+}
+
+
+
+void scopePlot::plot1(QString curveName,QString yLeftLabel)
+{
+  plW->setAxisTitle(QwtPlot::yLeft, yLeftLabel);
+  plW->setAxisAutoScale(QwtPlot::yLeft);
+  plW->setAxisAutoScale(QwtPlot::xBottom);
+  curve1->setTitle(curveName);
+  curve1->setPen(QPen(Qt::yellow));
+  curve1->setYAxis(QwtPlot::yLeft);
+  curve1->setSamples(x.data(), c1.data(), x.size());
+  setupWheels(x.size());
+}
+
+
+
+void scopePlot::plot2(QString curveName)
+{
+
+  // axes
+  plW->setAxisAutoScale(QwtPlot::yLeft);
+  plW->enableAxis(QwtPlot::yLeft);
+  curve2->setTitle(curveName);
+  curve2->setPen(QPen(Qt::red));
+  curve2->setYAxis(QwtPlot::yLeft);
+  curve2->setSamples(x.data(), c2.data(), x.size());
+  plW->replot();
+}
+
+void scopePlot::plot3(QString curveName,QString yRightLabel)
+{
+
+  // axes
+  plW->setAxisTitle(QwtPlot::yRight,yRightLabel);
+  plW->enableAxis(QwtPlot::yRight);
+  curve3->setTitle(curveName);
+  curve3->setPen(QPen(Qt::green));
+  curve3->setYAxis(QwtPlot::yRight);
+  curve3->setSamples(x.data(), c3.data(), x.size());
+  plW->setAxisAutoScale(QwtPlot::yRight);
+  plW->replot();
+}
+
+void scopePlot::plot4(QString curveName)
+{
+
+  // axes
+  plW->setAxisAutoScale(QwtPlot::yRight);
+  plW->enableAxis(QwtPlot::yRight);
+  curve4->setTitle(curveName);
+  curve4->setPen(QPen(Qt::white));
+  curve4->setYAxis(QwtPlot::yRight);
+  curve4->setSamples(x.data(), c4.data(), x.size());
+  plW->replot();
+}
+
+
+
+
+void scopePlot::slotZoom(bool)
+{
+}
+
+void scopePlot::slotOffsetChanged(double ioffset)
+{
+  if (setup) return;
+  if((ioffset-range/2)<startPoint)
+    {
+      ui.offsetWheel->setValue(startPoint+range/2);
+      return;
+    }
+  if((ioffset+range/2)>endPoint)
+    {
+      ui.offsetWheel->setValue(endPoint-range/2);
+      return;
+    }
+  dispCenter=ioffset;
+  plW->setAxisScale(QwtPlot::xBottom,dispCenter-range/2,dispCenter+range/2);
+  plW->replot();
+}
+
+void scopePlot::slotRangeChanged(double irange)
+{
+  if (setup) return;
+
+  if((dispCenter-irange/2)<startPoint)
+    {
+      ui.rangeWheel->setValue((dispCenter-startPoint)*1.999);
+      return;
+    }
+  if((dispCenter+irange/2)>endPoint)
+    {
+      ui.rangeWheel->setValue((endPoint-dispCenter)*1.999);
+      return;
+    }
+
+  range=irange;
+  plW->setAxisScale(QwtPlot::xBottom,dispCenter-range/2,dispCenter+range/2);
+  plW->replot();
+}
+
+void scopePlot::setupWheels(int size)
+{
+  setup=true;
+  // offset is from 0 to size-range
+  // range is from 10 to
+  if (x.size()==0)
+    {
+      QMessageBox::warning(0,"Scope Plot",
+                           "No data in Scope Plot" ,
+                           QMessageBox::Ok,0 );
+      return;
+    }
+  blockSignals(true);
+  startPoint=x[0];
+  endPoint=x[size-1];
+  range=endPoint-startPoint;
+  dispCenter=(endPoint+startPoint)/2;
+  ui.offsetWheel->setMass(0.5);
+  ui.rangeWheel->setMass(0.5);
+
+  ui.offsetWheel->setRange(startPoint, endPoint);
+  ui.offsetWheel->setTotalAngle(3600.0);
+  ui.rangeWheel->setRange(range/200., range);
+  //	range=endPoint-startPoint;
+  ui.rangeWheel->setTotalAngle(3600.0);
+  blockSignals(false);
+  ui.offsetWheel->setValue(dispCenter);
+  ui.rangeWheel->setValue(range);
+  setup=false;
+  plW->setAxisScale(QwtPlot::xBottom,dispCenter-range/2,dispCenter+range/2);
+}
+
+
+void scopePlot::pickerMoved(const QPointF &pos)
+{
+  QString info;
+  info.sprintf("x=%7g, yL=%7g, yR=%7g",
+               plW->invTransform(QwtPlot::xBottom, pos.x()),
+               plW->invTransform(QwtPlot::yLeft, pos.y()),
+               plW->invTransform(QwtPlot::yRight, pos.y())
+               );
+  // ui.positionLabel->setText(info);
+}
+
+
+void scopePlot::pickerSelected(const QPointF  &pos)
+{
+  // qDebug() << "selected" << toggleMarker;
+  if (!toggleMarker)
+    {
+      marker1->setValue(pos);
+    }
+  else
+    {
+      marker2->setValue(pos);
+    }
+  toggleMarker=!toggleMarker;
+  showMarker();
+  plW->replot();
+}
+
+
+void scopePlot::showMarker()
+{
+  QString t1,t2,t3;
+
+  t1="M1 : "+QString::number(marker1->xValue(),'g',7).rightJustified(11)+QString::number(marker1->yValue(),'g',7).rightJustified(11);
+  t2="M2 : "+QString::number(marker2->xValue(),'g',7).rightJustified(11)+QString::number(marker2->yValue(),'g',7).rightJustified(11);
+  t3="DIF: "+QString::number(marker2->xValue()-marker1->xValue(),'g',7).rightJustified(11)+QString::number(marker2->yValue()-marker1->yValue(),'g',7).rightJustified(11);
+  ui.marker1Label->setText(t1);
+  ui.marker2Label->setText(t2);
+  ui.marker3Label->setText(t3);
+}
+
+
+
+void scopePlot::legendClicked(const QVariant &itemInfo, bool on)
+{
+  QwtPlotItem *plotItem = plW->infoToItem( itemInfo );
+  if ( plotItem )
+    {
+      plotItem->setVisible( on );
+      plW->replot();
+    }
+}
+
+
diff --git a/qsstv/scope/scopeplot.h b/qsstv/scope/scopeplot.h
new file mode 100644
index 0000000..d37f517
--- /dev/null
+++ b/qsstv/scope/scopeplot.h
@@ -0,0 +1,200 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Johan Maes                                      *
+ *   on4qz at telenet.be                                                      *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef SCOPEPLOT_H
+#define SCOPEPLOT_H
+#include <QtGui>
+#include <QMainWindow>
+#include "ui_plotform.h"
+#include "qwt_plot.h"
+#include "qwt_legend.h"
+
+class QwtPlotCurve;
+class QwtPlotMarker;
+class QwtPlotPicker;
+
+
+
+/**Data  plotting in scope format
+  *@author Johan Maes -- ON4QZ 
+  */
+
+class scopePlot : public QMainWindow  {
+   Q_OBJECT
+public: 
+	scopePlot(QString title,QWidget *parent=0);
+	~scopePlot();
+  void setXScaleMultiplier(double mul);
+  void setAlternativeScaleMultiplier(double mul);
+  void setOffset(unsigned int offset) {xOffset=offset;}
+  void init(QString title=QString::null);
+
+  void add1(short int *data, unsigned long len,QString curveName,QString yLeftLabel);
+  void add1(double *data, unsigned long len,QString curveName,QString yLeftLabel);
+  void add2(double *data, unsigned long len,QString curveName);
+  void add3(double *data, unsigned long len,QString curveName,QString yRightLabel);
+  void add4(double *data, unsigned long len,QString curveName);
+  void plot1(QString name,QString yLeftLabel);
+  void plot2(QString curveName);
+  void plot3(QString curveName,QString yRightLabel);
+  void plot4(QString curveName);
+	void plotData(unsigned int size,
+                      short int * iData, QString curve1Name, QString yLLabel,
+											double * dData, QString curve2Name, QString yRLabel);
+	void plotData(unsigned int size,
+                      double * dData1, QString curve1Name, QString yLLabel,
+											double * dData2, QString curve2Name, QString yRLabel);
+											
+											
+	void show();
+	void plot1DUpdate(double *data);
+  void XYL(unsigned int i,int ix,int iy)
+    {
+      x[i]=(double)ix;
+      c1[i]=(double)iy;
+    }
+  void X(unsigned int i,int ix)
+    {
+      x[i]=(double)ix;
+      
+    }
+  void YL(unsigned int i,int iy)
+    {
+      c1[i]=(double)iy;
+    }
+  void YR(unsigned int i,int iy)
+    {
+      c3[i]=(double)iy;
+
+    }
+  void XYLYR(unsigned int i,int ix,int iyl,int iyr)
+    {
+      x[i]=(double)ix;
+      c1[i]=(double)iyl;
+      c3[i]=(double)iyr;
+    }
+
+  void XYL(unsigned int i,double ix,double iy)
+    {
+      x[i]=ix;
+      c1[i]=iy;
+    }
+    
+  void X(unsigned int i,double ix)
+    {
+      x[i]=ix;
+
+    }
+  void YL(unsigned int i,double iy)
+    {
+      c1[i]=iy;
+    }
+  void YR(unsigned int i,double iy)
+    {
+      c3[i]=iy;
+
+    }
+  void XYLYR(unsigned int i,double ix,double iyl,double iyr)
+    {
+      x[i]=ix;
+      c1[i]=iyl;
+      c3[i]=iyr;
+    }
+  void resize(unsigned long i)
+    {
+      x.resize(i);
+      c1.resize(i);
+      c3.resize(i);
+    }
+	void refresh();
+public slots:
+	void slotZoom(bool b);
+	void slotOffsetChanged(double offset);
+	void slotRangeChanged(double range);
+  void pickerMoved(const QPointF &pos);
+  void pickerSelected(const QPointF &pos);
+//  void plotMouseMoved(const QMouseEvent &e);
+//	void plotMouseReleased(const QMouseEvent &e);
+  void legendClicked(const QVariant &itemInfo, bool on);
+	void slotNext()
+		{
+			emit next();
+		}
+	void slotPrevious()
+		{
+			emit previous();
+		}
+	void slotSamplesButtton();
+  void setCurveOn(int i,bool b);
+signals:
+	void next();
+	void previous();  
+private:
+	QwtPlot *plW;
+	QToolBar *toolsToolbar;
+	QMenu *toolsMenu;
+	QAction *zoomAction;
+//	plotForm *pl;
+
+	QVector<double> x;
+  QVector<double> c1;
+  QVector<double> c2;
+	QVector<double> c3;
+  QVector<double> c4;
+	double startPoint;
+	double endPoint;
+	double dispCenter;
+
+	
+	void initActions();
+	void initMenuBar();
+	void initToolBar();
+	void initStatusBar();
+	void setupWheels(int size);
+	double xScaleMul;
+	double xPrimeScaleMul;
+	double xAltScaleMul;
+	bool setup;
+	long int mrk1,mrk2;
+	void showMarker();
+	bool toggleMarker;
+	bool showCrv1;
+	bool showCrv2;
+	bool showCrv3;
+  bool showCrv4;
+	Ui::plotForm ui;
+	QWidget *wd;
+
+	QwtPlotCurve *curve1; 
+	QwtPlotCurve *curve2;
+	QwtPlotCurve *curve3;
+  QwtPlotCurve *curve4;
+	QwtPlotMarker *marker1;
+	QwtPlotMarker *marker2;
+	QwtLegend *legend;
+	QwtPlotPicker *picker;
+  QString xAxisTitle;
+  QString xAltAxisTitle;
+	double range;
+  unsigned int xOffset;
+
+};
+
+#endif
diff --git a/qsstv/scope/scopeview.cpp b/qsstv/scope/scopeview.cpp
new file mode 100644
index 0000000..b6c510b
--- /dev/null
+++ b/qsstv/scope/scopeview.cpp
@@ -0,0 +1,285 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "scopeview.h"
+#include "utils/loggingparams.h"
+
+
+
+scopeView::scopeView(QString title) : scopePlot(title)
+{
+  xOffset=0;
+  init();
+}
+
+
+scopeView::~scopeView()
+{
+}
+
+void scopeView::init()
+{
+  int i;
+  index=0;
+  for (i=0;i<SCOPEMAXDATA;i++)
+    {
+      array1[i]=0.;
+      array2[i]=0.;
+      array3[i]=0.;
+      array4[i]=0.;
+    }
+  setCurveName("data1",SCDATA1);
+  setCurveName("data2",SCDATA2);
+  setCurveName("data3",SCDATA3);
+  setCurveName("data4",SCDATA4);
+
+}
+
+void scopeView::setOffset (int xoffset)
+{
+  xOffset=xoffset;
+  scopePlot::setOffset(xOffset);
+}
+
+void scopeView::addData(ecurve Idx,double *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+void scopeView::addData(ecurve Idx,float *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+
+void scopeView::addData(ecurve Idx, qint8 *data, unsigned int position, unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+       ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+void scopeView::addData(ecurve Idx,quint8 *data, unsigned int position, unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+       ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+
+void scopeView::addData(ecurve Idx,qint16 *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+void scopeView::addData(ecurve Idx,quint16 *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+
+void scopeView::addData(ecurve Idx,qint32 *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+void scopeView::addData(ecurve Idx,quint32 *data,unsigned int position,unsigned int len)
+{
+  unsigned int i,j;
+  double *ar;
+  if(position<xOffset) return;
+  switch(Idx)
+    {
+    case SCDATA1: ar=array1; break;
+    case SCDATA2: ar=array2; break;
+    case SCDATA3: ar=array3; break;
+    case SCDATA4: ar=array4; break;
+    }
+  for(i=position-xOffset,j=0;i<(position+len-xOffset)&& (i<SCOPEMAXDATA);i++,j++)
+    {
+
+      ar[i]=(double)data[j];
+    }
+  if(i>SCOPEMAXDATA) i=SCOPEMAXDATA;
+  index=i;
+  addToLog(QString("data1 %1").arg(index+xOffset),LOGSCOPE);
+}
+
+
+
+void scopeView::setCurveName(QString title,int idx)
+{
+  if((idx>=0)&&(idx<=SCDATA4))
+    {
+      curveNameArray[idx]=title;
+    }
+}
+void scopeView::show(bool d1,bool d2,bool d3,bool d4)
+{
+  if(d1)
+    {
+      add1(array1,index,curveNameArray[SCDATA1],yLeftTitle);
+      setCurveOn(SCDATA1,true);
+    }
+  else setCurveOn(SCDATA1,false);
+  if(d2)
+    {
+      add2(array2,index,curveNameArray[SCDATA2]);
+      setCurveOn(SCDATA2,true);
+    }
+  else setCurveOn(SCDATA2,false);
+
+  if (d3)
+    {
+      add3(array3,index,curveNameArray[SCDATA3],yRightTitle);
+      setCurveOn(SCDATA3,true);
+    }
+  else setCurveOn(SCDATA3,false);
+  if (d4)
+    {
+      add4(array4,index,curveNameArray[SCDATA4]);
+      setCurveOn(SCDATA4,true);
+    }
+  else setCurveOn(SCDATA4,false);
+  scopePlot::show();
+}
diff --git a/qsstv/scope/scopeview.h b/qsstv/scope/scopeview.h
new file mode 100644
index 0000000..cec3279
--- /dev/null
+++ b/qsstv/scope/scopeview.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef SCOPEVIEW_H
+#define SCOPEVIEW_H
+#include "qsstvglobal.h"
+#include <QString>
+
+#include "scopeplot.h"
+
+#define SCOPEMAXDATA	200000
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+
+enum ecurve{SCDATA1,SCDATA2,SCDATA3,SCDATA4};
+#define NUMCURVES 4
+
+class scopeView: public scopePlot
+{
+public:
+  scopeView(QString title);
+  ~scopeView();
+  void init();
+  void setOffset (int xoffset);
+
+  void addData(ecurve Idx,double  *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,float   *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,qint8   *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,quint8  *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,qint16  *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,quint16 *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,qint32  *data,unsigned int position,unsigned int len);
+  void addData(ecurve Idx,quint32 *data,unsigned int position,unsigned int len);
+
+
+  void show(bool data,bool sync,bool state,bool d4);
+  void setCurveName(QString title,int idx);
+  void setAxisTitles(QString x,QString yData1,QString yData2)
+  {
+    xTitle=x;
+    yLeftTitle=yData1;
+    yRightTitle=yData2;
+  }
+
+private:
+  double array1[SCOPEMAXDATA];
+  double array2[SCOPEMAXDATA];
+  double array3[SCOPEMAXDATA];
+  double array4[SCOPEMAXDATA];
+  unsigned int index;
+  QString curveNameArray[NUMCURVES];
+  QString xTitle;
+  QString yLeftTitle;
+  QString yRightTitle;
+  unsigned int xOffset;
+};
+
+#endif
+
diff --git a/qsstv/sound/calibration.cpp b/qsstv/sound/calibration.cpp
new file mode 100644
index 0000000..4576dc3
--- /dev/null
+++ b/qsstv/sound/calibration.cpp
@@ -0,0 +1,209 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "calibration.h"
+#include "ui_calibration.h"
+#include <QApplication>
+#include "qsstvglobal.h"
+#include "sound/soundio.h"
+#include <math.h>
+#include "QMessageBox"
+#include <QPushButton>
+
+
+#define ITERATIONS 100
+
+
+/**
+ * \class calibration
+ *
+ * Check first if ntp is running and it is synchronised. A dialog window will appear and show the progress ofr the RX and TX clocks.
+ * About 10000 blocks of data will be read/written to calculate the exact timing. If the OK button is pressed, the clocks will be saved for later use.
+ *
+ **/
+
+
+
+/**
+ * @brief Calibration constructor
+ *
+ * @param parent parent widget pointer
+ */
+calibration::calibration(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::calibration)
+{
+  ui->setupUi(this);
+  init();
+}
+
+
+
+calibration::~calibration()
+{
+  delete ui;
+}
+
+
+
+/**
+ * @brief start calibration
+ *
+ * Call this function to start the calibration proces. The use the results, check the return status and get the value of the clocks by calling getRXClock() and getTXClock().
+ *
+ * \sa getRXClock() \sa getTXClock()
+ *
+ * @return bool true if calibration is successful. Return false if an error occured or the dialog was canceled
+ */
+int calibration::exec()
+{
+
+  init();
+  show();
+  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+  if(!start(true)) return QDialog::Rejected;
+  if(!start(false)) return QDialog::Rejected;
+  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+  while(!stopped)
+    {
+      qApp->processEvents();
+    }
+ if(!canceled) return QDialog::Accepted;
+ return QDialog::Rejected;
+}
+
+
+/**
+ * @brief initialize
+ *
+ * This function is called by exec to initialize the calibration and setup the dialog box
+ *
+  */
+
+void calibration::init()
+{
+  stopped=false;
+  canceled=false;
+  // find out if we working with nanoseconds or microseconds
+  ui->rxProgress->setMaximum(ITERATIONS-1);
+  ui->txProgress->setMaximum(ITERATIONS-1);
+  ui->rxProgress->setValue(0);
+  ui->txProgress->setValue(0);
+  display(BASESAMPLERATE,ui->rxLCD);
+  display(BASESAMPLERATE,ui->txLCD);
+  connect(this,SIGNAL(finished(int)),SLOT(hasFinished(int)));
+
+}
+
+/**
+ * @brief slot for finish
+ *
+ * This slot is called when the dialog is closed by pressing CANCEL or OK. It will abort the loop executed in start and set the bool canceled to false or true
+ * depending on the CANCEL or OK button being presssed.
+ **/
+
+void calibration::hasFinished(int result)
+{
+  stopped=true;
+  if(result==QDialog::Rejected) canceled=true;
+}
+
+
+/**
+ * @brief initialize
+ *
+ * @param bool isRX: If isRX is set then the receive clock will be calibrated, else the transmit clock will be calibrated.
+ *
+ * Start is called by exec and performs the clock calibration using NTP (Network Time Protocol).
+ * It starts counting when the first 100 blocks are read or when the first 100 blocks are written in order to start with a stable condition.
+ * @return bool returns true if calibration is successful or false when either the CANCEL button was pressed or a read NTP time erro has occured.
+ *
+ **/
+
+bool calibration::start(bool isRX)
+{
+  unsigned int i;
+  double clock=48000;
+  int frames;
+  int framesDone=-1;
+  int elapsed;
+  QString blockStr;
+  if(isRX)
+    {
+      blockStr="blockRX: ";
+    }
+  else
+    {
+      blockStr="blockTX: ";
+    }
+
+  if(!soundIOPtr->startCalibration(isRX))
+    {
+      QMessageBox::critical(this,"Calibration Error","Souncard not active");
+      return false;
+    }
+  for(i=0;i<ITERATIONS;)
+    {
+      qApp->processEvents();
+      if(!soundIOPtr->isRunning()) return false;
+      elapsed=soundIOPtr->calibrationCount(frames);
+      if((frames%50==0)&&(frames>0)&&(frames!=framesDone))
+        {
+          i++;
+          framesDone=frames;
+          clock=((double)frames*1000.*PERIODSIZE)/((double)elapsed);
+          if(isRX)
+            {
+              display(clock,ui->rxLCD);
+              ui->rxProgress->setValue(i);
+            }
+          else
+            {
+              display(clock,ui->txLCD);
+              ui->txProgress->setValue(i);
+            }
+        }
+     }
+  soundIOPtr->idle();
+  if(isRX)
+    {
+      rxCardClock=clock;
+    }
+  else
+    {
+      txCardClock=clock;
+    }
+  return true;
+}
+
+
+void calibration::display(double value,QLCDNumber *dspl)
+{
+  QString tmp=QString::number(value,'g',7);
+  switch(tmp.length())
+    {
+    case 5: tmp+=".00"; break;
+    case 6: tmp+="00"; break;
+    case 7: tmp+="0"; break;
+    }
+   dspl->display(tmp);
+}
+
+
diff --git a/qsstv/sound/calibration.h b/qsstv/sound/calibration.h
new file mode 100644
index 0000000..fad9f3e
--- /dev/null
+++ b/qsstv/sound/calibration.h
@@ -0,0 +1,49 @@
+#ifndef CALIBRATION_H
+#define CALIBRATION_H
+
+#include <QDialog>
+#include <QTime>
+
+class QLCDNumber;
+
+namespace Ui {
+  class calibration;
+  }
+
+class calibration : public QDialog
+{
+  Q_OBJECT
+  
+public:
+  explicit calibration(QWidget *parent = 0);
+  ~calibration();
+  int exec();
+  /**
+   ** @brief get calibrated receive clock
+   *
+   * @return double calibrated value of the rxclock
+   */
+  double getRXClock() {return rxCardClock;}
+
+  /**
+   * @brief get calibrated transmit clock
+   *
+   * @return double calibrated value of the txclock
+   */
+  double getTXClock(){return txCardClock;}
+
+public slots:
+  void hasFinished(int result);
+  
+private:
+  Ui::calibration *ui;
+  double rxCardClock;
+  double txCardClock;
+  bool stopped;
+  void init();
+  bool start(bool isRX);
+  void display(double value,QLCDNumber *dspl);
+  bool canceled;
+};
+
+#endif // CALIBRATION_H
diff --git a/qsstv/sound/calibration.ui b/qsstv/sound/calibration.ui
new file mode 100644
index 0000000..dc392cf
--- /dev/null
+++ b/qsstv/sound/calibration.ui
@@ -0,0 +1,747 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>calibration</class>
+ <widget class="QDialog" name="calibration">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>488</width>
+    <height>207</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Soundcard Calibration</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0">
+     <item>
+      <widget class="QLabel" name="rxClockLabel">
+       <property name="text">
+        <string>RX Clock</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="rxProgress">
+       <property name="value">
+        <number>24</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLCDNumber" name="rxLCD">
+       <property name="minimumSize">
+        <size>
+         <width>144</width>
+         <height>46</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>76</red>
+             <green>76</green>
+             <blue>76</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>85</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>76</red>
+             <green>76</green>
+             <blue>76</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>85</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="lineWidth">
+        <number>2</number>
+       </property>
+       <property name="midLineWidth">
+        <number>2</number>
+       </property>
+       <property name="smallDecimalPoint">
+        <bool>false</bool>
+       </property>
+       <property name="numDigits">
+        <number>8</number>
+       </property>
+       <property name="digitCount">
+        <number>8</number>
+       </property>
+       <property name="segmentStyle">
+        <enum>QLCDNumber::Filled</enum>
+       </property>
+       <property name="value" stdset="0">
+        <double>48000.000000000000000</double>
+       </property>
+       <property name="intValue" stdset="0">
+        <number>48000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0">
+     <item>
+      <widget class="QLabel" name="txClockLabel">
+       <property name="text">
+        <string>TX Clock</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="txProgress">
+       <property name="value">
+        <number>24</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLCDNumber" name="txLCD">
+       <property name="minimumSize">
+        <size>
+         <width>144</width>
+         <height>46</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>76</red>
+             <green>76</green>
+             <blue>76</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>85</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>85</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>76</red>
+             <green>76</green>
+             <blue>76</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>85</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>127</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="autoFillBackground">
+        <bool>true</bool>
+       </property>
+       <property name="lineWidth">
+        <number>2</number>
+       </property>
+       <property name="midLineWidth">
+        <number>2</number>
+       </property>
+       <property name="smallDecimalPoint">
+        <bool>false</bool>
+       </property>
+       <property name="numDigits">
+        <number>8</number>
+       </property>
+       <property name="digitCount">
+        <number>8</number>
+       </property>
+       <property name="segmentStyle">
+        <enum>QLCDNumber::Filled</enum>
+       </property>
+       <property name="value" stdset="0">
+        <double>48000.000000000000000</double>
+       </property>
+       <property name="intValue" stdset="0">
+        <number>48000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>238</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>calibration</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>calibration</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/sound/calibrationform.ui b/qsstv/sound/calibrationform.ui
new file mode 100644
index 0000000..791266d
--- /dev/null
+++ b/qsstv/sound/calibrationform.ui
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CalibrationForm</class>
+ <widget class="QDialog" name="CalibrationForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>345</width>
+    <height>96</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>110</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Calibration</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticallAYOUT">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="rxLabel">
+       <property name="minimumSize">
+        <size>
+         <width>150</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>RX</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="rxProgressBar">
+       <property name="value">
+        <number>24</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QLabel" name="txLabel">
+       <property name="minimumSize">
+        <size>
+         <width>150</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>TX</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="txProgressBar">
+       <property name="value">
+        <number>24</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+     <property name="centerButtons">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CalibrationForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CalibrationForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/sound/resamplefilter.cpp b/qsstv/sound/resamplefilter.cpp
new file mode 100644
index 0000000..935af0e
--- /dev/null
+++ b/qsstv/sound/resamplefilter.cpp
@@ -0,0 +1,149 @@
+/* Automatically generated file with MATLAB */
+/* File name: "ResampleFilter.m" */
+/* Filter taps in time-domain */
+
+#include "resamplefilter.h"
+
+/* Filter for ratios close to 1 */
+float fResTaps1To1[INTERP_DECIM_I_D][RES_FILT_NUM_TAPS_PER_PHASE] = {
+{
+	-0.00129181992672801360f,
+	0.00561586829442904840f,
+	-0.01349857823816511800f,
+	0.02541150940858524100f,
+	-0.04267869501534898200f,
+	0.07724474282951483700f,
+	0.96609875058711103000f,
+	-0.01641812005088002400f,
+	-0.00427135103965109450f,
+	0.00726225824406205160f,
+	-0.00544188094946287510f,
+	0.00266742068076876060f
+},
+{
+	-0.00207886551285772290f,
+	0.00866090598717600930f,
+	-0.02161960909069559500f,
+	0.04383507935997314800f,
+	-0.08302470868585065700f,
+	0.18738870090358245000f,
+	0.93524350914423104000f,
+	-0.09031872116141286000f,
+	0.02909509423931267600f,
+	-0.00897188476756275060f,
+	0.00178311012364952820f,
+	0.00010586149691723067f
+},
+{
+	-0.00287519800425638110f,
+	0.01143197533872717000f,
+	-0.02889142869399521600f,
+	0.06060641890050100900f,
+	-0.12152802242786863000f,
+	0.30933747340895279000f,
+	0.87539536840978205000f,
+	-0.14271415809850990000f,
+	0.05516985095031713000f,
+	-0.02205265100214613000f,
+	0.00761119378345958850f,
+	-0.00187713739944610450f
+},
+{
+	-0.00354120720771153910f,
+	0.01351098086300389300f,
+	-0.03433664370844288100f,
+	0.07367662235517660800f,
+	-0.15398027155782226000f,
+	0.43728178746780866000f,
+	0.79013921003423337000f,
+	-0.17341770937821352000f,
+	0.07263788052016696700f,
+	-0.03120859084480779800f,
+	0.01170664402374247200f,
+	-0.00319259334815649940f
+},
+{
+	-0.00391755659664638590f,
+	0.01447751287549226700f,
+	-0.03701682481313090000f,
+	0.08107302414568577600f,
+	-0.17606165300033697000f,
+	0.56464344237183917000f,
+	0.68451472884717957000f,
+	-0.18369620562420094000f,
+	0.08111657494320076400f,
+	-0.03614676421513295800f,
+	0.01396276906259418800f,
+	-0.00384568128202934270f
+},
+{
+	-0.00384568128202934270f,
+	0.01396276906259418800f,
+	-0.03614676421513295800f,
+	0.08111657494320076400f,
+	-0.18369620562420094000f,
+	0.68451472884717957000f,
+	0.56464344237183917000f,
+	-0.17606165300033697000f,
+	0.08107302414568577600f,
+	-0.03701682481313090000f,
+	0.01447751287549226700f,
+	-0.00391755659664638590f
+},
+{
+	-0.00319259334815649940f,
+	0.01170664402374247200f,
+	-0.03120859084480779800f,
+	0.07263788052016696700f,
+	-0.17341770937821352000f,
+	0.79013921003423337000f,
+	0.43728178746780866000f,
+	-0.15398027155782226000f,
+	0.07367662235517660800f,
+	-0.03433664370844288100f,
+	0.01351098086300389300f,
+	-0.00354120720771153910f
+},
+{
+	-0.00187713739944610450f,
+	0.00761119378345958850f,
+	-0.02205265100214613000f,
+	0.05516985095031713000f,
+	-0.14271415809850990000f,
+	0.87539536840978205000f,
+	0.30933747340895279000f,
+	-0.12152802242786863000f,
+	0.06060641890050100900f,
+	-0.02889142869399521600f,
+	0.01143197533872717000f,
+	-0.00287519800425638110f
+},
+{
+	0.00010586149691723067f,
+	0.00178311012364952820f,
+	-0.00897188476756275060f,
+	0.02909509423931267600f,
+	-0.09031872116141286000f,
+	0.93524350914423104000f,
+	0.18738870090358245000f,
+	-0.08302470868585065700f,
+	0.04383507935997314800f,
+	-0.02161960909069559500f,
+	0.00866090598717600930f,
+	-0.00207886551285772290f
+},
+{
+	0.00266742068076876060f,
+	-0.00544188094946287510f,
+	0.00726225824406205160f,
+	-0.00427135103965109450f,
+	-0.01641812005088002400f,
+	0.96609875058711103000f,
+	0.07724474282951483700f,
+	-0.04267869501534898200f,
+	0.02541150940858524100f,
+	-0.01349857823816511800f,
+	0.00561586829442904840f,
+	-0.00129181992672801360f
+}
+};
diff --git a/qsstv/sound/resamplefilter.h b/qsstv/sound/resamplefilter.h
new file mode 100644
index 0000000..b3d2b7b
--- /dev/null
+++ b/qsstv/sound/resamplefilter.h
@@ -0,0 +1,14 @@
+/* Automatically generated file with MATLAB */
+/* File name: "ResampleFilter.m" */
+/* Filter taps in time-domain */
+
+#ifndef _RESAMPLEFILTER_H_
+#define _RESAMPLEFILTER_H_
+
+#define RES_FILT_NUM_TAPS_PER_PHASE  12
+#define INTERP_DECIM_I_D             10
+
+/* Filter for ratios close to 1 */
+extern float fResTaps1To1[INTERP_DECIM_I_D][RES_FILT_NUM_TAPS_PER_PHASE];
+
+#endif	/* _RESAMPLEFILTER_H_ */
diff --git a/qsstv/sound/resampler.cpp b/qsstv/sound/resampler.cpp
new file mode 100644
index 0000000..fba536d
--- /dev/null
+++ b/qsstv/sound/resampler.cpp
@@ -0,0 +1,146 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   Based on software from                                                *
+*   Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik   *
+*   Author(s):                                                            *
+*   Volker Fischer                                                        *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "resampler.h"
+#include "qsstvglobal.h"
+#include "sound/soundio.h"
+
+/**
+ * @brief Resample routine for arbitrary sample-rate conversions
+ *
+ * Resample routine for arbitrary sample-rate conversions in a low range (for
+ * frequency offset correction).
+ * The algorithm is based on a polyphase structure. We upsample the input
+ * signal with a factor INTERP_DECIM_I_D and calculate two successive samples
+ * whereby we perform a linear interpolation between these two samples to get
+ * an arbitraty sample grid.
+ *
+ * @param prInput input buffer data (e.g. soundcard)
+ * @param prOutput output buffer (resampled data)
+ * @param rRation  conversion ratio
+ * @return int
+ */
+
+resampler::resampler()
+{
+  resamplerBuffer=NULL;
+}
+
+resampler::~resampler()
+{
+  if(resamplerBuffer!=NULL) delete  resamplerBuffer;
+}
+
+
+/**
+ * @brief resample the audio data
+ *
+ * When this function is called, there is already enough data in the sound carad buffers
+ *
+ * @param rRation the resample ratio
+ */
+void resampler::resample(DSPFLOAT rRation, DSPFLOAT *inputBuffer)
+{
+  unsigned int i;
+
+  /* Move old data from the end to the history part of the buffer and add new data (shift register) */
+  for (i = 0; i < iHistorySize; i++) resamplerBuffer[i] = resamplerBuffer[i + iInputBlockSize];
+  for (i = 0; i < iInputBlockSize; i++)  resamplerBuffer[i + iHistorySize] = inputBuffer[i];
+
+  /* Sample-interval of new sample frequency in relation to interpolated  sample-interval */
+  rTStep = (DSPFLOAT) INTERP_DECIM_I_D / rRation;
+
+	/* Init output counter */
+	int im = 0;
+//    if (input_samples_buffer_request == 0)	/* no resampling needed, just copy */
+
+      {
+        im = iInputBlockSize;
+        for (i = 0; i < iInputBlockSize; i++) rxBuffer.put(resamplerBuffer[i + iHistorySize]);
+        return ;
+      }
+
+	/* Main loop */
+	do
+	{
+		/* Quantize output-time to interpolated time-index */
+    const int ik = (int) rtOut;
+
+
+		/* Calculate convolutions for the two interpolation-taps ------------ */
+		/* Phase for the linear interpolation-taps */
+		const int ip1 = ik % INTERP_DECIM_I_D;
+		const int ip2 = (ik + 1) % INTERP_DECIM_I_D;
+
+		/* Sample positions in input vector */
+		const int in1 = (int) (ik / INTERP_DECIM_I_D);
+		const int in2 = (int) ((ik + 1) / INTERP_DECIM_I_D);
+
+		/* Convolution */
+    DSPFLOAT ry1 = (DSPFLOAT) 0.0;
+    DSPFLOAT ry2 = (DSPFLOAT) 0.0;
+		for (int i = 0; i < RES_FILT_NUM_TAPS_PER_PHASE; i++)
+		{
+      ry1 += fResTaps1To1[ip1][i] * resamplerBuffer[in1 - i];
+      ry2 += fResTaps1To1[ip2][i] * resamplerBuffer[in2 - i];
+		}
+
+
+		/* Linear interpolation --------------------------------------------- */
+		/* Get numbers after the comma */
+    const DSPFLOAT rxInt = rtOut - (int) rtOut;
+    rxBuffer.put((ry2 - ry1) * rxInt + ry1);
+		/* Increase output counter */
+		im++;
+
+		/* Increase output-time and index one step */
+		rtOut = rtOut + rTStep;
+	} 
+	while (rtOut < rBlockDuration);
+
+	/* Set rtOut back */
+	rtOut -= iInputBlockSize * INTERP_DECIM_I_D;
+}
+
+void resampler::init(int blocksize)
+{
+  unsigned int i;
+  iInputBlockSize = blocksize;
+
+  /* History size must be one sample larger, because we use always TWO convolutions */
+	iHistorySize = RES_FILT_NUM_TAPS_PER_PHASE + 1;
+
+	/* Calculate block duration */
+  rBlockDuration = (iInputBlockSize + RES_FILT_NUM_TAPS_PER_PHASE) * INTERP_DECIM_I_D;
+
+	/* Allocate memory for internal buffer, clear sample history */
+  resamplerBuffer= new DSPFLOAT[iInputBlockSize + iHistorySize];
+  for(i=0;i<(iInputBlockSize + iHistorySize);i++) resamplerBuffer[i]= 0;
+	/* Init absolute time for output stream (at the end of the history part) */
+  rtOut = (DSPFLOAT) RES_FILT_NUM_TAPS_PER_PHASE * INTERP_DECIM_I_D;
+  rxBuffer.reset();
+}
+
+
diff --git a/qsstv/sound/resampler.cpp_new b/qsstv/sound/resampler.cpp_new
new file mode 100644
index 0000000..d35c4cf
--- /dev/null
+++ b/qsstv/sound/resampler.cpp_new
@@ -0,0 +1,147 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   Based on software from                                                *
+*   Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik   *
+*   Author(s):                                                            *
+*   Volker Fischer                                                        *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "resampler.h"
+#include "qsstvglobal.h"
+#include "sound/soundio.h"
+
+/**
+ * @brief Resample routine for arbitrary sample-rate conversions
+ *
+ * Resample routine for arbitrary sample-rate conversions in a low range (for
+ * frequency offset correction).
+ * The algorithm is based on a polyphase structure. We upsample the input
+ * signal with a factor INTERP_DECIM_I_D and calculate two successive samples
+ * whereby we perform a linear interpolation between these two samples to get
+ * an arbitraty sample grid.
+ *
+ * @param prInput input buffer data (e.g. soundcard)
+ * @param prOutput output buffer (resampled data)
+ * @param rRation  conversion ratio
+ * @return int
+ */
+
+resampler::resampler()
+{
+  resamplerBuffer=NULL;
+}
+
+resampler::~resampler()
+{
+  if(resamplerBuffer!=NULL) delete  resamplerBuffer;
+}
+
+
+/**
+ * @brief resample the audio data
+ *
+ * When this function is called, there is already enough data in the sound carad buffers
+ *
+ * @param rRation the resample ratio
+ */
+void resampler::resample(DSPFLOAT rRation, short int *inputBuffer)
+{
+  unsigned int i;
+
+  /* Move old data from the end to the history part of the buffer and add new data (shift register) */
+  for (i = 0; i < iHistorySize; i++) resamplerBuffer[i] = resamplerBuffer[i + iInputBlockSize];
+  for (i = 0; i < iInputBlockSize; i++)  resamplerBuffer[i + iHistorySize] = (DSPFLOAT) inputBuffer[i];
+
+  /* Sample-interval of new sample frequency in relation to interpolated  sample-interval */
+  rTStep = (DSPFLOAT) INTERP_DECIM_I_D / rRation;
+
+	/* Init output counter */
+	int im = 0;
+  if(rRation==1)
+//    if (input_samples_buffer_request == 0)	/* no resampling needed, just copy */
+
+      {
+        im = iInputBlockSize;
+        for (i = 0; i < iInputBlockSize; i++) rxBuffer.put(resamplerBuffer[i + iHistorySize]);
+        return ;
+      }
+
+	/* Main loop */
+	do
+	{
+		/* Quantize output-time to interpolated time-index */
+    const int ik = (int) rtOut;
+
+
+		/* Calculate convolutions for the two interpolation-taps ------------ */
+		/* Phase for the linear interpolation-taps */
+		const int ip1 = ik % INTERP_DECIM_I_D;
+		const int ip2 = (ik + 1) % INTERP_DECIM_I_D;
+
+		/* Sample positions in input vector */
+		const int in1 = (int) (ik / INTERP_DECIM_I_D);
+		const int in2 = (int) ((ik + 1) / INTERP_DECIM_I_D);
+
+		/* Convolution */
+    DSPFLOAT ry1 = (DSPFLOAT) 0.0;
+    DSPFLOAT ry2 = (DSPFLOAT) 0.0;
+		for (int i = 0; i < RES_FILT_NUM_TAPS_PER_PHASE; i++)
+		{
+      ry1 += fResTaps1To1[ip1][i] * resamplerBuffer[in1 - i];
+      ry2 += fResTaps1To1[ip2][i] * resamplerBuffer[in2 - i];
+		}
+
+
+		/* Linear interpolation --------------------------------------------- */
+		/* Get numbers after the comma */
+    const DSPFLOAT rxInt = rtOut - (int) rtOut;
+    rxBuffer.put((ry2 - ry1) * rxInt + ry1);
+		/* Increase output counter */
+		im++;
+
+		/* Increase output-time and index one step */
+		rtOut = rtOut + rTStep;
+	} 
+	while (rtOut < rBlockDuration);
+
+	/* Set rtOut back */
+	rtOut -= iInputBlockSize * INTERP_DECIM_I_D;
+}
+
+void resampler::init(int blocksize)
+{
+  unsigned int i;
+  iInputBlockSize = blocksize;
+
+  /* History size must be one sample larger, because we use always TWO convolutions */
+	iHistorySize = RES_FILT_NUM_TAPS_PER_PHASE + 1;
+
+	/* Calculate block duration */
+  rBlockDuration = (iInputBlockSize + RES_FILT_NUM_TAPS_PER_PHASE) * INTERP_DECIM_I_D;
+
+	/* Allocate memory for internal buffer, clear sample history */
+  resamplerBuffer= new DSPFLOAT[iInputBlockSize + iHistorySize];
+  for(i=0;i<(iInputBlockSize + iHistorySize);i++) resamplerBuffer[i]= 0;
+	/* Init absolute time for output stream (at the end of the history part) */
+  rtOut = (DSPFLOAT) RES_FILT_NUM_TAPS_PER_PHASE * INTERP_DECIM_I_D;
+  rxBuffer.reset();
+}
+
+
diff --git a/qsstv/sound/resampler.h b/qsstv/sound/resampler.h
new file mode 100644
index 0000000..b95f9fb
--- /dev/null
+++ b/qsstv/sound/resampler.h
@@ -0,0 +1,28 @@
+#ifndef RESAMPLER_H
+#define RESAMPLER_H
+
+#include "resamplefilter.h"
+#include "qsstvdefs.h"
+#include "utils/buffermanag.h"
+
+
+/* Classes ********************************************************************/
+class resampler
+{
+public:
+  resampler();
+  ~resampler();
+  void init(int blocksize);
+  void resample(DSPFLOAT rRation, DSPFLOAT *inputBuffer);
+  buffer<DSPFLOAT,16> rxBuffer;
+protected:
+  DSPFLOAT *resamplerBuffer;
+  DSPFLOAT rTStep;
+  DSPFLOAT rtOut;
+  DSPFLOAT rBlockDuration;
+  unsigned int	iHistorySize;
+  unsigned int iInputBlockSize;
+
+};
+
+#endif
diff --git a/qsstv/sound/soundcontrol.cpp b/qsstv/sound/soundcontrol.cpp
new file mode 100644
index 0000000..42a19f8
--- /dev/null
+++ b/qsstv/sound/soundcontrol.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "soundcontrol.h"
+#include "ui_soundcontrol.h"
+#include "configparams.h"
+#include "utils/supportfunctions.h"
+#include "soundio.h"
+#include <QSettings>
+
+soundControl::soundControl(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::soundControl)
+{
+  QStringList *inputPCMList, *outputPCMList;
+  ui->setupUi(this);
+  soundIOPtr->listCards(); //get input devices
+  inputPCMList=soundIOPtr->getPCMNamList(true);
+  ui->inputPCMNameComboBox->addItems(*inputPCMList);
+  outputPCMList=soundIOPtr->getPCMNamList(false);
+  ui->outputPCMNameComboBox->addItems(*outputPCMList);
+  changed=false;
+}
+
+
+soundControl::~soundControl()
+{
+  delete ui;
+}
+
+void soundControl::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("Sound");
+  rxClock=qSettings.value("rxclock",BASESAMPLERATE).toDouble();
+  txClock=qSettings.value("txclock",BASESAMPLERATE).toDouble();
+  if(fabs(1-rxClock/BASESAMPLERATE)>0.002) rxClock=BASESAMPLERATE;
+  if(fabs(1-txClock/BASESAMPLERATE)>0.002) txClock=BASESAMPLERATE;
+  inputAudioDeviceIndex=qSettings.value("inputAudioDeviceIndex",0).toInt();
+  outputAudioDeviceIndex=qSettings.value("outputAudioDeviceIndex",0).toInt();
+  soundIOPtr->soundRoutingInput=  (soundIO::edataSrc)qSettings.value("soundRoutingInput",  0 ).toInt();
+  soundIOPtr->soundRoutingOutput= (soundIO::edataDst)qSettings.value("soundRoutingOutput", 0 ).toInt();
+  soundIOPtr->recordingSize= qSettings.value("recordingSize", 100 ).toInt();
+  qSettings.endGroup();
+  setParams();
+}
+
+void soundControl::writeSettings()
+{
+  getParams();
+  QSettings qSettings;
+  qSettings.beginGroup("Sound");
+  qSettings.setValue("rxclock",rxClock);
+  qSettings.setValue("txclock",txClock);
+  qSettings.setValue("inputAudioDeviceIndex",inputAudioDeviceIndex);
+  qSettings.setValue("outputAudioDeviceIndex",outputAudioDeviceIndex);
+  qSettings.setValue ("soundRoutingInput", soundIOPtr->soundRoutingInput );
+  qSettings.setValue ("soundRoutingOutput",soundIOPtr->soundRoutingOutput );
+  qSettings.setValue ("recordingSize",soundIOPtr->recordingSize );
+  qSettings.endGroup();
+}
+
+
+void soundControl::setParams()
+{
+  setValue(rxClock,ui->inputClockLineEdit,9);
+  setValue(txClock,ui->outputClockLineEdit,9);
+  setIndex(inputAudioDeviceIndex,ui->inputPCMNameComboBox);
+  setIndex(outputAudioDeviceIndex,ui->outputPCMNameComboBox);
+  soundIOPtr->inputAudioDevice=ui->inputPCMNameComboBox->currentText();
+  soundIOPtr->outputAudioDevice=ui->outputPCMNameComboBox->currentText();
+
+  if(soundIOPtr->soundRoutingInput==soundIO::SNDINCARD) ui->inFromCard->setChecked(true);
+  else if (soundIOPtr->soundRoutingInput==soundIO::SNDINFILE) ui->inFromFile->setChecked(true);
+  else ui->inRecordFromCard->setChecked(true);
+
+  if(soundIOPtr->soundRoutingOutput==soundIO::SNDOUTCARD) ui->outToCard->setChecked(true);
+  else ui->outRecord->setChecked(true);
+  setValue(soundIOPtr->recordingSize,ui->mbSpinBox);
+}
+
+void soundControl::getParams()
+{
+  changed=false;
+  int savedInputIdx=inputAudioDeviceIndex;
+  int savedOutputIdx=outputAudioDeviceIndex;
+  getValue(rxClock,ui->inputClockLineEdit);
+  getValue(txClock,ui->inputClockLineEdit);
+
+  getIndex(inputAudioDeviceIndex,ui->inputPCMNameComboBox);
+  getIndex(outputAudioDeviceIndex,ui->outputPCMNameComboBox);
+
+  if (ui->inFromCard->isChecked()) soundIOPtr->soundRoutingInput=soundIO::SNDINCARD;
+  else if(ui->inFromFile->isChecked()) soundIOPtr->soundRoutingInput=soundIO::SNDINFILE;
+  else soundIOPtr->soundRoutingInput=soundIO::SNDINCARDTOFILE;
+
+  if (ui->outToCard->isChecked()) soundIOPtr->soundRoutingOutput=soundIO::SNDOUTCARD;
+  else soundIOPtr->soundRoutingOutput=soundIO::SNDOUTTOFILE;
+  getValue(soundIOPtr->recordingSize,ui->mbSpinBox);
+  if(savedInputIdx!=inputAudioDeviceIndex) changed=true;
+  if(savedOutputIdx!=outputAudioDeviceIndex) changed=true;
+}
+
diff --git a/qsstv/sound/soundcontrol.h b/qsstv/sound/soundcontrol.h
new file mode 100644
index 0000000..a0a2cec
--- /dev/null
+++ b/qsstv/sound/soundcontrol.h
@@ -0,0 +1,31 @@
+#ifndef SOUNDCONTROL_H
+#define SOUNDCONTROL_H
+
+#include <QWidget>
+
+namespace Ui {
+  class soundControl;
+  }
+
+class soundControl : public QWidget
+{
+  Q_OBJECT
+  
+public:
+  explicit soundControl(QWidget *parent = 0);
+  ~soundControl();
+  void readSettings();
+  void writeSettings();
+  void setParams();
+  bool needsRestart() { return changed;}
+
+
+private:
+  Ui::soundControl *ui;
+  int inputAudioDeviceIndex;
+  int outputAudioDeviceIndex;
+  bool changed;
+  void getParams();
+};
+
+#endif // SOUNDCONTROL_H
diff --git a/qsstv/sound/soundcontrol.ui b/qsstv/sound/soundcontrol.ui
new file mode 100644
index 0000000..ac3b712
--- /dev/null
+++ b/qsstv/sound/soundcontrol.ui
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>soundControl</class>
+ <widget class="QWidget" name="soundControl">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>341</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="inputAudioDeviceLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>25</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Input Audio Device</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QComboBox" name="inputPCMNameComboBox"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="outputAudioDeviceLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>25</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Output Audio Device</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QComboBox" name="outputPCMNameComboBox"/>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="inputClockLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>145</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Input  Clock Frequency</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <widget class="QLineEdit" name="inputClockLineEdit"/>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="outputClockLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>25</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Output Clock Frequency</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <layout class="QHBoxLayout" name="horizontalLayout_3">
+       <item>
+        <widget class="QLineEdit" name="outputClockLineEdit"/>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
+        <string>Sound Input</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout">
+        <item>
+         <widget class="QRadioButton" name="inFromCard">
+          <property name="text">
+           <string>From sound card</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="inFromFile">
+          <property name="text">
+           <string>From file</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="inRecordFromCard">
+          <property name="text">
+           <string>From sound card and record</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox_2">
+       <property name="title">
+        <string>Sound Output</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QRadioButton" name="outToCard">
+          <property name="text">
+           <string>To sound card</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="outRecord">
+          <property name="text">
+           <string>To sound card and record</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="_5">
+     <item>
+      <widget class="QSpinBox" name="mbSpinBox">
+       <property name="minimum">
+        <number>1</number>
+       </property>
+       <property name="maximum">
+        <number>999</number>
+       </property>
+       <property name="singleStep">
+        <number>5</number>
+       </property>
+       <property name="value">
+        <number>100</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="textLabel1">
+       <property name="text">
+        <string>Maximum recording size (in MB)</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="hspacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>51</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>25</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/sound/soundio.cpp b/qsstv/sound/soundio.cpp
new file mode 100644
index 0000000..e8ac009
--- /dev/null
+++ b/qsstv/sound/soundio.cpp
@@ -0,0 +1,855 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "soundio.h"
+#include "qsstvglobal.h"
+#include"soundcontrol.h"
+#include <QDebug>
+#include <QApplication>
+#include <stdio.h>
+#include <unistd.h>
+#include "utils/supportfunctions.h"
+//#include "scope/scopeview.h"
+#include <sys/time.h>
+#include <sys/timex.h>
+
+
+soundIO *soundIOPtr;
+
+
+static const unsigned int rates[] =
+{
+  5512,
+  8000,
+  11025,
+  16000,
+  22050,
+  32000,
+  44100,
+  48000,
+  64000,
+  88200,
+  96000,
+  176400,
+  192000,
+};
+
+
+soundIO::soundIO(int s)
+{
+  setSamplerate(s);
+  soundOK=false;
+
+}
+
+
+void soundIO::setSamplerate(int s)
+{
+  samplerate=s;
+}
+
+void  soundIO::run()
+{
+  unsigned int delay;
+  delay=((playbackBufferSize*1000)/samplerate);
+  abort=false;
+  playbackState=PBINIT;
+  captureState=CPINIT;
+  if(!soundOK) return;
+  while(!abort)
+    {
+      switch(playbackState)
+        {
+        case PBINIT:
+          txBuffer.reset();
+          detectedPlaybackState= PBINIT;
+        break;
+        case PBSTARTING:
+          if (play()==0) msleep(10);
+          else
+            {
+              playbackState=PBRUNNING;
+              addToLog("playback started",LOGPERFORM);
+            }
+          detectedPlaybackState= PBSTARTING;
+        break;
+        case PBRUNNING:
+          if (play()==0)
+            {
+               addToLog(QString("playback stopped: delay=%1").arg(delay),LOGPERFORM);
+               msleep(delay);
+               waveOut.close();
+               addToLog("playback stopped",LOGPERFORM);
+               emit playbackStopped();
+               playbackState=PBINIT;
+            }
+          detectedPlaybackState= PBRUNNING;
+        break;
+        case PBCALIBRATE1:
+          {
+            detectedPlaybackState= PBCALIBRATE1;
+            if (playbackCalibration()!=0)
+              {
+                playbackState=PBCALIBRATE2;
+              }
+          }
+        break;
+        case PBCALIBRATE2:
+          {
+            if (playbackCalibration()==0) break;
+            detectedPlaybackState= PBCALIBRATE2;
+          }
+          break;
+       }
+      switch(captureState)
+        {
+        case CPINIT:
+//          rxBuffer.reset();
+          detectedCaptureState=CPINIT;
+        break;
+        case CPSTARTING:
+          rxBuffer.reset(); //clear the rxBuffer
+          snd_pcm_prepare (captureHandle );
+          snd_pcm_start (captureHandle);
+          detectedCaptureState=CPSTARTING;
+          captureState=CPRUNNING;
+        break;
+        case CPRUNNING:
+          if (capture()==0) msleep(60);
+          detectedCaptureState=CPRUNNING;
+        break;
+        case CPCALIBRATE:
+          {
+            if (captureCalibration()==0) msleep(0);
+            detectedCaptureState= CPCALIBRATE;
+          }
+          break;
+        case CPEND:
+          msleep(1000);
+          captureState=CPINIT;
+         break;
+       }
+      if((captureState==CPINIT) &&(playbackState==PBINIT))   msleep(200);
+    }
+  abort=false;
+  playbackState=PBINIT;
+  captureState=CPINIT;
+}
+
+
+double soundIO::getPlaybackStartupTime()
+{
+  return (double)playbackBufferSize/BASESAMPLERATE;
+}
+
+
+
+/**
+ * @brief play sound from txBuffer
+ *
+ * The sound is controlled by soundRoutingOutput (via configuration).
+ * - SNDOUTCARD  sound to card only
+ * - SNDOUTTOFILE sound to card and saved in wav file
+ * - SNDOUTFILETOCARD sound from wav file to card
+ *
+ * @return number of frames sent.
+ *
+ * The readIndex must always be a multiple of 2*PERIODSIZE because we don't check the txBuffer for wrap-around.
+ *
+ */
+
+int soundIO::play()
+{
+ unsigned int numFrames,restFrames;
+ int framesWritten,error;
+  addToLog(QString("readIndex: %1,writeIndex: %2 count: %3").arg(txBuffer.getReadIndex()).arg(txBuffer.getWriteIndex()).arg(txBuffer.count()),LOGSOUND);
+  if(playbackState==PBSTARTING)
+    {
+      // prefill buffers to the max
+      if((numFrames=txBuffer.count())<playbackBufferSize) return 0;
+      addToLog(QString("start numFrame: %1").arg(numFrames),LOGSOUND);
+      numFrames=playbackBufferSize;
+    }
+  else
+    {
+      if((numFrames=txBuffer.count())>=(2*PERIODSIZE))
+        {
+          numFrames=2*PERIODSIZE;
+        }
+    }
+
+  if(soundRoutingOutput==SNDOUTTOFILE)
+    {
+      if(storedFrames<=(ulong)recordingSize*1048576L)
+        {
+          waveOut.write((quint16*)txBuffer.readPointer(),numFrames,isStereo);
+          storedFrames+=numFrames;
+//          msleep(10); // give some processingtime
+//          return numFrames;
+        }
+    }
+  restFrames=numFrames;
+  while(restFrames)
+    {
+      framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), restFrames);
+//      arrayDump(QString("sio %1").arg(frameCounter++),txBuffer.readPointer(),32,true);
+      if(framesWritten<0)
+        {
+          if ( framesWritten ==  -EAGAIN )
+            {
+              return -1;
+            }
+          else if ( framesWritten == -EPIPE )
+            {
+              /* underrun */
+              errorHandler(framesWritten,QString("Underrun recovery for %1").arg(numFrames));
+              error = snd_pcm_prepare (playbackHandle);
+              if ( error < 0 )
+                {
+                  errorHandler(framesWritten,"Can't recover from underrun, prepare failed");
+                  snd_pcm_drop (playbackHandle);
+                }
+            }
+          else
+            {
+              errorHandler(framesWritten,"Unhandled error in playback");
+              snd_pcm_drop (playbackHandle );
+            }
+        }
+      else
+        {
+          addToLog(QString("framesWritten:%1").arg(framesWritten),LOGSOUND);
+          txBuffer.skip(framesWritten);
+          if(restFrames!=numFrames)
+            {
+              addToLog(QString("restFrames: %1; framesWritten %2").arg(restFrames).arg(framesWritten),LOGSOUND);
+            }
+          msleep(10);
+          restFrames-=framesWritten;
+        }
+    }
+  return numFrames;
+}
+
+int soundIO::playbackCalibration()
+{
+  int numFrames,framesWritten;
+  if(playbackState==PBCALIBRATE1)
+    {
+      numFrames=playbackBufferSize;
+      framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames);
+      calibrationFrames=0;
+      return numFrames;
+    }
+  else
+    {
+      numFrames=1*PERIODSIZE;
+    }
+  framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames);
+  if(numFrames!=framesWritten)
+    {
+       framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames-framesWritten);
+    }
+  else
+    {
+
+    }
+  mutex.lock();
+  calibrationFrames+=1;
+  calibrationTime=stopwatch.elapsed();
+  if(calibrationFrames==CALIBRATIONLEADIN)
+    {
+      stopwatch.start();
+      addToLog("start calrx",LOGCAM);
+    }
+  mutex.unlock();
+  return numFrames;
+}
+
+/**
+ * @brief capture sound to rxBuffer
+ *
+ * The sound recording is controlled by soundRoutingInput (via configuration).
+ *
+ * - SNDINCARD sound from card
+ * - SNDINFILE sound from a wav file
+ * - SNDINCARDTOFILE sound from card and recording to wav file
+ *
+ * @return number of frames recorded.
+ */
+
+int soundIO::capture()
+{
+  int count;
+  QString debugStr;
+  if(rxBuffer.spaceLeft()<PERIODSIZE) return 0;
+  if(soundRoutingInput==SNDINFILE)
+    {
+      count=waveIn.read((quint32*)tempRXBuffer,PERIODSIZE);
+     //delay to give realtime feeling
+      msleep((100*count)/samplerate);
+      if(count<=0)
+        {
+          waveIn.close();
+          captureState=CPEND;
+          return 0;
+        }
+    }
+  else
+    {
+      count=snd_pcm_avail(captureHandle); // check for available frames
+//      addToLog(QString("countcheck %1 ").arg(count),LOGSOUND);
+
+      if(count>=PERIODSIZE)
+        {
+          count = snd_pcm_readi(captureHandle, tempRXBuffer,PERIODSIZE);
+        }
+      if ( count < 0 )
+        {
+          if ( count != -EAGAIN )
+            {
+              if ( count == -EPIPE )
+                {
+                  // Overrun
+                  snd_pcm_prepare (captureHandle );
+                  snd_pcm_start (captureHandle);
+                  qDebug()<< "Overrun";
+                }
+              else
+                {
+                  snd_pcm_drop (captureHandle );
+                  qDebug()<<"Overrun , reason: "<< count << "Stopping device";
+                }
+            }
+          addToLog("soundIO: sound eagain",LOGSOUND);
+          return 0;
+        }
+      if(count!=PERIODSIZE)
+        {
+          return 0;
+        }
+      else
+        {
+          if(soundRoutingInput==SNDINCARDTOFILE)
+            {
+              if(storedFrames<=(ulong)recordingSize*1048576L)
+                {
+                  addToLog(QString("writen %1 tofile").arg(count),LOGSOUND);
+                  waveOut.write((quint16*)tempRXBuffer,count,isStereo);
+                  storedFrames+=count;
+                }
+              else
+                {
+                  captureState=CPINIT;
+                }
+            }
+        }
+    }
+//  addToLog(QString("rxBuffercount: %1").arg(count),LOGSOUND);
+
+  if((isStereo) || (soundRoutingInput==SNDINFILE))
+    {
+      for(int i=0;i<count;i++)
+        {
+          tempRXBuffer[i]=tempRXBuffer[2*i];  // setup as mono channel
+        }
+     }
+  rxBuffer.putNoCheck(tempRXBuffer,count);
+//  addToLog(QString("in buffer count: %1").arg(rxBuffer.count()),LOGSOUND);
+  return count;
+}
+
+int soundIO::captureCalibration()
+{
+  int count;
+  addToLog("calibration",LOGSOUND);
+  count = snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE);
+//  addToLog(QString("calibration count: %1").arg(count),LOGSOUND);
+  if (count==PERIODSIZE)
+    {
+//      addToLog("calibration 2",LOGSOUND);
+      mutex.lock();
+      calibrationFrames++;
+      calibrationTime=stopwatch.elapsed();
+      if(calibrationFrames==CALIBRATIONLEADIN)
+        {
+          stopwatch.start();
+        }
+      mutex.unlock();
+    }
+  else if(count>=0)
+    {
+
+      count = snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE-count);
+      mutex.lock();
+      calibrationFrames++;
+      calibrationTime=stopwatch.elapsed();
+      mutex.unlock();
+
+    }
+  else
+    {
+      calibrationFrames=0; // restart calibration
+      qDebug() << "restarting calibration";
+      if ( count != -EAGAIN )
+        {
+          if ( count == -EPIPE )
+            {
+              // Overrun
+              snd_pcm_prepare (captureHandle );
+              snd_pcm_start (captureHandle);
+              qDebug() << "Overrun";
+            }
+          else
+            {
+              snd_pcm_drop (captureHandle );
+              qDebug()<<"Overrun , reason: "<< count << "Stopping device";
+            }
+        }
+      addToLog("soundIO: sound eagain",LOGSOUND);
+      return 0;
+    }
+
+  return count;
+}
+
+/**
+ * @brief Initialisation of the soundcard hardware in full-duplex
+ *
+ * This function must be called before using the soundcard and after the configuration has been loaded
+ *@return bool true if call was succesfull
+ */
+
+bool soundIO::init()
+{
+  int err;
+  QString tempDevice;
+  lastError.clear();
+  if(soundOK)
+    {
+     // we have to stop thread first
+      stopAndWait();
+      snd_pcm_close(playbackHandle);
+      snd_pcm_close(captureHandle);
+    }
+  soundOK=false;
+  tempDevice=outputAudioDevice.left(outputAudioDevice.indexOf(" "));
+  err = snd_pcm_open(&playbackHandle,tempDevice.toLatin1().data(), SND_PCM_STREAM_PLAYBACK,0); //open in blocking mode
+  if(!errorHandler(err,"Unable to open "+outputAudioDevice)) return false;
+  tempDevice=inputAudioDevice.left(inputAudioDevice.indexOf(" "));
+  err = snd_pcm_open(&captureHandle,tempDevice.toLatin1().data(), SND_PCM_STREAM_CAPTURE, 0);
+  if(!errorHandler(err,"Unable to open "+inputAudioDevice)) return false;
+  snd_pcm_hw_params_malloc ( &hwparams );
+  snd_pcm_sw_params_malloc ( &swparams );
+  if(setupSoundParams(true))
+    {
+      if(setupSoundParams(false)) soundOK=true;
+    }
+
+  snd_pcm_hw_params_free ( hwparams );
+  snd_pcm_sw_params_free ( swparams );
+   return soundOK;
+}
+
+bool soundIO::stoppedPlaying()
+{
+  if(playbackState==PBINIT)
+    {
+      return true;
+    }
+  else
+    {
+      return false;
+    }
+}
+
+bool soundIO::setupSoundParams(bool isCapture)
+{
+  int err;
+  int dir=0;
+  snd_pcm_t *handle;
+
+  playbackPeriodSize=PERIODSIZE;
+  playbackBufferSize=BUFFERSIZE;
+  capturePeriodSize=PERIODSIZE;
+  captureBufferSize=BUFFERSIZE;
+
+  if(isCapture) handle=captureHandle;
+  else handle=playbackHandle;
+
+  /* Choose all parameters */
+  err = snd_pcm_hw_params_any ( handle, hwparams );
+  if(!errorHandler(err,"Broken configuration : no configurations available")) return false;
+
+  /* Set the interleaved read/write format */
+  err = snd_pcm_hw_params_set_access ( handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED );
+  if(!errorHandler(err,"Access type not available : ")) return false;
+
+  /* Set the sample format */
+  err = snd_pcm_hw_params_set_format ( handle, hwparams, SND_PCM_FORMAT_S16_LE );
+  if(!errorHandler(err,"Sample format Float not available : ")) return false;
+  /* Set the count of channels */
+  if(isCapture)
+    {
+      err=snd_pcm_hw_params_get_channels_min(hwparams,&minChannelsCapture);
+      err=snd_pcm_hw_params_get_channels_max(hwparams,&maxChannelsCapture);
+      err = snd_pcm_hw_params_set_channels ( handle, hwparams, minChannelsCapture);
+      if(!errorHandler(err,"Channels count not correct; " )) return false;
+     }
+  else
+    {
+
+      err=snd_pcm_hw_params_get_channels_min(hwparams,&minChannelsPlayback);
+      err=snd_pcm_hw_params_get_channels_max(hwparams,&maxChannelsPlayback);
+      err = snd_pcm_hw_params_set_channels ( handle, hwparams, 2); //allways stereo output
+      if(!errorHandler(err,"Channels count not correct; " ))
+        {
+          return false;
+        }
+    }
+
+  err = snd_pcm_hw_params_set_rate ( handle, hwparams, samplerate, 0 );
+  if(!errorHandler(err,QString("Samplerate %1 not available").arg(samplerate))) return false;
+
+  if(isCapture)
+    {
+      err = snd_pcm_hw_params_set_period_size_near ( handle, hwparams, &capturePeriodSize, &dir );
+      if(!errorHandler(err,QString("Unable to set period size %1 for capture").arg(capturePeriodSize))) return false;
+      err = snd_pcm_hw_params_set_buffer_size_near ( handle, hwparams, &captureBufferSize );
+      if(!errorHandler(err,QString("Unable to set buffersize %1 for capture").arg(captureBufferSize))) return false;
+    }
+  else
+    {
+      err = snd_pcm_hw_params_set_period_size_near ( handle, hwparams, &playbackPeriodSize, &dir );
+      if(!errorHandler(err,QString("Unable to set period size %1 for playback").arg(playbackPeriodSize))) return false;
+      err = snd_pcm_hw_params_set_buffer_size_near ( handle, hwparams, &playbackBufferSize );
+      if(!errorHandler(err,QString("Unable to set buffersize %1 for playback").arg(playbackBufferSize))) return false;
+    }
+  err = snd_pcm_hw_params ( handle, hwparams );
+  if(isCapture)
+    {
+      if(!errorHandler(err,QString("Unable to set hw params for capture:"))) return false;
+    }
+  else
+    {
+      if(!errorHandler(err,QString("Unable to set hw params for playback:"))) return false;
+    }
+
+  /* Get the current swparams */
+  err = snd_pcm_sw_params_current ( handle, swparams );
+  if(!errorHandler(err,"Unable to determine current swparams")) return false;
+  err = snd_pcm_sw_params_set_start_threshold ( handle, swparams, 2048 );
+  if(!errorHandler(err,"Unable to set start threshold mode")) return false;
+   /* Write the parameters to the record/playback device */
+  err = snd_pcm_sw_params ( handle, swparams );
+  if(!errorHandler(err,"Unable to set sw params for output")) return false;
+  return true;
+ }
+
+bool soundIO::listCards()
+{
+  int  err;
+  int  cardNum, totalCards;
+  char   str[64];
+  snd_ctl_card_info_t *cardInfo;
+  int      devNum;
+//  int totalDevices;
+  snd_pcm_info_t *pcmInfo;
+  snd_ctl_t *cardHandle;
+  inputPCMNameList << "default";
+  outputPCMNameList << "default";
+
+  // No cards found yet
+  totalCards = 0;
+
+  // Start with first card
+  cardNum = -1;
+
+  for (;;)
+    {
+
+      // Get next sound card's card number. When "cardNum" == -1, then ALSA fetches the first card
+      if ((err = snd_card_next(&cardNum)) < 0)
+        {
+          qDebug() << "Can't get the next card number:" << snd_strerror(err);
+          return false;
+        }
+
+      if (cardNum < 0) break; // No more cards? ALSA sets "cardNum" to -1 if so
+      sprintf(str, "hw:%i", cardNum);
+      if ((err = snd_ctl_open(&cardHandle, str, 0)) < 0)
+        {
+          qDebug() << "Can't open card "<< cardNum << snd_strerror(err);
+          continue;
+        }
+      // We need to get a snd_ctl_card_info_t. Just alloc it on the stack
+      snd_ctl_card_info_alloca(&cardInfo);
+
+      // Tell ALSA to fill in our snd_ctl_card_info_t with info about this card
+      if ((err = snd_ctl_card_info(cardHandle, cardInfo)) >= 0)
+        qDebug() << "Card " << cardNum << "name: " << snd_ctl_card_info_get_name(cardInfo);
+//      totalDevices = 0;
+      devNum = -1; // Start with the first wave device on this card
+      for (;;)
+        {
+          if ((err = snd_ctl_pcm_next_device(cardHandle, &devNum)) < 0)  // Get the number of the next wave device on this card
+            {
+              qDebug()<< "Can't get next wave device number: " << snd_strerror(err);
+              break;
+            }
+
+          // No more wave devices on this card? ALSA sets "devNum" to -1 if so.
+          // NOTE: It's possible that this sound card may have no wave devices on it
+          // at all, for example if it's only a MIDI card
+          if (devNum < 0) break;
+          // To get some info about the subdevices of this wave device (on the card), we need a
+          // snd_pcm_info_t, so let's allocate one on the stack
+          snd_pcm_info_alloca(&pcmInfo);
+          memset(pcmInfo, 0, snd_pcm_info_sizeof());
+
+          // Tell ALSA which device (number) we want info about
+          snd_pcm_info_set_device(pcmInfo, devNum);
+          // Get info on the wave outs of this device
+          getDevices(cardHandle,cardNum,devNum,pcmInfo, SND_PCM_STREAM_PLAYBACK);
+          getDevices(cardHandle,cardNum,devNum,pcmInfo, SND_PCM_STREAM_CAPTURE);
+        }
+      ++totalCards;
+      // Close the card's control interface after we're done with it
+      snd_ctl_close(cardHandle);
+//      qDebug() << "Found:" << totalDevices << " digital audio devices on card " <<  cardNum;
+    }
+
+//  qDebug() << "ALSA found cards" << totalCards;
+
+  // ALSA allocates some mem to load its config file when we call
+  // snd_card_next. Now that we're done getting the info, let's tell ALSA
+  // to unload the info and free up that mem
+  snd_config_update_free_global();
+  return true;
+}
+
+
+void soundIO::getDevices(snd_ctl_t *cardHandle,int cardNum,int devNum,snd_pcm_info_t *pcmInfo, snd_pcm_stream_t direction)
+{
+  int  err;
+  int i= -1;
+  int subDevCount = 1;
+  snd_pcm_info_set_stream(pcmInfo, direction);
+
+
+  // More subdevices?
+  while (++i < subDevCount)
+    {
+      // Tell ALSA to fill in our snd_pcm_info_t with info on this subdevice
+      snd_pcm_info_set_subdevice(pcmInfo, i);
+      if ((err = snd_ctl_pcm_info(cardHandle, pcmInfo)) < 0)
+        {
+//          qDebug() << QString("Can't get info for wave output subdevice hw:%1,%2,%3: %4").arg(cardNum).arg(devNum).arg(i).arg(snd_strerror(err));
+          continue;
+        }
+//      qDebug() << "PCM name" << snd_pcm_info_get_name(pcmInfo);
+      // Print out how many subdevices (once only)
+      if (!i)
+        {
+          subDevCount = snd_pcm_info_get_subdevices_count(pcmInfo);
+//          qDebug() << QString("Found %1 wave output subdevices on card %2").arg(subDevCount).arg(cardNum);
+        }
+
+      // NOTE: If there's only one subdevice, then the subdevice number is immaterial,
+      // and can be omitted when you specify the hardware name
+      if(subDevCount>1)
+        {
+//          qDebug()<< QString("hw:%1,%2,%3").arg(cardNum).arg(devNum).arg(i);
+          if(direction==SND_PCM_STREAM_CAPTURE)
+            {
+              inputPCMNameList << QString("hw:%1,%2,%3  %4").arg(cardNum).arg(devNum).arg(i).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+          else
+            {
+              outputPCMNameList << QString("hw:%1,%2,%3  %4").arg(cardNum).arg(devNum).arg(i).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+         }
+      else
+        {
+//          qDebug()<< QString("hw:%1,%2").arg(cardNum).arg(devNum);
+          if(direction==SND_PCM_STREAM_CAPTURE)
+            {
+              inputPCMNameList << QString("hw:%1,%2  %3").arg(cardNum).arg(devNum).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+          else
+            {
+              outputPCMNameList << QString("hw:%1,%2  %3").arg(cardNum).arg(devNum).arg(snd_pcm_info_get_name(pcmInfo));
+           }
+        }
+
+    }
+
+}
+
+bool soundIO::errorHandler(int err,QString info)
+{
+  if(err<0)
+    {
+      lastError=info+ ": "+ QString ( snd_strerror ( err ) );
+      qDebug() << lastError;
+      addToLog(lastError,LOGALL);
+      return false;
+    }
+  return true;
+}
+
+void soundIO::errorHandler(QString info)
+{
+  lastError=info;
+  qDebug() << lastError;
+  addToLog(lastError,LOGALL);
+}
+
+
+
+bool soundIO::startCapture()
+{
+  playbackState=PBINIT;
+  if(!soundOK) return false;
+  switch(soundRoutingInput)
+    {
+      case SNDINCARD:
+      if(minChannelsCapture==1) isStereo=false; else isStereo=true;
+      break;
+      case SNDINFILE:
+      if(!waveIn.openFileForRead("",true))
+        {
+          errorHandler("File not opened");
+          return false;
+        }
+      if(waveIn.getNumberOfChannels()==1) isStereo=false; else isStereo=true;
+      break;
+      case SNDINCARDTOFILE:
+        {
+        if(minChannelsCapture==1) isStereo=false; else isStereo=true;
+          if(!waveOut.openFileForWrite("",true,isStereo))
+            {
+              errorHandler("File not opened");
+              return false;
+            }
+        }
+      break;
+    }
+  rxBuffer.reset();
+  captureState=CPSTARTING;
+  addToLog("start capturing",LOGSOUND);
+  return true;
+}
+
+bool soundIO::startPlayback()
+{
+  frameCounter=0;
+  captureState=CPINIT;
+  if(!soundOK) return false;
+  soundIOPtr->txBuffer.reset();
+
+  if(soundRoutingOutput==SNDOUTTOFILE)
+    {
+      if(!waveOut.openFileForWrite("",true,true)) // indicate stereo
+        {
+          errorHandler("File not opened");
+          return false;
+        }
+    }
+  playbackState=PBSTARTING;
+  snd_pcm_prepare (playbackHandle);
+  addToLog(QString("start playback, txbuffercount: %1").arg(txBuffer.count()),LOGSOUND);
+
+  return true;
+}
+
+bool soundIO::startCalibration(bool capture)
+{
+  captureState=CPINIT;
+  playbackState=PBINIT;
+  if(!soundOK) return false;
+  while(detectedCaptureState!=CPINIT)
+    {
+      qApp->processEvents();
+    };
+  while(detectedPlaybackState!=PBINIT)
+    {
+      qApp->processEvents();
+    };
+  calibrationFrames=0;
+  if(capture)
+    {
+     snd_pcm_prepare (captureHandle );
+     snd_pcm_start (captureHandle);
+     captureState=CPCALIBRATE;
+    }
+  else
+    {
+      txBuffer.fill(0);
+     //
+      snd_pcm_start (playbackHandle);
+      //snd_pcm_prepare (playbackHandle );
+      playbackState=PBCALIBRATE1;
+    }
+  return true;
+}
+
+void soundIO::idle()
+{
+  waveOut.close();
+  waveIn.closeFile();
+  captureState=CPINIT;
+  playbackState=PBINIT;
+}
+
+/**
+ * @brief stopping soundio thread
+ */
+
+void soundIO::stopAndWait()
+{
+  if(!soundOK) return;
+  waveOut.write(NULL,0,false);
+  abort=true;
+  while(abort)
+    {
+      qApp->processEvents();
+    }
+}
+
+
+int soundIO::calibrationCount(int &frames)
+{
+  int tempT,tempC;
+  mutex.lock();
+  tempC=calibrationFrames;
+  tempT=calibrationTime;
+  mutex.unlock();
+  frames=tempC-CALIBRATIONLEADIN;
+  return tempT;
+}
+
+
+
+
+
diff --git a/qsstv/sound/soundio.cpp_new b/qsstv/sound/soundio.cpp_new
new file mode 100644
index 0000000..78be941
--- /dev/null
+++ b/qsstv/sound/soundio.cpp_new
@@ -0,0 +1,851 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "soundio.h"
+#include "qsstvglobal.h"
+#include"soundcontrol.h"
+#include <QDebug>
+#include <QApplication>
+#include <stdio.h>
+#include <unistd.h>
+#include "utils/supportfunctions.h"
+//#include "scope/scopeview.h"
+#include <sys/time.h>
+#include <sys/timex.h>
+
+
+soundIO *soundIOPtr;
+
+
+static const unsigned int rates[] =
+{
+  5512,
+  8000,
+  11025,
+  16000,
+  22050,
+  32000,
+  44100,
+  48000,
+  64000,
+  88200,
+  96000,
+  176400,
+  192000,
+};
+
+
+soundIO::soundIO(int s)
+{
+  setSamplerate(s);
+  soundOK=FALSE;
+
+}
+
+
+void soundIO::setSamplerate(int s)
+{
+  samplerate=s;
+}
+
+void  soundIO::run()
+{
+  unsigned int delay;
+  delay=((playbackBufferSize*1000)/samplerate);
+  abort=FALSE;
+  playbackState=PBINIT;
+  captureState=CPINIT;
+  if(!soundOK) return;
+  while(!abort)
+    {
+      switch(playbackState)
+        {
+        case PBINIT:
+          msleep(10);
+          detectedPlaybackState= PBINIT;
+        break;
+        case PBSTARTING:
+          if (play()==0) msleep(10);
+          else
+            {
+              playbackState=PBRUNNING;
+              addToLog("playback started",LOGPERFORM);
+            }
+          detectedPlaybackState= PBSTARTING;
+        break;
+        case PBRUNNING:
+          if (play()==0)
+            {
+               addToLog(QString("playback stopped: delay=%1").arg(delay),LOGPERFORM);
+               msleep(delay);
+               waveOut.close();
+               addToLog("playback stopped",LOGPERFORM);
+               emit playbackStopped();
+               playbackState=PBINIT;
+            }
+          detectedPlaybackState= PBRUNNING;
+        break;
+        case PBCALIBRATE1:
+          {
+            detectedPlaybackState= PBCALIBRATE1;
+            if (playbackCalibration()!=0)
+              {
+                playbackState=PBCALIBRATE2;
+              }
+          }
+        break;
+        case PBCALIBRATE2:
+          {
+            if (playbackCalibration()==0) break;
+            detectedPlaybackState= PBCALIBRATE2;
+          }
+          break;
+       }
+      switch(captureState)
+        {
+        case CPINIT:
+          msleep(10);
+          detectedCaptureState=CPINIT;
+        break;
+        case CPSTARTING:
+          rxBuffer.reset(); //clear the rxBuffer
+          snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE);
+          detectedCaptureState=CPSTARTING;
+          captureState=CPRUNNING;
+        break;
+        case CPRUNNING:
+          if (capture()==0) msleep(75);
+          detectedCaptureState=CPRUNNING;
+        break;
+        case CPCALIBRATE:
+          {
+            if (captureCalibration()==0) msleep(0);
+            detectedCaptureState= CPCALIBRATE;
+          }
+       }
+    }
+  abort=FALSE;
+  playbackState=PBINIT;
+  captureState=CPINIT;
+}
+
+
+/**
+ * @brief play sound from txBuffer
+ *
+ * The sound is controlled by soundRoutingOutput (via configuration).
+ * - SNDOUTCARD  sound to card only
+ * - SNDOUTTOFILE sound to card and saved in wav file
+ * - SNDOUTFILETOCARD sound from wav file to card
+ *
+ * @return number of frames sent.
+ *
+ * The readIndex must always be a multiple of 2*PERIODSIZE because we don't check the txBuffer for wrap-around.
+ *
+ */
+
+double soundIO::getPlaybackStartupTime()
+{
+  return (double)playbackBufferSize/BASESAMPLERATE;
+}
+
+int soundIO::play()
+{
+ unsigned int numFrames,restFrames;
+ int framesWritten,error;
+  addToLog(QString("readIndex: %1,writeIndex: %2 count: %3").arg(txBuffer.getReadIndex()).arg(txBuffer.getWriteIndex()).arg(txBuffer.count()),LOGSOUND);
+  if(playbackState==PBSTARTING)
+    {
+      // prefill buffers to the max
+      if((numFrames=txBuffer.count())<playbackBufferSize) return 0;
+      addToLog(QString("start numFrame: %1").arg(numFrames),LOGSOUND);
+      numFrames=playbackBufferSize;
+    }
+  else
+    {
+      if((numFrames=txBuffer.count())>=(2*PERIODSIZE))
+        {
+          numFrames=2*PERIODSIZE;
+        }
+    }
+
+  if(soundRoutingOutput==SNDOUTTOFILE)
+    {
+      if(storedFrames<=(ulong)recordingSize*1048576L)
+        {
+          waveOut.write(txBuffer.readPointer(),numFrames);
+          storedFrames+=numFrames;
+          txBuffer.skip(numFrames);
+          return numFrames;
+        }
+      else
+        {
+          return 0;
+        }
+
+    }
+  restFrames=numFrames;
+  while(restFrames)
+    {
+      framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), restFrames);
+//      arrayDump(QString("sio %1").arg(frameCounter++),txBuffer.readPointer(),32,TRUE);
+      if(framesWritten<0)
+        {
+          if ( framesWritten ==  -EAGAIN )
+            {
+              return -1;
+            }
+          else if ( framesWritten == -EPIPE )
+            {
+              /* underrun */
+              errorHandler(framesWritten,QString("Underrun recovery for %1").arg(numFrames));
+              error = snd_pcm_prepare (playbackHandle);
+              if ( error < 0 )
+                {
+                  errorHandler(framesWritten,"Can't recover from underrun, prepare failed");
+                  snd_pcm_drop (playbackHandle);
+                }
+            }
+          else
+            {
+              errorHandler(framesWritten,"Unhandled error in playback");
+              snd_pcm_drop (playbackHandle );
+            }
+        }
+      else
+        {
+          addToLog(QString("framesWritten:%1").arg(framesWritten),LOGSOUND);
+          txBuffer.skip(framesWritten);
+          if(restFrames!=numFrames)
+            {
+              addToLog(QString("restFrames: %1; framesWritten %2").arg(restFrames).arg(framesWritten),LOGSOUND);
+            }
+          msleep(10);
+          restFrames-=framesWritten;
+        }
+    }
+  return numFrames;
+}
+
+int soundIO::playbackCalibration()
+{
+  int numFrames,framesWritten;
+  if(playbackState==PBCALIBRATE1)
+    {
+      numFrames=playbackBufferSize;
+      framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames);
+      if(numFrames!=framesWritten)
+        {
+//          qDebug() << " error in calibration framesWriiten" <<framesWritten << numFrames ;
+        }
+      calibrationFrames=0;
+      return numFrames;
+    }
+  else
+    {
+      numFrames=1*PERIODSIZE;
+    }
+  framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames);
+  if(numFrames!=framesWritten)
+    {
+//       qDebug() << " error in calibration framesWriiten" <<framesWritten << numFrames ;
+       framesWritten =  snd_pcm_writei ( playbackHandle, txBuffer.readPointer(), numFrames-framesWritten);
+    }
+  else
+    {
+
+    }
+  mutex.lock();
+  calibrationFrames+=1;
+  calibrationTime=stopwatch.elapsed();
+  if(calibrationFrames==CALIBRATIONLEADIN)
+    {
+      stopwatch.start();
+      addToLog("start calrx",LOGCAM);
+    }
+  mutex.unlock();
+  return numFrames;
+}
+
+/**
+ * @brief capture sound to rxBuffer
+ *
+ * The sound recording is controlled by soundRoutingInput (via configuration).
+ *
+ * - SNDINCARD sound from card
+ * - SNDINFILE sound from a wav file
+ * - SNDINCARDTOFILE sound from card and recording to wav file
+ *
+ * @return number of frames recorded.
+ */
+
+int soundIO::capture()
+{
+  int count;
+  QString debugStr;
+  if(rxBuffer.spaceLeft()<1024) return 0;
+  if(soundRoutingInput==SNDINFILE)
+    {
+      count=waveIn.read((quint32*)tempRXBuffer,PERIODSIZE);
+     //delay to give realtime feeling
+      msleep((2*count)/samplerate);
+      if(count<=0)
+        {
+          waveIn.close();
+          captureState=CPINIT;
+        }
+    }
+  else
+    {
+      count=snd_pcm_avail(captureHandle); // check for available frames
+      if(count>=PERIODSIZE)
+        {
+          count = snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE);
+        }
+      if ( count < 0 )
+        {
+          if ( count != -EAGAIN )
+            {
+              if ( count == -EPIPE )
+                {
+                  // Overrun
+                  snd_pcm_prepare (captureHandle );
+                  snd_pcm_start (captureHandle);
+                  printf ( "Overrun\n" );
+                }
+              else
+                {
+                  snd_pcm_drop (captureHandle );
+                  printf ( "Overrun , reason %d\nStopping device", count );
+                }
+            }
+          addToLog("soundIO: sound eagain",LOGSOUND);
+          return 0;
+        }
+      else
+        {
+          if(soundRoutingInput==SNDINCARDTOFILE)
+            {
+              if(storedFrames<=(ulong)recordingSize*1048576L)
+                {
+                  waveOut.write((quint32*)tempRXBuffer,count);
+                  //             addToLog(QString("storing data: %1").arg(storedFrames),LOGSOUND);
+                  storedFrames+=count;
+                }
+              else
+                {
+                  captureState=CPINIT;
+                }
+            }
+        }
+    }
+//  addToLog(QString("rxBuffercount: %1").arg(count),LOGSOUND);
+  if(count!=PERIODSIZE)
+    {
+//      addToLog("count not periodsize",LOGSOUND);
+      return 0;
+    }
+  if(isStereo)
+    {
+      for(int i=0;i<count;i++)
+        {
+          tempRXBuffer[i]=tempRXBuffer[2*i];  // setup as mono channel
+        }
+     }
+  rxBuffer.putNoCheck(tempRXBuffer,count);
+  addToLog(QString("in buffer count: %1 % readIndex=%2 writeindex=%3").arg(rxBuffer.count()).arg(rxBuffer.getReadIndex()).arg(rxBuffer.getWriteIndex()),LOGSOUND);
+  return count;
+}
+
+int soundIO::captureCalibration()
+{
+  int count;
+  count = snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE);
+  if (count==PERIODSIZE)
+    {
+      mutex.lock();
+      calibrationFrames++;
+      calibrationTime=stopwatch.elapsed();
+      if(calibrationFrames==CALIBRATIONLEADIN)
+        {
+          stopwatch.start();
+          addToLog("start calrx",LOGCAM);
+        }
+      mutex.unlock();
+      if(calibrationFrames%50==0) addToLog(QString("calrx %1 T=%2").arg(calibrationFrames).arg(calibrationTime),LOGCAM);
+    }
+  else if(count>=0)
+    {
+      qDebug() << "invalid count" << count << "frames" << calibrationFrames << "time" << calibrationTime;
+      qDebug() <<  "requested" << PERIODSIZE-count;
+      count = snd_pcm_readi( captureHandle, tempRXBuffer,PERIODSIZE-count);
+      qDebug() << "new count" << count ;
+      mutex.lock();
+      calibrationFrames++;
+      calibrationTime=stopwatch.elapsed();
+      mutex.unlock();
+
+    }
+  else
+    {
+      calibrationFrames=0; // restart calibration
+      qDebug() << "restarting calibration";
+      if ( count != -EAGAIN )
+        {
+          if ( count == -EPIPE )
+            {
+              // Overrun
+              snd_pcm_prepare (captureHandle );
+              snd_pcm_start (captureHandle);
+              printf ( "Overrun\n" );
+            }
+          else
+            {
+              snd_pcm_drop (captureHandle );
+              printf ( "Overrun , reason %d\nStopping device", count );
+            }
+        }
+      addToLog("soundIO: sound eagain",LOGSOUND);
+      return 0;
+    }
+
+  return count;
+}
+
+/**
+ * @brief Initialisation of the soundcard hardware in full-duplex
+ *
+ * This function must be called before using the soundcard and after the configuration has been loaded
+ *@return bool TRUE if call was succesfull
+ */
+
+bool soundIO::init()
+{
+  int err;
+  stopSoundIO();
+  QString tempDevice;
+  lastError.clear();
+  soundOK=FALSE;
+  tempDevice=outputAudioDevice.left(outputAudioDevice.indexOf(" "));
+  err = snd_pcm_open(&playbackHandle,tempDevice.toLatin1().data(), SND_PCM_STREAM_PLAYBACK,0); //open in blocking mode
+  if(!errorHandler(err,"Unable to open "+outputAudioDevice)) return FALSE;
+  tempDevice=inputAudioDevice.left(inputAudioDevice.indexOf(" "));
+  err = snd_pcm_open(&captureHandle,tempDevice.toLatin1().data(), SND_PCM_STREAM_CAPTURE, 0);
+  if(!errorHandler(err,"Unable to open "+inputAudioDevice)) return FALSE;
+  snd_pcm_hw_params_malloc ( &hwparams );
+  snd_pcm_sw_params_malloc ( &swparams );
+  if(setupSoundParams(TRUE))
+    {
+      if(setupSoundParams(FALSE)) soundOK=TRUE;
+    }
+
+  snd_pcm_hw_params_free ( hwparams );
+  snd_pcm_sw_params_free ( swparams );
+   return soundOK;
+}
+
+void soundIO::stopSoundIO()
+{
+  int err;
+  if(soundOK)
+    {
+      err=snd_pcm_close(playbackHandle);
+      err=snd_pcm_close(captureHandle);
+      soundOK=FALSE;
+    }
+}
+
+bool soundIO::setupSoundParams(bool isCapture)
+{
+  int err;
+  int dir=0;
+  snd_pcm_t *handle;
+
+  playbackPeriodSize=PERIODSIZE;
+  playbackBufferSize=BUFFERSIZE;
+  capturePeriodSize=PERIODSIZE;
+  captureBufferSize=BUFFERSIZE;
+
+  if(isCapture) handle=captureHandle;
+  else handle=playbackHandle;
+
+  /* Choose all parameters */
+  err = snd_pcm_hw_params_any ( handle, hwparams );
+  if(!errorHandler(err,"Broken configuration : no configurations available")) return FALSE;
+
+  /* Set the interleaved read/write format */
+  err = snd_pcm_hw_params_set_access ( handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED );
+  if(!errorHandler(err,"Access type not available : ")) return FALSE;
+
+  /* Set the sample format */
+  err = snd_pcm_hw_params_set_format ( handle, hwparams, SND_PCM_FORMAT_S16_LE );
+  if(!errorHandler(err,"Sample format Float not available : ")) return FALSE;
+  /* Set the count of channels */
+  if(isCapture)
+    {
+      err=snd_pcm_hw_params_get_channels_min(hwparams,&minChannelsCapture);
+      err=snd_pcm_hw_params_get_channels_max(hwparams,&maxChannelsCapture);
+      qDebug() << "numChannels Capture" << minChannelsCapture << maxChannelsCapture;
+      err = snd_pcm_hw_params_set_channels ( handle, hwparams, minChannelsCapture);
+      if(!errorHandler(err,"Channels count not correct; " )) return FALSE;
+     }
+  else
+    {
+
+      err=snd_pcm_hw_params_get_channels_min(hwparams,&minChannelsPlayback);
+      err=snd_pcm_hw_params_get_channels_max(hwparams,&maxChannelsPlayback);
+      qDebug() << "numChannels Playback" << minChannelsPlayback << maxChannelsPlayback;
+      err = snd_pcm_hw_params_set_channels ( handle, hwparams, minChannelsPlayback);
+      if(!errorHandler(err,"Channels count not correct; " ))
+        {
+          return FALSE;
+        }
+    }
+
+  err = snd_pcm_hw_params_set_rate ( handle, hwparams, samplerate, 0 );
+  if(!errorHandler(err,QString("Samplerate %1 not available").arg(samplerate))) return FALSE;
+
+  if(isCapture)
+    {
+      err = snd_pcm_hw_params_set_period_size_near ( handle, hwparams, &capturePeriodSize, &dir );
+      if(!errorHandler(err,QString("Unable to set period size %1 for capture").arg(capturePeriodSize))) return FALSE;
+      err = snd_pcm_hw_params_set_buffer_size_near ( handle, hwparams, &captureBufferSize );
+      if(!errorHandler(err,QString("Unable to set buffersize %1 for capture").arg(captureBufferSize))) return FALSE;
+    }
+  else
+    {
+      err = snd_pcm_hw_params_set_period_size_near ( handle, hwparams, &playbackPeriodSize, &dir );
+      if(!errorHandler(err,QString("Unable to set period size %1 for playback").arg(playbackPeriodSize))) return FALSE;
+      err = snd_pcm_hw_params_set_buffer_size_near ( handle, hwparams, &playbackBufferSize );
+      if(!errorHandler(err,QString("Unable to set buffersize %1 for playback").arg(playbackBufferSize))) return FALSE;
+    }
+  err = snd_pcm_hw_params ( handle, hwparams );
+  if(isCapture)
+    {
+      if(!errorHandler(err,QString("Unable to set hw params for capture:"))) return FALSE;
+    }
+  else
+    {
+      if(!errorHandler(err,QString("Unable to set hw params for playback:"))) return FALSE;
+    }
+
+  /* Get the current swparams */
+  err = snd_pcm_sw_params_current ( handle, swparams );
+  if(!errorHandler(err,"Unable to determine current swparams")) return FALSE;
+  err = snd_pcm_sw_params_set_start_threshold ( handle, swparams, 2048 );
+  if(!errorHandler(err,"Unable to set start threshold mode")) return FALSE;
+   /* Write the parameters to the record/playback device */
+  err = snd_pcm_sw_params ( handle, swparams );
+  if(!errorHandler(err,"Unable to set sw params for output")) return FALSE;
+  return TRUE;
+ }
+
+bool soundIO::listCards()
+{
+  int  err;
+  int  cardNum, totalCards;
+  char   str[64];
+  snd_ctl_card_info_t *cardInfo;
+  int      devNum, totalDevices;
+  snd_pcm_info_t *pcmInfo;
+  snd_ctl_t *cardHandle;
+
+  // No cards found yet
+  totalCards = 0;
+
+  // Start with first card
+  cardNum = -1;
+
+  for (;;)
+    {
+
+      // Get next sound card's card number. When "cardNum" == -1, then ALSA fetches the first card
+      if ((err = snd_card_next(&cardNum)) < 0)
+        {
+          qDebug() << "Can't get the next card number:" << snd_strerror(err);
+          return FALSE;
+        }
+
+      if (cardNum < 0) break; // No more cards? ALSA sets "cardNum" to -1 if so
+      sprintf(str, "hw:%i", cardNum);
+      if ((err = snd_ctl_open(&cardHandle, str, 0)) < 0)
+        {
+          qDebug() << "Can't open card "<< cardNum << snd_strerror(err);
+          continue;
+        }
+      // We need to get a snd_ctl_card_info_t. Just alloc it on the stack
+      snd_ctl_card_info_alloca(&cardInfo);
+
+      // Tell ALSA to fill in our snd_ctl_card_info_t with info about this card
+      if ((err = snd_ctl_card_info(cardHandle, cardInfo)) < 0)
+        qDebug() << "Can't get info for card" << cardNum << snd_strerror(err);
+      else
+        qDebug() << "Card " << cardNum << "name: " << snd_ctl_card_info_get_name(cardInfo);
+      totalDevices = 0;
+      devNum = -1; // Start with the first wave device on this card
+      for (;;)
+        {
+          if ((err = snd_ctl_pcm_next_device(cardHandle, &devNum)) < 0)  // Get the number of the next wave device on this card
+            {
+              qDebug()<< "Can't get next wave device number: " << snd_strerror(err);
+              break;
+            }
+
+          // No more wave devices on this card? ALSA sets "devNum" to -1 if so.
+          // NOTE: It's possible that this sound card may have no wave devices on it
+          // at all, for example if it's only a MIDI card
+          if (devNum < 0) break;
+          // To get some info about the subdevices of this wave device (on the card), we need a
+          // snd_pcm_info_t, so let's allocate one on the stack
+          snd_pcm_info_alloca(&pcmInfo);
+          memset(pcmInfo, 0, snd_pcm_info_sizeof());
+
+          // Tell ALSA which device (number) we want info about
+          snd_pcm_info_set_device(pcmInfo, devNum);
+          // Get info on the wave outs of this device
+          getDevices(cardHandle,cardNum,devNum,pcmInfo, SND_PCM_STREAM_PLAYBACK);
+          getDevices(cardHandle,cardNum,devNum,pcmInfo, SND_PCM_STREAM_CAPTURE);
+        }
+      ++totalCards;
+      // Close the card's control interface after we're done with it
+      snd_ctl_close(cardHandle);
+      qDebug() << "Found:" << totalDevices << " digital audio devices on card " <<  cardNum;
+    }
+
+  qDebug() << "ALSA found cards" << totalCards;
+
+  // ALSA allocates some mem to load its config file when we call
+  // snd_card_next. Now that we're done getting the info, let's tell ALSA
+  // to unload the info and free up that mem
+  snd_config_update_free_global();
+  return TRUE;
+}
+
+
+void soundIO::getDevices(snd_ctl_t *cardHandle,int cardNum,int devNum,snd_pcm_info_t *pcmInfo, snd_pcm_stream_t direction)
+{
+  int  err;
+  int i= -1;
+  int subDevCount = 1;
+  snd_pcm_info_set_stream(pcmInfo, direction);
+
+  // More subdevices?
+  while (++i < subDevCount)
+    {
+      // Tell ALSA to fill in our snd_pcm_info_t with info on this subdevice
+      snd_pcm_info_set_subdevice(pcmInfo, i);
+      if ((err = snd_ctl_pcm_info(cardHandle, pcmInfo)) < 0)
+        {
+          qDebug() << QString("Can't get info for wave output subdevice hw:%1,%2,%3: %4").arg(cardNum).arg(devNum).arg(i).arg(snd_strerror(err));
+          continue;
+        }
+      qDebug() << "PCM name" << snd_pcm_info_get_name(pcmInfo);
+      // Print out how many subdevices (once only)
+      if (!i)
+        {
+          subDevCount = snd_pcm_info_get_subdevices_count(pcmInfo);
+          qDebug() << QString("Found %1 wave output subdevices on card %2").arg(subDevCount).arg(cardNum);
+        }
+
+      // NOTE: If there's only one subdevice, then the subdevice number is immaterial,
+      // and can be omitted when you specify the hardware name
+      if(subDevCount>1)
+        {
+          qDebug()<< QString("hw:%1,%2,%3").arg(cardNum).arg(devNum).arg(i);
+          if(direction==SND_PCM_STREAM_CAPTURE)
+            {
+              inputPCMNameList << QString("hw:%1,%2,%3  %4").arg(cardNum).arg(devNum).arg(i).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+          else
+            {
+              outputPCMNameList << QString("hw:%1,%2,%3  %4").arg(cardNum).arg(devNum).arg(i).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+         }
+      else
+        {
+          qDebug()<< QString("hw:%1,%2").arg(cardNum).arg(devNum);
+          if(direction==SND_PCM_STREAM_CAPTURE)
+            {
+              inputPCMNameList << QString("hw:%1,%2  %3").arg(cardNum).arg(devNum).arg(snd_pcm_info_get_name(pcmInfo));
+            }
+
+          else
+            {
+              outputPCMNameList << QString("hw:%1,%2  %3").arg(cardNum).arg(devNum).arg(snd_pcm_info_get_name(pcmInfo));
+           }
+        }
+
+    }
+
+}
+
+
+
+bool soundIO::errorHandler(int err,QString info)
+{
+  if(err<0)
+    {
+      lastError=info+ ": "+ QString ( snd_strerror ( err ) );
+      qDebug() << lastError;
+      addToLog(lastError,LOGALL);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+void soundIO::errorHandler(QString info)
+{
+  lastError=info;
+  qDebug() << lastError;
+  addToLog(lastError,LOGALL);
+}
+
+
+
+bool soundIO::startCapture()
+{
+  init();
+  playbackState=PBINIT;
+  if(!soundOK) return FALSE;
+  switch(soundRoutingInput)
+    {
+      case SNDINCARD:
+      if(minChannelsCapture==1) isStereo=FALSE; else isStereo=TRUE;
+      break;
+      case SNDINFILE:
+      if(!waveIn.openFileForRead("",TRUE))
+        {
+          errorHandler("File not opened");
+          return FALSE;
+        }
+      if(waveIn.getNumberOfChannels()==1) isStereo=FALSE; else isStereo=TRUE;
+      break;
+      case SNDINCARDTOFILE:
+        {
+          if(!waveOut.openFileForWrite("",TRUE))
+            {
+              errorHandler("File not opened");
+              return FALSE;
+            }
+        }
+      break;
+    }
+  rxBuffer.reset();
+  captureState=CPSTARTING;
+  addToLog("start capturing",LOGSOUND);
+  return TRUE;
+}
+
+bool soundIO::startPlayback()
+{
+  int count;
+  frameCounter=0;
+  captureState=CPINIT;
+  if(!soundOK) return FALSE;
+  soundIOPtr->txBuffer.reset();
+  if(soundRoutingOutput==SNDOUTFILETOCARD)
+    {
+      if(!waveIn.openFileForRead("",TRUE))
+        {
+          errorHandler("File not opened");
+          return FALSE;
+        }
+      snd_pcm_prepare (playbackHandle);
+      playbackState=PBSTARTING;
+      do
+        {
+          while(txBuffer.spaceLeft()<1024)
+            {
+              qApp->processEvents();
+            }
+          count=waveIn.read(txBuffer.writePointer(),1024);
+          txBuffer.advance(count);
+        }
+      while(count>0);
+      waveIn.close();
+    }
+  else
+    {
+      if(soundRoutingOutput==SNDOUTTOFILE)
+        {
+          if(!waveOut.openFileForWrite("",TRUE))
+            {
+              errorHandler("File not opened");
+              return FALSE;
+            }
+        }
+      playbackState=PBSTARTING;
+      snd_pcm_prepare (playbackHandle);
+      addToLog(QString("start playback, txbuffercount: %1").arg(txBuffer.count()),LOGSOUND);
+    }
+  return TRUE;
+}
+
+bool soundIO::startCalibration(bool capture)
+{
+  captureState=CPINIT;
+  playbackState=PBINIT;
+  if(!soundOK) return FALSE;
+  while(detectedCaptureState!=CPINIT) {};
+  while(detectedPlaybackState!=PBINIT) {};
+  calibrationFrames=0;
+  if(capture)
+    {
+     snd_pcm_prepare (captureHandle );
+     snd_pcm_start (captureHandle);
+     captureState=CPCALIBRATE;
+    }
+  else
+    {
+      txBuffer.fill(0);
+     //
+      snd_pcm_start (playbackHandle);
+      //snd_pcm_prepare (playbackHandle );
+      playbackState=PBCALIBRATE1;
+    }
+  return TRUE;
+}
+
+void soundIO::idle()
+{
+  captureState=CPINIT;
+  playbackState=PBINIT;
+}
+
+/**
+ * @brief stopping soundio thread
+ */
+
+void soundIO::stopAndWait()
+{
+  if(!soundOK) return;
+  abort=TRUE;
+  while(abort)
+    {
+      qApp->processEvents();
+    }
+}
+
+
+
+
+
diff --git a/qsstv/sound/soundio.h b/qsstv/sound/soundio.h
new file mode 100644
index 0000000..2aec2b1
--- /dev/null
+++ b/qsstv/sound/soundio.h
@@ -0,0 +1,142 @@
+#ifndef SOUNDIO_H
+#define SOUNDIO_H
+
+#include <alsa/asoundlib.h>
+#include "qsstvglobal.h"
+#include <QStringList>
+#include <QThread>
+#include "utils/buffermanag.h"
+#include "wavio.h"
+#include "qsstvdefs.h"
+#include <QTime>
+
+/*! \file soundio.h */
+
+/**
+ * @brief soundio class
+ *
+ *General Info<br>
+ * A frame is equivalent of one sample being played, irrespective of the number of channels or the number of bits.<br>
+ * e.g.
+    1 frame of a Stereo 48khz 16bit PCM stream is 4 bytes.
+    1 frame of a 5.1 48khz 16bit PCM stream is 12 bytes.
+
+A period is the number of frames in between each hardware interrupt. The poll() will return once a period.
+
+The buffer is a ring buffer. The buffer size always has to be greater than one period size. Commonly this is 2*period size, but some hardware can do 8 periods per buffer. It is also possible for the buffer size to not be an integer multiple of the period size.
+
+Now, if the hardware has been set to 48000Hz , 2 periods, of 1024 frames each, making a buffer size of 2048 frames. The hardware will interrupt 2 times per buffer. ALSA will endeavor to keep the buffer as full as possible. Once the first period of samples has been played, the third period of samples is transfered into the space the first one occupied while the second period of samples is being played. (normal ring buffer behaviour).
+
+ */
+
+#define CALIBRATIONLEADIN 100
+
+class soundIO:public QThread
+{
+  Q_OBJECT
+public:
+  enum edataSrc{SNDINCARD,SNDINFILE,SNDINCARDTOFILE};
+  enum edataDst{SNDOUTCARD,SNDOUTTOFILE};
+  enum eplaybackState{PBINIT,PBSTARTING,PBRUNNING,PBCALIBRATE1,PBCALIBRATE2};
+  enum ecaptureState{CPINIT,CPSTARTING,CPRUNNING,CPCALIBRATE,CPEND};
+#define PERIODBITS 12
+#define PERIODSIZE (1<<PERIODBITS)
+#define BUFFERSIZE (8*PERIODSIZE)
+  soundIO(int s=48000);
+  void setSamplerate(int s);
+  bool init();
+  bool isSoundOK() {return soundOK;}
+  bool listCards();
+  void run();
+  bool startCapture();
+  bool startPlayback();
+  bool startCalibration(bool capture);
+  double getPlaybackStartupTime();
+  int play();
+  int capture();
+  int captureCalibration();
+  int playbackCalibration();
+  void idle();
+  void stopAndWait();
+  bool isCapturing() {return captureState!=CPINIT;}
+  bool stoppedPlaying();
+  void abortPlayback() {playbackState=PBINIT;}
+  QString *getLastError() {return &lastError;}
+  QStringList *getPCMNamList(bool input)
+    {
+    if (input) return &inputPCMNameList;
+    else return &outputPCMNameList;
+    }
+  QString inputAudioDevice; //!< audio device (e.g. /dev/dsp)
+  QString outputAudioDevice; //!< audio device (e.g. /dev/dsp)
+  edataSrc soundRoutingInput;
+  edataDst soundRoutingOutput;
+  uint recordingSize;
+  buffer<short int,24> rxBuffer;
+  buffer<SOUNDFRAME,PERIODBITS+5> txBuffer;
+  short int tempRXBuffer[2*PERIODSIZE]; // twice because we are using stereo input
+  wavIO waveIn;
+  wavIO waveOut;
+  uint frameCounter;
+  void setStereo(bool st) {isStereo=st;}
+  int calibrationCount(int &frames);
+//  {
+//    int tempT,tempC;
+//    mutex.lock();
+//    tempC=calibrationFrames;
+//    tempT=calibrationTime;
+//    mutex.unlock();
+//    frames=tempC-CALIBRATIONLEADIN;
+//    return tempT;
+//  }
+
+signals:
+  void playbackStopped();
+
+
+private:
+  bool initialized;
+  snd_pcm_t      *playbackHandle;
+  snd_pcm_t      *captureHandle;
+  QStringList inputPCMNameList; /**< list of PCM Names for input*/
+  //QStringList inputPCMInfoList; /**< list of PCM Descriptions for input*/
+  QStringList outputPCMNameList; /**< list of PCM Names for output*/
+  //QStringList outputPCMInfoList; /**< list of PCM Descriptions for output*/
+  QString lastError;
+  bool errorHandler(int err,QString info);
+  void errorHandler(QString info);
+  bool setupSoundParams(bool isCapture);
+  int samplerate;
+  snd_pcm_uframes_t playbackPeriodSize;
+  snd_pcm_uframes_t playbackBufferSize;
+  snd_pcm_uframes_t capturePeriodSize;
+  snd_pcm_uframes_t captureBufferSize;
+  snd_pcm_hw_params_t *hwparams;
+  snd_pcm_sw_params_t *swparams;
+  bool abort;
+  unsigned long storedFrames;
+  eplaybackState playbackState;
+  eplaybackState detectedPlaybackState;
+  ecaptureState  captureState;
+  ecaptureState  detectedCaptureState;
+  bool soundOK;
+
+  unsigned int minChannelsCapture;
+  unsigned int maxChannelsCapture;
+  unsigned int minChannelsPlayback;
+  unsigned int maxChannelsPlayback;
+  bool isStereo;
+  void getDevices(snd_ctl_t *cardHandle, int cardNum, int devNum, snd_pcm_info_t *pcmInfo, snd_pcm_stream_t direction);
+  QTime stopwatch;
+  QMutex mutex;
+  unsigned int calibrationFrames;
+  int calibrationTime;
+
+};
+
+/*! \var soundIO *soundIOPtr
+\brief predefined pointer to soundIO, must be initialized by the main program
+*/
+extern soundIO *soundIOPtr;
+
+#endif // SOUNDIO_H
diff --git a/qsstv/sound/waterfalltext.cpp b/qsstv/sound/waterfalltext.cpp
new file mode 100644
index 0000000..7e841b1
--- /dev/null
+++ b/qsstv/sound/waterfalltext.cpp
@@ -0,0 +1,190 @@
+#include "waterfalltext.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include "math.h"
+#include "widgets/imageviewer.h"
+#include <QPainter>
+#include <QDebug>
+#include "dsp/filter.h"
+#include "dsp/filterparam.h"
+#include "utils/supportfunctions.h"
+
+#define FREQ_AMPLITUDE 16E3
+#define FREQ_OFFSET 350.0
+#define FREQ_MAX 2600.
+
+#define FONTSIZE 13
+#define FONTNAME "Arial"
+
+waterfallText::waterfallText()
+{
+  out=NULL;
+  outFiltered=NULL;
+  dataBuffer=NULL;
+  txFilter=NULL;
+  phr=phi=NULL;
+
+}
+
+
+waterfallText::~waterfallText()
+{
+  fftw_destroy_plan(plan);
+  if(out) fftw_free(out);
+  if(outFiltered) delete outFiltered;
+  if(dataBuffer) fftw_free(dataBuffer);
+}
+
+
+void waterfallText::init()
+{
+  int i;
+  double ph;
+  double binSize;
+  if(phr!=NULL) delete phr;
+  if(phi!=NULL) delete phi;
+  fftLength=TXSTRIPE*SUBSAMPLINGRATIO/2;
+  samplingrate=BASESAMPLERATE;
+  binSize=(double)(BASESAMPLERATE)/((double)fftLength);
+  txFilter= new filter(TXSTRIPE*SUBSAMPLINGRATIO,wfFilter,TXWFNUMTAPS,0,BASESAMPLERATE,true,1);
+  out = (fftw_complex *) fftw_malloc(sizeof(fftw_complex)*fftLength);
+  dataBuffer = (fftw_complex *) fftw_malloc(sizeof(fftw_complex)*fftLength);
+  outFiltered = new DSPFLOAT [fftLength];
+  audioBuf = new DSPFLOAT [fftLength];
+   // create the fftw plan
+  plan = fftw_plan_dft_1d(fftLength, dataBuffer, out, FFTW_BACKWARD, FFTW_ESTIMATE);
+  imageWidth=(FREQ_MAX-FREQ_OFFSET)/binSize;
+
+//  imageWidth=200;
+  startFreqIndex=(int)round(FREQ_OFFSET/binSize);
+
+
+
+  //Chirp
+  phr=new double[imageWidth];
+  phi=new double[imageWidth];
+  amplitude=FREQ_AMPLITUDE/sqrt(imageWidth);
+  for(i=0;i<imageWidth;i++)
+    {
+      ph=(-M_PI/imageWidth)*i*i;
+      phr[i]=amplitude*cos(ph);
+      phi[i]=amplitude*sin(ph);
+    }
+
+
+
+}
+
+double waterfallText::getDuration(QString txt)
+{
+  QString t=convert(txt);
+
+  if(t!=NULL)
+    {
+      setupImage(t);
+    }
+  return ((double)(line*2*fftLength))/(double)samplingrate;
+}
+
+void waterfallText::setText(QString txt)
+{
+  QString t=convert(txt);
+  setupImage(t);
+}
+
+
+DSPFLOAT * waterfallText::nextLine()
+{
+  QRgb *cPtr;
+  int i,freqIndex;
+
+  if(dLine%3==0)
+    {
+      line--;
+      if(line<0)
+        {
+          return NULL;
+        }
+
+      addToLog(QString("sendingline %1").arg(line),LOGSYNTHES);
+      cPtr=(QRgb *)image.scanLine(line);
+      for(i=0;i<fftLength;i++)
+        {
+        dataBuffer[i][0]=0.0;
+        dataBuffer[i][1]=0.0;
+        }
+
+      for(i=0;i<imageWidth;i++)
+        {
+          freqIndex=i+startFreqIndex;
+          if((cPtr[i]&0xffffff)!=0)
+            {
+              dataBuffer[freqIndex][0]= phr[i];
+              dataBuffer[freqIndex][1]= phi[i];
+            }
+        }
+      fftw_execute(plan);
+      for(i=0;i<fftLength;i++)
+        {
+          outFiltered[i]=(DSPFLOAT) out[i][0];
+        }
+//        arrayDump(QString("wf_fil"),outFiltered,32,true);
+//      txFilter->process(outFiltered,audioBuf,fftLength);
+    }
+//  for (i=0; i <10; i++)
+//    {
+//      outFiltered[i] = (audioBuf[i]*exp(-(10-i)*0.100));
+//    }
+//  for (i=10; i <fftLength-10; i++)
+//    {
+//      outFiltered[i]=audioBuf[i];
+//    }
+//  for (i=fftLength-10; i <fftLength; i++)
+//    {
+//      outFiltered[i]= (audioBuf[i]*exp((fftLength-10 -i)*0.1));
+//    }
+  dLine++;
+  return outFiltered;
+}
+
+
+
+void waterfallText::setupImage(QString txt)
+{
+  QRect rct;
+  QColor c;
+  QPainter p;
+  QPen pen;
+  pen.setColor(Qt::white);
+  dLine=0;
+  image=QImage(QSize(imageWidth,80),QImage::Format_ARGB32_Premultiplied);
+  image.fill(Qt::black);
+  p.begin(&image);
+  p.setPen(pen);
+  p.setFont(QFont(FONTNAME,FONTSIZE,QFont::Light));
+  rct=p.boundingRect(QRect(0,0,imageWidth,30),Qt::AlignTop|Qt::AlignCenter,txt);
+  p.end();
+  height=rct.height();
+  width=imageWidth;
+  image=QImage(QSize(width,height),QImage::Format_ARGB32_Premultiplied);
+  image.fill(Qt::black);
+  p.begin(&image);
+  p.setPen(pen);
+  p.setFont(QFont(FONTNAME,FONTSIZE,QFont::Light));
+  p.drawText(QRectF(0,0,width,height),Qt::AlignCenter,txt);
+  p.end();
+  line=image.height();
+}
+
+
+QString  waterfallText::convert(QString txt)
+{
+  mexp.clear();
+ mexp.addConversion('m',myCallsign);
+ mexp.addConversion('s',QString::number(lastAvgSNR,'g',2));
+ QString t=mexp.convert(txt);
+ return t;
+}
+
+
+
diff --git a/qsstv/sound/waterfalltext.h b/qsstv/sound/waterfalltext.h
new file mode 100644
index 0000000..57bf55a
--- /dev/null
+++ b/qsstv/sound/waterfalltext.h
@@ -0,0 +1,50 @@
+#ifndef WATERFALLTEXT_H
+#define WATERFALLTEXT_H
+#include "qsstvdefs.h"
+//#include <complex.h>
+#include "fftw3.h"
+#include <QString>
+#include <QImage>
+#include "utils/macroexpansion.h"
+
+class imageViewer;
+class filter;
+
+
+class waterfallText
+{
+public:
+  waterfallText();
+  ~ waterfallText();
+  void init();
+  void setText(QString txt);
+  QImage *getImagePtr() {return ℑ}
+ DSPFLOAT *nextLine();
+ int getLength() {return fftLength;}
+ double getDuration(QString txt=NULL);
+private:
+  int fftLength;
+  int samplingrate;
+  fftw_complex *out;
+  fftw_complex *dataBuffer;
+
+  DSPFLOAT *outFiltered;
+  DSPFLOAT *audioBuf;
+  fftw_plan plan;
+  void setupImage(QString txt);
+  QString convert(QString txt);
+  int imageWidth;
+  int width;
+  int height;
+  int line;
+  filter *txFilter;
+  QImage image;
+  int dLine;
+  int startFreqIndex;
+  double *phr;
+  double *phi;
+  double amplitude;
+  macroExpansion mexp;
+};
+
+#endif // WATERFALLTEXT_H
diff --git a/qsstv/sound/wavio.cpp b/qsstv/sound/wavio.cpp
new file mode 100644
index 0000000..1993a82
--- /dev/null
+++ b/qsstv/sound/wavio.cpp
@@ -0,0 +1,337 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2012 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "wavio.h"
+#include "qsstvglobal.h"
+#include <qfiledialog.h>
+#include "configparams.h"
+#include "utils/supportfunctions.h"
+#include "unistd.h"
+
+/**
+  constructor: creates a waveIO instance
+  \param samplingRate wave file samplingrate (e.g. 8000, 11025 ...)
+*/
+
+wavIO::wavIO(unsigned int samplingRate)
+{
+  samplingrate=samplingRate;
+  reading=false;
+  writing=false;
+}
+
+
+wavIO::~wavIO()
+{
+}
+
+void wavIO::closeFile()
+{
+  inopf.close();
+  reading=false;
+  writing=false;
+}
+
+/**
+   opens a wave file for reading
+  \param fname the name of the file to open
+  \param ask if ask==true, a filedialog will be opened
+  \return true if the file is succcesfully opened. The file is also checked if it is a supported format.
+  \sa read
+*/ 
+
+bool  wavIO::openFileForRead(QString fname,bool ask)
+{
+  QString tmp;
+
+  if (ask)
+    {
+      dirDialog d((QWidget *)mainWindowPtr,"Wave file");
+      QString s=d.openFileName(audioPath,"*");
+      if (s==QString::null) return false;
+      if (s.isEmpty()) return false;
+      inopf.setFileName(s);
+    }
+  else
+    {
+      inopf.setFileName(fname);
+    }
+  if(!inopf.open(QIODevice::ReadOnly))
+    {
+      return false;
+    }
+  reading=true;
+  if(inopf.read(&waveHeader.chunkID[0],sizeof(sWave))!=sizeof(sWave))
+    {
+      closeFile();
+      return false;
+    }
+
+
+  // check the header
+  if(  (!checkString(waveHeader.chunkID,"RIFF"))
+       ||(!checkString(waveHeader.format,"WAVE"))
+       ||(!checkString(waveHeader.subChunk1ID,"fmt "))
+       ||(!checkString(waveHeader.subChunk2ID,"data")))
+    {
+      addToLog("wavio read header error",LOGALL);
+      closeFile();
+      return false;
+    }
+
+  if( (waveHeader.subChunk1Size!=16)
+      ||(waveHeader.audioFormat!=1)
+      ||(waveHeader.numChannels>MAXNUMCHANNELS)
+      ||(waveHeader.sampleRate!=samplingrate)
+      //      ||(waveHeader.byteRate!=sizeof(SOUNDFRAME)*samplingrate)
+      ||(waveHeader.blockAlign!=waveHeader.numChannels*2)
+      ||(waveHeader.bitsPerSample!=16))
+    {
+      addToLog("wavio read header error, not supported",LOGALL);
+      closeFile();
+      return false;
+    }
+  numberOfChannels=waveHeader.numChannels;
+  numberOfSamples=waveHeader.subChunk2Size/(2*numberOfChannels);
+
+  samplesRead=0;
+  return true;
+}
+
+/**
+  read data from wave file
+
+  \param dPtr pointer to buffer for SOUNDFRAME type samples.
+  \param numSamples  number of samples to read
+  \return returns the nummber of samples read. -1 is returned if the end of the file is reached. The file is then automatically closed.
+*/
+
+int  wavIO::read(SOUNDFRAME *dPtr ,uint numSamples)
+{
+  int i,llen,result;
+  quint16 *tempBuf;
+
+  if(!inopf.isOpen())
+    {
+      addToLog("wavio not open during read",LOGALL);
+      return -2;
+    }
+  if(numberOfSamples<=samplesRead)
+    {
+      closeFile();
+      return -1;
+    }
+  llen=numSamples*sizeof(quint16)*numberOfChannels;
+  if(sizeof(SOUNDFRAME)/numberOfChannels==sizeof(quint16))
+    {
+      //we do not need conversion
+       result=inopf.read((char*)dPtr,llen);
+    }
+  else
+    {
+      tempBuf=new quint16[llen/2];
+      if(numberOfChannels==1) // if input is mono -> output in stereo
+        {
+          quint16 *tempBuf=new quint16[llen/2];
+          result=inopf.read((char*)tempBuf,llen);
+          for(i=0;i<(result/2);i++)
+            {
+              dPtr[i]=tempBuf[i]+(tempBuf[i]<<16);
+            }
+          delete tempBuf;
+        }
+      else // if input is stereo  -> output in mono (SOUNDFRAME is quint16)
+        {
+          result=inopf.read((char*)tempBuf,llen);
+          for(i=0;i<(result/4);i++)
+            {
+              dPtr[i]=(tempBuf[2*i]+tempBuf[2*i+1])/2;
+            }
+        }
+    }
+  if(result==0) inopf.close();
+  samplesRead+=result/(sizeof(quint16)*numberOfChannels);
+  return result/(sizeof(quint16)*numberOfChannels);
+}
+
+
+/**
+   opens a wave file for writing
+  \param fname the name of the file to open
+  \param ask if ask==true, a filedialog will be opened
+  \return true if the file is succcesfully opened, and the header written, false otherwise?
+  \sa write
+*/
+
+bool  wavIO::openFileForWrite(QString fname,bool ask,bool isStereo)
+{
+  QFileInfo fin;
+  if (ask)
+    {
+      dirDialog d((QWidget *)mainWindowPtr,"wave IO");
+      QString fn=d.saveFileName(audioPath,"*.wav","wav");
+      inopf.setFileName(fn);
+    }
+  else
+    {
+      inopf.setFileName(fname);
+    }
+  if(!inopf.open(QIODevice::WriteOnly|QIODevice::Truncate))
+    {
+      return false;
+    }
+  numberOfSamples=0;
+  if(isStereo) numberOfChannels=2;
+  else numberOfChannels=1;
+
+  initHeader();
+
+  if(!writeHeader()) return false;
+  writing=true;
+  numberOfSamples=0;
+  return true;
+}
+
+/**
+  \brief write data to wave file
+
+  To signal the end, call this function with numSamples=0. The file will automatically be closed.
+  \param dPtr pointer to buffer for 16 bit samples SOUNDFRAME indicates if samples are mono or stereo
+  \param numSamples  number of samples to read
+  \return returns true the correct number of samples are written. false otherwise.
+*/
+
+bool  wavIO::write(quint16 *dPtr, uint numSamples, bool isStereo)
+{
+  uint i;
+  int len;
+  quint16 *tempBufPtr;
+  len=numSamples*sizeof(SOUNDFRAME);
+  tempBufPtr=0;
+  quint16 *tmpPtr;
+
+  tmpPtr=dPtr;
+
+
+  if((!writing)&&(numSamples!=0))
+    {
+      addToLog("wavio not open during write",LOGALL);
+      return true;
+    }
+  if((!writing)&&(numSamples==0)) return true;
+  if(numSamples==0)
+    {
+      addToLog(QString("wavio write close samples=%1").arg(numberOfSamples),LOGWAVIO);
+      inopf.flush();
+      writeHeader();
+      closeFile();
+      return true;
+    }
+
+  if((sizeof(SOUNDFRAME)==2) && (isStereo))  // we need stereo output and input is mono
+    {
+      tempBufPtr=new quint16 [numSamples*2];
+      tmpPtr=tempBufPtr;
+      for(i=0;i<numSamples;i++)
+        {
+          tempBufPtr[i*2]=dPtr[i];
+          tempBufPtr[i*2+1]=0;
+        }
+    }
+
+  if(inopf.write((char *)tmpPtr,len)!=len)
+    {
+      addToLog("wavio write error",LOGALL);
+      closeFile();
+      if(tempBufPtr) delete tempBufPtr;
+      return false;
+    }
+  numberOfSamples+=numSamples;
+  addToLog(QString("wavio write:%1 total samples=%2").arg(numSamples).arg(numberOfSamples),LOGWAVIO);
+  if(tempBufPtr) delete tempBufPtr;
+  return true;
+}
+
+
+/** setup the defaults in the wave header */
+
+void wavIO::initHeader()
+{
+  waveHeader.chunkID[0]='R';
+  waveHeader.chunkID[1]='I';
+  waveHeader.chunkID[2]='F';
+  waveHeader.chunkID[3]='F';
+
+  waveHeader.format[0]='W';
+  waveHeader.format[1]='A';
+  waveHeader.format[2]='V';
+  waveHeader.format[3]='E';
+
+
+  waveHeader.subChunk1ID[0]='f';
+  waveHeader.subChunk1ID[1]='m';
+  waveHeader.subChunk1ID[2]='t';
+  waveHeader.subChunk1ID[3]=' ';
+
+  waveHeader.subChunk2ID[0]='d';
+  waveHeader.subChunk2ID[1]='a';
+  waveHeader.subChunk2ID[2]='t';
+  waveHeader.subChunk2ID[3]='a';
+
+  waveHeader.subChunk1Size=16;      // always 16 for PCM
+  waveHeader.audioFormat=1;         // PCM
+  waveHeader.numChannels=sizeof(SOUNDFRAME)/sizeof(quint16);         // Stereo
+  waveHeader.sampleRate=samplingrate;
+  waveHeader.byteRate=sizeof(SOUNDFRAME)*samplingrate;    // 16 bit samples
+  waveHeader.blockAlign=4;
+  waveHeader.bitsPerSample=16;
+  waveHeader.chunkSize=36+numberOfSamples*sizeof(short int);
+  waveHeader.subChunk2Size=numberOfSamples*sizeof(short int);
+}
+
+bool  wavIO::checkString(char *str,const char *cstr)
+{
+  for (int i=0;i<4;i++)
+    {
+      if (str[i]!=cstr[i]) return false;
+    }
+  return true;
+}
+
+bool  wavIO::writeHeader()
+{
+  int err;
+  waveHeader.subChunk2Size=numberOfSamples*sizeof(quint16)*numberOfChannels;
+  lseek(inopf.handle(),0,SEEK_SET); //position at beginning
+  if((err=inopf.write(&waveHeader.chunkID[0],sizeof(sWave)))!=sizeof(sWave))
+    {
+
+      addToLog(QString("wavio write header error %1").arg(err),LOGWAVIO);
+      closeFile();
+      return false;
+    }
+  inopf.flush();
+  lseek(inopf.handle(),0,SEEK_END); //position at beginning
+  addToLog(QString("wavio write header %1 %2 %3 %4").arg(waveHeader.chunkID[0]).arg(waveHeader.chunkID[1]).arg(waveHeader.chunkID[2]).arg(waveHeader.chunkID[3]),LOGWAVIO);
+  addToLog(QString("wavio write header samples=%1").arg(numberOfSamples),LOGWAVIO);
+  addToLog(QString("wavio write header total bytes=%1").arg(numberOfSamples*2+sizeof(sWave)),LOGWAVIO);
+  return true;
+}
diff --git a/qsstv/sound/wavio.h b/qsstv/sound/wavio.h
new file mode 100644
index 0000000..a369716
--- /dev/null
+++ b/qsstv/sound/wavio.h
@@ -0,0 +1,119 @@
+#ifndef WAVEIO_H
+#define WAVEIO_H
+
+#include <qfile.h>
+#include "qsstvdefs.h"
+
+/*! a WAVE format structure
+
+		The canonical WAVE format starts with the RIFF header:
+    \verbatim
+0         4   ChunkID          Contains the letters "RIFF" in ASCII form
+                               (0x52494646 big-endian form).
+4         4   ChunkSize        36 + SubChunk2Size, or more precisely:
+                               4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
+                               This is the size of the rest of the chunk 
+                               following this number.  This is the size of the 
+                               entire file in bytes minus 8 bytes for the
+                               two fields not included in this count:
+                               ChunkID and ChunkSize.
+8         4   Format           Contains the letters "WAVE"
+                               (0x57415645 big-endian form).
+
+The "WAVE" format consists of two subchunks: "fmt " and "data":
+The "fmt " subchunk describes the sound data's format:
+
+12        4   Subchunk1ID      Contains the letters "fmt "
+                               (0x666d7420 big-endian form).
+16        4   Subchunk1Size    16 for PCM.  This is the size of the
+                               rest of the Subchunk which follows this number.
+20        2   AudioFormat      PCM = 1 (i.e. Linear quantization)
+                               Values other than 1 indicate some 
+                               form of compression.
+22        2   NumChannels      Mono = 1, Stereo = 2, etc.
+24        4   SampleRate       8000, 44100, etc.
+28        4   ByteRate         == SampleRate * NumChannels * BitsPerSample/8
+32        2   BlockAlign       == NumChannels * BitsPerSample/8
+                               The number of bytes for one sample including
+                               all channels. I wonder what happens when
+                               this number isn't an integer?
+34        2   BitsPerSample    8 bits = 8, 16 bits = 16, etc.
+          2   ExtraParamSize   if PCM, then doesn't exist
+          X   ExtraParams      space for extra parameters
+
+The "data" subchunk contains the size of the data and the actual sound:
+
+36        4   Subchunk2ID      Contains the letters "data"
+                               (0x64617461 big-endian form).
+40        4   Subchunk2Size    == NumSamples * NumChannels * BitsPerSample/8
+                               This is the number of bytes in the data.
+                               You can also think of this as the size
+                               of the read of the subchunk following this 
+                               number.
+44        *   Data             The actual sound data.
+
+		\endverbatim 
+*/
+
+
+struct sWave
+{
+  char chunkID[4];					//!< Contains the letters "RIFF"
+  int  chunkSize;						//!< 36 + SubChunk2Size
+  char format[4];	 					//!< Contains the letters "WAVE"
+  char subChunk1ID[4]; 			//!< Contains the letters "fmt "
+  int  subChunk1Size;				//!< 16 for PCM
+  short int audioFormat;		//!< PCM = 1 (i.e. Linear quantization)
+  short int numChannels;		//!< Mono = 1, Stereo = 2, etc.
+  unsigned int sampleRate;	//!< 8000, 44100, etc.
+  unsigned int byteRate;		//!< == SampleRate * NumChannels * BitsPerSample/8
+  short int blockAlign;			//!< == NumChannels * BitsPerSample/8
+  short int bitsPerSample;	//!< 8 bits = 8, 16 bits = 16, etc.
+  char subChunk2ID[4];			//!< Contains the letters "data"
+  int  subChunk2Size;				//!< NumSamples * NumChannels * BitsPerSample/8
+};
+
+//! class for accessing .wav files
+class wavIO
+{
+public:
+  wavIO(unsigned int samplingR=BASESAMPLERATE);
+	~wavIO();
+	bool openFileForRead(QString fname,bool ask);
+  bool openFileForWrite(QString fname, bool ask, bool isStereo);
+  int  read (quint32 *dPtr, uint len);
+  bool write(quint16 *dPtr, uint len, bool isStereo);
+	void setSamplingrate(int sr) {samplingrate=sr;}
+  int getNumberOfChannels(){return numberOfChannels;}
+  void closeFile();
+
+	/** return the number of samples in the opened file */
+	unsigned int getNumberOfSamples()
+		{
+			return numberOfSamples;
+		}
+	/** close all opened files */
+	void close()
+		{
+    if(inopf.isOpen())
+      {
+      write(NULL,0,false); // flush everything in case we are writing
+			closeFile();
+      }
+		}
+  private:
+    sWave waveHeader;
+    unsigned int numberOfSamples;
+		unsigned int samplesRead;
+		unsigned int samplingrate;
+    int numberOfChannels;
+    QFile inopf;
+    void initHeader();
+		bool writeHeader();
+    bool checkString(char *str,const char *cstr);
+		bool reading;
+		bool writing;
+
+
+};
+#endif
diff --git a/qsstv/sstv/cw.cpp b/qsstv/sstv/cw.cpp
new file mode 100644
index 0000000..15be797
--- /dev/null
+++ b/qsstv/sstv/cw.cpp
@@ -0,0 +1,219 @@
+/***************************************************************************
+                          cw.cpp  -  QSSTV
+                             -------------------
+    begin                : Tue Apr 17 22:27:58 CEST 2001
+    copyright            : (C) 2001 by Johan Maes ON1MH
+    email                : on1mh at pandora.be
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "cw.h"
+#include <ctype.h>
+#include "qsstvglobal.h"
+#include "configparams.h"
+
+
+
+
+enum eCWState {CWNEW,CWNEXTDOT,CWDOTSPACING,CWNEXTCHAR,CWCHARSPACING,CWWORDSPACING,CWEND,CWFINISHED};
+enum  eCWResult {CWIDLE,CWfalse,CWtrue};
+
+static int dotIndex;
+static const char *dotPtr;
+
+static int charIndex;
+static float dotSpacing;
+
+static  eCWState cwState;
+static  eCWResult result;
+//  const char *charLookupCW(char a);
+//  bool sendChar(float &duration);
+//  const char *s;
+
+static struct {
+	char key;
+	const char	*cw;
+} charTable[] = 
+{
+  {'A',	".-"	}, {'B',"-..."  },{'C',	"-.-." },
+  {'D',	"-.."	}, {'E',"."	},{'F',"..-."  },
+  {'G',	"--."	}, {'H',"...."	},{'I',".."    },
+  {'J',	".---"	}, {'K',"-.-"	},{'L',".-.."  },
+  {'M',	"--"	}, {'N',"-."	},{'O',"---"   },
+  {'P',	".--."	}, {'Q',"--.-"	},{'R',".-."   },
+  {'S',	"..."	}, {'T',"-"	},{'U',"..-"   },
+  {'V',	"...-"	}, {'W',".--"	},{'X',"-..-"  },
+  {'Y',	"-.--"	}, {'Z',"--.."	},
+  {'0',	"-----"	}, {'1',".----"	},{'2',	"..---"},
+  {'3',	"...--"	}, {'4',"....-"	},{'5',"....." },
+  {'6',	"-...."	}, {'7',"--..."	},{'8',"---.." },
+  {'9',	"----."	},
+  {'"',	".-..-."}, {'\'', ".----." },{'$',"...-..-"},
+  {'(',	"-.--." }, { ')', "-.--.-" },{'+',".-.-."},
+  {',',	"--..--"}, {'-',  "-....-" },{'.',".-.-.-"},
+  {'/',	"-..-." }, { ':', "---..." },{';',"-.-.-."},
+  {'=',	"-...-" }, { '?', "..--.." },{'_',"..--.-"},
+  {0,	""	}
+};
+
+static QString cwString;
+void initCW(QString cwTxt)
+{ 
+  cwState=CWNEW;
+  dotSpacing=1.2/(float)cwWPM;
+	cwString=cwTxt;
+}
+
+const char *charLookupCW(const char a)
+{
+  char b;
+  int i=0;;
+  b=toupper(a);
+	dotIndex=0;
+  while (charTable[i].key!=0)
+    {
+      if(charTable[i].key==b)
+				{
+					
+	  			return (charTable[i].cw);
+				}
+      i++;
+    }
+  return NULL;
+}
+
+bool nextSymbolCW(float &duration)
+{
+  if (dotPtr[dotIndex]==0)
+    {
+      return false;
+    }
+  else if(dotPtr[dotIndex]=='.')
+    {
+      duration=dotSpacing;
+    }
+  else
+    {
+      duration=3*dotSpacing;
+    }
+  dotIndex++;
+  return true;
+}
+
+
+bool sendTextCW(float &tone,float &duration)
+{
+  result=CWIDLE;
+  do
+    {
+      switch (cwState)
+			{
+				case CWNEW:
+	  			{
+	    			charIndex=0;
+	    			if (cwString[0]==0)
+	      			{
+								result=CWfalse;
+	      			}
+	    			cwState=CWNEXTCHAR;
+	  			}
+	  		break;
+				case CWNEXTCHAR:
+	  			{
+	    			if(cwString[charIndex]==' ')
+	      			{
+								charIndex++;
+								cwState=CWWORDSPACING;
+	      			}
+	    			else
+	      			{
+                dotPtr=charLookupCW(cwString[charIndex++].toLatin1());
+								if (dotPtr==NULL)
+		  						{
+		  							cwState=CWEND;
+		  						}
+								else
+		  						{
+		    						dotIndex=0;
+		    						cwState=CWNEXTDOT;
+		  						}
+	      			}
+	  			}
+	  		break;
+				case CWNEXTDOT:
+	  			{
+	    			if(nextSymbolCW(duration))
+	      			{
+								tone=(float)cwTone;
+								cwState=CWDOTSPACING;
+								result=CWtrue;
+	      			}
+	    			else
+	      			{
+								cwState=CWCHARSPACING;
+	      			}
+	  			}
+	  		break;
+				case CWDOTSPACING:
+	  			{
+	    			tone=0;
+	    			duration=dotSpacing;
+	    			cwState=CWNEXTDOT;
+	    			result=CWtrue;
+	  			}
+	  		break;
+				case CWCHARSPACING:
+	  			{
+	    			tone=0;
+	    			duration=2*dotSpacing;  // we already had a dotspace
+	    			cwState=CWNEXTCHAR;
+	   				result=CWtrue;
+	  			}
+	  		break;
+				case CWWORDSPACING:
+	  			{
+	    			tone=0;
+	    			duration=4*dotSpacing; // we already had a charspace
+	    			cwState=CWNEXTCHAR;
+	    			result=CWtrue;
+	  			}
+	  		break;
+	
+				case CWEND:
+	  			{
+	    			tone=0;
+	    			duration=7*dotSpacing;
+	    			cwState=CWFINISHED;
+	    			result=CWtrue;
+	  			}
+	  		break;
+				case CWFINISHED:
+	  			{
+	    			result=CWfalse;
+	  			}
+	  		break;
+	  
+			}
+    }
+	while(result==CWIDLE);
+	return (result==CWtrue);
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qsstv/sstv/cw.h b/qsstv/sstv/cw.h
new file mode 100644
index 0000000..e19fb5d
--- /dev/null
+++ b/qsstv/sstv/cw.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+                          cw.h  -  QSSTV
+                             -------------------
+    begin                : Tue Apr 17 22:27:58 CEST 2001
+    copyright            : (C) 2001 by Johan Maes ON1MH
+    email                : on1mh at pandora.be
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 CW_H
+#define CW_H
+#include <qstring.h>
+
+void initCW(QString cwTxt);
+bool sendTextCW(float &tone,float &duration);
+
+
+
+
+#endif
diff --git a/qsstv/sstv/modes/modeavt.cpp b/qsstv/sstv/modes/modeavt.cpp
new file mode 100644
index 0000000..2816415
--- /dev/null
+++ b/qsstv/sstv/modes/modeavt.cpp
@@ -0,0 +1,267 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "modeavt.h"
+#include "configparams.h"
+// one number is 1 startbit + 16 databits.
+#define WORDTIME (5.3108/32.)
+#define BITTIME (WORDTIME/17)
+
+modeAVT::modeAVT(esstvMode m,unsigned int len, bool tx):modeBase(m,len,tx)
+{
+  avtTrailerDetect=true;
+  trailerState=D1900;
+  code=0;
+  duration=0;
+}
+
+
+modeAVT:: ~modeAVT()
+{
+}
+
+
+void modeAVT::setupParams(double clock)
+{
+  visibleLineLength=(lineLength(mode,clock))/3.;
+}
+
+
+modeBase::eModeBase modeAVT::process(int *demod,unsigned int syncPos,bool goToSync)
+{
+  unsigned int i=0;
+  unsigned char a,b;
+  if(goToSync)
+    {
+      if(syncPos >=length)
+        {
+          addToLog(QString("modebase:process: syncPos: %1 > length %2").arg(syncPos).arg(length),LOGMODES);
+          return MBENDOFIMAGE;
+        }
+      else
+        {
+            for(i=0;i<syncPos;i++)  debugStatePtr[i]=debugState;
+        }
+    }
+  if(avtTrailerDetect)
+  {
+      for(;i<length;i++)
+        {
+          duration++;
+          sample=demod[i];
+          debugStatePtr[i]=debugState;
+          avgSample+=0.9*(sample-avgSample);
+          switch(trailerState)
+            {
+                case D1900:
+                  if(fabs(avgSample-1900.) <50.)
+                    {
+                      debugState=st1900B;
+                      count++;
+                    }
+                  else
+                    {
+                      if (count >0) count --;
+                      else duration=0;
+                    }
+                  if(count>50)
+                    {
+                      count=10;
+                      trailerState=D1900END;
+                    }
+                break;
+                case D1900END:
+                  if(fabs(avgSample-1900.) <50.)
+                    {
+                      if(count<10) count++;
+                    }
+                  else
+                    {
+                      debugState=st1900E;
+                      if (count >0) count --;
+                    }
+                  if (count==0)
+                    {
+                      duration-=5;
+                      if((duration<(unsigned int)(0.011*rxClock)) && (duration>(unsigned int)(0.009*rxClock)))
+                        {
+                          bitCounter=0;
+                          count=10;
+                          trailerState=DELAYHALF;
+                        }
+                       else
+                        {
+                         duration=0;
+                         trailerState=D1900;
+                        }
+                    }
+                break;
+                case DELAYHALF:
+                  debugState=stHALF;
+                  count++;
+                  code=0;
+                  if(count>=(unsigned int)(round((BITTIME/2)*rxClock))) trailerState=BITS;
+                break;
+                case BITS:
+                  debugState=stBITS+bitCounter;
+                  code=code<<1;
+                  if(avgSample>1900.) code|=0x0001;
+                  bitCounter++;
+                  if (bitCounter==16) trailerState=CALCDELAY;
+                  else
+                    {
+                      count=0;
+                      trailerState=DELAYFULL;
+                    }
+                break;
+                case DELAYFULL:
+                  debugState=stFULL;
+                  count++;
+                  if(count>=(unsigned int)(round(BITTIME*rxClock))) trailerState=BITS;
+                break;
+                case CALCDELAY:
+                  //check if
+                  a=code>>8;
+                  b=(code&0xFF)^0xFF;
+                  addToLog(QString("avtcode =%1 mode=%1,pos=%1").arg(QString::number(code,16)).arg((code&0xE000)>>13).arg((code&0x1F00)>>8),LOGMODES);
+                  count=0;
+                  duration=0;
+                  if(a!=b)
+                    {
+                      trailerState=D1900;
+                      break;
+                    }
+                   a&=0x1F;
+                   delay=(unsigned int)(((31-a)*WORDTIME+BITTIME/2)*rxClock);
+                   trailerState=WAITSTART;
+                break;
+                case WAITSTART:
+                  debugState=stWAIT;
+                  delay--;
+                  if(delay==0)
+                    {
+                      avtTrailerDetect=false;
+                      debugState=stColorLine0;
+                    return modeBase::process(demod,i,true);
+                    }
+                break;
+            }
+        }
+
+  }
+
+
+  else
+    {
+      return modeBase::process(demod);
+    }
+  return MBRUNNING;
+}
+
+
+
+modeBase::embState modeAVT::rxSetupLine()
+{	
+  start=lineTimeTableRX[lineCounter];
+//	addToLog(QString("modeAVT: subLine %1").arg(subLine),LOGMODES);
+	
+  switch(subLine)
+		{
+			case 0:
+        calcPixelPositionTable(REDLINE,false);
+        pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+      case 1:
+        calcPixelPositionTable(GREENLINE,false);
+        pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+      case 2:
+        calcPixelPositionTable(BLUELINE,false);
+        pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			break;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeAVT::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+	debugState=stColorLine0+colorLine;
+	
+	switch (colorLine)
+		{
+      case REDLINE:
+
+			break;
+      case GREENLINE:
+        start+=(visibleLineLength);
+			break;
+      case BLUELINE:
+        start+=(2.*visibleLineLength);
+			break;
+		}
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+      pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+		}
+}
+
+
+modeBase::embState modeAVT::txSetupLine()
+{
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(GREENLINE,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 2:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 3:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 4:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 5:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 6:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
diff --git a/qsstv/sstv/modes/modeavt.h b/qsstv/sstv/modes/modeavt.h
new file mode 100644
index 0000000..534108a
--- /dev/null
+++ b/qsstv/sstv/modes/modeavt.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef MODEAVT_H
+#define MODEAVT_H
+
+#include "modebase.h"
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeAVT : public modeBase
+{
+  enum eTrailerState {D1900,D1900END,DELAYHALF,DELAYFULL,BITS,CALCDELAY,WAITSTART};
+public:
+  modeAVT(esstvMode m,unsigned int len,bool tx);
+  ~modeAVT();
+  eModeBase process(int *demod, unsigned int syncPos=0, bool goToSync=false);
+protected:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+  embState txSetupLine();
+  bool avtTrailerDetect;
+  unsigned int duration;
+  unsigned int bitCounter;
+  unsigned int code;
+  unsigned int count;
+  eTrailerState trailerState;
+  DSPFLOAT avgSample;
+  unsigned int delay;
+};
+
+
+#endif
diff --git a/qsstv/sstv/modes/modebase.cpp b/qsstv/sstv/modes/modebase.cpp
new file mode 100644
index 0000000..7e31dd7
--- /dev/null
+++ b/qsstv/sstv/modes/modebase.cpp
@@ -0,0 +1,532 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "modebase.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include "dispatcher.h"
+#include "dsp/synthes.h"
+#include "rxwidget.h"
+#include "txwidget.h"
+#include <QApplication>
+
+
+modeBase::modeBase(esstvMode m,unsigned int len,bool tx)
+{
+	mode=m;
+	transmit=tx;
+	length=len;
+	greenArrayPtr=NULL;
+	blueArrayPtr=NULL;
+	redArrayPtr=NULL;
+	yArrayPtr=NULL;
+	pixelArrayPtr=NULL;
+	pixelPositionTable=NULL;
+	debugStatePtr=NULL;
+  addToLog(QString("mb constructor mode=%1").arg((int) m),LOGMODES);
+  if(transmit)
+    {
+      localClock=txClock;
+      activeSSTVParam=&txSSTVParam;
+    }
+  else
+    {
+      localClock=rxClock;
+      activeSSTVParam=&rxSSTVParam;
+    }
+}
+
+modeBase::~modeBase()
+{
+	deleteBuffers();
+}
+
+
+/**
+    \brief delete existing buffers
+
+		Deletes all buffers, and sets the pointers to NULL;
+ */
+void modeBase::deleteBuffers()
+{
+	if(pixelPositionTable) delete [] pixelPositionTable;
+	if(greenArrayPtr) delete [] greenArrayPtr;
+	if(blueArrayPtr) delete [] blueArrayPtr;
+	if(redArrayPtr) delete [] redArrayPtr;
+	if(yArrayPtr) delete [] yArrayPtr;
+	if(debugStatePtr) delete [] debugStatePtr;
+	greenArrayPtr=blueArrayPtr=redArrayPtr=yArrayPtr=NULL;
+}
+
+/**
+	\brief initialize the selected mode
+
+	This function initializes all buffers and mode parameters. The localClock will be set to rxClk if rxClk is not zero in receive mode, else the rxClock of the configuration will be used. The localClock is always equal to the txClock from the configuration while in transmit mode;
+	\param[in] rxClk adjusted receive clock
+*/
+
+void modeBase::init(DSPFLOAT clk)
+{
+	if(!transmit)
+		{
+      if(clk==0.) localClock=clk;
+      else localClock=clk;
+		}
+	lineCounter=0;
+	displayLineCounter=0;
+	subLine=0;
+	sampleCounter=0;
+  setupSSTVLineTimeTable(mode,localClock,transmit);
+	state=MBSETUPLINE;
+	debugState=stHUNT;
+	deleteBuffers();
+  pixelPositionTable=new unsigned int[activeSSTVParam->numberOfPixels];
+  greenArrayPtr=new unsigned char[activeSSTVParam->numberOfPixels];
+  blueArrayPtr=new unsigned char[activeSSTVParam->numberOfPixels];
+  redArrayPtr=new unsigned char[activeSSTVParam->numberOfPixels];
+  yArrayPtr=new unsigned char[activeSSTVParam->numberOfPixels];
+	debugStatePtr=new unsigned char [length];
+  for(unsigned int i=0;i<length;i++)
+  {
+    debugStatePtr[i]=0;
+  }
+	if(transmit)
+		{
+      fp=activeSSTVParam->fpt*localClock;
+      bp=activeSSTVParam->bpt*localClock;
+      blank=activeSSTVParam->blankt*localClock;
+		}
+	else
+		{
+      fp=activeSSTVParam->fp*localClock;
+      bp=activeSSTVParam->bp*localClock;
+      blank=activeSSTVParam->blank*localClock;
+		}
+  syncDuration=activeSSTVParam->sync*localClock;
+	setupParams(localClock);
+  activeSSTVParam->pixelDuration=visibleLineLength/(double)activeSSTVParam->numberOfPixels;
+//  if(transmit)
+//    {
+//      txWidgetPtr->getImageViewerPtr()->createImage(QSize(activeSSTVParam->numberOfPixels,activeSSTVParam->numberOfDisplayLines),QColor(128,128,128));
+//    }
+//  else
+//    {
+//     rxWidgetPtr->getImageViewerPtr()->createImage(QSize(activeSSTVParam->numberOfPixels,activeSSTVParam->numberOfDisplayLines),QColor(128,128,128));
+//    }
+}
+
+
+void modeBase::redrawFast(bool r)
+{
+	fastRedraw=r;
+	if (!fastRedraw)
+		{
+			lineDisplayEvent *ce= new lineDisplayEvent(displayLineCounter);
+      QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+		}
+}
+
+modeBase::eModeBase modeBase::process(int *demod,unsigned int syncPos,bool goToSync)
+{
+	unsigned int i=0;
+
+	if(goToSync)
+		{
+			if(syncPos >=length)
+				{
+          addToLog(QString("modebase:process: syncPos: %1 > length %2").arg(syncPos).arg(length),LOGMODES);
+					return MBENDOFIMAGE;
+				}
+			else
+				{
+            for(i=0;i<syncPos;i++)  ;//debugStatePtr[i]=debugState;
+				}
+      rxSampleCounter+=syncPos;
+		}
+	for(;i<length;i++)
+		{
+			if(state==MBSETUPLINE)
+				{
+          state=rxSetupLine();
+					if(state==MBENDOFLINE)
+						{
+							showLine();
+							if (!fastRedraw)
+									{
+                    lineDisplayEvent *ce= new lineDisplayEvent(displayLineCounter);
+                    QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+									}
+							lineCounter++;
+              if (displayLineCounter>=activeSSTVParam->numberOfDisplayLines)
+								{
+						 			state=MBEOIMAGE;
+								}
+							else
+								{
+									subLine=0;
+                  state=rxSetupLine();
+								}
+						}
+					pixelCounter=0;
+					subLine++;
+				}
+			sample=demod[i];
+			debugStatePtr[i]=debugState;
+//			pos=(++pos&DSPINPUTINDEXMASK);
+			
+			switch(state)
+				{
+					case MBPIXELS:
+						if(getPixels())
+							{
+							 	state=MBSETUPLINE;
+							}
+					break;
+					case MBEOIMAGE:
+						return  MBENDOFIMAGE;
+					break;
+					case MBRXWAIT:
+						if(sampleCounter>=marker)
+							{
+              //  addToLog(QString("modebase:mbrxwait =%1").arg(sampleCounter+rxSampleCounter),LOGMODES);
+								state=MBSETUPLINE;
+							}
+					break;
+					case MBSYNC:
+						{
+							if(sampleCounter>=syncPosition)
+								{
+              //    addToLog(QString("modebase:mbsync =%1").arg(sampleCounter+rxSampleCounter),LOGMODES);
+									state=MBSETUPLINE;
+								}
+						}
+					break;					
+					default:
+            addToLog(QString("unknown state in modeBase: %1 receive").arg((int)state),LOGMODES);
+					break;
+				}
+			sampleCounter++;
+		}
+	return MBRUNNING;
+}
+
+/**
+	\brief transfer pixels to pixelArrayPtr
+
+	This function checks the sampleCounter and stores the pixel in the pixelArray 
+	\return true if end of line (all pixels stored)
+*/
+
+bool modeBase::getPixels()
+{
+	int color;
+  double dev=activeSSTVParam->deviation*2;
+  double fc=activeSSTVParam->subcarrier;
+  if(sampleCounter>=pixelPositionTable[pixelCounter]+(activeSSTVParam->pixelDuration/2))
+    {
+//      addToLog(QString("modebase:getPixels[0] =%1").arg(sampleCounter+rxSampleCounter),LOGMODES);
+      color=128+(int)round(((double)sample-fc)*255./dev);
+      if(color<0) color=0; if (color>255) color=255;
+      pixelArrayPtr[pixelCounter]=(unsigned char)color;
+      pixelCounter++;
+      if(pixelCounter>=activeSSTVParam->numberOfPixels) return true;
+		}
+	return false; // indicate, it's not the end of the line
+}	
+
+/**
+	\brief tranfer data to rxImage (mode depended)
+
+	This function is the default behaviour. it calls combineColors(). This function must be reimplemented in the derived classes for all other colour modes.
+*/
+
+void modeBase::showLine()
+{
+	combineColors();
+}
+/**
+	\brief tranfer data to rxImage in RGB mode
+
+	Combine  R, G and B arrays (like in Martin mode) into the rxImage and advances the displayCounter 	
+*/
+
+void modeBase::combineColors()
+{
+  unsigned int i;
+  QRgb *pixelArray=rxWidgetPtr->getImageViewerPtr()->getScanLineAddress(displayLineCounter);
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+    {
+      pixelArray[i]=qRgb(redArrayPtr[i],greenArrayPtr[i],blueArrayPtr[i]);
+//        pixelArray[i]=qRgb(255,0,0);
+    }
+  displayLineCounter++;
+}
+
+
+
+/**
+	\brief tranfer data to rxImage in grayscale
+
+	Black and White image transfer. greenArray contains the luminance info.
+*/
+
+
+void modeBase::grayConversion()
+{
+	unsigned int i;
+  QRgb *pixelArray=rxWidgetPtr->getImageViewerPtr()->getScanLineAddress(displayLineCounter);
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+			pixelArray[i]=qRgb(greenArrayPtr[i],greenArrayPtr[i],greenArrayPtr[i]);
+		}
+	displayLineCounter++;
+}
+
+/**
+	\brief tranfer data to rxImage in YUV mode
+
+	Combine  Y, U  and V arrays (like in PD modes) into the rxImage and advances the displayCounter 	
+*/
+
+void modeBase::yuvConversion(unsigned char *array)
+{
+	unsigned int i;
+	int r,g,b;
+  QRgb *pixelArray=rxWidgetPtr->getImageViewerPtr()->getScanLineAddress(displayLineCounter);
+  for (i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+			r=(100*array[i]+140*redArrayPtr[i]-17850)/100;
+			b=(100*array[i]+178*blueArrayPtr[i]-22695)/100;
+			g=(100*array[i]- 71*redArrayPtr[i]-33*blueArrayPtr[i]+13260)/100;
+			r=(r>255 ? 255 : r);
+			r=(r<0 ? 0 : r);
+		  b=(b>255 ? 255 : b);
+		  b=(b<0 ? 0 : b);
+		  g=(g>255 ? 255 : g);
+		  g=(g<0 ? 0 : g);
+		  pixelArray[i]=qRgb(r,g,b);
+		}
+	displayLineCounter++;
+}
+
+modeBase::eModeBase modeBase::transmitImage(imageViewer *iv)
+{
+  txImPtr=iv;
+  if(!iv->hasValidImage()) return MBENDOFIMAGE;
+  displayLineCounter=0;
+  lineCounter=0;
+  getLine();
+  state=MBSETUPLINE;
+  start=0;
+  abortRun=false;
+  while(!abortRun)
+    {
+      if(state==MBSETUPLINE)
+        {
+          state=txSetupLine();
+          subLine++;
+          pixelCounter=0;
+        }
+      switch (state)
+        {
+          case MBPIXELS:
+            {
+              addToLog(QString("MBPIXELS: samplcntr=%1").arg(sampleCounter),LOGMODES);
+              sendPixelBuffer();
+              state=MBSETUPLINE; // check for end of subline
+            }
+          break;
+          case MBTXGAP:
+            {
+       //       addToLog(QString("MBTXGAP: samplcntr=%1").arg(sampleCounter),LOGMODES);
+              synthesPtr->sendSamples(txDur,txFreq); //expressed in samples;
+              sampleCounter+=txDur;
+
+              state=MBSETUPLINE;
+          }
+        break;
+        case MBENDOFLINE:
+          {
+       //     addToLog(QString("MBENDOFLINE samplcntr=%1 line: %2").arg(sampleCounter).arg(lineCounter),LOGMODES);
+            if(++lineCounter>=activeSSTVParam->numberOfDataLines) state=MBEOIMAGE;
+            else
+              {
+               getLine();
+               state=MBSETUPLINE;
+               subLine=0;
+              }
+
+           }
+        break;
+        default:
+       //   addToLog(QString("default: samplcntr=%1").arg(sampleCounter),LOGMODES);
+          sampleCounter=0;
+          return MBENDOFIMAGE;
+      }
+    }
+  addToLog("abortrun detected",LOGMODES);
+  return MBENDOFIMAGE;
+}
+
+/**
+  \brief abort tx
+
+  Only used for aborting a transmission
+*/
+
+void  modeBase::abort()
+{
+  addToLog("modebase: abort received",LOGMODES);
+  abortRun=true;
+}
+
+/**
+  \brief send pixels in the pixelArray
+
+  This function sends one subLine of colour information contained in the pixelArray.
+
+*/
+
+void modeBase::sendPixelBuffer()
+{
+  double f;
+ // addToLog (QString(" sendPixelBuffer: pixelBuffer: %1").arg(QString::number((ulong)pixelArrayPtr,16)),LOGMODES);
+  do
+    {
+      f=1500.+((double)pixelArrayPtr[pixelCounter]*(2300.-1500.)/255.);
+      while(sampleCounter<pixelPositionTable[pixelCounter])
+        {
+          if(f>2300.) f=2300.;
+          if (f<1500.) f=1500.;
+          synthesPtr->sendSample(f);
+          sampleCounter++;
+        }
+      pixelCounter++;
+    }
+  while(pixelCounter<activeSSTVParam->numberOfPixels);
+//  addToLog(QString("modebase: lpw=%1").arg(sampleCounter),LOGMODES);
+}
+
+/**
+  \brief get YUV information
+
+  Get the YUV information from the image and setup the colour arrays. The displayCounter is incremented by 1 or 2, depending on the mode (e.g. 1 if Robot24 and 2 if Robot36).
+
+*/
+
+void modeBase::getLineY(bool evenodd)
+{
+  // we will process 2 lines at a time
+//	QColor c;
+  int tO,tE;
+  int r,yo,ye,b;
+  if ((displayLineCounter&1) && (evenodd)) return; // only even lines accepted
+//  txImPtr->createImage(QSize(activeSSTVParam->numberOfPixels,activeSSTVParam->numberOfDisplayLines),QColor(128,128,128));
+  unsigned int *pixelArrayE=txImPtr->getScanLineAddress(displayLineCounter);
+  if (evenodd)
+    {
+      unsigned int *pixelArrayO=txImPtr->getScanLineAddress(displayLineCounter+1);
+      for (unsigned int i=0;i<activeSSTVParam->numberOfPixels;i++)
+        {
+          tE=pixelArrayE[i];
+          ye=(59*qGreen(tE)+30*qRed(tE)+11*qBlue(tE))/100;
+          tO=pixelArrayO[i];
+          yo=(59*qGreen(tO)+30*qRed(tO)+11*qBlue(tO))/100;
+          r=(qRed(tO)+qRed(tE))/2;
+          b=(qBlue(tO)+qBlue(tE))/2;
+          r=(10*r-5*(yo+ye)+7*255)/14;
+          b=(100*b-50*(yo+ye)+89*255)/178;
+          redArrayPtr[i]=(r>255 ? 255 : r); redArrayPtr[i]=(r<0 ? 0 : r);
+          blueArrayPtr[i]=(b>255 ? 255 : b); blueArrayPtr[i]=(b<0 ? 0 : b);
+          yArrayPtr[i]=(ye>255 ? 255 : ye); yArrayPtr[i]=(ye<0 ? 0 : ye);
+          greenArrayPtr[i]=(yo>255 ? 255 : yo); greenArrayPtr[i]=(yo<0 ? 0 : yo);
+        }
+      displayLineCounter++;
+    }
+  else
+    {
+//			addToLog(QString("getline=%1").arg(lineCounter),LOGMODES);
+      for (unsigned int i=0;i<activeSSTVParam->numberOfPixels;i++)
+        {
+          tE=pixelArrayE[i];
+          ye=(59*qGreen(tE)+30*qRed(tE)+11*qBlue(tE))/100;
+          r=qRed(tE);
+          b=qBlue(tE);
+          r=(10*r-10*(ye)+7*255)/14;
+          b=(100*b-100*(ye)+89*255)/178;
+          redArrayPtr[i]=(r>255 ? 255 : r<0 ? 0 : r);
+          blueArrayPtr[i]=(b>255 ? 255 : b<0 ? 0 : b);
+          yArrayPtr[i]=(ye>255 ? 255 : ye<0 ? 0 : ye);
+        }
+    }
+  displayLineCounter++;
+}
+
+/**
+  \brief get luminance (B&W) information
+
+  Get the luminance information from the image and setup the colour array. The displayCounter is incremented
+
+*/
+
+void modeBase::getLineBW()
+{
+
+  unsigned int t;
+//  txImPtr->createImage(QSize(activeSSTVParam->numberOfPixels,activeSSTVParam->numberOfDisplayLines),QColor(128,128,128));
+  unsigned int *pixelArray=txImPtr->getScanLineAddress(displayLineCounter);
+  for (unsigned int i=0;i<activeSSTVParam->numberOfPixels;i++)
+    {
+      t=pixelArray[i];
+      greenArrayPtr[i]=qGray(t);
+    }
+  displayLineCounter++;
+}
+
+
+/**
+  \brief get RGB information
+
+  Get the RGB information from the image and setup the colour arrays. The displayCounter is incremented
+
+*/
+
+void modeBase::getLine()
+{
+  unsigned int t;
+
+//  txImPtr->createImage(QSize(activeSSTVParam->numberOfPixels,activeSSTVParam->numberOfDisplayLines),QColor(128,128,128));
+//  addToLog (QString(" Bufferpointers: green: %1 red %2, blue %3")
+//      .arg(QString::number((ulong)greenArrayPtr,16))
+//      .arg(QString::number((ulong)redArrayPtr,16))
+//      .arg(QString::number((ulong)blueArrayPtr,16)),LOGMODES);
+  unsigned int *pixelArray=txImPtr->getScanLineAddress(displayLineCounter);
+  for (unsigned int i=0;i<activeSSTVParam->numberOfPixels;i++)
+    {
+      t=pixelArray[i];
+      greenArrayPtr[i]=qGreen(t);
+      redArrayPtr[i]=qRed(t);
+      blueArrayPtr[i]=qBlue(t);
+//            greenArrayPtr[i]=255;
+//            redArrayPtr[i]=0;
+//            blueArrayPtr[i]=0;
+    }
+  displayLineCounter++;
+}
diff --git a/qsstv/sstv/modes/modebase.h b/qsstv/sstv/modes/modebase.h
new file mode 100644
index 0000000..fdbbb00
--- /dev/null
+++ b/qsstv/sstv/modes/modebase.h
@@ -0,0 +1,159 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef MODEBASE_H
+#define MODEBASE_H
+#include "../sstvparam.h"
+#include "widgets/imageviewer.h"
+#include <math.h>
+
+#define stHUNT			 0
+#define stColorLine0 1 
+#define stColorLine1 2
+#define stColorLine2 3
+#define stColorLine3 4
+#define stColorLine4 5
+#define stG1				 6
+#define stG2				 7
+#define stRepTone		 8
+#define stWaitVIS		 9
+#define stSTART			10
+#define stWaitSync	11
+#define stFP				12
+#define stBP				13
+#define stSync			14
+#define st1900B			15
+#define st1900E     16
+#define stWAIT      17
+#define stHALF      18
+#define stFULL      19
+#define stBITS			20
+
+
+
+class imageViewer;
+
+class modeBase
+{
+public:
+	enum embState {MBERROR,MBSETUPLINE,MBPIXELS,MBSYNC,MBENDOFLINE,MBEOIMAGE,MBRXWAIT,MBTXGAP,MBTXGAPROBOT};
+	enum eModeBase {MBRUNNING,MBENDOFIMAGE};
+	modeBase(esstvMode m,unsigned int len,bool tx);
+	virtual ~modeBase();
+  /*!
+    \brief initialize mode specific items
+
+    This function is called by modeInit(), and can overwrite some or all of the local parameters. At least it should set the visible length.
+    \param[in] clock  can be the rxClock or the txClock
+*/
+	virtual void setupParams(double clock)=0;
+
+	virtual bool getPixels();
+	virtual unsigned long adjustSyncPosition(unsigned long syncPos)
+		{
+			return syncPos;
+		}
+
+	void redrawFast(bool r);
+  virtual eModeBase process(int *demod,unsigned int syncPos=0,bool goToSync=false);
+  void init(DSPFLOAT clk);
+	unsigned char *debugStatePtr;
+  void abort();
+  esstvMode getMode() { return mode;}
+  eModeBase transmitImage(imageViewer *iv);
+  void setRxSampleCounter(int sc) { rxSampleCounter=sc;}
+  void saveImage();
+  int receivedLines() {return displayLineCounter;}
+  int imageLines() {return activeSSTVParam->numberOfDisplayLines;}
+  int imagePixels() {return activeSSTVParam->numberOfPixels;}
+protected:
+	DSPFLOAT visibleLineLength;
+	esstvMode mode;
+	bool transmit;
+	bool fastRedraw;
+	DSPFLOAT fp;
+	DSPFLOAT bp;
+	DSPFLOAT blank;
+	DSPFLOAT syncDuration;
+	DSPFLOAT localClock;
+
+  int sample;
+  DSPFLOAT start;
+  sSSTVParam *activeSSTVParam;
+
+
+	unsigned int marker;
+	unsigned int syncPosition;
+	unsigned int syncEndPosition;
+	unsigned int lineCounter;
+	unsigned int displayLineCounter;
+	unsigned int pixelCounter;
+	unsigned int sampleCounter;
+	embState state;
+	unsigned int subLine;
+	unsigned int length;
+
+	unsigned char *greenArrayPtr;
+	unsigned char *blueArrayPtr;
+	unsigned char *redArrayPtr;
+	unsigned char *yArrayPtr;
+	unsigned char *pixelArrayPtr;
+	unsigned int *pixelPositionTable;
+	unsigned char debugState;
+
+	DSPFLOAT txFreq;
+	unsigned int txDur;
+	void deleteBuffers();
+	virtual void showLine();
+	void combineColors();
+	void yuvConversion(unsigned char *array);
+  void grayConversion();
+  /*!
+      \brief setup the rx mode timing for one subline  at a time.
+
+      Each line is subdivided in subLines. A subLine can be a pixel line, a delay or a sync. This function is called from the receive function in the modebase.
+      \param subLine  active phase in the reception of a image line
+  */
+  virtual embState rxSetupLine()=0;
+
+/**
+      \brief setup the tx mode timing for one subline  at a time.
+
+      Each line is subdivided in subLines. A subLine can be a pixel line, a delay or a sync. This function is called from transmit function in the modebase.
+      \param subln  active phase in the reception of a image line
+  */
+  virtual embState txSetupLine()=0;
+
+  virtual void getLine();
+  void getLineY(bool evenodd);
+  void getLineBW();
+  int rxSampleCounter;
+
+
+private:
+  void sendPixelBuffer();
+  bool abortRun;
+  imageViewer *txImPtr;
+
+
+
+};
+
+#endif
diff --git a/qsstv/sstv/modes/modebw.cpp b/qsstv/sstv/modes/modebw.cpp
new file mode 100644
index 0000000..9fc7ede
--- /dev/null
+++ b/qsstv/sstv/modes/modebw.cpp
@@ -0,0 +1,126 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "modebw.h"
+#include "math.h"
+
+modeBW::modeBW(esstvMode m,unsigned int len, bool tx) : modeBase(m,len,tx)
+{
+}
+
+
+modeBW::~modeBW()
+{
+}
+
+
+void modeBW::setupParams(double clock)
+{
+  visibleLineLength=(lineLength(mode,clock)-fp-bp-syncDuration);
+}
+
+modeBase::embState modeBW::rxSetupLine()
+{
+  start=lineTimeTableRX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(GREENLINE,false);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				debugState=stSync;
+        syncPosition=(unsigned int)round(lineTimeTableRX[lineCounter+1]);
+				return MBSYNC;
+			break;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+
+void modeBW::showLine()
+{
+	grayConversion();
+}
+
+
+
+void modeBW::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+  double start;
+  if(tx) start=lineTimeTableTX[lineCounter];
+  else start=lineTimeTableRX[lineCounter];
+	debugState=colorLine;
+	start+=bp;
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+      pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+		}
+}
+
+modeBase::embState modeBW::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+        calcPixelPositionTable(GREENLINE,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 2:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 3:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				return MBTXGAP;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+/**
+	\brief get the pixel information for transmission in B&W
+*/
+
+void modeBW::getLine()
+{
+	getLineBW();
+}
+
diff --git a/qsstv/sstv/modes/modebw.h b/qsstv/sstv/modes/modebw.h
new file mode 100644
index 0000000..1d61525
--- /dev/null
+++ b/qsstv/sstv/modes/modebw.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODEBW_H
+#define MODEBW_H
+
+#include "modebase.h"
+
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeBW : public modeBase
+{
+public:
+    modeBW(esstvMode m,unsigned int len, bool tx);
+
+    ~modeBW();
+protected:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+	void showLine();
+  embState txSetupLine();
+	void getLine();
+
+};
+
+#endif
diff --git a/qsstv/sstv/modes/modegbr.cpp b/qsstv/sstv/modes/modegbr.cpp
new file mode 100644
index 0000000..a32e324
--- /dev/null
+++ b/qsstv/sstv/modes/modegbr.cpp
@@ -0,0 +1,147 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "modegbr.h"
+
+
+modeGBR::modeGBR(esstvMode m,unsigned int len, bool tx):modeBase(m,len,tx)
+{
+	
+}
+
+
+modeGBR::~modeGBR()
+{
+}
+
+
+void modeGBR::setupParams(double clock)
+{
+	visibleLineLength=(lineLength(mode,clock)-fp-bp-2*blank-syncDuration)/3.;
+}
+
+modeBase::embState modeGBR::rxSetupLine()
+{	
+  start=lineTimeTableRX[lineCounter];
+  //if(subLine==0) addToLog(QString("modeGBR: subLine %1, line=%2, absSampleCounter %3").arg(subLine).arg(lineCounter).arg(start+rxSampleCounter),DBMODES);
+	
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(GREENLINE,false);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				debugState=stG1;
+				marker=(unsigned int)round(start+bp+blank+visibleLineLength);
+				return MBRXWAIT;
+			case 2:
+				calcPixelPositionTable(BLUELINE,false);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 3:
+				debugState=stG2;
+				marker=(unsigned int)round(start+bp+2*blank+2*visibleLineLength);
+				return MBRXWAIT;
+			case 4:
+				calcPixelPositionTable(REDLINE,false);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 5:
+				debugState=stSync;
+        syncPosition=(unsigned int) round(lineTimeTableRX[lineCounter+1]);
+				return MBSYNC;
+			break;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeGBR::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+	debugState=stColorLine0+colorLine;
+	
+	switch (colorLine)
+		{
+			case GREENLINE:
+				start+=bp;
+ //       addToLog(QString("calcPixelPosition: startGreen %1").arg(start+rxSampleCounter),DBMODES);
+			break;
+			case BLUELINE:
+        start+=(bp+visibleLineLength+blank);
+ //       addToLog(QString("calcPixelPosition: startBlue %1").arg(start+rxSampleCounter),DBMODES);
+			break;
+			case REDLINE:
+        start+=(bp+2.*visibleLineLength+2.*blank);
+ //        addToLog(QString("calcPixelPosition: startRed %1").arg(start+rxSampleCounter),DBMODES);
+			break;
+		}
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+      pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+		}
+}
+
+
+modeBase::embState modeGBR::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(GREENLINE,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 2:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 3:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 4:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 5:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 6:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+      case 7:
+        txFreq=1500;
+        txDur=(unsigned int)rint(bp);
+      return MBTXGAP;
+      default:
+				return MBENDOFLINE;
+		}
+}
+
diff --git a/qsstv/sstv/modes/modegbr.h b/qsstv/sstv/modes/modegbr.h
new file mode 100644
index 0000000..954fa0d
--- /dev/null
+++ b/qsstv/sstv/modes/modegbr.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef MODEGBR_H
+#define MODEGBR_H
+
+#include "modebase.h"
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeGBR : public modeBase
+{
+public:
+	modeGBR(esstvMode m,unsigned int len,bool tx);
+	~modeGBR();
+protected:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+  embState txSetupLine();
+};
+
+
+#endif
diff --git a/qsstv/sstv/modes/modegbr2.cpp b/qsstv/sstv/modes/modegbr2.cpp
new file mode 100644
index 0000000..9490f61
--- /dev/null
+++ b/qsstv/sstv/modes/modegbr2.cpp
@@ -0,0 +1,183 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "modegbr2.h"
+
+
+modeGBR2::modeGBR2(esstvMode m,unsigned int len, bool tx):modeBase(m,len,tx)
+{
+}
+
+
+modeGBR2::~modeGBR2()
+{
+}
+
+void modeGBR2::setupParams(double clock)
+{
+	visibleLineLength=(lineLength(mode,clock)-fp-bp-2*blank-syncDuration)/3.;
+}
+
+modeBase::embState modeGBR2::rxSetupLine()
+{
+	// the start of the Scottie mode is always at the start of the green line 
+  start=lineTimeTableRX[lineCounter];
+ // if(subLine==0) addToLog(QString("modeGBR2: subLine %1, line=%2").arg(subLine).arg(lineCounter),DBMODES);
+  switch(subLine)
+		{
+
+			case 0:
+				calcPixelPositionTable(GREENLINE,false);
+				debugState=stColorLine0;
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				debugState=stG1;
+				marker=(unsigned int)round(start+blank+visibleLineLength);
+				return MBRXWAIT;
+			case 2:
+				calcPixelPositionTable(BLUELINE,false);
+				debugState=stColorLine1;
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 3:
+				debugState=stFP;
+				marker=(unsigned int)round(start+blank+fp+2*visibleLineLength);
+				return MBRXWAIT;
+			case 4:
+				debugState=stSync;
+				syncPosition=syncEndPosition;
+				return MBSYNC;
+			case 5:
+				debugState=stBP;
+				marker=(unsigned int)round(syncEndPosition+bp);
+				return MBRXWAIT;
+			case 6:
+				calcPixelPositionTable(REDLINE,false);
+				debugState=stColorLine2;
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 7:
+				debugState=stG1;
+        marker=(unsigned int)round(lineTimeTableRX[lineCounter]);
+				return MBRXWAIT;
+			break;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeGBR2::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+	debugState=colorLine;
+	switch (colorLine)
+		{
+			case GREENLINE:
+				start+=bp;
+       // addToLog(QString("gbr2: greenstart=%1").arg(start),DBMODES);
+
+			break;
+			case BLUELINE:
+				start+=(bp+blank+visibleLineLength);
+				syncEndPosition=(unsigned int)(start+visibleLineLength+fp+syncDuration);
+
+       // addToLog(QString("gbr2: bluestart=%1").arg(start),DBMODES);
+
+			break;
+			case REDLINE:
+				start+=bp+fp+syncDuration+bp+blank+2.*visibleLineLength;
+       // addToLog(QString("gbr2: redstart=%1").arg(start),DBMODES);
+
+			break;
+		}
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+      pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+		}
+}
+
+
+unsigned long modeGBR2::adjustSyncPosition(unsigned long syncPos)
+{
+  if(syncPos<(unsigned long)(fp+2*visibleLineLength+2*blank+syncDuration))
+  {
+    return syncPos+(unsigned long)(bp+visibleLineLength);
+  }
+  else
+  {
+    return syncPos-(unsigned long)(fp+2*visibleLineLength+2*blank+syncDuration);
+  }
+}
+
+modeBase::embState modeGBR2::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+ // if(subLine==0) addToLog(QString("modeGBR2: subLine %1, line=%2").arg(subLine).arg(lineCounter),DBMODES);
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(GREENLINE,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 2:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 3:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 4:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 5:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				return MBTXGAP;	
+			case 6:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 7:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			default:
+				return MBENDOFLINE;
+		}
+}
diff --git a/qsstv/sstv/modes/modegbr2.h b/qsstv/sstv/modes/modegbr2.h
new file mode 100644
index 0000000..f6d7fcd
--- /dev/null
+++ b/qsstv/sstv/modes/modegbr2.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODEGBR2_H
+#define MODEGBR2_H
+
+#include "modebase.h"
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeGBR2 : public modeBase
+{
+public:
+	modeGBR2(esstvMode m,unsigned int len,bool tx);
+	~modeGBR2();
+
+protected:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+	unsigned long adjustSyncPosition(unsigned long syncPos);
+
+//	int adaptStartPosition(bool vertRetrace);
+  embState txSetupLine();
+};
+
+#endif
diff --git a/qsstv/sstv/modes/modepd.cpp b/qsstv/sstv/modes/modepd.cpp
new file mode 100644
index 0000000..ecf8133
--- /dev/null
+++ b/qsstv/sstv/modes/modepd.cpp
@@ -0,0 +1,206 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "modepd.h"
+
+modePD::modePD(esstvMode m,unsigned int len,bool tx): modeBase(m,len,tx)
+{
+
+}
+
+
+modePD::~modePD()
+{
+
+}
+
+void modePD::setupParams(double clock)
+{
+  visibleLineLength=(lineLength(mode,clock)-fp-bp-3*blank-syncDuration)/4;
+}
+
+modeBase::embState modePD::rxSetupLine()
+{
+  start=lineTimeTableRX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(YLINEODD,false);
+        addToLog(QString("yOdd %1").arg(pixelPositionTable[0]),LOGMODES);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+      case 1:
+        debugState=stG1;
+        start=lineTimeTableRX[lineCounter];
+        marker=(unsigned int)round(start+bp+blank+visibleLineLength);
+        addToLog(QString("mrk1 %1").arg(marker),LOGMODES);
+        return MBRXWAIT;
+      case 2:
+				calcPixelPositionTable(REDLINE,false);
+				pixelArrayPtr=redArrayPtr;
+        addToLog(QString("red %1").arg(pixelPositionTable[0]),LOGMODES);
+				return MBPIXELS;
+      case 3:
+        debugState=stG1;
+        start=lineTimeTableRX[lineCounter];
+        marker=(unsigned int)round(start+bp+2*blank+2*visibleLineLength);
+        addToLog(QString("mrk2 %1").arg(marker),LOGMODES);
+        return MBRXWAIT;
+      case 4:
+				calcPixelPositionTable(BLUELINE,false);
+				pixelArrayPtr=blueArrayPtr;
+        addToLog(QString("blue %1").arg(pixelPositionTable[0]),LOGMODES);
+				return MBPIXELS;
+      case 5:
+        debugState=stG1;
+        start=lineTimeTableRX[lineCounter];
+        marker=(unsigned int)round(start+bp+3*blank+3*visibleLineLength);
+        addToLog(QString("mrk3 %1").arg(marker),LOGMODES);
+        return MBRXWAIT;
+      case 6:
+				calcPixelPositionTable(YLINEEVEN,false);
+				pixelArrayPtr=greenArrayPtr;
+        addToLog(QString("yEven %1").arg(pixelPositionTable[0]),LOGMODES);
+				return MBPIXELS;				
+      case 7:
+				debugState=stSync;
+        syncPosition=(unsigned int)round(lineTimeTableRX[lineCounter+1]);
+				return MBSYNC;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+
+
+void modePD::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+  double start;
+  if(tx) start=lineTimeTableTX[lineCounter];
+  else start=lineTimeTableRX[lineCounter];
+	debugState=colorLine;
+	switch (colorLine)
+		{
+			case YLINEODD:
+				start+=bp;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case REDLINE:
+				start+=(bp+blank+visibleLineLength);
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case BLUELINE:
+				start+=bp+2*blank+2*visibleLineLength;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case YLINEEVEN:
+				start+=bp+(3*blank+3*visibleLineLength);
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+		}
+}
+
+void modePD::showLine()
+{
+	yuvConversion(yArrayPtr);
+	yuvConversion(greenArrayPtr);
+}
+
+modeBase::embState modePD::txSetupLine()
+{
+    start=lineTimeTableTX[lineCounter];
+    switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(YLINEODD,true);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 2:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 3:
+				txFreq=1500;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 4:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 5:
+				txFreq=1500;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 6:
+				calcPixelPositionTable(YLINEEVEN,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 7:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 8:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 9:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				return MBTXGAP;
+			default:
+//				lineCounter++;
+				return MBENDOFLINE;
+		}
+}
+
+
+void  modePD::getLine()
+{
+	getLineY(true);
+}
diff --git a/qsstv/sstv/modes/modepd.h b/qsstv/sstv/modes/modepd.h
new file mode 100644
index 0000000..4c57b6b
--- /dev/null
+++ b/qsstv/sstv/modes/modepd.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODEPD_H
+#define MODEPD_H
+
+#include "modebase.h"
+
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modePD : public modeBase
+{
+public:
+  modePD(esstvMode m,unsigned int len,bool tx);
+	~modePD();
+protected:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+	void showLine();
+  embState txSetupLine();
+	void getLine();
+
+};
+
+#endif
diff --git a/qsstv/sstv/modes/modergb.cpp b/qsstv/sstv/modes/modergb.cpp
new file mode 100644
index 0000000..f6e44d3
--- /dev/null
+++ b/qsstv/sstv/modes/modergb.cpp
@@ -0,0 +1,158 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "modergb.h"
+
+modeRGB::modeRGB(esstvMode m,unsigned int len, bool tx):modeBase(m,len,tx)
+{
+}
+
+#define MODERGBDEBUG
+
+modeRGB::~modeRGB()
+{
+}
+
+void modeRGB::setupParams(double clock)
+{
+	visibleLineLength=(lineLength(mode,clock)-fp-bp-2*blank-syncDuration)/3.;
+}
+
+
+modeBase::embState modeRGB::rxSetupLine()
+{	
+
+  start=lineTimeTableRX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(REDLINE,false);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 1:
+				debugState=stG1;
+				marker=(unsigned int)round(start+blank+visibleLineLength);
+				return MBRXWAIT;
+			case 2:
+				calcPixelPositionTable(GREENLINE,false);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 3:
+				debugState=stG1;
+				marker=(unsigned int)round(start+2*blank+2*visibleLineLength);
+				return MBRXWAIT;				
+			case 4:
+				calcPixelPositionTable(BLUELINE,false);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 5:
+				debugState=stFP;
+				marker=(unsigned int)round(start+2*blank+3*visibleLineLength+fp);
+				return MBRXWAIT;				
+			case 6:
+				debugState=stSync;
+        syncPosition=(unsigned int)round(lineTimeTableRX[lineCounter+1]-bp);
+				return MBSYNC;
+			case 7:
+				debugState=stBP;
+        marker=(unsigned int)round(lineTimeTableRX[lineCounter+1]);
+				return MBRXWAIT;				
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeRGB::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+	debugState=colorLine;
+	switch (colorLine)
+		{
+			case REDLINE:
+				start+=bp;
+			break;
+			case GREENLINE:
+				start+=bp+blank+visibleLineLength;
+			break;
+			case BLUELINE:
+				start+=bp+2.*blank+2.*visibleLineLength;
+			break;
+		}
+  for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+		{
+      pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+
+		}
+}
+
+modeBase::embState modeRGB::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 2:
+				calcPixelPositionTable(GREENLINE,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 3:
+				txFreq=1500.;
+				txDur=(unsigned int)rint(blank);
+				return MBTXGAP;
+			case 4:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 5:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 6:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 7:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				return MBTXGAP;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
diff --git a/qsstv/sstv/modes/modergb.h b/qsstv/sstv/modes/modergb.h
new file mode 100644
index 0000000..20a4263
--- /dev/null
+++ b/qsstv/sstv/modes/modergb.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODERGB_H
+#define MODERGB_H
+
+#include "modebase.h"
+
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeRGB : public modeBase
+{
+public:
+	modeRGB(esstvMode m,unsigned int len,bool tx);
+	~modeRGB();
+private:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+  embState txSetupLine();
+};
+
+#endif
diff --git a/qsstv/sstv/modes/moderobot1.cpp b/qsstv/sstv/modes/moderobot1.cpp
new file mode 100644
index 0000000..146c769
--- /dev/null
+++ b/qsstv/sstv/modes/moderobot1.cpp
@@ -0,0 +1,228 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "moderobot1.h"
+
+modeRobot1::modeRobot1(esstvMode m,unsigned int len, bool tx):modeBase(m,len,tx)
+{
+}
+
+
+modeRobot1::~modeRobot1()
+{
+}
+
+void modeRobot1::setupParams(double clock)
+{
+  visibleLineLength=(lineLength(mode,clock)-fp-bp-blank-syncDuration)/3.;
+}
+
+modeBase::embState modeRobot1::rxSetupLine()
+{
+  start=lineTimeTableRX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(YLINEODD,false);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+			case 1:
+				debugState=stG1;
+				marker=(unsigned int)round(start+blank+2*visibleLineLength);
+				return MBRXWAIT;
+			case 2:
+				calcPixelPositionTable(REDLINE,false);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 3:
+				debugState=stFP;
+				marker=(unsigned int)round(start+blank+3*visibleLineLength+fp);
+				return MBRXWAIT;
+			case 4:
+				debugState=stSync;
+        syncPosition=(unsigned int)round(lineTimeTableRX[lineCounter+1]-bp);
+				return MBSYNC;
+			case 5:
+				debugState=stBP;
+        marker=(unsigned int)round(lineTimeTableRX[lineCounter]);
+				lineCounter++;
+				return MBRXWAIT;		
+			case 6:
+				calcPixelPositionTable(YLINEEVEN,false);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 7:
+				debugState=stG1;
+        marker=(unsigned int)round(lineTimeTableRX[lineCounter]+blank+2*visibleLineLength);
+				return MBRXWAIT;						
+			case 8:
+				calcPixelPositionTable(BLUELINE,false);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 9:
+				debugState=stFP;
+				marker=(unsigned int)round(start+blank+3*visibleLineLength+fp);
+				return MBRXWAIT;				
+			case 10:
+				debugState=stSync;
+        syncPosition=(unsigned int)round(lineTimeTableRX[lineCounter+1]-bp);
+				return MBSYNC;
+			case 11:
+				debugState=stBP;
+        marker=(unsigned int)round(lineTimeTableRX[lineCounter+1]);
+				return MBRXWAIT;		
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeRobot1::showLine()
+{
+	yuvConversion(yArrayPtr);
+	yuvConversion(greenArrayPtr);
+}
+
+
+
+
+void modeRobot1::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+  double start;
+  if(tx) start=lineTimeTableTX[lineCounter];
+  else start=lineTimeTableRX[lineCounter];
+	debugState=colorLine;
+	switch (colorLine)
+		{
+			case YLINEODD:
+			case YLINEEVEN:
+				start+=bp;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength*2)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case REDLINE:
+				start+=bp+blank+visibleLineLength*2;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+						pixelPositionTable[i]=
+            (unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case BLUELINE:
+				start+=bp+blank+visibleLineLength*2;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+						pixelPositionTable[i]=
+            (unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+		}
+}
+
+/**
+	\todo resync odd/even line via frequency detection
+*/
+
+modeBase::embState modeRobot1::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				calcPixelPositionTable(YLINEODD,true);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+			case 1:
+				txFreq=1500.;
+				txDur=(unsigned int)rint((2*blank)/3);
+				return MBTXGAP;
+			case 2:
+				txFreq=1900.;
+				txDur=(unsigned int)rint(blank/3);
+				return MBTXGAP;
+			case 3:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 4:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 5:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 6:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				lineCounter++;
+				return MBTXGAP;
+			case 7:
+				calcPixelPositionTable(YLINEEVEN,true);
+				pixelArrayPtr=greenArrayPtr;
+				return MBPIXELS;
+			case 8:
+				txFreq=2300.;
+				txDur=(unsigned int)rint((2*blank)/3);
+				return MBTXGAP;
+			case 9:
+				txFreq=1900.;
+				txDur=(unsigned int)rint(blank/3);
+				return MBTXGAP;
+			case 10:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 11:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 12:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+			case 13:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp);
+				return MBTXGAP;
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+void modeRobot1::getLine()
+{
+	getLineY(true);
+}
+
diff --git a/qsstv/sstv/modes/moderobot1.h b/qsstv/sstv/modes/moderobot1.h
new file mode 100644
index 0000000..2433c5d
--- /dev/null
+++ b/qsstv/sstv/modes/moderobot1.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODEROBOT1_H
+#define MODEROBOT1_H
+
+#include "modebase.h"
+
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeRobot1 : public modeBase
+{
+public:
+  modeRobot1 (esstvMode m,unsigned int len,bool tx);
+  ~modeRobot1();
+private:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+	void showLine();
+  embState txSetupLine();
+	void getLine();
+
+};
+
+#endif
diff --git a/qsstv/sstv/modes/moderobot2.cpp b/qsstv/sstv/modes/moderobot2.cpp
new file mode 100644
index 0000000..1bcdf85
--- /dev/null
+++ b/qsstv/sstv/modes/moderobot2.cpp
@@ -0,0 +1,201 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#include "moderobot2.h"
+
+
+modeRobot2::modeRobot2(esstvMode m,unsigned int len,bool tx): modeBase(m,len,tx)
+{
+}
+
+
+modeRobot2::~modeRobot2()
+{
+}
+
+void modeRobot2::setupParams(double clock)
+{
+  //double tmp=(activeSSTVParam->imageTime/(double)activeSSTVParam->numberOfDataLines)*clock;
+  //visibleLineLength=(tmp-fp-bp-2*blank-syncDuration)/4;
+  visibleLineLength=(lineLength(mode,clock)-fp-bp-2*blank-syncDuration)/4.;
+}
+
+
+modeBase::embState modeRobot2::rxSetupLine()
+{
+  start=lineTimeTableRX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				debugState=stBP;
+//				marker=(unsigned int)round(lineTimeTable[lineCounter])-1;
+				marker=(unsigned int)rint(start+bp);
+				return MBRXWAIT;
+			case 1:
+				calcPixelPositionTable(YLINEODD,false);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+			case 2:
+				debugState=stG1;
+				marker=(unsigned int)rint(start+bp+blank+2*visibleLineLength);
+				return MBRXWAIT;
+			case 3:
+				calcPixelPositionTable(REDLINE,false);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 4:
+				debugState=stG1;
+				marker=(unsigned int)rint(start+bp+2*blank+3*visibleLineLength);
+				return MBRXWAIT;
+			case 5:
+				calcPixelPositionTable(BLUELINE,false);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 6:
+				debugState=stFP;
+				marker=(unsigned int)rint(start+bp+2*blank+4*visibleLineLength+fp);
+				return MBRXWAIT;
+			case 7:
+				debugState=stSync;
+        syncPosition=(unsigned int)rint(lineTimeTableRX[lineCounter+1]);
+				return MBSYNC;
+
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+
+
+void modeRobot2::showLine()
+{
+	yuvConversion(yArrayPtr);
+}
+
+void modeRobot2::calcPixelPositionTable(unsigned int colorLine,bool tx)
+{
+	unsigned int i;
+	int ofx=0;
+	if(tx) ofx=1;
+  double start;
+  if (tx) start=lineTimeTableTX[lineCounter];
+  else start=lineTimeTableRX[lineCounter];
+	debugState=colorLine;
+	if (tx) start+=9;
+	switch (colorLine)
+		{
+			case YLINEODD:
+				start+=bp;
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*2*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case REDLINE:
+				start+=(bp+blank+2*visibleLineLength);
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)round(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+			case BLUELINE:
+				start+=(bp+2*blank+3*visibleLineLength);
+        for(i=0;i<activeSSTVParam->numberOfPixels;i++)
+					{
+            pixelPositionTable[i]=(unsigned int)(round)(start+(((float)(i+ofx)*visibleLineLength)/activeSSTVParam->numberOfPixels));
+					}
+			break;
+		}
+}
+
+
+
+modeBase::embState modeRobot2::txSetupLine()
+{
+  start=lineTimeTableTX[lineCounter];
+  switch(subLine)
+		{
+			case 0:
+				txFreq=1500;
+				txDur=(unsigned int)rint(bp+6);
+				return MBTXGAP;
+			case 1:
+				calcPixelPositionTable(YLINEODD,true);
+				pixelArrayPtr=yArrayPtr;
+				return MBPIXELS;
+			case 2:
+				txFreq=1500.;
+				txDur=(unsigned int)rint((2*blank)/3);
+				return MBTXGAP;
+			case 3:
+				txFreq=1900.;
+				txDur=(unsigned int)rint(blank/3);
+				return MBTXGAP;
+			case 4:
+				calcPixelPositionTable(REDLINE,true);
+				pixelArrayPtr=redArrayPtr;
+				return MBPIXELS;
+			case 5:
+				txFreq=2300.;
+				txDur=(unsigned int)rint((2*blank)/3);
+				return MBTXGAP;
+			case 6:
+				txFreq=1900.;
+				txDur=(unsigned int)rint(blank/3);
+				return MBTXGAP;
+			case 7:
+				calcPixelPositionTable(BLUELINE,true);
+				pixelArrayPtr=blueArrayPtr;
+				return MBPIXELS;
+			case 8:
+				txFreq=1500;
+				txDur=(unsigned int)rint(fp);
+				return MBTXGAP;
+			case 9:
+				txFreq=1200;
+				txDur=(unsigned int)rint(syncDuration);
+				return MBTXGAP;
+
+			default:
+				return MBENDOFLINE;
+		}
+}
+
+
+
+
+void modeRobot2::getLine()
+{
+	getLineY(false);
+}
+
+
+
+
diff --git a/qsstv/sstv/modes/moderobot2.h b/qsstv/sstv/modes/moderobot2.h
new file mode 100644
index 0000000..cb39e3f
--- /dev/null
+++ b/qsstv/sstv/modes/moderobot2.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Johan Maes   *
+ *   on4qz at telenet.be   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *   In addition, as a special exception, the copyright holders give       *
+ *   permission to link the code of this program with any edition of       *
+ *   the Qt library by Trolltech AS, Norway (or with modified versions     *
+ *   of Qt that use the same license as Qt), and distribute linked         *
+ *   combinations including the two.  You must obey the GNU General        *
+ *   Public License in all respects for all of the code used other than    *
+ *   Qt.  If you modify this file, you may extend this exception to        *
+ *   your version of the file, but you are not obligated to do so.  If     *
+ *   you do not wish to do so, delete this exception statement from        *
+ *   your version.                                                         *
+ ***************************************************************************/
+#ifndef MODEROBOT2_H
+#define MODEROBOT2_H
+
+#include "modebase.h"
+
+/**
+	@author Johan Maes <on4qz at telenet.be>
+*/
+class modeRobot2 : public modeBase
+{
+public:
+  modeRobot2(esstvMode m,unsigned int len,bool tx);
+	~modeRobot2();
+private:
+  embState rxSetupLine();
+	void calcPixelPositionTable(unsigned int colorLine,bool tx);
+	void setupParams(double clock);
+	void showLine();
+  embState txSetupLine();
+  void getLine();
+};
+
+#endif
diff --git a/qsstv/sstv/modes/modes.h b/qsstv/sstv/modes/modes.h
new file mode 100644
index 0000000..426bf1e
--- /dev/null
+++ b/qsstv/sstv/modes/modes.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef MODES_H
+#define MODES_H
+#include "modegbr.h"
+#include "modegbr2.h"
+#include "modergb.h"
+#include "moderobot1.h"
+#include "moderobot2.h"
+#include "modepd.h"
+#include "modebw.h"
+#include "modeavt.h"
+#endif
diff --git a/qsstv/sstv/sstvparam.cpp b/qsstv/sstv/sstvparam.cpp
new file mode 100644
index 0000000..b89dd97
--- /dev/null
+++ b/qsstv/sstv/sstvparam.cpp
@@ -0,0 +1,423 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "sstvparam.h"
+#include "qsstvglobal.h"
+#include "utils/supportfunctions.h"
+#include "configparams.h"
+#include <math.h>
+
+bool useVIS;
+bool autoSlantAdjust;
+bool autoSave;
+int squelch;
+int filterIndex;
+
+DSPFLOAT *lineTimeTableRX=NULL;
+DSPFLOAT *lineTimeTableTX=NULL;
+sSSTVParam rxSSTVParam;
+//sFAXParam  rxFAXParam;
+sSSTVParam txSSTVParam;
+//sFAXParam txFAXParam;
+esstvMode sstvModeIndex;
+
+/**
+ 		\brief setup the lineTable 
+
+	Setup a table containing the relative positions expressed in samples	
+	\param modeIndex index of the different modes: Martin1=0 , ....
+	\param clock the adjusted samplingrate
+*/
+void setupSSTVLineTimeTable(esstvMode modeIndex,DSPFLOAT clock, bool transmit)
+{
+  unsigned int i;
+  if(transmit)
+    {
+      if (lineTimeTableTX!=NULL) delete [] lineTimeTableTX;
+      lineTimeTableTX=new DSPFLOAT [SSTVTable[modeIndex].numberOfDataLines+1];
+      for (i=0;i<SSTVTable[modeIndex].numberOfDataLines+1;i++)
+        {
+          lineTimeTableTX[i]=((SSTVTable[modeIndex].imageTime*((DSPFLOAT)i))/(DSPFLOAT)SSTVTable[modeIndex].numberOfDataLines)*clock;
+        }
+    }
+  else
+  {
+    if (lineTimeTableRX!=NULL) delete [] lineTimeTableRX;
+    lineTimeTableRX=new DSPFLOAT [SSTVTable[modeIndex].numberOfDataLines+1];
+    for (i=0;i<SSTVTable[modeIndex].numberOfDataLines+1;i++)
+      {
+        lineTimeTableRX[i]=((SSTVTable[modeIndex].imageTime*((DSPFLOAT)i))/(DSPFLOAT)SSTVTable[modeIndex].numberOfDataLines)*clock;
+      }
+  }
+
+
+
+}
+
+/*!
+	return the linelength expressed in number of samples
+*/
+
+DSPFLOAT lineLength(esstvMode modeIndex,DSPFLOAT clock)
+{
+	return (SSTVTable[modeIndex].imageTime/(DSPFLOAT)SSTVTable[modeIndex].numberOfDataLines)*clock;
+}
+
+/*!
+	return the sync width expressed in number of samples
+*/
+
+DSPFLOAT syncWidth(esstvMode modeIndex,DSPFLOAT clock)
+{
+	return (SSTVTable[modeIndex].sync*clock);
+}
+
+
+/**
+	\brief setup active parameters
+  
+	Setup active parameters given the VIS code 
+  \param[in] viscode VIS code of the mode to select
+  \return Returns the modeIndex if successful else NOTVALID
+*/
+esstvMode initializeParametersVIS(unsigned int viscode,bool tx)
+{
+  esstvMode t;
+  t=lookupVIS(viscode);
+  initializeSSTVParametersIndex(t,tx);
+	return t;
+}
+
+
+
+/**
+ * \brief setup active parameters
+
+		Setup active parameters given the modeIndex
+ * \param[in] modeIndex selected mode
+ * \return true if successful 
+*/
+bool  initializeSSTVParametersIndex(esstvMode modeIndex,bool tx)
+{
+  if (modeIndex < NUMSSTVMODES)
+    {
+      if(tx) txSSTVParam=SSTVTable[modeIndex];
+      else rxSSTVParam=SSTVTable[modeIndex];
+      printActiveSSTVParam(tx);
+      return true;
+    } 
+	return false;
+}
+
+/**
+ * \brief print active parameters
+
+	Print the active parameters for debugging purposes (to logfile and/or console) \sa logFile
+*/
+
+
+#ifndef QT_NO_DEBUG
+void printActiveSSTVParam(bool tx)
+{
+
+  double clock;
+  sSSTVParam * SSTVParam;
+  if(tx)
+  {
+    SSTVParam=&txSSTVParam;
+    clock=txClock;
+  }
+  else
+  {
+    SSTVParam=&rxSSTVParam;
+    clock=rxClock;
+  }
+  addToLog(QString("name=%1, shortname=%2").arg(SSTVParam->name).arg(SSTVParam->shortName),LOGPARAM);
+  addToLog(QString("imageTime=%1,numpix=%2,numDisplayLines=%3, numDataLines=%4,viscode=%5").arg(SSTVParam->imageTime)
+              .arg(SSTVParam->numberOfPixels).arg(SSTVParam->numberOfDisplayLines).arg(SSTVParam->numberOfDataLines).arg(QString::number(SSTVParam->VISCode,16)),LOGPARAM);
+  addToLog(QString("Samplecounters imageTime=%1 , lineTime=%2").arg(rint(SSTVParam->imageTime*clock)).arg(rint(SSTVParam->imageTime*11025./SSTVParam->numberOfDataLines)),LOGPARAM);
+}
+#else
+void printActiveSSTVParam(bool) {}
+#endif
+
+/**
+ * setup parameters given the VIS code 
+ * @param vc VIS code of the mode to select
+ * @return Returns the modeIndex if successful else NOTVALID
+*/
+esstvMode lookupVIS(unsigned int vc)
+{
+	if(vc==0) return NOTVALID;
+  esstvMode t=M1;
+  do
+    {
+      if (SSTVTable[(int)t].VISCode==vc)
+    {
+        break;
+      }
+	    t=(esstvMode)(t+1);
+    }
+  while (t<NUMSSTVMODES);
+  if(t>=NUMSSTVMODES) return NOTVALID;
+	return t;
+}
+
+/**
+	\brief longname lookup
+	
+	Returns the long name of the mode
+	\param[in] modeIndex selected mode 
+	\return  short name of the mode or empty string if not a valid mode
+*/
+
+QString getSSTVModeNameLong(esstvMode modeIndex)
+{
+	if(modeIndex==NOTVALID) return QString("");
+	return(SSTVTable[(int)modeIndex].name);
+}
+
+/**
+	\brief shortname lookup
+	
+	Returns the short name of the mode
+	\param[in] modeIndex selected mode 
+	\return short name of the mode or empty string  if not a valid mode
+*/
+
+QString getSSTVModeNameShort(esstvMode modeIndex)
+{
+  if(modeIndex==NOTVALID) return QString("");
+  return(SSTVTable[(int)modeIndex].shortName);
+}
+
+
+//QString getFAXModeNameLong(efaxMode modeIndex)
+//{
+//	if(modeIndex>=FAXNONE) return QString("");
+//	return(FAXTable[(int)modeIndex].name);
+//}
+
+//QString getFAXModeShort(efaxMode modeIndex)
+//{
+//  if(modeIndex>=FAXNONE) return QString("");
+//  return(FAXTable[(int)modeIndex].shortName);
+//}
+
+/**
+	\brief lookup mode by line length
+	
+	Returns the closest match for a given line length
+	\param[in] lineLength line length in samples
+	\param clock	the rx or tx clock to be used
+	\return returns the modeIndex or NOTVALID if none found
+*/
+
+esstvMode modeLookup(unsigned int lineLength,DSPFLOAT clock)
+{
+  int i;
+  DSPFLOAT errLine, totalError;
+  totalError=9999999;
+  esstvMode lmode=NOTVALID;
+  for (i=M1;i<NOTVALID;i++)
+    {
+      errLine=1.-(DSPFLOAT)lineLength/((SSTVTable[i].imageTime/((DSPFLOAT)SSTVTable[i].numberOfDataLines))*clock);
+      errLine*=errLine;
+      if(errLine<totalError)
+				{
+	  			lmode=(esstvMode)i;
+	  			totalError=errLine;
+				}
+    }
+	 if (totalError<0.001) return (esstvMode)lmode;
+	 return NOTVALID;
+}
+
+bool lineIsValid(esstvMode mode,unsigned int lineLength,DSPFLOAT clock)
+{
+	DSPFLOAT errLine;
+	errLine=1.-(DSPFLOAT)lineLength/((SSTVTable[mode].imageTime/((DSPFLOAT)SSTVTable[mode].numberOfDataLines))*clock);
+  errLine*=errLine;
+	return (errLine<0.001);
+}
+
+/**
+	returns the value (in samples) of the longest line in the table
+	*/
+DSPFLOAT longestLine(DSPFLOAT clock)
+{
+	int i;
+	DSPFLOAT highest=0;
+	DSPFLOAT length;
+	for(i=0;i<(int)AVT24;i++)
+		{
+			length=(SSTVTable[i].imageTime/(DSPFLOAT)SSTVTable[i].numberOfDataLines)*clock;
+			if (length>highest) highest=length;
+		}
+	return highest;
+}
+
+
+
+
+
+// if slant \ raise image time
+
+/**
+	\brief parameter table for all SSTV, FAX and CALIBRATION modes
+*/
+sSSTVParam SSTVTable[NUMSSTVMODES+1]=
+{
+//  name      shortName  mode      imagetime Pix Dis Txl  VIS     sync    fp      bp       blank  synctx  fptx   bptx    blanktx
+  {"Martin 1"  ,"M1",     M1,      114.29500,320,256,256,0x00AC,0.00500,0.00080,0.00050,0.00050,0.00500,0.00080,0.00000,0.00050,0.,1900,400 },
+  {"Martin 2"  ,"M2",     M2,       58.06300,320,256,256,0x0028,0.00500,0.00080,0.00050,0.00050,0.00500,0.00080,0.00000,0.00050,0.,1900,400 },
+  {"Scottie 1" ,"S1",     S1,      109.62950,320,256,256,0x003c,0.00900,0.00080,0.00125,0.00125,0.00900,0.00080,0.00080,0.00125,0.,1900,400 },
+  {"Scottie 2" ,"S2",     S2,       71.09250,320,256,256,0x00b8,0.00900,0.00080,0.00125,0.00125,0.00900,0.00000,0.00110,0.00125,0.,1900,400 },
+  {"Scottie DX","SDX",    SDX,     268.89080,320,256,256,0x00cc,0.00900,0.00000,0.00000,0.00100,0.00900,0.00000,0.00000,0.00100,0.,1900,400 },
+  {"SC2 60",    "SC2-60", SC2_60,   61.54150,320,256,256,0x00BB,0.00500,0.00100,0.00100,0.00100,0.00500,0.00000,0.00000,0.00000,0.,1900,400 },
+  {"SC2 120",   "SC2-120",SC2_120, 121.74050,320,256,256,0x003F,0.00500,0.00100,0.00100,0.00100,0.00500,0.00000,0.00000,0.00000,0.,1900,400 },
+  {"SC2 180",   "SC2-180",SC2_180, 182.03650,320,256,256,0x00B7,0.00500,0.00100,0.00100,0.00100,0.00500,0.00000,0.00000,0.00000,0.,1900,400 },
+  {"Robot 24",  "R24",    R24,      24.00150,160,120,120,0x0084,0.00600,0.00180,0.00125,0.00450,0.00600,0.00000,0.00120,0.00380,0.,1900,400 },
+  {"Robot 36"  ,"R36",    R36,      36.00200,320,240,240,0x0088,0.00900,0.00100,0.00340,0.00820,0.00900,0.00000,0.00300,0.00540,0.,1900,400 },
+  {"Robot 72"  ,"R72",    R72,      72.00375,320,240,240,0x000C,0.00900,0.00040,0.00250,0.00600,0.00900,0.00040,0.00250,0.00600,0.,1900,400 },
+  {"P3"        ,"P3" ,    P3,      203.05960,640,496,496,0x0071,0.00520,0.00210,0.00080,0.00250,0.00520,0.00104,0.00104,0.00104,0.,1900,400 },
+  {"P5"        ,"P5" ,    P5,      304.59050,640,496,496,0x0072,0.00780,0.00160,0.00160,0.00160,0.00780,0.00160,0.00160,0.00160,0.,1900,400 },
+  {"P7"        ,"P7" ,    P7,      406.12000,640,496,496,0x00F3,0.01040,0.00210,0.00210,0.00210,0.01040,0.00210,0.00210,0.00210,0.,1900,400 },
+  {"B/W 8"     ,"BW8" ,   BW8,       8.02850,160,120,120,0x0082,0.00600,0.00100,0.00100,0.00000,0.00600,0.00100,0.00100,0.00000,0.,1900,400 },
+  {"B/W 12"    ,"BW12" ,  BW12,     12.00100,160,120,120,0x0086,0.00600,0.00100,0.00100,0.00000,0.00600,0.00100,0.00100,0.00000,0.,1900,400 },
+  {"PD50"      ,"PD50",   PD50,     49.68690,320,256,128,0x00DD,0.02000,0.00000,0.00208,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD90"      ,"PD90",   PD90,     89.99300,320,256,128,0x0063,0.02000,0.00000,0.00208,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD120"     ,"PD120",  PD120,   126.10930,640,496,248,0x005F,0.02000,0.00000,0.00208,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD160"     ,"PD160",  PD160,   160.89120,512,400,200,0x00E2,0.02000,0.00000,0.00200,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD180"     ,"PD180",  PD180,   187.06100,640,496,248,0x0060,0.02000,0.00000,0.00200,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD240"     ,"PD240",  PD240,   248.01300,640,496,248,0x00E1,0.02000,0.00000,0.00200,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"PD290"     ,"PD290",  PD290,   288.69524,800,616,308,0x00DE,0.02000,0.00000,0.00200,0.00000,0.02000,0.00000,0.00230,0.00000,0.,1900,400 },
+  {"MP73"      ,"MP73",   MP73,     72.96350,320,256,128,0x2523,0.00900,0.00000,0.00100,0.00000,0.00900,0.00000,0.00100,0.00000,0.,1900,400 },
+	{"MP115"     ,"MP115",  MP115,   115.46000,320,256,128,0x2923,0.00900,0.00000,0.00100,0.00000,0.00900,0.00000,0.00100,0.00000,0.,1900,400 },
+	{"MP140"     ,"MP140",  MP140,   139.54000,320,256,128,0x2A23,0.00900,0.00000,0.00100,0.00000,0.00900,0.00000,0.00100,0.00000,0.,1900,400 },
+	{"MP175"     ,"MP175",  MP175,   175.38011,320,256,128,0x2C23,0.00900,0.00000,0.00100,0.00000,0.00900,0.00000,0.00100,0.00000,0.,1900,400 },
+	{"FAX480"    ,"FAX480", FAX480,  133.63300,512,500,500,0x0000,0.00512,0.00000,0.00000,0.00000,0.00512,0.00000,0.00000,0.00000,0.,1900,400 },
+	{"AVT24"     ,"AVT24",  AVT24,    22.50160,128,120,120,0x00c0,0.00500,0.00080,0.00050,0.00050,0.00500,0.00080,0.00000,0.00050,0.,1900,400 },
+	{"AVT90"     ,"AVT90",  AVT90,    90.00450,320,240,240,0x0044,0.00500,0.00080,0.00050,0.00050,0.00500,0.00080,0.00000,0.00050,0.,1900,400 },
+	{"AVT94"     ,"AVT94",  AVT94,    93.75000,320,200,200,0x0048,0.00500,0.00080,0.00050,0.00050,0.00500,0.00080,0.00000,0.00050,0.,1900,400 },
+	{"No Mode"   ,"NOTVALID",NOTVALID ,0.00000,0,0,0,0x0000,0.00000,0.00000,0.000000,0.0000,0.00000,0.00000,0.000000,0.0000,0.,0,0}
+};
+
+
+//sFAXParam FAXTable[NUMFAXMODES+1]=
+//{
+////  name      shortName     mode      modulation     lpm  ioc  lines  pixels carr dev  start sFreq stop sFreq  phL invert  colorM
+	
+//	{"NOAA"    	 ,"NOAA",		 NOAA       ,DEMODAM,      120.,  576,  800, 1810, 2400, 400,  0   , 300,  0,  450 ,  0,  false,  1},
+//	{"HFFAX"   	 ,"HFFXAX",	 HFFAX      ,DEMODFM,      120.,  288,  800,  905, 1900, 400,  5   , 300,  5,  450 , 20,  false,  1},
+//	{"Custom"    ,"Custom",	 FAXCUSTOM  ,DEMODAM,      121.,  288,  180,  905, 1900, 400,  5   , 300,  5,  450 , 20,  false,  1},
+//	{"No Mode"   ,"NOTVALID",FAXNONE    ,0,         0,    0,    0,     0,  0,   0,  0   ,   0,  0,    0 ,  0,  false,  0},
+//};
+
+//#ifndef QT_NO_DEBUG
+
+//void printActiveFAXParam(bool tx)
+//{
+//  sFAXParam * FAXParam;
+//  if(tx)
+//  {
+//    FAXParam=&txFAXParam;
+//  }
+//  else
+//  {
+//    FAXParam=&rxFAXParam;
+//  }
+
+
+//  addToLog(QString("name=%1, shortname=%2\n").arg(FAXParam->name).arg(FAXParam->shortName),LOGPARAM);
+//  addToLog(QString("modulation=%1,lpm=%2,ioc=%3,numDisplayLines=%4, subcarrier=%5,deviation=%6\n").arg(FAXParam->modulation)
+//              .arg(FAXParam->lpm).arg(FAXParam->ioc).arg(FAXParam->numberOfDisplayLines).arg(FAXParam->subcarrier)
+//              .arg(FAXParam->deviation),LOGPARAM);
+//}
+//#else
+//void printActiveFAXParam(bool ) {}
+//#endif
+
+
+
+///**
+// * \brief setup active parameters
+
+//		Setup active parameters given the modeIndex
+// * \param[in] modeIndex selected mode
+// * \return true if successful
+//*/
+//bool  initializeFAXParametersIndex(efaxMode modeIndex,bool tx)
+//{
+
+//  if (modeIndex < NUMFAXMODES)
+//    {
+//      if(tx)
+//      {
+//        txFAXParam=FAXTable[modeIndex];
+//        txFAXParam.numberOfPixels=(txFAXParam.ioc*31419+5000)/10000;
+//      }
+//      else
+//      {
+//        rxFAXParam=FAXTable[modeIndex];
+//        rxFAXParam.numberOfPixels=(rxFAXParam.ioc*31419+5000)/10000;
+//      }
+
+//      printActiveFAXParam(tx);
+//      return true;
+//    }
+//	return false;
+//}
+
+//void copyCustomParam(bool tx)
+//{
+////	memmove((char *)&FAXTable[FAXCUSTOM],(char *)&activeFAXParam,sizeof(sFAXParam));
+//  if(tx) FAXTable[FAXCUSTOM]=txFAXParam;
+//  else FAXTable[FAXCUSTOM]=rxFAXParam;;
+//}
+
+/***
+ 		\brief setup the FAX lineTable 
+
+	Setup a table containing the relative positions expressed in samples	
+	\param clock the adjusted samplingrate
+*/
+//void setupFAXLineTimeTable(DSPFLOAT clock,bool tx)
+//{
+//  unsigned int i;
+//  if(tx)
+//    {
+//      for (i=1;i<=txFAXParam.numberOfDisplayLines;i++)
+//        {
+//          // one lineTime= lpm/60
+//          lineTimeTableTX[i-1]=((60.*(DSPFLOAT)i)/txFAXParam.lpm)*clock;
+//        }
+//    }
+//  else
+//  {
+//    for (i=1;i<=rxFAXParam.numberOfDisplayLines;i++)
+//      {
+//        // one lineTime= lpm/60
+//        lineTimeTableRX[i-1]=((60.*(DSPFLOAT)i)/rxFAXParam.lpm)*clock;
+//      }
+//  }
+
+//}
+
diff --git a/qsstv/sstv/sstvparam.h b/qsstv/sstv/sstvparam.h
new file mode 100644
index 0000000..4dd4e0b
--- /dev/null
+++ b/qsstv/sstv/sstvparam.h
@@ -0,0 +1,191 @@
+#ifndef SSTVPARAM_H
+#define SSTVPARAM_H
+#include <QString>
+#include "qsstvglobal.h"
+#include "qsstvdefs.h"
+
+/** \file */
+
+/**
+SSTV Parameter functions
+ at author Johan Maes - ON4QZ
+*/
+
+#define NUMSSTVMODES 31 //!< Number of SSTV Modes for RX and TX
+//#define NUMFAXMODES 3 //!< Number of FAX Modes for RX and TX
+
+
+#define GREENLINE 0 //!< index for green line buffer
+#define BLUELINE 1  //!< index for blue line buffer
+#define REDLINE 2		//!< index for red line buffer
+#define YLINEODD 3 //!< index for intensity line buffer 0 
+#define YLINEEVEN 4 //!< index for intensity line buffer 1
+#define VIDEOBW 800 
+#define MAXLINES 800
+
+#define DEMODAM 0
+#define DEMODFM 1
+#define MINSYNCWIDTH 0.004
+#define MAXSYNCWIDTH 0.020
+#define RETRACEWIDTH 0.29
+
+
+
+/**
+	\brief SSTV Modes
+	
+  M1 to FAX480 are using sync pulses, AVT modes do not use syncs.
+*/
+enum esstvMode
+{
+M1,
+M2,
+S1,
+S2,
+SDX,
+SC2_60,
+SC2_120,
+SC2_180,
+R24,
+R36,
+R72,
+P3,
+P5,
+P7,
+BW8,
+BW12,
+PD50,
+PD90,
+PD120,
+PD160,
+PD180,
+PD240,
+PD290,
+MP73,
+MP115,
+MP140,
+MP175,
+FAX480,
+AVT24,
+AVT90,
+AVT94,
+NOTVALID
+};
+
+//enum efaxMode
+//{
+//	NOAA,
+//	HFFAX,
+//	FAXCUSTOM,
+//	FAXNONE
+//};
+
+/*
+
+// struc used in frequency detection 
+struct sTimeFreq
+{
+  DSPFLOAT t;            // time in sec
+  DSPFLOAT dt;           // minimum duration in sec
+  unsigned int f;     // frequency to detect (freq=0 if any freq)
+};
+*/
+
+/** \brief sstv parameter structure
+
+	Structure containing all the mode depended parameters  Some of the parameters are updated at run time
+	
+*/
+
+struct sSSTVParam
+{
+  QString name;
+  QString shortName;
+	enum esstvMode mode;
+  DSPFLOAT imageTime;
+  unsigned int numberOfPixels; // NumberOfPixels per Line
+  unsigned int numberOfDisplayLines;
+	unsigned int numberOfDataLines;  // data lines
+  unsigned short int VISCode;
+	float sync;			//used for rx
+	float fp;
+	float bp;
+	float blank;
+	float synct;		// used for tx
+	float fpt;
+	float bpt;
+	float blankt;
+	DSPFLOAT pixelDuration;
+	int subcarrier;
+	int deviation;
+};
+
+//struct sFAXParam
+//{
+//	~sFAXParam(){}
+//  QString name;
+//  QString shortName;
+////	enum efaxMode mode;
+//	uint modulation;
+//	double lpm;
+//	unsigned int ioc;
+//	unsigned int numberOfDisplayLines;
+//	unsigned int numberOfPixels;
+//	int subcarrier;
+//	int deviation;
+//	int aptStartDuration;
+//	int aptStartFreq;
+//	int aptStopDuration;
+//	int aptStopFreq;
+//	unsigned int numberOfPhasingLines;
+//	bool inverted;
+//	unsigned int colorMode;
+//};
+
+
+extern sSSTVParam SSTVTable[NUMSSTVMODES+1];
+//extern sFAXParam FAXTable[NUMFAXMODES+1];
+
+extern sSSTVParam rxSSTVParam;
+//extern sFAXParam  rxFAXParam;
+extern sSSTVParam txSSTVParam;
+//extern sFAXParam txFAXParam;
+extern DSPFLOAT *lineTimeTableRX;
+extern DSPFLOAT *lineTimeTableTX;
+
+
+void setupSSTVLineTimeTable(esstvMode modeIndex,DSPFLOAT clock,bool transmit);
+DSPFLOAT lineLength(esstvMode modeIndex,DSPFLOAT clock);
+DSPFLOAT syncWidth(esstvMode modeIndex,DSPFLOAT clock);
+//void setupFAXLineTimeTable(DSPFLOAT clock);
+esstvMode initializeParametersVIS(unsigned int viscode,bool tx);
+bool  initializeSSTVParametersIndex(esstvMode modeIndex,bool tx);
+//bool  initializeFAXParametersIndex(efaxMode modeIndex,bool tx);
+esstvMode lookupVIS(unsigned int vc);
+QString getSSTVModeNameLong(esstvMode m);
+QString getSSTVModeNameShort(esstvMode m);
+//QString getFAXModeNameLong(efaxMode m);
+//QString getFAXModeShort(efaxMode m);
+
+esstvMode modeLookup(unsigned int lineLength,DSPFLOAT clock);
+DSPFLOAT longestLine(DSPFLOAT clock);
+bool lineIsValid(esstvMode mode,unsigned int lineLength,DSPFLOAT clock);
+void printActiveSSTVParam(bool tx);
+void copyCustomParam(bool tx);
+
+
+extern bool useVIS;
+extern bool autoSlantAdjust;
+extern bool autoSave;
+extern int squelch;
+extern int filterIndex;
+extern esstvMode sstvModeIndex;
+
+
+
+#endif
+
+
+
+
+
diff --git a/qsstv/sstv/syncprocessor.cpp b/qsstv/sstv/syncprocessor.cpp
new file mode 100644
index 0000000..b04d813
--- /dev/null
+++ b/qsstv/sstv/syncprocessor.cpp
@@ -0,0 +1,725 @@
+#include "syncprocessor.h"
+#include "qsstvglobal.h"
+#ifndef QT_NO_DEBUG
+#include "scope/scopeview.h"
+#endif
+#include "dsp/filterparam.h"
+#include "configparams.h"
+#include "dispatcher.h"
+#include <QApplication>
+#include <QDebug>
+
+/*!
+  \class syncProcessor Filters the incoming signal using a narrow bandpassfilter centered around 1200 Hz
+
+  The basspand filter is wide enough to detect the 1100Hz-1300Hz used during retarce, carrying the VisCode.
+  Event generated: verticalRetraceEvent
+  <br> isInSync: test for sync status \sa getSyncPosition()
+
+
+*/
+
+const QString squelchStr[NUMSENSITIVITIES]=
+{
+  "Strong",
+  "Medium",
+  "Weak",
+  "DX"
+};
+
+ssenitivity sensitivityArray[NUMSENSITIVITIES]= /**< TODO */
+{
+  {0.70,  0.45, 1000, 7},
+  {0.60,  0.45, 1000, 15},
+  {0.60,  0.45, 100, 20},
+  {0.60,  0.45, 100, 999}
+
+};
+
+/**
+ * @brief
+ *
+ * @param parent
+ */
+syncProcessor::syncProcessor(QObject *parent) :  QObject(parent)
+{
+  syncFilter=NULL;
+}
+
+/**
+ * @brief
+ *
+ */
+void syncProcessor::init()
+{
+  freqPtr=syncFilter->filteredDataPtr();
+  syncVolumePtr=syncFilter->volumePtr();
+  rxVolumePtr=rxFilter->volumePtr();
+  sampleCounter=0;
+  syncArrayIndex=0;
+  shadowSyncArrayIndex=0;
+  syncState=SYNCOFF;
+  syncFound=false;
+  slantAdjustLine=6;
+  signalQuality=0;
+  modifiedClock=rxClock/SUBSAMPLINGRATIO;
+  newClock=false;
+  idxStart=M1;
+  idxEnd=FAX480;
+  volumeOffCounter=0;
+  syncDeviation=SYNCDEVIATION;
+  retraceDetected=false;
+  if(sstvModeIndex!=0)
+    {
+      idxEnd=idxStart=(esstvMode)(sstvModeIndex-1);
+    }
+#ifndef QT_NO_DEBUG
+  scopeViewerSync->setCurveName("SYNC VOL",SCDATA1);
+  scopeViewerSync->setCurveName("Sync State",SCDATA2);
+  scopeViewerSync->setCurveName("RX VOL",SCDATA3);
+  scopeViewerSync->setCurveName("SYNC Freq",SCDATA4);
+  scopeViewerSync->setAxisTitles("Samples","int","State");
+#endif
+  displaySyncEvent* ce;
+  ce = new displaySyncEvent(0,0);
+  QApplication::postEvent(dispatcherPtr, ce);
+
+}
+
+#ifndef QT_NO_DEBUG
+void syncProcessor::setOffset(unsigned int dataScopeOffset)
+{
+  xOffset=dataScopeOffset;
+  scopeViewerSync->setOffset(xOffset);
+}
+#endif
+
+/**
+ * @brief
+ *
+ */
+void syncProcessor::process()
+{
+  if(!syncFound) syncQuality=0;
+
+//  addToLog(QString("sync samplecounter:%1").arg(sampleCounter),LOGSYNC1);
+
+  extractSync();
+  #ifndef QT_NO_DEBUG
+  scopeViewerSync->addData(SCDATA1,syncVolumePtr,sampleCounter,RXSTRIPE);
+  scopeViewerSync->addData(SCDATA2,syncStateBuffer,sampleCounter,RXSTRIPE);
+  scopeViewerSync->addData(SCDATA3,rxVolumePtr,sampleCounter,RXSTRIPE);
+  scopeViewerSync->addData(SCDATA4,freqPtr,sampleCounter,RXSTRIPE);
+#endif
+  sampleCounter+=RXSTRIPE;
+}
+
+/**
+ * @brief
+ *
+ */
+void syncProcessor::extractSync()
+{
+  int i;
+  int syncDurationCount=0;
+
+  DSPFLOAT syncAvgFreq=0;
+
+  for(i=0;i<RXSTRIPE;i++)
+    {
+      sampleCounterIndexed=sampleCounter+i;
+      if(rxVolumePtr[i]<sensitivityArray[squelch].minVolume)
+        {
+//          addToLog(QString("volume off %1").arg(rxVolumePtr[i]),LOGSYNC1);
+          syncState=SYNCOFF;
+          volumeOffCounter++;
+        }
+      else
+        {
+          if(volumeOffCounter>0) volumeOffCounter--;
+          if(syncState==SYNCOFF)
+            {
+              if(syncVolumePtr[i]>(sensitivityArray[squelch].switchOn*rxVolumePtr[i]))
+                {
+//                   addToLog(QString("volume switch on %1").arg(rxVolumePtr[i]),LOGSYNC1);
+                  syncState=SYNCON;
+                  visCode=0;
+                  visCounter=0;
+                  visAvg=0.;
+                  syncDurationCount=0;
+                  syncAvgFreq=1200;
+                  syncArray[syncArrayIndex].start=sampleCounter+i;
+                }
+
+            }
+          else
+            {
+              if(syncVolumePtr[i]<(sensitivityArray[squelch].switchOff*rxVolumePtr[i]))
+                {
+//                  addToLog(QString("volume switch off %1").arg(rxVolumePtr[i]),LOGSYNC1);
+                  syncState=SYNCOFF;
+                  syncArray[syncArrayIndex].end=sampleCounter+i;
+                  syncArray[syncArrayIndex].width=syncArray[syncArrayIndex].end-syncArray[syncArrayIndex].start;
+                  syncArray[syncArrayIndex].freq=syncAvgFreq;
+
+                  if(syncArray[syncArrayIndex].width>(MINSYNCWIDTH*SAMPLERATE))
+                    {
+                      if(syncArray[syncArrayIndex].width>(RETRACEWIDTH*SAMPLERATE))
+                        {
+                          syncArray[syncArrayIndex].retrace=true;
+                          modeDetect(true);
+                         }
+                      else
+                        {
+                          visCode=0;
+                          syncArray[syncArrayIndex].retrace=false;
+                          modeDetect(false);
+
+                        }
+
+                      incrementSyncArray();
+                    }
+                }
+              else // we are still detecting a sync
+                {
+                  visCounter++;
+                  syncDurationCount++;
+                  if((syncDurationCount>10) && (syncDurationCount<50))
+                    {
+                      syncAvgFreq=syncAvgFreq+0.1*(freqPtr[i]-syncAvgFreq);
+                    }
+                  if(visCounter>100)
+                    {
+                      visAvg+=freqPtr[i];
+                    }
+                  if(visCounter==200)
+                    {
+                      visAvg/=100.;
+                      visCode=visCode>>1;
+                      if(visAvg<=1200.) visCode=visCode | 0X100;
+                      addToLog(QString("visfreq %1, counter %2, code=%3").arg(visAvg).arg(visCounter).arg(visCode),LOGSYNC2);
+                     }
+                  if(visCounter > (unsigned int)(0.030*SAMPLERATE)) //reset visCounter per bit
+                    {
+                      visCounter=0;
+                      visAvg=0.;
+                    }
+                }
+            }
+        }
+      syncStateBuffer[i]=(unsigned int)syncState*STATEMULTIPLIER;
+    }
+  if(syncFound)
+    {
+      if(volumeOffCounter>(samplesPerLine*sensitivityArray[squelch].syncLost))
+        {
+          restart();
+        }
+    }
+}
+
+
+
+/**
+ * @brief
+ *
+ */
+void syncProcessor::modeDetect(bool atRetrace)
+{
+//  bool done;
+  shadowSyncArray[shadowSyncArrayIndex]=syncArray[syncArrayIndex];
+  dumpSyncArray(false,syncArrayIndex);
+  if(!atRetrace) modeDetectByLine();
+  else
+    {
+      if(syncFound==true)
+        {
+          //  we have a retrace during image capturing
+//          verticalRetraceEvent* ce;
+//          ce = new 	verticalRetraceEvent();
+//          ce->waitFor(&done);
+//          QApplication::postEvent(dispatcherPtr, ce);
+//          addToLog("Vertical retrace while imagecapturing",LOGSYNC1);
+          retraceDetected=true;
+          return;
+        }
+      moveToTop(syncArrayIndex);
+      if((mode=lookupVIS(visCode))!=NOTVALID)
+        {
+          syncFound=true;
+          syncQuality=4;
+          syncPosition=syncArray[0].end; /// this always indicates a valid retrace
+          syncArrayIndex=0;
+          addToLog(QString("modeDetect: after retrace syncpos:=%1").arg(syncPosition),LOGSYNC1);
+          samplesPerLine=lineLength(mode,modifiedClock);
+          syncWd=syncWidth(mode,modifiedClock);
+          addToLog(QString("syncProcessor:modeDetect Viscode used: %1,mode=%2").arg(QString::number(visCode,16)).arg(mode),LOGSYNC1);
+//          logfile->addToAux(QString("Index\tStart\tEnd\tClosestLine\tRetrace\t%1").arg(samplesPerLine));
+          return;
+        }
+      else if(visCode!=0)
+        {
+          syncFound=false;
+          addToLog(QString("syncProcessor:modeDetect Viscode rejected: %1").arg(QString::number(visCode,16)),LOGSYNC1);
+        }
+    }
+}
+
+
+/**
+ * @brief use sync interval to detect mode
+ *
+ * The syncArrayIndex is pointing to the last valid entry in the array (i.e. it is post incremented)
+ * We need a minimal number of syncs to determine the mode.
+ *
+ */
+
+void syncProcessor::modeDetectByLine()
+{
+  int i;
+  esstvMode selectedMode;
+  unsigned int highest=0;
+  selectedMode=NOTVALID;
+  if(syncArrayIndex<3) return; // we need enough sync entries
+  for(i=idxStart;i<idxEnd;i++)
+   {
+     modeArray[i].clear();
+     getMatchingSyncPulse((esstvMode) i);
+     if((modeArray[i].match>highest) && (modeArray[i].match>=3))
+       {
+         if(modeArray[i].consecutiveSyncs<3) continue;
+         highest=modeArray[i].match;
+         selectedMode=(esstvMode)i;
+       }
+   }
+  if(syncFound==true)
+    {
+      trackSyncs(selectedMode);
+      return;
+    }
+  if(selectedMode==NOTVALID) return;
+  if(modeArray[selectedMode].consecutiveSyncs<3) return;
+  // we have a good match
+
+  //dumpSyncArray();
+  mode=selectedMode;
+  samplesPerLine=lineLength(mode,modifiedClock);
+  syncWd=syncWidth(mode,modifiedClock);
+  //find first occurence
+  moveToTop(modeArray[(int)selectedMode].firstSyncIndex);
+  syncPosition=syncArray[0].end;
+//  logfile->addToAux(QString("Index\tStart\tEnd\tClosestLine\tRetrace\t%1").arg(samplesPerLine));
+  cleanupSyncArray(selectedMode);
+
+  syncFound=true; // must be after cleaunupSyncArray
+  syncQuality=4;;
+  addToLog(QString("syncFound at: %1 mode=%2").arg(syncPosition).arg(selectedMode),LOGSYNC1);
+}
+
+
+void syncProcessor::getMatchingSyncPulse(esstvMode idx)
+{
+  int i;
+  bool adjecent=true;
+  DSPFLOAT ratio,length,syncW;
+  unsigned int offset;
+  unsigned int pos;
+  int start,result;
+  ssyncArray *ptr;
+  int syncIndex;
+  if(syncFound)
+    {
+      ptr=shadowSyncArray;
+      syncIndex=shadowSyncArrayIndex;
+    }
+  else
+    {
+      ptr=syncArray;
+      syncIndex=syncArrayIndex;
+    }
+  length=lineLength(idx,modifiedClock);
+  syncW=syncWidth(idx,modifiedClock);
+  offset=ptr[syncIndex].end- (int)rint(syncW/2);
+  start=syncIndex-1;
+  if(offset<=length) return;
+  pos=offset-length;
+  modeArray[idx].clear();
+  ratio=1000;
+  for(i=0;i<=syncIndex;i++)
+    {
+      ptr[i].inUse=false;
+    }
+  while(1)
+    {
+      if((result=matchingSync(start,pos,syncW,syncFound))>=0)
+        {
+          pos=ptr[result].end-(int)rint(syncW/2);
+          start=result-1;
+          if(ptr[result].spurious<2)
+            {
+              modeArray[idx].match++;
+              modeArray[idx].firstSyncIndex=result;
+              ptr[result].inUse=1;
+              if(adjecent==true) modeArray[idx].consecutiveSyncs++;
+            }
+         }
+      else
+        {
+          adjecent=false;
+        }
+      modeArray[idx].lineNumber++;
+      if(pos<length) break;
+      if(pos<ptr[0].start)break;
+      pos-=length;
+    }
+
+  if( modeArray[idx].consecutiveSyncs>=3)
+    {
+      ratio=((double)(ptr[syncIndex].end-ptr[modeArray[idx].firstSyncIndex].end))/(length*(double) modeArray[idx].match);
+      ratio*=(double) modeArray[idx].match/(double) modeArray[idx].consecutiveSyncs;
+    }
+  modeArray[idx].ratio=ratio;
+}
+
+
+
+/**
+ * @brief check if we find a syncpulse at position pos
+ *
+ * @param start index of last position in syncArray
+ * @param pos  samplecounter value of the sync
+ * @param syncWidth
+ * @return int  index of syncArray where we found the sync at position pos, -1 if not found
+ */
+int syncProcessor::matchingSync(int start, unsigned int pos, DSPFLOAT syncWidth, bool shadow)
+{
+  int i;
+  int spurious=0;
+  DSPFLOAT sw;
+  ssyncArray *ptr;
+  if(shadow) ptr=shadowSyncArray;
+  else ptr=syncArray;
+  for(i=start;i>=0;i--)
+    {
+      if(i>=SYNCARRAYLENGTH)
+        {
+//          qDebug() << "index error";
+        }
+      sw=(DSPFLOAT)(ptr[i].end-ptr[i].start);
+      if(ptr[i].retrace==true)
+        {
+          if((pos>ptr[i].end-MAXSYNCWIDTH*modifiedClock) && (pos<ptr[i].end)) return i;
+        }
+      else if((syncWidth>(1.2*sw))||(syncWidth<(0.8*sw)))
+        {
+          continue;
+        }
+      else if((pos>ptr[i].start) && (pos<ptr[i].end))
+        {
+          ptr[i].spurious=spurious;
+          return i;
+        }
+      if(pos>ptr[i].end) return -1;
+      spurious++;
+    }
+  return -1;
+}
+
+/*!
+returns 0 if not in Sync
+returns 1 if in Sync without a retrace
+returns 2 if in Sync and retrace -- the syncPosition is at the end of the retrace
+*/
+int syncProcessor::isInSync()
+{
+  int result=0;
+  if(syncFound)
+    {
+      result++;
+      if(syncArray[0].retrace==true)
+        {
+          result++;
+        }
+    }
+
+  return result;
+}
+
+
+/**
+ * @brief
+ *
+ */
+void syncProcessor::incrementSyncArray()
+{
+  if (syncArrayIndex==(SYNCARRAYLENGTH-1))
+    {
+      memmove(syncArray,&syncArray[1],sizeof(ssyncArray)*(SYNCARRAYLENGTH-1));
+    }
+  else syncArrayIndex++;
+  //copy syncArray to shadowSyncArray
+  {
+    if (shadowSyncArrayIndex==(SYNCARRAYLENGTH-1))
+      {
+        memmove(shadowSyncArray,&shadowSyncArray[1],sizeof(ssyncArray)*(SYNCARRAYLENGTH-1));
+      }
+    else shadowSyncArrayIndex++;
+  }
+}
+
+/*!  shift up the syncArray so that syncArray[index] is at the top.
+*/
+void syncProcessor::moveToTop(int index)
+{
+  if(index==0) return;
+  memmove(syncArray,syncArray+index,sizeof(ssyncArray)*(syncArrayIndex+1-index));
+  syncArrayIndex-=index;
+  shadowSyncArrayIndex=0; // reset shadowIndex
+}
+
+
+void syncProcessor::cleanupSyncArray(esstvMode modeIndex)
+{
+  unsigned int i,stpos;
+  getMatchingSyncPulse(modeIndex);
+  for(i=0;i<syncArrayIndex;) // keep last one
+  {
+    if(!syncArray[i].inUse)
+    {
+      deleteSyncEntry(i);
+    }
+    else
+    {
+      syncArray[i].lineNumber=(int) round(((double)(syncArray[i].end-syncArray[0].end))/samplesPerLine);
+      i++;
+    }
+   }
+  syncArray[syncArrayIndex].lineNumber=(int) round(((double)(syncArray[syncArrayIndex].end-syncArray[0].end))/samplesPerLine);
+  for(i=1;i<=syncArrayIndex;i++)
+    {
+      if(syncArray[0].retrace) stpos=syncArray[i-1].end-syncWidth(modeIndex,modifiedClock)/2;
+      else stpos=syncArray[i-1].start +(syncArray[i-1].end-syncArray[i-1].start)/2;
+      syncArray[i].length=(syncArray[i].start +(syncArray[i].end-syncArray[i].start)/2)-stpos;
+    }
+  dumpSyncArray(false,0);
+}
+
+
+
+void syncProcessor::deleteSyncEntry(int index)
+{
+  memmove(syncArray+index,syncArray+index+1,sizeof(ssyncArray)*(syncArrayIndex-index));
+  syncArrayIndex--;
+//  if(syncQuality>0) syncQuality--;
+}
+
+/*!
+ The modeDetect tries to find the sstv mode based on the line length. If there are a number (based on the squelch) of consecutive correct lines the syncFound and the mode are set. The syncArray is the searched for the first valid line length and the pointer is set at the end of the  sync pulse.
+*/
+
+
+
+void syncProcessor::dumpSyncArray(bool withShadow, int start)
+{
+  int st=start;
+  if(start==0)
+    {
+      addToLog(QString("index=%1 start=%2 end=%3 width=%4 linelength=n/a  frequency=%5 volume=%6")
+               .arg(0).arg(syncArray[0].start).arg(syncArray[0].end)
+               .arg(syncArray[0].end-syncArray[0].start).arg(syncArray[0].freq).arg(rxVolumePtr[0]),LOGSYNC2);
+      st=1;
+    }
+
+  for(uint i=st;i<=syncArrayIndex;i++)
+    {
+      addToLog(QString("index=%1 start=%2 end=%3 width=%4 linelength=%5 frequency=%6 volume=%7")
+               .arg(i).arg(syncArray[i].start).arg(syncArray[i].end)
+               .arg(syncArray[i].end-syncArray[i].start).arg(syncArray[i].end-syncArray[i-1].end)
+               .arg(syncArray[i].freq).arg(rxVolumePtr[0]),LOGSYNC2);
+    }
+  if(withShadow)
+    {
+      if(start==0)
+        {
+          addToLog(QString("syncProcessor: shadowSyncArrayDump index=%1 start=%2 end=%3 width=%4 linelength=n/a  frequency=%5 volume=%6")
+                   .arg(0).arg(shadowSyncArray[0].start).arg(shadowSyncArray[0].end)
+                   .arg(shadowSyncArray[0].end-shadowSyncArray[0].start).arg(shadowSyncArray[0].freq).arg(rxVolumePtr[0]),LOGSYNC2);
+                st=1;
+        }
+      for(uint i=st;i<=shadowSyncArrayIndex;i++)
+        {
+          addToLog(QString("syncProcessor: shadowSyncArrayDump index=%1 start=%2 end=%3 width=%4 linelength=%5 frequency=%6 volume=%7")
+                   .arg(i).arg(shadowSyncArray[i].start).arg(shadowSyncArray[i].end)
+                   .arg(shadowSyncArray[i].end-shadowSyncArray[i].start).arg(shadowSyncArray[i].end-shadowSyncArray[i-1].end)
+                   .arg(shadowSyncArray[i].freq).arg(rxVolumePtr[0]),LOGSYNC2);
+        }
+    }
+}
+
+int syncProcessor::getSignalQuality()
+{
+  if(!syncFound)
+    {
+      syncQuality=0;
+      lostLines=0;
+      return 0;
+    }
+  if(syncArrayIndex<7)
+    {
+      syncQuality=4;
+    }
+//  qDebug() << "diff" << sampleCounterIndexed-syncArray[syncArrayIndex-1].end << "index" << syncArrayIndex-1;
+//  qDebug() << "samplesPerLine" << samplesPerLine << "lost" <<sensitivityArray[squelch].syncLost;
+  if((sampleCounterIndexed-syncArray[syncArrayIndex-1].end)>(samplesPerLine*sensitivityArray[squelch].syncLost))
+    {
+      syncQuality=0;
+    }
+
+  if(lostLines!=0)
+    {
+      if(lostLines>sensitivityArray[squelch].syncLost/3) syncQuality--;
+      if(lostLines>sensitivityArray[squelch].syncLost/2) syncQuality--;
+      if(syncQuality<0) syncQuality=0;
+      addToLog(QString("lostLines %1, sq:=%2").arg(lostLines).arg(syncQuality),LOGSYNC1);
+      lostLines=0;
+    }
+  else
+    {
+      addToLog(QString("sq:=%2").arg(syncQuality),LOGSYNC1);
+      if(syncQuality<10) syncQuality++;
+    }
+  return syncQuality;
+}
+
+
+void syncProcessor::trackSyncs(esstvMode selectedMode)
+{
+  double temp;
+  double tempr;
+
+  if((selectedMode!=mode)&&(selectedMode!=NOTVALID))
+    {
+//      qDebug() << "new mode detected";
+   //   dumpSyncArray();
+    }
+  temp=(double)(syncArray[syncArrayIndex].end-syncArray[syncArrayIndex-1].end);
+  tempr =round(temp/samplesPerLine)*samplesPerLine+0.00000001; // make it non-zero to avoid divide by zero
+  temp=temp/tempr;
+  if(fabs(1-temp)>SYNCDEVIATION)
+    {
+//      addToLog(QString("delete sync entry %1").arg(syncArrayIndex),LOGSYNC1);
+      deleteSyncEntry(syncArrayIndex);
+      return;
+    }
+  ssyncArray *ptr=&syncArray[syncArrayIndex];
+  addToLog(QString("SyncArrayIndex %1").arg(syncArrayIndex),LOGSYNC1);
+
+  if(syncArrayIndex>0)
+    {
+      ptr->length=ptr->end-syncArray[syncArrayIndex-1].end;
+    }
+  else  ptr->length=0;
+  syncArray[syncArrayIndex].lineNumber=(int) round(((double)(syncArray[syncArrayIndex].end-syncArray[0].end))/samplesPerLine);
+  lostLines=syncArray[syncArrayIndex].lineNumber-syncArray[syncArrayIndex-1].lineNumber-1;
+  if(getSignalQuality()<3)
+    {
+      restart();
+      return;
+    }
+  if(!autoSlantAdjust) return;
+  if ((mode>=AVT24) && (mode <= AVT94)) return;
+  if (syncArray[syncArrayIndex].lineNumber<slantAdjustLine) return;
+  if(lostLines<2)
+    {
+      slantAdjust();
+    }
+}
+
+
+void syncProcessor::restart()
+{
+//  bool done=false;
+
+//  //      dumpSyncArray(false,0);
+//  syncLostEvent* ce;
+//  ce = new 	syncLostEvent();
+//  ce->waitFor(&done);
+//  QApplication::postEvent(dispatcherPtr, ce);
+//  while(!done) { qApp->processEvents();}
+  addToLog("sync lost",LOGSYNC1);
+  syncFound=false;
+}
+
+bool syncProcessor::regression(DSPFLOAT &a,DSPFLOAT &b,int start, int end)
+{
+  /* calculate linear regression
+    formula x=a+by
+    b=sum((x[i]-xm)*(y[i]-ym))/sum((y[i]-ym)*(y[i]-ym))
+    a=xm-b*ym
+  */
+  int i,j;
+  unsigned int tempCount=0;
+
+  DSPFLOAT sum_x,sum_y,sum_xx,sum_xy;
+  sum_x=sum_y=sum_xx=sum_xy=a=b=0;
+  for(j=1,i=start;i<=end;i++,j++)
+    {
+      slantArray[j].y= (DSPFLOAT)(syncArray[i].end-syncArray[start-1].end);
+      slantArray[j].x= round(slantArray[j].y/samplesPerLine)*samplesPerLine;
+//      qDebug() << slantArray[j].x << slantArray[j].y << slantArray[j].x-slantArray[j].y;
+      if(slantArray[j].x-slantArray[j].y<150)
+        {
+        sum_x+=slantArray[tempCount].x;
+        sum_y+=slantArray[tempCount].y;
+        sum_xx+=slantArray[tempCount].x*slantArray[tempCount].x;
+        sum_xy+=slantArray[tempCount].x*slantArray[tempCount].y;
+        tempCount++;
+        }
+    }
+  if(tempCount < 5) return false;
+  b=((tempCount)*sum_xy-(sum_x*sum_y))/((tempCount)*sum_xx-(sum_x*sum_x));
+  a=sum_y/(tempCount)-(b*sum_x)/(tempCount);
+  return true;
+}
+
+
+
+void syncProcessor::slantAdjust()
+{
+  DSPFLOAT a,b;
+  if ((mode>=AVT24) && (mode <= AVT94)) return ;
+  if(mode==NOTVALID) return ;
+//  logfile->addToAux(QString("%1\t%2\t%3\t%4")
+//                    .arg(syncArrayIndex)
+//                    .arg(syncArray[syncArrayIndex].start)
+//                    .arg(syncArray[syncArrayIndex].end)
+//                    .arg(syncArray[syncArrayIndex].retrace)
+//                    );
+
+  if (((mode==S1)||(mode==S2)||(mode==SDX))&&(syncArray[0].retrace==true))
+    {
+      if(!regression(a,b,2,syncArrayIndex)) return;
+    }
+  else
+    {
+      if(!regression(a,b,1,syncArrayIndex)) return;
+    }
+  addToLog(QString("step:%1 a=%2 b=%3, modified rxclock=%4").arg(syncArrayIndex).arg(a).arg(b).arg(modifiedClock*b),LOGSLANT);
+  slantAdjustLine+=7;
+  if((fabs(1.-b)>0.00001)||(fabs(a)>1))
+    {
+      newClock=true;
+      modifiedClock*=b;
+      samplesPerLine=lineLength(mode,modifiedClock); //recalculate the samples per line
+      addToLog("new clock accepted",LOGSLANT);
+      syncPosition=syncArray[0].end+(long)round(a);
+      addToLog(QString("slantAdjust: modified  syncpos:=%1").arg(syncPosition),LOGSLANT);
+      syncDeviation=SYNCDEVIATION/3.;
+    }
+
+}
+
+
+
+
+
diff --git a/qsstv/sstv/syncprocessor.h b/qsstv/sstv/syncprocessor.h
new file mode 100644
index 0000000..96bb9cb
--- /dev/null
+++ b/qsstv/sstv/syncprocessor.h
@@ -0,0 +1,162 @@
+#ifndef SYNCPROCESSOR_H
+#define SYNCPROCESSOR_H
+#include "qsstvdefs.h"
+#include "dsp/filter.h"
+#include "sstvparam.h"
+
+#define SYNCARRAYLENGTH 512
+#define SYNCDEVIATION 0.008
+#define STATEMULTIPLIER 1000
+#define NUMSENSITIVITIES 4
+
+extern const QString squelchStr[NUMSENSITIVITIES];
+
+struct ssyncArray
+{
+  ssyncArray()
+  {
+    init();
+  }
+  void init()
+  {
+    start=end=length=width=0;
+    retrace=false;
+    keep=false;
+    inUse=false;
+    lineNumber=0;
+    spurious=0;
+  }
+  unsigned int start;
+  unsigned int end;
+  unsigned int width;
+  unsigned int length;
+  unsigned int lineNumber;
+  unsigned int spurious;
+  DSPFLOAT freq;
+  DSPFLOAT maxVol;
+  bool retrace;
+  bool keep;
+  bool inUse;
+};
+
+struct smodeArray
+{
+  void clear()
+  {
+    match=0;
+    lineNumber=0;
+    consecutiveSyncs=0;
+    firstSyncIndex=0;
+    ratio=0;
+  }
+  unsigned int match;
+  unsigned int lineNumber;
+  unsigned int consecutiveSyncs;
+  unsigned int firstSyncIndex;
+  DSPFLOAT ratio;
+};
+
+struct ssenitivity
+{
+  DSPFLOAT switchOn;
+  DSPFLOAT switchOff;
+  DSPFLOAT minVolume;
+  unsigned int syncLost;
+};
+
+struct sslantArray
+{
+  DSPFLOAT x;
+  DSPFLOAT y;
+};
+
+#include <QObject>
+
+class syncProcessor : public QObject
+{
+  enum esyncState {SYNCOFF,SYNCON,SYNCVALID};
+  Q_OBJECT
+public:
+  explicit syncProcessor(QObject *parent = 0);
+  void init();
+  void setFilters(filter *rx,filter *sync) {rxFilter=rx; syncFilter=sync;}
+  void process();
+  int isInSync();
+  bool hasRetrace(){return retraceDetected;}
+  bool hasNewClock()
+  {
+    bool nc=newClock;
+    newClock=false;
+    return nc;
+  }
+  unsigned long getSyncPosition() { return syncPosition;}
+
+  esstvMode getMode() {return  mode ;}
+  //	DSPFLOAT *getSyncBufferPtr() {return syncBufferPtr;}
+  DSPFLOAT getNewClock() {return modifiedClock;}
+  void setOffset(unsigned int dataScopeOffset);
+    int syncQuality;
+
+  
+signals:
+  
+public slots:
+private:
+  filter *rxFilter;
+  filter *syncFilter;
+  unsigned int sampleCounter;
+  unsigned int sampleCounterIndexed;
+  void extractSync();
+  void incrementSyncArray();
+  void modeDetect(bool atRetrace);
+  void modeDetectByLine();
+  void getMatchingSyncPulse(esstvMode idx);
+  int  matchingSync(int start,unsigned int pos,DSPFLOAT syncWidth, bool shadow);
+  void moveToTop(int index);
+  void cleanupSyncArray(esstvMode modeIndex);
+  void deleteSyncEntry(int index);
+  void dumpSyncArray(bool withShadow,int start=0);
+  int getSignalQuality();
+  void trackSyncs(esstvMode selectedMode);
+  bool regression(DSPFLOAT &a,DSPFLOAT &b,int start, int end);
+  void slantAdjust();
+  void restart();
+
+  int *syncVolumePtr;
+  int *rxVolumePtr;
+  int *freqPtr;
+  unsigned int syncStateBuffer[RXSTRIPE];
+  esyncState syncState;
+  ssyncArray syncArray[SYNCARRAYLENGTH];
+  unsigned int syncArrayIndex;
+  ssyncArray shadowSyncArray[SYNCARRAYLENGTH];
+  unsigned int shadowSyncArrayIndex;
+  smodeArray modeArray[AVT24];
+  int signalQuality;
+
+  unsigned int visCode;
+  unsigned int visCounter;
+  DSPFLOAT visAvg;
+  unsigned int volumeOffCounter;
+
+  esstvMode mode;
+
+  DSPFLOAT modifiedClock;
+  DSPFLOAT samplesPerLine;
+  DSPFLOAT syncWd;
+  unsigned int syncPosition;
+  bool syncFound;
+  unsigned int slantAdjustLine;
+  sslantArray slantArray [SYNCARRAYLENGTH];
+  bool newClock;
+  DSPFLOAT syncDeviation;
+
+  esstvMode idxStart;
+  esstvMode idxEnd;
+  unsigned int xOffset;
+
+  unsigned int lostLines;
+  bool retraceDetected;
+};
+
+#endif // SYNCPROCESSOR_H
diff --git a/qsstv/txfunctions.cpp b/qsstv/txfunctions.cpp
new file mode 100644
index 0000000..4607b48
--- /dev/null
+++ b/qsstv/txfunctions.cpp
@@ -0,0 +1,740 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "txfunctions.h"
+#include "qsstvglobal.h"
+#include "configparams.h"
+#include <qmutex.h>
+//#include "imagecoder.h"
+#include "dsp/synthes.h"
+#include "sstv/modes/modes.h"
+#include "dispatcher.h"
+#include "sstv/cw.h"
+#include <QApplication>
+#include <assert.h>
+#include "txwidget.h"
+#include "sound/soundio.h"
+#include "sound/waterfalltext.h"
+#include "utils/hybridcrypt.h"
+#include "rig/rigcontrol.h"
+
+
+
+#define SILENCEDELAY 0.600           // send silence after transmission
+
+
+QMutex txMutex;
+
+esstvMode modeIndexTx;
+int templateIndex;
+bool useTemplate;
+bool useCW;
+bool useVOX;
+
+txFunctions::txFunctions()
+{
+  synthesPtr=new synthesizer(txClock);
+  currentMode=0;
+  oldMode=NOTVALID;
+  txState=TXIDLE;
+  started=false;
+  txDRM=new drmTransmitter;
+  txList.clear();
+  useHybrid=false;
+}
+
+txFunctions::~txFunctions()
+{
+  delete synthesPtr;
+  if(currentMode) delete currentMode;
+}
+
+void txFunctions::run()
+{
+  abort=false;
+  init();
+  while(!abort)
+    {
+      progressTXEvent *ce;
+      started=true;
+      switch (txState)
+        {
+        case TXIDLE:
+          msleep(10);
+        break;
+        case TXACTIVE:
+          msleep(1);
+        break;
+        case TXSENDTONE:
+          waitTxOn();
+          addToLog("txFunc: entered TXSENDTONE",LOGTXFUNC);
+          synthesPtr->sendTone(toneDuration,toneLowerFrequency,toneUpperFrequency,false);
+          addToLog("txFunc: TXSENDTONE waiting for end",LOGTXFUNC);
+          waitEnd();
+          txState=TXIDLE;
+        break;
+        case TXSENDDRM:
+          waitTxOn();
+//          synthesPtr->sendTone(5,1900,1900,false);
+          waterfallTime=waterfallPtr->getDuration(endPicWF);
+          waterfallTime+=waterfallPtr->getDuration(startPicWF);
+          initDRMImage();
+          ce=new progressTXEvent(calcTxTime(true,waterfallTime));
+          QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+          addToLog("start of wf",LOGTXFUNC);
+          waterfallPtr->setText(startPicWF);
+          synthesPtr->sendWFText();
+          addToLog("start of txDrm",LOGTXFUNC);
+          txDRM->start(true);
+
+          addToLog("end of txDrm",LOGTXFUNC);
+          if(txState==TXSENDDRM) // abort if txState is idle
+            {
+              waterfallPtr->setText(endPicWF);
+              synthesPtr->sendWFText();
+              addToLog("end of wf",LOGTXFUNC);
+              addToLog("txFunc: TXSENDDRM waiting for end",LOGTXFUNC);
+              waitEnd();
+            }
+          txState=TXIDLE;
+        break;
+        case TXSENDDRMBSR:
+          waitTxOn();
+          waterfallTime=waterfallPtr->getDuration(bsrWF);
+          ce=new progressTXEvent(calcTxTime(true,waterfallTime));
+          QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+          synthesPtr->sendWFText();
+          txDRM->start(true);
+          addToLog("txFunc: TXSENDDRMBSR waiting for end",LOGTXFUNC);
+          waitEnd();
+          txState=TXIDLE;
+        break;
+
+        case TXSENDDRMFIX:
+          waitTxOn();
+          waterfallTime=waterfallPtr->getDuration(fixWF);
+          ce=new progressTXEvent(calcTxTime(true,waterfallTime));
+          QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+          synthesPtr->sendWFText();
+          txDRM->start(true);
+          addToLog("txFunc: TXSENDDRMFIX waiting for end",LOGTXFUNC);
+          if(txState==TXSENDDRMFIX) // abort if txState is idle
+            {
+              waterfallPtr->setText(endPicWF);
+              synthesPtr->sendWFText();
+              addToLog("end of wf",LOGTXFUNC);
+              addToLog("txFunc: TXSENDDRM waiting for end",LOGTXFUNC);
+              waitEnd();
+            }
+          txState=TXIDLE;
+        break;
+        case TXSENDDRMTXT:
+          waitTxOn();
+        break;
+        case TXSENDID:
+          waitTxOn();
+          addToLog("txFunc: entered TXSENDID",LOGTXFUNC);
+          ce=new progressTXEvent(waterfallPtr->getDuration(NULL));
+          QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+          synthesPtr->sendWFText();
+          addToLog("txFunc: TXSENDID waiting for end",LOGTXFUNC);
+          waitEnd();
+          txState=TXIDLE;
+        break;
+
+        case TXFAXSTART:
+          waitTxOn();
+          addToLog("txFunc: TXFAXSTART",LOGTXFUNC);
+          //            initSSTVImage();
+          faxStart();
+          addToLog("txFunc: TXFAXSTART waiting for end",LOGTXFUNC);
+          waitEnd();
+          txState=TXIDLE;
+        break;
+
+        case TXSSTVIMAGE:
+          waitTxOn();
+          addToLog("txFunc: entered TXIMAGE",LOGTXFUNC);
+          //            initSSTVImage();
+          ce=new progressTXEvent(calcTxTime(false,0));
+          QApplication::postEvent( dispatcherPtr, ce );  // Qt will delete it when done
+          if(txSSTVParam.mode==FAX480)
+            {
+              for (int i=0;i<1220;i++)
+                {
+                  synthesPtr->sendTone(0.00205,1500,0,true);
+                  synthesPtr->sendTone(0.00205,2300,0,true);
+                }
+            }
+          else
+            {
+              sendPreamble();
+              sendVIS();
+            }
+          addToLog("txFunc: sendImage",LOGTXFUNC);
+          sendImage();
+          addToLog("txFunc: endImage",LOGTXFUNC);
+          txState=TXSSTVPOST;
+        break;
+        case TXSSTVPOST:
+          addToLog("txFunc: TXSSTVPOST ",LOGTXFUNC);
+          if (useCW)
+            {
+              sendCW();
+            }
+          else
+            {
+              sendFSKID();
+            }
+
+          waitEnd();
+          txState=TXIDLE;
+        break;
+        case TXTEST:
+          sendTestPattern();
+        break;
+
+        }
+    }
+  started=false;
+  addToLog("txFunc stopped",LOGTXFUNC);
+  abort=false;
+}
+
+
+
+void txFunctions::init()
+{
+  txState=TXIDLE;
+  addToLog("txFunc: Init",LOGTXFUNC);
+	sampleCounter=0;
+}
+
+void txFunctions::waitTxOn()
+{
+  double txDelay;
+  txDelay=rigController->getTxDelay();
+  if(txDelay!=0)
+    {
+      synthesPtr->sendSilence(txDelay);
+    }
+}
+
+void txFunctions::stopAndWait()
+{
+
+  stopDRM=true;
+  if(!isRunning()) return;
+  if(currentMode) currentMode->abort();
+  addToLog("txFunc: stop initiated",LOGTXFUNC);
+  logStatus();
+  txState=TXIDLE;
+  while(txState!=TXIDLE)
+    {
+      if(!isRunning()) return;
+      qApp->processEvents();
+    }
+  addToLog("txFunc: stop executed",LOGTXFUNC);
+  stopTXEvent *ce = new  stopTXEvent();
+  QApplication::postEvent(dispatcherPtr, ce);
+}
+
+
+
+
+bool txFunctions::create(esstvMode m,DSPFLOAT clock)
+{
+  if(oldMode==m)
+    {
+      currentMode->init(clock);
+      return true;
+    }
+  oldMode=m;
+  if(currentMode) delete currentMode;
+  currentMode=0;
+  switch (m)
+    {
+      case M1:
+      case M2:
+        currentMode=new modeGBR(m,TXSTRIPE,true);
+      break;
+      case S1:
+      case S2:
+      case SDX:
+        currentMode=new modeGBR2(m,TXSTRIPE,true);
+      break;
+      case R36:
+        currentMode=new modeRobot1(m,TXSTRIPE,true);
+      break;
+      case R24:
+      case R72:
+          currentMode=new modeRobot2(m,TXSTRIPE,true);
+    break;
+      case SC2_60:
+      case SC2_120:
+      case SC2_180:
+      case P3:
+      case P5:
+      case P7:
+          currentMode=new modeRGB(m,TXSTRIPE,true);
+      break;
+      case FAX480:
+      case BW8:
+      case BW12:
+          currentMode=new modeBW(m,TXSTRIPE,true);
+      break;
+      case AVT24:
+      case AVT90:
+      case AVT94:
+          currentMode=new modeAVT(m,TXSTRIPE,true);
+      break;
+      case PD50:
+      case PD90:
+      case PD120:
+      case PD160:
+      case PD180:
+      case PD240:
+      case PD290:
+      case MP73:
+      case MP115:
+      case MP140:
+      case MP175:
+          currentMode=new modePD(m,TXSTRIPE,true);
+      break;
+      default:
+        m=NOTVALID;
+      break;
+    }
+  if (m!=NOTVALID)
+    {
+      initializeSSTVParametersIndex(m,true);
+      QString s=getSSTVModeNameLong(m);
+      addToLog("create: create TX mode",LOGTXFUNC);
+      assert(currentMode);
+     currentMode->init(clock);
+      return true;
+    }
+  return false;
+}
+
+
+
+
+
+void txFunctions::sendTestPattern()
+{
+  if(patternNumber==TPBURST) syncBurst();
+}
+
+
+
+/** send a burst of syncs */
+/*
+void txFunctions::syncBurst()
+{
+  // we will send 5msec 1200Hz followed by 50 msec silence
+//	synthesPtr->sendTone(0.005,1200.);
+  for (int i=300;i<3000;i++)
+    {
+      synthesPtr->sendTone(0.005,(double) i);
+    }
+  synthesPtr->sendSilence(0.100);;
+}
+*/
+
+/** send a burst of syncs */
+void txFunctions::syncBurst()
+{
+  // send 5msec 1200Hz followed by 50 msec silence
+//	synthesPtr->sendTone(0.005,1200.);
+  for (int i=0;i<50;i++)
+    {
+      synthesPtr->sendTone(0.005,1200,0,true);
+      synthesPtr->sendTone(0.020,1700,0,true);
+    }
+  synthesPtr->sendSilence(0.100);;
+}
+
+void txFunctions::faxStart()
+{
+  // send 300Hz modulated 1500Hz/2300Hz tone
+//	synthesPtr->sendTone(0.005,1200.);
+  for (int i=0;i<100;i++)
+    {
+      synthesPtr->sendTone(0.0333,1500,0,true);
+      synthesPtr->sendTone(0.0333,2300,0,true);
+    }
+  synthesPtr->sendSilence(0.100);
+}
+
+
+
+
+void txFunctions::startTestPattern(uint tpnum)
+{
+  patternNumber=tpnum;
+  txState=TXTEST;
+}
+
+
+//void txFunctions:: initSSTVImage()
+//{
+//  create(modeIndexTx,txClock);
+//}
+
+void txFunctions:: sendPreamble()
+{
+  addToLog("txFunc:sendPreamble",LOGTXFUNC);
+  if(useVOX) synthesPtr->sendTone(1.,1700.,0,false);
+  synthesPtr->sendTone(0.1,1900.,0,true);
+  synthesPtr->sendTone(0.1,1500.,0,true);
+  synthesPtr->sendTone(0.1,1900.,0,true);
+  synthesPtr->sendTone(0.1,1500.,0,true);
+  synthesPtr->sendTone(0.1,2300.,0,true);
+  synthesPtr->sendTone(0.1,1500.,0,true);
+  synthesPtr->sendTone(0.1,2300.,0,true);
+  synthesPtr->sendTone(0.1,1500.,0,true);
+  synthesPtr->sendTone(0.3,1900.,0,true);
+  synthesPtr->sendTone(0.01,1200.,0,true);
+  synthesPtr->sendTone(0.3,1900.,0,true);
+}
+
+
+
+void txFunctions:: sendVIS()
+{
+  int i,l;
+  int t=txSSTVParam.VISCode;
+  addToLog("txFunc:sendVis",LOGTXFUNC);
+  if ((t&0xFF)==0x23) l=16;
+  else l=8;
+  synthesPtr->sendTone(0.030,1200,0,false); // startbit
+  for (i=0;i<l;i++)
+    {
+      if((t&1)==1) synthesPtr->sendTone(0.030,1100,0,true);
+      else synthesPtr->sendTone(0.030,1300,0,true);
+      t>>=1;
+    }
+  synthesPtr->sendTone(0.030,1200,0,true); // stopbit
+}
+
+
+void txFunctions::sendCW()
+{
+  addToLog("txFunc:sendCW",LOGTXFUNC);
+  float tone;
+  float duration;
+  initCW(cwText);
+  synthesPtr->sendSilence(0.5);
+  while(sendTextCW(tone,duration))
+    {
+      synthesPtr->sendTone(duration,tone,0,true);
+    }
+}
+
+void txFunctions::waitEnd()
+{
+  synthesPtr->sendTone(SILENCEDELAY,00,0,false); // send silence
+  addToLog("waitEnd() posting endTXImage",LOGTXFUNC);
+  endImageTXEvent *ce=new endImageTXEvent;
+  QApplication::postEvent(dispatcherPtr, ce );  // Qt will delete it when done
+}
+
+double txFunctions::calcTxTime(bool isDRM,int overheadTime)
+{
+  double tim=0;
+  float tone;
+  float duration;
+  tim= soundIOPtr->getPlaybackStartupTime();
+  tim+=SILENCEDELAY;
+  if(!isDRM)
+    {
+      initializeSSTVParametersIndex(modeIndexTx,true);
+      int t=txSSTVParam.VISCode;
+      initCW(cwText);
+      tim+=1.41; //preamble;
+      if ((t&0xFF)==0x23) tim+=18.*0.03;
+      else tim+=10.*0.03;
+      tim+=txSSTVParam.imageTime;
+      tim+=0.5 ;//CW silence gap
+      if(useCW)
+        {
+          while(sendTextCW(tone,duration))
+            {
+              tim+=duration;
+            }
+        }
+      tim+=0.3; // trailer;
+    }
+  else
+    {
+      tim+=overheadTime;
+      tim+=txDRM->getDuration();
+    }
+  addToLog(QString("txFunc: calcTimeTx %1").arg(tim),LOGTXFUNC);
+  return tim;
+}
+
+
+
+
+void txFunctions::sendImage()
+{
+  addToLog("txFunc: sendImage",LOGTXFUNC);
+  currentMode->transmitImage(txWidgetPtr->getImageViewerPtr());
+}
+
+void txFunctions::initDRMImage()
+{
+  eRSType rsType;
+  reedSolomonCoder rsd;
+  QString fn;
+  QString ext;
+  QFileInfo finf;
+  hybridCrypt hc;
+  // we need to save it as a jpg file
+  if(!useHybrid)
+    {
+      finf.setFile(txWidgetPtr->getImageViewerPtr()->getFilename());
+      fn=QDateTime::currentDateTime().toUTC().toString("yyyyMMddHHmmss");
+      fn+="-"+finf.baseName();
+      ext=finf.suffix();
+    }
+  else
+    {
+     finf.setFile(hybridFilename);
+     fn= finf.baseName();
+     ext=finf.suffix();
+    }
+  fixBlockList.clear();
+
+  if(txList.count()>5) txList.removeFirst();
+  txList.append(txSession());
+  txList.last().filename=fn;
+  txList.last().extension=ext;
+  if(!useHybrid)
+    {
+      txList.last().drmParams=drmTxParameters;
+      txWidgetPtr->getImageViewerPtr()->copyToBuffer(&(txList.last().ba));
+      rsType=(eRSType)txList.last().drmParams.reedSolomon;
+      baDRM=txList.last().ba;
+      if(rsType!=RSTNONE)
+        {
+          rsd.encode(baDRM,txList.last().extension,rsType);
+          txDRM->init(&baDRM,txList.last().filename,rsTypeStr[rsType],txList.last().drmParams);
+        }
+      else
+        {
+          txDRM->init(&baDRM,txList.last().filename,txList.last().extension,txList.last().drmParams);
+        }
+    }
+  else
+    {
+      txList.last().drmParams.bandwith=1; // bw 2.2
+      txList.last().drmParams.robMode=2;  // mode E
+      txList.last().drmParams.interleaver=0; // long
+      txList.last().drmParams.protection=0; // high
+      txList.last().drmParams.qam=0; // 4bit QAM
+      txList.last().drmParams.callsign=myCallsign;
+
+      // we have to fill in the body
+      txList.last().ba.clear();
+      hc.enCrypt(&txList.last().ba);
+      txDRM->init(&txList.last().ba,txList.last().filename,txList.last().extension,txList.last().drmParams);
+    }
+
+
+  // transportID is set
+  txList.last().transportID=txTransportID;
+}
+
+
+void txFunctions::initDRMBSR(QByteArray *ba)
+{
+  baDRM=*ba;
+  fixBlockList.clear();
+  txDRM->init(&baDRM,"bsr","bin",drmTxParameters);
+  addToLog(QString("bsr.bin send %1").arg(baDRM.size()),LOGPERFORM);
+}
+
+bool txFunctions::initDRMFIX(QString fileName,QString extension,eRSType rsType,int mode)
+{
+  reedSolomonCoder rsd;
+  QFile fi(fileName);
+  if(fi.open(QIODevice::ReadOnly)<=0) return false;
+  baDRM=fi.readAll();
+  if(rsType!=RSTNONE)
+    {
+      rsd.encode(baDRM,extension,rsType);
+      txDRM->init(&baDRM,fileName,rsTypeStr[rsType],modeToParams(mode));
+    }
+  else
+    {
+      txDRM->init(&baDRM,fileName,extension,modeToParams(mode));
+    }
+  return true;
+}
+
+bool txFunctions::initDRMFIX(txSession *sessionPtr)
+{
+  reedSolomonCoder rsd;
+  eRSType rsType;
+  rsType=(eRSType)sessionPtr->drmParams.reedSolomon;
+  baDRM=sessionPtr->ba;
+  if(rsType!=RSTNONE)
+    {
+      rsd.encode(baDRM,sessionPtr->extension,rsType);
+      txDRM->init(&baDRM,sessionPtr->filename,rsTypeStr[rsType],sessionPtr->drmParams);
+    }
+  else
+    {
+      txDRM->init(&baDRM,sessionPtr->filename,sessionPtr->extension,sessionPtr->drmParams);
+    }
+  return true;
+}
+
+
+void txFunctions::sendDRMFIX()
+{
+//  QByteArray ba;
+//  QFile fi(rxImagesPath+"/bsr.bin");
+//  if(fi.open(QIODevice::ReadOnly)<=0) return;
+//  ba=fi.readAll();
+//  txDRM->init(&ba,"bsr","bin",drmTxParameters);
+//  addToLog(QString("bsr.bin send").arg(ba.size()),LOGPERFORM);
+//  txDRM->start();
+}
+
+
+void txFunctions::logStatus()
+{
+  QString stat,statr;
+  if(isRunning()) statr="true"; else statr="false";
+  addToLog(QString("TX Is running %1").arg(statr),LOGDISPAT);
+  stat="TX state: ";
+  switch (txState)
+    {
+    case TXIDLE: stat+="IDLE";  break;
+    case TXACTIVE: stat+="ACTIVE";  break;
+    case TXSENDTONE: stat+="SENDTONE";  break;
+    case TXSENDID: stat+="SENDID";  break;
+    case TXSENDDRM: stat+="SENDDRM";  break;
+    case TXSENDDRMBSR: stat+="SENDDRMBSR";  break;
+    case TXSENDDRMFIX: stat+="SENDDRMFIX";  break;
+    case TXSENDDRMTXT: stat+="SENDDRMTXT";  break;
+    case TXSSTVIMAGE: stat+="SSTVIMAGE";  break;
+    case TXSSTVPOST: stat+="SSTVPOST";  break;
+    case TXFAXSTART: stat+="FAXSTART";  break;
+    case TXTEST: stat+="TEST";  break;
+    }
+  addToLog(stat,LOGTXFUNC);
+}
+
+txSession *txFunctions::getSessionPtr(uint transportID)
+{
+  int i;
+  for(i=0;i<txList.count();i++)
+    {
+      if(txList.at(i).transportID==transportID)
+        {
+          return &txList[i];
+        }
+    }
+  return NULL;
+}
+
+
+void txFunctions:: sendFSKChar(int IDChar)
+{
+  int TxBit;
+
+    for (int i=0;i<6;i++)
+   {
+     TxBit = IDChar & 0x01;
+     IDChar = IDChar >> 1;
+     if (TxBit == 0x01)
+  synthesPtr->sendTone(0.022,1900.,0,true);
+     else
+  synthesPtr->sendTone(0.022,2100.,0,true);
+    }
+}
+
+// sendFSKID by DL3YAP
+
+void txFunctions:: sendFSKID()
+{
+  int idx;
+  int l;
+  int IDChar;
+  int Checksum;
+
+
+  if (myCallsign.isEmpty()) return;
+  // addToLog("txFunc:sendFSKID",LOGFSKID);
+
+   QTextStream out(stdout);
+
+  l=myCallsign.size();
+  idx=0;
+  Checksum=0;
+ // synthesPtr->sendTone(2.0,00,0,false);
+  synthesPtr->sendTone(0.3,1500.,0,false);
+  synthesPtr->sendTone(0.1,2100.,0,true);
+  synthesPtr->sendTone(0.022,1900.,0,true);
+
+  IDChar = 0x2A;
+  sendFSKChar(IDChar);
+
+  QChar IDText=	QChar(myCallsign[idx]);
+  IDChar=int(IDText.toLatin1());
+
+  IDChar = (IDChar - 0x20);
+  Checksum = IDChar;
+
+  while (idx < l)
+  {
+    sendFSKChar(IDChar);
+    idx++;
+    QChar IDText=QChar(myCallsign[idx]);
+    IDChar=int(IDText.toLatin1());
+    IDChar = (IDChar - 0x20);
+
+    if (idx < l)
+    {
+      Checksum = Checksum ^ IDChar;
+    }
+  }
+
+  IDChar = 0x01;
+
+  sendFSKChar(IDChar);
+
+  IDChar = Checksum & 0x3F ;
+
+  sendFSKChar(IDChar);
+
+  // synthesPtr->sendTone(0.022,2100.,0,true);
+  synthesPtr->sendTone(0.1,1900.,0,true);
+}
+
+
+
+
+
diff --git a/qsstv/txfunctions.h b/qsstv/txfunctions.h
new file mode 100644
index 0000000..8352d9d
--- /dev/null
+++ b/qsstv/txfunctions.h
@@ -0,0 +1,154 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef TXFUNC_H
+#define TXFUNC_H
+#include <QThread>
+#include "dsp/filterparam.h"
+#include "sstv/sstvparam.h"
+#include "sstv/modes/modebase.h"
+#include "drmtx/drmtransmitter.h"
+#include "utils/reedsolomoncoder.h"
+
+class imageCoder;
+
+#define TPBURST 0
+
+extern esstvMode modeIndexTx;
+extern int templateIndex;
+extern bool useTemplate;
+extern bool useCW;
+extern bool useVOX;
+
+class drmTransmitter;
+
+struct txSession
+{
+  drmTxParams drmParams;
+  QByteArray ba;  // contains the image data in jpg, jp2 .... format
+  uint transportID;
+  QString filename;
+  QString extension;
+};
+
+class txFunctions: public QThread
+{
+public:
+  enum etxState
+    {
+    TXIDLE,	//!< in idle loop
+    TXACTIVE,
+    TXSENDTONE,
+    TXSENDID,
+    TXSENDDRM,
+    TXSENDDRMBSR,
+    TXSENDDRMFIX,
+    TXSENDDRMTXT,
+    TXSSTVIMAGE,
+    TXSSTVPOST,
+    TXFAXSTART,
+    TXTEST
+    };
+public:
+  txFunctions();
+  ~txFunctions();
+  bool create(esstvMode m,DSPFLOAT clock);
+  modeBase *currentMode;
+  void run();
+  void stopAndWait();
+  bool hasStarted() {return started;}
+  void setTXState(etxState d) { txState=d;}
+  etxState getTXState() { return txState;}
+  void syncBurst();
+  void faxStart();
+  void setDRMTxParams(drmTxParams params)
+  {
+    drmTxParameters=params;
+  }
+  txSession *getSessionPtr(uint transportID);
+
+  void enableNoise(bool en,double level=0.)
+  {
+    if (level==0)
+      {
+        noiseEnabled=en;
+      }
+  }
+  void setFilter(efilterType filterType);
+  void startTestPattern(uint tpnum);
+  //  void setCall(const QString &str);
+  //  void setToOperator(const QString &str);
+  //  void setRSV(const QString &str);
+  void setToneParam(double duration,double lowerFreq,double upperFreq=0)
+  {
+    toneDuration=duration;
+    toneLowerFrequency=lowerFreq;
+    toneUpperFrequency=upperFreq;
+  }
+  double calcTxTime(bool isDRM, int  overheadTime);
+  void logStatus();
+  void initDRMBSR(QByteArray *ba);
+  bool initDRMFIX(QString fileName, QString extension, eRSType rsType, int mode);
+  bool initDRMFIX(txSession *sessionPtr);
+  void setHybrid(bool h,QString hbfn=QString::Null())
+  {
+    useHybrid=h;
+    hybridFilename=hbfn;
+  }
+private:
+  bool abort;
+  void init();
+  unsigned long sampleCounter;
+  bool noiseEnabled;
+
+  short int filter(double sample);
+  void write(double sample);
+
+  etxState txState;
+  uint patternNumber;
+  void sendTestPattern();
+  void sendPreamble();
+  void sendVIS();
+  void sendImage();
+  void initDRMImage();
+  void sendDRMFIX();
+  void sendCW();
+  void sendFSKChar(int IDChar);
+  void sendFSKID();
+  void waitTxOn();
+  //  void initSSTVImage();
+  void waitEnd();
+  double toneDuration;
+  double toneLowerFrequency;
+  double toneUpperFrequency;
+  esstvMode oldMode;
+  bool started;
+  drmTransmitter *txDRM;
+  drmTxParams drmTxParameters;
+  QByteArray baDRM;
+  double waterfallTime;
+  QList <txSession> txList;
+  bool useHybrid;
+  QString hybridFilename;
+};
+
+#endif
+
+
diff --git a/qsstv/txwidget.cpp b/qsstv/txwidget.cpp
new file mode 100644
index 0000000..c87a484
--- /dev/null
+++ b/qsstv/txwidget.cpp
@@ -0,0 +1,749 @@
+#include "txwidget.h"
+#include <rxwidget.h>
+#include "ui_txwidget.h"
+#include <QTimer>
+#include "configparams.h"
+#include "dispatcher.h"
+#include "utils/supportfunctions.h"
+#include "gallerywidget.h"
+
+#include "ui_freqform.h"
+#include "ui_sweepform.h"
+#include "widgets/cameracontrol.h"
+#include "videocapt/videocapture.h"
+#include "sound/soundio.h"
+#include "widgets/waterfallform.h"
+#include "drmtx/bsrform.h"
+#include "drmrx/fixform.h"
+#include "mainwindow.h"
+
+
+
+
+txWidget *txWidgetPtr;
+
+txWidget::txWidget(QWidget *parent) :
+  QWidget(parent),
+  ui(new Ui::txWidget)
+{
+  int i;
+  QString tmp;
+  ui->setupUi(this);
+  txFunctionsPtr=new txFunctions();
+  imageViewerPtr=ui->imageFrame;
+  imageViewerPtr->createImage(QSize(320,256),QColor(0,0,128));
+  imageViewerPtr->displayImage(false);
+  readSettings();
+  setProfileNames();
+
+  for(i=0;i<NUMSSTVMODES-1;i++) // exclude Calibrate
+    {
+      ui->sstvModeComboBox->addItem(getSSTVModeNameLong((esstvMode)i));
+    }
+  connect(ui->sstvModeComboBox,SIGNAL(activated(int)),SLOT(slotModeChanged(int )));
+  connect(ui->templatesComboBox,SIGNAL(currentIndexChanged(int)),SLOT(slotGetParams()));
+  connect(ui->templateCheckBox,SIGNAL(toggled(bool)),SLOT(slotGetParams()));
+  connect(ui->cwCheckBox,SIGNAL(toggled(bool)),SLOT(slotGetParams()));
+  connect(ui->voxCheckBox,SIGNAL(toggled(bool)),SLOT(slotGetParams()));
+  connect(ui->toCallLineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->operatorLineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->rsvLineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->comment1LineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->comment2LineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->comment3LineEdit,SIGNAL(editingFinished ()),SLOT(slotGetParams()));
+  connect(ui->startToolButton, SIGNAL(clicked()), this, SLOT(slotStart()));
+//  connect(ui->hybridCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotHybridStart(bool)));
+  connect(ui->stopToolButton, SIGNAL(clicked()), this, SLOT(slotStop()));
+  connect(ui->drmProfileComboBox,SIGNAL(activated(int)),SLOT(slotProfileChanged(int )));
+
+  connect(ui->generateToneToolButton, SIGNAL(clicked()), this, SLOT(slotGenerateSignal()));
+  connect(ui->sweepToneToolButton, SIGNAL(clicked()), this, SLOT(slotSweepSignal()));
+  connect(ui->repeaterToneToolButton, SIGNAL(clicked()), this, SLOT(slotGenerateRepeaterTone()));
+  connect(ui->openToolButton, SIGNAL(clicked()), this, SLOT(slotFileOpen()));
+  //   connect(ui->actionAlignement, SIGNAL(triggered()), this, SLOT(slotAlignementSignal()));
+  connect(ui->editToolButton, SIGNAL(clicked()), this, SLOT(slotEdit()));
+  //   connect(ui->actionReplay, SIGNAL(triggered()), this, SLOT(slotReplay()));
+  connect(ui->snapshotToolButton, SIGNAL(clicked()), this, SLOT(slotSnapshot()));
+  connect(imageViewerPtr, SIGNAL(imageChanged()), this, SLOT(slotGetParams()));
+  connect(ui->sizeSlider,SIGNAL(valueChanged(int)),SLOT(slotSize(int)));
+  connect(ui->sizeSlider,SIGNAL(sliderReleased()),SLOT(slotSizeApply()));
+  connect(ui->settingsTableWidget,SIGNAL(currentChanged(int)),this, SLOT(slotTransmissionMode(int)));
+  connect(ui->imageFrame,SIGNAL(imageChanged()),SLOT(slotImageChanged()));
+
+
+  currentTXMode=NOMODE;
+}
+
+txWidget::~txWidget()
+{
+    delete ui;
+}
+
+void txWidget::init()
+{
+  splashStr+=QString( "Setting up TX" ).rightJustified(25,' ')+"\n";
+  splashPtr->showMessage ( splashStr ,Qt::AlignLeft,Qt::white);
+  qApp->processEvents();
+  ed=NULL;
+  repeaterIndex=0;
+  initView();
+  repeaterTimer=new QTimer(this);
+  connect(repeaterTimer,SIGNAL(timeout()),SLOT(slotRepeaterTimer()));
+  repeaterTimer->start(60000*repeaterImageInterval);
+  imageViewerPtr->setType(imageViewer::TXIMG);
+  slotModeChanged(modeIndexTx);
+  slotSize(sizeRatio);
+  txFunctionsPtr->start();
+
+}
+
+void txWidget::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("TX");
+  modeIndexTx=((esstvMode)qSettings.value("modeIndexTx",0).toInt());
+  templateIndex=qSettings.value("templateIndex",0).toInt();
+  useTemplate=qSettings.value("useTemplate",false).toBool();
+  useCW=qSettings.value("useCW",false).toBool();
+  useVOX=qSettings.value("useVOX",false).toBool();
+  useHybrid=qSettings.value("useHybrid",false).toBool();
+  sizeRatio=qSettings.value("sizeRatio",25).toInt();
+  drmParams.bandwith=qSettings.value("drmBandWith",0).toInt();
+  drmParams.interleaver=qSettings.value("drmInterLeaver",0).toInt();
+  drmParams.protection=qSettings.value("drmProtection",0).toInt();
+  drmParams.qam=qSettings.value("drmQAM",0).toInt();
+  drmParams.robMode=qSettings.value("drmRobMode",0).toInt();
+  drmParams.reedSolomon=qSettings.value("drmReedSolomon",0).toInt();
+  qSettings.endGroup();
+  setParams();
+}
+
+void txWidget::writeSettings()
+{
+  QSettings qSettings;
+  slotGetParams();
+  qSettings.beginGroup("TX");
+  qSettings.setValue( "modeIndexTx", modeIndexTx);
+  qSettings.setValue( "templateIndex", templateIndex);
+  qSettings.setValue( "useTemplate", useTemplate);
+  qSettings.setValue( "useVOX", useVOX);
+  qSettings.setValue( "useCW", useCW);
+  qSettings.setValue( "useHybrid", useHybrid);
+  qSettings.setValue("drmBandWith",drmParams.bandwith);
+  qSettings.setValue("drmInterLeaver",drmParams.interleaver);
+  qSettings.setValue("drmProtection",drmParams.protection);
+  qSettings.setValue("drmQAM",drmParams.qam);
+  qSettings.setValue("drmRobMode",drmParams.robMode);
+  qSettings.setValue("drmReedSolomon",drmParams.reedSolomon);
+  qSettings.setValue("sizeRatio",sizeRatio);
+  qSettings.endGroup();
+}
+
+void txWidget::slotGetParams()
+{
+  int temp=modeIndexTx;
+  getIndex(temp,ui->sstvModeComboBox);
+  modeIndexTx=esstvMode(temp);
+  getIndex(templateIndex,ui->templatesComboBox);
+  getValue(useTemplate,ui->templateCheckBox);
+  getValue(useVOX,ui->voxCheckBox);
+  getValue(useCW,ui->cwCheckBox);
+  getValue(useHybrid,ui->hybridCheckBox);
+  getValue(imageViewerPtr->toCall,ui->toCallLineEdit);
+  getValue(imageViewerPtr->toOperator,ui->operatorLineEdit);
+  getValue(imageViewerPtr->rsv,ui->rsvLineEdit);
+  getValue(imageViewerPtr->comment1,ui->comment1LineEdit);
+  getValue(imageViewerPtr->comment2,ui->comment2LineEdit);
+  getValue(imageViewerPtr->comment3,ui->comment3LineEdit);
+  getIndex(drmParams.bandwith,ui->drmTxBandwidthComboBox);
+  getIndex(drmParams.interleaver,ui->drmTxInterleaveComboBox);
+  getIndex(drmParams.protection,ui->drmTxProtectionComboBox);
+  getIndex(drmParams.qam,ui->drmTxQAMComboBox);
+  getIndex(drmParams.robMode,ui->drmTxModeComboBox);
+  getIndex(drmParams.reedSolomon,ui->drmTxReedSolomonComboBox);
+  getValue(sizeRatio,ui->sizeSlider);
+  drmParams.callsign=myCallsign;
+//  if(!txFunctionsPtr->isRunning())
+  if(txFunctionsPtr->getTXState()==txFunctions::TXIDLE)
+    {
+      applyTemplate();
+    }
+}
+
+void txWidget::setParams()
+{
+  setIndex(((int)modeIndexTx),ui->sstvModeComboBox);
+  setIndex(templateIndex,ui->templatesComboBox);
+  setValue(useTemplate,ui->templateCheckBox);
+  setValue(useVOX,ui->voxCheckBox);
+  setValue(useCW,ui->cwCheckBox);
+  setValue(useHybrid,ui->hybridCheckBox);
+  setIndex(drmParams.bandwith,ui->drmTxBandwidthComboBox);
+  setIndex(drmParams.interleaver,ui->drmTxInterleaveComboBox);
+  setIndex(drmParams.protection,ui->drmTxProtectionComboBox);
+  setIndex(drmParams.qam,ui->drmTxQAMComboBox);
+  setIndex(drmParams.robMode,ui->drmTxModeComboBox);
+  setIndex(drmParams.reedSolomon,ui->drmTxReedSolomonComboBox);
+  setValue(sizeRatio,ui->sizeSlider);
+}
+
+void txWidget::copyProfile(drmTxParams d)
+{
+  drmParams=d;
+  setIndex(drmParams.bandwith,ui->drmTxBandwidthComboBox);
+  setIndex(drmParams.interleaver,ui->drmTxInterleaveComboBox);
+  setIndex(drmParams.protection,ui->drmTxProtectionComboBox);
+  setIndex(drmParams.qam,ui->drmTxQAMComboBox);
+  setIndex(drmParams.robMode,ui->drmTxModeComboBox);
+  setIndex(drmParams.reedSolomon,ui->drmTxReedSolomonComboBox);
+}
+
+void txWidget::initView()
+{
+  ui->sstvModeComboBox->setCurrentIndex((int)modeIndexTx);
+  setupTemplatesComboBox();
+  ui->progressBar->setRange(0,100);
+}
+
+void txWidget::setupTemplatesComboBox()
+{
+  QStringList sl;
+   int i;
+  ui->templatesComboBox->clear();
+  sl=galleryWidgetPtr->getFilenames();
+  for(i=0;i<sl.count();i++)
+  {
+    ui->templatesComboBox->insertItem(i,sl.at(i));
+  }
+   ui->templatesComboBox->setCurrentIndex(templateIndex);
+}
+
+void  txWidget::setPreviewWidget(QString fn)
+{
+   ui->previewWidget->openImage(fn,false,false);
+}
+
+void txWidget::start(bool st,bool check)
+{
+  if(st)
+    {
+      slotGetParams();
+      if(check)
+        {
+          if(!imageViewerPtr->hasValidImage())
+            {
+              QMessageBox::warning(this,"TX Error","No image loaded");
+              return;
+            }
+        }
+      soundIOPtr->startPlayback();
+    }
+  else
+    {
+      writeSettings();
+      txFunctionsPtr->stopAndWait();
+    }
+}
+
+void txWidget::slotStart()
+{
+//  start(true);
+  QFileInfo finf;
+  QString fn;
+  slotGetParams();
+  switch(currentTXMode)
+    {
+    case SSTV:
+       dispatcherPtr->startSSTVTx();
+    break;
+    case DRM:
+      txFunctionsPtr->setDRMTxParams(drmParams);
+      if(ui->hybridCheckBox->isChecked())
+        {
+          finf.setFile(txWidgetPtr->getImageViewerPtr()->getFilename());
+
+          fn="de_"+myCallsign+"-1-"+finf.baseName()+"."+finf.suffix();
+          txFunctionsPtr->setHybrid(true,fn);
+          dispatcherPtr->startDRMHybridTx(fn);
+        }
+      else
+        {
+          txFunctionsPtr->setHybrid(false);
+          dispatcherPtr->startDRMTx();
+        }
+
+
+    break;
+//    case FAX:
+//    break;
+    case NOMODE:
+      break;
+    }
+}
+
+
+void txWidget::sendBSR()
+{
+  QByteArray *p;
+  bsrForm::eResult res;
+  bsrForm bsrf;
+  bsrf.init();
+  res=(bsrForm::eResult)bsrf.exec();
+  if(res==bsrForm::CANCEL) return;
+  p=bsrf.getBA(res==bsrForm::COMPAT);
+  drmParams=bsrf.getDRMParams();
+  txFunctionsPtr->setDRMTxParams(drmParams);
+  dispatcherPtr->startDRMBSRTx(p);
+}
+
+
+void txWidget::sendID()
+{
+  dispatcherPtr->sendWF(myCallsign);
+}
+
+void txWidget::sendWfText()
+{
+  waterfallForm wf;
+  if((wf.exec()==QDialog::Accepted)&&(!wf.text().isEmpty()))
+    {
+      dispatcherPtr->sendWF(wf.text());
+    }
+}
+
+void txWidget::slotStop()
+{
+//  qDebug() << "slotStop tx";
+  soundIOPtr->abortPlayback();
+  start(false);
+  dispatcherPtr->restartRX();
+}
+
+
+void txWidget::slotDisplayStatusMessage(QString s)
+{
+    statusBarPtr->showMessage(s);
+}
+
+
+//void txWidget::slotReplay()
+//{
+//  QString fn;
+//  QImage *im=NULL;
+//  fn=galleryWidgetPtr->getLastRxImage();
+//  editorScene scene(0);
+//  QFile f(fn);
+//  if(!scene.load(f))
+//    {
+//      QMessageBox::warning(this,"Image Properties","Error while loading\n" + fn);
+//      return;
+//    }
+//  im=scene.renderImage();
+//  dispatcherPtr->setTXImage(im);
+//  slotStartTX();
+//}
+
+
+void txWidget::slotFileOpen()
+{
+  QString fileName;
+  imageViewerPtr->openImage(fileName,txImagesPath,true,true,true);
+}
+
+void txWidget::slotGenerateSignal()
+{
+  QDialog qd;
+   Ui::freqForm *ff=new Ui::freqForm;
+
+  ff->setupUi(&qd);
+  int freq;
+  int  duration;
+  if(qd.exec())
+    {
+      getValue(freq,ff->frequencySpinBox);
+      getValue(duration,ff->durationSpinBox);
+      dispatcherPtr->sendTone((double)duration,(double)freq);
+    }
+}
+
+void txWidget::slotSweepSignal()
+{
+  QDialog qd;
+ Ui::sweepForm *ff=new Ui::sweepForm;
+  ff->setupUi(&qd);
+  int upperFreq;
+  int lowerFreq;
+  int  duration;
+  if(qd.exec())
+    {
+      getValue(lowerFreq,ff->lowerFrequencySpinBox);
+      getValue(upperFreq,ff->upperFrequencySpinBox);
+      getValue(duration,ff->durationSpinBox);
+      dispatcherPtr->sendSweepTone((double)duration,(double)lowerFreq,(double)upperFreq);
+    }
+}
+
+
+void txWidget::slotGenerateRepeaterTone()
+{
+  addToLog(QString("start of buffer %1").arg(soundIOPtr->txBuffer.count()),LOGTXMAIN);
+  dispatcherPtr->sendTone(3.,1750);
+}
+
+
+
+void txWidget::slotEdit()
+{
+  if (ed!=NULL) delete ed;
+  ed=new editor(this);
+  if(txFunctionsPtr->isRunning())
+    {
+      QMessageBox::warning(this,"Editor","Transmission busy, editor not available");
+      return;
+    }
+  connect(ed,SIGNAL(imageAvailable(QImage *)),SLOT(setImage(QImage *)));
+  ed->setImage(imageViewerPtr->getImagePtr());
+  ed->show();
+}
+
+
+void txWidget::slotModeChanged(int m)
+{
+  addToLog("slotModeChange",LOGTXMAIN);
+  modeIndexTx=(esstvMode)m;
+//  txFunctionsPtr->create(modeIndexTx,txClock/SUBSAMPLINGRATIO);
+  applyTemplate();
+}
+
+
+
+
+/** \todo implement repeater */
+void txWidget::applyTemplate()
+{
+  if(currentTXMode!=DRM)
+    {
+      txFunctionsPtr->create(modeIndexTx,txClock/SUBSAMPLINGRATIO);
+      imageViewerPtr->applyTemplate(galleryWidgetPtr->getTemplateFileName(ui->templatesComboBox->currentIndex()),useTemplate, txSSTVParam.numberOfPixels,txSSTVParam.numberOfDisplayLines);
+    }
+  else
+    {
+      slotSize(sizeRatio);
+      imageViewerPtr->applyTemplate(galleryWidgetPtr->getTemplateFileName(ui->templatesComboBox->currentIndex()),useTemplate);
+    }
+}
+
+void 	txWidget::setImage(QImage *im)
+{
+  if(imageViewerPtr->openImage(*im))
+    {
+      applyTemplate();
+    }
+}
+
+void 	txWidget::setImage(QString fn)
+{
+  imageViewerPtr->openImage(fn,true,true);
+}
+
+
+void txWidget::setProgress(uint prg)
+{
+  ui->progressBar->setValue(prg);
+}
+
+
+/** \todo implement repeater */
+
+// void txWidget::repeat(QImage *im,esstvMode sm)
+void txWidget::repeat(QImage *,esstvMode )
+{
+/*	setValue((int)sm,ui->modeComboBox);
+  txf->setModeIndex((sstvMode)sm);
+//	slotModeChanged(sm);
+
+  txf->setImage(im);
+  dsp->setCaptureSource(true,NULL,false);
+  txf->process();*/
+}
+
+void txWidget::slotRepeaterTimer()
+{
+  QString fn;
+  QFile fi;
+  if(txFunctionsPtr->isRunning()) repeaterTimer->start(60000*repeaterImageInterval);
+
+  else if ((rxWidgetPtr->functionsPtr()->isRunning()) || (!repeaterEnable))
+    {
+      // wait 10 seconds and check if busy or repeaterEnable has changed
+      repeaterTimer->start(10000);
+    }
+  else
+    {
+      switch(repeaterIndex)
+        {
+          case 0:
+            fn=repeaterImage1;
+          break;
+          case 1:
+            fn=repeaterImage2;
+          break;
+          case 2:
+            fn=repeaterImage3;
+          break;
+          case 3:
+            fn=repeaterImage4;
+          break;
+          default:
+            fn=repeaterImage1;
+          break;
+        }
+      fi.setFileName(fn);
+      if (fi.exists())
+        {
+          slotModeChanged(repeaterTxMode);
+          setImage(fn);
+        }
+      repeaterIndex++;
+      if(repeaterIndex>3) repeaterIndex=0;
+      repeaterTimer->start(60000*repeaterImageInterval);
+      //QApplication::processEvents();
+      startAutoRepeaterEvent* ce = new startAutoRepeaterEvent();
+      QApplication::postEvent(dispatcherPtr, ce );
+    }
+}
+
+
+
+void txWidget::slotSnapshot()
+{
+  cameraControl cc(this);
+  if(cc.exec())
+    {
+      setImage(cc.getImage());
+    }
+}
+
+
+
+
+
+//void txWidget::test()
+//{
+
+//}
+
+void txWidget::setSettingsTab()
+{
+  int i;
+  currentTXMode=transmissionModeIndex;
+  {
+    if(currentTXMode==DRM)
+      {
+        ui->hybridCheckBox->setEnabled(true);
+      }
+    else
+      {
+        ui->hybridCheckBox->setEnabled(false);
+      }
+
+  }
+
+  if((transmissionModeIndex>=0)&&(transmissionModeIndex<NOMODE))
+    {
+      for(i=0;i<NOMODE;i++)
+        {
+          if(i!=transmissionModeIndex) ui->settingsTableWidget->widget(i)->setEnabled(false);
+        }
+      ui->settingsTableWidget->widget(transmissionModeIndex)->setEnabled(true);
+      ui->settingsTableWidget->setCurrentIndex(transmissionModeIndex);
+    }
+  applyTemplate();
+}
+
+bool txWidget::prepareFIX(QByteArray bsrByteArray)
+{
+  int i,j;
+  displayMBoxEvent *stce;
+//  unsigned int mode=0;
+//  eRSType rsType;
+  bool inSeries;
+  QString fileName,extension;
+  txSession *sessionPtr;
+  bool extended,done;
+  int block;
+  unsigned short trID,lastBlock;
+
+  fixBlockList.clear();
+  txFunctionsPtr->setHybrid(true);
+  QString str(bsrByteArray);
+  str.replace("\r","");
+  //  information is in the QByteArray ba
+  QStringList sl;
+  sl=str.split("\n",QString::SkipEmptyParts);
+
+  if(sl.at(1)!="H_OK")
+    {
+      return false;
+    }
+  trID=sl.at(0).toUInt();
+  lastBlock=sl.at(3).toUInt();
+  fixBlockList.append(lastBlock++);
+  inSeries=false;
+  done=false;
+  extended=false;
+  for(i=4;(!done)&&i<sl.count();i++)
+    {
+      block=sl.at(i).toInt();
+      if(block==-99)
+        {
+          done=true;
+          i++;
+          break;
+        }
+      if(block<0) inSeries=true;
+      else
+        {
+          if(inSeries)
+            {
+              inSeries=false;
+              for(j=lastBlock;j<block;j++) fixBlockList.append(j);
+            }
+          fixBlockList.append(block);
+          lastBlock=block+1;
+        }
+    }
+  // check if we have a filename beyond -99
+  if((i+1)<sl.count()) // we need an additional 2 entries (filename and mode)
+    {
+      extended=true;
+      fileName=sl.at(i++);
+//      mode=sl.at(i).toUInt();
+//      qDebug() << " bsr received with " << fileName << mode;
+    }
+  fixForm fx(mainWindowPtr);
+  sessionPtr=txFunctionsPtr->getSessionPtr(trID);
+  if ((sessionPtr==NULL) && (!extended))
+    {
+      stce= new displayMBoxEvent("BSR Received","This BSR is not for you");
+      QApplication::postEvent( dispatcherPtr, stce );  // Qt will delete it when done
+
+      return false;
+    }
+  else if (sessionPtr!=NULL)
+    {
+      // take it from the transmitlist
+
+      fx.setInfoInternal(paramsToMode(sessionPtr->drmParams),sessionPtr->filename,fixBlockList.count(),&sessionPtr->ba);
+      if(fx.exec()==QDialog::Rejected) return false;
+      txFunctionsPtr->initDRMFIX(sessionPtr);
+     }
+  else
+    {
+      QMessageBox *mbox = new QMessageBox(this);
+      mbox->setWindowTitle(tr("BSR Received"));
+      mbox->setText("This BSR is not for you");
+      mbox->show();
+      QTimer::singleShot(4000, mbox, SLOT(hide()));
+      return false;
+    }
+  return true;
+}
+
+//bool txWidget::prepareHybrid(QString fn)
+//{
+//  txFunctionsPtr->setHybrid(true);
+//  return true;
+//}
+
+bool txWidget::prepareText(QString )
+{
+  txFunctionsPtr->setHybrid(false);
+//  QString fn;
+//  QDateTime dt(QDateTime::currentDateTime().toUTC()); //this is compatible with QT 4.6
+//  dt.setTimeSpec(Qt::UTC);
+//  dt
+//  int intdate=dt.date().year()*10000+dt.date().month()*100+dt.date().day();
+//  int inttime=dt.time().hour()*10000+100*dt.time().minute()+dt.time().second();
+//  s.sprintf("%s_%d_%d",rxWidgetPtr->functionsPtr()->getModeString().toLatin1().data(),intdate,inttime);
+//  fileName=rxImagesPath+"/"+s+"."+defaultImageFormat;
+//  QFile fo("de ON4QZ")
+  return true;
+}
+
+
+void txWidget::slotSize(int v)
+{
+  QString t;
+  if(sizeRatio!=v)
+    {
+      sizeRatioChanged=true;
+      sizeRatio=v;
+      ui->sizeKbLabel->setText( QString::number(imageViewerPtr->calcSize(v))+ "kB");
+    }
+}
+
+void txWidget::slotSizeApply()
+{
+  QApplication::setOverrideCursor(Qt::WaitCursor);
+  sizeRatioChanged=false;
+  imageViewerPtr->setSizeRatio(sizeRatio);
+  imageViewerPtr->displayImage(currentTXMode==DRM);
+  QApplication::restoreOverrideCursor();
+}
+
+void txWidget::slotTransmissionMode(int rxtxMode)
+{
+  transmissionModeIndex=(etransmissionMode)rxtxMode;
+  start(false);
+  rxWidgetPtr->setSettingsTab();
+  setSettingsTab();
+  if(transmissionModeIndex==DRM)
+    {
+      ui->sizeLabel->setEnabled(true);
+      ui->sizeSlider->setEnabled(true);
+      ui->sizeKbLabel->setEnabled(true);
+//      ui->sizeApplyPushButton->setEnabled(true);
+      mainWindowPtr->setBSRPushButton(true);
+    }
+  else
+    {
+      ui->sizeLabel->setEnabled(false);
+      ui->sizeSlider->setEnabled(false);
+      ui->sizeKbLabel->setEnabled(false);
+//      ui->sizeApplyPushButton->setEnabled(false);
+      mainWindowPtr->setBSRPushButton(false);
+    }
+}
+
+void txWidget::slotProfileChanged(int i)
+{
+    drmProfilePtr->getDRMParams(i,drmParams);
+    setParams();
+}
+
+void txWidget::setProfileNames()
+{
+    QString tmp;
+    ui->drmProfileComboBox->clear();
+    if(drmProfilePtr->getName(0,tmp))
+    {
+      ui->drmProfileComboBox->addItem(tmp);
+    }
+    if(drmProfilePtr->getName(1,tmp))
+    {
+      ui->drmProfileComboBox->addItem(tmp);
+    }
+    if(drmProfilePtr->getName(2,tmp))
+    {
+      ui->drmProfileComboBox->addItem(tmp);
+    }
+}
+
+void txWidget::slotImageChanged()
+{
+    int sz=sizeRatio;
+    sizeRatio=0;
+    slotSize(sz);
+    slotSizeApply();
+}
diff --git a/qsstv/txwidget.h b/qsstv/txwidget.h
new file mode 100644
index 0000000..ff1fa38
--- /dev/null
+++ b/qsstv/txwidget.h
@@ -0,0 +1,99 @@
+#ifndef TXWIDGET_H
+#define TXWIDGET_H
+
+#include <QWidget>
+#include "widgets/imageviewer.h"
+#include "sstv/sstvparam.h"
+#include "txfunctions.h"
+#include "drmtx/drmtransmitter.h"
+
+class drmTransmitter;
+namespace Ui {
+    class txWidget;
+}
+
+class txWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+  explicit txWidget(QWidget *parent = 0);
+    ~txWidget();
+  void init();
+  void start(bool st, bool check=true);
+  void writeSettings();
+  void readSettings();
+  imageViewer *getImagePtr();
+  void repeat(QImage *im,esstvMode sm);
+  void setImage(QImage *ima);
+  void setImage(QString fn);
+  void setProgress(uint prg);
+  void setupTemplatesComboBox();
+  void setPreviewWidget(QString fn);
+  void setSettingsTab();
+  txFunctions *functionsPtr() {return txFunctionsPtr;}
+  imageViewer *getImageViewerPtr(){ return imageViewerPtr;}
+  bool prepareFIX(QByteArray bsrByteArray);
+//  bool prepareHybrid(QString fn);
+  bool prepareText(QString txt);
+  void copyProfile(drmTxParams d);
+  void setProfileNames();
+
+
+//  void test();
+//  void sendFIX();
+  void sendBSR();
+  void sendWfText();
+  void sendID();
+
+public slots:
+  void slotGetParams();
+  void slotStart();
+  void slotStop();
+  void slotDisplayStatusMessage(QString);
+
+  void slotGenerateSignal();
+  void slotSweepSignal();
+  void slotGenerateRepeaterTone();
+  void slotEdit();
+//  void slotReplay();
+  void slotRepeaterTimer();
+  void slotFileOpen();
+  void slotModeChanged(int);
+  void slotSnapshot();
+  void slotSize(int);
+  void slotSizeApply();
+  void slotTransmissionMode(int rxtxMode);
+  void slotProfileChanged(int );
+  void slotImageChanged();
+
+private:
+    Ui::txWidget *ui;
+    txFunctions *txFunctionsPtr;
+    void initView();
+    void setParams();
+    editor *ed;
+    QTimer *repeaterTimer;
+    int repeaterIndex;
+    QImage origImage;
+    QImage resultImage;
+    void applyTemplate();
+    imageViewer *imageViewerPtr;
+    etransmissionMode currentTXMode;
+    drmTxParams drmParams;
+    int sizeRatio;
+    bool sizeRatioChanged;
+    int drmProfileIdx;
+
+};
+
+extern txWidget *txWidgetPtr;
+
+#endif // TXWIDGET_H
+
+
+
+
+
+
+
diff --git a/qsstv/txwidget.ui b/qsstv/txwidget.ui
new file mode 100644
index 0000000..80a221d
--- /dev/null
+++ b/qsstv/txwidget.ui
@@ -0,0 +1,1493 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>txWidget</class>
+ <widget class="QWidget" name="txWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>840</width>
+    <height>529</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>8</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout_7">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1,0">
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_5">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <item>
+        <widget class="QToolButton" name="startToolButton">
+         <property name="toolTip">
+          <string>Start transmit</string>
+         </property>
+         <property name="statusTip">
+          <string>Start transmit</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/start.png</normaloff>:/icons/start.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="stopToolButton">
+         <property name="toolTip">
+          <string>Stop transmit</string>
+         </property>
+         <property name="statusTip">
+          <string>Stop transmit</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/stop.png</normaloff>:/icons/stop.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="repeaterToneToolButton">
+         <property name="toolTip">
+          <string>Send repeater tone</string>
+         </property>
+         <property name="statusTip">
+          <string>Send repeater tone</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/tone.png</normaloff>:/icons/tone.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="openToolButton">
+         <property name="toolTip">
+          <string>Load image</string>
+         </property>
+         <property name="statusTip">
+          <string>Load image</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/fileopen.png</normaloff>:/icons/fileopen.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="editToolButton">
+         <property name="toolTip">
+          <string>Edit image</string>
+         </property>
+         <property name="statusTip">
+          <string>Edit image</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/edit.png</normaloff>:/icons/edit.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="snapshotToolButton">
+         <property name="toolTip">
+          <string>Camera snapshot</string>
+         </property>
+         <property name="statusTip">
+          <string>Camera snapshot</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/camera.png</normaloff>:/icons/camera.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="hybridCheckBox">
+         <property name="text">
+          <string>Hybrid</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QToolButton" name="generateToneToolButton">
+         <property name="toolTip">
+          <string>Send tone</string>
+         </property>
+         <property name="statusTip">
+          <string>Send tone</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/doubletone.png</normaloff>:/icons/doubletone.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="sweepToneToolButton">
+         <property name="toolTip">
+          <string>Send sweep tones</string>
+         </property>
+         <property name="statusTip">
+          <string>Send sweep tones</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="qsstv.qrc">
+           <normaloff>:/icons/sweep.png</normaloff>:/icons/sweep.png</iconset>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="imageViewer" name="imageFrame" native="true">
+       <property name="minimumSize">
+        <size>
+         <width>580</width>
+         <height>360</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>1200</width>
+         <height>800</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_6">
+       <item>
+        <widget class="QLabel" name="sizeLabel">
+         <property name="text">
+          <string>Size</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSlider" name="sizeSlider">
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="pageStep">
+          <number>10</number>
+         </property>
+         <property name="sliderPosition">
+          <number>25</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="tickPosition">
+          <enum>QSlider::TicksBelow</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="sizeKbLabel">
+         <property name="minimumSize">
+          <size>
+           <width>80</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>80</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::StyledPanel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Sunken</enum>
+         </property>
+         <property name="lineWidth">
+          <number>3</number>
+         </property>
+         <property name="midLineWidth">
+          <number>1</number>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_3">
+     <property name="spacing">
+      <number>1</number>
+     </property>
+     <item>
+      <widget class="QTabWidget" name="settingsTableWidget">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="palette">
+        <palette>
+         <active>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>211</red>
+             <green>221</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>233</red>
+             <green>238</green>
+             <blue>246</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>105</red>
+             <green>110</green>
+             <blue>119</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>140</red>
+             <green>147</green>
+             <blue>158</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>215</red>
+             <green>229</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Highlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="HighlightedText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Link">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="LinkVisited">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </active>
+         <inactive>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>211</red>
+             <green>221</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>244</red>
+             <green>248</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>105</red>
+             <green>110</green>
+             <blue>119</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>140</red>
+             <green>147</green>
+             <blue>158</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>215</red>
+             <green>229</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Highlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="HighlightedText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Link">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>192</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="LinkVisited">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>0</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </inactive>
+         <disabled>
+          <colorrole role="WindowText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Button">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>211</red>
+             <green>221</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Light">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Midlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>244</red>
+             <green>248</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Dark">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>105</red>
+             <green>110</green>
+             <blue>119</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Mid">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>140</red>
+             <green>147</green>
+             <blue>158</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Text">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="BrightText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="ButtonText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>128</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Base">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Window">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>215</red>
+             <green>229</green>
+             <blue>238</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Shadow">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>0</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Highlight">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="HighlightedText">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>255</red>
+             <green>255</green>
+             <blue>255</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="Link">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>0</red>
+             <green>0</green>
+             <blue>192</blue>
+            </color>
+           </brush>
+          </colorrole>
+          <colorrole role="LinkVisited">
+           <brush brushstyle="SolidPattern">
+            <color alpha="255">
+             <red>128</red>
+             <green>0</green>
+             <blue>128</blue>
+            </color>
+           </brush>
+          </colorrole>
+         </disabled>
+        </palette>
+       </property>
+       <property name="currentIndex">
+        <number>1</number>
+       </property>
+       <widget class="QWidget" name="sstvTXTab">
+        <attribute name="title">
+         <string>SSTV</string>
+        </attribute>
+        <layout class="QVBoxLayout" name="verticalLayout_6">
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_5">
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_4">
+             <item>
+              <widget class="QLabel" name="rstLabel_7">
+               <property name="text">
+                <string>Mode</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+               <property name="wordWrap">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="sstvModeComboBox">
+               <property name="toolTip">
+                <string>Transmit mode</string>
+               </property>
+               <property name="statusTip">
+                <string>Transmit mode</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <spacer name="verticalSpacer_2">
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>20</width>
+               <height>40</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="drmTXTab">
+        <attribute name="title">
+         <string>DRM</string>
+        </attribute>
+        <layout class="QVBoxLayout" name="verticalLayout">
+         <property name="spacing">
+          <number>1</number>
+         </property>
+         <property name="margin">
+          <number>1</number>
+         </property>
+         <item>
+          <layout class="QGridLayout" name="gridLayout_2">
+           <property name="spacing">
+            <number>0</number>
+           </property>
+           <item row="1" column="0">
+            <widget class="QLabel" name="drmQAMLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>QAM</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QComboBox" name="drmTxReedSolomonComboBox">
+             <item>
+              <property name="text">
+               <string>None</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>RS1</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>RS2</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>RS3</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>RS4</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="drmModeLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Mode</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="drmReadSolomonLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Rs</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="drmProtectionLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Prot.</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="4">
+            <widget class="QSpinBox" name="drmTxInstancesSpinBox"/>
+           </item>
+           <item row="0" column="4">
+            <widget class="QComboBox" name="drmTxBandwidthComboBox">
+             <item>
+              <property name="text">
+               <string>2.2 KHz</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>2.5 KHz</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QComboBox" name="drmTxModeComboBox">
+             <item>
+              <property name="text">
+               <string>A</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>B</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>E</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="1" column="4">
+            <widget class="QComboBox" name="drmTxInterleaveComboBox">
+             <item>
+              <property name="text">
+               <string>Short</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Long</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QComboBox" name="drmTxProtectionComboBox">
+             <item>
+              <property name="text">
+               <string>High</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Low</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="0" column="3">
+            <widget class="QLabel" name="drmBandwidthLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>BW</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="3">
+            <widget class="QLabel" name="drmInterleavelabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Interleave</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="3">
+            <widget class="QLabel" name="drmInstancesLabel">
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Instances</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+             <property name="wordWrap">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QComboBox" name="drmTxQAMComboBox">
+             <item>
+              <property name="text">
+               <string>4</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>16</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>64</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item row="3" column="3">
+            <widget class="QLabel" name="drmProfileLabel">
+             <property name="text">
+              <string>Profile</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="4">
+            <widget class="QComboBox" name="drmProfileComboBox"/>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <spacer name="verticalSpacer_3">
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>0</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>2</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="progressLabel">
+         <property name="text">
+          <string>TX Progress</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QProgressBar" name="progressBar">
+         <property name="value">
+          <number>0</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_3">
+       <property name="horizontalSpacing">
+        <number>1</number>
+       </property>
+       <property name="verticalSpacing">
+        <number>0</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QCheckBox" name="voxCheckBox">
+         <property name="toolTip">
+          <string>Send leading tone</string>
+         </property>
+         <property name="statusTip">
+          <string>Send leading tone</string>
+         </property>
+         <property name="text">
+          <string>VOX</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QCheckBox" name="cwCheckBox">
+         <property name="toolTip">
+          <string>Send CW at end</string>
+         </property>
+         <property name="statusTip">
+          <string>Send CW at end</string>
+         </property>
+         <property name="text">
+          <string>CW</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QCheckBox" name="templateCheckBox">
+         <property name="text">
+          <string>Use Template</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QComboBox" name="templatesComboBox">
+         <property name="toolTip">
+          <string>Select template</string>
+         </property>
+         <property name="statusTip">
+          <string>Select template</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="horizontalSpacing">
+        <number>0</number>
+       </property>
+       <property name="verticalSpacing">
+        <number>1</number>
+       </property>
+       <item row="4" column="1" colspan="5">
+        <widget class="QLineEdit" name="comment1LineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>208</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="0">
+        <widget class="QLabel" name="rstLabel_6">
+         <property name="minimumSize">
+          <size>
+           <width>10</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>z</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="1" colspan="5">
+        <widget class="QLineEdit" name="comment2LineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>208</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="4">
+        <widget class="QLineEdit" name="rsvLineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>50</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="toCallLineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLabel" name="rsvLabel">
+         <property name="minimumSize">
+          <size>
+           <width>30</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>RSV</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <spacer name="horizontalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="toCallLabel">
+         <property name="minimumSize">
+          <size>
+           <width>30</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>To:</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="0">
+        <widget class="QLabel" name="rstLabel_5">
+         <property name="minimumSize">
+          <size>
+           <width>10</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>y</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="1" colspan="5">
+        <widget class="QLineEdit" name="comment3LineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>126</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>208</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="0">
+        <widget class="QLabel" name="rstLabel_4">
+         <property name="minimumSize">
+          <size>
+           <width>10</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>x</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLineEdit" name="operatorLineEdit">
+         <property name="minimumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>120</width>
+           <height>25</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Template info</string>
+         </property>
+         <property name="statusTip">
+          <string>Template info</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="operatorLabel">
+         <property name="minimumSize">
+          <size>
+           <width>30</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Op:</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <spacer name="horizontalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0">
+       <property name="spacing">
+        <number>4</number>
+       </property>
+       <item>
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="imageViewer" name="previewWidget" native="true">
+         <property name="minimumSize">
+          <size>
+           <width>100</width>
+           <height>80</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>0</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>imageViewer</class>
+   <extends>QWidget</extends>
+   <header>widgets/imageviewer.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="qsstv.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/qsstv/utils/buffermanag.h b/qsstv/utils/buffermanag.h
new file mode 100644
index 0000000..1809682
--- /dev/null
+++ b/qsstv/utils/buffermanag.h
@@ -0,0 +1,204 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#ifndef BUFFERMANAG_H
+#define BUFFERMANAG_H
+#include <string.h>
+#include <QMutex>
+#include "qsstvglobal.h"
+#include <QDebug>
+
+
+template <class T,unsigned int N> class buffer
+{
+public:
+  buffer()
+  {
+    reset();
+  }
+  unsigned int count()
+  {
+
+    return ((writeIndex-readIndex)& ((1<<N)-1));
+  }
+  unsigned int spaceLeft()
+  {
+
+    return ((1<<N)-count()-1);
+  }
+  unsigned int getBufferSize() {return (1<<N);}
+  bool get(T &v)
+  {
+    mutex.lock();
+    if(writeIndex==readIndex)
+      {
+        mutex.unlock();
+        return false;
+      }
+    v=memblock[readIndex++];
+    readIndex&= ((1<<N)-1);
+    mutex.unlock();
+    return true;
+  }
+  bool get(T *cp,unsigned int len)
+  {
+    int i;
+    mutex.lock();
+    if(len>count())
+      {
+        mutex.unlock();
+        return false;
+      }
+    for(i=0;i<len;i++)
+      {
+        cp[i]=memblock[readIndex++];
+        readIndex&= ((1<<N)-1);
+      }
+
+    mutex.unlock();
+    return true;
+  }
+  bool put(T v)
+  {
+    mutex.lock();
+    if(((writeIndex+1)&((1<<N)-1))==readIndex)
+      {
+        mutex.unlock();
+        return false;
+      }
+    memblock[writeIndex++]=v;
+    writeIndex&= ((1<<N)-1);
+    mutex.unlock();
+    return true;
+  }
+  bool put(T *cp,unsigned int len)
+  {
+    unsigned int i;
+    mutex.lock();
+
+    if(len>spaceLeft())
+      {
+        mutex.unlock();
+        return false;
+      }
+//    addToLog(QString("writing %1").arg(len),LOGSOUND);
+    for(i=0;i<len;i++)
+      {
+        memblock[writeIndex++]=cp[i];
+        writeIndex&= ((1<<N)-1);
+      }
+//    addToLog(QString("writeIndex %1").arg(writeIndex),LOGSOUND);
+    mutex.unlock();
+    return true;
+  }
+  void putNoCheck(T *cp,unsigned int len) // no boundary check performed
+  {
+    mutex.lock();
+    memcpy(&memblock[writeIndex],cp,len*sizeof(T));
+    writeIndex+=len;
+    writeIndex&= ((1<<N)-1);
+    mutex.unlock();
+  }
+
+  void reset()
+  {
+    mutex.lock();
+    readIndex=0;
+    writeIndex=0;
+    mutex.unlock();
+  }
+  void fill(T f)
+  {
+    mutex.lock();
+    for(int i=0; i< (1<<N) ;i++) memblock[i]=f;
+    mutex.unlock();
+  }
+  T *readPointer() { return &memblock[readIndex];}
+  T *writePointer() { return &memblock[writeIndex];}
+  T *basePointer() { return memblock;}
+
+  unsigned int getReadIndex() { return readIndex;}
+  unsigned int getWriteIndex() { return writeIndex;}
+  bool skip(unsigned int s)
+  {
+    mutex.lock();
+    if(s>count())
+      {
+        mutex.unlock();
+        return false;
+      }
+    readIndex+=s;
+    readIndex&= ((1<<N)-1);
+    mutex.unlock();
+    return true;
+  }
+  bool rewind(unsigned int s)
+  {
+    mutex.lock();
+    if(s>spaceLeft())
+      {
+        mutex.unlock();
+        return false;
+      }
+
+    readIndex-=s;
+    readIndex&= ((1<<N)-1);
+    mutex.unlock();
+    return true;
+  }
+  // no check is made if space available
+  bool advance(unsigned int s)
+  {
+    mutex.lock();
+    writeIndex+=s;
+    writeIndex&= ((1<<N)-1);
+    mutex.unlock();
+    return true;
+  }
+  T at(unsigned int i)
+  {
+    i&=((1<<N)-1);
+    return memblock[i];
+  }
+  void copy(T *dst,int len)
+  {
+    mutex.lock();
+    for(int i=0; i< len ;i++) dst[i]=memblock[(readIndex+i)&((1<<N)-1)];
+    mutex.unlock();
+  }
+  void copyNoCheck(T *dst,int len)
+  {
+    mutex.lock();
+    memcpy(dst,&memblock[readIndex],len*sizeof(T));
+    readIndex+=len;
+    readIndex&= ((1<<N)-1);
+    mutex.unlock();
+  }
+
+private:
+  T memblock [1<<N];
+  unsigned int readIndex;
+  unsigned int writeIndex;
+  QMutex mutex;
+};
+
+
+#endif
diff --git a/qsstv/utils/ftp.cpp b/qsstv/utils/ftp.cpp
new file mode 100644
index 0000000..226b859
--- /dev/null
+++ b/qsstv/utils/ftp.cpp
@@ -0,0 +1,390 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes                                      *
+ *   on4qz at telenet.be                                                      *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "ftp.h"
+#include "qsstvglobal.h"
+//#include <qmessagebox.h>
+#include <qfiledialog.h>
+
+#include <qapplication.h>
+#include "configparams.h"
+#include <QDebug>
+#include "dispatcher.h"
+#define FTPTIMEOUTTIME 12000
+
+ftpInterface::ftpInterface(QString id)
+{
+  ftp=NULL;
+  name=id;
+  sourceFn=NULL;
+  init();
+
+}
+
+ftpInterface::~ftpInterface()
+{
+  addToLog("FTP destroy in delete",LOGFTP);
+  destroy();
+  if(ftp) delete ftp;
+
+}
+
+void ftpInterface::init()
+{
+  if(ftp)
+    {
+      destroy();
+      delete ftp;
+    }
+  ftp = new QFtp( 0);
+  connect( ftp, SIGNAL(commandStarted(int)),SLOT(ftp_commandStarted(int)) );
+  connect( ftp, SIGNAL(commandFinished(int,bool)),SLOT(ftp_commandFinished(int,bool)) );
+  connect( ftp, SIGNAL(done(bool)),SLOT(ftp_done(bool)) );
+  connect( ftp, SIGNAL(stateChanged(int)),SLOT(ftp_stateChanged(int)) );
+  connect( ftp, SIGNAL(listInfo(const QUrlInfo &)),SLOT(ftp_listInfo(const QUrlInfo &)) );
+  connect( ftp, SIGNAL(rawCommandReply(int, const QString &)),SLOT(ftp_rawCommandReply(int, const QString &)) );
+  connect( ftp, SIGNAL(dataTransferProgress(qint64,qint64)),SLOT(slotProgress(qint64,qint64)) );
+  //    connect( progress, SIGNAL(canceled()), SLOT(slotAbort()) );
+}
+
+void ftpInterface::destroy()
+{
+  addToLog("FTP show state in destroy",LOGFTP);
+  ftp_stateChanged(ftp->state());
+  if ( ftp->state() != QFtp::Unconnected )
+    {
+
+      addToLog("FTP destroy",LOGFTP);
+      ftp->close();
+    }
+}
+
+eftpError ftpInterface::doConnect()
+{
+  ftpDone=true;
+  aborting=false;
+  addToLog(QString("FTP doConnect"),LOGFTP);
+  dumpState();
+  if(isUnconnected())
+    {
+      addToLog(QString("FTP connect to host %1").arg(host),LOGFTP);
+      ftpDone=false;
+      connectToHost();
+    }
+  else
+    {
+      addToLog(QString("FTP already connected to host %1").arg(host),LOGFTP);
+    }
+  tim.setSingleShot(true);
+  tim.start(FTPTIMEOUTTIME);
+  while(!ftpDone)
+    {
+      if(aborting)
+        {
+
+          return FTPCANCELED;
+        }
+      qApp->processEvents();
+      if(!tim.isActive())
+        {
+          slotAbort();
+          addToLog("ftp Timeout",LOGALL);
+          return FTPTIMEOUT;
+        }
+    }
+  tim.stop();
+  if(!ftpCommandSuccess)
+    {
+      addToLog("FTP not connected",LOGFTP);
+      return FTPERROR;
+    }
+  addToLog(QString("FTP connected to %1").arg(host),LOGFTP);
+  return FTPOK;
+}
+
+
+eftpError ftpInterface::uploadFile(QString fileName,QString targetFilename,bool reconnect)
+{
+  int id;
+  eftpError result;
+  addToLog("FTP uploadFile",LOGFTP);
+  if ( fileName.isNull() )  return FTPNAMEERROR;
+  if(reconnect)
+    {
+      result=doConnect();
+      if(result!=FTPOK) return result;
+    }
+  sourceFn=new QFile(fileName);
+  if ( !sourceFn->open( QIODevice::ReadOnly ) )
+    {
+      //		   QMessageBox::critical( 0, tr("Upload error"),
+      //                tr("Can't open file '%1' for reading.").arg(fileName) );
+      sourceFn=NULL;
+      return FTPNAMEERROR;
+    }
+  QFileInfo fi( fileName );
+  QFileInfo fin(targetFilename);
+  addToLog(QString("FTP bytes: %1").arg(sourceFn->size()),LOGFTP);
+
+  ftpDone=false;
+  if(fin.fileName().isEmpty())
+    {
+      id=ftp->put( sourceFn, fi.fileName(),QFtp::Binary);
+    }
+  else
+    {
+      id=ftp->put( sourceFn, fin.fileName(),QFtp::Binary);
+    }
+  addToLog(QString("FTP put file id: %1").arg(id),LOGFTP);
+  while(!ftpDone)
+    {
+      if(aborting) return FTPCANCELED;
+      qApp->processEvents();
+    }
+  if(!ftpCommandSuccess) return FTPERROR;
+  addToLog("FTP starting progress",LOGFTP);
+  return FTPOK;
+}
+
+
+eftpError ftpInterface::downloadFile(QString sourceFileName,QString destinationFilename)
+{
+  eftpError result;
+  addToLog("FTP downloadFile",LOGFTP);
+  QFile *destFn;
+  destFn=new QFile(destinationFilename);
+  if(!destFn->open(QIODevice::WriteOnly))
+    {
+      addToLog(QString("FTP unable to open destinationFilename %1").arg(destinationFilename),LOGFTP);
+      return FTPNAMEERROR;
+    }
+
+
+  if (sourceFileName.isNull() )  return FTPNAMEERROR;
+  result=doConnect();
+  if(result!=FTPOK) return result;
+  ftpDone=false;
+  ftp->get( sourceFileName, destFn,QFtp::Binary);
+  addToLog(QString("FTP get sourcefile %1 destination %2").arg(sourceFileName).arg(destFn->fileName()),LOGFTP);
+  while(!ftpDone)
+    {
+      if(aborting) return FTPCANCELED;
+      qApp->processEvents();
+    }
+  if(!ftpCommandSuccess) return FTPERROR;
+  addToLog(QString("FTP file: %1 bytes: %2").arg(destinationFilename).arg(QFile(destinationFilename).size()),LOGFTP);
+  return FTPOK;
+}
+
+
+void ftpInterface::connectToHost()
+{
+  destroy();
+  ftp->connectToHost(host,port);
+  ftp->login( user, passwd );
+  if(!directory.isEmpty()) changePath(directory);
+  //    qDebug() << QString("connecting to host %1,%2,%3,%4").arg(host).arg(port).arg(user).arg(passwd);
+}
+
+// This slot is connected to the QComboBox::activated() signal of the
+// remotePath.
+void ftpInterface::changePath( const QString &newPath )
+{
+  ftp->cd( newPath );
+}
+
+
+/****************************************************************************
+**
+** Slots connected to signals of the QFtp class
+**
+*****************************************************************************/
+
+void ftpInterface::ftp_commandStarted(int id)
+{
+  addToLog(QString("FTP commandStarted id:%1, %2").arg(id).arg(ftp->currentCommand()),LOGFTP);
+  if ( ftp->currentCommand() == QFtp::List )
+    {
+    }
+}
+
+void ftpInterface::ftp_commandFinished(int id,bool err)
+{
+  QIODevice *p;
+
+  slotProgress(0,0);
+
+  addToLog(QString("FTP commandFinished id:%1, error:%2").arg(id).arg(err),LOGFTP);
+  if(err)
+    {
+      addToLog(QString("FTP error:%1").arg(ftp->errorString()),LOGFTP);
+      ftpCommandSuccess=false;
+    }
+  else
+    {
+      ftpCommandSuccess=true;
+    }
+
+
+  p=ftp->currentDevice();
+  if(p)
+    {
+      delete ftp->currentDevice();
+    }
+
+}
+
+void ftpInterface::ftp_done( bool error )
+{
+
+  if ( error )
+    {
+
+      //        QMessageBox::critical( 0, tr("FTP Error"), ftp->errorString() );
+
+      // If we are connected, but not logged in, it is not meaningful to stay
+      // connected to the server since the error is a really fatal one (login
+      // failed).
+      if(!isLoggedIn())
+        {
+          addToLog("FTP done:  error and not logged in-> disconnecting",LOGFTP);
+          destroy();
+          ftpDone=true;
+          return;
+        }
+      addToLog("FTP done:  error",LOGFTP);
+    }
+  else
+    {
+      addToLog("FTP done:  no error",LOGFTP);
+    }
+  ftpDone=true;
+}
+
+bool ftpInterface::isLoggedIn()
+{
+  return ftp->state() == QFtp::LoggedIn;
+}
+
+bool ftpInterface::isUnconnected()
+{
+  return ftp->state() == QFtp::Unconnected;
+}
+
+void ftpInterface::ftp_stateChanged( int )
+{
+  dumpState();
+}
+
+void ftpInterface::dumpState()
+{
+  switch (ftp->state() )
+    {
+    case QFtp::Unconnected:
+      addToLog(QString("FTPss Unconnected name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    case QFtp::HostLookup:
+      addToLog(QString("FTPss Host lookup name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    case QFtp::Connecting:
+      addToLog(QString("FTPss Connecting name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    case QFtp::Connected:
+      addToLog(QString("FTPss Connected name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    case QFtp::LoggedIn:
+      addToLog(QString("FTPss Logged In name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    case QFtp::Closing:
+      addToLog(QString("FTPss Closing name:=%1 :host=%2").arg(name).arg(host),LOGFTP);
+    break;
+    default:
+      addToLog(QString("FTPss uknown %1 name:=%2 host=%3").arg(ftp->state()).arg(name).arg(host),LOGFTP);
+    break;
+    }
+}
+
+
+void ftpInterface::ftp_listInfo( const QUrlInfo &)
+{
+
+}
+
+void ftpInterface::ftp_rawCommandReply( int code, const QString &text )
+{
+  addToLog(QString("FTP Raw Command Reply: code=%1 , %2").arg(code).arg(text),LOGFTP);
+}
+
+
+
+void ftpInterface::slotAbort()
+{
+  aborting=true;
+  ftp->abort();
+}
+
+void ftpInterface::slotProgress(qint64 bytes ,qint64 total)
+{
+  displayProgressFTPEvent *stmb;
+  stmb=new displayProgressFTPEvent(bytes,total);
+  QApplication::postEvent( dispatcherPtr, stmb );  // Qt will delete it when done
+
+}
+
+eftpError ftpInterface::uploadToRXServer(QString fn)
+{
+  int i;
+  eftpError result;
+  addToLog("FTP show state",LOGFTP);
+  ftp_stateChanged(ftp->state());
+  result=doConnect();
+
+  if(result!=FTPOK) return result;
+  if(ftpSaveFormat==FTPIM)
+    {
+      ftpDone=false;
+      ftp->remove(QString("image%1").arg(ftpNumImages));
+      while(!ftpDone)
+        {
+          qApp->processEvents();
+        }
+
+      for(i=0;i<ftpNumImages-1;i++)
+        //        for(i=0;i<1;i++)
+        {
+          //          qDebug() << "ftp renaming" << i;
+          ftpDone=false;
+          ftp->rename(QString("image%1").arg(ftpNumImages-1-i),QString("image%1").arg(ftpNumImages-i));
+          while(!ftpDone)
+            {
+              qApp->processEvents();
+            }
+          addToLog("FTP done:  in rename",LOGFTP);
+        }
+      ftpDone=false;
+      return uploadFile(fn,"image1",false);
+    }
+  else
+    {
+      ftpDone=false;
+      return uploadFile(fn,"",false);
+    }
+}
+
+
diff --git a/qsstv/utils/ftp.h b/qsstv/utils/ftp.h
new file mode 100644
index 0000000..b4a96d3
--- /dev/null
+++ b/qsstv/utils/ftp.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Johan Maes                                      *
+ *   on4qz at telenet.be                                                      *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef FTPINTERFACE_H
+#define FTPINTERFACE_H
+
+#include "qglobal.h"
+
+# if(QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
+#include "qftp.h"
+# else
+#include <QFtp>
+#endif
+
+#include <QObject>
+#include <QFile>
+
+#include <QTimer>
+
+enum eftpError {FTPOK,FTPERROR,FTPNAMEERROR,FTPCANCELED,FTPTIMEOUT};
+class ftpInterface: public QObject
+{
+  Q_OBJECT
+public:
+  ftpInterface(QString id);
+  ~ftpInterface();
+  void setupConnection(QString tHost,int tPort,QString tUser,QString tPasswd,QString tDirectory)
+  {
+    host=tHost;
+    user=tUser;
+    passwd=tPasswd;
+    directory=tDirectory;
+    port=tPort;
+
+  }
+  eftpError uploadFile(QString fileName, QString fixFilename, bool reconnect);
+  eftpError downloadFile(QString sourceFileName,QString destinationFilename);
+  bool isUnconnected();
+  bool isLoggedIn();
+  eftpError uploadToRXServer(QString fn);
+  QString getLastError() {return ftp->errorString();}
+private slots:
+  void ftp_commandStarted(int);
+  void ftp_commandFinished(int,bool);
+  void ftp_done(bool);
+  void ftp_stateChanged(int);
+  void ftp_listInfo(const QUrlInfo &);
+  void ftp_rawCommandReply(int, const QString &);
+  void slotAbort();
+  void slotProgress(qint64 total, qint64 bytes);
+
+private:
+  void connectToHost();
+  void changePath( const QString &newPath );
+  eftpError doConnect();
+  void destroy();
+  void init();
+  QFtp *ftp;
+  QFile *sourceFn;
+  bool ftpDone;
+  bool aborting;
+  QString host;
+  QString user;
+  QString passwd;
+  QString directory;
+  int port;
+  bool ftpCommandSuccess;
+  QTimer tim;
+  void dumpState();
+  QString name;
+};
+
+extern ftpInterface *ftpIntf;
+
+#endif
diff --git a/qsstv/utils/hybridcrypt.cpp b/qsstv/utils/hybridcrypt.cpp
new file mode 100644
index 0000000..b393aa9
--- /dev/null
+++ b/qsstv/utils/hybridcrypt.cpp
@@ -0,0 +1,267 @@
+#include "hybridcrypt.h"
+#include <QDebug>
+#include "configparams.h"
+#include "QResource"
+
+
+
+hybridCrypt::hybridCrypt()
+{
+  const uchar *d;
+  QByteArray ba;
+  QResource qrc(":/icons/mgc.raw");
+  QResource qrc2(":/icons/mgc2.raw");
+  d=qrc.data();
+  key1=d[32]-0x36;
+  key2=d[33]-0x72;
+  key3=d[34]-0xd8;
+  key4=d[35]-0xFE;
+  ba.clear();
+  ba.append((const char *)qrc2.data());
+  deCrypt(&ba);
+  vhcFtpRemoteHost=hcFtpRemoteHost;
+  vhcFtpLogin=hcFtpLogin;
+  vhcFtpPassword=hcFtpPassword;
+  vhcFtpRemoteDirectory=hcFtpRemoteDirectory;
+  vhcFtpRemoteDirectory="";
+  vhcFtpPort=21; // at this moment no port can by specified
+  hcFtpPort=vhcFtpPort;
+  if((enableSpecialServer) && (!hybridFtpRemoteHost.isEmpty()))
+    {
+      hcFtpRemoteHost=hybridFtpRemoteHost;
+      hcFtpLogin=hybridFtpLogin;
+      hcFtpPassword=hybridFtpPassword;
+      hcFtpRemoteDirectory=hybridFtpRemoteDirectory; //always relatif to hybridFtpHybridFilesDirectory
+    }
+  else
+    {
+      hcFtpRemoteHost=vhcFtpRemoteHost;
+      hcFtpLogin=vhcFtpLogin;
+      hcFtpPassword=vhcFtpPassword;
+      hcFtpRemoteDirectory=vhcFtpRemoteDirectory;
+    }
+}
+
+
+
+bool hybridCrypt::enCrypt(QByteArray *ba)
+{
+  int i;
+  QString string,hstr;
+
+
+  hstr=QChar(63)+hcFtpRemoteHost+QChar(34)+hcFtpLogin+QChar(60)+hcFtpPassword+QChar(62)+hcFtpRemoteDirectory+QChar(58);
+
+  hcFtpRemoteHost.clear();
+  hcFtpLogin.clear();
+  hcFtpPassword.clear();
+  hcFtpRemoteDirectory.clear();
+  reverseString(hstr);
+
+  for(i=0;i<hstr.length();i++)
+    {
+
+      string.append(charToHex(hstr.at(i)));
+    }
+
+
+
+  //int strLen=string.length();
+  int sc=0;
+  short int bufI,  num1, num2, num3, num4, res1, res2, res3, res4;
+  unsigned char r1,r2;
+  QString encStr;
+  ba->clear();
+  while(string.length()%4!=0)
+    {
+      string.append(QChar(0));
+    }
+  do
+    {
+      num1=string.at(sc++).toLatin1();
+      num2=string.at(sc++).toLatin1();
+      num3=string.at(sc++).toLatin1();
+      num4=string.at(sc++).toLatin1();
+      res1=num1*key1;
+      bufI=num2*key3;
+      res1=res1+bufI;
+      res2= num1 * key2;
+      bufI= num2 * key4;
+      res2= res2 + bufI;
+      res3= num3 * key1;
+      bufI= num4 * key3;
+      res3= res3 + bufI;
+      res4= num3 * key2;
+      bufI= num4 * key4;
+      res4= res4 + bufI;
+      for(bufI=0;bufI<4;bufI++)
+        {
+          switch(bufI)
+            {
+            case 0: r1=res1>>8; r2=res1&0xff; break;
+            case 1: r1=res2>>8; r2=res2&0xff; break;
+            case 2: r1=res3>>8; r2=res3&0xff; break;
+            case 3: r1=res4>>8; r2=res4&0xff; break;
+            }
+          if((r1==0) &&(r2==0))
+            {
+              r1=0xFF;
+              r2=0xFF;
+            }
+          if(r1==0xFF) r1=0xFE;
+          if(r2==0)
+            {
+              r2=r1;
+              r1=0xFD;
+            }
+          encStr.append(r1);
+          encStr.append(r2);
+        }
+    }
+  while(sc<string.length());
+
+  ba->append(encStr);
+  ba->append("\r\n");
+  //  QFile tf("mgc2.raw");
+  //  if(!tf.open(QIODevice::WriteOnly))
+  //    {
+  //    return false;
+  //    }
+  //  tf.write(*ba);
+  //  tf.close();
+
+
+  return true;
+}
+
+
+bool hybridCrypt::deCrypt(QByteArray *ba)
+{
+  QString result;
+  int baSize;
+  bool ok;
+  int charCount=0;
+  QString tempStr="0x00";
+  short int bufI, bufI2, divzr, num1, num2, num3, num4, res1, res2, res3, res4;
+  unsigned char r1,r2;
+  result="";
+  res1=res2=res3=res4=0;
+  divzr=key1*key4;
+  bufI2=key3*key2;
+  divzr-=bufI2;
+  if(divzr==0) return false;
+  baSize=ba->size();
+  if(baSize!=3)
+    {
+      baSize=ba->size()-2; //drop /r/n
+      if(baSize%2!=0) return false;
+    }
+
+  if(baSize<20)
+    {
+      hcFtpRemoteHost=vhcFtpRemoteHost;
+      hcFtpLogin=vhcFtpLogin;
+      hcFtpPassword=vhcFtpPassword;
+      hcFtpRemoteDirectory=vhcFtpRemoteDirectory;
+      return true;
+    }
+  do
+    {
+      for(bufI=0;bufI<4;bufI++,charCount+=2)
+        {
+          r1=ba->at(charCount);
+          r2=ba->at(charCount+1);
+          if(r1==0xFF)
+            {
+              r1=r2=0;
+            }
+          if(r1==0xFE)
+            {
+              r1=0;
+            }
+          if(r1==0xFD)
+            {
+              r1=r2;
+              r2=0;
+            }
+          switch(bufI)
+            {
+            case 0: res1=r1*256+r2; break;
+            case 1: res2=r1*256+r2; break;
+            case 2: res3=r1*256+r2; break;
+            case 3: res4=r1*256+r2; break;
+            }
+        }
+
+      bufI= res1 * key4;
+      bufI2= res2 * key3;
+      num1= bufI - bufI2;
+      num1= num1 / divzr;
+      bufI= res2 * key1;
+      bufI2= res1 * key2;
+      num2= bufI - bufI2;
+      num2 = num2 / divzr;
+      bufI= res3 * key4;
+      bufI2= res4 * key3;
+      num3= bufI - bufI2;
+      num3= num3 / divzr;
+      bufI= res4 * key1;
+      bufI2= res3 * key2;
+      num4= bufI - bufI2;
+      num4= num4 / divzr;
+      tempStr[2]=QChar(num1);
+      tempStr[3]=QChar(num2);
+      result.append(QChar(tempStr.toInt(&ok,16)));
+      tempStr[2]=QChar(num3);
+      tempStr[3]=QChar(num4);
+      result.append(QChar(tempStr.toInt(&ok,16)));
+    }
+  while(charCount<baSize);
+  reverseString(result);
+  getParam(result);
+  return true;
+}
+
+
+void hybridCrypt::reverseString(QString & s)
+{
+  int i,j;
+  QString t;
+  for (i=0,j=s.length()-1;i<s.length();i++,j--)
+    {
+      t[j]=s[i];
+    }
+  s=t;
+}
+
+bool hybridCrypt::getParam(QString result)
+{
+  int a,b,c,d,e;
+  a=result.indexOf(QChar(63));
+  b=result.indexOf(QChar(34),a+1);
+  c=result.indexOf(QChar(60),b+1);
+  d=result.indexOf(QChar(62),c+1);
+  e=result.indexOf(QChar(58),d+1);
+  hcFtpRemoteHost=result.mid(a+1,b-a-1);
+  hcFtpLogin=result.mid(b+1,c-b-1);
+  hcFtpPassword=result.mid(c+1,d-c-1);
+  hcFtpRemoteDirectory=result.mid(d+1,e-d-1);
+  //  qDebug() <<  "host" << hcFtpRemoteHost;
+  //  qDebug() << "login" << hcFtpLogin;
+  //  qDebug() <<  "pwd" <<hcFtpPassword;
+  //  qDebug() <<  "dir" << hcFtpRemoteDirectory;
+  return true;
+}
+
+QString hybridCrypt::charToHex(QChar c)
+{
+  QString hb;
+  hb=QString::number(c.toLatin1(),16);
+  if(hb.length()<2) hb.prepend("0");
+  return hb;
+}
+
+
+
+
+
diff --git a/qsstv/utils/hybridcrypt.h b/qsstv/utils/hybridcrypt.h
new file mode 100644
index 0000000..e256ff6
--- /dev/null
+++ b/qsstv/utils/hybridcrypt.h
@@ -0,0 +1,35 @@
+#ifndef HYBRIDCRYPT_H
+#define HYBRIDCRYPT_H
+#include <QByteArray>
+#include <QString>
+
+class hybridCrypt
+{
+public:
+    hybridCrypt();
+    bool enCrypt(QByteArray *ba);
+    bool deCrypt(QByteArray *ba);
+    QString host()  {return hcFtpRemoteHost;}
+    QString user()  {return hcFtpLogin;}
+    QString passwd(){return hcFtpPassword;}
+    QString dir()   {return hcFtpRemoteDirectory;}
+    int port() {return hcFtpPort;}
+private:
+    QString hcFtpRemoteHost;
+    QString hcFtpLogin;
+    QString hcFtpPassword;
+    QString hcFtpRemoteDirectory;
+    int hcFtpPort;
+    QString vhcFtpRemoteHost;
+    QString vhcFtpLogin;
+    QString vhcFtpPassword;
+    QString vhcFtpRemoteDirectory;
+    int vhcFtpPort;
+
+    short int key1,key2,key3,key4;
+    void reverseString(QString & s);
+    bool getParam(QString result);
+    QString charToHex(QChar c);
+};
+
+#endif // HYBRIDCRYPT_H
diff --git a/qsstv/utils/logging.cpp b/qsstv/utils/logging.cpp
new file mode 100644
index 0000000..831aedd
--- /dev/null
+++ b/qsstv/utils/logging.cpp
@@ -0,0 +1,281 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#include "logging.h"
+
+#include <QDebug>
+#include <QTextStream>
+#include <QFileInfo>
+#include <QStringList>
+#include <QDir>
+#include "ui_loggingform.h"
+#include "qsstvdefs.h"
+
+
+
+
+
+
+
+/*! class logFile
+  \brief utility class to enable logging facilities
+
+  Create an instance of this class giving the basename of the logfile.
+  By default the log is disabled. call setEnabled(true) to enable logging
+
+
+*/
+//logFile logfile;
+
+
+logFile::logFile()
+{
+#ifndef QT_NO_DEBUG
+  lf=new QFile;
+  auxFile=new QFile;
+#endif
+  logCount=0;
+  savedLogEntry="";
+  savedPosMask=0;
+  mask.set(); //all masks set:this will enable all logfile messages
+}
+
+/*!
+  creates logfile with name=logname, and opens it for writing
+*/
+
+#ifndef QT_NO_DEBUG
+bool logFile::open(QString logname)
+{
+
+  lf->setFileName(QDir::homePath()+"/"+logname);
+  auxFile->setFileName(QDir::homePath()+"/aux_"+logname);
+  return reopen();
+
+}
+#else
+bool logFile::open(QString ) { return true;}
+#endif
+
+
+/*!
+  closes the logfile
+*/
+
+logFile::~logFile()
+{
+ close();
+}
+
+void logFile::close()
+{
+#ifndef QT_NO_DEBUG
+ qDebug() << "closing logfile";
+ add("End of logfile",LOGALL);
+ add("....,",LOGALL);
+ delete ts;
+ delete auxTs;
+ lf->close();
+ auxFile->close();
+#endif
+}
+
+void logFile::reset()
+{
+  close();
+  reopen();
+}
+
+bool logFile::reopen()
+{
+#ifndef QT_NO_DEBUG
+  setEnabled(false);
+  QFileInfo finf(*lf);
+  QFileInfo finfaux(*auxFile);
+  qDebug() << "opening logfile--: " << finf.absoluteFilePath();
+  if(!lf->open(QIODevice::WriteOnly))
+    {
+      qDebug() << "logfile creation failed";
+      return false;
+    }
+  qDebug() << "opening logfile: " << finfaux.absoluteFilePath();
+  if(!auxFile->open(QIODevice::WriteOnly))
+    {
+      qDebug() << "auxillary file creation failed";
+      lf->close();
+      return false;
+    }
+  setEnabled(true);
+  ts= new QTextStream( lf );
+  auxTs= new QTextStream( auxFile);
+  savedLogEntry="";
+  logCount=0;
+  timer.start();
+  *ts<< "Time \tElapsed  \t  Level  \t  Count\t          Info\n";
+  ts->flush();
+#endif
+  return true;
+}
+
+/*!
+  \brief Writes to the logfile
+
+  The output is flushed after every access.Identical messages are only logged once. The count indicates the number of duplicate messages.
+*/
+
+#ifndef QT_NO_DEBUG
+void logFile::add(QString t,short unsigned int posMask)
+{
+  if(!(posMask==LOGALL)) // always show messages with DBALL
+    {
+      if (!mask.test(posMask)) return;
+    }
+  if (!enabled) return;
+  mutex.lock();
+  if(logCount==0)
+    {
+      logCount=1;
+      savedLogEntry=t;
+      timer.restart();
+      tmp=QString("%1 ").arg(timer.elapsed(),5);
+      tmp2=timer.currentTime().toString("HH:mm:ss:zzz ");
+      savedPosMask=posMask;
+    }
+  if ((t==savedLogEntry) &&(deduplicate)) logCount++;
+  else
+    {
+      if(!deduplicate)
+        {
+          savedLogEntry=t;
+          tmp=QString("%1 ").arg(timer.elapsed(),5);
+          tmp2 = timer.currentTime().toString("HH:mm:ss:zzz ");
+          savedPosMask=posMask;
+        }
+      if(savedPosMask==LOGALL)
+        {
+          *ts << tmp2<< "\t" << tmp << "\tALL     \t" << logCount << "\t" << savedLogEntry <<"\n";
+        }
+      else
+        {
+          *ts << tmp2<< "\t" << tmp << "\t" << levelStr[savedPosMask] <<"\t" << logCount << "\t" << savedLogEntry <<"\n";
+        }
+      tmp=QString("%1 ").arg(timer.elapsed(),5);
+      tmp2 = timer.currentTime().toString("HH:mm:ss:zzz ");
+      timer.restart();;
+      savedLogEntry=t;
+      savedPosMask=posMask;
+      logCount=1;
+    }
+  ts->flush();
+  lf->flush();
+  mutex.unlock();
+}
+#else
+void logFile::add(QString ,short unsigned int) {}
+#endif
+
+void logFile::add(const char *fileName,const char *functionName, int line, QString t,short unsigned int posMask)
+{
+  QString s;
+  s=QString(fileName)+":"+QString(functionName)+":"+QString::number(line)+" "+ t;
+  add(s,posMask);
+}
+
+#ifndef QT_NO_DEBUG
+void logFile::addToAux(QString t)
+{
+  if (!enabled) return;
+  mutex.lock();
+  *auxTs << t << "\n";
+  auxTs->flush();
+  auxFile->flush();
+  mutex.unlock();
+}
+#else
+void logFile::addToAux(QString ){}
+#endif
+/*!
+  if enable=true logging wil be performed
+  \return previous logging state (true if logging was enabled)
+*/
+
+bool logFile::setEnabled(bool enable)
+{
+  bool t=enabled;
+  enabled=enable;
+  return t;
+}
+
+void logFile::setLogMask(std::bitset<NUMDEBUGLEVELS> logMask)
+{
+  mask=logMask;
+}
+
+void logFile::maskSelect(QWidget *wPtr)
+{
+  int i,j;
+   QDialog lf(wPtr);
+   QCheckBox *cb;
+ //  QTableWidgetItem *item;
+   Ui::loggingForm ui;
+   ui.setupUi(&lf);
+   ui.maskTableWidget->setRowCount((NUMDEBUGLEVELS+1)/2);
+   for(i=0;i<ui.maskTableWidget->rowCount();i++)
+     {
+       for(j=0;(j<2)&(i*2+j<NUMDEBUGLEVELS);j++)
+       {
+         cb=new QCheckBox(levelStr[i*2+j]);
+         cb->setChecked(mask.test(i*2+j));
+         ui.maskTableWidget->setCellWidget(i,j,cb);
+       }
+     }
+   ui.deduplicateCheckBox->setChecked(deduplicate);
+   if(lf.exec()==QDialog::Accepted)
+     {
+       for(i=0;i<ui.maskTableWidget->rowCount();i++)
+         {
+           for(j=0;(j<2)&(i*2+j<NUMDEBUGLEVELS);j++)
+           {
+             cb=(QCheckBox *)ui.maskTableWidget->cellWidget(i,j);
+             mask.set(i*2+j,cb->isChecked());
+           }
+         }
+       deduplicate=ui.deduplicateCheckBox->isChecked();
+      }
+}
+
+void logFile::readSettings(QSettings &qSettings)
+{
+  qSettings.beginGroup ("logging");
+  mask=qSettings.value("mask",1).toUInt();
+  deduplicate=qSettings.value("deduplicate",true).toBool();
+  qSettings.endGroup();
+}
+
+void logFile::writeSettings(QSettings &qSettings)
+{
+  qSettings.beginGroup ("logging");
+  qSettings.setValue ( "mask", (uint)mask.to_ulong());
+  qSettings.setValue ( "deduplicate", deduplicate);
+  qSettings.endGroup();
+}
+
+
diff --git a/qsstv/utils/logging.h b/qsstv/utils/logging.h
new file mode 100644
index 0000000..e5ab8d7
--- /dev/null
+++ b/qsstv/utils/logging.h
@@ -0,0 +1,63 @@
+
+
+#ifndef LOGGING_H
+#define LOGGING_H
+#include <QString>
+#include <QFile>
+#include <QMutex>
+#include <QTime>
+#include <bitset>
+#include <QSettings>
+#include "loggingparams.h"
+
+#ifndef QT_NO_DEBUG
+#define addToLog(x,y) logfile->add(__FILE__,__func__,__LINE__,x,y)
+#else
+#define addToLog(x,y) logfile->dummyAdd(x,y) // to avoid parameter not used warnings
+#endif
+
+class QTextStream;
+
+class logFile
+{
+  public:
+    logFile();
+    logFile(QString logname);
+    ~logFile();
+    bool open(QString logname);
+    void add(QString t,short unsigned int posMask);
+    void add(const char *fileName,const char *functionName, int line, QString t,short unsigned int posMask);
+    void dummyAdd(QString,int) {}
+    void addToAux(QString t);
+    bool setEnabled(bool e);
+    void setLogMask(std::bitset<NUMDEBUGLEVELS> logMask);
+    void maskSelect(QWidget *wPtr=0);
+    void readSettings(QSettings &qSettings);
+    void writeSettings(QSettings &qSettings);
+    void close();
+    void reset();
+    bool reopen();
+
+  private:
+    QString tmp;
+    QString tmp2;
+    QFile *lf;
+    QTextStream *ts;
+    QFile *auxFile;
+    QTextStream *auxTs;
+    bool enabled;
+    QMutex mutex;
+    QTime timer;
+    std::bitset <NUMDEBUGLEVELS> mask;
+    QString savedLogEntry;
+    int logCount;
+    int savedPosMask;
+    bool deduplicate;
+};
+
+
+
+
+
+
+#endif
diff --git a/qsstv/utils/loggingform.ui b/qsstv/utils/loggingform.ui
new file mode 100644
index 0000000..bcf5904
--- /dev/null
+++ b/qsstv/utils/loggingform.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>loggingForm</class>
+ <widget class="QDialog" name="loggingForm">
+  <property name="windowModality">
+   <enum>Qt::WindowModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>449</width>
+    <height>459</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Logging Selector</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTableWidget" name="maskTableWidget">
+     <property name="rowCount">
+      <number>10</number>
+     </property>
+     <property name="columnCount">
+      <number>2</number>
+     </property>
+     <attribute name="horizontalHeaderVisible">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="horizontalHeaderDefaultSectionSize">
+      <number>200</number>
+     </attribute>
+     <attribute name="horizontalHeaderHighlightSections">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="horizontalHeaderMinimumSectionSize">
+      <number>200</number>
+     </attribute>
+     <attribute name="verticalHeaderVisible">
+      <bool>false</bool>
+     </attribute>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <row/>
+     <column/>
+     <column/>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="deduplicateCheckBox">
+     <property name="text">
+      <string>Deduplicate</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>loggingForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>loggingForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/utils/loggingparams.cpp b/qsstv/utils/loggingparams.cpp
new file mode 100644
index 0000000..67c7914
--- /dev/null
+++ b/qsstv/utils/loggingparams.cpp
@@ -0,0 +1,56 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+#include "loggingparams.h"
+
+QString levelStr[NUMDEBUGLEVELS]=
+{
+  "GALLERY ",
+  "PARAMS  ",
+  "FFT     ",
+  "WAVIO   ",
+  "SYNTHES ",
+  "DISPATC ",
+  "SOUNDIO ",
+  "RXFUNC  ",
+  "TXFUNC  ",
+  "SYNCPRC1",
+  "SYNCPRC2",
+  "SLANTAD ",
+  "MODES   ",
+  "FTP     ",
+  "IMAGE   ",
+  "SCOPE   ",
+  "CAM     ",
+  "RX MAIN ",
+  "TX MAIN ",
+  "EDITOR  ",
+  "RIGCTRL ",
+  "DRMDEMOD",
+  "DRMRXSRC",
+  "DRMMOT  ",
+  "DRMTX   ",
+  "DRMTXMOT",
+  "DRMTXCOD",
+  "DRMTXAAA",
+  "XMLRPC  ",
+  "PERFORM "
+};
+
diff --git a/qsstv/utils/loggingparams.h b/qsstv/utils/loggingparams.h
new file mode 100644
index 0000000..93541ca
--- /dev/null
+++ b/qsstv/utils/loggingparams.h
@@ -0,0 +1,40 @@
+#ifndef LOGGINGPARAMS_H
+#define LOGGINGPARAMS_H
+#include <QString>
+
+//debuglevels
+#define LOGALL       99
+#define LOGGALLERY   0
+#define LOGPARAM     1
+#define LOGFFT       2
+#define LOGWAVIO     3
+#define LOGSYNTHES   4
+#define LOGDISPAT    5
+#define LOGSOUND     6
+#define LOGRXFUNC    7
+#define LOGTXFUNC    8
+#define LOGSYNC1     9
+#define LOGSYNC2     10
+#define LOGSLANT     11
+#define LOGMODES     12
+#define LOGFTP       13
+#define LOGIMAG      14
+#define LOGSCOPE     15
+#define LOGCAM       16
+#define LOGRXMAIN    17
+#define LOGTXMAIN    18
+#define LOGEDIT      19
+#define LOGRIGCTRL   20
+#define LOGDRMDEMOD  21
+#define LOGDRMSRC    22
+#define LOGDRMMOT    23
+#define LOGDRMTX     24
+#define LOGDRMTXMOT  25
+#define LOGDRMTXCOD  26
+#define LOGDRMTXAAA  27
+#define LOGXML       28
+#define LOGPERFORM   29
+#define NUMDEBUGLEVELS (LOGPERFORM+1)
+
+extern QString levelStr[NUMDEBUGLEVELS];
+#endif // LOGGINGPARAMS_H
diff --git a/qsstv/utils/macroexpansion.cpp b/qsstv/utils/macroexpansion.cpp
new file mode 100644
index 0000000..1a9c2db
--- /dev/null
+++ b/qsstv/utils/macroexpansion.cpp
@@ -0,0 +1,55 @@
+#include "macroexpansion.h"
+
+
+macroExpansion::macroExpansion()
+{
+  convertList.clear();
+}
+
+
+
+QString macroExpansion::convert(QString txt)
+{
+  int i,j;
+  bool special=false;
+  QChar c;
+  QString convertedText;
+  {
+    for (i=0;i<txt.length();i++)
+    {
+      if (special)
+        {
+          special=false;
+          c=txt.at(i);
+          if(c=='%')
+          {
+            convertedText.append('%');
+            continue;
+          }
+          for (j=0;j<convertList.count();j++)
+            {
+              if(c==convertList.at(j).tag)
+              {
+                convertedText.append(convertList.at(j).replacement);
+              }
+            }
+        }
+      else
+        {
+          if(txt.at(i)!='%') convertedText.append(txt.at(i));
+          else special=true;
+        }
+    }
+
+  }
+  return convertedText;
+}
+
+
+void macroExpansion::addConversion(QChar tag,QString value)
+{
+  sconvert cnv;
+  cnv.tag=tag;
+  cnv.replacement=value;
+  convertList.append(cnv);
+}
diff --git a/qsstv/utils/macroexpansion.h b/qsstv/utils/macroexpansion.h
new file mode 100644
index 0000000..45a5c4a
--- /dev/null
+++ b/qsstv/utils/macroexpansion.h
@@ -0,0 +1,23 @@
+#ifndef MACROEXPANSION_H
+#define MACROEXPANSION_H
+#include <QString>
+#include <QList>
+
+
+struct sconvert
+{
+  QChar tag;
+  QString replacement;
+};
+
+class macroExpansion
+{
+public:
+  macroExpansion();
+  QString convert(QString txt);
+  void addConversion(QChar tag,QString value);
+  void clear() {convertList.clear();}
+private:
+  QList<sconvert> convertList;
+};
+#endif // MACROEXPANSION_H
diff --git a/qsstv/utils/qftp.cpp b/qsstv/utils/qftp.cpp
new file mode 100644
index 0000000..f3fd58c
--- /dev/null
+++ b/qsstv/utils/qftp.cpp
@@ -0,0 +1,2403 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+#include "qftp.h"
+#include "qabstractsocket.h"
+
+#ifndef QT_NO_FTP
+
+#include "qcoreapplication.h"
+#include "qtcpsocket.h"
+#include "qurlinfo.h"
+#include "qstringlist.h"
+#include "qregexp.h"
+#include "qtimer.h"
+#include "qfileinfo.h"
+#include "qhash.h"
+#include "qtcpserver.h"
+#include "qlocale.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFtpPI;
+
+/*
+    The QFtpDTP (DTP = Data Transfer Process) controls all client side
+    data transfer between the client and server.
+*/
+class QFtpDTP : public QObject
+{
+    Q_OBJECT
+
+public:
+    enum ConnectState {
+        CsHostFound,
+        CsConnected,
+        CsClosed,
+        CsHostNotFound,
+        CsConnectionRefused
+    };
+
+    QFtpDTP(QFtpPI *p, QObject *parent = 0);
+
+    void setData(QByteArray *);
+    void setDevice(QIODevice *);
+    void writeData();
+    void setBytesTotal(qint64 bytes);
+
+    bool hasError() const;
+    QString errorMessage() const;
+    void clearError();
+
+    void connectToHost(const QString & host, quint16 port);
+    int setupListener(const QHostAddress &address);
+    void waitForConnection();
+
+    QTcpSocket::SocketState state() const;
+    qint64 bytesAvailable() const;
+    qint64 read(char *data, qint64 maxlen);
+    QByteArray readAll();
+
+    void abortConnection();
+
+    static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);
+
+signals:
+    void listInfo(const QUrlInfo&);
+    void readyRead();
+    void dataTransferProgress(qint64, qint64);
+
+    void connectState(int);
+
+private slots:
+    void socketConnected();
+    void socketReadyRead();
+    void socketError(QAbstractSocket::SocketError);
+    void socketConnectionClosed();
+    void socketBytesWritten(qint64);
+    void setupSocket();
+
+    void dataReadyRead();
+
+private:
+    void clearData();
+
+    QTcpSocket *socket;
+    QTcpServer listener;
+
+    QFtpPI *pi;
+    QString err;
+    qint64 bytesDone;
+    qint64 bytesTotal;
+    bool callWriteData;
+
+    // If is_ba is true, ba is used; ba is never 0.
+    // Otherwise dev is used; dev can be 0 or not.
+    union {
+        QByteArray *ba;
+        QIODevice *dev;
+    } data;
+    bool is_ba;
+
+    QByteArray bytesFromSocket;
+};
+
+/**********************************************************************
+ *
+ * QFtpPI - Protocol Interpreter
+ *
+ *********************************************************************/
+
+class QFtpPI : public QObject
+{
+    Q_OBJECT
+
+public:
+    QFtpPI(QObject *parent = 0);
+
+    void connectToHost(const QString &host, quint16 port);
+
+    bool sendCommands(const QStringList &cmds);
+    bool sendCommand(const QString &cmd)
+        { return sendCommands(QStringList(cmd)); }
+
+    void clearPendingCommands();
+    void abort();
+
+    QString currentCommand() const
+        { return currentCmd; }
+
+    bool rawCommand;
+    bool transferConnectionExtended;
+
+    QFtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
+                 // makes the design simpler this way
+signals:
+    void connectState(int);
+    void finished(const QString&);
+    void error(int, const QString&);
+    void rawFtpReply(int, const QString&);
+
+private slots:
+    void hostFound();
+    void connected();
+    void connectionClosed();
+    void delayedCloseFinished();
+    void readyRead();
+    void error(QAbstractSocket::SocketError);
+
+    void dtpConnectState(int);
+
+private:
+    // the states are modelled after the generalized state diagram of RFC 959,
+    // page 58
+    enum State {
+        Begin,
+        Idle,
+        Waiting,
+        Success,
+        Failure
+    };
+
+    enum AbortState {
+        None,
+        AbortStarted,
+        WaitForAbortToFinish
+    };
+
+    bool processReply();
+    bool startNextCmd();
+
+    QTcpSocket commandSocket;
+    QString replyText;
+    char replyCode[3];
+    State state;
+    AbortState abortState;
+    QStringList pendingCommands;
+    QString currentCmd;
+
+    bool waitForDtpToConnect;
+    bool waitForDtpToClose;
+
+    QByteArray bytesFromSocket;
+
+    friend class QFtpDTP;
+};
+
+/**********************************************************************
+ *
+ * QFtpCommand implemenatation
+ *
+ *********************************************************************/
+class QFtpCommand
+{
+public:
+    QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba);
+    QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev = 0);
+    ~QFtpCommand();
+
+    int id;
+    QFtp::Command command;
+    QStringList rawCmds;
+
+    // If is_ba is true, ba is used; ba is never 0.
+    // Otherwise dev is used; dev can be 0 or not.
+    union {
+        QByteArray *ba;
+        QIODevice *dev;
+    } data;
+    bool is_ba;
+
+    static QBasicAtomicInt idCounter;
+};
+
+QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba)
+    : command(cmd), rawCmds(raw), is_ba(true)
+{
+    id = idCounter.fetchAndAddRelaxed(1);
+    data.ba = new QByteArray(ba);
+}
+
+QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev)
+    : command(cmd), rawCmds(raw), is_ba(false)
+{
+    id = idCounter.fetchAndAddRelaxed(1);
+    data.dev = dev;
+}
+
+QFtpCommand::~QFtpCommand()
+{
+    if (is_ba)
+        delete data.ba;
+}
+
+/**********************************************************************
+ *
+ * QFtpDTP implemenatation
+ *
+ *********************************************************************/
+QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) :
+    QObject(parent),
+    socket(0),
+    listener(this),
+    pi(p),
+    callWriteData(false)
+{
+    clearData();
+    listener.setObjectName(QLatin1String("QFtpDTP active state server"));
+    connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket()));
+}
+
+void QFtpDTP::setData(QByteArray *ba)
+{
+    is_ba = true;
+    data.ba = ba;
+}
+
+void QFtpDTP::setDevice(QIODevice *dev)
+{
+    is_ba = false;
+    data.dev = dev;
+}
+
+void QFtpDTP::setBytesTotal(qint64 bytes)
+{
+    bytesTotal = bytes;
+    bytesDone = 0;
+    emit dataTransferProgress(bytesDone, bytesTotal);
+}
+
+void QFtpDTP::connectToHost(const QString & host, quint16 port)
+{
+    bytesFromSocket.clear();
+
+    if (socket) {
+        delete socket;
+        socket = 0;
+    }
+    socket = new QTcpSocket(this);
+#ifndef QT_NO_BEARERMANAGEMENT
+    //copy network session down to the socket
+    socket->setProperty("_q_networksession", property("_q_networksession"));
+#endif
+    socket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));
+    connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
+    connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
+    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+    connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
+    connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+    socket->connectToHost(host, port);
+}
+
+int QFtpDTP::setupListener(const QHostAddress &address)
+{
+#ifndef QT_NO_BEARERMANAGEMENT
+    //copy network session down to the socket
+    listener.setProperty("_q_networksession", property("_q_networksession"));
+#endif
+    if (!listener.isListening() && !listener.listen(address, 0))
+        return -1;
+    return listener.serverPort();
+}
+
+void QFtpDTP::waitForConnection()
+{
+    // This function is only interesting in Active transfer mode; it works
+    // around a limitation in QFtp's design by blocking, waiting for an
+    // incoming connection. For the default Passive mode, it does nothing.
+    if (listener.isListening())
+        listener.waitForNewConnection();
+}
+
+QTcpSocket::SocketState QFtpDTP::state() const
+{
+    return socket ? socket->state() : QTcpSocket::UnconnectedState;
+}
+
+qint64 QFtpDTP::bytesAvailable() const
+{
+    if (!socket || socket->state() != QTcpSocket::ConnectedState)
+        return (qint64) bytesFromSocket.size();
+    return socket->bytesAvailable();
+}
+
+qint64 QFtpDTP::read(char *data, qint64 maxlen)
+{
+    qint64 read;
+    if (socket && socket->state() == QTcpSocket::ConnectedState) {
+        read = socket->read(data, maxlen);
+    } else {
+        read = qMin(maxlen, qint64(bytesFromSocket.size()));
+        memcpy(data, bytesFromSocket.data(), read);
+        bytesFromSocket.remove(0, read);
+    }
+
+    bytesDone += read;
+    return read;
+}
+
+QByteArray QFtpDTP::readAll()
+{
+    QByteArray tmp;
+    if (socket && socket->state() == QTcpSocket::ConnectedState) {
+        tmp = socket->readAll();
+        bytesDone += tmp.size();
+    } else {
+        tmp = bytesFromSocket;
+        bytesFromSocket.clear();
+    }
+    return tmp;
+}
+
+void QFtpDTP::writeData()
+{
+    if (!socket)
+        return;
+
+    if (is_ba) {
+#if defined(QFTPDTP_DEBUG)
+        qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size());
+#endif
+        if (data.ba->size() == 0)
+            emit dataTransferProgress(0, bytesTotal);
+        else
+            socket->write(data.ba->data(), data.ba->size());
+
+        socket->close();
+
+        clearData();
+    } else if (data.dev) {
+        callWriteData = false;
+        const qint64 blockSize = 16*1024;
+        char buf[16*1024];
+        qint64 read = data.dev->read(buf, blockSize);
+#if defined(QFTPDTP_DEBUG)
+        qDebug("QFtpDTP::writeData: write() of size %lli bytes", read);
+#endif
+        if (read > 0) {
+            socket->write(buf, read);
+        } else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {
+            // error or EOF
+            if (bytesDone == 0 && socket->bytesToWrite() == 0)
+                emit dataTransferProgress(0, bytesTotal);
+            socket->close();
+            clearData();
+        }
+
+        // do we continue uploading?
+        callWriteData = data.dev != 0;
+    }
+}
+
+void QFtpDTP::dataReadyRead()
+{
+    writeData();
+}
+
+inline bool QFtpDTP::hasError() const
+{
+    return !err.isNull();
+}
+
+inline QString QFtpDTP::errorMessage() const
+{
+    return err;
+}
+
+inline void QFtpDTP::clearError()
+{
+    err.clear();
+}
+
+void QFtpDTP::abortConnection()
+{
+#if defined(QFTPDTP_DEBUG)
+    qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",
+           socket ? socket->bytesAvailable() : (qint64) 0);
+#endif
+    callWriteData = false;
+    clearData();
+
+    if (socket)
+        socket->abort();
+}
+
+static void _q_fixupDateTime(QDateTime *dateTime)
+{
+    // Adjust for future tolerance.
+    const int futureTolerance = 86400;
+    if (dateTime->secsTo(QDateTime::currentDateTime()) < -futureTolerance) {
+        QDate d = dateTime->date();
+        d.setDate(d.year() - 1, d.month(), d.day());
+        dateTime->setDate(d);
+    }
+}
+
+static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
+{
+    // Unix style, 7 + 1 entries
+    // -rw-r--r--    1 ftp      ftp      17358091 Aug 10  2004 qt-x11-free-3.3.3.tar.gz
+    // drwxr-xr-x    3 ftp      ftp          4096 Apr 14  2000 compiled-examples
+    // lrwxrwxrwx    1 ftp      ftp             9 Oct 29  2005 qtscape -> qtmozilla
+    if (tokens.size() != 8)
+        return;
+
+    char first = tokens.at(1).at(0).toLatin1();
+    if (first == 'd') {
+        info->setDir(true);
+        info->setFile(false);
+        info->setSymLink(false);
+    } else if (first == '-') {
+        info->setDir(false);
+        info->setFile(true);
+        info->setSymLink(false);
+    } else if (first == 'l') {
+        info->setDir(true);
+        info->setFile(false);
+        info->setSymLink(true);
+    }
+
+    // Resolve filename
+    QString name = tokens.at(7);
+    if (info->isSymLink()) {
+        int linkPos = name.indexOf(QLatin1String(" ->"));
+        if (linkPos != -1)
+            name.resize(linkPos);
+    }
+    info->setName(name);
+
+    // Resolve owner & group
+    info->setOwner(tokens.at(3));
+    info->setGroup(tokens.at(4));
+
+    // Resolve size
+    info->setSize(tokens.at(5).toLongLong());
+
+    QStringList formats;
+    formats << QLatin1String("MMM dd  yyyy") << QLatin1String("MMM dd hh:mm") << QLatin1String("MMM  d  yyyy")
+            << QLatin1String("MMM  d hh:mm") << QLatin1String("MMM  d yyyy") << QLatin1String("MMM dd yyyy");
+
+    QString dateString = tokens.at(6);
+    dateString[0] = dateString[0].toUpper();
+
+    // Resolve the modification date by parsing all possible formats
+    QDateTime dateTime;
+    int n = 0;
+#ifndef QT_NO_DATESTRING
+    do {
+        dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));
+    }  while (n < formats.size() && (!dateTime.isValid()));
+#endif
+
+    if (n == 2 || n == 4) {
+        // Guess the year.
+        dateTime.setDate(QDate(QDate::currentDate().year(),
+                               dateTime.date().month(),
+                               dateTime.date().day()));
+        _q_fixupDateTime(&dateTime);
+    }
+    if (dateTime.isValid())
+        info->setLastModified(dateTime);
+
+    // Resolve permissions
+    int permissions = 0;
+    QString p = tokens.at(2);
+    permissions |= (p[0] == QLatin1Char('r') ? QUrlInfo::ReadOwner : 0);
+    permissions |= (p[1] == QLatin1Char('w') ? QUrlInfo::WriteOwner : 0);
+    permissions |= (p[2] == QLatin1Char('x') ? QUrlInfo::ExeOwner : 0);
+    permissions |= (p[3] == QLatin1Char('r') ? QUrlInfo::ReadGroup : 0);
+    permissions |= (p[4] == QLatin1Char('w') ? QUrlInfo::WriteGroup : 0);
+    permissions |= (p[5] == QLatin1Char('x') ? QUrlInfo::ExeGroup : 0);
+    permissions |= (p[6] == QLatin1Char('r') ? QUrlInfo::ReadOther : 0);
+    permissions |= (p[7] == QLatin1Char('w') ? QUrlInfo::WriteOther : 0);
+    permissions |= (p[8] == QLatin1Char('x') ? QUrlInfo::ExeOther : 0);
+    info->setPermissions(permissions);
+
+    bool isOwner = info->owner() == userName;
+    info->setReadable((permissions & QUrlInfo::ReadOther) || ((permissions & QUrlInfo::ReadOwner) && isOwner));
+    info->setWritable((permissions & QUrlInfo::WriteOther) || ((permissions & QUrlInfo::WriteOwner) && isOwner));
+}
+
+static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
+{
+    // DOS style, 3 + 1 entries
+    // 01-16-02  11:14AM       <DIR>          epsgroup
+    // 06-05-03  03:19PM                 1973 readme.txt
+    if (tokens.size() != 4)
+        return;
+
+    Q_UNUSED(userName);
+
+    QString name = tokens.at(3);
+    info->setName(name);
+    info->setSymLink(name.toLower().endsWith(QLatin1String(".lnk")));
+
+    if (tokens.at(2) == QLatin1String("<DIR>")) {
+        info->setFile(false);
+        info->setDir(true);
+    } else {
+        info->setFile(true);
+        info->setDir(false);
+        info->setSize(tokens.at(2).toLongLong());
+    }
+
+    // Note: We cannot use QFileInfo; permissions are for the server-side
+    // machine, and QFileInfo's behavior depends on the local platform.
+    int permissions = QUrlInfo::ReadOwner | QUrlInfo::WriteOwner
+                      | QUrlInfo::ReadGroup | QUrlInfo::WriteGroup
+                      | QUrlInfo::ReadOther | QUrlInfo::WriteOther;
+    QString ext;
+    int extIndex = name.lastIndexOf(QLatin1Char('.'));
+    if (extIndex != -1)
+        ext = name.mid(extIndex + 1);
+    if (ext == QLatin1String("exe") || ext == QLatin1String("bat") || ext == QLatin1String("com"))
+        permissions |= QUrlInfo::ExeOwner | QUrlInfo::ExeGroup | QUrlInfo::ExeOther;
+    info->setPermissions(permissions);
+
+    info->setReadable(true);
+    info->setWritable(info->isFile());
+
+    QDateTime dateTime;
+#ifndef QT_NO_DATESTRING
+    dateTime = QLocale::c().toDateTime(tokens.at(1), QLatin1String("MM-dd-yy  hh:mmAP"));
+    if (dateTime.date().year() < 1971) {
+        dateTime.setDate(QDate(dateTime.date().year() + 100,
+                               dateTime.date().month(),
+                               dateTime.date().day()));
+    }
+#endif
+
+    info->setLastModified(dateTime);
+
+}
+
+bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info)
+{
+    if (buffer.isEmpty())
+        return false;
+
+    QString bufferStr = QString::fromLatin1(buffer).trimmed();
+
+    // Unix style FTP servers
+    QRegExp unixPattern(QLatin1String("^([\\-dl])([a-zA-Z\\-]{9,9})\\s+\\d+\\s+(\\S*)\\s+"
+                                      "(\\S*)\\s+(\\d+)\\s+(\\S+\\s+\\S+\\s+\\S+)\\s+(\\S.*)"));
+    if (unixPattern.indexIn(bufferStr) == 0) {
+        _q_parseUnixDir(unixPattern.capturedTexts(), userName, info);
+        return true;
+    }
+
+    // DOS style FTP servers
+    QRegExp dosPattern(QLatin1String("^(\\d\\d-\\d\\d-\\d\\d\\d?\\d?\\ \\ \\d\\d:\\d\\d[AP]M)\\s+"
+                                     "(<DIR>|\\d+)\\s+(\\S.*)$"));
+    if (dosPattern.indexIn(bufferStr) == 0) {
+        _q_parseDosDir(dosPattern.capturedTexts(), userName, info);
+        return true;
+    }
+
+    // Unsupported
+    return false;
+}
+
+void QFtpDTP::socketConnected()
+{
+    bytesDone = 0;
+#if defined(QFTPDTP_DEBUG)
+    qDebug("QFtpDTP::connectState(CsConnected)");
+#endif
+    emit connectState(QFtpDTP::CsConnected);
+}
+
+void QFtpDTP::socketReadyRead()
+{
+    if (!socket)
+        return;
+
+    if (pi->currentCommand().isEmpty()) {
+        socket->close();
+#if defined(QFTPDTP_DEBUG)
+        qDebug("QFtpDTP::connectState(CsClosed)");
+#endif
+        emit connectState(QFtpDTP::CsClosed);
+        return;
+    }
+
+    if (pi->abortState == QFtpPI::AbortStarted) {
+        // discard data
+        socket->readAll();
+        return;
+    }
+
+    if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {
+        while (socket->canReadLine()) {
+            QUrlInfo i;
+            QByteArray line = socket->readLine();
+#if defined(QFTPDTP_DEBUG)
+            qDebug("QFtpDTP read (list): '%s'", line.constData());
+#endif
+            if (parseDir(line, QLatin1String(""), &i)) {
+                emit listInfo(i);
+            } else {
+                // some FTP servers don't return a 550 if the file or directory
+                // does not exist, but rather write a text to the data socket
+                // -- try to catch these cases
+                if (line.endsWith("No such file or directory\r\n"))
+                    err = QString::fromLatin1(line);
+            }
+        }
+    } else {
+        if (!is_ba && data.dev) {
+            do {
+                QByteArray ba;
+                ba.resize(socket->bytesAvailable());
+                qint64 bytesRead = socket->read(ba.data(), ba.size());
+                if (bytesRead < 0) {
+                    // a read following a readyRead() signal will
+                    // never fail.
+                    return;
+                }
+                ba.resize(bytesRead);
+                bytesDone += bytesRead;
+#if defined(QFTPDTP_DEBUG)
+                qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone);
+#endif
+                if (data.dev)       // make sure it wasn't deleted in the slot
+                    data.dev->write(ba);
+                emit dataTransferProgress(bytesDone, bytesTotal);
+
+                // Need to loop; dataTransferProgress is often connected to
+                // slots that update the GUI (e.g., progress bar values), and
+                // if events are processed, more data may have arrived.
+            } while (socket->bytesAvailable());
+        } else {
+#if defined(QFTPDTP_DEBUG)
+            qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",
+                   bytesAvailable(), bytesDone);
+#endif
+            emit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);
+            emit readyRead();
+        }
+    }
+}
+
+void QFtpDTP::socketError(QAbstractSocket::SocketError e)
+{
+    if (e == QTcpSocket::HostNotFoundError) {
+#if defined(QFTPDTP_DEBUG)
+        qDebug("QFtpDTP::connectState(CsHostNotFound)");
+#endif
+        emit connectState(QFtpDTP::CsHostNotFound);
+    } else if (e == QTcpSocket::ConnectionRefusedError) {
+#if defined(QFTPDTP_DEBUG)
+        qDebug("QFtpDTP::connectState(CsConnectionRefused)");
+#endif
+        emit connectState(QFtpDTP::CsConnectionRefused);
+    }
+}
+
+void QFtpDTP::socketConnectionClosed()
+{
+    if (!is_ba && data.dev) {
+        clearData();
+    }
+
+//    bytesFromSocket = socket->readAll();
+#if defined(QFTPDTP_DEBUG)
+    qDebug("QFtpDTP::connectState(CsClosed)");
+#endif
+    emit connectState(QFtpDTP::CsClosed);
+}
+
+void QFtpDTP::socketBytesWritten(qint64 bytes)
+{
+    bytesDone += bytes;
+#if defined(QFTPDTP_DEBUG)
+    qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone);
+#endif
+    emit dataTransferProgress(bytesDone, bytesTotal);
+    if (callWriteData)
+        writeData();
+}
+
+void QFtpDTP::setupSocket()
+{
+    socket = listener.nextPendingConnection();
+    socket->setObjectName(QLatin1String("QFtpDTP Active state socket"));
+    connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
+    connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
+    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+    connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
+    connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+    listener.close();
+}
+
+void QFtpDTP::clearData()
+{
+    is_ba = false;
+    data.dev = 0;
+}
+
+/**********************************************************************
+ *
+ * QFtpPI implemenatation
+ *
+ *********************************************************************/
+QFtpPI::QFtpPI(QObject *parent) :
+    QObject(parent),
+    rawCommand(false),
+    transferConnectionExtended(true),
+    dtp(this),
+    commandSocket(0),
+    state(Begin), abortState(None),
+    currentCmd(QString()),
+    waitForDtpToConnect(false),
+    waitForDtpToClose(false)
+{
+    commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));
+    connect(&commandSocket, SIGNAL(hostFound()),
+            SLOT(hostFound()));
+    connect(&commandSocket, SIGNAL(connected()),
+            SLOT(connected()));
+    connect(&commandSocket, SIGNAL(disconnected()),
+            SLOT(connectionClosed()));
+    connect(&commandSocket, SIGNAL(readyRead()),
+            SLOT(readyRead()));
+    connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+            SLOT(error(QAbstractSocket::SocketError)));
+
+    connect(&dtp, SIGNAL(connectState(int)),
+             SLOT(dtpConnectState(int)));
+}
+
+void QFtpPI::connectToHost(const QString &host, quint16 port)
+{
+    emit connectState(QFtp::HostLookup);
+#ifndef QT_NO_BEARERMANAGEMENT
+    //copy network session down to the socket & DTP
+    commandSocket.setProperty("_q_networksession", property("_q_networksession"));
+    dtp.setProperty("_q_networksession", property("_q_networksession"));
+#endif
+    commandSocket.connectToHost(host, port);
+}
+
+/*
+  Sends the sequence of commands \a cmds to the FTP server. When the commands
+  are all done the finished() signal is emitted. When an error occurs, the
+  error() signal is emitted.
+
+  If there are pending commands in the queue this functions returns false and
+  the \a cmds are not added to the queue; otherwise it returns true.
+*/
+bool QFtpPI::sendCommands(const QStringList &cmds)
+{
+    if (!pendingCommands.isEmpty())
+        return false;
+
+    if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {
+        emit error(QFtp::NotConnected, QFtp::tr("Not connected"));
+        return true; // there are no pending commands
+    }
+
+    pendingCommands = cmds;
+    startNextCmd();
+    return true;
+}
+
+void QFtpPI::clearPendingCommands()
+{
+    pendingCommands.clear();
+    dtp.abortConnection();
+    currentCmd.clear();
+    state = Idle;
+}
+
+void QFtpPI::abort()
+{
+    pendingCommands.clear();
+
+    if (abortState != None)
+        // ABOR already sent
+        return;
+
+    abortState = AbortStarted;
+#if defined(QFTPPI_DEBUG)
+    qDebug("QFtpPI send: ABOR");
+#endif
+    commandSocket.write("ABOR\r\n", 6);
+
+    if (currentCmd.startsWith(QLatin1String("STOR ")))
+        dtp.abortConnection();
+}
+
+void QFtpPI::hostFound()
+{
+    emit connectState(QFtp::Connecting);
+}
+
+void QFtpPI::connected()
+{
+    state = Begin;
+#if defined(QFTPPI_DEBUG)
+//    qDebug("QFtpPI state: %d [connected()]", state);
+#endif
+    // try to improve performance by setting TCP_NODELAY
+    commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
+
+    emit connectState(QFtp::Connected);
+}
+
+void QFtpPI::connectionClosed()
+{
+    commandSocket.close();
+    emit connectState(QFtp::Unconnected);
+}
+
+void QFtpPI::delayedCloseFinished()
+{
+    emit connectState(QFtp::Unconnected);
+}
+
+void QFtpPI::error(QAbstractSocket::SocketError e)
+{
+    if (e == QTcpSocket::HostNotFoundError) {
+        emit connectState(QFtp::Unconnected);
+        emit error(QFtp::HostNotFound,
+                    QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));
+    } else if (e == QTcpSocket::ConnectionRefusedError) {
+        emit connectState(QFtp::Unconnected);
+        emit error(QFtp::ConnectionRefused,
+                    QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));
+    } else if (e == QTcpSocket::SocketTimeoutError) {
+        emit connectState(QFtp::Unconnected);
+        emit error(QFtp::ConnectionRefused,
+                   QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));
+    }
+}
+
+void QFtpPI::readyRead()
+{
+    if (waitForDtpToClose)
+        return;
+
+    while (commandSocket.canReadLine()) {
+        // read line with respect to line continuation
+        QString line = QString::fromLatin1(commandSocket.readLine());
+        if (replyText.isEmpty()) {
+            if (line.length() < 3) {
+                // protocol error
+                return;
+            }
+            const int lowerLimit[3] = {1,0,0};
+            const int upperLimit[3] = {5,5,9};
+            for (int i=0; i<3; i++) {
+                replyCode[i] = line[i].digitValue();
+                if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {
+                    // protocol error
+                    return;
+                }
+            }
+        }
+        QString endOfMultiLine;
+        endOfMultiLine[0] = '0' + replyCode[0];
+        endOfMultiLine[1] = '0' + replyCode[1];
+        endOfMultiLine[2] = '0' + replyCode[2];
+        endOfMultiLine[3] = QLatin1Char(' ');
+        QString lineCont(endOfMultiLine);
+        lineCont[3] = QLatin1Char('-');
+        QString lineLeft4 = line.left(4);
+
+        while (lineLeft4 != endOfMultiLine) {
+            if (lineLeft4 == lineCont)
+                replyText += line.mid(4); // strip 'xyz-'
+            else
+                replyText += line;
+            if (!commandSocket.canReadLine())
+                return;
+            line = QString::fromLatin1(commandSocket.readLine());
+            lineLeft4 = line.left(4);
+        }
+        replyText += line.mid(4); // strip reply code 'xyz '
+        if (replyText.endsWith(QLatin1String("\r\n")))
+            replyText.chop(2);
+
+        if (processReply())
+            replyText = QLatin1String("");
+    }
+}
+
+/*
+  Process a reply from the FTP server.
+
+  Returns true if the reply was processed or false if the reply has to be
+  processed at a later point.
+*/
+bool QFtpPI::processReply()
+{
+#if defined(QFTPPI_DEBUG)
+//    qDebug("QFtpPI state: %d [processReply() begin]", state);
+    if (replyText.length() < 400)
+        qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());
+    else
+        qDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
+#endif
+
+    int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
+
+    // process 226 replies ("Closing Data Connection") only when the data
+    // connection is really closed to avoid short reads of the DTP
+    if (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {
+        if (dtp.state() != QTcpSocket::UnconnectedState) {
+            waitForDtpToClose = true;
+            return false;
+        }
+    }
+
+    switch (abortState) {
+        case AbortStarted:
+            abortState = WaitForAbortToFinish;
+            break;
+        case WaitForAbortToFinish:
+            abortState = None;
+            return true;
+        default:
+            break;
+    }
+
+    // get new state
+    static const State table[5] = {
+        /* 1yz   2yz      3yz   4yz      5yz */
+        Waiting, Success, Idle, Failure, Failure
+    };
+    switch (state) {
+        case Begin:
+            if (replyCode[0] == 1) {
+                return true;
+            } else if (replyCode[0] == 2) {
+                state = Idle;
+                emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));
+                break;
+            }
+            // reply codes not starting with 1 or 2 are not handled.
+            return true;
+        case Waiting:
+            if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)
+                state = Failure;
+            else
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
+            {
+                // work around a crash on 64 bit gcc IRIX
+                State *t = (State *) table;
+                state = t[replyCode[0] - 1];
+            }
+#else
+            if (replyCodeInt == 202)
+                state = Failure;
+            else
+                state = table[replyCode[0] - 1];
+#endif
+            break;
+        default:
+            // ignore unrequested message
+            return true;
+    }
+#if defined(QFTPPI_DEBUG)
+//    qDebug("QFtpPI state: %d [processReply() intermediate]", state);
+#endif
+
+    // special actions on certain replies
+    emit rawFtpReply(replyCodeInt, replyText);
+    if (rawCommand) {
+        rawCommand = false;
+    } else if (replyCodeInt == 227) {
+        // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
+        // rfc959 does not define this response precisely, and gives
+        // both examples where the parenthesis are used, and where
+        // they are missing. We need to scan for the address and host
+        // info.
+        QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
+        if (addrPortPattern.indexIn(replyText) == -1) {
+#if defined(QFTPPI_DEBUG)
+            qDebug("QFtp: bad 227 response -- address and port information missing");
+#endif
+            // this error should be reported
+        } else {
+            QStringList lst = addrPortPattern.capturedTexts();
+            QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
+            quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();
+            waitForDtpToConnect = true;
+            dtp.connectToHost(host, port);
+        }
+    } else if (replyCodeInt == 229) {
+        // 229 Extended Passive mode OK (|||10982|)
+        int portPos = replyText.indexOf(QLatin1Char('('));
+        if (portPos == -1) {
+#if defined(QFTPPI_DEBUG)
+            qDebug("QFtp: bad 229 response -- port information missing");
+#endif
+            // this error should be reported
+        } else {
+            ++portPos;
+            QChar delimiter = replyText.at(portPos);
+            QStringList epsvParameters = replyText.mid(portPos).split(delimiter);
+
+            waitForDtpToConnect = true;
+            dtp.connectToHost(commandSocket.peerAddress().toString(),
+                              epsvParameters.at(3).toInt());
+        }
+
+    } else if (replyCodeInt == 230) {
+        if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
+            pendingCommands.first().startsWith(QLatin1String("PASS "))) {
+            // no need to send the PASS -- we are already logged in
+            pendingCommands.pop_front();
+        }
+        // 230 User logged in, proceed.
+        emit connectState(QFtp::LoggedIn);
+    } else if (replyCodeInt == 213) {
+        // 213 File status.
+        if (currentCmd.startsWith(QLatin1String("SIZE ")))
+            dtp.setBytesTotal(replyText.simplified().toLongLong());
+    } else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {
+        dtp.waitForConnection();
+        dtp.writeData();
+    }
+
+    // react on new state
+    switch (state) {
+        case Begin:
+            // should never happen
+            break;
+        case Success:
+            // success handling
+            state = Idle;
+            // no break!
+        case Idle:
+            if (dtp.hasError()) {
+                emit error(QFtp::UnknownError, dtp.errorMessage());
+                dtp.clearError();
+            }
+            startNextCmd();
+            break;
+        case Waiting:
+            // do nothing
+            break;
+        case Failure:
+            // If the EPSV or EPRT commands fail, replace them with
+            // the old PASV and PORT instead and try again.
+            if (currentCmd.startsWith(QLatin1String("EPSV"))) {
+                transferConnectionExtended = false;
+                pendingCommands.prepend(QLatin1String("PASV\r\n"));
+            } else if (currentCmd.startsWith(QLatin1String("EPRT"))) {
+                transferConnectionExtended = false;
+                pendingCommands.prepend(QLatin1String("PORT\r\n"));
+            } else {
+                emit error(QFtp::UnknownError, replyText);
+            }
+            if (state != Waiting) {
+                state = Idle;
+                startNextCmd();
+            }
+            break;
+    }
+#if defined(QFTPPI_DEBUG)
+//    qDebug("QFtpPI state: %d [processReply() end]", state);
+#endif
+    return true;
+}
+
+/*
+  Starts next pending command. Returns false if there are no pending commands,
+  otherwise it returns true.
+*/
+bool QFtpPI::startNextCmd()
+{
+    if (waitForDtpToConnect)
+        // don't process any new commands until we are connected
+        return true;
+
+#if defined(QFTPPI_DEBUG)
+    if (state != Idle)
+        qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state);
+#endif
+    if (pendingCommands.isEmpty()) {
+        currentCmd.clear();
+        emit finished(replyText);
+        return false;
+    }
+    currentCmd = pendingCommands.first();
+
+    // PORT and PASV are edited in-place, depending on whether we
+    // should try the extended transfer connection commands EPRT and
+    // EPSV. The PORT command also triggers setting up a listener, and
+    // the address/port arguments are edited in.
+    QHostAddress address = commandSocket.localAddress();
+    if (currentCmd.startsWith(QLatin1String("PORT"))) {
+        if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {
+            int port = dtp.setupListener(address);
+            currentCmd = QLatin1String("EPRT |");
+            currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');
+            currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);
+            currentCmd += QLatin1Char('|');
+        } else if (address.protocol() == QTcpSocket::IPv4Protocol) {
+            int port = dtp.setupListener(address);
+            QString portArg;
+            quint32 ip = address.toIPv4Address();
+            portArg += QString::number((ip & 0xff000000) >> 24);
+            portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);
+            portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);
+            portArg += QLatin1Char(',') + QString::number(ip & 0xff);
+            portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);
+            portArg += QLatin1Char(',') + QString::number(port & 0xff);
+
+            currentCmd = QLatin1String("PORT ");
+            currentCmd += portArg;
+        } else {
+            // No IPv6 connection can be set up with the PORT
+            // command.
+            return false;
+        }
+
+        currentCmd += QLatin1String("\r\n");
+    } else if (currentCmd.startsWith(QLatin1String("PASV"))) {
+        if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)
+            currentCmd = QLatin1String("EPSV\r\n");
+    }
+
+    pendingCommands.pop_front();
+#if defined(QFTPPI_DEBUG)
+    qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData());
+#endif
+    state = Waiting;
+    commandSocket.write(currentCmd.toLatin1());
+    return true;
+}
+
+void QFtpPI::dtpConnectState(int s)
+{
+    switch (s) {
+        case QFtpDTP::CsClosed:
+            if (waitForDtpToClose) {
+                // there is an unprocessed reply
+                if (processReply())
+                    replyText = QLatin1String("");
+                else
+                    return;
+            }
+            waitForDtpToClose = false;
+            readyRead();
+            return;
+        case QFtpDTP::CsConnected:
+            waitForDtpToConnect = false;
+            startNextCmd();
+            return;
+        case QFtpDTP::CsHostNotFound:
+        case QFtpDTP::CsConnectionRefused:
+            emit error(QFtp::ConnectionRefused,
+                        QFtp::tr("Connection refused for data connection"));
+            startNextCmd();
+            return;
+        default:
+            return;
+    }
+}
+
+/**********************************************************************
+ *
+ * QFtpPrivate
+ *
+ *********************************************************************/
+
+class QFtpPrivate
+{
+    Q_DECLARE_PUBLIC(QFtp)
+public:
+
+    inline QFtpPrivate(QFtp *owner) : close_waitForStateChange(false), state(QFtp::Unconnected),
+        transferMode(QFtp::Passive), error(QFtp::NoError), q_ptr(owner)
+    { }
+
+    ~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }
+
+    // private slots
+    void _q_startNextCommand();
+    void _q_piFinished(const QString&);
+    void _q_piError(int, const QString&);
+    void _q_piConnectState(int);
+    void _q_piFtpReply(int, const QString&);
+
+    int addCommand(QFtpCommand *cmd);
+
+    QFtpPI pi;
+    QList<QFtpCommand *> pending;
+    bool close_waitForStateChange;
+    QFtp::State state;
+    QFtp::TransferMode transferMode;
+    QFtp::Error error;
+    QString errorString;
+
+    QString host;
+    quint16 port;
+    QString proxyHost;
+    quint16 proxyPort;
+    QFtp *q_ptr;
+};
+
+int QFtpPrivate::addCommand(QFtpCommand *cmd)
+{
+    pending.append(cmd);
+
+    if (pending.count() == 1) {
+        // don't emit the commandStarted() signal before the ID is returned
+        QTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));
+    }
+    return cmd->id;
+}
+
+/**********************************************************************
+ *
+ * QFtp implementation
+ *
+ *********************************************************************/
+/*!
+    \class QFtp
+    \brief The QFtp class provides an implementation of the client side of FTP protocol.
+
+    \ingroup network
+    \inmodule QtNetwork
+
+
+    This class provides a direct interface to FTP that allows you to
+    have more control over the requests. However, for new
+    applications, it is recommended to use QNetworkAccessManager and
+    QNetworkReply, as those classes possess a simpler, yet more
+    powerful API.
+
+    The class works asynchronously, so there are no blocking
+    functions. If an operation cannot be executed immediately, the
+    function will still return straight away and the operation will be
+    scheduled for later execution. The results of scheduled operations
+    are reported via signals. This approach depends on the event loop
+    being in operation.
+
+    The operations that can be scheduled (they are called "commands"
+    in the rest of the documentation) are the following:
+    connectToHost(), login(), close(), list(), cd(), get(), put(),
+    remove(), mkdir(), rmdir(), rename() and rawCommand().
+
+    All of these commands return a unique identifier that allows you
+    to keep track of the command that is currently being executed.
+    When the execution of a command starts, the commandStarted()
+    signal with the command's identifier is emitted. When the command
+    is finished, the commandFinished() signal is emitted with the
+    command's identifier and a bool that indicates whether the command
+    finished with an error.
+
+    In some cases, you might want to execute a sequence of commands,
+    e.g. if you want to connect and login to a FTP server. This is
+    simply achieved:
+
+    \snippet doc/src/snippets/code/src_network_access_qftp.cpp 0
+
+    In this case two FTP commands have been scheduled. When the last
+    scheduled command has finished, a done() signal is emitted with
+    a bool argument that tells you whether the sequence finished with
+    an error.
+
+    If an error occurs during the execution of one of the commands in
+    a sequence of commands, all the pending commands (i.e. scheduled,
+    but not yet executed commands) are cleared and no signals are
+    emitted for them.
+
+    Some commands, e.g. list(), emit additional signals to report
+    their results.
+
+    Example: If you want to download the INSTALL file from the Qt
+    FTP server, you would write this:
+
+    \snippet doc/src/snippets/code/src_network_access_qftp.cpp 1
+
+    For this example the following sequence of signals is emitted
+    (with small variations, depending on network traffic, etc.):
+
+    \snippet doc/src/snippets/code/src_network_access_qftp.cpp 2
+
+    The dataTransferProgress() signal in the above example is useful
+    if you want to show a \link QProgressBar progress bar \endlink to
+    inform the user about the progress of the download. The
+    readyRead() signal tells you that there is data ready to be read.
+    The amount of data can be queried then with the bytesAvailable()
+    function and it can be read with the read() or readAll()
+    function.
+
+    If the login fails for the above example, the signals would look
+    like this:
+
+    \snippet doc/src/snippets/code/src_network_access_qftp.cpp 3
+
+    You can then get details about the error with the error() and
+    errorString() functions.
+
+    For file transfer, QFtp can use both active or passive mode, and
+    it uses passive file transfer mode by default; see the
+    documentation for setTransferMode() for more details about this.
+
+    Call setProxy() to make QFtp connect via an FTP proxy server.
+
+    The functions currentId() and currentCommand() provide more
+    information about the currently executing command.
+
+    The functions hasPendingCommands() and clearPendingCommands()
+    allow you to query and clear the list of pending commands.
+
+    If you are an experienced network programmer and want to have
+    complete control you can use rawCommand() to execute arbitrary FTP
+    commands.
+
+    \warning The current version of QFtp doesn't fully support
+    non-Unix FTP servers.
+
+    \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+        {FTP Example}
+*/
+
+
+/*!
+    Constructs a QFtp object with the given \a parent.
+*/
+QFtp::QFtp(QObject *parent)
+    : QObject(parent), d(new QFtpPrivate(this))
+{
+    d->errorString = tr("Unknown error");
+
+    connect(&d->pi, SIGNAL(connectState(int)),
+            SLOT(_q_piConnectState(int)));
+    connect(&d->pi, SIGNAL(finished(QString)),
+            SLOT(_q_piFinished(QString)));
+    connect(&d->pi, SIGNAL(error(int,QString)),
+            SLOT(_q_piError(int,QString)));
+    connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),
+            SLOT(_q_piFtpReply(int,QString)));
+
+    connect(&d->pi.dtp, SIGNAL(readyRead()),
+            SIGNAL(readyRead()));
+    connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),
+            SIGNAL(dataTransferProgress(qint64,qint64)));
+    connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
+            SIGNAL(listInfo(QUrlInfo)));
+}
+
+/*!
+    \enum QFtp::State
+
+    This enum defines the connection state:
+
+    \value Unconnected There is no connection to the host.
+    \value HostLookup A host name lookup is in progress.
+    \value Connecting An attempt to connect to the host is in progress.
+    \value Connected Connection to the host has been achieved.
+    \value LoggedIn Connection and user login have been achieved.
+    \value Closing The connection is closing down, but it is not yet
+    closed. (The state will be \c Unconnected when the connection is
+    closed.)
+
+    \sa stateChanged() state()
+*/
+/*!
+    \enum QFtp::TransferMode
+
+    FTP works with two socket connections; one for commands and
+    another for transmitting data. While the command connection is
+    always initiated by the client, the second connection can be
+    initiated by either the client or the server.
+
+    This enum defines whether the client (Passive mode) or the server
+    (Active mode) should set up the data connection.
+
+    \value Passive The client connects to the server to transmit its
+    data.
+
+    \value Active The server connects to the client to transmit its
+    data.
+*/
+/*!
+    \enum QFtp::TransferType
+
+    This enum identifies the data transfer type used with get and
+    put commands.
+
+    \value Binary The data will be transferred in Binary mode.
+
+    \value Ascii The data will be transferred in Ascii mode and new line
+    characters will be converted to the local format.
+*/
+/*!
+    \enum QFtp::Error
+
+    This enum identifies the error that occurred.
+
+    \value NoError No error occurred.
+    \value HostNotFound The host name lookup failed.
+    \value ConnectionRefused The server refused the connection.
+    \value NotConnected Tried to send a command, but there is no connection to
+    a server.
+    \value UnknownError An error other than those specified above
+    occurred.
+
+    \sa error()
+*/
+
+/*!
+    \enum QFtp::Command
+
+    This enum is used as the return value for the currentCommand() function.
+    This allows you to perform specific actions for particular
+    commands, e.g. in a FTP client, you might want to clear the
+    directory view when a list() command is started; in this case you
+    can simply check in the slot connected to the start() signal if
+    the currentCommand() is \c List.
+
+    \value None No command is being executed.
+    \value SetTransferMode set the \link TransferMode transfer\endlink mode.
+    \value SetProxy switch proxying on or off.
+    \value ConnectToHost connectToHost() is being executed.
+    \value Login login() is being executed.
+    \value Close close() is being executed.
+    \value List list() is being executed.
+    \value Cd cd() is being executed.
+    \value Get get() is being executed.
+    \value Put put() is being executed.
+    \value Remove remove() is being executed.
+    \value Mkdir mkdir() is being executed.
+    \value Rmdir rmdir() is being executed.
+    \value Rename rename() is being executed.
+    \value RawCommand rawCommand() is being executed.
+
+    \sa currentCommand()
+*/
+
+/*!
+    \fn void QFtp::stateChanged(int state)
+
+    This signal is emitted when the state of the connection changes.
+    The argument \a state is the new state of the connection; it is
+    one of the \l State values.
+
+    It is usually emitted in response to a connectToHost() or close()
+    command, but it can also be emitted "spontaneously", e.g. when the
+    server closes the connection unexpectedly.
+
+    \sa connectToHost() close() state() State
+*/
+
+/*!
+    \fn void QFtp::listInfo(const QUrlInfo &i);
+
+    This signal is emitted for each directory entry the list() command
+    finds. The details of the entry are stored in \a i.
+
+    \sa list()
+*/
+
+/*!
+    \fn void QFtp::commandStarted(int id)
+
+    This signal is emitted when processing the command identified by
+    \a id starts.
+
+    \sa commandFinished() done()
+*/
+
+/*!
+    \fn void QFtp::commandFinished(int id, bool error)
+
+    This signal is emitted when processing the command identified by
+    \a id has finished. \a error is true if an error occurred during
+    the processing; otherwise \a error is false.
+
+    \sa commandStarted() done() error() errorString()
+*/
+
+/*!
+    \fn void QFtp::done(bool error)
+
+    This signal is emitted when the last pending command has finished;
+    (it is emitted after the last command's commandFinished() signal).
+    \a error is true if an error occurred during the processing;
+    otherwise \a error is false.
+
+    \sa commandFinished() error() errorString()
+*/
+
+/*!
+    \fn void QFtp::readyRead()
+
+    This signal is emitted in response to a get() command when there
+    is new data to read.
+
+    If you specify a device as the second argument in the get()
+    command, this signal is \e not emitted; instead the data is
+    written directly to the device.
+
+    You can read the data with the readAll() or read() functions.
+
+    This signal is useful if you want to process the data in chunks as
+    soon as it becomes available. If you are only interested in the
+    complete data, just connect to the commandFinished() signal and
+    read the data then instead.
+
+    \sa get() read() readAll() bytesAvailable()
+*/
+
+/*!
+    \fn void QFtp::dataTransferProgress(qint64 done, qint64 total)
+
+    This signal is emitted in response to a get() or put() request to
+    indicate the current progress of the download or upload.
+
+    \a done is the amount of data that has already been transferred
+    and \a total is the total amount of data to be read or written. It
+    is possible that the QFtp class is not able to determine the total
+    amount of data that should be transferred, in which case \a total
+    is 0. (If you connect this signal to a QProgressBar, the progress
+    bar shows a busy indicator if the total is 0).
+
+    \warning \a done and \a total are not necessarily the size in
+    bytes, since for large files these values might need to be
+    "scaled" to avoid overflow.
+
+    \sa get(), put(), QProgressBar
+*/
+
+/*!
+    \fn void QFtp::rawCommandReply(int replyCode, const QString &detail);
+
+    This signal is emitted in response to the rawCommand() function.
+    \a replyCode is the 3 digit reply code and \a detail is the text
+    that follows the reply code.
+
+    \sa rawCommand()
+*/
+
+/*!
+    Connects to the FTP server \a host using port \a port.
+
+    The stateChanged() signal is emitted when the state of the
+    connecting process changes, e.g. to \c HostLookup, then \c
+    Connecting, then \c Connected.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa stateChanged() commandStarted() commandFinished()
+*/
+int QFtp::connectToHost(const QString &host, quint16 port)
+{
+    QStringList cmds;
+    cmds << host;
+    cmds << QString::number((uint)port);
+    int id = d->addCommand(new QFtpCommand(ConnectToHost, cmds));
+    d->pi.transferConnectionExtended = true;
+    return id;
+}
+
+/*!
+    Logs in to the FTP server with the username \a user and the
+    password \a password.
+
+    The stateChanged() signal is emitted when the state of the
+    connecting process changes, e.g. to \c LoggedIn.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::login(const QString &user, const QString &password)
+{
+    QStringList cmds;
+    cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));
+    cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));
+    return d->addCommand(new QFtpCommand(Login, cmds));
+}
+
+/*!
+    Closes the connection to the FTP server.
+
+    The stateChanged() signal is emitted when the state of the
+    connecting process changes, e.g. to \c Closing, then \c
+    Unconnected.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa stateChanged() commandStarted() commandFinished()
+*/
+int QFtp::close()
+{
+    return d->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n"))));
+}
+
+/*!
+    Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.
+
+    \sa QFtp::TransferMode
+*/
+int QFtp::setTransferMode(TransferMode mode)
+{
+    int id = d->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
+    d->pi.transferConnectionExtended = true;
+    d->transferMode = mode;
+    return id;
+}
+
+/*!
+    Enables use of the FTP proxy on host \a host and port \a
+    port. Calling this function with \a host empty disables proxying.
+
+    QFtp does not support FTP-over-HTTP proxy servers. Use
+    QNetworkAccessManager for this.
+*/
+int QFtp::setProxy(const QString &host, quint16 port)
+{
+    QStringList args;
+    args << host << QString::number(port);
+    return d->addCommand(new QFtpCommand(SetProxy, args));
+}
+
+/*!
+    Lists the contents of directory \a dir on the FTP server. If \a
+    dir is empty, it lists the contents of the current directory.
+
+    The listInfo() signal is emitted for each directory entry found.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa listInfo() commandStarted() commandFinished()
+*/
+int QFtp::list(const QString &dir)
+{
+    QStringList cmds;
+    cmds << QLatin1String("TYPE A\r\n");
+    cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+    if (dir.isEmpty())
+        cmds << QLatin1String("LIST\r\n");
+    else
+        cmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));
+    return d->addCommand(new QFtpCommand(List, cmds));
+}
+
+/*!
+    Changes the working directory of the server to \a dir.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::cd(const QString &dir)
+{
+    return d->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+    Downloads the file \a file from the server.
+
+    If \a dev is 0, then the readyRead() signal is emitted when there
+    is data available to read. You can then read the data with the
+    read() or readAll() functions.
+
+    If \a dev is not 0, the data is written directly to the device \a
+    dev. Make sure that the \a dev pointer is valid for the duration
+    of the operation (it is safe to delete it when the
+    commandFinished() signal is emitted). In this case the readyRead()
+    signal is \e not emitted and you cannot read data with the
+    read() or readAll() functions.
+
+    If you don't read the data immediately it becomes available, i.e.
+    when the readyRead() signal is emitted, it is still available
+    until the next command is started.
+
+    For example, if you want to present the data to the user as soon
+    as there is something available, connect to the readyRead() signal
+    and read the data immediately. On the other hand, if you only want
+    to work with the complete data, you can connect to the
+    commandFinished() signal and read the data when the get() command
+    is finished.
+
+    The data is transferred as Binary or Ascii depending on the value
+    of \a type.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa readyRead() dataTransferProgress() commandStarted()
+    commandFinished()
+*/
+int QFtp::get(const QString &file, QIODevice *dev, TransferType type)
+{
+    QStringList cmds;
+    if (type == Binary)
+        cmds << QLatin1String("TYPE I\r\n");
+    else
+        cmds << QLatin1String("TYPE A\r\n");
+    cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");
+    cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+    cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");
+    return d->addCommand(new QFtpCommand(Get, cmds, dev));
+}
+
+/*!
+    \overload
+
+    Writes a copy of the given \a data to the file called \a file on
+    the server. The progress of the upload is reported by the
+    dataTransferProgress() signal.
+
+    The data is transferred as Binary or Ascii depending on the value
+    of \a type.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    Since this function takes a copy of the \a data, you can discard
+    your own copy when this function returns.
+
+    \sa dataTransferProgress() commandStarted() commandFinished()
+*/
+int QFtp::put(const QByteArray &data, const QString &file, TransferType type)
+{
+    QStringList cmds;
+    if (type == Binary)
+        cmds << QLatin1String("TYPE I\r\n");
+    else
+        cmds << QLatin1String("TYPE A\r\n");
+    cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+    cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");
+    cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
+    return d->addCommand(new QFtpCommand(Put, cmds, data));
+}
+
+/*!
+    Reads the data from the IO device \a dev, and writes it to the
+    file called \a file on the server. The data is read in chunks from
+    the IO device, so this overload allows you to transmit large
+    amounts of data without the need to read all the data into memory
+    at once.
+
+    The data is transferred as Binary or Ascii depending on the value
+    of \a type.
+
+    Make sure that the \a dev pointer is valid for the duration of the
+    operation (it is safe to delete it when the commandFinished() is
+    emitted).
+*/
+int QFtp::put(QIODevice *dev, const QString &file, TransferType type)
+{
+    QStringList cmds;
+    if (type == Binary)
+        cmds << QLatin1String("TYPE I\r\n");
+    else
+        cmds << QLatin1String("TYPE A\r\n");
+    cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+    if (!dev->isSequential())
+        cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");
+    cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
+    return d->addCommand(new QFtpCommand(Put, cmds, dev));
+}
+
+/*!
+    Deletes the file called \a file from the server.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::remove(const QString &file)
+{
+    return d->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n"))));
+}
+
+/*!
+    Creates a directory called \a dir on the server.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::mkdir(const QString &dir)
+{
+    return d->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+    Removes the directory called \a dir from the server.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::rmdir(const QString &dir)
+{
+    return d->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+    Renames the file called \a oldname to \a newname on the server.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa commandStarted() commandFinished()
+*/
+int QFtp::rename(const QString &oldname, const QString &newname)
+{
+    QStringList cmds;
+    cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");
+    cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");
+    return d->addCommand(new QFtpCommand(Rename, cmds));
+}
+
+/*!
+    Sends the raw FTP command \a command to the FTP server. This is
+    useful for low-level FTP access. If the operation you wish to
+    perform has an equivalent QFtp function, we recommend using the
+    function instead of raw FTP commands since the functions are
+    easier and safer.
+
+    The function does not block and returns immediately. The command
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    commandStarted() and commandFinished().
+
+    When the command is started the commandStarted() signal is
+    emitted. When it is finished the commandFinished() signal is
+    emitted.
+
+    \sa rawCommandReply() commandStarted() commandFinished()
+*/
+int QFtp::rawCommand(const QString &command)
+{
+    QString cmd = command.trimmed() + QLatin1String("\r\n");
+    return d->addCommand(new QFtpCommand(RawCommand, QStringList(cmd)));
+}
+
+/*!
+    Returns the number of bytes that can be read from the data socket
+    at the moment.
+
+    \sa get() readyRead() read() readAll()
+*/
+qint64 QFtp::bytesAvailable() const
+{
+    return d->pi.dtp.bytesAvailable();
+}
+
+/*! \fn qint64 QFtp::readBlock(char *data, quint64 maxlen)
+
+    Use read() instead.
+*/
+
+/*!
+    Reads \a maxlen bytes from the data socket into \a data and
+    returns the number of bytes read. Returns -1 if an error occurred.
+
+    \sa get() readyRead() bytesAvailable() readAll()
+*/
+qint64 QFtp::read(char *data, qint64 maxlen)
+{
+    return d->pi.dtp.read(data, maxlen);
+}
+
+/*!
+    Reads all the bytes available from the data socket and returns
+    them.
+
+    \sa get() readyRead() bytesAvailable() read()
+*/
+QByteArray QFtp::readAll()
+{
+    return d->pi.dtp.readAll();
+}
+
+/*!
+    Aborts the current command and deletes all scheduled commands.
+
+    If there is an unfinished command (i.e. a command for which the
+    commandStarted() signal has been emitted, but for which the
+    commandFinished() signal has not been emitted), this function
+    sends an \c ABORT command to the server. When the server replies
+    that the command is aborted, the commandFinished() signal with the
+    \c error argument set to \c true is emitted for the command. Due
+    to timing issues, it is possible that the command had already
+    finished before the abort request reached the server, in which
+    case, the commandFinished() signal is emitted with the \c error
+    argument set to \c false.
+
+    For all other commands that are affected by the abort(), no
+    signals are emitted.
+
+    If you don't start further FTP commands directly after the
+    abort(), there won't be any scheduled commands and the done()
+    signal is emitted.
+
+    \warning Some FTP servers, for example the BSD FTP daemon (version
+    0.3), wrongly return a positive reply even when an abort has
+    occurred. For these servers the commandFinished() signal has its
+    error flag set to \c false, even though the command did not
+    complete successfully.
+
+    \sa clearPendingCommands()
+*/
+void QFtp::abort()
+{
+    if (d->pending.isEmpty())
+        return;
+
+    clearPendingCommands();
+    d->pi.abort();
+}
+
+/*!
+    Returns the identifier of the FTP command that is being executed
+    or 0 if there is no command being executed.
+
+    \sa currentCommand()
+*/
+int QFtp::currentId() const
+{
+    if (d->pending.isEmpty())
+        return 0;
+    return d->pending.first()->id;
+}
+
+/*!
+    Returns the command type of the FTP command being executed or \c
+    None if there is no command being executed.
+
+    \sa currentId()
+*/
+QFtp::Command QFtp::currentCommand() const
+{
+    if (d->pending.isEmpty())
+        return None;
+    return d->pending.first()->command;
+}
+
+/*!
+    Returns the QIODevice pointer that is used by the FTP command to read data
+    from or store data to. If there is no current FTP command being executed or
+    if the command does not use an IO device, this function returns 0.
+
+    This function can be used to delete the QIODevice in the slot connected to
+    the commandFinished() signal.
+
+    \sa get() put()
+*/
+QIODevice* QFtp::currentDevice() const
+{
+    if (d->pending.isEmpty())
+        return 0;
+    QFtpCommand *c = d->pending.first();
+    if (c->is_ba)
+        return 0;
+    return c->data.dev;
+}
+
+/*!
+    Returns true if there are any commands scheduled that have not yet
+    been executed; otherwise returns false.
+
+    The command that is being executed is \e not considered as a
+    scheduled command.
+
+    \sa clearPendingCommands() currentId() currentCommand()
+*/
+bool QFtp::hasPendingCommands() const
+{
+    return d->pending.count() > 1;
+}
+
+/*!
+    Deletes all pending commands from the list of scheduled commands.
+    This does not affect the command that is being executed. If you
+    want to stop this as well, use abort().
+
+    \sa hasPendingCommands() abort()
+*/
+void QFtp::clearPendingCommands()
+{
+    // delete all entires except the first one
+    while (d->pending.count() > 1)
+        delete d->pending.takeLast();
+}
+
+/*!
+    Returns the current state of the object. When the state changes,
+    the stateChanged() signal is emitted.
+
+    \sa State stateChanged()
+*/
+QFtp::State QFtp::state() const
+{
+    return d->state;
+}
+
+/*!
+    Returns the last error that occurred. This is useful to find out
+    what went wrong when receiving a commandFinished() or a done()
+    signal with the \c error argument set to \c true.
+
+    If you start a new command, the error status is reset to \c NoError.
+*/
+QFtp::Error QFtp::error() const
+{
+    return d->error;
+}
+
+/*!
+    Returns a human-readable description of the last error that
+    occurred. This is useful for presenting a error message to the
+    user when receiving a commandFinished() or a done() signal with
+    the \c error argument set to \c true.
+
+    The error string is often (but not always) the reply from the
+    server, so it is not always possible to translate the string. If
+    the message comes from Qt, the string has already passed through
+    tr().
+*/
+QString QFtp::errorString() const
+{
+    return d->errorString;
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_startNextCommand()
+{
+    Q_Q(QFtp);
+    if (pending.isEmpty())
+        return;
+    QFtpCommand *c = pending.first();
+
+    error = QFtp::NoError;
+    errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));
+
+    if (q->bytesAvailable())
+        q->readAll(); // clear the data
+    emit q->commandStarted(c->id);
+
+    // Proxy support, replace the Login argument in place, then fall
+    // through.
+    if (c->command == QFtp::Login && !proxyHost.isEmpty()) {
+        QString loginString = c->rawCmds.first().trimmed();
+        loginString += QLatin1Char('@') + host;
+        if (port && port != 21)
+            loginString += QLatin1Char(':') + QString::number(port);
+        loginString += QLatin1String("\r\n");
+        c->rawCmds[0] = loginString;
+    }
+
+    if (c->command == QFtp::SetTransferMode) {
+        _q_piFinished(QLatin1String("Transfer mode set"));
+    } else if (c->command == QFtp::SetProxy) {
+        proxyHost = c->rawCmds[0];
+        proxyPort = c->rawCmds[1].toUInt();
+        c->rawCmds.clear();
+        _q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));
+    } else if (c->command == QFtp::ConnectToHost) {
+#ifndef QT_NO_BEARERMANAGEMENT
+        //copy network session down to the PI
+        pi.setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
+        if (!proxyHost.isEmpty()) {
+            host = c->rawCmds[0];
+            port = c->rawCmds[1].toUInt();
+            pi.connectToHost(proxyHost, proxyPort);
+        } else {
+            pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());
+        }
+    } else {
+        if (c->command == QFtp::Put) {
+            if (c->is_ba) {
+                pi.dtp.setData(c->data.ba);
+                pi.dtp.setBytesTotal(c->data.ba->size());
+            } else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {
+                pi.dtp.setDevice(c->data.dev);
+                if (c->data.dev->isSequential()) {
+                    pi.dtp.setBytesTotal(0);
+                    pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));
+                    pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));
+                } else {
+                    pi.dtp.setBytesTotal(c->data.dev->size());
+                }
+            }
+        } else if (c->command == QFtp::Get) {
+            if (!c->is_ba && c->data.dev) {
+                pi.dtp.setDevice(c->data.dev);
+            }
+        } else if (c->command == QFtp::Close) {
+            state = QFtp::Closing;
+            emit q->stateChanged(state);
+        }
+        pi.sendCommands(c->rawCmds);
+    }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piFinished(const QString&)
+{
+    if (pending.isEmpty())
+        return;
+    QFtpCommand *c = pending.first();
+
+    if (c->command == QFtp::Close) {
+        // The order of in which the slots are called is arbitrary, so
+        // disconnect the SIGNAL-SIGNAL temporary to make sure that we
+        // don't get the commandFinished() signal before the stateChanged()
+        // signal.
+        if (state != QFtp::Unconnected) {
+            close_waitForStateChange = true;
+            return;
+        }
+    }
+    emit q_func()->commandFinished(c->id, false);
+    pending.removeFirst();
+
+    delete c;
+
+    if (pending.isEmpty()) {
+        emit q_func()->done(false);
+    } else {
+        _q_startNextCommand();
+    }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piError(int errorCode, const QString &text)
+{
+    Q_Q(QFtp);
+
+    if (pending.isEmpty()) {
+        qWarning("QFtpPrivate::_q_piError was called without pending command!");
+        return;
+    }
+
+    QFtpCommand *c = pending.first();
+
+    // non-fatal errors
+    if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
+        pi.dtp.setBytesTotal(0);
+        return;
+    } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
+        return;
+    }
+
+    error = QFtp::Error(errorCode);
+    switch (q->currentCommand()) {
+        case QFtp::ConnectToHost:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Login:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::List:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Cd:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Get:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Put:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Remove:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Mkdir:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
+                          .arg(text);
+            break;
+        case QFtp::Rmdir:
+            errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
+                          .arg(text);
+            break;
+        default:
+            errorString = text;
+            break;
+    }
+
+    pi.clearPendingCommands();
+    q->clearPendingCommands();
+    emit q->commandFinished(c->id, true);
+
+    pending.removeFirst();
+    delete c;
+    if (pending.isEmpty())
+        emit q->done(true);
+    else
+        _q_startNextCommand();
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piConnectState(int connectState)
+{
+    state = QFtp::State(connectState);
+    emit q_func()->stateChanged(state);
+    if (close_waitForStateChange) {
+        close_waitForStateChange = false;
+        _q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));
+    }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
+{
+    if (q_func()->currentCommand() == QFtp::RawCommand) {
+        pi.rawCommand = true;
+        emit q_func()->rawCommandReply(code, text);
+    }
+}
+
+/*!
+    Destructor.
+*/
+QFtp::~QFtp()
+{
+    abort();
+    close();
+}
+
+QT_END_NAMESPACE
+
+#include "qftp.moc"
+
+#include "moc_qftp.cpp"
+
+#endif // QT_NO_FTP
+#endif // QT_VERSION
diff --git a/qsstv/utils/qftp.h b/qsstv/utils/qftp.h
new file mode 100644
index 0000000..a288686
--- /dev/null
+++ b/qsstv/utils/qftp.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFTP_H
+#define QFTP_H
+
+#include <QtCore/qstring.h>
+#include "qurlinfo.h"
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+class QFtpPrivate;
+
+class QFtp : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit QFtp(QObject *parent = 0);
+    virtual ~QFtp();
+
+    enum State {
+        Unconnected,
+        HostLookup,
+        Connecting,
+        Connected,
+        LoggedIn,
+        Closing
+    };
+    enum Error {
+        NoError,
+        UnknownError,
+        HostNotFound,
+        ConnectionRefused,
+        NotConnected
+    };
+    enum Command {
+        None,
+        SetTransferMode,
+        SetProxy,
+        ConnectToHost,
+        Login,
+        Close,
+        List,
+        Cd,
+        Get,
+        Put,
+        Remove,
+        Mkdir,
+        Rmdir,
+        Rename,
+        RawCommand
+    };
+    enum TransferMode {
+        Active,
+        Passive
+    };
+    enum TransferType {
+        Binary,
+        Ascii
+    };
+
+    int setProxy(const QString &host, quint16 port);
+    int connectToHost(const QString &host, quint16 port=21);
+    int login(const QString &user = QString(), const QString &password = QString());
+    int close();
+    int setTransferMode(TransferMode mode);
+    int list(const QString &dir = QString());
+    int cd(const QString &dir);
+    int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);
+    int put(const QByteArray &data, const QString &file, TransferType type = Binary);
+    int put(QIODevice *dev, const QString &file, TransferType type = Binary);
+    int remove(const QString &file);
+    int mkdir(const QString &dir);
+    int rmdir(const QString &dir);
+    int rename(const QString &oldname, const QString &newname);
+
+    int rawCommand(const QString &command);
+
+    qint64 bytesAvailable() const;
+    qint64 read(char *data, qint64 maxlen);
+    QByteArray readAll();
+
+    int currentId() const;
+    QIODevice* currentDevice() const;
+    Command currentCommand() const;
+    bool hasPendingCommands() const;
+    void clearPendingCommands();
+
+    State state() const;
+
+    Error error() const;
+    QString errorString() const;
+
+public Q_SLOTS:
+    void abort();
+
+Q_SIGNALS:
+    void stateChanged(int);
+    void listInfo(const QUrlInfo&);
+    void readyRead();
+    void dataTransferProgress(qint64, qint64);
+    void rawCommandReply(int, const QString&);
+
+    void commandStarted(int);
+    void commandFinished(int, bool);
+    void done(bool);
+
+private:
+    Q_DISABLE_COPY(QFtp)
+    QScopedPointer<QFtpPrivate> d;
+
+    Q_PRIVATE_SLOT(d, void _q_startNextCommand())
+    Q_PRIVATE_SLOT(d, void _q_piFinished(const QString&))
+    Q_PRIVATE_SLOT(d, void _q_piError(int, const QString&))
+    Q_PRIVATE_SLOT(d, void _q_piConnectState(int))
+    Q_PRIVATE_SLOT(d, void _q_piFtpReply(int, const QString&))
+};
+
+QT_END_HEADER
+
+#endif // QFTP_H
diff --git a/qsstv/utils/qjp2io.cpp b/qsstv/utils/qjp2io.cpp
new file mode 100644
index 0000000..7d3a72d
--- /dev/null
+++ b/qsstv/utils/qjp2io.cpp
@@ -0,0 +1,400 @@
+/*****************************************************************************
+ JPEG 2000 support for Qt based on JasPer lib
+ Copyright (c) 2003 by Dmitry V. Fedorov <www.dimin.net>
+
+ This wrapper is in it's initial state, it's quite slow now because
+ of something, so soon will fix this performance probs...
+
+ IMPLEMENTATION
+
+ Programmer: Dima V. Fedorov <mailto:dima at dimin.net> <http://www.dimin.net/>
+
+ History:
+   13/06/2003 17:02 - First creation
+
+
+ Ver : 1
+*****************************************************************************/
+#include "qsstvdefs.h"
+#include <QImage>
+#include <QImageReader>
+#include <QImageWriter>
+#include <QFile>
+#include <QColor>
+
+#include <QDebug>
+
+#include <jasper/jasper.h>
+
+
+#include "qjp2io.h"
+
+
+QImage readJP2Image(QString fileName)
+{
+
+  // pass on file name if known
+  QFile f(fileName);
+  long y;
+  jas_image_t *jp2_image;
+  jas_matrix_t *pixels[4];
+  jas_stream_t *jp2_stream;
+  int format;
+
+  register unsigned long i, x;
+  int components[4];
+  unsigned long number_components;
+  unsigned int channel_scale[4], maximum_component_depth=8;
+
+  int_fast32_t height = 0;
+  int_fast32_t width = 0;
+  uchar *px;
+  uchar pixel[4];
+  
+  // Initialize JPEG 2000 API.
+  jas_init();  // always returns 0
+
+  QByteArray ba = fileName.toLocal8Bit();
+  const char *fn = ba.data();
+//  qDebug() << "fn:" << fn;
+
+  if ( !(jp2_stream = jas_stream_fopen(fn, "rb")) ) return QImage();
+
+  if ((format = jas_image_getfmt(jp2_stream)) < 0)
+    {
+      return QImage(); // Unknown format
+    }
+
+
+  jp2_image = jas_image_decode(jp2_stream, format, 0);
+
+  if (jp2_image == (jas_image_t *) NULL) {
+      (void) jas_stream_close(jp2_stream);
+      return QImage(); //UnableToDecodeImageFile
+    }
+
+
+  switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
+    {
+    case JAS_CLRSPC_FAM_RGB:
+      {
+        components[0]=jas_image_getcmptbytype(jp2_image,
+                                              JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
+        components[1]=jas_image_getcmptbytype(jp2_image,
+                                              JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
+        components[2]=jas_image_getcmptbytype(jp2_image,
+                                              JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
+        if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
+          {
+            (void) jas_stream_close(jp2_stream);
+            jas_image_destroy(jp2_image);
+            return QImage(); //MissingImageChannel
+          }
+        number_components=3;
+        components[3]=jas_image_getcmptbytype(jp2_image,
+                                              JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
+        if (components[3] > 0)
+          {
+            //image->matte=True; // dima - what a hell is this???
+            number_components++;
+          }
+        break;
+      }
+    case JAS_CLRSPC_FAM_GRAY:
+      {
+        components[0]=jas_image_getcmptbytype(jp2_image,
+                                              JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
+        if (components[0] < 0)
+          {
+            (void) jas_stream_close(jp2_stream);
+            jas_image_destroy(jp2_image);
+            return QImage(); //MissingImageChannel
+          }
+        number_components=1;
+        break;
+      }
+    case JAS_CLRSPC_FAM_YCBCR:
+    default:
+      {
+        (void) jas_stream_close(jp2_stream);
+        jas_image_destroy(jp2_image);
+        return QImage(); //ColorspaceModelIsNotSupported
+      }
+    }
+
+  //-------------------------------------------------------------------
+  // Now create the QImage
+  //-------------------------------------------------------------------
+
+  height = jas_image_height(jp2_image);
+  width  = jas_image_width(jp2_image);
+  
+  // test for components geometry
+  for (i=0; i < (ulong) number_components; i++)
+    {
+      if ((jas_image_cmptwidth(jp2_image,components[i]) != width) ||
+          (jas_image_cmptheight(jp2_image,components[i]) != height) ||
+          (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
+          (jas_image_cmpttly(jp2_image,components[i]) != 0) ||
+          (jas_image_cmpthstep(jp2_image,components[i]) != 1) ||
+          (jas_image_cmptvstep(jp2_image,components[i]) != 1) ||
+          (jas_image_cmptsgnd(jp2_image,components[i]) != false))
+        {
+          (void) jas_stream_close(jp2_stream);
+          jas_image_destroy(jp2_image);
+          return QImage(); // IrregularChannelGeometryNotSupported
+        }
+    }
+
+  // now get the max component depth
+  for (i=0; i < (ulong) number_components; i++)
+    {
+      maximum_component_depth = max((unsigned int) jas_image_cmptprec(jp2_image,components[i]),
+                                    maximum_component_depth);
+      pixels[i] = jas_matrix_create(1, (unsigned int) width);
+      if (pixels[i] == (jas_matrix_t *) NULL)
+        {
+          jas_image_destroy(jp2_image);
+          return QImage(); //MemoryAllocationFailed
+        }
+    }
+
+  // we should set it here, since QT works with only 8bit channels, done for now
+  /*
+  if (maximum_component_depth <= 8)
+    image->depth=Min(QuantumDepth,8);
+  else
+    image->depth=Min(QuantumDepth,16);
+  */
+
+  if (number_components == 1)
+    {
+      return QImage();
+    }
+  QImage image(width, height, QImage::Format_RGB32);
+  if(image.isNull()) return QImage();
+
+  for (i=0; i < (ulong) number_components; i++)
+    {
+      channel_scale[i]=1;
+      if (jas_image_cmptprec(jp2_image,components[i]) < 16)
+        channel_scale[i]=
+            (1 << (16-jas_image_cmptprec(jp2_image,components[i])))+1;
+    }
+
+  //-------------------------------------------------------------------
+  // Now copy image data
+  //-------------------------------------------------------------------
+  
+  for (y=0; y < (long) height; y++)
+    {
+      for (i=0; i < number_components; i++)
+        jas_image_readcmpt(jp2_image, components[i], 0, y, width, 1, pixels[i]);
+
+      px = image.scanLine(y);
+
+      switch (number_components)
+        {
+
+        case 1: // GRAY SCALE IMAGE
+          {
+            for (x=0; x < (ulong) width; x++)
+              {
+                px[x] = (unsigned char) jas_matrix_getv(pixels[0],x)*channel_scale[0];
+              }
+
+            break;
+          }
+
+        case 3: // RGB
+          {
+
+            for (x=0; x < (ulong) width; x++)
+              {
+                QRgb *row = (QRgb*) px;
+
+                pixel[0] = (jas_matrix_getv(pixels[0],x)*channel_scale[0]);
+                pixel[1] = (jas_matrix_getv(pixels[1],x)*channel_scale[1]);
+                pixel[2] = (jas_matrix_getv(pixels[2],x)*channel_scale[2]);
+
+                row[x] = qRgb(pixel[0], pixel[1], pixel[2] );
+              }
+            break;
+          }
+        case 4: // RGBA
+          {
+            for (x=0; x < (ulong) width; x++)
+              {
+                QRgb *row = (QRgb*) px;
+
+                pixel[0] = (jas_matrix_getv(pixels[0],x)*channel_scale[0]);
+                pixel[1] = (jas_matrix_getv(pixels[1],x)*channel_scale[1]);
+                pixel[2] = (jas_matrix_getv(pixels[2],x)*channel_scale[2]);
+                pixel[3] = (jas_matrix_getv(pixels[3],x)*channel_scale[3]);
+
+                row[x] = qRgba(pixel[0], pixel[1], pixel[2], pixel[3]);
+              }
+            break;
+          }
+        }
+
+    }
+  
+  // clean up all mess in memory
+  (void) jas_stream_close(jp2_stream);
+  jas_image_destroy(jp2_image);
+  for (i=0; i < (ulong) number_components; i++)
+    jas_matrix_destroy(pixels[i]);
+
+  jas_init();
+
+  return image;
+}
+
+//! QImageIO write handler for TIFF files.
+//
+bool writeJP2Image(QImage img, QString fileName,double ratio)
+{
+  QFile f(fileName);
+  char options[MaxTextExtent];
+  double rate;
+  int format;
+  ulong y;
+  jas_image_cmptparm_t component_info;
+  jas_image_t *jp2_image;
+  jas_matrix_t *pixels[4];
+  jas_stream_t *jp2_stream;
+  register unsigned long i, x;
+  int status;
+  unsigned long number_components;
+
+  uchar *px;
+
+
+  // Intialize JasPer JPEG 2000 API
+  jas_init();
+  QByteArray ba = fileName.toLocal8Bit();
+  const char *fn = ba.data();
+//  qDebug() << "fn:" << fn;
+
+  if ( !(jp2_stream = jas_stream_fopen(fn, "wb")) ) return false;
+
+  if (img.depth() == 8)
+    number_components = 1;
+  else
+    number_components = 4;
+
+  jp2_image=jas_image_create0();
+  if (jp2_image == (jas_image_t *) NULL) return false; //UnableToCreateImage
+
+
+  for (i=0; i < (ulong) number_components; i++)
+    {
+      (void) memset((void *) &component_info,0,sizeof(jas_image_cmptparm_t));
+      component_info.tlx=0;
+      component_info.tly=0;
+      component_info.hstep=1;
+      component_info.vstep=1;
+      component_info.width=(unsigned int) img.width();
+      component_info.height=(unsigned int) img.height();
+      component_info.prec=(unsigned int) 8;//img.depth() <= 8 ? 8 : 16;
+      component_info.sgnd=false;
+      if (jas_image_addcmpt(jp2_image,i,&component_info))
+        {
+          jas_image_destroy(jp2_image);
+          return false; //UnableToCreateImageComponent
+        }
+    }
+
+  if (number_components == 1)
+    {
+      //  sRGB Grayscale
+      jas_image_setclrspc(jp2_image, JAS_CLRSPC_SGRAY);
+      jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
+    }
+  else
+    {
+      //  sRGB
+      jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB);
+      jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
+      jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
+      jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
+      if (number_components == 4 )
+        jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
+    }
+
+
+  //  Convert to JPEG 2000 pixels.
+  for (i=0; i < (ulong) number_components; i++)
+    {
+      pixels[i] = jas_matrix_create(1, (unsigned int) img.width());
+      if (pixels[i] == (jas_matrix_t *) NULL)
+        {
+          for (x=0; x < i; x++) jas_matrix_destroy(pixels[x]);
+          jas_image_destroy(jp2_image);
+          return false; //MemoryAllocationFailed
+        }
+    }
+  
+  
+  // now copy the data itself
+  for (y=0; y < (ulong) img.height(); y++)
+    {
+      px = img.scanLine(y);
+
+      for (x=0; x < (ulong) img.width(); x++)
+        {
+          if (number_components == 1)
+            jas_matrix_setv(pixels[0], x, px[x] );
+          else
+            {
+              QRgb *row = (QRgb*) px;
+
+              jas_matrix_setv(pixels[0], x, qRed(row[x]) );
+              jas_matrix_setv(pixels[1], x, qGreen(row[x]) );
+              jas_matrix_setv(pixels[2], x, qBlue(row[x]) );
+              if (number_components > 3)
+                jas_matrix_setv(pixels[3], x, qAlpha(*(row+x)) );
+            }
+        }
+
+
+      for (i=0; i < (ulong) number_components; i++)
+        jas_image_writecmpt(jp2_image, i, 0, y, img.width(), 1, pixels[i]);
+
+    }  // end of byte data copy
+
+  rate=1;
+  format = jas_image_fmtfromname(fileName.toLatin1().data());
+  double qrate=ratio;
+
+
+  if (img.width()*img.height() > 2500)
+    {
+      double alpha, header_size, number_pixels, target_size;
+
+      alpha=115-100;
+      rate=100.0/(alpha*alpha);
+      header_size=550.0;
+      header_size+=(number_components-1)*142;
+      number_pixels=(double) img.width()*img.height()*(img.depth()/8)*number_components;
+      target_size=(number_pixels*rate)+header_size;
+      rate=target_size/number_pixels;
+    }
+  if(rate>qrate)
+    {
+      rate=qrate;
+    }
+  
+  sprintf(options, "rate=%g", rate);
+  status = jas_image_encode(jp2_image, jp2_stream, format, options);
+  
+  // clean up our mess
+  (void) jas_stream_close(jp2_stream);
+  for (i=0; i < (ulong) number_components; i++) jas_matrix_destroy(pixels[i]);
+  jas_image_destroy(jp2_image);
+
+  if (status == -1) return false; //UnableToEncodeImageFile
+  return true;
+}
+
diff --git a/qsstv/utils/qjp2io.h b/qsstv/utils/qjp2io.h
new file mode 100644
index 0000000..a022b97
--- /dev/null
+++ b/qsstv/utils/qjp2io.h
@@ -0,0 +1,61 @@
+/*****************************************************************************
+ JPEG 2000 support for Qt based on JasPer lib
+ Copyright (c) 2003 by Dmitry V. Fedorov <www.dimin.net>
+
+ DEFINES
+
+ Programmer: Dima V. Fedorov <mailto:dima at dimin.net> <http://www.dimin.net/>
+
+ History:
+   13/06/2003 17:02 - First creation
+   17/12/2013 // changed by ON4QZ
+
+
+ Ver : 1
+*****************************************************************************/
+#ifndef QJP2_H
+#define QJP2_H
+
+#include <QString>
+
+
+/* we better use jasper defined types
+#ifndef int8
+typedef	char int8;
+#endif
+
+#ifndef uint8
+typedef	unsigned char uint8;
+#endif
+
+#ifndef int16
+typedef	short int16;
+#endif
+
+#ifndef uint16
+typedef	unsigned short uint16;
+#endif
+
+#ifndef int32
+typedef	long int32;
+#endif
+
+#ifndef uint32
+typedef	unsigned long uint32;	// sizeof (uint32) must == 4
+#endif
+*/
+
+#ifndef max
+#define max(a,b)      (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)      (((a) < (b)) ? (a) : (b))
+#endif
+
+#define MaxTextExtent 2053 
+
+QImage readJP2Image(QString fileName);
+bool writeJP2Image(QImage im, QString fileName, double ratio);
+
+#endif
diff --git a/qsstv/utils/qurlinfo.cpp b/qsstv/utils/qurlinfo.cpp
new file mode 100644
index 0000000..3429542
--- /dev/null
+++ b/qsstv/utils/qurlinfo.cpp
@@ -0,0 +1,728 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qurlinfo.h"
+
+#include "qurl.h"
+#include "qdir.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUrlInfoPrivate
+{
+public:
+    QUrlInfoPrivate() :
+        permissions(0),
+        size(0),
+        isDir(false),
+        isFile(true),
+        isSymLink(false),
+        isWritable(true),
+        isReadable(true),
+        isExecutable(false)
+    {}
+
+    QString name;
+    int permissions;
+    QString owner;
+    QString group;
+    qint64 size;
+
+    QDateTime lastModified;
+    QDateTime lastRead;
+    bool isDir;
+    bool isFile;
+    bool isSymLink;
+    bool isWritable;
+    bool isReadable;
+    bool isExecutable;
+};
+
+
+/*!
+    \class QUrlInfo
+    \brief The QUrlInfo class stores information about URLs.
+
+    \ingroup io
+    \ingroup network
+    \inmodule QtNetwork
+
+    The information about a URL that can be retrieved includes name(),
+    permissions(), owner(), group(), size(), lastModified(),
+    lastRead(), isDir(), isFile(), isSymLink(), isWritable(),
+    isReadable() and isExecutable().
+
+    You can create your own QUrlInfo objects passing in all the
+    relevant information in the constructor, and you can modify a
+    QUrlInfo; for each getter mentioned above there is an equivalent
+    setter. Note that setting values does not affect the underlying
+    resource that the QUrlInfo provides information about; for example
+    if you call setWritable(true) on a read-only resource the only
+    thing changed is the QUrlInfo object, not the resource.
+
+    \sa QUrl, {FTP Example}
+*/
+
+/*!
+    \enum QUrlInfo::PermissionSpec
+
+    This enum is used by the permissions() function to report the
+    permissions of a file.
+
+    \value ReadOwner The file is readable by the owner of the file.
+    \value WriteOwner The file is writable by the owner of the file.
+    \value ExeOwner The file is executable by the owner of the file.
+    \value ReadGroup The file is readable by the group.
+    \value WriteGroup The file is writable by the group.
+    \value ExeGroup The file is executable by the group.
+    \value ReadOther The file is readable by anyone.
+    \value WriteOther The file is writable by anyone.
+    \value ExeOther The file is executable by anyone.
+*/
+
+/*!
+    Constructs an invalid QUrlInfo object with default values.
+
+    \sa isValid()
+*/
+
+QUrlInfo::QUrlInfo()
+{
+    d = 0;
+}
+
+/*!
+    Copy constructor, copies \a ui to this URL info object.
+*/
+
+QUrlInfo::QUrlInfo(const QUrlInfo &ui)
+{
+    if (ui.d) {
+        d = new QUrlInfoPrivate;
+        *d = *ui.d;
+    } else {
+        d = 0;
+    }
+}
+
+/*!
+    Constructs a QUrlInfo object by specifying all the URL's
+    information.
+
+    The information that is passed is the \a name, file \a
+    permissions, \a owner and \a group and the file's \a size. Also
+    passed is the \a lastModified date/time and the \a lastRead
+    date/time. Flags are also passed, specifically, \a isDir, \a
+    isFile, \a isSymLink, \a isWritable, \a isReadable and \a
+    isExecutable.
+*/
+
+QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner,
+                    const QString &group, qint64 size, const QDateTime &lastModified,
+                    const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+                    bool isWritable, bool isReadable, bool isExecutable)
+{
+    d = new QUrlInfoPrivate;
+    d->name = name;
+    d->permissions = permissions;
+    d->owner = owner;
+    d->group = group;
+    d->size = size;
+    d->lastModified = lastModified;
+    d->lastRead = lastRead;
+    d->isDir = isDir;
+    d->isFile = isFile;
+    d->isSymLink = isSymLink;
+    d->isWritable = isWritable;
+    d->isReadable = isReadable;
+    d->isExecutable = isExecutable;
+}
+
+
+/*!
+    Constructs a QUrlInfo object by specifying all the URL's
+    information.
+
+    The information that is passed is the \a url, file \a
+    permissions, \a owner and \a group and the file's \a size. Also
+    passed is the \a lastModified date/time and the \a lastRead
+    date/time. Flags are also passed, specifically, \a isDir, \a
+    isFile, \a isSymLink, \a isWritable, \a isReadable and \a
+    isExecutable.
+*/
+
+QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner,
+                    const QString &group, qint64 size, const QDateTime &lastModified,
+                    const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+                    bool isWritable, bool isReadable, bool isExecutable)
+{
+    d = new QUrlInfoPrivate;
+    d->name = QFileInfo(url.path()).fileName();
+    d->permissions = permissions;
+    d->owner = owner;
+    d->group = group;
+    d->size = size;
+    d->lastModified = lastModified;
+    d->lastRead = lastRead;
+    d->isDir = isDir;
+    d->isFile = isFile;
+    d->isSymLink = isSymLink;
+    d->isWritable = isWritable;
+    d->isReadable = isReadable;
+    d->isExecutable = isExecutable;
+}
+
+
+/*!
+    Sets the name of the URL to \a name. The name is the full text,
+    for example, "http://qt.nokia.com/doc/qurlinfo.html".
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setName(const QString &name)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->name = name;
+}
+
+
+/*!
+    If \a b is true then the URL is set to be a directory; if \a b is
+    false then the URL is set not to be a directory (which normally
+    means it is a file). (Note that a URL can refer to both a file and
+    a directory even though most file systems do not support this.)
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setDir(bool b)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->isDir = b;
+}
+
+
+/*!
+    If \a b is true then the URL is set to be a file; if \b is false
+    then the URL is set not to be a file (which normally means it is a
+    directory). (Note that a URL can refer to both a file and a
+    directory even though most file systems do not support this.)
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setFile(bool b)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->isFile = b;
+}
+
+
+/*!
+    Specifies that the URL refers to a symbolic link if \a b is true
+    and that it does not if \a b is false.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setSymLink(bool b)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->isSymLink = b;
+}
+
+
+/*!
+    Specifies that the URL is writable if \a b is true and not
+    writable if \a b is false.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setWritable(bool b)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->isWritable = b;
+}
+
+
+/*!
+    Specifies that the URL is readable if \a b is true and not
+    readable if \a b is false.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setReadable(bool b)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->isReadable = b;
+}
+
+/*!
+    Specifies that the owner of the URL is called \a s.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setOwner(const QString &s)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->owner = s;
+}
+
+/*!
+    Specifies that the owning group of the URL is called \a s.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setGroup(const QString &s)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->group = s;
+}
+
+/*!
+    Specifies the \a size of the URL.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setSize(qint64 size)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->size = size;
+}
+
+/*!
+    Specifies that the URL has access permissions \a p.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setPermissions(int p)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->permissions = p;
+}
+
+/*!
+    Specifies that the object the URL refers to was last modified at
+    \a dt.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setLastModified(const QDateTime &dt)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->lastModified = dt;
+}
+
+/*!
+    \since 4.4
+
+    Specifies that the object the URL refers to was last read at
+    \a dt.
+
+    If you call this function for an invalid URL info, this function
+    turns it into a valid one.
+
+    \sa isValid()
+*/
+
+void QUrlInfo::setLastRead(const QDateTime &dt)
+{
+    if (!d)
+        d = new QUrlInfoPrivate;
+    d->lastRead = dt;
+}
+
+/*!
+    Destroys the URL info object.
+*/
+
+QUrlInfo::~QUrlInfo()
+{
+    delete d;
+}
+
+/*!
+    Assigns the values of \a ui to this QUrlInfo object.
+*/
+
+QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui)
+{
+    if (ui.d) {
+        if (!d)
+            d= new QUrlInfoPrivate;
+        *d = *ui.d;
+    } else {
+        delete d;
+        d = 0;
+    }
+    return *this;
+}
+
+/*!
+    Returns the file name of the URL.
+
+    \sa isValid()
+*/
+
+QString QUrlInfo::name() const
+{
+    if (!d)
+        return QString();
+    return d->name;
+}
+
+/*!
+    Returns the permissions of the URL. You can use the \c PermissionSpec flags
+    to test for certain permissions.
+
+    \sa isValid()
+*/
+
+int QUrlInfo::permissions() const
+{
+    if (!d)
+        return 0;
+    return d->permissions;
+}
+
+/*!
+    Returns the owner of the URL.
+
+    \sa isValid()
+*/
+
+QString QUrlInfo::owner() const
+{
+    if (!d)
+        return QString();
+    return d->owner;
+}
+
+/*!
+    Returns the group of the URL.
+
+    \sa isValid()
+*/
+
+QString QUrlInfo::group() const
+{
+    if (!d)
+        return QString();
+    return d->group;
+}
+
+/*!
+    Returns the size of the URL.
+
+    \sa isValid()
+*/
+
+qint64 QUrlInfo::size() const
+{
+    if (!d)
+        return 0;
+    return d->size;
+}
+
+/*!
+    Returns the last modification date of the URL.
+
+    \sa isValid()
+*/
+
+QDateTime QUrlInfo::lastModified() const
+{
+    if (!d)
+        return QDateTime();
+    return d->lastModified;
+}
+
+/*!
+    Returns the date when the URL was last read.
+
+    \sa isValid()
+*/
+
+QDateTime QUrlInfo::lastRead() const
+{
+    if (!d)
+        return QDateTime();
+    return d->lastRead;
+}
+
+/*!
+    Returns true if the URL is a directory; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isDir() const
+{
+    if (!d)
+        return false;
+    return d->isDir;
+}
+
+/*!
+    Returns true if the URL is a file; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isFile() const
+{
+    if (!d)
+        return false;
+    return d->isFile;
+}
+
+/*!
+    Returns true if the URL is a symbolic link; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isSymLink() const
+{
+    if (!d)
+        return false;
+    return d->isSymLink;
+}
+
+/*!
+    Returns true if the URL is writable; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isWritable() const
+{
+    if (!d)
+        return false;
+    return d->isWritable;
+}
+
+/*!
+    Returns true if the URL is readable; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isReadable() const
+{
+    if (!d)
+        return false;
+    return d->isReadable;
+}
+
+/*!
+    Returns true if the URL is executable; otherwise returns false.
+
+    \sa isValid()
+*/
+
+bool QUrlInfo::isExecutable() const
+{
+    if (!d)
+        return false;
+    return d->isExecutable;
+}
+
+/*!
+    Returns true if \a i1 is greater than \a i2; otherwise returns
+    false. The objects are compared by the value, which is specified
+    by \a sortBy. This must be one of QDir::Name, QDir::Time or
+    QDir::Size.
+*/
+
+bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
+                            int sortBy)
+{
+    switch (sortBy) {
+    case QDir::Name:
+        return i1.name() > i2.name();
+    case QDir::Time:
+        return i1.lastModified() > i2.lastModified();
+    case QDir::Size:
+        return i1.size() > i2.size();
+    default:
+        return false;
+    }
+}
+
+/*!
+    Returns true if \a i1 is less than \a i2; otherwise returns false.
+    The objects are compared by the value, which is specified by \a
+    sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
+*/
+
+bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
+                         int sortBy)
+{
+    return !greaterThan(i1, i2, sortBy);
+}
+
+/*!
+    Returns true if \a i1 equals to \a i2; otherwise returns false.
+    The objects are compared by the value, which is specified by \a
+    sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
+*/
+
+bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2,
+                      int sortBy)
+{
+    switch (sortBy) {
+    case QDir::Name:
+        return i1.name() == i2.name();
+    case QDir::Time:
+        return i1.lastModified() == i2.lastModified();
+    case QDir::Size:
+        return i1.size() == i2.size();
+    default:
+        return false;
+    }
+}
+
+/*!
+    Returns true if this QUrlInfo is equal to \a other; otherwise
+    returns false.
+
+    \sa lessThan(), equal()
+*/
+
+bool QUrlInfo::operator==(const QUrlInfo &other) const
+{
+    if (!d)
+        return other.d == 0;
+    if (!other.d)
+        return false;
+
+    return (d->name == other.d->name &&
+            d->permissions == other.d->permissions &&
+            d->owner == other.d->owner &&
+            d->group == other.d->group &&
+            d->size == other.d->size &&
+            d->lastModified == other.d->lastModified &&
+            d->lastRead == other.d->lastRead &&
+            d->isDir == other.d->isDir &&
+            d->isFile == other.d->isFile &&
+            d->isSymLink == other.d->isSymLink &&
+            d->isWritable == other.d->isWritable &&
+            d->isReadable == other.d->isReadable &&
+            d->isExecutable == other.d->isExecutable);
+}
+
+/*!
+    \fn bool QUrlInfo::operator!=(const QUrlInfo &other) const
+    \since 4.2
+
+    Returns true if this QUrlInfo is not equal to \a other; otherwise
+    returns false.
+
+    \sa lessThan(), equal()
+*/
+
+/*!
+    Returns true if the URL info is valid; otherwise returns false.
+    Valid means that the QUrlInfo contains real information.
+
+    You should always check if the URL info is valid before relying on
+    the values.
+*/
+bool QUrlInfo::isValid() const
+{
+    return d != 0;
+}
+
+QT_END_NAMESPACE
diff --git a/qsstv/utils/qurlinfo.h b/qsstv/utils/qurlinfo.h
new file mode 100644
index 0000000..b182690
--- /dev/null
+++ b/qsstv/utils/qurlinfo.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QURLINFO_H
+#define QURLINFO_H
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qiodevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+
+class QUrl;
+class QUrlInfoPrivate;
+
+class QUrlInfo
+{
+public:
+    enum PermissionSpec {
+        ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100,
+        ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010,
+        ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 };
+
+    QUrlInfo();
+    QUrlInfo(const QUrlInfo &ui);
+    QUrlInfo(const QString &name, int permissions, const QString &owner,
+             const QString &group, qint64 size, const QDateTime &lastModified,
+             const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+             bool isWritable, bool isReadable, bool isExecutable);
+    QUrlInfo(const QUrl &url, int permissions, const QString &owner,
+             const QString &group, qint64 size, const QDateTime &lastModified,
+             const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+             bool isWritable, bool isReadable, bool isExecutable);
+    QUrlInfo &operator=(const QUrlInfo &ui);
+    virtual ~QUrlInfo();
+
+    virtual void setName(const QString &name);
+    virtual void setDir(bool b);
+    virtual void setFile(bool b);
+    virtual void setSymLink(bool b);
+    virtual void setOwner(const QString &s);
+    virtual void setGroup(const QString &s);
+    virtual void setSize(qint64 size);
+    virtual void setWritable(bool b);
+    virtual void setReadable(bool b);
+    virtual void setPermissions(int p);
+    virtual void setLastModified(const QDateTime &dt);
+    void setLastRead(const QDateTime &dt);
+
+    bool isValid() const;
+
+    QString name() const;
+    int permissions() const;
+    QString owner() const;
+    QString group() const;
+    qint64 size() const;
+    QDateTime lastModified() const;
+    QDateTime lastRead() const;
+    bool isDir() const;
+    bool isFile() const;
+    bool isSymLink() const;
+    bool isWritable() const;
+    bool isReadable() const;
+    bool isExecutable() const;
+
+    static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
+                             int sortBy);
+    static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
+                          int sortBy);
+    static bool equal(const QUrlInfo &i1, const QUrlInfo &i2,
+                       int sortBy);
+
+    bool operator==(const QUrlInfo &i) const;
+    inline bool operator!=(const QUrlInfo &i) const
+    { return !operator==(i); }
+
+private:
+    QUrlInfoPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QURLINFO_H
diff --git a/qsstv/utils/reedsolomoncoder.cpp b/qsstv/utils/reedsolomoncoder.cpp
new file mode 100644
index 0000000..932a09b
--- /dev/null
+++ b/qsstv/utils/reedsolomoncoder.cpp
@@ -0,0 +1,325 @@
+#include "reedsolomoncoder.h"
+#include <QFileInfo>
+#include <QDebug>
+#include "rs.h"
+#include "configparams.h"
+
+#define RSBSIZE 255
+#define rse32 encode_rs
+#define rsd32 eras_dec_rs
+#define ENCODE 0
+#define DECODE 1
+
+QString rsTypeStr[RST4+1]=
+{
+  "",
+  "rs1",
+  "rs2",
+  "rs3",
+  "rs4"
+};
+
+reedSolomonCoder::reedSolomonCoder()
+{
+  zeroPositions=NULL;
+  newZeroPositions=NULL;
+}
+
+reedSolomonCoder::~reedSolomonCoder()
+{
+  if(zeroPositions!=NULL) delete zeroPositions;
+  if(newZeroPositions!=NULL) delete newZeroPositions;
+}
+
+void reedSolomonCoder::init()
+{
+  ec_buf.clear();  /* pointer to encoding/decoding buffer */
+  tr_buf.clear();  /* pointer to transmit-buffer (fread/fwrite) */
+  bk_buf.clear();  /* pointer to backup-buffer for resync */
+
+  rs_bsize=0;
+  rs_dsize=0;
+  bep_size=0;
+  sumOfFailures=0;
+  uncorrectableFailures=0;
+  if(zeroPositions!=NULL) delete zeroPositions;
+  if(newZeroPositions!=NULL) delete newZeroPositions;
+}
+
+
+// if the extension is not rs1,rs2 or rs3 then newFilename is set to fn and return value is true
+
+bool reedSolomonCoder::decode(QByteArray &ba,QString fn,QString &newFileName,QByteArray &baFile,QString extension,QList<int> &erasuresArray)
+{
+
+  int i,j;
+  int startOfSegment,row;
+//  QByteArray *t;
+
+  init();
+//  qDebug() << "size of ba" << ba.size();
+  origFileName=fn;
+  fpin.setFileName(fn);
+
+  if(extension=="rs1") fileType=RST1;
+  else if(extension=="rs2") fileType=RST2;
+  else if(extension=="rs3") fileType=RST3;
+  else if(extension=="rs4") fileType=RST4;
+  else
+    {
+      return false;
+    }
+  tr_buf=ba;
+  got=tr_buf.size();
+  bep_size = got/RSBSIZE;
+  if (got % RSBSIZE)
+    {
+      bep_size++ ;
+      tr_buf=tr_buf.leftJustified(bep_size*RSBSIZE,'\0');
+    }
+//  qDebug() << "got" << got << "bep_size" << bep_size;
+  int rest=tr_buf.count()%64;
+   if(rest!=0)
+     {
+       tr_buf=tr_buf.leftJustified(bep_size*RSBSIZE+(64-rest),'\0');
+//       qDebug() << "size of tr_buf extended" << tr_buf.count();
+
+   }
+  ec_buf.resize(bep_size*RSBSIZE);
+  bk_buf.resize(bep_size*RSBSIZE);
+
+  rs_bsize=RSBSIZE;
+  switch (fileType)
+    {
+    case RST1: rs_dsize=RSDSIZERS1; break;
+    case RST2: rs_dsize=RSDSIZERS2; break;
+    case RST3: rs_dsize=RSDSIZERS3; break;
+    case RST4: rs_dsize=RSDSIZERS4; break;
+    case RSTNONE: return false;
+    }
+  init_rs(rs_dsize);
+  // setup erasure info
+  numMissing=0;
+  if(erasuresArray.count()>2)  // we have erasure positions
+    {
+      totalSegments=erasuresArray.at(0);
+      segmentLength=erasuresArray.at(1);
+      numMissing=erasuresArray.count()-2;
+//      qDebug() << "numMissing" << numMissing;
+      if(zeroPositions) delete zeroPositions;
+      if(newZeroPositions) delete newZeroPositions;
+      zeroPositions=new int[segmentLength*(totalSegments+1)];
+      newZeroPositions=new int[256*bep_size];
+//      qDebug() << "newZeroPositions len" << (segmentLength*(totalSegments+1));
+      for(i=0;i<(segmentLength*totalSegments);i++) zeroPositions[i]=-1;
+    }
+  else
+    {
+      qDebug() << "no erasure info";
+      return false;
+    }
+  /* now label the erasures positions */
+  for (i= 0 ; i < numMissing; i++)
+    {
+      startOfSegment = erasuresArray.at(i+2)*segmentLength ;  // +2 because of header in array
+      for (j=0; j < segmentLength ; j++)
+        {
+          row = (startOfSegment +j ) / bep_size;
+          /* if ( row < rs_dsize)  */
+          zeroPositions[startOfSegment+j] = row;
+        }
+    }
+  /*  distribute version pa0mbo for the indexes */
+  int *pointzero=newZeroPositions;
+  for (i=0; i < bep_size ; i++)
+    {
+      for (j=0; j < 255 ; j++)
+        {
+          *(pointzero++) = *(zeroPositions + j*bep_size + i);
+        }
+    }
+
+  distribute((byte *)tr_buf.data(),(byte *)ec_buf.data(),bep_size,rs_bsize,DECODE);
+  if(!decode_and_write())
+    {
+//      fpout.close();
+      return false;
+    }
+  //  fpin.close();
+//  fpout.close();
+//  tr_buf=ec_buf;
+  if(uncorrectableFailures>0) return false;
+//  if(fpout.open(QIODevice::ReadOnly)<=0) return false;
+//  tr_buf=fpout.readAll();
+  if (bep_size != (((unsigned char) tr_buf[1]) + ((unsigned char) tr_buf[2])*256 ))
+    {
+      qDebug()<< "problems with bep_size coded in file";
+      qDebug() << "bep_size: " << bep_size <<" coded size: " << (((unsigned char) tr_buf[1]) + ((unsigned char) tr_buf[2])*256 ) ;
+      qDebug() << "bep_sizeaa: " << bep_size <<" coded size: " << (((unsigned char) ba[1]) + ((unsigned char)ba[2])*256 ) ;
+      return false;
+    }
+  coded_file_size = bep_size*rs_dsize - (int)tr_buf[0];
+  strncpy(coded_file_ext, tr_buf.data()+3,3);
+  coded_file_ext[3]=0;
+
+
+  QFileInfo fileInfo(origFileName);
+  QString baseName=rxImagesPath+"/"+fileInfo.completeBaseName();
+  baseName.append(".");
+  baseName.append(coded_file_ext);
+
+//  qDebug() <<" new filename: " << baseName;
+//  qDebug() << "tr_buf count before truncation: " << tr_buf.count();
+  tr_buf=tr_buf.right(tr_buf.count()-7);
+  tr_buf=tr_buf.left(coded_file_size);
+//  qDebug() << "coded_file_size: " << coded_file_size << "tr_buf count: " << tr_buf.count();
+
+  newFileName=baseName;
+//  t=&tr_buf;
+  baFile=tr_buf;
+//  fpout.close();
+  if(uncorrectableFailures>0)
+    {
+//      qDebug() <<"uncorrectable failures:" << uncorrectableFailures;
+      return false;
+    }
+
+  return true;
+}
+
+
+void reedSolomonCoder::distribute(byte *src, byte *dst, int rows, int cols, int reverse)
+{
+  unsigned int i,j,rc,ri,rl;
+  rc=rows*cols;
+  ri=0;
+  rl = reverse ? cols : rows;
+
+
+  for(i=0;i<rc;i+=64) // ON4QZ changed from original- > using union gives problems with allignement
+    {
+      for(j=0;j<64 && i+j<rc;j++)
+        {
+          *(dst+ri)=*(src+i+j);
+          ri+=rl; /* next position in dst */
+          if(ri>=rc)
+            {
+              ri-=rc-1;  /* go around and add one. */
+            }
+        }
+    }
+
+}
+
+
+
+/* decode buffer and write to fpout */
+
+bool reedSolomonCoder::decode_and_write()
+{
+
+  register int i, j;
+  int nr_erasures;
+  int eras_pos[255];
+  tr_buf.clear();
+
+  for(i=0;i<bep_size;i++)
+    {
+      /* get the erasure positions and their number */
+      nr_erasures =0;
+      for (j=0; j < rs_bsize ; j++)
+        {
+          if (newZeroPositions[i*rs_bsize + j] != -1)
+            {
+              eras_pos[nr_erasures++]= newZeroPositions[i*rs_bsize+j] ;
+              if(nr_erasures>(rs_bsize-rs_dsize))
+                {
+//                  uncorrectableFailures++;
+//                  qDebug() << "ersasures too high" << nr_erasures;
+                  return false;
+                }
+             }
+        }
+//      qDebug()<< "block: " << i << "nr_erasures" << nr_erasures;
+      if(nr_erasures>(rs_bsize-rs_dsize)) nr_erasures=rs_bsize-rs_dsize-1;
+      int failure=rsd32(((byte *)ec_buf.data()+(i*rs_bsize)),eras_pos, nr_erasures);
+      if (failure>0)
+        {
+
+          sumOfFailures+=failure;
+        }
+      else if (failure==-1)
+        {
+
+          uncorrectableFailures++;
+          return false;
+        }
+//      qDebug() <<"block: " << i << "nr_erasures:" << nr_erasures << "failure corrected:" << failure << "uncorrectable failure" << uncorrectableFailures;
+//      qDebug() << "ec_buf len" << ec_buf.count();
+        tr_buf.append(ec_buf.data()+i*rs_bsize,rs_dsize);
+//      fpout.write(ec_buf.data()+i*rs_bsize,rs_dsize);
+//      if(i==0) qDebug() << "rs write" <<((unsigned char) ec_buf[1]) << ((unsigned char)ec_buf[2]);
+    }
+//  qDebug() << "trsize" << tr_buf.count() << "bepsize" << bep_size << "rs_bsize" << rs_bsize << "rs_dsize" << rs_dsize;
+  return true;
+}
+
+bool reedSolomonCoder::encode(QByteArray &ba,QString extension,eRSType rsType)
+{
+  int i,j;
+  unsigned char dataByte;
+  QByteArray temp;
+  tr_buf=ba;
+  fileType=rsType;
+  rs_bsize=RSBSIZE;
+  switch (fileType)
+    {
+    case RST1: rs_dsize=RSDSIZERS1; break;
+    case RST2: rs_dsize=RSDSIZERS2; break;
+    case RST3: rs_dsize=RSDSIZERS3; break;
+    case RST4: rs_dsize=RSDSIZERS4; break;
+    case RSTNONE: return false;
+    }
+  init_rs(rs_dsize);
+  got = tr_buf.size();
+  chunks = (got+7) / rs_dsize ;
+  if (((got+7) % rs_dsize ) > 0) chunks++ ;
+  bep_size=chunks;
+//  ec_buf.resize(bep_size*RSBSIZE);
+  ec_buf.clear();
+  bk_buf.resize(bep_size*RSBSIZE);
+
+  dataByte = (unsigned char) ( rs_dsize - ( got % rs_dsize)) ; /* surplus in filelength */
+  ec_buf.append(dataByte);
+  dataByte = (unsigned char) ( chunks % 256) ;
+  ec_buf.append(dataByte);
+  dataByte = (unsigned char) (chunks/256) ;
+  ec_buf.append(dataByte);
+  ec_buf.append(extension.toLatin1().at(0));newZeroPositions=NULL;
+  ec_buf.append(extension.toLatin1().at(1));
+  ec_buf.append(extension.toLatin1().at(2));
+  dataByte=0;
+  ec_buf.append(dataByte);
+  ec_buf.append(tr_buf.left(rs_dsize-7));
+  ec_buf.resize(ec_buf.count()+RSBSIZE-rs_dsize);
+  rse32(((byte *)ec_buf.data()),((byte *)ec_buf.data()+(rs_dsize)));
+  for (i=1;i<bep_size;i++)
+    {
+      temp=tr_buf.mid(i*rs_dsize-7,rs_dsize);
+      if(temp.count()==0) break;
+      ec_buf.append(temp);
+      if(temp.count()<rs_dsize)
+        {
+          for(j=0;j<(rs_dsize-temp.count());j++)
+            {
+              ec_buf.append((char)0);
+            }
+        }
+      ec_buf.resize(ec_buf.count()+RSBSIZE-rs_dsize);
+      rse32(((byte *)ec_buf.data()+i*rs_bsize),((byte *)ec_buf.data()+i*rs_bsize+rs_dsize));
+    }
+  ba.resize(ec_buf.count());
+  distribute((byte *)ec_buf.data(),(byte *)ba.data(),bep_size,rs_bsize,ENCODE);
+  return true;
+}
+
diff --git a/qsstv/utils/reedsolomoncoder.h b/qsstv/utils/reedsolomoncoder.h
new file mode 100644
index 0000000..dcfe7a9
--- /dev/null
+++ b/qsstv/utils/reedsolomoncoder.h
@@ -0,0 +1,54 @@
+#ifndef REEDSOLOMONCODER_H
+#define REEDSOLOMONCODER_H
+#include "qsstvglobal.h"
+#include "qsstvdefs.h"
+#include <QFile>
+#include <QByteArray>
+
+enum eRSType {RSTNONE,RST1,RST2,RST3,RST4};
+
+extern QString rsTypeStr[RST4+1];
+
+union long_byte_union
+{
+        quint64 i;
+        unsigned char b[8];
+};
+
+
+class reedSolomonCoder
+{
+public:
+  reedSolomonCoder();
+  ~reedSolomonCoder();
+  void init();
+  bool decode(QByteArray &ba, QString fn, QString &newFileName, QByteArray &baFile, QString extension, QList<int> &erasuresArray);
+  bool encode(QByteArray &ba, QString extension, eRSType rsType);
+private:
+  void distribute(byte *src, byte *dst, int rows, int cols, int reverse);
+  bool decode_and_write();
+  QByteArray ec_buf;  /* pointer to encoding/decoding buffer */
+  QByteArray tr_buf;  /* pointer to transmit-buffer (fread/fwrite) */
+  QByteArray bk_buf; /* pointer to backup-buffer for resync */
+  int rs_bsize;
+  int rs_dsize;
+  int bep_size;
+  unsigned long sumOfFailures;
+  unsigned long uncorrectableFailures;
+  int k;
+  QFile fpin, fpout;
+  long got,chunks;
+  int coded_file_size ;
+  char coded_file_ext[4] ;
+  QString origFileName;
+  char *p ;
+  eRSType fileType;
+  int totalSegments;
+  int segmentLength;
+  int *zeroPositions;
+  int *newZeroPositions;
+  int numMissing;
+};
+
+#endif // REEDSOLOMONCODER_H
+
diff --git a/qsstv/utils/rs.cpp b/qsstv/utils/rs.cpp
new file mode 100644
index 0000000..029b1f0
--- /dev/null
+++ b/qsstv/utils/rs.cpp
@@ -0,0 +1,462 @@
+/*
+ * Reed-Solomon coding and decoding
+ * Phil Karn (karn at ka9q.ampr.org) September 1996
+ * Separate CCSDS version create Dec 1998, merged into this version May 1999
+ * 
+ * This file is derived from my generic RS encoder/decoder, which is
+ * in turn based on the program "new_rs_erasures.c" by Robert
+ * Morelos-Zaragoza (robert at spectra.eng.hawaii.edu) and Hari Thirumoorthy
+ * (harit at spectra.eng.hawaii.edu), Aug 1995
+ 
+ * Copyright 1999 Phil Karn, KA9Q
+ * May be used under the terms of the GNU public license
+ */
+#include <stdio.h>
+#include "rs.h"
+
+static int KK;
+
+/* MM, KK, B0, PRIM are user-defined in rs.h */
+
+/* Primitive polynomials - see Lin & Costello, Appendix A,
+ * and  Lee & Messerschmitt, p. 453.
+ */
+
+/* 1+x^2+x^3+x^4+x^8 */
+int Pp[MM+1] = { 1, 0, 1, 1, 1, 0, 0, 0, 1 };
+
+
+
+/* This defines the type used to store an element of the Galois Field
+ * used by the code. Make sure this is something larger than a char if
+ * if anything larger than GF(256) is used.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium.
+ */
+typedef int gf;
+
+/* index->polynomial form conversion table */
+static gf Alpha_to[NN + 1];
+
+/* Polynomial->index form conversion table */
+static gf Index_of[NN + 1];
+
+/* No legal value in index form represents zero, so
+ * we need a special value for this purpose
+ */
+#define A0	(NN)
+
+/* Generator polynomial g(x) in index form */
+//static gf Gg[NN - KK + 1];
+static gf Gg[NN-RSDSIZERS4+1]; //worst case
+static int RS_init=0; /* Initialization flag */
+
+/* Compute x % NN, where NN is 2**MM - 1,
+ * without a slow divide
+ */
+static  gf
+modnn(int x)
+{
+  while (x >= NN) {
+    x -= NN;
+    x = (x >> MM) + (x & NN);
+  }
+  return x;
+}
+
+#define	min(a,b)	((a) < (b) ? (a) : (b))
+
+#define	CLEAR(a,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = 0;\
+}
+
+#define	COPY(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define	COPYDOWN(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define Ldec 1
+
+
+/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
+   lookup tables:  index->polynomial form   alpha_to[] contains j=alpha**i;
+                   polynomial form -> index form  index_of[j=alpha**i] = i
+   alpha=2 is the primitive element of GF(2**m)
+   HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
+        Let @ represent the primitive element commonly called "alpha" that
+   is the root of the primitive polynomial p(x). Then in GF(2^m), for any
+   0 <= i <= 2^m-2,
+        @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+   where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
+   of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
+   example the polynomial representation of @^5 would be given by the binary
+   representation of the integer "alpha_to[5]".
+                   Similarily, index_of[] can be used as follows:
+        As above, let @ represent the primitive element of GF(2^m) that is
+   the root of the primitive polynomial p(x). In order to find the power
+   of @ (alpha) that has the polynomial representation
+        a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+   we consider the integer "i" whose binary representation with a(0) being LSB
+   and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
+   "index_of[i]". Now, @^index_of[i] is that element whose polynomial 
+    representation is (a(0),a(1),a(2),...,a(m-1)).
+   NOTE:
+        The element alpha_to[2^m-1] = 0 always signifying that the
+   representation of "@^infinity" = 0 is (0,0,0,...,0).
+        Similarily, the element index_of[0] = A0 always signifying
+   that the power of alpha which has the polynomial representation
+   (0,0,...,0) is "infinity".
+ 
+*/
+
+static void
+generate_gf(void)
+{
+  register int i, mask;
+
+  mask = 1;
+  Alpha_to[MM] = 0;
+  for (i = 0; i < MM; i++) {
+    Alpha_to[i] = mask;
+    Index_of[Alpha_to[i]] = i;
+    /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
+    if (Pp[i] != 0)
+      Alpha_to[MM] ^= mask;	/* Bit-wise EXOR operation */
+    mask <<= 1;	/* single left-shift */
+  }
+  Index_of[Alpha_to[MM]] = MM;
+  /*
+   * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
+   * poly-repr of @^i shifted left one-bit and accounting for any @^MM
+   * term that may occur when poly-repr of @^i is shifted.
+   */
+  mask >>= 1;
+  for (i = MM + 1; i < NN; i++) {
+    if (Alpha_to[i - 1] >= mask)
+      Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
+    else
+      Alpha_to[i] = Alpha_to[i - 1] << 1;
+    Index_of[Alpha_to[i]] = i;
+  }
+  Index_of[0] = A0;
+  Alpha_to[NN] = 0;
+}
+
+/*
+ * Obtain the generator polynomial of the TT-error correcting, length
+ * NN=(2**MM -1) Reed Solomon code from the product of (X+@**(B0+i)), i = 0,
+ * ... ,(2*TT-1)
+ *
+ * Examples:
+ *
+ * If B0 = 1, TT = 1. deg(g(x)) = 2*TT = 2.
+ * g(x) = (x+@) (x+@**2)
+ *
+ * If B0 = 0, TT = 2. deg(g(x)) = 2*TT = 4.
+ * g(x) = (x+1) (x+@) (x+@**2) (x+@**3)
+ */
+static void
+gen_poly(void)
+{
+  register int i, j;
+
+  Gg[0] = 1;
+  for (i = 0; i < NN - KK; i++) {
+    Gg[i+1] = 1;
+    /*
+     * Below multiply (Gg[0]+Gg[1]*x + ... +Gg[i]x^i) by
+     * (@**(B0+i)*PRIM + x)
+     */
+    for (j = i; j > 0; j--)
+      if (Gg[j] != 0)
+	Gg[j] = Gg[j - 1] ^ Alpha_to[modnn((Index_of[Gg[j]]) + (B0 + i) *PRIM)];
+      else
+	Gg[j] = Gg[j - 1];
+    /* Gg[0] can never be zero */
+    Gg[0] = Alpha_to[modnn(Index_of[Gg[0]] + (B0 + i) * PRIM)];
+  }
+  /* convert Gg[] to index form for quicker encoding */
+  for (i = 0; i <= NN - KK; i++)
+    Gg[i] = Index_of[Gg[i]];
+}
+
+
+/*
+ * take the string of symbols in data[i], i=0..(k-1) and encode
+ * systematically to produce NN-KK parity symbols in bb[0]..bb[NN-KK-1] data[]
+ * is input and bb[] is output in polynomial form. Encoding is done by using
+ * a feedback shift register with appropriate connections specified by the
+ * elements of Gg[], which was generated above. Codeword is   c(X) =
+ * data(X)*X**(NN-KK)+ b(X)
+ */
+int
+encode_rs(dtype data[], dtype bb[])
+{
+  register int i, j;
+  gf feedback;
+  CLEAR(bb,NN-KK);
+
+
+  for(i = KK - 1; i >= 0; i--) {
+    feedback = Index_of[data[i] ^ bb[NN - KK - 1]];
+    if (feedback != A0) {	/* feedback term is non-zero */
+      for (j = NN - KK - 1; j > 0; j--)
+	if (Gg[j] != A0)
+	  bb[j] = bb[j - 1] ^ Alpha_to[modnn(Gg[j] + feedback)];
+	else
+	  bb[j] = bb[j - 1];
+      bb[0] = Alpha_to[modnn(Gg[0] + feedback)];
+    } else {	/* feedback term is zero. encoder becomes a
+		 * single-byte shifter */
+      for (j = NN - KK - 1; j > 0; j--)
+	bb[j] = bb[j - 1];
+      bb[0] = 0;
+    }
+  }
+  return 0;
+}
+
+/*
+ * Performs ERRORS+ERASURES decoding of RS codes. If decoding is successful,
+ * writes the codeword into data[] itself. Otherwise data[] is unaltered.
+ *
+ * Return number of symbols corrected, or -1 if codeword is illegal
+ * or uncorrectable. If eras_pos is non-null, the detected error locations
+ * are written back. NOTE! This array must be at least NN-KK elements long.
+ * 
+ * First "no_eras" erasures are declared by the calling program. Then, the
+ * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
+ * If the number of channel errors is not greater than "t_after_eras" the
+ * transmitted codeword will be recovered. Details of algorithm can be found
+ * in R. Blahut's "Theory ... of Error-Correcting Codes".
+
+ * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
+ * will result. The decoder *could* check for this condition, but it would involve
+ * extra time on every decoding operation.
+ */
+int
+eras_dec_rs(dtype data[], int eras_pos[], int no_eras)
+{
+  int deg_lambda, el, deg_omega;
+  int i, j, r,k;
+
+  gf u,q,tmp,num1,num2,den,discr_r;
+  gf lambda[NN-KK + 1], s[NN-KK + 1];	/* Err+Eras Locator poly
+           * and syndrome poly */
+  gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
+  gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
+  int syn_error, count;
+
+
+  /* form the syndromes; i.e., evaluate data(x) at roots of g(x)
+   * namely @**(B0+i)*PRIM, i = 0, ... ,(NN-KK-1)
+   */
+  for(i=1;i<=NN-KK;i++)
+    {
+      s[i] = data[0];
+    }
+  for(j=1;j<NN;j++)
+    {
+      if(data[j] == 0) continue;
+      tmp = Index_of[data[j]];
+
+      /*	s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*j)]; */
+      for(i=1;i<=NN-KK;i++)
+        s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
+    }
+  /* Convert syndromes to index form, checking for nonzero condition */
+  syn_error = 0;
+  for(i=1;i<=NN-KK;i++)
+    {
+      syn_error |= s[i];
+      s[i] = Index_of[s[i]];
+    }
+  
+  if (!syn_error) {
+      /* if syndrome is zero, data[] is a codeword and there are no
+     * errors to correct. So return data[] unmodified
+     */
+      count = 0;
+      goto finish;
+    }
+  CLEAR(&lambda[1],NN-KK);
+  lambda[0] = 1;
+
+  if (no_eras > 0) {
+      /* Init lambda to be the erasure locator polynomial */
+      lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
+      for (i = 1; i < no_eras; i++)
+        {
+          u = modnn(PRIM*eras_pos[i]);
+          for (j = i+1; j > 0; j--)
+            {
+              tmp = Index_of[lambda[j - 1]];
+              if(tmp != A0)
+                {
+                  lambda[j] ^= Alpha_to[modnn(u + tmp)];
+                }
+            }
+        }
+     }
+  for(i=0;i<NN-KK+1;i++)
+    b[i] = Index_of[lambda[i]];
+  
+  /*
+   * Begin Berlekamp-Massey algorithm to determine error+erasure
+   * locator polynomial
+   */
+  r = no_eras;
+  el = no_eras;
+  while (++r <= NN-KK) {	/* r is the step number */
+      /* Compute discrepancy at the r-th step in poly-form */
+      discr_r = 0;
+      for (i = 0; i < r; i++){
+          if ((lambda[i] != 0) && (s[r - i] != A0)) {
+              discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
+            }
+        }
+      discr_r = Index_of[discr_r];	/* Index form */
+      if (discr_r == A0) {
+          /* 2 lines below: B(x) <-- x*B(x) */
+          COPYDOWN(&b[1],b,NN-KK);
+          b[0] = A0;
+        } else {
+          /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+          t[0] = lambda[0];
+          for (i = 0 ; i < NN-KK; i++) {
+              if(b[i] != A0)
+                t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
+              else
+                t[i+1] = lambda[i+1];
+            }
+          if (2 * el <= r + no_eras - 1) {
+              el = r + no_eras - el;
+              /*
+   * 2 lines below: B(x) <-- inv(discr_r) *
+   * lambda(x)
+   */
+              for (i = 0; i <= NN-KK; i++)
+                b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
+            } else {
+              /* 2 lines below: B(x) <-- x*B(x) */
+              COPYDOWN(&b[1],b,NN-KK);
+              b[0] = A0;
+            }
+          COPY(lambda,t,NN-KK+1);
+        }
+    }
+
+  /* Convert lambda to index form and compute deg(lambda(x)) */
+  deg_lambda = 0;
+  for(i=0;i<NN-KK+1;i++){
+      lambda[i] = Index_of[lambda[i]];
+      if(lambda[i] != A0)
+        deg_lambda = i;
+    }
+  /*
+   * Find roots of the error+erasure locator polynomial by Chien
+   * Search
+   */
+  COPY(&reg[1],&lambda[1],NN-KK);
+  count = 0;		/* Number of roots of lambda(x) */
+  for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+      q = 1;
+      for (j = deg_lambda; j > 0; j--){
+          if (reg[j] != A0) {
+              reg[j] = modnn(reg[j] + j);
+              q ^= Alpha_to[reg[j]];
+            }
+        }
+      if (q != 0)
+        continue;
+      /* store root (index-form) and error location number */
+      root[count] = i;
+      loc[count] = k;
+      /* If we've already found max possible roots,
+     * abort the search to save time
+     */
+      if(++count == deg_lambda)
+        break;
+    }
+  if (deg_lambda != count) {
+      /*
+     * deg(lambda) unequal to number of roots => uncorrectable
+     * error detected
+     */
+      count = -1;
+      goto finish;
+    }
+  /*
+   * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+   * x**(NN-KK)). in index form. Also find deg(omega).
+   */
+  deg_omega = 0;
+  for (i = 0; i < NN-KK;i++){
+      tmp = 0;
+      j = (deg_lambda < i) ? deg_lambda : i;
+      for(;j >= 0; j--){
+          if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
+            tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
+        }
+      if(tmp != 0)
+        deg_omega = i;
+      omega[i] = Index_of[tmp];
+    }
+  omega[NN-KK] = A0;
+  
+  /*
+   * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+   * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
+   */
+  for (j = count-1; j >=0; j--) {
+      num1 = 0;
+      for (i = deg_omega; i >= 0; i--) {
+          if (omega[i] != A0)
+            num1  ^= Alpha_to[modnn(omega[i] + i * root[j])];
+        }
+      num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
+      den = 0;
+
+      /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+      for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
+          if(lambda[i+1] != A0)
+            den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
+        }
+      if (den == 0) {
+
+          /* Convert to dual- basis */
+          count = -1;
+          goto finish;
+        }
+      /* Apply error to data */
+      if (num1 != 0) {
+          data[loc[j]] ^= Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
+        }
+    }
+finish:
+  if(eras_pos != NULL){
+      for(i=0;i<count;i++){
+          if(eras_pos!= NULL)
+            eras_pos[i] = loc[i];
+        }
+    }
+  return count;
+}
+/* Encoder/decoder initialization - call this first! */
+void init_rs(int kk)
+{
+  KK=kk;
+  generate_gf();
+  gen_poly();
+  RS_init = 1;
+}
diff --git a/qsstv/utils/rs.h b/qsstv/utils/rs.h
new file mode 100644
index 0000000..7426899
--- /dev/null
+++ b/qsstv/utils/rs.h
@@ -0,0 +1,60 @@
+/* Global definitions for Reed-Solomon encoder/decoder
+ * Phil Karn KA9Q, September 1996
+ */
+/* Set one of these to enable encoder/decoder debugging and error checking,
+ * at the expense of speed */
+/* #undef DEBUG 1 */
+/* #undef DEBUG 2 */
+
+/* To select the CCSDS standard (255,223) code, define CCSDS. This
+ * implies standard values for MM, KK, B0 and PRIM.
+ */
+/* #undef CCSDS 1 */
+
+#define MM 8
+
+#define RSDSIZERS1 224
+#define RSDSIZERS2 192
+#define RSDSIZERS3 160
+#define RSDSIZERS4 128
+
+
+/*
+ * Set KK to be the number of data symbols in each block, which must be
+ * less than the block size. The code will then be able to correct up
+ * to NN-KK erasures or (NN-KK)/2 errors, or combinations thereof with
+ * each error counting as two erasures.
+ */
+
+/* Set B0 to the first root of the generator polynomial, in alpha form, and
+ * set PRIM to the power of alpha used to generate the roots of the
+ * generator polynomial. The generator polynomial will then be
+ * @**PRIM*B0, @**PRIM*(B0+1), @**PRIM*(B0+2)...@**PRIM*(B0+NN-KK)
+ * where "@" represents a lower case alpha.
+ */
+#define B0 1 /* First root of generator polynomial, alpha form */
+#define PRIM 1 /* power of alpha used to generate roots of generator poly */
+
+/* If you want to select your own field generator polynomial, you'll have
+ * to edit that in rs.c.
+ */
+#define	NN ((1 << MM) - 1)
+typedef unsigned char dtype;
+
+/* Reed-Solomon encoding
+ * data[] is the input block, parity symbols are placed in bb[]
+ * bb[] may lie past the end of the data, e.g., for (255,223):
+ *	encode_rs(&data[0],&data[223]);
+ */
+int encode_rs(dtype data[], dtype bb[]);
+
+/* Reed-Solomon erasures-and-errors decoding
+ * The received block goes into data[], and a list of zero-origin
+ * erasure positions, if any, goes in eras_pos[] with a count in no_eras.
+ *
+ * The decoder corrects the symbols in place, if possible and returns
+ * the number of corrected symbols. If the codeword is illegal or
+ * uncorrectible, the data array is unchanged and -1 is returned
+ */
+int eras_dec_rs(dtype data[], int eras_pos[], int no_eras);
+void init_rs(int kk);
diff --git a/qsstv/utils/supportfunctions.cpp b/qsstv/utils/supportfunctions.cpp
new file mode 100644
index 0000000..a7f3b90
--- /dev/null
+++ b/qsstv/utils/supportfunctions.cpp
@@ -0,0 +1,631 @@
+/**************************************************************************
+*   Copyright (C) 2000-2012 by Johan Maes                                 *
+*   on4qz at telenet.be                                                      *
+*   http://users.telenet.be/on4qz                                         *
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program is distributed in the hope that it will be useful,       *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+*   GNU General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+***************************************************************************/
+
+#include "supportfunctions.h"
+#include <QDateTime>
+#include <QDebug>
+#include <stdarg.h>
+#include "qsstvglobal.h"
+
+
+QString lastPath("");
+
+bool getValue(int &val, QLineEdit* input)
+{
+	bool ok;
+	QString s;
+	s=input->text();
+	val=s.toInt(&ok,0); // allow ayutomatic conversion from hex to decimal in the classic C++ way : 0x is hex other are decimal
+	return ok;
+}
+
+bool getValue(double &val, QLineEdit* input)
+{
+	bool ok;
+	QString s;
+	s=input->text();
+	val=s.toDouble(&ok);
+	return ok;
+}
+
+bool getValue(int &val, QString input)
+{
+	bool ok;
+	val=input.toInt(&ok);
+	return ok;
+}
+bool getValue(double &val, QString input)
+{
+	bool ok;
+	val=input.toDouble(&ok);
+	return ok;
+}
+
+void getValue(bool &val, QCheckBox *input)
+{
+	val=input->isChecked();
+}
+
+void getValue(int &val, QSpinBox *input)
+{
+	val=input->value();
+}
+
+void getValue(uint &val, QSpinBox *input)
+{
+  val=input->value();
+}
+
+void getValue(double &val, QDoubleSpinBox *input)
+{
+  val=input->value();
+}
+
+void getValue(QString &s, QLineEdit *input)
+{
+	s=input->text();
+}
+
+void getValue(QString &s, QPlainTextEdit *input)
+{
+  s=input->toPlainText();
+}
+
+
+
+void getValue(int &s, QComboBox *input)
+{
+	s=input->currentText().toInt();
+}
+
+void getIndex(int &s, QComboBox *input)
+{
+  s=input->currentIndex();
+}
+
+void getValue(QString &s, QComboBox *input)
+{
+	s=input->currentText();
+}
+
+void getValue(int &s, QButtonGroup *input)
+{
+	s=input->checkedId();
+}
+
+void getValue(bool &s, QRadioButton *input)
+{
+	s=input->isChecked();
+}
+
+void getValue(int &val, QSlider *input)
+{
+  val=input->value();
+}
+
+void setValue(int val, QLineEdit* output)
+{
+	output->setText(QString::number(val));
+}
+ 
+void setValue(double val, QLineEdit* output)
+{
+	output->setText(QString::number(val));
+}
+/**
+	\brief sets double number in a QlineEdit
+	\param val  the value to set
+	\param output pointer to QLineEdit
+	\param prec the required precision
+*/
+ 
+void setValue(double val, QLineEdit* output,int prec)
+{
+	output->setText(QString::number(val,'g',prec));
+}
+
+void setValue(bool val, QCheckBox *input)
+{
+	input->setChecked(val);
+}
+
+void setValue(int val, QSpinBox *input)
+{
+	input->setValue(val);
+}
+
+void setValue(uint val, QSpinBox *input)
+{
+  input->setValue(val);
+}
+
+void setValue(double val, QDoubleSpinBox *input)
+{
+  input->setValue(val);
+}
+
+void setValue(QString s, QLineEdit *input)
+{
+	input->setText(s);
+}
+
+void setValue(QString s, QPlainTextEdit *input)
+{
+  input->setPlainText(s);
+}
+
+void setValue(int s, QComboBox *input)
+{
+	int i;
+	for(i=0;i<input->count();i++)
+		{
+			if(input->itemText(i).toInt()==s)
+				{
+					input->setCurrentIndex(i);
+					return;
+				}
+		}
+	input->setCurrentIndex(0);
+}
+
+void setIndex(int s, QComboBox *input)
+{
+  input->setCurrentIndex(s);
+}
+
+void setValue(QString s, QComboBox *input)
+{
+	int i;
+	for(i=0;i<input->count();i++)
+		{
+			if(input->itemText(i)==s)
+				{
+					input->setCurrentIndex(i);
+					return;
+				}
+		}
+	input->setCurrentIndex(0);
+}
+
+void setValue(int s, QButtonGroup *input)
+{
+	input->button(s)->setChecked(true);
+}
+
+void setValue(bool s, QRadioButton *input)
+{
+	input->setChecked(s);
+}
+
+void setValue(int val, QSlider *input)
+{
+  input->setValue(val);
+}
+
+
+
+
+bool browseGetFile(QLineEdit *le,QString deflt, const QString &filter)
+{
+    dirDialog d((QWidget *)le,"Browse");
+    QString s=d.openFileName(deflt,filter);
+	if (s==QString::null) return false;
+	if (s.isEmpty()) return false;
+	le->setText(s);
+	return true;
+}
+
+bool browseSaveFile(QLineEdit *le,QString deflt,const QString &filter)
+{
+    dirDialog d((QWidget *)le,"Browse");
+	QString s=d.saveFileName(deflt,filter,"");
+	if (s==QString::null) return false;
+	if (s.isEmpty()) return false;
+	le->setText(s);
+	return true;
+}
+
+bool browseDir(QLineEdit *le,QString deflt)
+{
+    dirDialog d((QWidget *)le,"Browse");
+    QString s=d.openDirName(deflt);
+	if (s==QString::null) return false;
+	if (s.isEmpty()) return false;
+	le->setText(s);
+	return true;
+}
+
+
+
+
+
+
+dirDialog::dirDialog(QWidget * parent,QString title)
+{
+    parentPtr=parent;
+    dialogTitle=title;
+}
+
+dirDialog::~dirDialog()
+{
+}
+
+
+/*!
+    \fn dirDialog::openFileName(const QString &path, const QString &filter, bool single)
+    \brief selection of a file
+
+    \param path  directory to open (preselected) if empty, the last accessed directory will be used
+    \param filter  types to select from (e.g. mydirs*)
+    \param single  allows selection of more than one file if true
+    \return if canceled or no selection then returns an empty string else return string containing absolute filename
+*/
+
+
+QString dirDialog::openFileName(const QString &path, const QString &filter)
+{
+    QString fn;
+  if (path.isEmpty() && lastPath.isEmpty())
+    {
+      lastPath=QDir::homePath();
+    }
+  else if (!path.isEmpty())
+    {
+      lastPath=path;
+    }
+
+  fn=QFileDialog::getOpenFileName(parentPtr,dialogTitle,lastPath,filter);
+  if(!fn.isEmpty())
+  {
+      QFileInfo  fi(fn);
+        lastPath=fi.absolutePath();
+  }
+  return fn;
+}
+
+/*!
+    \fn dirDialog::openDirName(const QString &path, const QString &filter)
+    \brief selection of a directory
+
+    \param path directory to open (preselected)
+    \param filter    types to select from (e.g. mydirs*)
+
+    \return if canceled or no selection then return an empty string else return string containing absolute dirname
+*/
+
+QString dirDialog::openDirName(const QString &path)
+{
+  QString fn;
+  if ((path.isEmpty()) && lastPath.isEmpty())
+    {
+      lastPath=QDir::homePath();
+    }
+  else if (!path.isEmpty())
+    {
+      lastPath=path;
+    }
+  fn=QFileDialog::getExistingDirectory(parentPtr,dialogTitle,lastPath);
+  if(!fn.isEmpty())
+  {
+      lastPath=fn;
+  }
+  return fn;
+}
+
+/*!
+    \fn dirDialog::saveFileName(const QString &path, const QString &filter,QString extension)
+    \brief Save a file to disk
+
+    Saves a file to disk. A dialogbox is opened with \a startWith directory (or /dir/subdir/..../filename) preselected
+    \param path directory to open (can include filename to preselect)
+    \param filter    file types to select from (e.g. *.txt *.doc)
+    \param extension if extension is not empty or NULL, thenn this string will be appended to the filename. A dot will automatically be insterted (i.e specify "txt" not ".txt").
+    \return if canceled or no selection then return an empty string else return string containing absolute filename.
+*/
+
+QString dirDialog::saveFileName(const QString &path, const QString &filter, QString extension)
+{
+  QString fn;
+  if ((path.isEmpty()) && lastPath.isEmpty())
+    {
+      lastPath=QDir::currentPath();
+    }
+  else if (!path.isEmpty())
+    {
+      lastPath=path;
+    }
+  QString exten(extension);
+  fn=QFileDialog::getSaveFileName(parentPtr,dialogTitle,lastPath,filter);
+  if(fn.isEmpty()) return fn;
+  QFileInfo  fi(fn);
+  if(!exten.isEmpty())
+    {
+      if(fi.suffix()=="")
+        {
+          fi.setFile(fi.absoluteFilePath()+"."+exten);
+        }
+    }
+    lastPath=fi.absolutePath();
+  return fi.absoluteFilePath();
+}
+
+void deleteFiles(QString dirPath,QString extension)
+{
+  int i;
+  QDir dir(dirPath);
+  QStringList filters;
+  QFile fi;
+  filters << extension;
+  dir.setNameFilters(filters);
+  QFileInfoList entries = dir.entryInfoList(filters,QDir::Files|QDir::NoSymLinks);
+  for(i=0;i<entries.count();i++)
+    {
+      fi.setFileName(entries.at(i).absoluteFilePath());
+      fi.remove();
+    }
+}
+
+bool trash(QString filename,bool forceDelete)
+{
+  QString tmp;
+  QFile orgFile(filename);
+  QFileInfo modifiedFileInfo(filename);
+  QFileInfo info(filename);
+  QFile infoFile;
+  QFile modifiedFile;
+  QDir trDir;
+  QDir infoDir;
+  QDir filesDir;
+  QString infoTxt;
+  trDir.setPath(getenv("XDG_DATA_HOME"));
+  if (trDir.path().isEmpty())  trDir.setPath(QDir::homePath()+"/.local/share/Trash");
+  infoDir.setPath(trDir.path()+"/info");
+  filesDir.setPath(trDir.path()+"/files");
+  infoFile.setFileName(infoDir.path()+"/"+info.fileName()+".trashinfo");
+  modifiedFile.setFileName(filesDir.path()+"/"+modifiedFileInfo.fileName());
+  int counter=0;
+  do
+    {
+      if(!modifiedFile.exists()) break;
+      counter++;
+      tmp=QString("%1/%2_%3.%4").arg(filesDir.path()).arg(modifiedFileInfo.completeBaseName()).arg(QString::number(counter)).arg(modifiedFileInfo.suffix());
+      modifiedFile.setFileName(tmp);
+      tmp=QString("%1/%2_%3.%4").arg(infoDir.path()).arg(modifiedFileInfo.completeBaseName()).arg(QString::number(counter)).arg(modifiedFileInfo.suffix());
+      infoFile.setFileName(tmp+".trashinfo");
+    }
+  while(1);
+
+  infoTxt=QString("[Trash Info]\nPath=%1\nDeletionDate=%2")
+      .arg(filename).arg(QDateTime::currentDateTime().toString(Qt::ISODate));
+
+  if((!trDir.exists()) || (!infoDir.exists()) || (!filesDir.exists()))
+    {
+      qDebug() << "Trash folder or one of its components does not exist";
+      if(forceDelete) orgFile.remove();
+      return false;
+    }
+
+  if(!infoFile.open(QIODevice::WriteOnly))
+  {
+    qDebug() << QString("Trash folder: can't open %1 for writing").arg(infoFile.fileName());
+    if(forceDelete) orgFile.remove();
+    return false;
+  }
+  infoFile.write(infoTxt.toLatin1().data());
+  infoFile.close();
+  QFile trashFile(info.absoluteFilePath());
+  QString target;
+  target=QString("%1").arg(modifiedFile.fileName());
+  if(!trashFile.rename(filename,target))
+  {
+    qDebug() << QString("Trash folder: can't rename %1 to %2").arg(filename).arg(target);
+    if(forceDelete) orgFile.remove();
+    return false;
+  }
+  return true;
+}
+
+void arrayComplexDump(QString label,CVectorEx<_COMPLEX> data,unsigned int len,bool toAux)
+{
+  CVectorEx<_COMPLEX> vct=data;
+  _COMPLEX c;
+  unsigned int i,j;
+//  unsigned int len;
+//  len=vct.Size()/8;
+  QString dumpStr,tmp;
+
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          c=vct[i+j];
+          tmp=QString::number(c.real())+","+QString::number(c.imag());
+//          while(tmp.length()<2) tmp.prepend("0");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+
+void arrayBinDump(QString label,CVector<_BINARY> data,unsigned int len,bool toAux)
+{
+  CVector<_BINARY> vct=data;
+  unsigned int i,j;
+//  unsigned int len;
+//  len=vct.Size()/8;
+  QString dumpStr,tmp;
+
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          tmp=QString::number(vct.Separate(8),16).right(2);
+          while(tmp.length()<2) tmp.prepend("0");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+
+void arrayDump(QString label,short int *data, unsigned int len,bool toAux)
+{
+  unsigned int i,j;
+  QString dumpStr,tmp;
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          tmp=QString::number(data[i+j],16).right(4);
+          while(tmp.length()<4) tmp.prepend("0");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+void arrayDump(QString label,int *data, unsigned int len,bool toAux)
+{
+  unsigned int i,j;
+  QString dumpStr,tmp;
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          tmp=QString::number(data[i+j],16).right(4);
+          while(tmp.length()<4) tmp.prepend("0");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+void arrayDump(QString label, float *data, unsigned int len, bool toAux)
+{
+  unsigned int i,j;
+  QString dumpStr,tmp;
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          tmp=QString::number(data[i+j]).right(8);
+          while(tmp.length()<8) tmp.prepend(" ");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+
+void arrayDump(QString label, quint32* data, unsigned int len, bool toAux)
+{
+  unsigned int i,j;
+  QString dumpStr,tmp;
+  for( i=0;i<len;i+=16)
+    {
+      dumpStr=label+" ";
+      for(j=0;(j<16)&&((i+j)<len);j++)
+        {
+          tmp=QString::number(data[i+j],16).right(8);
+          while(tmp.length()<8) tmp.prepend("0");
+          dumpStr+=tmp+" ";
+        }
+      dumpStr.chop(1);
+      if(toAux)
+        {
+          logfile->addToAux(dumpStr);
+        }
+      else
+        {
+          addToLog(dumpStr,LOGALL);
+        }
+    }
+}
+
+timingAnalyser::timingAnalyser()
+{
+
+}
+
+timingAnalyser::~timingAnalyser()
+{
+}
+void timingAnalyser::start()
+{
+  tm.start();
+}
+
+unsigned long timingAnalyser::result()
+{
+  return tm.elapsed();
+}
+
+
+
+
+
+
diff --git a/qsstv/utils/supportfunctions.h b/qsstv/utils/supportfunctions.h
new file mode 100644
index 0000000..7819080
--- /dev/null
+++ b/qsstv/utils/supportfunctions.h
@@ -0,0 +1,104 @@
+#ifndef SUPPORTFUNCTIONS_H
+#define SUPPORTFUNCTIONS_H
+
+#include <QString>
+#include <QLineEdit>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QButtonGroup>
+#include <QSpinBox>
+#include <QFileDialog>
+#include <QRadioButton>
+#include <QSlider>
+#include <QPlainTextEdit>
+#include <QTime>
+#include "vector.h"
+
+/** \file */
+
+#define OK true
+#define NOK false
+
+
+
+/** get int value from a QLinedit */ 
+bool getValue(int &val, QLineEdit *input);
+/** get double value from a QLinedit */ 
+bool getValue(double &val, QLineEdit *input);
+/** get int value from a QString */ 
+bool getValue(int &val, QString input);
+bool getValue(double &val, QString input);
+void getValue(bool &val, QCheckBox *input);
+void getValue(int &val, QSpinBox *input);
+void getValue(uint &val, QSpinBox *input);
+void getValue(double &val, QDoubleSpinBox *input);
+void getValue(QString &s, QLineEdit *input);
+void getValue(QString &s, QPlainTextEdit *input);
+
+void getValue(int &s, QComboBox *input);
+void getIndex(int &s, QComboBox *input);
+void getValue(QString &s, QComboBox *input);
+void getValue(int &s, QButtonGroup *input);
+void getValue(bool &val, QRadioButton *input);
+void getValue(int &val, QSlider *input);
+
+void setValue(int val, QLineEdit* output);
+void setValue(double val, QLineEdit* output);
+void setValue(double val, QLineEdit* output,int prec);
+void setValue(bool val, QCheckBox *input);
+void setValue(int val, QSpinBox *input);
+void setValue(uint val, QSpinBox *input);
+void setValue(double val, QDoubleSpinBox *input);
+void setValue(QString s, QLineEdit *input);
+void setValue(QString s, QPlainTextEdit *input);
+void setValue(int s, QComboBox *input);
+void setIndex(int s, QComboBox *input);
+void setValue(QString s, QComboBox *input);
+void setValue(int s, QButtonGroup *input);
+void setValue(bool val, QRadioButton *input);
+void setValue(int val, QSlider *input);
+
+
+
+bool browseGetFile(QLineEdit *le,QString deflt,const QString &filter="*");
+bool browseSaveFile(QLineEdit *le,QString deflt,const QString &filter="*");
+bool browseDir(QLineEdit *le, QString deflt);
+void deleteFiles(QString dirPath,QString extension);
+
+bool trash(QString filename,bool forceDelete);
+void arrayDump(QString label, short int *data, unsigned int len, bool toAux);
+void arrayDump(QString label, int *data, unsigned int len, bool toAux);
+void arrayDump(QString label,float *data, unsigned int len,bool toAux);
+void arrayDump(QString label, quint32 *data, unsigned int len, bool toAux);
+void arrayBinDump(QString label, CVector<_BINARY> data, unsigned int len, bool toAux);
+void arrayComplexDump(QString label,CVectorEx<_COMPLEX> data,unsigned int len,bool toAux);
+
+
+class dirDialog
+{
+public:
+    dirDialog(QWidget *parent,QString title="");
+    ~dirDialog();
+    QString openFileName(const QString &startWith, const QString &filter="*");
+    QString openDirName(const QString &path);
+    QString saveFileName(const QString &path, const QString &filter,QString extension);
+private:
+    QWidget * parentPtr;
+    QString  dialogTitle;
+};
+
+class timingAnalyser
+{
+public:
+  timingAnalyser();
+  ~timingAnalyser();
+  void start();
+  unsigned long result();
+private:
+  QTime tm;
+};
+
+
+
+#endif
+
diff --git a/qsstv/utils/vector.h b/qsstv/utils/vector.h
new file mode 100644
index 0000000..f67f915
--- /dev/null
+++ b/qsstv/utils/vector.h
@@ -0,0 +1,516 @@
+/******************************************************************************\
+ * Technische Universitaet Darmstadt, Institut fuer Nachrichtentechnik
+ * Copyright (c) 2001
+ *
+ * Author(s):
+ *	Volker Fischer
+ *
+ * Description:
+ *	
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#if !defined(VECTOR_H__3B0BA660_CA6LIUBEFIB2B_23E7A0D31912__INCLUDED_)
+#define VECTOR_H__3B0BA660_CA6LIUBEFIB2B_23E7A0D31912__INCLUDED_
+
+//#include "drmtx/common/GlobalDefinitions.h"
+#include "qsstvdefs.h"
+using namespace std; /* Because of the library: "complex" */
+#include <string>
+#include <stdio.h>
+#include <math.h>
+#include <vector>
+
+
+/******************************************************************************\
+* CVector base class                                                           *
+\******************************************************************************/
+template<class TData> class CVector
+    : public vector<TData>
+{
+public:
+	CVector() : iBitArrayCounter(0), iVectorSize(0) {pData = this->begin();}
+	CVector(const int iNeSi) {Init(iNeSi);}
+	CVector(const int iNeSi, const TData tInVa) {Init(iNeSi, tInVa);}
+	virtual	~CVector() {}
+
+	/* Copy constructor: The order of the initialization list must not be
+	   changed. First, the base class must be initialized, then the pData
+	   pointer must be set to the new data source. The bit access is, by
+	   default, reset */
+	CVector(const CVector<TData>& vecI) :
+		vector<TData>(static_cast<const vector<TData>&>(vecI)),
+		iBitArrayCounter(0), iVectorSize(vecI.Size()) {pData = this->begin();}
+
+	virtual void Init(const int iNewSize);
+
+	/* Use this init to give all elements a defined value */
+	virtual void Init(const int iNewSize, const TData tIniVal);
+	void Reset(const TData tResetVal);
+
+	void Enlarge(const int iAddedSize);
+	void Add(const TData& tI) {Enlarge(1); pData[iVectorSize - 1] = tI;}
+
+	inline int Size() const {return iVectorSize;}
+
+	/* This operator allows for a l-value assignment of this object:
+	   CVector[x] = y is possible */
+	inline TData& operator[](const int iPos) {
+#ifdef _DEBUG_
+		if ((iPos < 0) || (iPos > iVectorSize - 1))
+		{
+			DebugError("Writing vector out of bounds", "Vector size",
+				iVectorSize, "New parameter", iPos);
+		}
+#endif		
+		return pData[iPos];}
+
+	inline TData operator[](const int iPos) const {
+#ifdef _DEBUG_
+		if ((iPos < 0) || (iPos > iVectorSize - 1))
+		{
+			DebugError("Reading vector out of bounds", "Vector size",
+				iVectorSize, "New parameter", iPos);
+		}
+#endif
+		return pData[iPos];}
+
+	inline CVector<TData>& operator=(const CVector<TData>& vecI) {
+#ifdef _DEBUG_
+		/* Vectors which shall be copied MUST have same size! (If this is
+		   satisfied, the parameter "iVectorSize" must not be adjusted as
+		   a side effect) */
+		if (vecI.Size() != iVectorSize)
+		{
+			DebugError("Vector operator=() different size", "Vector size",
+				iVectorSize, "New parameter", vecI.Size());
+		}
+#endif
+		vector<TData>::operator=(vecI);
+
+		/* Reset my data pointer in case, the operator=() of the base class
+		   did change the actual memory */
+		pData = this->begin();
+
+		return *this;
+	}
+
+
+	/* Bit operation functions */
+	void		Enqueue(uint32_t iInformation, const int iNumOfBits);
+	uint32_t	Separate(const int iNumOfBits);
+	void		ResetBitAccess() {iBitArrayCounter = 0;}
+
+protected:
+	typename vector<TData>::iterator	pData;
+	int									iBitArrayCounter;
+	int									iVectorSize;
+};
+
+
+/* Implementation *************************************************************/
+template<class TData> void CVector<TData>::Init(const int iNewSize)
+{
+	iVectorSize = iNewSize;
+
+	/* Clear old buffer and reserve memory for new buffer, get iterator
+	   for pointer operations */
+	this->clear();
+	this->resize(iNewSize);
+	pData = this->begin();
+}
+
+template<class TData> void CVector<TData>::Init(const int iNewSize, 
+												const TData tIniVal)
+{
+	/* Call actual init routine */
+	Init(iNewSize);
+
+	/* Set values */
+	Reset(tIniVal);
+}
+
+template<class TData> void CVector<TData>::Enlarge(const int iAddedSize)
+{
+	iVectorSize += iAddedSize;
+	this->resize(iVectorSize);
+
+	/* We have to reset the pointer since it could be that the vector size was
+	   zero before enlarging the vector */
+	pData = this->begin();
+}
+
+template<class TData> void CVector<TData>::Reset(const TData tResetVal)
+{
+	/* Set all values to reset value */
+	for (int i = 0; i < iVectorSize; i++)
+		pData[i] = tResetVal;
+}
+
+template<class TData> void CVector<TData>::Enqueue(uint32_t iInformation,
+												   const int iNumOfBits)
+{
+	/* Enqueue bits in bit array */
+	for (int i = 0; i < iNumOfBits; i++)
+	{
+		/* We want to put the bits on the array with the MSB first */
+		operator[](iBitArrayCounter + iNumOfBits - i - 1) = _BINARY(iInformation & 1);
+
+		/* Shift one bit to mask next bit at LSB-position */
+		iInformation >>= 1;
+	}
+
+	iBitArrayCounter += iNumOfBits;
+}
+
+template<class TData> uint32_t CVector<TData>::Separate(const int iNumOfBits)
+{
+	uint32_t iInformation;
+
+	/* Check, if current position plus new bit-size is smaller than the maximum
+	   length of the bit vector. Error code: return a "0" */
+	if (iBitArrayCounter + iNumOfBits > iVectorSize)
+		return 0;
+
+	/* Separate out bits from bit-array */
+	iInformation = 0;
+	for (int i = 0; i < iNumOfBits; i++)
+	{
+		/* MSB comes first, therefore shift left */
+		iInformation <<= 1;
+
+		iInformation |= pData[iBitArrayCounter + i] & 1;
+	}
+
+	iBitArrayCounter += iNumOfBits;
+
+	return iInformation;
+}
+
+
+/******************************************************************************\
+* CShiftRegister class                                                         *
+\******************************************************************************/
+template<class TData> class CShiftRegister : public CVector<TData>
+{
+public:
+	CShiftRegister() : CVector<TData>() {}
+	CShiftRegister(const int iNeSi) : CVector<TData>(iNeSi) {}
+	CShiftRegister(const int iNeSi, const TData tInVa) :
+		CVector<TData>(iNeSi, tInVa) {}
+
+	/* Add one value at the beginning, shift the others to the right */
+	void AddBegin(const TData tNewD);
+
+	/* Add one value at the end, shift the others to the left */
+	void AddEnd(const TData tNewD);
+
+	/* Add a vector at the end, shift others to the left */
+	void AddEnd(const CVector<TData>& vectNewD, const int iLen);
+};
+
+
+/* Implementation *************************************************************/
+template<class TData> void CShiftRegister<TData>::AddBegin(const TData tNewD)
+{
+	/* Shift old values */
+	for (int i = this->iVectorSize - 1; i > 0; i--)
+		this->pData[i] = this->pData[i - 1];
+
+	/* Add new value */
+	this->pData[0] = tNewD;
+}
+
+template<class TData> void CShiftRegister<TData>::AddEnd(const TData tNewD)
+{
+	/* Shift old values */
+	for (int i = 0; i < this->iVectorSize - 1; i++)
+		this->pData[i] = this->pData[i + 1];
+
+	/* Add new value */
+	this->pData[this->iVectorSize - 1] = tNewD;
+}
+
+template<class TData> void CShiftRegister<TData>::AddEnd(const CVector<TData>& vectNewD,
+														 const int iLen)
+{
+	int i, iBlockEnd, iMovLen;
+
+	iBlockEnd = this->iVectorSize - iLen;
+	iMovLen = iLen;
+
+	/* Shift old values */
+	for (i = 0; i < iBlockEnd; i++)
+		this->pData[i] = this->pData[iMovLen++];
+
+	/* Add new block of data */
+	for (i = 0; i < iLen; i++)
+		this->pData[iBlockEnd++] = vectNewD[i];
+}
+
+
+/******************************************************************************\
+* CFIFO class (first in, first out)                                            *
+\******************************************************************************/
+template<class TData> class CFIFO : public CVector<TData>
+{
+public:
+	CFIFO() : CVector<TData>(), iCurIdx(0) {}
+	CFIFO(const int iNeSi) : CVector<TData>(iNeSi), iCurIdx(0) {}
+	CFIFO(const int iNeSi, const TData tInVa) :
+		CVector<TData>(iNeSi, tInVa), iCurIdx(0) {}
+
+	void Add(const TData tNewD);
+	inline TData Get() {return this->pData[iCurIdx];}
+
+	virtual void Init(const int iNewSize);
+	virtual void Init(const int iNewSize, const TData tIniVal);
+
+protected:
+	int iCurIdx;
+};
+
+template<class TData> void CFIFO<TData>::Init(const int iNewSize)
+{
+	iCurIdx = 0;
+	CVector<TData>::Init(iNewSize);
+}
+
+template<class TData> void CFIFO<TData>::Init(const int iNewSize,
+											  const TData tIniVal)
+{
+	iCurIdx = 0;
+	CVector<TData>::Init(iNewSize, tIniVal);
+}
+
+template<class TData> void CFIFO<TData>::Add(const TData tNewD)
+{
+	this->pData[iCurIdx] = tNewD;
+
+	/* Increment index */
+	iCurIdx++;
+	if (iCurIdx >= this->iVectorSize)
+		iCurIdx = 0;
+}
+
+
+/******************************************************************************\
+* CMovingAv class (moving average)                                             *
+\******************************************************************************/
+template<class TData> class CMovingAv : public CVector<TData>
+{
+public:
+	CMovingAv() : CVector<TData>(), iCurIdx(0) {}
+	CMovingAv(const int iNeSi) : CVector<TData>(iNeSi), iCurIdx(0) {}
+	CMovingAv(const int iNeSi, const TData tInVa) :
+		CVector<TData>(iNeSi, tInVa), iCurIdx(0) {}
+
+	void Add(const TData tNewD);
+	inline TData GetAverage() {return tCurAvResult;}
+
+	virtual void Init(const int iNewSize);
+	void InitVec(const int iNewSize, const int iNewVecSize);
+
+protected:
+	int		iCurIdx;
+	TData	tCurAvResult;
+};
+
+template<class TData> void CMovingAv<TData>::InitVec(const int iNewSize,
+													 const int iNewVecSize)
+{
+	iCurIdx = 0;
+	CVector<TData>::Init(iNewSize);
+
+	/* Init each vector in vector */
+	for (int i = 0; i < iNewSize; i++)
+		this->pData[i].Init(iNewVecSize, 0);
+
+	/* Init current average result */
+	tCurAvResult.Init(iNewVecSize, 0);
+}
+
+template<class TData> void CMovingAv<TData>::Init(const int iNewSize)
+{
+	iCurIdx = 0;
+	tCurAvResult = TData(0); /* Only for scalars! */
+	CVector<TData>::Init(iNewSize);
+}
+
+template<class TData> void CMovingAv<TData>::Add(const TData tNewD)
+{
+/*
+	Optimized calculation of the moving average. We only add a new value and
+	subtract the old value from the result. We only need one addition and a
+	history buffer
+*/
+	/* Subtract oldest value */
+	tCurAvResult -= this->pData[iCurIdx];
+
+	/* Add new value and write in memory */
+	tCurAvResult += tNewD;
+	this->pData[iCurIdx] = tNewD;
+
+	/* Increase position pointer and test if wrap */
+	iCurIdx++;
+	if (iCurIdx >= this->iVectorSize)
+		iCurIdx = 0;
+}
+
+
+/******************************************************************************\
+* CVectorEx class (Extended vector with additional information)                *
+\******************************************************************************/
+class CExtendedVecData
+{
+public:
+	/* Symbol ID of the current block. This number only identyfies the
+	   position in a frame, NOT in a super-frame */
+	int			iSymbolID;
+
+	/* This flag indicates that the symbol ID has changed */
+	_BOOLEAN	bSymbolIDHasChanged;
+
+	/* The channel estimation needs information about timing corrections,
+	   because it is using information from the symbol memory */
+	int			iCurTimeCorr;
+};
+
+template<class TData> class CVectorEx : public CVector<TData>
+{
+public:
+	CVectorEx() {}
+	virtual	~CVectorEx() {}
+
+	CExtendedVecData&	GetExData() {return ExtendedData;}
+	void				SetExData(CExtendedVecData& NewExData) 
+							{ExtendedData = NewExData;}
+
+protected:
+	CExtendedVecData ExtendedData;
+};
+
+
+/******************************************************************************\
+* CMatrix base class                                                           *
+\******************************************************************************/
+template<class TData> class CMatrix
+{
+public:
+	CMatrix() : ppData(NULL), iRow(0), iCol(0) {}
+	CMatrix(const int iNewR, const int iNewC) {Init(iNewR, iNewC);}
+	CMatrix(const int iNewR, const int iNewC, const TData tInVa) 
+		{Init(iNewR, iNewC, tInVa);}
+	CMatrix(const CMatrix& m): ppData(NULL) {
+		Init(m.iRow,m.iCol); 
+		for (int i=0; i<m.NumRows(); i++)
+			ppData[i] = m[i];
+	}
+	virtual	~CMatrix();
+
+	void Init(const int iNewRow, const int iNewColumn);
+
+	/* Use this init to give all elements a defined value */
+	void Init(const int iNewRow, const int iNewColumn, const TData tIniVal);
+	void Reset(const TData tResetVal);
+
+	inline CVector<TData>& operator[](const int iPos) const {
+#ifdef _DEBUG_
+		if ((iPos < 0) || (iPos > iRow - 1))
+		{
+			DebugError("Matrix: Writing vector out of bounds", "Row size",
+				iRow, "New parameter", iPos);
+		}
+#endif		
+		return ppData[iPos];}
+
+	inline CMatrix& operator=(const CMatrix& m) {
+		this->Init(m.NumRows(), m.NumColumns());
+		for (int i=0; i<m.NumRows(); i++)
+			this->ppData[i] = m[i];
+		return *this;
+	}
+
+#ifdef _DEBUG_
+	inline CVector<TData> operator[](const int iPos) const {
+		if ((iPos < 0) || (iPos > iRow - 1))
+		{
+			DebugError("Matrix: Reading vector out of bounds", "Row size",
+				iRow, "New parameter", iPos);
+		}
+		return ppData[iPos];}
+#endif
+	inline int NumRows(void) const { return iRow;}
+	inline int NumColumns(void) const { return iCol;}
+
+protected:
+	CVector<TData>*	ppData;
+	int				iRow;
+	int				iCol;
+};
+
+
+/* Implementation *************************************************************/
+template<class TData> void CMatrix<TData>::Init(const int iNewRow,
+												const int iNewColumn)
+{
+	iRow = iNewRow;
+	iCol = iNewColumn;
+
+	if (iRow > 0)
+	{
+		/* Delete resources from previous init */
+		if (ppData != NULL)
+			delete[] ppData;
+
+		/* Allocate new memory for history buffer */
+		ppData = new CVector<TData>[iRow];
+		for (int i = 0; i < iRow; i++)
+			ppData[i].Init(iNewColumn);
+	}
+}
+
+template<class TData> void CMatrix<TData>::Init(const int iNewRow,
+												const int iNewColumn,
+												const TData tIniVal)
+{
+	/* Call actual init routine */
+	Init(iNewRow, iNewColumn);
+
+	/* Set values */
+	Reset(tIniVal);
+}
+
+template<class TData> void CMatrix<TData>::Reset(const TData tResetVal)
+{
+	/* Set all values to reset value */
+	for (int i = 0; i < iRow; i++)
+		for (int j = 0; j < ppData[i].Size(); j++)
+			ppData[i][j] = tResetVal;
+}
+
+template<class TData> CMatrix<TData>::~CMatrix()
+{
+	/* Delete buffer */
+	if (ppData != NULL)
+		delete[] ppData;
+}
+
+
+#endif // !defined(VECTOR_H__3B0BA660_CA6LIUBEFIB2B_23E7A0D31912__INCLUDED_)
diff --git a/qsstv/videocapt/videocapture.cpp b/qsstv/videocapt/videocapture.cpp
new file mode 100644
index 0000000..c15f800
--- /dev/null
+++ b/qsstv/videocapt/videocapture.cpp
@@ -0,0 +1,823 @@
+/*
+ *      videoCapture.cpp -- Kapture
+ *
+ *      Copyright (C) 2006-2009
+ *          Detlev Casanova (detlev.casanova at gmail.com)
+ *
+ *      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.
+ *
+ */
+#define DEBUG
+#include "videocapture.h"
+#include "configparams.h"
+#include "qsstvglobal.h"
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/select.h>
+
+#include <QtGui>
+#include <QApplication>
+#include <QMainWindow>
+#include <QImage>
+#include <QPixmap>
+#include <QLabel>
+#include <QSize>
+
+
+#define CLIP(x) ( (x)>=0xFF ? 0xFF : ( (x) <= 0x00 ? 0x00 : (x) ) )
+
+
+videoCapture::videoCapture()
+{
+	dev = 0;
+	opened = false;
+	allocated = false;
+  localImage=NULL;
+  numBuffers=2;
+
+}
+
+videoCapture::~videoCapture()
+{
+  if(localImage!=NULL) delete localImage;
+	close();
+}
+
+void videoCapture::close()
+{
+  if(!opened) return;
+	::close(dev);
+	opened = false;
+	allocated = false;
+}
+
+bool videoCapture::open()
+{
+	struct v4l2_capability cap;
+	int ret;
+
+
+  if (opened) return true;
+  addToLog("opening Videocapture device",LOGCAM);
+
+  dev = ::open(videoDevice.toLatin1().data(), O_RDWR);
+	if (dev < 0) 
+    {
+     addToLog(QString("Error opening %1, %2").arg(videoDevice).arg(errno),LOGCAM);
+      return false;
+    }
+
+  memset(&cap, 0, sizeof cap);
+  ret = ioctl(dev, VIDIOC_QUERYCAP, &cap);
+  if (ret < 0)
+    {
+      addToLog(QString("Error querying capabilities for %1, %2").arg(videoDevice).arg(errno),LOGCAM);
+      return false;
+    }
+  dumpCaps(cap);
+
+	if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) 
+    {
+      addToLog(QString("Error checking caps for %1").arg(videoDevice),LOGCAM);
+      return false;
+    }
+
+  formatList=getFormatList(descripList);
+  getFormat();
+  setFormat(currentWidth(), currentHeight(), currentPixelFormat());
+  sizeList=getSizesList();
+
+  //Allocate buffers
+  if (!allocated)
+    {
+      memset(&rb, 0, sizeof rb);
+      rb.count = numBuffers;
+      rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+      rb.memory = V4L2_MEMORY_MMAP;
+
+        ret = ioctl(dev, VIDIOC_REQBUFS, &rb);
+        if (ret < 0)
+          {
+            addToLog(QString("Unable to allocate buffersfor %1, %2").arg(videoDevice).arg(errno),LOGCAM);
+            return false;
+          }
+        allocated = true;
+     }
+
+
+
+
+	opened = true;
+  return true;
+}
+
+QList<int> videoCapture::getFormatList(QList<QString> &description) const
+{
+	QList<int> formatList;
+	int ret;
+	struct v4l2_fmtdesc fmtList;
+  addToLog("getFomatList()",LOGCAM);
+	memset(&fmtList, 0, sizeof fmtList);
+	fmtList.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	int i = 0;
+
+	do
+	{
+		fmtList.index = i;
+		if ((ret = ioctl(dev, VIDIOC_ENUM_FMT, &fmtList)) < 0)
+			break;
+		else
+		{
+			formatList.append((int)fmtList.pixelformat);
+			description.append((char*)fmtList.description);
+		}
+		i++;
+	}
+	while (ret != EINVAL);
+	return formatList;
+}
+
+QList<QSize> videoCapture::getSizesList() const
+{
+	int i = 0;
+	QList<QSize> rSizes;
+	QSize tmp;
+#ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY // sort of test for v4l2 if this one does not exist, v4l2_frmsizeenum will not exist
+	struct v4l2_frmsizeenum sizes;
+  addToLog("getSizesList()",LOGCAM);
+	memset(&sizes, 0, sizeof sizes);
+	sizes.pixel_format = currentPixelFormat();
+	sizes.index = i;
+	while(ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &sizes) != -1)
+	{
+		tmp.setWidth((int)sizes.discrete.width);
+		tmp.setHeight((int)sizes.discrete.height);
+		rSizes.append(tmp);
+		i++;
+		sizes.index = i;
+	}
+#else
+	tmp.setWidth(320);
+	tmp.setHeight(240);
+	rSizes.append(tmp);
+#endif
+	return rSizes;
+}
+
+
+bool videoCapture::setFormat(unsigned int width, unsigned int height, int pixelformat)
+{
+  addToLog("setFormat",LOGCAM);
+  memset(&fmt, 0, sizeof fmt);
+  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  fmt.fmt.pix.width = width;
+  fmt.fmt.pix.height = height;
+  fmt.fmt.pix.field = V4L2_FIELD_ANY;
+  fmt.fmt.pix.pixelformat = pixelformat;
+	if (ioctl(dev, VIDIOC_S_FMT, &fmt) < 0)
+    {
+      addToLog(QString("Error while setting format , %1").arg(strerror(errno)),LOGCAM);
+      return false;
+    }
+  return true;
+}
+
+bool videoCapture::getFormat()
+{
+  addToLog("getFormat",LOGCAM);
+  memset(&fmt, 0, sizeof fmt);
+  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  if (ioctl(dev, VIDIOC_G_FMT, &fmt) < 0)
+    {
+      addToLog(QString("Error while getting format , %1").arg(errno),LOGCAM);
+      return false;
+    }
+  return true;
+}
+
+
+
+bool videoCapture::getFrame()
+{
+	int ret = 0;
+ // addToLog("getFrame",LOGCAM);
+
+	// Dequeue a buffer.
+  ret = ioctl(dev, VIDIOC_DQBUF, &buf);
+  //addToLog(QString("Dequeue buffer %1").arg(buf.index),LOGCAM);
+  if (ret < 0)
+    {
+      addToLog(QString("Unable to dequeue buffer , %1").arg(strerror(errno)),LOGCAM);
+      return false;
+  }
+//  while(ioctl(dev, VIDIOC_DQBUF, &buf)<0)
+//  {
+//    qApp->processEvents();
+//  }
+
+	// Save the image.
+	if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+	{
+    addToLog(QString("Unsupported MJPEG Format"),LOGCAM);
+    return false;
+	}
+
+	if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+	{
+    YUV422toRGB888_ITU_R(mem[buf.index],currentWidth(), currentHeight());
+
+	}
+	
+	// Requeue the buffer.
+	ret = ioctl(dev, VIDIOC_QBUF, &buf);
+  //addToLog(QString("Requeue buffer %1").arg(buf.index),LOGCAM);
+	if (ret < 0) 
+	{
+    addToLog(QString("Unable to requeue buffer %1").arg(errno),LOGCAM);
+    return false;
+	}
+
+  return true;
+}
+
+/**
+  Convert from YUV422 format to RGB888 using ITU_R_FLOAT. Formulae are described on http://en.wikipedia.org/wiki/YUV
+
+  \param width width of image
+  \param height height of image
+  \param src source
+  \param dst destination
+*/
+void videoCapture::YUV422toRGB888_ITU_R( unsigned char *src,int width, int height)
+{
+  int line, column;
+  unsigned char *py, *pu, *pv;
+  unsigned char r,g,b;
+  QRgb *dst;
+
+  if (localImage!=NULL) delete localImage;
+  localImage=new QImage( width,height,QImage::Format_RGB32);
+
+  /* In this format each four bytes is two pixels. Each four bytes is two Y's, a Cb and a Cr.
+     Each Y goes to one of the pixels, and the Cb and Cr belong to both pixels. */
+  py = src;
+  pu = src + 1;
+  pv = src + 3;
+  for (line = 0; line < height; ++line)
+    {
+      dst=(QRgb *)localImage->scanLine(line);
+      for (column = 0; column < width; ++column) // ITU-R float
+        {
+          r = CLIP((double)*py + 1.402*((double)*pv-128.0));
+          g = CLIP((double)*py - 0.344*((double)*pu-128.0) - 0.714*((double)*pv-128.0));
+          b = CLIP((double)*py + 1.772*((double)*pu-128.0));
+          dst[column]=qRgb(r,g,b);
+          // increase py every time
+          py += 2;
+          // increase pu,pv every second time
+          if ((column & 1)==1)
+            {
+              pu += 4;
+              pv += 4;
+            }
+        }
+    }
+}
+
+
+/**
+  Convert from YUV422 format to RGB888 using NTSC. Formulae are described on http://en.wikipedia.org/wiki/YUV
+
+  \param width width of image
+  \param height height of image
+  \param src source
+  \param dst destination
+*/
+
+void videoCapture::YUV422toRGB888_NTSC(unsigned char *src,int width, int height)
+{
+  int line, column;
+  unsigned char *py, *pu, *pv;
+  unsigned char r,g,b;
+  QRgb *dst;
+
+  if (localImage!=NULL) delete localImage;
+  localImage=new QImage( width,height,QImage::Format_RGB32);
+
+  /* In this format each four bytes is two pixels. Each four bytes is two Y's, a Cb and a Cr.
+     Each Y goes to one of the pixels, and the Cb and Cr belong to both pixels. */
+  py = src;
+  pu = src + 1;
+  pv = src + 3;
+  for (line = 0; line < height; ++line)
+    {
+      dst=(QRgb *)localImage->scanLine(line);
+      for (column = 0; column < width; ++column)
+        {
+          // NTSC integer
+          r = CLIP( (298*(*py-16) + 409*(*pv-128) + 128) >> 8 );
+          g = CLIP( (298*(*py-16) - 100*(*pu-128) - 208*(*pv-128) + 128) >> 8 );
+          b = CLIP( (298*(*py-16) + 516*(*pu-128) + 128) >> 8 );
+          dst[column]=qRgb(r,g,b);
+          // increase py every time
+          py += 2;
+          // increase pu,pv every second time
+          if ((column & 1)==1)
+            {
+              pu += 4;
+              pv += 4;
+            }
+        }
+    }
+}
+
+
+int videoCapture::changeCtrl(int ctrl, int value) // an enum for formats and reorganisation would be great...
+{
+	struct v4l2_queryctrl queryctrl;
+	struct v4l2_control control;
+	
+	if(!opened) // At the begining of the function.
+	{
+		return -1;
+	}
+/*
+ * ctrl values :
+ * 	0 : Saturation
+ * 	1 : Power line Frequency (néons)
+ * 	2 : Brightness
+ * 	3 : Contrast
+ * 	4 : Sharpness
+ * 	5 : Reset Pan/Tilt
+ */
+	__u32 CTRL;
+	switch(ctrl)
+	{
+		case Saturation: 
+		{
+			CTRL = V4L2_CID_SATURATION;
+			break;
+		}
+		case Brightness: 
+		{
+			CTRL = V4L2_CID_BRIGHTNESS;
+			break;
+		}
+  case Hue:
+  {
+    CTRL = V4L2_CID_HUE;
+    break;
+  }
+		case Contrast: 
+		{
+			CTRL = V4L2_CID_CONTRAST;
+			break;
+		}
+//#ifdef V4L2_CID_POWER_LINE_FREQUENCY
+//		case PowerLineFreq:
+//		{
+//			CTRL = V4L2_CID_POWER_LINE_FREQUENCY;
+//			break;
+//		}
+//#endif
+		case Sharpness:
+		{
+#ifdef 	V4L2_CID_SHARPNESS
+		CTRL = V4L2_CID_SHARPNESS;
+#else
+	        CTRL=0;
+#endif
+
+		break;
+		}
+
+		default:
+			CTRL = 0;
+	}
+
+	memset (&queryctrl, 0, sizeof queryctrl);
+	memset (&control, 0, sizeof control);
+	queryctrl.id = CTRL;
+	if (-1 == ioctl (dev, VIDIOC_QUERYCTRL, &queryctrl)) 
+	{
+	        if (errno != EINVAL) 
+		{
+#ifdef DEBUG
+			perror ("VIDIOC_QUERYCTRL");
+#endif
+			return EXIT_FAILURE;
+		} 
+	} else 
+	{
+		control.id = CTRL;
+		control.value = value;
+		if (-1 == ioctl (dev, VIDIOC_S_CTRL, &control)) {
+#ifdef DEBUG
+			perror("VIDIOC_S_CTRL");
+			printf(" * Error while setting control\n");
+#endif
+			return EXIT_FAILURE;
+        	}
+	}
+	return EXIT_SUCCESS;
+}
+
+int videoCapture::currentWidth() const
+{
+	return (int) fmt.fmt.pix.width;
+}
+
+int videoCapture::currentHeight() const
+{
+	return (int) fmt.fmt.pix.height;
+}
+
+int videoCapture::currentPixelFormat() const
+{
+	return (int) fmt.fmt.pix.pixelformat;
+}
+
+
+int videoCapture::defaultCtrlVal(unsigned int control, int &defaultValue)
+{
+	struct v4l2_queryctrl queryctrl;
+	QString ctrl;
+	
+	if(!opened)
+	{
+		return false;
+	}
+	
+	memset(&queryctrl, 0, sizeof queryctrl);
+	switch(control)
+	{
+		case Saturation : 
+		{
+			ctrl = "Saturation";
+			queryctrl.id = V4L2_CID_SATURATION;
+			break;
+		}
+		case Brightness : 
+		{
+			ctrl = "Brightness";
+			queryctrl.id = V4L2_CID_BRIGHTNESS;
+			break;
+		}
+  case Hue :
+  {
+    ctrl = "Hue";
+    queryctrl.id = V4L2_CID_HUE;
+    break;
+  }
+		case Contrast : 
+		{
+			ctrl = "Contrast";
+			queryctrl.id = V4L2_CID_CONTRAST;
+			break;
+		}
+
+//		case PowerLineFreq :
+//		{
+//			ctrl = "Powerline Frequecy";
+//			queryctrl.id = V4L2_CID_POWER_LINE_FREQUENCY;
+//			break;
+//		}
+
+		case Sharpness : 
+		{
+			ctrl = "Sharpness";
+#ifdef V4L2_CID_SHARPNESS
+			queryctrl.id = V4L2_CID_SHARPNESS;
+#endif
+			break;
+		}
+
+		default :
+			ctrl = "ERROR";
+	}
+
+	QString str;
+	if (-1 == ioctl(dev, VIDIOC_QUERYCTRL, &queryctrl))
+	{
+    addToLog(QString("Unable to set control %1, %2").arg(ctrl).arg(errno),LOGCAM);
+    return false;
+	}
+
+	defaultValue = (int)queryctrl.default_value;
+
+	return true;
+}
+
+//bool videoCapture::panTiltSupported()
+//{
+//	struct v4l2_queryctrl queryctrl;
+
+//  if(!opened)	return false;
+
+//	memset(&queryctrl, 0, sizeof queryctrl);
+//	queryctrl.id = V4L2_CID_TILT_RELATIVE; // Could be V4L2_CID_PAN_RELATIVE.
+
+//  if (ioctl(dev, VIDIOC_QUERYCTRL, &queryctrl)<0)
+//	{
+//     addToLog(QString("Unable to check wether Pan Tilt is supported, %1").arg(errno),LOGCAM);
+//      return false;
+//	}
+
+//	if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+//	{
+//     addToLog("Pan & Tilt not supported."),LOGCAM);
+//     return false; //FLAG_NOT_SUPPORTED;
+//	}
+
+//	return true;
+//}
+
+//void videoCapture::turnRight()
+//{
+//#ifdef V4L2_CID_PAN_RELATIVE
+//	struct v4l2_queryctrl queryctrl;
+//	struct v4l2_control control;
+
+//	memset (&queryctrl, 0, sizeof queryctrl);
+//	memset (&control, 0, sizeof control);
+//	queryctrl.id = V4L2_CID_PAN_RELATIVE;
+//	if (-1 == ioctl (dev, VIDIOC_QUERYCTRL, &queryctrl))
+//	{
+//	        if (errno != EINVAL)
+//		{
+//			perror ("VIDIOC_QUERYCTRL");
+//			return;
+//		}
+//	} else
+//	{
+//		control.id = V4L2_CID_PAN_RELATIVE;
+//		control.value = -320;
+//		if (-1 == ioctl (dev, VIDIOC_S_CTRL, &control)) {
+//			perror("VIDIOC_S_CTRL");
+//			return;
+//        	}
+//	}
+//#endif
+//}
+
+//void videoCapture::turnLeft()
+//{
+//#ifdef V4L2_CID_PAN_RELATIVE
+//	struct v4l2_queryctrl queryctrl;
+//	struct v4l2_control control;
+
+//	memset (&queryctrl, 0, sizeof queryctrl);
+//	memset (&control, 0, sizeof control);
+//	queryctrl.id = V4L2_CID_PAN_RELATIVE;
+//	if (-1 == ioctl (dev, VIDIOC_QUERYCTRL, &queryctrl))
+//	{
+//	        if (errno != EINVAL)
+//		{
+//			perror ("VIDIOC_QUERYCTRL");
+//			return;
+//		}
+//	} else
+//	{
+//		control.id = V4L2_CID_PAN_RELATIVE;
+//		control.value = 320;
+//		if (-1 == ioctl (dev, VIDIOC_S_CTRL, &control)) {
+//			perror("VIDIOC_S_CTRL");
+//			return;
+//        	}
+//	}
+//#endif
+//}
+
+//void videoCapture::turnUp()
+//{
+//#ifdef V4L2_CID_TILT_RELATIVE
+//	struct v4l2_queryctrl queryctrl;
+//	struct v4l2_control control;
+	
+//	memset (&queryctrl, 0, sizeof queryctrl);
+//	memset (&control, 0, sizeof control);
+//	queryctrl.id = V4L2_CID_TILT_RELATIVE;
+//	if (-1 == ioctl (dev, VIDIOC_QUERYCTRL, &queryctrl))
+//	{
+//	        if (errno != EINVAL)
+//		{
+//			perror ("VIDIOC_QUERYCTRL");
+//			return;
+//		}
+//	} else
+//	{
+//		control.id = V4L2_CID_TILT_RELATIVE;
+//		control.value = -320;
+//		if (-1 == ioctl (dev, VIDIOC_S_CTRL, &control)) {
+//			perror("VIDIOC_S_CTRL");
+//			return;
+//        	}
+//	}
+//#endif
+//}
+
+//void videoCapture::turnDown()
+//{
+//#ifdef V4L2_CID_TILT_RELATIVE
+//	struct v4l2_queryctrl queryctrl;
+//	struct v4l2_control control;
+
+//	memset (&queryctrl, 0, sizeof queryctrl);
+//	memset (&control, 0, sizeof control);
+//	queryctrl.id = V4L2_CID_TILT_RELATIVE;
+//	if (-1 == ioctl (dev, VIDIOC_QUERYCTRL, &queryctrl))
+//	{
+//	        if (errno != EINVAL)
+//		{
+//			perror ("VIDIOC_QUERYCTRL");
+//			return;
+//		}
+//	} else
+//	{
+//		control.id = V4L2_CID_TILT_RELATIVE;
+//		control.value = 320;
+//		if (-1 == ioctl (dev, VIDIOC_S_CTRL, &control)) {
+//			perror("VIDIOC_S_CTRL");
+//			return;
+//        	}
+//	}
+//#endif
+//}
+
+
+bool videoCapture::captureStop()
+{
+  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  int ret;
+
+  if(!streaming) return false;
+  ret = ioctl(dev, VIDIOC_STREAMOFF, &type);
+  if (ret < 0)
+    {
+     addToLog(QString("Unable  to stop capture, %1").arg(errno),LOGCAM);
+     return false;
+    }
+
+  streaming = false;
+  return true;
+}
+
+bool videoCapture::captureStart()
+{
+  int i, ret;
+  if (!opened) return false;
+  abortCap=false;
+  addToLog("captureStart",LOGCAM);
+
+//  if ((ret = setFormat(currentWidth(), currentHeight(), currentPixelFormat())) != 0)
+//    {
+//      addToLog(QString("Set format error :  %1").arg(ret),LOGCAM);
+//      return false;
+//    }
+
+////Allocate buffers
+//  if (!allocated)
+//    {
+//      memset(&rb, 0, sizeof rb);
+//      rb.count = 2;
+//      rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+//      rb.memory = V4L2_MEMORY_MMAP;
+
+//      ret = ioctl(dev, VIDIOC_REQBUFS, &rb);
+//      if (ret < 0)
+//        {
+//         addToLog(QString("Unable to allocate buffers %1").arg(ret),LOGCAM);
+//         return false;
+//      }
+//      allocated = true;
+//    }
+
+  // Map the buffers.
+  memset(&buf, 0, sizeof buf);
+  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  buf.memory = V4L2_MEMORY_MMAP;
+  for (i = 0; i < numBuffers; i++)
+    {
+      buf.index = i;
+      ret = ioctl(dev, VIDIOC_QUERYBUF, &buf);
+      if (ret < 0)
+        {
+          addToLog(QString("Unable to query buffer %1").arg(ret),LOGCAM);
+          return false;
+        }
+      mem[i] = (uchar *) mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, dev, buf.m.offset);
+      if (mem[i] == MAP_FAILED)
+         {
+           addToLog(QString("Unable to map buffers %1").arg(ret),LOGCAM);
+           return false;
+         }
+       bufLength = buf.length;
+       mmaped = true;
+     }
+
+ // Queue the buffers
+
+  for (i = 0; i < numBuffers; i++)
+  {
+    buf.index = i;
+    ret = ioctl(dev, VIDIOC_QBUF, &buf);
+    if (ret < 0)
+    {
+      addToLog(QString("Unable to queue buffer %1").arg(errno),LOGCAM);
+      return false;
+    }
+  }
+
+  // Start streaming.
+  ret = ioctl(dev, VIDIOC_STREAMON, &buf.type);
+  if (ret < 0)
+  {
+    addToLog(QString("Unable to start capture %1").arg(errno),LOGCAM);
+    return false;
+  }
+  streaming = true;
+  return true;
+}
+
+bool videoCapture::stopStreaming()
+{
+  if(!streaming) return false;
+  if (munmap(mem[0], bufLength) == -1)
+  {
+    addToLog(QString("videoCapture::stopStreaming : munmap 0 failed. errno = %1").arg(errno),LOGCAM);
+  }
+
+  if (munmap(mem[1], bufLength) == -1)
+  {
+    addToLog(QString("videoCapture::stopStreaming : munmap 1 failed. errno = %1").arg(errno),LOGCAM);
+  }
+
+  if (munmap(mem[2], bufLength) == -1)
+  {
+    addToLog(QString("videoCapture::stopStreaming : munmap 2 failed. errno = %1").arg(errno),LOGCAM);
+  }
+
+  if (munmap(mem[3], bufLength) == -1)
+  {
+    addToLog(QString("videoCapture::stopStreaming : munmap 3 failed. errno = %1").arg(errno),LOGCAM);
+  }
+
+
+
+
+  else
+    mmaped = false;
+
+  if(captureStop() == 0)
+  {
+    streaming = false;
+    addToLog(" * Succesful Stopped",LOGCAM);
+  }
+  return true;
+}
+
+bool videoCapture::takeSnapshot()
+{
+  if(!open()) return false;
+  if(!captureStart()) return false;
+  if(!getFrame()) return false; // first frame always corrupted
+  if(!getFrame()) return false;
+  return stopStreaming();
+}
+
+bool videoCapture::startSnapshots()
+{
+  if(!open()) return false;
+  if(!captureStart()) return false;
+  return true;
+}
+
+
+void videoCapture::dumpCaps(v4l2_capability &cap)
+{
+  if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) addToLog("The device supports the Video Capture interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) addToLog("The device supports the Video Output interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) addToLog("The device supports the Video Overlay interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_VBI_CAPTURE) addToLog("The device supports the Raw VBI Capture interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_VBI_OUTPUT) addToLog("The device supports the Raw VBI Output interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) addToLog("The device supports the Sliced VBI Capture interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) addToLog("The device supports the Sliced VBI Output interface",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_RDS_CAPTURE) addToLog("[to be defined]",LOGCAM);
+#ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY
+  if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) addToLog("The device supports the Video Output Overlay (OSD) interface",LOGCAM);
+#endif
+  if (cap.capabilities & V4L2_CAP_TUNER) addToLog("The device has some sort of tuner or modulator",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_AUDIO) addToLog("The device has audio inputs or outputs.",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_READWRITE) addToLog("The device supports the read() and/or write() I/O methods.",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_ASYNCIO) addToLog("The device supports the asynchronous I/O methods.",LOGCAM);
+  if (cap.capabilities & V4L2_CAP_STREAMING) addToLog("The device supports the streaming I/O method.",LOGCAM);
+}
+
diff --git a/qsstv/videocapt/videocapture.h b/qsstv/videocapt/videocapture.h
new file mode 100644
index 0000000..a46b9d4
--- /dev/null
+++ b/qsstv/videocapt/videocapture.h
@@ -0,0 +1,92 @@
+/*
+ *      videoCapture.h -- Kapture
+ *
+ *      Copyright (C) 2006-2007
+ *          Detlev Casanova (detlev.casanova at gmail.com)
+ *
+ *      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 _VIDEOCAPTURE_H
+#define _VIDEOCAPTURE_H
+
+#include <QObject>
+#include <QImage>
+#include <linux/videodev2.h>
+#include "qsstvdefs.h"
+
+struct tableFormat
+{
+  QString description;
+  unsigned int val1;
+  unsigned int val2;
+};
+extern tableFormat sizeList[];
+extern tableFormat fpsList[];
+
+enum ecamSize{PICMAX,PICMIN,PICHALF};
+
+class videoCapture : public QObject
+{
+	Q_OBJECT
+
+public:
+  videoCapture();
+  ~videoCapture();
+	
+	void close();
+  bool  open();
+	QList<int>   getFormatList(QList<QString> &description) const;
+	QList<QSize> getSizesList() const;
+  QList<int> formatList;
+  QList<QSize> sizeList;
+  QList<QString> descripList;
+
+  bool setFormat(unsigned int width, unsigned int height, int pixelformat=V4L2_PIX_FMT_MJPEG);
+  bool getFormat();
+  bool getFrame();
+	int currentWidth() const;
+	int currentHeight() const;
+	int currentPixelFormat() const;
+	int changeCtrl(int ctrl, int value = 0);
+	int defaultCtrlVal(unsigned int control, int &defaultValue);
+  bool isOpened() const {return opened;};
+  bool takeSnapshot();
+  QImage *getImage() {return localImage;}
+  bool captureStart();
+  bool captureStop();
+  bool stopStreaming();
+  bool startSnapshots();
+  void abortCapture() { abortCap=true;}
+
+
+  enum econtrol {Saturation = 0,Hue,Brightness,Contrast,Sharpness};
+signals:
+  void imageReady();
+	
+
+private:
+	int dev;
+	v4l2_format fmt;
+	v4l2_buffer buf;
+	v4l2_requestbuffers rb;
+	bool allocated;
+	
+  uchar *mem[4];
+	size_t bufLength;
+  QImage *localImage;
+	bool opened;
+	bool mmaped;
+  void YUV422toRGB888_ITU_R( unsigned char *src,int width, int height);
+  void YUV422toRGB888_NTSC( unsigned char *src,int width, int height);
+  bool streaming;
+  void dumpCaps(v4l2_capability &cap);
+  int numBuffers;
+  bool abortCap;
+};
+#endif
+
diff --git a/qsstv/widgets/blockview.cpp b/qsstv/widgets/blockview.cpp
new file mode 100644
index 0000000..521ead0
--- /dev/null
+++ b/qsstv/widgets/blockview.cpp
@@ -0,0 +1,55 @@
+#include "blockview.h"
+#include "ui_blockview.h"
+#include <QPainter>
+
+blockView::blockView(QWidget *parent) :
+  QFrame(parent),
+  ui(new Ui::blockView)
+{
+  maxBlocks=40;
+  colFail=QColor(Qt::red);
+  colOK=QColor(Qt::green);
+  ui->setupUi(this);
+}
+
+blockView::~blockView()
+{
+  delete ui;
+}
+
+
+void blockView::paintEvent(QPaintEvent *)
+{
+  int i;
+  QRectF rct;
+  QPainter painter(this);
+  painter.setPen(QPen(colFail, 1, Qt::SolidLine));
+  painter.setBrush(QBrush(colFail));
+  painter.setRenderHint(QPainter::Antialiasing);
+  rct=QRectF(0,0,maxBlocks-1,30);
+  painter.setWindow(0, 0, maxBlocks-1,30);
+  painter.drawRect(rct);
+  painter.setPen(QPen(colOK, 1, Qt::SolidLine));
+  for(i=0;i<blockList.count();i++)
+    {
+      painter.drawLine(blockList.at(i),0,blockList.at(i),29);
+    }
+}
+
+void blockView::setColorFail(QColor color)
+{
+  colFail = color;
+//  update();
+}
+
+void blockView::setColorOK(QColor color)
+{
+  colOK = color;
+//  update();
+}
+
+ void blockView::setBlocks(QList<unsigned short> blkList)
+ {
+   blockList=blkList;
+//   update();
+ }
diff --git a/qsstv/widgets/blockview.h b/qsstv/widgets/blockview.h
new file mode 100644
index 0000000..41f8b5f
--- /dev/null
+++ b/qsstv/widgets/blockview.h
@@ -0,0 +1,34 @@
+#ifndef BLOCKVIEW_H
+#define BLOCKVIEW_H
+
+#include <QFrame>
+
+namespace Ui {
+  class blockView;
+  }
+
+class blockView : public QFrame
+{
+  Q_OBJECT
+  
+public:
+  explicit blockView(QWidget *parent = 0);
+  ~blockView();
+  void setColorFail(QColor color);
+  void setColorOK(QColor color);
+  void setMaxBlocks(int mb) {maxBlocks=mb;}
+  void setBlocks(QList<short unsigned int> blkList);
+
+protected:
+
+    void paintEvent(QPaintEvent *);
+  
+private:
+  Ui::blockView *ui;
+  QColor colFail;
+  QColor colOK;
+  QList<short unsigned int> blockList;
+  short int maxBlocks;
+};
+
+#endif // BLOCKVIEW_H
diff --git a/qsstv/widgets/blockview.ui b/qsstv/widgets/blockview.ui
new file mode 100644
index 0000000..4151c92
--- /dev/null
+++ b/qsstv/widgets/blockview.ui
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>blockView</class>
+ <widget class="QFrame" name="blockView">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>414</width>
+    <height>192</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::StyledPanel</enum>
+  </property>
+  <property name="frameShadow">
+   <enum>QFrame::Sunken</enum>
+  </property>
+  <property name="lineWidth">
+   <number>3</number>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/widgets/cameracontrol.cpp b/qsstv/widgets/cameracontrol.cpp
new file mode 100644
index 0000000..82a8330
--- /dev/null
+++ b/qsstv/widgets/cameracontrol.cpp
@@ -0,0 +1,144 @@
+#include "cameracontrol.h"
+#include "qsstvglobal.h"
+#include "videocapt/videocapture.h"
+#include "ui_cameracontrol.h"
+#include "utils/supportfunctions.h"
+#include <QSettings>
+#include <QPainter>
+
+cameraControl::cameraControl(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::cameraControl)
+{
+    ui->setupUi(this);
+    camera=new videoCapture;
+    readSettings();
+
+ }
+
+cameraControl::~cameraControl()
+{
+    camera->close();
+    delete camera;
+    writeSettings();
+    delete ui;
+}
+
+void cameraControl::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("Capture");
+  brightness=qSettings.value("brightness",128).toInt();
+  hue=qSettings.value("hue",0).toInt();
+  saturation=qSettings.value("saturation",128).toInt();
+  contrast=qSettings.value("contrast",128).toInt();
+  sharpness=qSettings.value("sharpness",128).toInt();
+  qSettings.endGroup();
+  setParams();
+  cameraActive=camera->open();
+  slotBrightnessSliderChanged();
+  slotHueSliderChanged();
+  slotSaturationSliderChanged();
+  slotContrastSliderChanged();
+  slotSharpnessSliderChanged();
+  connect(ui->brightnessSlider,SIGNAL(valueChanged(int)),SLOT(slotBrightnessSliderChanged()));
+  connect(ui->hueSlider,SIGNAL(valueChanged(int)),SLOT(slotHueSliderChanged()));
+  connect(ui->saturationSlider,SIGNAL(valueChanged(int)),SLOT(slotSaturationSliderChanged()));
+  connect(ui->contrastSlider,SIGNAL(valueChanged(int)),SLOT(slotContrastSliderChanged()));
+  connect(ui->sharpnessSlider,SIGNAL(valueChanged(int)),SLOT(slotSharpnessSliderChanged()));
+}
+
+void cameraControl::writeSettings()
+{
+  getParams();
+  QSettings qSettings;
+  qSettings.beginGroup("Capture");
+  qSettings.setValue( "brightness",brightness);
+  qSettings.setValue( "hue",hue);
+  qSettings.setValue( "saturation",saturation);
+  qSettings.setValue( "contrast",contrast);
+  qSettings.setValue( "sharpness",sharpness);
+  qSettings.endGroup();
+}
+
+
+void cameraControl::getParams()
+{
+  getValue(brightness,ui->brightnessSlider);
+  getValue(hue,ui->hueSlider);
+  getValue(saturation,ui->saturationSlider);
+  getValue(contrast,ui->contrastSlider);
+  getValue(sharpness,ui->sharpnessSlider);
+}
+
+void cameraControl::setParams()
+{
+  setValue(brightness,ui->brightnessSlider);
+  setValue(hue,ui->hueSlider);
+  setValue(saturation,ui->saturationSlider);
+  setValue(contrast,ui->contrastSlider);
+  setValue(sharpness,ui->sharpnessSlider);
+}
+
+int cameraControl::exec()
+{
+  if(!cameraActive) return false;
+  int timerID;
+  int result;
+  addToLog("cameracontrol exec",LOGCAM);
+  camera->startSnapshots();
+  timerID=startTimer(50);
+  result=QDialog::exec();
+  killTimer(timerID);
+  camera->abortCapture();
+  camera->stopStreaming();
+  if(result==QDialog::Accepted) return true;
+  return false;
+}
+
+
+void cameraControl::timerEvent(QTimerEvent *)
+ {
+     if(camera->getFrame())
+      {
+        ui->imageFrame->openImage(*camera->getImage());
+      }
+ }
+
+QImage *cameraControl::getImage()
+{
+  return camera->getImage();
+}
+
+void cameraControl::slotBrightnessSliderChanged()
+{
+  getParams();
+  camera->changeCtrl(videoCapture::Brightness,brightness);
+}
+
+void cameraControl::slotHueSliderChanged()
+{
+  getParams();
+  camera->changeCtrl(videoCapture::Hue,hue);
+}
+
+void cameraControl::slotSaturationSliderChanged()
+{
+  getParams();
+  camera->changeCtrl(videoCapture::Saturation,saturation);
+}
+
+void cameraControl::slotContrastSliderChanged()
+{
+  getParams();
+  camera->changeCtrl(videoCapture::Contrast,contrast);
+}
+
+void cameraControl::slotSharpnessSliderChanged()
+{
+  getParams();
+  camera->changeCtrl(videoCapture::Sharpness,sharpness);
+}
+
+
+
diff --git a/qsstv/widgets/cameracontrol.h b/qsstv/widgets/cameracontrol.h
new file mode 100644
index 0000000..98b1454
--- /dev/null
+++ b/qsstv/widgets/cameracontrol.h
@@ -0,0 +1,46 @@
+#ifndef CAMERACONTROL_H
+#define CAMERACONTROL_H
+
+#include <QDialog>
+
+
+namespace Ui {
+    class cameraControl;
+}
+class videoCapture;
+
+
+class cameraControl : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit cameraControl(QWidget *parent = 0);
+    ~cameraControl();
+    void readSettings();
+    void writeSettings();
+    int exec();
+    QImage *getImage();
+  private slots:
+    void slotBrightnessSliderChanged();
+    void slotHueSliderChanged();
+    void slotSaturationSliderChanged();
+    void slotContrastSliderChanged();
+    void slotSharpnessSliderChanged();
+
+private:
+    Ui::cameraControl *ui;
+    void getParams();
+    void setParams();
+    bool cameraActive;
+
+    int brightness;
+    int hue;
+    int saturation;
+    int contrast;
+    int sharpness;
+    videoCapture *camera;
+    void timerEvent(QTimerEvent *event);
+};
+
+#endif // CAMERACONTROL_H
diff --git a/qsstv/widgets/cameracontrol.ui b/qsstv/widgets/cameracontrol.ui
new file mode 100644
index 0000000..3bb34dd
--- /dev/null
+++ b/qsstv/widgets/cameracontrol.ui
@@ -0,0 +1,400 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>cameraControl</class>
+ <widget class="QDialog" name="cameraControl">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>230</width>
+    <height>364</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0,0,0,0">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="margin">
+    <number>1</number>
+   </property>
+   <item>
+    <widget class="imageViewer" name="imageFrame">
+     <property name="frameShape">
+      <enum>QFrame::StyledPanel</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Raised</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="_5">
+     <item>
+      <widget class="QLabel" name="sharpnessLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Courier [Xft]</family>
+         <pointsize>9</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Sharpness</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="sharpnessSlider">
+       <property name="minimum">
+        <number>0</number>
+       </property>
+       <property name="maximum">
+        <number>255</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>10</number>
+       </property>
+       <property name="value">
+        <number>127</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickInterval">
+        <number>1000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="_4">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="brightnessLabel">
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Courier [Xft]</family>
+         <pointsize>9</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Brightness</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="brightnessSlider">
+       <property name="maximum">
+        <number>255</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>10</number>
+       </property>
+       <property name="value">
+        <number>127</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickInterval">
+        <number>1000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="_3">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="hueLabel">
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Courier [Xft]</family>
+         <pointsize>9</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Hue</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="hueSlider">
+       <property name="maximum">
+        <number>255</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>10</number>
+       </property>
+       <property name="value">
+        <number>127</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickInterval">
+        <number>1000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="_2">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="saturationLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Courier [Xft]</family>
+         <pointsize>9</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Saturation</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="saturationSlider">
+       <property name="maximum">
+        <number>255</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>10</number>
+       </property>
+       <property name="value">
+        <number>127</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickInterval">
+        <number>1000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="contrastLabel">
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Courier [Xft]</family>
+         <pointsize>9</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Contrast</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="contrastSlider">
+       <property name="minimum">
+        <number>0</number>
+       </property>
+       <property name="maximum">
+        <number>255</number>
+       </property>
+       <property name="singleStep">
+        <number>1</number>
+       </property>
+       <property name="pageStep">
+        <number>10</number>
+       </property>
+       <property name="value">
+        <number>127</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickInterval">
+        <number>1000</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>imageViewer</class>
+   <extends>QFrame</extends>
+   <header>widgets/imageviewer.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>cameraControl</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>cameraControl</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/widgets/fftdisplay.cpp b/qsstv/widgets/fftdisplay.cpp
new file mode 100644
index 0000000..86cc0de
--- /dev/null
+++ b/qsstv/widgets/fftdisplay.cpp
@@ -0,0 +1,254 @@
+#include "fftdisplay.h"
+#include "qsstvglobal.h"
+#include "math.h"
+#include <QPainter>
+
+
+
+
+
+
+
+fftDisplay::fftDisplay(QWidget *parent) : QLabel(parent)
+{
+  blockIndex=0;
+  arMag=NULL;
+  arMagAvg=NULL;
+//  ready=false;
+  windowWidth=0;
+  windowHeight=0;
+  im=NULL;
+  fftArray=NULL;
+//  windowWidth=294;windowHeight=112;
+//  im=new QImage( windowWidth,windowHeight,QImage::Format_RGB32);
+  setSize(120,180);
+
+  out=NULL;
+  dataBuffer=NULL;
+  showWaterfall=false;
+  fftMax=FFTMAX;
+  range=RANGE;
+}
+
+fftDisplay::~fftDisplay()
+{
+//  ready=false;
+  delete im;
+  if(fftArray) delete fftArray;
+  fftw_destroy_plan(plan);
+  if(out) fftw_free(out);
+  if(dataBuffer) fftw_free(dataBuffer);
+  if (arMag) delete [] arMag;
+  if(arMagAvg) delete []arMagAvg;
+}
+
+
+
+void fftDisplay::setSize(int w,int h)
+{
+  windowWidth=w;
+  windowHeight=h;
+  if(w==0 || h==0)
+  {
+
+      qDebug() << "Null Image";
+  }
+  if(im)
+    {
+      *im=im->scaled(QSize(windowWidth, windowHeight));
+     }
+  else
+    {
+      im=new QImage( windowWidth,windowHeight,QImage::Format_RGB32);
+      im->fill(Qt::black);
+    }
+  if(fftArray) delete fftArray;
+  fftArray=new QPolygon(windowWidth);
+
+  if(arMag) delete []arMag;
+  arMag=new double[windowWidth];
+  if(arMagAvg) delete []arMagAvg;
+  arMagAvg=new double[windowWidth];
+  for(int i=0;i<windowWidth;i++) arMag[i]=0.;
+  for(int i=0;i<windowWidth;i++) arMagAvg[i]=0.;
+}
+
+
+
+void fftDisplay::init(int size,int nblocks,int isamplingrate)
+{
+  int i;
+  windowSize=size;
+  fftLength=windowSize*nblocks;
+  blocks=nblocks;
+  createHamming();
+  samplingrate=isamplingrate;
+  //prepare fft
+  out = (double *)fftw_malloc(fftLength * sizeof(double));
+  dataBuffer  = (double *)fftw_malloc(fftLength * sizeof(double));
+  arMag=new double[windowSize];
+  for(i=0;i<windowWidth;i++) arMag[i]=0.;
+  arMagAvg=new double[windowSize];
+  for(i=0;i<windowWidth;i++) arMagAvg[i]=0.;
+  fftArray=new QPolygon(windowWidth);
+   // create the fftw plan
+  plan = fftw_plan_r2r_1d(fftLength, dataBuffer, out, FFTW_R2HC, FFTW_ESTIMATE);
+  update();
+  QLabel::update();
+
+}
+
+void fftDisplay::createHamming()
+{
+  int i;
+  hammingBuffer= new double[fftLength];
+  for(i=0;i<fftLength;i++)
+    {
+      hammingBuffer[i]=0.54-(0.46*cos(2*M_PI*((double)i/((double)(fftLength-1)))));
+    }
+
+}
+
+void fftDisplay::realFFT(short int *data)
+{
+  int i,j;
+  for(i=0,j=windowSize*blockIndex;i<windowSize;i++,j++)
+  {
+    dataBuffer[j]=(double)data[i];
+  }
+  doFFT();
+}
+
+void fftDisplay::realFFT(float *data)
+{
+  int i,j;
+  double max=0;
+  for(i=0,j=windowSize*blockIndex;i<windowSize;i++,j++)
+  {
+    dataBuffer[j]=(double)data[i];
+    if(dataBuffer[j]>max) max=dataBuffer[j];
+  }
+  doFFT();
+}
+
+void fftDisplay::realFFT(double *data)
+{
+  int i,j;
+  for(i=0,j=windowSize*blockIndex;i<windowSize;i++,j++)
+  {
+    dataBuffer[j]=data[i];
+
+  }
+  doFFT();
+}
+
+void fftDisplay::doFFT()
+{
+  int i;
+  double val;
+  double re,imag,tmp,step;
+  double maxTmp=0;
+  double magTmp;
+  QColor c;
+  blockIndex++;
+  if(blockIndex<blocks) return;
+  blockIndex=0;
+//   apply hamming
+//  if(!ready) return;
+  for(i=0;i<fftLength;i++)
+    {
+
+      dataBuffer[i]*=hammingBuffer[i];
+      if(dataBuffer[i]>maxTmp) maxTmp=dataBuffer[i];
+    }
+
+  fftw_execute(plan);
+  step=(double)samplingrate/(double)fftLength;  //freq step per bin
+  arMag[0]=0;
+  arMagAvg[0]=0;
+  for(i=0;i<windowWidth;i++)
+    {
+      int idx=rint((FFTLOW+(i*FFTSPAN)/windowWidth)/step);
+      re=out[idx]/fftLength;
+      imag=out[fftLength-idx]/fftLength;
+      tmp=10*log10(2*(re*re+imag*imag))-55;
+      arMag[i]=tmp;
+      if(arMagAvg[i]<tmp) arMagAvg[i]=tmp;
+      else arMagAvg[i]=arMagAvg[i]*(1-VALAVG)+VALAVG*tmp;
+    }
+  maxMagnitude=fftMax;
+
+  for(i=0;i<windowWidth;i++)
+    {
+      magTmp=arMagAvg[i];
+      if(magTmp<(maxMagnitude-range)) magTmp=maxMagnitude-range;
+      fftArray->setPoint(i,i,((windowHeight)*(maxMagnitude-magTmp))/range);
+    }
+  memmove(im->scanLine(1),im->scanLine(0),(windowWidth*(windowHeight-2))*sizeof(uint));
+  uint *ptr=(uint *)im->scanLine(0);
+  for(i=0;i<windowWidth;i++)
+    {
+      //              arMag[i]=fftMin;
+      if(arMag[i]>fftMax) arMag[i]=fftMax;
+      if(arMag[i]<fftMax-range) arMag[i]=fftMax-range;
+      val=((arMag[i]-(fftMax-range))/range); // value in range 0-1
+      if(val<0) val=0;
+      if(val>1) val=1;
+      //              tmp=rint(180*((double)i)/((double)FFTSPAN));
+      //              c.setHsv(240-val*240,255,255);
+      c.setHsv(240-val*60,255,val*255);
+      //      c.setRgb(val*255,val*255,val*255);
+      ptr[i]=c.rgb();
+      //              ptr[i]=(uint) val*256+255;
+    }
+  update();
+}
+
+
+
+
+void fftDisplay::paintEvent(QPaintEvent *p)
+{
+
+  QPen pn;
+
+  //  if(!ready) return;
+  QPainter painter(this);
+  //  painter.setWindow(0,0,windowWidth,windowHeight);
+  if((windowWidth!=width())||(windowHeight!=height()))
+    {
+      setSize(width(),height());
+      return;
+    }
+  //  painter.setRenderHint(QPainter::Antialiasing);
+  if(!showWaterfall)
+    {
+      pn.setColor(Qt::red);
+      pn.setWidth(1);
+      painter.setPen(pn);
+      painter.drawLine((((marker1-FFTLOW)*windowWidth)/FFTSPAN),0,(((marker1-FFTLOW)*windowWidth)/FFTSPAN),windowHeight);
+      painter.drawLine((((marker2-FFTLOW)*windowWidth)/FFTSPAN),0,(((marker2-FFTLOW)*windowWidth)/FFTSPAN),windowHeight);
+      painter.drawLine((((marker3-FFTLOW)*windowWidth)/FFTSPAN),0,(((marker3-FFTLOW)*windowWidth)/FFTSPAN),windowHeight);
+
+      pn.setColor(Qt::green);
+      painter.setPen(pn);
+      painter.drawPolyline(*fftArray);
+
+    }
+  else
+    {
+      painter.drawImage(0,0,*im);
+    }
+  QLabel::paintEvent(p);
+}
+
+
+
+void fftDisplay::mousePressEvent( QMouseEvent *e )
+{
+  if (e->button() == Qt::LeftButton)
+    {
+      showWaterfall=!showWaterfall;
+    }
+}
+
diff --git a/qsstv/widgets/fftdisplay.h b/qsstv/widgets/fftdisplay.h
new file mode 100644
index 0000000..91bf8c3
--- /dev/null
+++ b/qsstv/widgets/fftdisplay.h
@@ -0,0 +1,71 @@
+#ifndef FFTDISPLAY_H
+#define FFTDISPLAY_H
+#include <QtGui>
+#include <QLabel>
+#include "qsstvdefs.h"
+#include "fftw3.h"
+#include <qpolygon.h>
+#include <QImage>
+
+
+#define FFTAVERAGING 0.1
+#define VALAVG 0.02
+#define FFTHIGH 3000
+#define FFTLOW  200
+#define FFTSPAN (FFTHIGH-FFTLOW)
+#define FFTMAX 75.
+#define RANGE 30.
+
+class fftDisplay : public QLabel
+{
+  Q_OBJECT
+  
+public:
+  explicit fftDisplay(QWidget *parent=0);
+  ~fftDisplay();
+
+  void init(int size,int slices,int isamplingrate);
+  void realFFT(short int *iBuffer);
+  void realFFT(float *iBuffer);
+  void realFFT(double *iBuffer);
+  void setMaxDb(int mb){fftMax=mb;}
+  void setRange(int rg) {range=rg;}
+  void displayWaterfall(bool wf) {showWaterfall=wf;}
+  void setMarkers(int mrk1=0, int mrk2=0, int mrk3=0){ marker1=mrk1;marker2=mrk2;marker3=mrk3;update();}
+
+
+private:
+  void createHamming();
+  void doFFT();
+  void paintEvent(QPaintEvent *p);
+  void mousePressEvent( QMouseEvent *e );
+  double *hammingBuffer;
+  double *dataBuffer;
+  int windowSize;
+  int fftLength;
+  int samplingrate;
+  unsigned int blocks;
+  unsigned int blockIndex;
+  fftw_plan plan;
+  double *arMag;
+  double *arMagAvg;
+  double *out;
+  double maxMagnitude;
+  QPolygon *fftArray;
+//  bool ready;
+  bool showWaterfall;
+  double fftMax;
+  double fftMin;
+  double range;
+  QImage *im;
+  int windowWidth;
+  int windowHeight;
+  void setSize(int w,int h);
+  QMutex mutex;
+  int marker1;
+  int marker2;
+  int marker3;
+
+};
+
+#endif // FFTDISPLAY_H
diff --git a/qsstv/widgets/freqform.ui b/qsstv/widgets/freqform.ui
new file mode 100644
index 0000000..e8e7158
--- /dev/null
+++ b/qsstv/widgets/freqform.ui
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>freqForm</class>
+ <widget class="QDialog" name="freqForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>270</width>
+    <height>139</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>8</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Test Signal</string>
+  </property>
+  <layout class="QVBoxLayout">
+   <property name="spacing">
+    <number>6</number>
+   </property>
+   <property name="margin">
+    <number>11</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QVBoxLayout">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="frequencyLabel">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="text">
+          <string>Frequency</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSpinBox" name="frequencySpinBox">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="minimum">
+          <number>300</number>
+         </property>
+         <property name="maximum">
+          <number>2500</number>
+         </property>
+         <property name="value">
+          <number>1200</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QVBoxLayout">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="durationLabel">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="text">
+          <string>Duration (sec)</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSpinBox" name="durationSpinBox">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="value">
+          <number>10</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Expanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>okButton</sender>
+   <signal>clicked()</signal>
+   <receiver>freqForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>cancelButton</sender>
+   <signal>clicked()</signal>
+   <receiver>freqForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/widgets/imageviewer.cpp b/qsstv/widgets/imageviewer.cpp
new file mode 100644
index 0000000..77af53f
--- /dev/null
+++ b/qsstv/widgets/imageviewer.cpp
@@ -0,0 +1,549 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "imageviewer.h"
+#include "utils/supportfunctions.h"
+#include "utils/logging.h"
+#include "configparams.h"
+#include "dispatcher.h"
+#include "txwidget.h"
+#include "utils/qjp2io.h"
+
+#include <QtGui>
+#include <QMenu>
+
+#define RATIOSCALE 800.
+
+/**
+  \class imageViewer
+
+  The image is stored in it's original format and size.
+  All interactions are done on the original image.
+  A scaled version is used to display the contents.
+*/	
+
+
+imageViewer::imageViewer(QWidget *parent): QLabel(parent)
+{
+  addToLog("image creation",LOGIMAG);
+  validImage=false;
+  setFrameStyle(QFrame::Sunken | QFrame::Panel);
+  QBrush b;
+  QPalette palette;
+  b.setTexture(QPixmap::fromImage(QImage(":/icons/transparency.png")));
+  palette.setBrush(QPalette::Active,QPalette::Base,b);
+  palette.setBrush(QPalette::Inactive,QPalette::Base,b);
+  palette.setBrush(QPalette::Disabled,QPalette::Base,b);
+  setPalette(palette);
+  setBackgroundRole(QPalette::Base);
+  setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+  setScaledContents(true);
+  setBackgroundRole(QPalette::Dark);
+
+  popup=new QMenu (this);
+  newAct = new QAction(tr("&New"),this);
+  connect(newAct, SIGNAL(triggered()), this, SLOT(slotNew()));
+  loadAct = new QAction(tr("&Load"), this);
+  connect(loadAct, SIGNAL(triggered()), this, SLOT(slotLoad()));
+  toTXAct = new QAction(tr("&To TX"), this);
+  connect(toTXAct, SIGNAL(triggered()), this, SLOT(slotToTX()));
+  editAct = new QAction(tr("&Edit"), this);
+  connect(editAct, SIGNAL(triggered()), this, SLOT(slotEdit()));
+  printAct = new QAction(tr("&Print"), this);
+  connect(printAct, SIGNAL(triggered()), this, SLOT(slotPrint()));
+  deleteAct = new QAction(tr("&Delete"), this);
+  connect(deleteAct, SIGNAL(triggered()), this, SLOT(slotDelete()));
+  propertiesAct = new QAction(tr("Propert&ies"), this);
+  connect(propertiesAct, SIGNAL(triggered()), this, SLOT(slotProperties()));
+  popup->addAction(newAct);
+  popup->addAction(loadAct);
+  popup->addAction(toTXAct);
+  popup->addAction(editAct);
+  popup->addAction(printAct);
+  popup->addAction(deleteAct);
+  popup->addAction(propertiesAct);
+  setScaledContents(false);
+  setAlignment(Qt::AlignCenter);
+  setAutoFillBackground(true);
+  QPalette mpalette;
+  mpalette.setColor(QPalette::Window,QColor(0,0,128));
+  setBackgroundRole(QPalette::Window);
+  mpalette.setColor(QPalette::WindowText, Qt::yellow);
+  setPalette(mpalette);
+  init(RXIMG);
+  activeMovie=false;
+  //
+}
+
+imageViewer::~imageViewer()
+{
+}
+
+void imageViewer::init(thumbType tp)
+{
+  setScaledContents(false);
+  setAlignment(Qt::AlignCenter);
+  setAutoFillBackground(true);
+  QPalette mpalette;
+  mpalette.setColor(QPalette::Window,QColor(0,0,128));
+  setBackgroundRole(QPalette::Window);
+  mpalette.setColor(QPalette::WindowText, Qt::yellow);
+  setPalette(mpalette);
+  addToLog("image creation",LOGIMAG);
+  imageFileName="";
+  image=QImage();
+  setType(tp);
+  popupEnabled=true;
+  setPixmap(QPixmap());
+  clear();
+}
+
+bool imageViewer::openImage(QString &filename,QString start,bool ask,bool showMessage,bool emitSignal)
+{
+  QImage tempImage;
+  displayMBoxEvent *stmb=0;
+  editorScene ed;
+  if(activeMovie)
+    {
+      activeMovie=false;
+      qm.stop();
+    }
+  if (filename.isEmpty()&&!ask) return false;
+  if(ask)
+    {
+      dirDialog dd((QWidget *)this,"Browse");
+      filename=dd.openFileName(start,"*");
+    }
+  if(filename.isEmpty())
+    {
+      imageFileName="";
+      return false;
+    }
+  if(!tempImage.load(filename))
+    {
+      QFile fi(filename);
+      // try to load it with the editor, it coud be a template file
+      if(!ed.load(fi))
+        {
+          //try loading jpg2
+          tempImage=readJP2Image(filename);
+          if(tempImage.isNull())
+            {
+              if(showMessage)
+                {
+                  stmb= new displayMBoxEvent("Image Loader",QString("Unable to load image:\n%1").arg(filename));
+                  QApplication::postEvent( dispatcherPtr, stmb );  // Qt will delete it when done
+                }
+              validImage=false;
+              imageFileName="";
+              return false;
+            }
+        }
+      else
+        {
+          tempImage=QImage(ed.renderImage(0,0)->copy());
+        }
+    }
+  image=tempImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+  imageFileName=filename;
+  QFileInfo fi(filename);
+  if (fi.suffix().toLower()=="gif")
+    {
+      //we will try a animated gif
+      qm.setFileName(filename);
+      if(qm.isValid())
+        {
+          if(qm.frameCount()>1)
+            {
+              activeMovie=true;
+              setMovie(&qm);
+              qm.start();
+            }
+        }
+    }
+  else
+    {
+      displayImage(false);
+    }
+  validImage=true;
+  if (emitSignal) emit imageChanged();
+  return true;
+}
+
+bool imageViewer::openImage(QString &filename,bool showMessage,bool emitSignal)
+{
+  return openImage(filename,"",false,showMessage,emitSignal);
+}
+
+bool imageViewer::openImage(QImage im)
+{
+  image=im;
+  if(!image.isNull())
+    {
+      displayImage(false);
+      validImage=true;
+      return true;
+    }
+  validImage=false;
+
+  return false;
+}
+
+bool imageViewer::openImage(QByteArray *ba)
+{
+  QImage tempImage;
+  QBuffer buffer(ba);
+  buffer.open(QIODevice::ReadOnly);
+  if(tempImage.load(&buffer,NULL))
+    {
+      imageFileName="";
+      validImage=true;
+      image=tempImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+      displayImage(false);
+      return true;
+    }
+  validImage=false;
+  return false;
+}
+
+
+
+bool imageViewer::hasValidImage()
+{
+  return validImage;
+}
+
+QImage *imageViewer::createImage(QSize sz,QColor fill)
+{
+  image=QImage(sz,QImage::Format_ARGB32_Premultiplied);
+  image.fill(fill.rgb());
+  displayImage(false);
+  emit imageChanged();
+  return ℑ
+}
+
+void imageViewer::copy(imageViewer *src)
+{
+  imageFileName=src->imageFileName;
+  ttype=src->ttype;
+  openImage(imageFileName,false,false);
+}
+
+
+
+QRgb *imageViewer::getScanLineAddress(int line)
+{
+  return (QRgb *)image.scanLine(line);
+}
+
+
+
+
+void imageViewer::displayImage(bool useCompression)
+{
+  QTemporaryFile itmp(QDir::tempPath()+"/qsstvXXXXXX.jp2");
+  if((image.width()==0) || (image.height()==0)) return;
+
+  //  qDebug()<< "displayImage" << image.width() << image.height() << width() << height();
+  if(ttype==TXIMG)
+    {
+      // we want to display the image with the defined quality
+      QBuffer buffer;
+      if(useCompression)
+        {
+          if(!itmp.open()) return;
+          writeJP2Image(image.convertToFormat(QImage::Format_RGB32), itmp.fileName(),psizeRatio);
+          compressedImage=readJP2Image(itmp.fileName());
+        }
+      else
+        {
+          compressedImage=image.copy();
+        }
+
+
+
+      if(compressedImage.isNull()||width()==0 || height()==0)
+        {
+
+          return;
+        }
+
+      if(hasScaledContents())
+        {
+          setPixmap(QPixmap::fromImage(compressedImage));
+        }
+      else
+        {
+//         qDebug() << "WxH" << width() << height() << compressedImage.width() << compressedImage.height();
+          setPixmap(QPixmap::fromImage(compressedImage.scaled(width(),height(),Qt::KeepAspectRatio,Qt::SmoothTransformation)));
+        }
+
+    }
+  else
+    {
+      if(hasScaledContents())
+        {
+          setPixmap(QPixmap::fromImage(image));
+        }
+      else
+        {
+//          qDebug() << "WxH" << width() << height() << image.width() << image.height();
+          setPixmap(QPixmap::fromImage(image.scaled(width()-2,height()-2,Qt::KeepAspectRatio,Qt::SmoothTransformation)));
+        }
+    }
+
+}
+
+
+bool imageViewer::readThumbSettings(QSettings *qs)
+{
+  qs->beginGroup("Thumbnails");
+  addToLog(QString("/THUMB/"+objectName()+"/"),LOGIMAG);
+  imageFileName =qs->value(objectName()+"/filename","").toString();
+  qs->endGroup();
+  return openImage(imageFileName,false,false);
+}
+
+void imageViewer::writeThumbSettings(QSettings *qs)
+{
+  qs->beginGroup("Thumbnails");
+  qs->setValue(objectName()+"/filename",imageFileName);
+  qs->endGroup();
+}
+
+void imageViewer::setType(thumbType tp)
+{
+  ttype=tp;
+  switch(tp)
+    {
+    case RXIMG:
+    case RXTHUMB:
+      imageFilePath=rxImagesPath;
+    break;
+    case TXIMG:
+    case TXTHUMB:
+      imageFilePath=txImagesPath;
+    break;
+    case TEMPLATETHUMB:
+      imageFilePath=templatesPath;
+    break;
+    }
+  if((tp==RXTHUMB) || (tp==TXTHUMB) || (tp==TEMPLATETHUMB))
+    {
+      setScaledContents(true);
+      setAlignment(Qt::AlignCenter);
+    }
+}
+
+void imageViewer::mousePressEvent( QMouseEvent *e )
+{
+  QString temp;
+  QString fn;
+  if (e->button() == Qt::RightButton)
+    {
+      if(popupEnabled)
+        {
+          popup->popup(QCursor::pos());
+        }
+    }
+}
+
+void imageViewer::slotDelete()
+{
+  if(imageFileName.isEmpty()) return;
+  if(QMessageBox::question(this,"Delete file","Do you want to delete the file and\n move it to the trash folder?",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes)
+    {
+      trash(imageFileName,true);
+    }
+  imageFileName="";
+  emit layoutChanged();
+}
+
+void imageViewer::slotEdit()
+{
+  if(imageFileName.isEmpty())
+    {
+      slotLoad();
+      if (imageFileName.isEmpty()) return;
+    }
+  callEditorEvent *ce = new callEditorEvent( this,imageFileName );
+  QApplication::postEvent(dispatcherPtr, ce );  // Qt will delete it when done
+}
+
+
+void imageViewer::slotLoad()
+{
+  QString fileNameTmp;
+  dirDialog dd((QWidget *)this,"Browse");
+  fileNameTmp=dd.openFileName(imageFilePath);
+  if(openImage(fileNameTmp,true,true))
+    {
+      imageFileName=fileNameTmp;
+      if(ttype==TEMPLATETHUMB)
+        {
+          templatesChangedEvent *ce = new templatesChangedEvent( );
+          QApplication::postEvent(dispatcherPtr, ce );  // Qt will delete it when done
+        }
+    }
+}
+
+
+
+void imageViewer::slotNew()
+{
+  callEditorEvent *ce = new callEditorEvent( this,NULL);
+  QApplication::postEvent(dispatcherPtr, ce );  // Qt will delete it when done
+}
+
+
+void imageViewer::slotPrint()
+{
+}
+
+void imageViewer::slotProperties()
+{
+  QFileInfo fi(imageFileName);
+  if(fi.exists())
+    {
+      QMessageBox::information(this,"Image Properties",
+                               "File: " + imageFileName
+                               + "\n File size:     " + QString::number(fi.size())
+                               + "\n Image width:   " + QString::number(image.width())
+                               + "\n Image heigth:  " + QString::number(image.height())
+                               + "\n Last Modified: " + fi.lastModified().toString()
+                               ,QMessageBox::Ok);
+    }
+  else
+    {
+      QMessageBox::information(this,"Image Properties",
+                               "No image loaded",QMessageBox::Ok);
+    }
+
+}
+
+void imageViewer::slotToTX()
+{
+  txWidgetPtr->setImage(imageFileName);
+}
+
+
+void imageViewer::save(QString fileName,QString fmt,bool convertRGB)
+{
+  QImage im;
+  if(image.isNull()) return;
+  if(!convertRGB)
+    {
+      image.save(fileName,fmt.toUpper().toLatin1().data());
+    }
+  else
+    {
+      im=image.convertToFormat(QImage::Format_RGB32);
+      im.save(fileName,fmt.toUpper().toLatin1().data());
+    }
+}
+
+void imageViewer::copyToBuffer(QByteArray *ba)
+{
+  QTemporaryFile itmp(QDir::tempPath()+"/qsstvXXXXXX.jp2");
+  if(image.isNull()) return;
+  if(!itmp.open()) return;
+  writeJP2Image(image.convertToFormat(QImage::Format_RGB32), itmp.fileName(),psizeRatio);
+  *ba=itmp.readAll();
+}
+
+int imageViewer::calcSize(int &sizeRatio)
+{
+  if(sizeRatio>100) sizeRatio=100;
+  if(sizeRatio<1) sizeRatio=1;
+  return (int)(rint(((double)image.byteCount()*sizeRatio))/(RATIOSCALE*1000.));
+}
+
+
+void imageViewer:: setSizeRatio(int sizeRatio)
+{
+  format="jp2";
+  if(sizeRatio>100) sizeRatio=100;
+  if(sizeRatio<1) sizeRatio=1;
+  psizeRatio=sizeRatio/RATIOSCALE;
+}
+
+bool imageViewer::reload()
+{
+  return openImage(imageFileName ,true,false);
+}
+
+
+void imageViewer::scale( int w, int h)
+{
+  image=image.scaled(QSize(w,h),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
+}
+
+void imageViewer::applyTemplate(QString templateName, bool useTemplate,int w,int h)
+{
+  if(!reload()) return ;
+  addToLog("apply temlate",LOGIMAG);
+  //  sconvert cnv;
+  editorScene tscene(0);
+  QFile fi(templateName);
+  if((!fi.fileName().isEmpty())  && (useTemplate))
+    {
+      tscene.load(fi);
+      tscene.addConversion('c',toCall,true);
+      tscene.addConversion('r',rsv);
+      tscene.addConversion('o',toOperator);
+      tscene.addConversion('t',QDateTime::currentDateTime().toUTC().toString("hh:mm"));
+      tscene.addConversion('d',QDateTime::currentDateTime().toUTC().toString("yyyy/MM/dd"));
+      tscene.addConversion('m',myCallsign);
+      tscene.addConversion('q',myQth);
+      tscene.addConversion('l',myLocator);
+      tscene.addConversion('n',myLastname);
+      tscene.addConversion('f',myFirstname);
+      tscene.addConversion('v',qsstvVersion);
+      tscene.addConversion('x',comment1);
+      tscene.addConversion('y',comment2);
+      tscene.addConversion('z',comment3);
+      addToLog(QString("applyTemplate size=%1,%2").arg(tscene.width()).arg(tscene.height()),LOGIMAG);
+      if(w!=0 && h!=0)
+        {
+          overlayedImage= QImage(image.scaled(w,h));
+          tscene.overlay(&overlayedImage);
+        }
+      else
+        {
+          tscene.overlay(&image);
+        }
+      openImage(*tscene.getImagePtr());
+    }
+  else
+    {
+      if(w!=0 && h!=0)
+        {
+          overlayedImage= QImage(image.scaled(w,h,Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); // same resolution as sstv mode
+          openImage(overlayedImage);
+        }
+      else openImage(image);
+    }
+  overlayedImage=QImage();
+}
+
+
+
+
+
diff --git a/qsstv/widgets/imageviewer.h b/qsstv/widgets/imageviewer.h
new file mode 100644
index 0000000..761b0f0
--- /dev/null
+++ b/qsstv/widgets/imageviewer.h
@@ -0,0 +1,133 @@
+/***************************************************************************
+ *   Copyright (C) 2000-2008 by Johan Maes                                 *
+ *   on4qz at telenet.be                                                      *
+ *   http://users.telenet.be/on4qz                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef IMAGEVIEWER_H
+#define IMAGEVIEWER_H
+#include <QLabel>
+#include <QSettings>
+#include <QEvent>
+#include <QMovie>
+#include "editor/editor.h"
+#include "editor/editorscene.h"
+
+
+class QMenu;
+class QAction;
+class editor;
+
+class imageViewer : public QLabel
+{
+  Q_OBJECT
+  /*! thumbnail type */
+public:
+  enum thumbType
+    {
+    RXIMG, /*!< just for receiver */
+    TXIMG, /*!< just for transmitter */
+    RXTHUMB, /*!< thumbnail for receiver. */
+    TXTHUMB,/*!< thumbnail for transmitter. */
+    TEMPLATETHUMB /*!< thumbnail for template. */
+    };
+  imageViewer(QWidget *parent=0);
+  ~imageViewer();
+  void init(thumbType tp);
+  bool openImage(QString &filename, QString start, bool ask, bool showMessage, bool emitSignal);
+  bool openImage(QString &filename, bool showMessage, bool emitSignal);
+  bool openImage(QImage im);
+  bool openImage(QByteArray *ba);
+  bool reload();
+  void scale( int w, int h);
+  QImage * getImagePtr() {return ℑ}
+  bool hasValidImage();
+  QImage *createImage(QSize sz,QColor fill);
+  QRgb *getScanLineAddress(int line);
+  void copy(imageViewer *src);
+
+  bool readThumbSettings(QSettings *qs);
+  void writeThumbSettings(QSettings *qs);
+  void setType(thumbType t);
+  QString getFilename() {return imageFileName;}
+  void enablePopup(bool en) {popupEnabled=en;}
+  void displayImage(bool useCompression);
+  void save(QString fileName, QString fmt, bool convertRGB);
+  void copyToBuffer(QByteArray *ba);
+  int calcSize(int &sizeRatio);
+  void setSizeRatio(int sizeRatio);
+  int getFileSize(){return fileSize;}
+  QString toCall;
+  QString toOperator;
+  QString rsv;
+  QString comment1;
+  QString comment2;
+  QString comment3;
+
+
+
+  void applyTemplate(QString templateName, bool useTemplate, int w=0, int h=0);
+
+  //  QSize sizeHint () {return QSize(32,26);}
+
+signals:
+  void layoutChanged();
+  void imageChanged();
+
+private:
+  QImage image;
+  QImage overlayedImage;
+  QImage compressedImage;
+
+  void mousePressEvent( QMouseEvent *e );
+  bool validImage;
+  QString imageFileName;
+  QString imageFilePath;
+  thumbType ttype;
+  bool popupEnabled;
+
+  QMenu *popup;
+  QAction *newAct;
+  QAction *loadAct;
+  QAction *toTXAct;
+  QAction *editAct;
+  QAction *printAct;
+  QAction *deleteAct;
+  QAction *propertiesAct;
+
+  double psizeRatio;
+  int fileSize;
+  QString format;
+  QMovie qm;
+  bool activeMovie;
+
+
+  // for overlays
+
+
+
+private slots:
+  void slotDelete();
+  void slotEdit();
+  void slotLoad();
+  void slotNew();
+  void slotPrint();
+  void slotProperties();
+  void slotToTX();
+};
+
+#endif
diff --git a/qsstv/widgets/markerwidget.cpp b/qsstv/widgets/markerwidget.cpp
new file mode 100644
index 0000000..0d7de2d
--- /dev/null
+++ b/qsstv/widgets/markerwidget.cpp
@@ -0,0 +1,25 @@
+#include "markerwidget.h"
+#include "fftdisplay.h"
+#include "QDebug"
+
+markerWidget::markerWidget(QWidget *parent) :  QLabel(parent)
+{
+}
+
+
+void markerWidget::paintEvent(QPaintEvent *p)
+{
+  QPen pn;
+  QPainter painter(this);
+  pn.setColor(Qt::red);
+  pn.setWidth(2);
+  painter.setPen(pn);
+
+  if ((marker1>FFTLOW)&&(marker1<(FFTLOW+FFTSPAN)))
+    {
+      painter.drawLine((((marker1-FFTLOW)*width())/FFTSPAN),0,(((marker1-FFTLOW)*width())/FFTSPAN),height());
+    }
+  if ((marker2>FFTLOW)&&(marker2<(FFTLOW+FFTSPAN))) painter.drawLine((((marker2-FFTLOW)*width())/FFTSPAN),0,(((marker2-FFTLOW)*width())/FFTSPAN),height());
+  if ((marker3>FFTLOW)&&(marker3<(FFTLOW+FFTSPAN))) painter.drawLine((((marker3-FFTLOW)*width())/FFTSPAN),0,(((marker3-FFTLOW)*width())/FFTSPAN),height());
+  QLabel::paintEvent(p);
+}
diff --git a/qsstv/widgets/markerwidget.h b/qsstv/widgets/markerwidget.h
new file mode 100644
index 0000000..a065772
--- /dev/null
+++ b/qsstv/widgets/markerwidget.h
@@ -0,0 +1,25 @@
+#ifndef MARKERWIDGET_H
+#define MARKERWIDGET_H
+
+#include <QLabel>
+
+class markerWidget : public QLabel
+{
+  Q_OBJECT
+public:
+  explicit markerWidget(QWidget *parent = 0);
+  void setMarkerLabel(QLabel *markerLabel);
+  void setMarkers(int mrk1, int mrk2=0, int mrk3=0){ marker1=mrk1;marker2=mrk2;marker3=mrk3;update();}
+  
+signals:
+  
+public slots:
+private:
+  void paintEvent(QPaintEvent *p);
+  int marker1;
+  int marker2;
+  int marker3;
+  
+};
+
+#endif // MARKERWIDGET_H
diff --git a/qsstv/widgets/qscale.cpp b/qsstv/widgets/qscale.cpp
new file mode 100644
index 0000000..7897e3b
--- /dev/null
+++ b/qsstv/widgets/qscale.cpp
@@ -0,0 +1,371 @@
+/*
+ *   Copyright 2008-2012 Meinert Jordan <meinert at gmx.at>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "qscale.h"
+#include <QtGui/QPainter>
+#include <cmath>
+
+QScale::QScale(QWidget *parent)
+    : QWidget(parent)
+{
+    m_minimum = 0;
+    m_maximum = 100;
+
+    m_value = 0;
+
+    m_labelsVisible = true;
+    m_scaleVisible = true;
+
+    m_borderWidth = 6;
+
+    m_labelsFormat = 'g';
+    m_labelsPrecision = -1;
+
+    m_majorStepSize = 0;
+    m_minorStepCount = -1;
+
+    m_invertedAppearance = false;
+
+    m_orientations = (Qt::Horizontal | Qt::Vertical);
+
+    pi = acos(-1);
+
+    setBackgroundRole(QPalette::Base);
+
+    updateLabelSample();
+    setMinimumSize(80,60);
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+QScale::~QScale()
+{
+
+}
+
+void QScale::setMinimum(double min)
+{
+    if(min < INFINITY && min > -INFINITY)
+        m_minimum = min;
+    updateLabelSample();
+    update();
+}
+
+double QScale::minimum() const
+{
+    return m_minimum;
+}
+
+void QScale::setMaximum(double max)
+{
+    if(max < INFINITY && max > -INFINITY)
+        m_maximum = max;
+    updateLabelSample();
+    update();
+}
+
+double QScale::maximum() const
+{
+   return m_maximum;
+}
+
+void QScale::setRange(double min, double max)
+{
+    if(max < INFINITY && max > -INFINITY)
+        m_maximum = max;
+    if(min < INFINITY && min > -INFINITY)
+        m_minimum = min;
+    updateLabelSample();
+    update();
+}
+
+void QScale::setValue(double value)
+{
+   m_value = value;
+   update();
+}
+
+double QScale::value() const
+{
+   return m_value;
+}
+
+void QScale::setLabelsVisible(bool visible)
+{
+    m_labelsVisible = visible;
+    update();
+}
+
+bool QScale::isLabelsVisible() const
+{
+    return m_labelsVisible;
+}
+
+void QScale::setScaleVisible(bool visible)
+{
+    m_scaleVisible = visible;
+    update();
+}
+
+bool QScale::isScaleVisible() const
+{
+    return m_scaleVisible;
+}
+
+void QScale::setBorderWidth(int width)
+{
+    m_borderWidth = qMin(width, 0);
+    update();
+}
+
+int QScale::borderWidth() const
+{
+    return m_borderWidth;
+}
+
+void QScale::setLabelsFormat(char format, int precision)
+{
+    m_labelsFormat = format;
+    m_labelsPrecision = precision;
+    updateLabelSample();
+    update();
+}
+
+void QScale::setMajorStepSize(double stepSize)
+{
+    m_majorStepSize = stepSize;
+    update();
+}
+
+double QScale::majorStepSize() const
+{
+    return m_majorStepSize;
+}
+
+void QScale::setMinorStepCount(int stepCount)
+{
+    m_minorStepCount = stepCount;
+    update();
+}
+
+int QScale::minorStepCount() const
+{
+    return m_minorStepCount;
+}
+
+void QScale::setInvertedAppearance(bool invert)
+{
+    m_invertedAppearance = invert;
+    update();
+}
+
+bool QScale::invertedAppearance() const
+{
+    return m_invertedAppearance;
+}
+
+void QScale::setOrientations(Qt::Orientations orientations)
+{
+    m_orientations = orientations;
+    update();
+}
+
+Qt::Orientations QScale::orientations() const
+{
+    return m_orientations;
+}
+
+void QScale::resizeEvent(QResizeEvent *re)
+{
+    QWidget::resizeEvent(re);
+}
+
+void QScale::paintEvent(QPaintEvent*)
+{
+    painter = new QPainter(this);
+
+    bool vertical;
+    int wWidget, hWidget;
+    double wLabel, hLabel;
+    double wScale, hScale;
+    double radius;
+    double angleSpan;
+    double angleStart;
+    double valueSpan;
+    double majorStep;
+    int minorSteps;
+    int order;
+    QRectF boundingRect;
+    QPointF center;
+    QPolygon polygon;
+
+    double u;
+
+    if(!(m_orientations & Qt::Vertical) ^ !(m_orientations & Qt::Horizontal))
+        vertical = (bool)(m_orientations & Qt::Vertical);
+    else
+        vertical = (height() > width());
+
+    wWidget = width();
+    hWidget = height();
+
+    painter->setRenderHint(QPainter::Antialiasing, true);
+
+    boundingRect = painter->boundingRect(QRectF(0, 0, width(), height()),
+                               Qt::AlignBottom | Qt::AlignHCenter, labelSample);
+    wLabel = (m_labelsVisible)? boundingRect.width() : 0;
+    hLabel = (m_labelsVisible)? boundingRect.height() : 0;
+
+    if(vertical){
+        qSwap(wWidget, hWidget);
+        qSwap(wLabel, hLabel);
+    }
+
+    wScale = wWidget - wLabel - 2. * m_borderWidth;
+    hScale = .5 * hWidget - hLabel - m_borderWidth;
+
+    radius = .125 * pow( wScale, 2) / hScale + .5 * hScale;
+    if(radius < hScale + .5* hWidget - m_borderWidth){
+        radius = (4. * (hLabel + m_borderWidth) +
+                  sqrt(4. * pow(hLabel + m_borderWidth, 2) +
+                       3. * pow(wScale, 2))
+                  ) / 3. - hLabel - 2. * m_borderWidth;
+        center = QPointF(.5 * wWidget, hWidget - m_borderWidth);
+    }
+    else
+        center = QPointF(.5 * wWidget, radius + hLabel + m_borderWidth);
+
+    angleSpan = - 360 / pi * asin(wScale/(2. * radius));
+    angleStart = 90. - .5 * angleSpan;
+
+    valueSpan = m_maximum - m_minimum;
+    majorStep = fabs(valueSpan) * qMax(wLabel, 1.5 * boundingRect.height()) /
+                                                                         wScale;
+    order = 0;
+    while(majorStep < 1)
+        majorStep *= 10, order--;
+    while(majorStep >= 10)
+        majorStep /= 10, order++;
+    if(majorStep > 5)
+        majorStep = 10 * pow(10, order), minorSteps = 5;
+    else if(majorStep > 2)
+        majorStep = 5 * pow(10, order), minorSteps = 5;
+    else
+        majorStep = 2 * pow(10, order), minorSteps = 4;
+    if(m_majorStepSize > 0)
+        majorStep = m_majorStepSize;
+    if(m_minorStepCount > 0)
+        minorSteps = m_minorStepCount;
+
+    // draw scale
+    painter->setPen(QPen(palette().color(QPalette::Text), 1));
+    if(m_scaleVisible && majorStep != 0){
+        if(vertical){
+            painter->rotate(90);
+            painter->translate(0, -hWidget + wLabel / 4);
+        }
+        painter->translate(center);
+        painter->rotate(fmod(m_minimum, majorStep / minorSteps) /
+                        valueSpan * angleSpan - angleStart);
+        int offsetCount = (minorSteps - (int)ceil(fmod(m_minimum, majorStep) /
+                                          majorStep * minorSteps)) % minorSteps;
+        double scaleWidth = qMin(qMin(.25 * (hWidget - m_borderWidth),
+                                    .25 * radius), 2.5 * boundingRect.height());
+        for(int i = 0; i <= (int)(minorSteps * valueSpan / majorStep); i++){
+            if(i % minorSteps == offsetCount)
+                painter->drawLine(QLineF(radius - scaleWidth, 0, radius, 0));
+            else
+                painter->drawLine(QLineF(radius - scaleWidth, 0,
+                                         radius - scaleWidth * 0.4, 0));
+            painter->rotate(majorStep * angleSpan / (-valueSpan * minorSteps));
+        }
+        painter->resetMatrix();
+    }
+
+    // draw labels
+    if(m_labelsVisible && majorStep != 0)
+        for(int i = (int)ceil(m_minimum / majorStep);
+            i <= (int)(m_maximum / majorStep); i++){
+            u = pi/180 * ((majorStep * i - m_minimum) / valueSpan *
+                                     angleSpan + angleStart);
+            QRect position;
+            Qt::Alignment align;
+            if(vertical){
+                align = Qt::AlignLeft | Qt::AlignVCenter;
+                position = QRect(width() - center.y() + radius * sin(u), 0,
+                                   width(), height() + 2 * radius * cos(u));
+            }
+            else{
+                align = Qt::AlignHCenter | Qt::AlignBottom;
+                position = QRect(0, 0, 2. * (center.x() + radius * cos(u)),
+                                              center.y() - radius * sin(u));
+            }
+            painter->resetMatrix();
+            painter->drawText(position, align, QString::number(i * majorStep,
+                                            m_labelsFormat, m_labelsPrecision));
+       }
+
+    // draw needle
+    if(vertical){
+        painter->rotate(90);
+        painter->translate(0, -hWidget + wLabel / 4);
+    }
+    painter->translate(center);
+    painter->rotate((m_minimum - m_value) / valueSpan * angleSpan - angleStart);
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(palette().color(QPalette::Text));
+    polygon.setPoints(5, 0, -2, (int)radius - 10, -2, (int)radius, 0,
+                      (int)radius - 10, 2, 0, 2);
+    painter->drawConvexPolygon(polygon);
+    painter->setPen(QPen(palette().color(QPalette::Base), 2));
+    painter->drawLine(0, 0, radius - 15, 0);
+    painter->resetMatrix();
+
+    // draw cover
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(palette().color(QPalette::Mid));
+
+    if(vertical){
+        painter->drawRect(QRect(0, 0, m_borderWidth, height()));
+        center = QPointF(width() - center.y() - wLabel / 4, .5 * height());
+        u = .25 * (hWidget - wLabel) - center.x() - m_borderWidth;
+    }
+    else{
+        painter->drawRect(QRect(0, hWidget, wWidget, -m_borderWidth));
+        u = center.y() - m_borderWidth - .75 * hWidget;
+    }
+    u = qMax(u, .25 * radius);
+    painter->drawEllipse(center, u, u);
+
+    delete painter;
+}
+
+void QScale::updateLabelSample()
+{
+    double margin = qMax(fabs(m_minimum), fabs(m_maximum));
+    double wildcard = (qMin(m_minimum, m_maximum) < 0)? -8 : 8;
+    while(margin < 1){
+        margin *= 10;
+        wildcard /= 10;
+    }
+    while(margin >= 10){
+        margin /= 10;
+        wildcard *= 10;
+    }
+    labelSample = QString::number(wildcard, m_labelsFormat, m_labelsPrecision);
+}
diff --git a/qsstv/widgets/qscale.h b/qsstv/widgets/qscale.h
new file mode 100644
index 0000000..330dd94
--- /dev/null
+++ b/qsstv/widgets/qscale.h
@@ -0,0 +1,111 @@
+/*
+ *   Copyright 2008-2012 Meinert Jordan <meinert at gmx.at>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef QSCALE_H
+#define QSCALE_H
+#include "qglobal.h"
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+#include <QtWidgets>
+#else
+#include <QWidget>
+#endif
+
+class QScale : public QWidget
+{
+    Q_OBJECT
+
+public:
+    QScale(QWidget *parent = 0);
+    ~QScale();
+
+    double minimum() const;
+    double maximum() const;
+
+    double value() const;
+
+    void setLabelsVisible(bool);
+    bool isLabelsVisible() const;
+
+    void setScaleVisible(bool);
+    bool isScaleVisible() const;
+
+    void setBorderWidth(int);
+    int borderWidth() const;
+
+    void setLabelsFormat(char format, int precision = -1);
+
+    double majorStepSize() const;
+    int minorStepCount() const;
+
+    void setInvertedAppearance(bool invert);
+    bool invertedAppearance() const;
+
+    Qt::Orientations orientations() const;
+
+//    QSize sizeHint() const;
+//    QSize minimumSizeHint() const;
+
+public Q_SLOTS:
+    void setMinimum(double);
+    void setMaximum(double);
+    void setRange(double min, double max);
+
+    void setValue(double);
+
+    void setMajorStepSize(double);
+    void setMinorStepCount(int);
+
+    void setOrientations(Qt::Orientations);
+
+protected:
+//    bool event(QEvent *e);
+    void resizeEvent(QResizeEvent *re);
+    void paintEvent(QPaintEvent *pe);
+
+private:
+    double m_minimum;
+    double m_maximum;
+
+    double m_value;
+
+    bool m_labelsVisible;
+    bool m_scaleVisible;
+
+    int m_borderWidth;
+
+    char m_labelsFormat;
+    int m_labelsPrecision;
+
+    double m_majorStepSize;
+    int m_minorStepCount;
+
+    bool m_invertedAppearance;
+
+    Qt::Orientations m_orientations;
+
+    QPainter *painter;
+    QString labelSample;
+
+    double pi;
+
+    void updateLabelSample();
+};
+
+#endif // QSCALE_H
diff --git a/qsstv/widgets/spectrumwidget.cpp b/qsstv/widgets/spectrumwidget.cpp
new file mode 100644
index 0000000..064dae2
--- /dev/null
+++ b/qsstv/widgets/spectrumwidget.cpp
@@ -0,0 +1,111 @@
+#include "spectrumwidget.h"
+#include "qsstvglobal.h"
+#include "ui_spectrumwidget.h"
+#include "utils/supportfunctions.h"
+#include "utils/logging.h"
+#include "markerwidget.h"
+
+spectrumWidget::spectrumWidget(QWidget *parent) :
+  QFrame(parent),
+  ui(new Ui::spectrumWidget)
+{
+  ui->setupUi(this);
+  readSettings();
+  connect(ui->maxDbSpinbox,SIGNAL(valueChanged(int)),SLOT(slotMaxDbChanged(int)));
+  connect(ui->rangeSpinBox,SIGNAL(valueChanged(int)),SLOT(slotRangeChanged(int)));
+}
+
+spectrumWidget::~spectrumWidget()
+{
+  writeSettings();
+  delete ui;
+}
+
+void spectrumWidget::init(int size,int slices,int isamplingrate)
+{
+  addToLog(QString("Size: %1, Slices %2, Samplingrate %3").arg(size).arg(slices).arg(isamplingrate),LOGFFT);
+  ui->fftFrame->init(size,slices,isamplingrate);
+//  ui->fftFrame->setMarkerLabel(ui->markerLabel);
+//  ui->fftFrame->setSize(ui->fftFrame->width(),ui->fftFrame->height());
+}
+void spectrumWidget::realFFT(short int *iBuffer)
+{
+  ui->fftFrame->realFFT(iBuffer);
+}
+void spectrumWidget::realFFT(float *iBuffer)
+{
+  ui->fftFrame->realFFT(iBuffer);
+}
+
+void spectrumWidget::realFFT(double *iBuffer)
+{
+  ui->fftFrame->realFFT(iBuffer);
+}
+
+
+
+
+void spectrumWidget::slotMaxDbChanged(int mb)
+{
+  ui->fftFrame->setMaxDb(mb);
+  maxdb=mb;
+}
+void spectrumWidget::slotRangeChanged(int rg)
+{
+  ui->fftFrame->setRange(rg);
+  range=rg;
+}
+
+void spectrumWidget::readSettings()
+{
+  QSettings qSettings;
+  qSettings.beginGroup("SPECTRUM");
+  maxdb=qSettings.value("maxdb",0).toInt();
+  slotMaxDbChanged(maxdb);
+  range=qSettings.value("range",35).toInt();
+  slotRangeChanged(range);
+  qSettings.endGroup();
+  setParams();
+}
+
+
+void spectrumWidget::writeSettings()
+{
+  QSettings qSettings;
+  getParams();
+  qSettings.beginGroup("SPECTRUM");
+  qSettings.setValue( "maxdb",maxdb);
+  qSettings.setValue( "range",range);
+  qSettings.endGroup();
+}
+
+void spectrumWidget::getParams()
+{
+  getValue(maxdb,ui->maxDbSpinbox);
+  getValue(range,ui->rangeSpinBox);
+}
+
+void spectrumWidget::setParams()
+{
+  setValue(maxdb,ui->maxDbSpinbox);
+  setValue(range,ui->rangeSpinBox);
+  slotMaxDbChanged(maxdb);
+  slotRangeChanged(range);
+}
+
+void spectrumWidget::displaySettings(bool wf, bool drm)
+{
+  ui->fftFrame->displayWaterfall(wf);
+  if(drm)
+  {
+      ui->fftFrame->setMarkers(725,1475,1850);
+      ui->markerLabel->setMarkers(725,1475,1850);
+    }
+  else
+    {
+      ui->fftFrame->setMarkers(1200,1500,2300);
+      ui->markerLabel->setMarkers(1200,1500,2300);
+    }
+}
+
+
diff --git a/qsstv/widgets/spectrumwidget.h b/qsstv/widgets/spectrumwidget.h
new file mode 100644
index 0000000..ea18822
--- /dev/null
+++ b/qsstv/widgets/spectrumwidget.h
@@ -0,0 +1,36 @@
+#ifndef SPECTRUMWIDGET_H
+#define SPECTRUMWIDGET_H
+
+#include <QFrame>
+
+namespace Ui {
+  class spectrumWidget;
+  }
+
+class spectrumWidget : public QFrame
+{
+  Q_OBJECT
+  
+public:
+  spectrumWidget(QWidget *parent = 0);
+  ~spectrumWidget();
+  void init(int size,int slices,int isamplingrate);
+  void realFFT(short int *iBuffer);
+  void realFFT(float *iBuffer);
+  void realFFT(double *iBuffer);
+  void readSettings();
+  void writeSettings();
+  void displaySettings(bool wf,bool drm);
+private slots:
+  void slotMaxDbChanged(int mb);
+  void slotRangeChanged(int rg);
+  void getParams();
+  void setParams();
+  
+private:
+  Ui::spectrumWidget *ui;
+  int maxdb;
+  int range;
+};
+
+#endif // SPECTRUMWIDGET_H
diff --git a/qsstv/widgets/spectrumwidget.ui b/qsstv/widgets/spectrumwidget.ui
new file mode 100644
index 0000000..1be7e66
--- /dev/null
+++ b/qsstv/widgets/spectrumwidget.ui
@@ -0,0 +1,629 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>spectrumWidget</class>
+ <widget class="QFrame" name="spectrumWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>334</width>
+    <height>219</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>334</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="font">
+   <font>
+    <family>Ubuntu Mono</family>
+    <pointsize>9</pointsize>
+    <weight>50</weight>
+    <bold>false</bold>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Frame</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::Panel</enum>
+  </property>
+  <property name="frameShadow">
+   <enum>QFrame::Sunken</enum>
+  </property>
+  <property name="lineWidth">
+   <number>2</number>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" stretch="0,10,0">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="markerWidget" name="markerLabel">
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>8</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="fftDisplay" name="fftFrame">
+     <property name="palette">
+      <palette>
+       <active>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>191</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>159</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>84</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </active>
+       <inactive>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>191</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>159</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>84</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </inactive>
+       <disabled>
+        <colorrole role="WindowText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Button">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Light">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>191</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Midlight">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>159</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Dark">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Mid">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>84</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Text">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="BrightText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>255</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ButtonText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>63</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Base">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Window">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="Shadow">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="AlternateBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>127</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipBase">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>255</red>
+           <green>255</green>
+           <blue>220</blue>
+          </color>
+         </brush>
+        </colorrole>
+        <colorrole role="ToolTipText">
+         <brush brushstyle="SolidPattern">
+          <color alpha="255">
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
+         </brush>
+        </colorrole>
+       </disabled>
+      </palette>
+     </property>
+     <property name="autoFillBackground">
+      <bool>true</bool>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Plain</enum>
+     </property>
+     <property name="lineWidth">
+      <number>1</number>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>4</number>
+     </property>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="font">
+        <font>
+         <pointsize>10</pointsize>
+         <weight>50</weight>
+         <italic>false</italic>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Max dB</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="maxDbSpinbox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>20</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>9</pointsize>
+        </font>
+       </property>
+       <property name="minimum">
+        <number>-30</number>
+       </property>
+       <property name="maximum">
+        <number>20</number>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="font">
+        <font>
+         <pointsize>10</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>Range</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="rangeSpinBox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>20</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>9</pointsize>
+        </font>
+       </property>
+       <property name="minimum">
+        <number>25</number>
+       </property>
+       <property name="maximum">
+        <number>40</number>
+       </property>
+       <property name="value">
+        <number>30</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>fftDisplay</class>
+   <extends>QLabel</extends>
+   <header>widgets/fftdisplay.h</header>
+  </customwidget>
+  <customwidget>
+   <class>markerWidget</class>
+   <extends>QLabel</extends>
+   <header>widgets/markerwidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qsstv/widgets/sweepform.ui b/qsstv/widgets/sweepform.ui
new file mode 100644
index 0000000..efa215d
--- /dev/null
+++ b/qsstv/widgets/sweepform.ui
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>sweepForm</class>
+ <widget class="QDialog" name="sweepForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>469</width>
+    <height>156</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Test Signal</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <layout class="QVBoxLayout">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="frequencyLabel">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="text">
+          <string>Lower Frequency</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSpinBox" name="lowerFrequencySpinBox">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="minimum">
+          <number>300</number>
+         </property>
+         <property name="maximum">
+          <number>2500</number>
+         </property>
+         <property name="value">
+          <number>1200</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="_2">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="frequencyLabel_2">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="text">
+          <string>Upper Frequency</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSpinBox" name="upperFrequencySpinBox">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="minimum">
+          <number>300</number>
+         </property>
+         <property name="maximum">
+          <number>2500</number>
+         </property>
+         <property name="value">
+          <number>2300</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QVBoxLayout">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="durationLabel">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="text">
+          <string>Duration (sec)</string>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QSpinBox" name="durationSpinBox">
+         <property name="font">
+          <font/>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="value">
+          <number>10</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="font">
+        <font/>
+       </property>
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Expanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>okButton</sender>
+   <signal>clicked()</signal>
+   <receiver>sweepForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>cancelButton</sender>
+   <signal>clicked()</signal>
+   <receiver>sweepForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/widgets/textdisplay.cpp b/qsstv/widgets/textdisplay.cpp
new file mode 100644
index 0000000..08e7d4e
--- /dev/null
+++ b/qsstv/widgets/textdisplay.cpp
@@ -0,0 +1,31 @@
+#include "textdisplay.h"
+#include "ui_textdisplay.h"
+
+textDisplay::textDisplay(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::textDisplay)
+{
+  ui->setupUi(this);
+}
+
+textDisplay::~textDisplay()
+{
+  delete ui;
+}
+
+
+void textDisplay::clear()
+{
+  ui->plainTextEdit->clear();
+}
+
+void textDisplay::append(QString t)
+{
+  int i;
+  QStringList sl;
+  sl=t.split("\r\n");
+  for(i=0;i<sl.count();i++)
+    {
+      ui->plainTextEdit->appendPlainText(sl.at(i));
+    }
+}
diff --git a/qsstv/widgets/textdisplay.h b/qsstv/widgets/textdisplay.h
new file mode 100644
index 0000000..41d651d
--- /dev/null
+++ b/qsstv/widgets/textdisplay.h
@@ -0,0 +1,24 @@
+#ifndef TEXTDISPLAY_H
+#define TEXTDISPLAY_H
+
+#include <QDialog>
+
+namespace Ui {
+class textDisplay;
+}
+
+class textDisplay : public QDialog
+{
+  Q_OBJECT
+  
+public:
+  explicit textDisplay(QWidget *parent = 0);
+  ~textDisplay();
+  void clear();
+  void append(QString t);
+  
+private:
+  Ui::textDisplay *ui;
+};
+
+#endif // TEXTDISPLAY_H
diff --git a/qsstv/widgets/textdisplay.ui b/qsstv/widgets/textdisplay.ui
new file mode 100644
index 0000000..a9b17e6
--- /dev/null
+++ b/qsstv/widgets/textdisplay.ui
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>textDisplay</class>
+ <widget class="QDialog" name="textDisplay">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Received Text</string>
+  </property>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>240</y>
+     <width>341</width>
+     <height>32</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Ok</set>
+   </property>
+  </widget>
+  <widget class="QPlainTextEdit" name="plainTextEdit">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>20</y>
+     <width>371</width>
+     <height>211</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>textDisplay</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>textDisplay</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/widgets/vumeter.cpp b/qsstv/widgets/vumeter.cpp
new file mode 100644
index 0000000..ed35e35
--- /dev/null
+++ b/qsstv/widgets/vumeter.cpp
@@ -0,0 +1,287 @@
+/***************************************************************************
+ *   Copyright (C) 2008 - Giuseppe Cigala                                  *
+ *   g_cigala at virgilio.it                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+
+#include "vumeter.h"
+#include <qsstvdefs.h>
+
+#define BW 5
+#define SG 100
+#define LG 500
+#define OFFSET 50
+
+
+
+vuMeter::vuMeter(QWidget *parent) : QWidget(parent)
+{
+  colBack = QColor(50, 50, 255);
+  colValue = Qt::white;
+  colHigh = Qt::red;
+  colMid =  Qt::green;
+  colLow =  Qt::blue;
+  min = 0;
+  max = 100;
+  val =9;
+  horizontal=false;
+  divisions=20;
+  labelText="V";
+}
+
+void vuMeter::setLabelText(QString t)
+{
+  labelText=t;
+}
+
+
+void vuMeter::paintEvent(QPaintEvent *)
+{
+  if(width()>height()) horizontal=true;
+  else horizontal=false;
+  if (horizontal)
+    {
+      w=LG;
+      h=SG;
+      rw=5;
+      rh=30; // rect rounding
+    }
+  else
+    {
+      w=SG;
+      h=LG;
+      rw=30;
+      rh=5; // rect rounding
+    }
+  paintBorder();
+  paintBar();
+  //paintValue();
+
+}
+
+void vuMeter::paintBorder()
+{
+
+
+  QLinearGradient linGrad;
+  QLinearGradient linGrad1;
+  QRectF border1;
+  QRectF rct;
+
+  QPainter painter(this);
+  QColor light = Qt::white;
+  QColor dark = colBack.darker(255);
+  painter.setPen(QPen(colBack, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+
+  painter.setRenderHint(QPainter::Antialiasing);
+
+  if (horizontal)
+    {
+      linGrad.setStart(250,BW); linGrad.setFinalStop(250, 2*BW);
+      linGrad1.setStart(1,SG-3*BW); linGrad1.setFinalStop(1, SG-BW);
+      border1=QRectF(5, 20, w-2*BW, h-5*BW);
+      rct=QRectF(2*BW,h/2-10, 20, 25);
+    }
+  else
+    {
+      linGrad.setStart(BW,1); linGrad.setFinalStop(2*BW, 1);
+      linGrad1.setStart(SG-3*BW,1); linGrad1.setFinalStop(SG-BW, 1);
+      border1=QRectF(20, 5, w-5*BW, h-2*BW);
+      rct=QRectF(w/2-10, h-4*BW-10, 20, 25);
+    }
+  painter.setWindow(0, 0, w, h);
+  linGrad.setColorAt(0, light);
+  linGrad.setColorAt(1, colBack);
+  linGrad.setSpread(QGradient::PadSpread);
+  painter.setBrush(linGrad);
+  QRectF border(5, 5, w-2*BW, h-2*BW);
+  painter.drawRoundRect(border, rw,rh);
+  linGrad1.setColorAt(0, colBack);
+  linGrad1.setColorAt(1, dark);
+  linGrad1.setSpread(QGradient::PadSpread);
+  painter.setBrush(linGrad1);
+  painter.drawRoundRect(border1, rw,rh);
+
+
+  //paint label
+
+  painter.setPen(QPen(colValue, 2));
+  QFont valFont("Arial", 24, QFont::Bold);
+  painter.setFont(valFont);
+  painter.drawText(rct, Qt::AlignCenter, labelText);
+}
+
+void vuMeter::paintBar()
+{
+  QLinearGradient linGrad;
+  int i;
+  double bar;
+  QRectF bar1;
+  QRectF bar2;
+  double length;
+  QPainter painter(this);
+  painter.setWindow(0, 0, w, h);
+  painter.setRenderHint(QPainter::Antialiasing);
+  if (horizontal)
+    {
+      linGrad.setStart(w,h); linGrad.setFinalStop(0,h);
+      bar2=QRectF(OFFSET,3*BW,w-OFFSET-3*BW,h-6*BW);
+      length = bar2.width();
+      bar = abs(length * (1-(val-min)/(max-min)));
+      bar1=QRectF(bar2.x()+bar2.width()-bar,bar2.y(),bar, bar2.height());
+    }
+  else
+    {
+      linGrad.setStart(w,0); linGrad.setFinalStop(w,h);
+      bar2=QRectF(3*BW,4*BW,w-6*BW,h-1*OFFSET);
+      length = bar2.height();
+      bar = abs(length * (val-min)/(max-min));
+      bar1=QRectF(bar2.x(),bar2.y(),bar2.width(), bar2.height()-bar);
+
+    }
+
+  linGrad.setColorAt(0, colHigh);
+  linGrad.setColorAt(0.5, colMid);
+  linGrad.setColorAt(1, colLow);
+  linGrad.setSpread(QGradient::PadSpread);
+  painter.setBrush(linGrad);
+  painter.drawRect(bar2);
+
+
+  // draw background bar
+  painter.setBrush(QColor(40, 40, 40));
+
+  painter.drawRect(bar1);
+  painter.setPen(QPen(Qt::black, 2));
+  for (i = 0; i <=divisions; i++)
+    {
+      if(horizontal)
+        {
+          painter.drawLine(bar2.left()+bar2.width()*i/divisions, bar2.top(), bar2.left()+bar2.width()*i/divisions, bar2.bottom());
+        }
+      else
+        {
+          painter.drawLine(bar2.left(), bar2.top()+bar2.height()*i/divisions, bar2.right(), bar2.top()+bar2.height()*i/divisions);
+        }
+    }
+
+
+}
+
+
+void vuMeter::setColorBg(QColor color)
+{
+  colBack = color;
+  update();
+}
+
+void vuMeter::setColorValue(QColor color)
+{
+  colValue = color;
+  update();
+}
+
+void vuMeter::setColorHigh(QColor color)
+{
+  colHigh = color;
+  update();
+}
+
+void vuMeter::setColorMid(QColor color)
+{
+  colMid = color;
+  update();
+}
+
+void vuMeter::setColors(QColor cL,QColor cM,QColor cH)
+{
+  colLow = cL;
+  colMid = cM;
+  colHigh = cH;
+  update();
+}
+
+void vuMeter::setColorLow(QColor color)
+{
+  colLow = color;
+  update();
+}
+
+
+
+void vuMeter::setValue(double value)
+{
+  if (value > max)
+    {
+      val = max;
+      update();
+    }
+  else if (value < min)
+    {
+      val = min;
+      update();
+    }
+  else
+    {
+      val = value;
+      update();
+    }
+}
+
+
+void vuMeter::setMinimum(double minValue)
+{
+  if (minValue > max)
+    {
+      min = max;
+      max = minValue;
+      update();
+    }
+  else
+    {
+      min = minValue;
+      update();
+    }
+}
+
+void vuMeter::setMaximum(double maxValue)
+{
+  if (maxValue < min)
+    {
+      max = min;
+      min = maxValue;
+      update();
+    }
+  else
+    {
+      max = maxValue;
+      update();
+    }
+}
+
+QSize vuMeter::minimumSizeHint() const
+{
+  return QSize(10, 54);
+}
+
+QSize vuMeter::sizeHint() const
+{
+  return QSize(100, 540);
+}
+
+
diff --git a/qsstv/widgets/vumeter.h b/qsstv/widgets/vumeter.h
new file mode 100644
index 0000000..2ba24a5
--- /dev/null
+++ b/qsstv/widgets/vumeter.h
@@ -0,0 +1,99 @@
+/***************************************************************************
+ *   Copyright (C) 2008 - Giuseppe Cigala                                  *
+ *   g_cigala at virgilio.it                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef VUMETER_H
+#define VUMETER_H
+
+#include <QtGui>
+#include <math.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+#include <QtWidgets>
+#endif
+
+
+class vuMeter : public QWidget
+{
+    Q_OBJECT
+
+    QColor colorBg() const { return colBack; }
+    QColor colorValue() const { return colValue;}
+    QColor colorLow() const { return colLow;}
+    QColor colorHigh() const { return colHigh;}
+    double value() const { return val; }
+    double minValue() const {return min;}
+    double maxValue() const {return max; }
+
+public:
+
+    vuMeter(QWidget *parent = 0);
+    QSize minimumSizeHint() const;
+    QSize sizeHint() const;
+    void setHorizontal(bool h) {horizontal=h;}
+    void setLabelText(QString t);
+
+
+signals:
+
+    void valueChanged(double);
+
+public slots:
+
+    void setColorBg(QColor);
+    void setColorValue(QColor);
+    void setColorHigh(QColor);
+    void setColorMid(QColor);
+    void setColorLow(QColor);
+    void setColors(QColor,QColor ,QColor);
+    void setDivisions(int div) {divisions=div;}
+    void setValue(double);
+    void setMaximum(double);
+    void setMinimum(double);
+
+
+protected:
+
+    void paintEvent(QPaintEvent *);
+    void paintBorder();
+    void paintBar();
+
+
+
+private:
+
+    double min;
+    double max;
+    double val;
+    QColor colBack;
+    QColor colValue;
+    QColor colHigh;
+    QColor colLow;
+    QColor colMid;
+    bool horizontal;
+    qreal w;
+    qreal h;
+    int rw;
+    int rh;
+    int divisions;
+    QString labelText;
+
+
+};
+
+#endif
diff --git a/qsstv/widgets/waterfallform.cpp b/qsstv/widgets/waterfallform.cpp
new file mode 100644
index 0000000..cd868e2
--- /dev/null
+++ b/qsstv/widgets/waterfallform.cpp
@@ -0,0 +1,101 @@
+#include "waterfallform.h"
+#include "ui_waterfallform.h"
+#include "qsstvglobal.h"
+#include "sound/waterfalltext.h"
+
+
+waterfallForm::waterfallForm(QWidget *parent) :
+  QDialog(parent),
+  ui(new Ui::waterfallForm)
+{
+  ui->setupUi(this);
+  connect (ui->text1PushButton,SIGNAL(clicked()),SLOT(slotText1()));
+  connect (ui->text2PushButton,SIGNAL(clicked()),SLOT(slotText2()));
+  connect (ui->text3PushButton,SIGNAL(clicked()),SLOT(slotText3()));
+  connect (ui->text4PushButton,SIGNAL(clicked()),SLOT(slotText4()));
+  readSettings();
+  txt="";
+}
+
+waterfallForm::~waterfallForm()
+{
+  writeSettings();
+  delete ui;
+}
+
+
+void waterfallForm::accept()
+{
+  writeSettings();
+  done(QDialog::Accepted);
+}
+
+void waterfallForm::slotText1()
+{
+  getParams();
+  txt=txt1;
+  accept();
+}
+
+void waterfallForm::slotText2()
+{
+  getParams();
+  txt=txt2;
+  accept();
+}
+
+void waterfallForm::slotText3()
+{
+  getParams();
+  txt=txt3;
+  accept();
+}
+
+void waterfallForm::slotText4()
+{
+  getParams();
+  txt=txt4;
+  accept();
+}
+
+void waterfallForm::getParams()
+{
+  txt1=ui->wfText1->toPlainText();
+  txt2=ui->wfText2->toPlainText();
+  txt3=ui->wfText3->toPlainText();
+  txt4=ui->wfText4->toPlainText();
+}
+
+void waterfallForm::setParams()
+{
+  ui->wfText1->setPlainText(txt1);
+  ui->wfText2->setPlainText(txt2);
+  ui->wfText3->setPlainText(txt3);
+  ui->wfText4->setPlainText(txt4);
+}
+
+void waterfallForm::readSettings()
+{
+  QFont ft;
+  QSettings qSettings;
+  qSettings.beginGroup("Waterfall");
+  txt1=qSettings.value("text1","").toString();
+  txt2=qSettings.value("text2","").toString();
+  txt3=qSettings.value("text3","").toString();
+  txt4=qSettings.value("text4","").toString();
+  qSettings.endGroup();
+  setParams();
+}
+
+void waterfallForm::writeSettings()
+{
+  getParams();
+  QSettings qSettings;
+  qSettings.beginGroup("Waterfall");
+  qSettings.setValue("text1",txt1);
+  qSettings.setValue("text2",txt2);
+  qSettings.setValue("text3",txt3);
+  qSettings.setValue("text4",txt4);
+  qSettings.endGroup();
+}
+
diff --git a/qsstv/widgets/waterfallform.h b/qsstv/widgets/waterfallform.h
new file mode 100644
index 0000000..a8aaf90
--- /dev/null
+++ b/qsstv/widgets/waterfallform.h
@@ -0,0 +1,38 @@
+#ifndef WATERFALLFORM_H
+#define WATERFALLFORM_H
+
+#include <QDialog>
+
+namespace Ui {
+  class waterfallForm;
+  }
+
+class waterfallForm : public QDialog
+{
+  Q_OBJECT
+  
+public:
+  explicit waterfallForm(QWidget *parent = 0);
+  ~waterfallForm();
+  QString text(){ return txt;}
+
+private slots:
+  void slotText1();
+  void slotText2();
+  void slotText3();
+  void slotText4();
+private:
+  Ui::waterfallForm *ui;
+  QString txt1;
+  QString txt2;
+  QString txt3;
+  QString txt4;
+  QString txt;
+  void readSettings();
+  void writeSettings();
+  void getParams();
+  void setParams();
+  void accept();
+};
+
+#endif // WATERFALLFORM_H
diff --git a/qsstv/widgets/waterfallform.ui b/qsstv/widgets/waterfallform.ui
new file mode 100644
index 0000000..af2e6f5
--- /dev/null
+++ b/qsstv/widgets/waterfallform.ui
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>waterfallForm</class>
+ <widget class="QDialog" name="waterfallForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>323</width>
+    <height>421</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>8</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Maximum 10 charaters per line</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QPlainTextEdit" name="wfText1">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>80</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="text1PushButton">
+       <property name="text">
+        <string>TX #1</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QPlainTextEdit" name="wfText2">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>80</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="text2PushButton">
+       <property name="text">
+        <string>TX #2</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QPlainTextEdit" name="wfText3">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>80</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="text3PushButton">
+       <property name="text">
+        <string>TX #3</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QPlainTextEdit" name="wfText4">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>80</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>80</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QPushButton" name="text4PushButton">
+       <property name="text">
+        <string>TX #4</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>waterfallForm</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>waterfallForm</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qsstv/xmlrpc/ipcmessage.cpp b/qsstv/xmlrpc/ipcmessage.cpp
new file mode 100644
index 0000000..7861972
--- /dev/null
+++ b/qsstv/xmlrpc/ipcmessage.cpp
@@ -0,0 +1,83 @@
+#include "ipcmessage.h"
+
+#include <QDebug>
+
+
+#include<string.h>
+#include<time.h>
+#include<sys/ipc.h>
+#include<sys/msg.h>
+#include<sys/wait.h>
+#include<sys/errno.h>
+
+//extern int errno;       // error NO.
+
+
+ipcMessage::ipcMessage(int messageKey)
+{
+  key=messageKey;
+  messageQId = msgget(key, MSGPERM|IPC_CREAT);
+  if(messageQId<0) qDebug() << "IPC Error" << strerror(errno);
+//  else   qDebug()<< "queue opened: id=" << messageQId;
+}
+
+
+ipcMessage::~ipcMessage()
+{
+  closeQueue();
+}
+
+
+bool ipcMessage::sendMessage(QString t)
+{
+  if(messageQId<0) return false;
+  int len;
+  // message to send
+  msgBuf.mtype = MTYPE; // set the type of message
+  strncpy(msgBuf.mtext,t.toLatin1().data(),MSGTXMAXLEN);
+  len=strlen(msgBuf.mtext);
+
+  // send the message to queue
+  rc = msgsnd(messageQId, &msgBuf, len+1, IPC_NOWAIT);
+  if (rc < 0)
+    {
+      if(rc<0) qDebug() << "IPC Error" << strerror(errno);
+      return false;
+    }
+//  qDebug() << "message type" << msgBuf.mtype;
+//  qDebug() << "tx message:" << t;
+
+  return true;
+}
+
+bool ipcMessage::receiveMessage(QString &t)
+{
+  if(messageQId<0) return false;
+  // read the message from queue
+  rc = msgrcv(messageQId, &msgBuf, sizeof(msgBuf.mtext), 0, IPC_NOWAIT);
+  if (rc < -1)
+    {
+      if(rc<0) qDebug() << "IPC Error" << strerror(errno);
+      return false;
+    }
+  if(rc>=0)
+    {
+      t=msgBuf.mtext;
+//      qDebug() << "rx message:" << t;
+      return true;
+    }
+  return false;
+}
+
+bool ipcMessage::closeQueue()
+{
+  // remove the queue
+  rc=msgctl(messageQId,IPC_RMID,NULL);
+  if (rc < 0)
+    {
+      if(rc<0) qDebug() << "IPC Error" << strerror(errno);
+      return false;
+    }
+//  qDebug()<< "queue closed";
+  return 0;
+}
diff --git a/qsstv/xmlrpc/ipcmessage.h b/qsstv/xmlrpc/ipcmessage.h
new file mode 100644
index 0000000..0f170c3
--- /dev/null
+++ b/qsstv/xmlrpc/ipcmessage.h
@@ -0,0 +1,36 @@
+#ifndef IPCMESSAGE_H
+#define IPCMESSAGE_H
+#include <QString>
+
+#define MSGTXMAXLEN 2048
+#define MSGPERM 0666    // msg queue permission
+#define MTYPE 88
+
+
+struct smessageBuf
+{
+  long mtype;
+  char mtext[MSGTXMAXLEN];
+};
+
+
+
+class ipcMessage
+{
+public:
+  ipcMessage(int messageKey);
+  ~ipcMessage();
+  void essage();
+  bool sendMessage(QString t);
+  bool receiveMessage(QString &t);
+  bool closeQueue();
+private:
+  smessageBuf msgBuf;
+  int key;
+  int messageQId, rc;
+  int done;
+
+
+};
+
+#endif // IPCMESSAGE_H
diff --git a/qsstv/xmlrpc/maiaFault.cpp b/qsstv/xmlrpc/maiaFault.cpp
new file mode 100644
index 0000000..8db12ec
--- /dev/null
+++ b/qsstv/xmlrpc/maiaFault.cpp
@@ -0,0 +1,50 @@
+/*
+ * libMaia - maiaFault.cpp
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maiaFault.h"
+#include "maiaObject.h"
+
+
+MaiaFault::MaiaFault(const MaiaFault &other) : QObject(other.parent()) {
+	fault = other.fault;
+}
+
+MaiaFault::MaiaFault(int faultCode, QString faultString, QObject *parent) : QObject(parent) {
+	fault["faultCode"] = faultCode;
+	fault["faultString"] = faultString;
+}
+
+QString MaiaFault::toString() {
+	QDomDocument doc;
+	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" )); 
+	doc.appendChild(header);
+	QDomElement methodResponse = doc.createElement("methodResponse");
+	doc.appendChild(methodResponse);
+	QDomElement faultelement = doc.createElement("fault");
+	methodResponse.appendChild(faultelement);
+	faultelement.appendChild(MaiaObject::toXml(fault));
+	return doc.toString();
+}
diff --git a/qsstv/xmlrpc/maiaFault.h b/qsstv/xmlrpc/maiaFault.h
new file mode 100644
index 0000000..af8efc7
--- /dev/null
+++ b/qsstv/xmlrpc/maiaFault.h
@@ -0,0 +1,46 @@
+/*
+ * libMaia - maiaFault.h
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAIAFAULT_H
+#define MAIAFAULT_H
+
+#include <QtCore>
+#include <QtXml>
+
+class MaiaFault : public QObject {
+	Q_OBJECT
+	
+	public:
+		MaiaFault(int faultCode = 0, QString faultString = QString(), QObject *parent = 0);
+		MaiaFault(const MaiaFault &other);
+		QString toString();
+		QMap<QString,QVariant> fault;
+};
+
+Q_DECLARE_METATYPE(MaiaFault)
+
+
+#endif
diff --git a/qsstv/xmlrpc/maiaObject.cpp b/qsstv/xmlrpc/maiaObject.cpp
new file mode 100644
index 0000000..3c103b8
--- /dev/null
+++ b/qsstv/xmlrpc/maiaObject.cpp
@@ -0,0 +1,327 @@
+/*
+ * libMaia - maiaObject.cpp
+ * Copyright (c) 2003 Frerich Raabe <raabe at kde.org> and
+ *                    Ian Reinhart Geiser <geiseri at kde.org>
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maiaObject.h"
+
+MaiaObject::MaiaObject(QObject* parent) : QObject(parent)
+{
+    QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
+}
+
+QDomElement MaiaObject::toXml(QVariant arg)
+{
+
+    //dummy document
+    QDomDocument doc;
+    //value element, we need this in each case
+
+    if(arg.isNull())
+    {
+        QDomElement tagString = doc.createElement("value");
+        QDomText textString = doc.createTextNode("");
+
+        //        tagValue.appendChild(tagString);
+        tagString.appendChild(textString);
+
+        return tagString;
+    }
+
+    QDomElement tagValue = doc.createElement("value");
+    switch(arg.type())
+    {
+    case QVariant::String:
+    {
+
+        QDomElement tagString = doc.createElement("string");
+        QDomText textString = doc.createTextNode(arg.toString());
+
+        tagValue.appendChild(tagString);
+        tagString.appendChild(textString);
+
+        return tagValue;
+
+    }
+    case QVariant::Int:
+    {
+
+        QDomElement tagInt = doc.createElement("int");
+        QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));
+
+        tagValue.appendChild(tagInt);
+        tagInt.appendChild(textInt);
+
+        return tagValue;
+
+    }
+    case QVariant::Double:
+    {
+
+        QDomElement tagDouble = doc.createElement("double");
+        QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble(),'g',9));
+
+        tagValue.appendChild(tagDouble);
+        tagDouble.appendChild(textDouble);
+
+        return tagValue;
+
+    }
+    case QVariant::Bool:
+    {
+
+        QString textValue = arg.toBool() ? "1" : "0";
+
+        QDomElement tag = doc.createElement("boolean");
+        QDomText text = doc.createTextNode(textValue);
+
+        tagValue.appendChild(tag);
+        tag.appendChild(text);
+
+        return tagValue;
+
+    }
+    case QVariant::ByteArray:
+    {
+
+        QString textValue = arg.toByteArray().toBase64();
+
+        QDomElement tag = doc.createElement("base64");
+        QDomText text = doc.createTextNode(textValue);
+
+        tagValue.appendChild(tag);
+        tag.appendChild(text);
+
+        return tagValue;
+
+    }
+    case QVariant::DateTime:
+    {
+
+        QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");
+
+        QDomElement tag = doc.createElement("datetime.iso8601");
+        QDomText text = doc.createTextNode(textValue);
+
+        tagValue.appendChild(tag);
+        tag.appendChild(text);
+
+        return tagValue;
+
+    }
+    case QVariant::List: {
+
+        QDomElement tagArray = doc.createElement("array");
+        QDomElement tagData = doc.createElement("data");
+        tagArray.appendChild(tagData);
+        tagValue.appendChild(tagArray);
+
+        const QList<QVariant> args = arg.toList();
+        for(int i = 0; i < args.size(); ++i) {
+            tagData.appendChild(toXml(args.at(i)));
+        }
+
+        return tagValue;
+
+    }
+    case QVariant::Map:
+    {
+
+        QDomElement tagStruct = doc.createElement("struct");
+        QDomElement member;
+        QDomElement name;
+
+        tagValue.appendChild(tagStruct);
+
+        QMap<QString, QVariant> map = arg.toMap();
+        QMapIterator<QString, QVariant> i(map);
+        while(i.hasNext())
+        {
+            i.next();
+
+            member = doc.createElement("member");
+            name = doc.createElement("name");
+
+            // (key) -> name -> member -> struct
+            tagStruct.appendChild(member);
+            member.appendChild(name);
+            name.appendChild(doc.createTextNode(i.key()));
+
+            // add variables by recursion
+            member.appendChild(toXml(i.value()));
+        }
+
+        return tagValue;
+
+    }
+    default:
+        qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
+    }
+    return QDomElement(); //QString::null;
+}
+
+QVariant MaiaObject::fromXml(const QDomElement &elem)
+{
+    if(elem.tagName().toLower() != "value")
+    {
+        return QVariant();
+    }
+
+    // If no type is indicated, the type is string.
+    if(!elem.firstChild().isElement())
+    {
+        return QVariant(elem.text());
+    }
+
+    const QDomElement typeElement = elem.firstChild().toElement();
+    const QString typeName = typeElement.tagName().toLower();
+
+    if(typeName == "string") return QVariant(typeElement.text());
+    else if(typeName == "i4" || typeName == "int") return QVariant(typeElement.text().toInt());
+    else if(typeName == "double") return QVariant(typeElement.text().toDouble());
+    else if (typeName == "boolean")
+    {
+        if(typeElement.text().toLower() == "true" || typeElement.text() == "1") return QVariant(true);
+        else  return QVariant(false);
+    }
+    else if(typeName == "base64") return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
+    else if(typeName == "datetime" || typeName == "datetime.iso8601") return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
+    else if(typeName == "nil")  return QVariant(); // Non-standard extension: http://ontosys.com/xml-rpc/extensions.php
+    else if ( typeName == "array" )
+    {
+        QList<QVariant> values;
+        QDomNode valueNode = typeElement.firstChild().firstChild();
+        while(!valueNode.isNull())
+        {
+            values << fromXml(valueNode.toElement());
+            valueNode = valueNode.nextSibling();
+        }
+        return QVariant(values);
+    }
+    else if ( typeName == "struct" )
+    {
+        QMap<QString, QVariant> map;
+        QDomNode memberNode = typeElement.firstChild();
+        while(!memberNode.isNull())
+        {
+            const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
+            const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
+            map[key] = data;
+            memberNode = memberNode.nextSibling();
+        }
+        return QVariant(map);
+    }
+    else
+    {
+        qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
+    }
+    return QVariant();
+}
+
+
+QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {
+
+
+    QDomDocument doc;
+
+    QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
+    doc.appendChild(header);
+
+    QDomElement methodCall = doc.createElement("methodCall");
+    QDomElement methodName = doc.createElement("methodName");
+    QDomElement params = doc.createElement("params");
+    QDomElement param;
+
+    doc.appendChild(methodCall);
+    methodCall.appendChild(methodName);
+    methodName.appendChild(doc.createTextNode(method));
+
+    methodCall.appendChild(params);
+
+    for(int i = 0; i < args.size(); ++i) {
+        param = doc.createElement("param");
+        param.appendChild(toXml(args.at(i)));
+        params.appendChild(param);
+    }
+
+    return doc.toString();
+}
+
+QString MaiaObject::prepareResponse(QVariant arg) {
+
+    QDomDocument doc;
+
+    QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
+    doc.appendChild(header);
+
+    QDomElement methodResponse = doc.createElement("methodResponse");
+    QDomElement params = doc.createElement("params");
+    QDomElement param;
+
+    doc.appendChild(methodResponse);
+
+    methodResponse.appendChild(params);
+
+    //    if(!arg.isNull())
+    {
+        param = doc.createElement("param");
+        param.appendChild(toXml(arg));
+        params.appendChild(param);
+    }
+    return doc.toString(-1);
+}
+
+void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
+    QDomDocument doc;
+    QVariant arg;
+    QString errorMsg;
+    int errorLine;
+    int errorColumn;
+    if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
+        emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
+        delete this;
+        return;
+    }
+    if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
+        QDomNode paramNode = doc.documentElement().firstChild().firstChild();
+        if(!paramNode.isNull()) {
+            arg = fromXml( paramNode.firstChild().toElement() );
+        }
+        emit aresponse(arg, reply);
+    } else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
+        const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
+        emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
+                errorVariant.toMap() [ "faultString" ].toString(),
+                reply);
+    } else {
+        emit fault(-32600,
+                   tr("parse error: invalid xml-rpc. not conforming to spec."),
+                   reply);
+    }
+    delete this;
+    return;
+}
+
diff --git a/qsstv/xmlrpc/maiaObject.h b/qsstv/xmlrpc/maiaObject.h
new file mode 100644
index 0000000..aea5bb8
--- /dev/null
+++ b/qsstv/xmlrpc/maiaObject.h
@@ -0,0 +1,56 @@
+/*
+ * libMaia - maiaObject.h
+ * Copyright (c) 2003 Frerich Raabe <raabe at kde.org> and
+ *                    Ian Reinhart Geiser <geiseri at kde.org>
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAIAOBJECT_H
+#define MAIAOBJECT_H
+
+#include <QtCore>
+#include <QtXml>
+#include <QNetworkReply>
+
+class MaiaObject : public QObject {
+	Q_OBJECT
+	
+	public:
+		MaiaObject(QObject* parent = 0);
+		static QDomElement toXml(QVariant arg);
+		static QVariant fromXml(const QDomElement &elem);
+		QString prepareCall(QString method, QList<QVariant> args);
+		static QString prepareResponse(QVariant arg);
+		
+	public slots:
+		void parseResponse(QString response, QNetworkReply* reply);
+	
+	signals:
+		void aresponse(QVariant &, QNetworkReply* reply);
+		void call(const QString, const QList<QVariant>);
+		void fault(int, const QString &, QNetworkReply* reply);
+		
+};
+
+#endif
diff --git a/qsstv/xmlrpc/maiaXmlRpcClient.cpp b/qsstv/xmlrpc/maiaXmlRpcClient.cpp
new file mode 100644
index 0000000..1e781b3
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcClient.cpp
@@ -0,0 +1,111 @@
+/*
+ * libMaia - maiaXmlRpcClient.cpp
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maiaXmlRpcClient.h"
+#include "maiaFault.h"
+
+MaiaXmlRpcClient::MaiaXmlRpcClient(QObject* parent) : QObject(parent),
+	manager(this), request() 
+{
+	init();
+}
+
+MaiaXmlRpcClient::MaiaXmlRpcClient(QUrl url, QObject* parent) : QObject(parent),
+	manager(this), request(url)
+{
+	init();
+	setUrl(url);
+}
+
+MaiaXmlRpcClient::MaiaXmlRpcClient(QUrl url, QString userAgent, QObject *parent) : QObject(parent) {
+	// userAgent should adhere to RFC 1945 http://tools.ietf.org/html/rfc1945
+	init();
+	request.setRawHeader("User-Agent", userAgent.toLatin1());
+	setUrl(url);
+}
+
+void MaiaXmlRpcClient::init()
+{
+	request.setRawHeader("User-Agent", "libmaia/0.2");
+	request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
+	
+	connect(&manager, SIGNAL(finished(QNetworkReply*)),
+			this, SLOT(replyFinished(QNetworkReply*)));
+	connect(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
+			this, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)));
+}
+
+void MaiaXmlRpcClient::setUrl(QUrl url)
+{
+	if(!url.isValid())
+		return;
+	
+	request.setUrl(url);
+}
+
+void MaiaXmlRpcClient::setUserAgent(QString userAgent) {
+	request.setRawHeader("User-Agent", userAgent.toLatin1());
+}
+
+QNetworkReply* MaiaXmlRpcClient::call(QString method, QList<QVariant> args,
+							QObject* responseObject, const char* responseSlot,
+							QObject* faultObject, const char* faultSlot) {
+	MaiaObject* call = new MaiaObject(this);
+	connect(call, SIGNAL(aresponse(QVariant &, QNetworkReply *)), responseObject, responseSlot);
+	connect(call, SIGNAL(fault(int, const QString &, QNetworkReply *)), faultObject, faultSlot);
+
+	QNetworkReply* reply = manager.post( request,
+		call->prepareCall(method, args).toUtf8() );
+
+	callmap[reply] = call;
+	return reply;
+}
+
+void MaiaXmlRpcClient::setSslConfiguration(const QSslConfiguration &config) {
+	request.setSslConfiguration(config);
+}
+
+QSslConfiguration MaiaXmlRpcClient::sslConfiguration () const {
+	return request.sslConfiguration();
+}
+
+void MaiaXmlRpcClient::replyFinished(QNetworkReply* reply) {
+	QString response;
+	if(!callmap.contains(reply))
+		return;
+	if(reply->error() != QNetworkReply::NoError) {
+		MaiaFault fault(-32300, reply->errorString());
+		response = fault.toString();
+	} else {
+		response = QString::fromUtf8(reply->readAll());
+	}
+	
+	// parseResponse deletes the MaiaObject
+	callmap[reply]->parseResponse(response, reply);
+	reply->deleteLater();
+	callmap.remove(reply);
+}
diff --git a/qsstv/xmlrpc/maiaXmlRpcClient.h b/qsstv/xmlrpc/maiaXmlRpcClient.h
new file mode 100644
index 0000000..3e118be
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcClient.h
@@ -0,0 +1,65 @@
+/*
+ * libMaia - maiaXmlRpcClient.h
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAIAXMLRPCCLIENT_H
+#define MAIAXMLRPCCLIENT_H
+
+#include <QtCore>
+#include <QtXml>
+#include <QtNetwork>
+
+#include "maiaObject.h"
+
+class MaiaXmlRpcClient : public QObject {
+	Q_OBJECT
+	
+	public:
+		MaiaXmlRpcClient(QObject* parent = 0);
+		MaiaXmlRpcClient(QUrl url, QObject* parent = 0);
+		MaiaXmlRpcClient(QUrl url, QString userAgent, QObject *parent = 0);
+		void setUrl(QUrl url);
+		void setUserAgent(QString userAgent);
+		QNetworkReply* call(QString method, QList<QVariant> args,
+		QObject* responseObject, const char* responseSlot,
+		QObject* faultObject, const char* faultSlot);
+		void setSslConfiguration(const QSslConfiguration &config);
+		QSslConfiguration sslConfiguration () const;
+	
+	signals:
+		void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
+	
+	private slots:
+		void replyFinished(QNetworkReply*);
+
+	private:
+		void init();
+		QNetworkAccessManager manager;
+		QNetworkRequest request;
+		QMap<QNetworkReply*, MaiaObject*> callmap;
+};
+
+#endif
diff --git a/qsstv/xmlrpc/maiaXmlRpcServer.cpp b/qsstv/xmlrpc/maiaXmlRpcServer.cpp
new file mode 100644
index 0000000..0d90bbe
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcServer.cpp
@@ -0,0 +1,91 @@
+/*
+ * libMaia - maiaXmlRpcServer.cpp
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maiaXmlRpcServer.h"
+#include "maiaFault.h"
+
+MaiaXmlRpcServer::MaiaXmlRpcServer(const QHostAddress &address, quint16 port, QObject* parent) : QObject(parent) {
+  allowedAddresses = NULL;
+  connect(&server, SIGNAL(newConnection()), this, SLOT(newConnection()));
+  server.listen(address, port);
+}
+
+MaiaXmlRpcServer::MaiaXmlRpcServer(quint16 port, QObject* parent) : QObject(parent)
+{
+  allowedAddresses = NULL;
+  connect(&server, SIGNAL(newConnection()), this, SLOT(newConnection()));
+  server.listen(QHostAddress::Any, port);
+}
+
+MaiaXmlRpcServer::MaiaXmlRpcServer(const QHostAddress &address, quint16 port, QList<QHostAddress> *allowedAddresses, QObject *parent) : QObject(parent) {
+  this->allowedAddresses = allowedAddresses;
+  connect(&server, SIGNAL(newConnection()), this, SLOT(newConnection()));
+  server.listen(address, port);
+}
+
+void MaiaXmlRpcServer::addMethod(QString method,
+                                 QObject* responseObject, const char* responseSlot) {
+  objectMap[method] = responseObject;
+  slotMap[method] = responseSlot;
+}
+
+void MaiaXmlRpcServer::removeMethod(QString method) {
+  objectMap.remove(method);
+  slotMap.remove(method);
+}
+
+void MaiaXmlRpcServer::getMethod(QString method, QObject **responseObject, const char **responseSlot)
+{
+  if(!objectMap.contains(method))
+    {
+      *responseObject = NULL;
+      *responseSlot = NULL;
+      return;
+    }
+  *responseObject = objectMap[method];
+  *responseSlot = slotMap[method];
+}
+
+void MaiaXmlRpcServer::newConnection()
+{
+  QTcpSocket *connection = server.nextPendingConnection();
+  if (!this->allowedAddresses || this->allowedAddresses->isEmpty() || this->allowedAddresses->contains(connection->peerAddress()))
+    {
+      MaiaXmlRpcServerConnection *client = new MaiaXmlRpcServerConnection(connection, this);
+      connect(client, SIGNAL(getMethod(QString, QObject **, const char**)),
+              this, SLOT(getMethod(QString, QObject **, const char**)));
+    } else {
+      qWarning() << "Rejected connection attempt from" << connection->peerAddress().toString();
+      connection->disconnectFromHost();
+    }
+}
+
+QHostAddress MaiaXmlRpcServer::getServerAddress()
+{
+  return server.serverAddress();
+}
+
diff --git a/qsstv/xmlrpc/maiaXmlRpcServer.h b/qsstv/xmlrpc/maiaXmlRpcServer.h
new file mode 100644
index 0000000..d912f47
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcServer.h
@@ -0,0 +1,65 @@
+/*
+ * libMaia - maiaXmlRpcServer.h
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAIAXMLRPCSERVER_H
+#define MAIAXMLRPCSERVER_H
+
+#include <QtCore>
+#include <QtXml>
+#include <QtNetwork>
+
+#include "maiaObject.h"
+#include "maiaXmlRpcServerConnection.h"
+
+class MaiaXmlRpcServer : public QObject {
+	Q_OBJECT
+	
+	public:
+		MaiaXmlRpcServer(const QHostAddress &address = QHostAddress::Any, quint16 port = 8080, QObject* parent = 0);
+		MaiaXmlRpcServer(const QHostAddress &address = QHostAddress::Any, quint16 port = 8080, QList<QHostAddress> *allowedAddresses = 0, QObject *parent = 0);
+		MaiaXmlRpcServer(quint16 port = 8080, QObject* parent = 0);
+		void addMethod(QString method, QObject *responseObject, const char* responseSlot);
+		void removeMethod(QString method);
+		QHostAddress getServerAddress();
+
+	public slots:
+		void getMethod(QString method, QObject **responseObject, const char** responseSlot);
+	
+	private slots:
+		void newConnection();
+	
+	private:
+		QTcpServer server;
+		QHash<QString, QObject*> objectMap;
+		QHash<QString, const char*> slotMap;
+		QList<QHostAddress> *allowedAddresses;
+		
+	friend class maiaXmlRpcServerConnection;
+		
+};
+
+#endif
diff --git a/qsstv/xmlrpc/maiaXmlRpcServerConnection.cpp b/qsstv/xmlrpc/maiaXmlRpcServerConnection.cpp
new file mode 100644
index 0000000..141e0eb
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcServerConnection.cpp
@@ -0,0 +1,332 @@
+/*
+ * libMaia - maiaXmlRpcServerConnection.cpp
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "maiaXmlRpcServerConnection.h"
+#include "maiaXmlRpcServer.h"
+
+MaiaXmlRpcServerConnection::MaiaXmlRpcServerConnection(QTcpSocket *connection, QObject* parent) : QObject(parent)
+{
+  header = NULL;
+  clientConnection = connection;
+  connect(clientConnection, SIGNAL(readyRead()), this, SLOT(readFromSocket()));
+  connect(clientConnection, SIGNAL(disconnected()), this, SLOT(deleteLater()));
+}
+
+MaiaXmlRpcServerConnection::~MaiaXmlRpcServerConnection()
+{
+  clientConnection->deleteLater();
+  delete header;
+}
+
+void MaiaXmlRpcServerConnection::readFromSocket()
+{
+  QString lastLine;
+
+  while(clientConnection->canReadLine() && !header)
+    {
+      lastLine = clientConnection->readLine();
+      headerString += lastLine;
+      if(lastLine == "\r\n") { /* http header end */
+          header = new QHttpRequestHeader(headerString);
+          if(!header->isValid())
+            {
+              /* return http error */
+              qDebug() << "Invalid Header";
+              return;
+            }
+          else if(header->method() != "POST")
+            {
+              /* return http error */
+              qDebug() << "No Post!";
+              return;
+            }
+          else if(!header->contentLength())
+            {
+              /* return fault */
+              qDebug() << "No Content Length";
+              return;
+            }
+        }
+    }
+
+  if(header)
+    {
+      if(header->contentLength() <= clientConnection->bytesAvailable())
+        {
+          /* all data complete */
+          parseCall(clientConnection->readAll());
+        }
+    }
+}
+
+void MaiaXmlRpcServerConnection::sendResponse(QString content) {
+  QHttpResponseHeader header(200, "OK");
+  QByteArray block;
+  header.setValue("Server", "MaiaXmlRpc/0.1");
+  header.setValue("Content-Type", "text/xml");
+  header.setValue("Connection","close");
+  header.setContentLength(content.toUtf8().length());
+  block.append(header.toString().toUtf8());
+  block.append(content.toUtf8());
+  clientConnection->write(block);
+  clientConnection->disconnectFromHost();
+}
+
+void MaiaXmlRpcServerConnection::parseCall(QString call)
+{
+  QDomDocument doc;
+  QList<QVariant> args;
+  QVariant ret;
+  QString response;
+  QObject *responseObject;
+  const char *responseSlot;
+
+  if(!doc.setContent(call)) { /* received invalid xml */
+      MaiaFault fault(-32700, "parse error: not well formed");
+      sendResponse(fault.toString());
+      return;
+    }
+
+  QDomElement methodNameElement = doc.documentElement().firstChildElement("methodName");
+  QDomElement params = doc.documentElement().firstChildElement("params");
+  if(methodNameElement.isNull()) { /* invalid call */
+      MaiaFault fault(-32600, "server error: invalid xml-rpc. not conforming to spec");
+      sendResponse(fault.toString());
+      return;
+    }
+
+  QString methodName = methodNameElement.text();
+//  qDebug() << "methodname" << methodName;
+
+  emit getMethod(methodName, &responseObject, &responseSlot);
+  if(!responseObject)
+    { /* unknown method */
+      MaiaFault fault(-32601, "server error: requested method not found");
+      sendResponse(fault.toString());
+      return;
+    }
+
+  QDomNode paramNode = params.firstChild();
+  while(!paramNode.isNull())
+    {
+      args << MaiaObject::fromXml( paramNode.firstChild().toElement());
+      paramNode = paramNode.nextSibling();
+    }
+
+
+  if(!invokeMethodWithVariants(responseObject, responseSlot, args, &ret))
+    { /* error invoking... */
+      MaiaFault fault(-32602, "server error: invalid method parameters");
+      sendResponse(fault.toString());
+      return;
+    }
+
+
+  if(ret.canConvert<MaiaFault>())
+    {
+      response = ret.value<MaiaFault>().toString();
+    }
+  else
+    {
+      response = MaiaObject::prepareResponse(ret);
+    }
+  sendResponse(response);
+}
+
+
+/*	taken from http://delta.affinix.com/2006/08/14/invokemethodwithvariants/
+  thanks to Justin Karneges once again :) */
+bool invokeMethodWithVariants(QObject *obj,
+                              const QByteArray &method, const QVariantList &args,
+                              QVariant *ret, Qt::ConnectionType type) {
+
+
+  // QMetaObject::invokeMethod() has a 10 argument maximum
+  if(args.count() > 10)
+    return false;
+
+  QList<QByteArray> argTypes;
+  for(int n = 0; n < args.count(); ++n)
+    argTypes += args[n].typeName();
+
+  // get return type
+  int metatype = 0;
+  QByteArray retTypeName = getReturnType(obj->metaObject(), method, argTypes);
+  if(!retTypeName.isEmpty()  && retTypeName != "QVariant")
+    {
+      metatype = QMetaType::type(retTypeName.data());
+      //      qDebug() << QMetaType::typeName(metatype);
+      if(metatype == 0) // lookup failed
+        return false;
+    }
+
+  QGenericArgument arg[10];
+  for(int n = 0; n < args.count(); ++n)
+    arg[n] = QGenericArgument(args[n].typeName(), args[n].constData());
+
+  QGenericReturnArgument retarg;
+  QVariant retval;
+  QString test(QMetaType::typeName(metatype));
+  if(metatype != 0)
+    {
+            if( test=="void")
+              {
+                retval=QVariant();
+                  retTypeName="";
+
+              }
+            else
+      {
+        retval = QVariant(metatype, (const void *)0);
+      }
+      retarg = QGenericReturnArgument(retval.typeName(), retval.data());
+    }
+  else
+    {
+      /* QVariant */
+      retarg = QGenericReturnArgument("QVariant", &retval);
+    }
+
+  if(retTypeName.isEmpty())
+    {
+      /* void */
+      if(!QMetaObject::invokeMethod(obj, method.data(), type,
+                                    arg[0], arg[1], arg[2], arg[3], arg[4],
+                                    arg[5], arg[6], arg[7], arg[8], arg[9]))
+        return false;
+    }
+  else
+    {
+      if(!QMetaObject::invokeMethod(obj, method.data(), type, retarg,
+                                    arg[0], arg[1], arg[2], arg[3], arg[4],
+                                    arg[5], arg[6], arg[7], arg[8], arg[9]))
+        return false;
+    }
+
+  if(retval.isValid() && ret)
+    *ret = retval;
+  return true;
+}
+
+QByteArray getReturnType(const QMetaObject *obj,const QByteArray &method, const QList<QByteArray> argTypes)
+{
+  for(int n = 0; n < obj->methodCount(); ++n)
+    {
+      QMetaMethod m = obj->method(n);
+#if QT_VERSION >= 0x050000
+      QByteArray sig = m.methodSignature();
+#else
+      QByteArray sig = m.signature();
+#endif
+      int offset = sig.indexOf('(');
+      if(offset == -1)  continue;
+      QByteArray name = sig.mid(0, offset);
+      if(name != method) continue;
+      if(m.parameterTypes() != argTypes) continue;
+
+      return m.typeName();
+    }
+  return QByteArray();
+}
+
+/*
+  simple Qt4 class emulater
+*/
+
+#if QT_VERSION >= 0x050000
+QHttpRequestHeader::QHttpRequestHeader(QString headerString)
+{
+  this->mHeaderString = headerString;
+
+  QStringList hdrs = headerString.split("\r\n");
+  QStringList hdrkv;
+  for (int i = 0; i < hdrs.size(); i++)
+    {
+      if (hdrs.at(i).trimmed().isEmpty()) break;
+      if (i == 0)
+        {
+          hdrkv = hdrs.at(i).split(" ");
+          this->mMethod = hdrkv.at(0);
+        }
+      else
+        {
+          hdrkv = hdrs.at(i).split(":");
+          this->mHeaders[hdrkv.at(0)] = hdrkv.at(1).trimmed();
+        }
+    }
+}
+
+bool QHttpRequestHeader::isValid()
+{
+  if (this->mHeaderString.isEmpty()) return false;
+  if (this->mMethod != "GET" && this->mMethod != "POST") return false;
+  if (this->mHeaders.size() < 2) return false;
+  return true;
+}
+
+QString QHttpRequestHeader::method()
+{
+  return this->mMethod;
+}
+
+uint QHttpRequestHeader::contentLength() const
+{
+  uint clen = 0;
+
+  clen = this->mHeaders.value("Content-length").toUInt();
+
+  return clen;
+}
+
+QHttpResponseHeader::QHttpResponseHeader(int code, QString text)
+{
+  this->mCode = code;
+  this->mText = text;
+}
+
+void QHttpResponseHeader::setValue(const QString &key, const QString &value)
+{
+  this->mHeaders[key] = value;
+}
+
+QString QHttpResponseHeader::toString() const
+{
+  QMapIterator<QString, QString> it(this->mHeaders);
+  QString hdrstr;
+
+  hdrstr += QString("HTTP/1.1 %1 %2\r\n").arg(this->mCode).arg(this->mText);
+  while (it.hasNext())
+    {
+      it.next();
+      hdrstr += it.key() + ": " + it.value() + "\r\n";
+    }
+  hdrstr += "\r\n";
+
+  return hdrstr;
+}
+
+#endif
diff --git a/qsstv/xmlrpc/maiaXmlRpcServerConnection.h b/qsstv/xmlrpc/maiaXmlRpcServerConnection.h
new file mode 100644
index 0000000..bad1d88
--- /dev/null
+++ b/qsstv/xmlrpc/maiaXmlRpcServerConnection.h
@@ -0,0 +1,106 @@
+/*
+ * libMaia - maiaXmlRpcServerConnection.h
+ * Copyright (c) 2007 Sebastian Wiedenroth <wiedi at frubar.net>
+ *                and Karl Glatz
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MAIAXMLRPCSERVERCONNECTION_H
+#define MAIAXMLRPCSERVERCONNECTION_H
+
+#include <QtCore>
+#include <QtXml>
+#include <QtNetwork>
+#include "maiaFault.h"
+
+#if QT_VERSION >= 0x050000
+class QHttpRequestHeader
+{
+public:
+    explicit QHttpRequestHeader(QString headerString);
+    virtual ~QHttpRequestHeader() {}
+
+    bool isValid();
+    QString method();
+    uint contentLength() const;
+
+private:
+    QString mHeaderString;
+    QString mMethod;
+    QMap<QString, QString> mHeaders;
+};
+
+class QHttpResponseHeader
+{
+public:
+    explicit QHttpResponseHeader(int code, QString text);
+    virtual ~QHttpResponseHeader() {}
+    void setValue(const QString &key, const QString &value);
+    virtual QString toString() const;
+    void setContentLength(int len)
+    {
+       setValue("Content-length", QString::number(len));
+    }
+
+private:
+    int mCode;
+    QString mText;
+    QMap<QString, QString> mHeaders;
+};
+#endif
+
+
+class MaiaXmlRpcServerConnection : public QObject {
+	Q_OBJECT
+	
+	public:
+		MaiaXmlRpcServerConnection(QTcpSocket *connection, QObject *parent = 0);
+		~MaiaXmlRpcServerConnection();
+		
+	signals:
+		void getMethod(QString method, QObject **responseObject, const char **responseSlot);
+
+	private slots:
+		void readFromSocket();
+	
+	private:
+		void sendResponse(QString content);
+		void parseCall(QString call);
+
+		
+
+		QTcpSocket *clientConnection;
+		QString headerString;
+		QHttpRequestHeader *header;
+		
+};
+
+
+QByteArray getReturnType(const QMetaObject *obj,
+            const QByteArray &method, const QList<QByteArray> argTypes);
+
+bool invokeMethodWithVariants(QObject *obj,
+        const QByteArray &method, const QVariantList &args,
+        QVariant *ret, Qt::ConnectionType type = Qt::AutoConnection);
+
+#endif
diff --git a/qsstv/xmlrpc/xmlinterface.cpp b/qsstv/xmlrpc/xmlinterface.cpp
new file mode 100644
index 0000000..3a838c1
--- /dev/null
+++ b/qsstv/xmlrpc/xmlinterface.cpp
@@ -0,0 +1,156 @@
+#include "xmlinterface.h"
+#include "qsstvglobal.h"
+
+xmlInterface::xmlInterface(QObject *parent) : QObject(parent)
+{
+  rigInfo.trxState="RX";
+  rpcServer = new MaiaXmlRpcServer(7362, this);
+  rpcServer->addMethod("main.get_trx_state", this, "getTrxState");
+  rpcServer->addMethod("rig.take_control", this, "takeControl");
+  rpcServer->addMethod("rig.set_name", this, "setName");
+  rpcServer->addMethod("rig.set_modes", this, "setModes");
+  rpcServer->addMethod("rig.set_mode", this, "setMode");
+  rpcServer->addMethod("rig.set_bandwidths", this, "setBandwidths");
+  rpcServer->addMethod("rig.set_bandwidth", this, "setBandwidth");
+  rpcServer->addMethod("main.set_wf_sideband", this, "setWfSideband");
+  rpcServer->addMethod("rig.set_frequency", this, "setFrequency");
+  rpcServer->addMethod("system.multicall", this, "systemMulticall");
+  rpcServer->addMethod("main.get_frequency", this, "getFrequency");
+  rpcServer->addMethod("rig.get_mode", this, "getMode");
+  rpcServer->addMethod("rig.get_bandwidth", this, "getBandwidth");
+}
+
+
+void xmlInterface::takeControl() { log("takeControl",""); }
+
+void xmlInterface::setName(QString t)
+{
+    rigInfo.rigName=t;
+    log("setName",t);
+}
+
+void xmlInterface::setMode(QString t)
+{
+    rigInfo.mode=t;
+    log("setMode",t);
+}
+
+void xmlInterface::setModes(QVariantList t)
+{
+    log("setModes",t);
+}
+
+void xmlInterface::setBandwidths(QVariantList t)
+{
+    log("setBandwidths",t);
+}
+
+void xmlInterface::setBandwidth(QString t)
+{
+    rigInfo.bandWidth=t;
+    log("setBandwidth",t);
+}
+
+void xmlInterface::setWfSideband(QString t)
+{
+    log("setWfSideband",t);
+}
+void xmlInterface::setFrequency(double d)
+{
+    rigInfo.frequency=d;
+    log("setFrequency",QString::number(d,'g',9));
+}
+
+QString xmlInterface::getTrxState()
+{
+//  log("getTrxSate",rigInfo.trxState);
+  return rigInfo.trxState;
+}
+
+double  xmlInterface::getFrequency()
+{
+  log("getFrequency",QString::number(rigInfo.frequency,'g',9));
+//  return rigInfo.frequency;
+  return 14077777;
+}
+
+QString  xmlInterface::getMode()
+{
+    log("getMode",rigInfo.mode);
+    return rigInfo.mode;
+}
+QString  xmlInterface::getBandwidth()
+{
+    log("getBandwidth",rigInfo.bandWidth);
+    return rigInfo.bandWidth;
+}
+
+
+QVariantList xmlInterface::systemMulticall(QVariantList s)
+{
+    QVariant ret;
+    QVariantMap m;
+    QVariantList args;
+        QVariantList tmp;
+    QVariantList results;
+
+    //    QString response;
+    QObject *responseObject;
+    const char *responseSlot;
+    int i,j;
+    log("systemMulticall",s);
+    for(i=0;i<s.length();i++)
+    {
+        tmp.clear();
+        m=s.at(i).toMap();
+
+        emit rpcServer->getMethod(m["methodName"].toString(), &responseObject, &responseSlot);
+        if(responseObject!=0)
+        {
+
+            args=m["params"].toList();
+            for(j=0;j<args.count();)
+            {
+                if(args.at(j).type()==QVariant::Invalid) args.takeAt(j);
+                else j++;
+            }
+            if(!invokeMethodWithVariants(responseObject, responseSlot, args, &ret))
+            { /* error invoking... */
+                continue;
+//                return QVariantList ();
+            }
+            tmp.append(ret);
+            results << (QVariant)tmp;
+        }
+    }
+
+    return  results;
+}
+
+void xmlInterface::activatePTT(bool b)
+{
+    if(b)
+    {
+        rigInfo.trxState="TX";
+    }
+    else
+      {
+        rigInfo.trxState="RX";
+    }
+}
+
+
+void xmlInterface::log(QString cmd,QString t)
+{
+  addToLog(cmd+": "+t,LOGXML);
+}
+
+
+
+void xmlInterface::log(QString cmd,QVariantList t)
+{
+    int i;
+    QString tmp;
+    for(i=0;i<t.length();i++) tmp+=t.at(i).toString();
+    log(cmd,tmp);
+}
diff --git a/qsstv/xmlrpc/xmlinterface.h b/qsstv/xmlrpc/xmlinterface.h
new file mode 100644
index 0000000..ef3295f
--- /dev/null
+++ b/qsstv/xmlrpc/xmlinterface.h
@@ -0,0 +1,52 @@
+#ifndef XMLINTERFACE_H
+#define XMLINTERFACE_H
+
+#include <QObject>
+#include "maiaXmlRpcServer.h"
+#include "maiaXmlRpcServerConnection.h"
+
+struct sxmlInfo
+{
+  sxmlInfo()
+  {
+    frequency=-1.;
+  }
+  QString rigName;
+  QString bandWidth;
+  double frequency;
+  QString mode;
+  QString trxState;
+};
+
+class xmlInterface : public QObject
+{
+  Q_OBJECT
+public:
+  explicit xmlInterface(QObject *parent = 0);
+  void activatePTT(bool b);
+
+  
+public slots:
+  void takeControl();
+  void setName(QString t);
+  void setModes(QVariantList t);
+  void setBandwidths(QVariantList t);
+  void setBandwidth(QString t);
+  void setWfSideband(QString t);
+  void setMode(QString t);
+  void setFrequency(double d);
+  QVariantList systemMulticall(QVariantList s);
+  double getFrequency();
+  QString getMode();
+  QString getBandwidth();
+  QString getTrxState();
+
+
+private:
+  MaiaXmlRpcServer *rpcServer;
+  void log(QString cmd,QString t);
+  void log(QString cmd, QVariantList t);
+  sxmlInfo rigInfo;
+};
+
+#endif // XMLINTERFACE_H
diff --git a/qsstv_8_2.pro b/qsstv_8_2.pro
new file mode 100644
index 0000000..6da9e42
--- /dev/null
+++ b/qsstv_8_2.pro
@@ -0,0 +1,15 @@
+TEMPLATE = subdirs
+
+CONFIG(release ,debug|release){
+SUBDIRS += \
+    qsstv
+}
+
+CONFIG(debug ,debug|release){
+SUBDIRS += \
+    qwt \
+    qsstv
+}
+
+
+
diff --git a/qwt/qwt.h b/qwt/qwt.h
new file mode 100644
index 0000000..9860453
--- /dev/null
+++ b/qwt/qwt.h
@@ -0,0 +1,23 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_H
+#define QWT_H
+
+
+#include "qwt_global.h"
+
+/*!
+  Some constants for use within Qwt.
+*/
+namespace Qwt
+{
+};
+
+#endif
diff --git a/qwt/qwt.pro b/qwt/qwt.pro
new file mode 100644
index 0000000..ee6de31
--- /dev/null
+++ b/qwt/qwt.pro
@@ -0,0 +1,213 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2014-03-11T09:49:32
+#
+#-------------------------------------------------
+QT += core gui concurrent
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = qwt
+TEMPLATE = lib
+CONFIG += staticlib
+
+SOURCES += \
+    qwt_point_3d.cpp \
+    qwt_abstract_legend.cpp \
+    qwt_pixel_matrix.cpp \
+    qwt_plot_xml.cpp \
+    qwt_math.cpp \
+    qwt_sampling_thread.cpp \
+    qwt_plot_seriesitem.cpp \
+    qwt_legend_data.cpp \
+    qwt_point_polar.cpp \
+    qwt_transform.cpp \
+    qwt_plot_magnifier.cpp \
+    qwt_plot_dict.cpp \
+    qwt_plot_textlabel.cpp \
+    qwt_scale_map.cpp \
+    qwt_painter_command.cpp \
+    qwt_analog_clock.cpp \
+    qwt_compass_rose.cpp \
+    qwt_plot_panner.cpp \
+    qwt_event_pattern.cpp \
+    qwt_date_scale_draw.cpp \
+    qwt_point_data.cpp \
+    qwt_column_symbol.cpp \
+    qwt_text_label.cpp \
+    qwt_plot_zoneitem.cpp \
+    qwt_compass.cpp \
+    qwt_scale_div.cpp \
+    qwt_plot_spectrocurve.cpp \
+    qwt_matrix_raster_data.cpp \
+    qwt_interval_symbol.cpp \
+    qwt_system_clock.cpp \
+    qwt_round_scale_draw.cpp \
+    qwt_arrow_button.cpp \
+    qwt_plot_directpainter.cpp \
+    qwt_interval.cpp \
+    qwt_widget_overlay.cpp \
+    qwt_spline.cpp \
+    qwt_text_engine.cpp \
+    qwt_series_data.cpp \
+    qwt_legend_label.cpp \
+    qwt_plot_picker.cpp \
+    qwt_plot_abstract_barchart.cpp \
+    qwt_abstract_scale_draw.cpp \
+    qwt_plot_grid.cpp \
+    qwt_color_map.cpp \
+    qwt_curve_fitter.cpp \
+    qwt_abstract_scale.cpp \
+    qwt_plot_scaleitem.cpp \
+    qwt_dial_needle.cpp \
+    qwt_plot_shapeitem.cpp \
+    qwt_plot_barchart.cpp \
+    qwt_raster_data.cpp \
+    qwt_magnifier.cpp \
+    qwt_panner.cpp \
+    qwt_clipper.cpp \
+    qwt_picker_machine.cpp \
+    qwt_null_paintdevice.cpp \
+    qwt_plot_marker.cpp \
+    qwt_dyngrid_layout.cpp \
+    qwt_plot_zoomer.cpp \
+    qwt_plot_rescaler.cpp \
+    qwt_plot_intervalcurve.cpp \
+    qwt_plot_item.cpp \
+    qwt_text.cpp \
+    qwt_date.cpp \
+    qwt_plot_spectrogram.cpp \
+    qwt_counter.cpp \
+    qwt_plot_axis.cpp \
+    qwt_plot_histogram.cpp \
+    qwt_plot_tradingcurve.cpp \
+    qwt_abstract_slider.cpp \
+    qwt_plot_multi_barchart.cpp \
+    qwt_point_mapper.cpp \
+    qwt_dial.cpp \
+    qwt_plot_legenditem.cpp \
+    qwt_knob.cpp \
+    qwt_legend.cpp \
+    qwt_scale_draw.cpp \
+    qwt_scale_widget.cpp \
+    qwt_thermo.cpp \
+    qwt_slider.cpp \
+    qwt_graphic.cpp \
+    qwt_plot_rasteritem.cpp \
+    qwt_plot_renderer.cpp \
+    qwt_scale_engine.cpp \
+    qwt_plot_canvas.cpp \
+    qwt_wheel.cpp \
+    qwt_plot_curve.cpp \
+    qwt_plot.cpp \
+    qwt_date_scale_engine.cpp \
+    qwt_painter.cpp \
+    qwt_picker.cpp \
+    qwt_plot_layout.cpp \
+    qwt_symbol.cpp
+
+HEADERS += \
+    qwt.h \
+    qwt_compat.h \
+    qwt_clipper.h \
+    qwt_global.h \
+    qwt_sampling_thread.h \
+    qwt_system_clock.h \
+    qwt_plot_magnifier.h \
+    qwt_arrow_button.h \
+    qwt_plot_panner.h \
+    qwt_plot_svgitem.h \
+    qwt_plot_dict.h \
+    qwt_plot_zoneitem.h \
+    qwt_plot_textlabel.h \
+    qwt_date_scale_draw.h \
+    qwt_text_label.h \
+    qwt_round_scale_draw.h \
+    qwt_legend_label.h \
+    qwt_plot_seriesitem.h \
+    qwt_matrix_raster_data.h \
+    qwt_abstract_legend.h \
+    qwt_compass.h \
+    qwt_plot_spectrocurve.h \
+    qwt_analog_clock.h \
+    qwt_legend_data.h \
+    qwt_compass_rose.h \
+    qwt_interval_symbol.h \
+    qwt_date_scale_engine.h \
+    qwt_pixel_matrix.h \
+    qwt_dyngrid_layout.h \
+    qwt_magnifier.h \
+    qwt_spline.h \
+    qwt_plot_grid.h \
+    qwt_plot_abstract_barchart.h \
+    qwt_plot_scaleitem.h \
+    qwt_raster_data.h \
+    qwt_point_mapper.h \
+    qwt_scale_div.h \
+    qwt_plot_picker.h \
+    qwt_abstract_scale.h \
+    qwt_panner.h \
+    qwt_plot_directpainter.h \
+    qwt_scale_draw.h \
+    qwt_plot_layout.h \
+    qwt_plot_shapeitem.h \
+    qwt_math.h \
+    qwt_null_paintdevice.h \
+    qwt_plot_marker.h \
+    qwt_legend.h \
+    qwt_slider.h \
+    qwt_transform.h \
+    qwt_scale_widget.h \
+    qwt_date.h \
+    qwt_plot_barchart.h \
+    qwt_point_data.h \
+    qwt_plot_spectrogram.h \
+    qwt_abstract_scale_draw.h \
+    qwt_plot_legenditem.h \
+    qwt_curve_fitter.h \
+    qwt_scale_map.h \
+    qwt_painter_command.h \
+    qwt_column_symbol.h \
+    qwt_plot_rescaler.h \
+    qwt_point_3d.h \
+    qwt_plot_intervalcurve.h \
+    qwt_plot_multi_barchart.h \
+    qwt_widget_overlay.h \
+    qwt_dial_needle.h \
+    qwt_plot_zoomer.h \
+    qwt_plot_histogram.h \
+    qwt_counter.h \
+    qwt_abstract_slider.h \
+    qwt_plot_renderer.h \
+    qwt_point_polar.h \
+    qwt_thermo.h \
+    qwt_knob.h \
+    qwt_plot_rasteritem.h \
+    qwt_series_store.h \
+    qwt_text_engine.h \
+    qwt_wheel.h \
+    qwt_plot_canvas.h \
+    qwt_samples.h \
+    qwt_dial.h \
+    qwt_color_map.h \
+    qwt_plot_tradingcurve.h \
+    qwt_graphic.h \
+    qwt_picker_machine.h \
+    qwt_event_pattern.h \
+    qwt_painter.h \
+    qwt_text.h \
+    qwt_symbol.h \
+    qwt_scale_engine.h \
+    qwt_interval.h \
+    qwt_plot_item.h \
+    qwt_plot.h \
+    qwt_series_data.h \
+    qwt_picker.h \
+    qwt_plot_curve.h
+unix:!symbian {
+    maemo5 {
+        target.path = /opt/usr/lib
+    } else {
+        target.path = /usr/lib
+    }
+    INSTALLS += target
+}
diff --git a/qwt/qwt_abstract_legend.cpp b/qwt/qwt_abstract_legend.cpp
new file mode 100644
index 0000000..4ecaac6
--- /dev/null
+++ b/qwt/qwt_abstract_legend.cpp
@@ -0,0 +1,38 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_abstract_legend.h"
+
+/*!
+  Constructor
+
+  \param parent Parent widget
+*/
+QwtAbstractLegend::QwtAbstractLegend( QWidget *parent ):
+    QFrame( parent )
+{
+}
+
+//! Destructor
+QwtAbstractLegend::~QwtAbstractLegend()
+{
+}
+
+/*!
+   Return the extent, that is needed for elements to scroll
+   the legend ( usually scrollbars ),
+
+   \param orientation Orientation
+   \return Extent of the corresponding scroll element
+*/
+int QwtAbstractLegend::scrollExtent( Qt::Orientation orientation ) const
+{
+    Q_UNUSED( orientation );
+    return 0;
+}
diff --git a/qwt/qwt_abstract_legend.h b/qwt/qwt_abstract_legend.h
new file mode 100644
index 0000000..18bd3f4
--- /dev/null
+++ b/qwt/qwt_abstract_legend.h
@@ -0,0 +1,71 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_LEGEND_H
+#define QWT_ABSTRACT_LEGEND_H
+
+#include "qwt_global.h"
+#include "qwt_legend_data.h"
+#include <qframe.h>
+#include <qlist.h>
+
+class QVariant;
+
+/*!
+  \brief Abstract base class for legend widgets
+
+  Legends, that need to be under control of the QwtPlot layout system
+  need to be derived from QwtAbstractLegend. 
+
+  \note Other type of legends can be implemented by connecting to
+        the QwtPlot::legendDataChanged() signal. But as these legends
+        are unknown to the plot layout system the layout code
+        ( on screen and for QwtPlotRenderer ) need to be organized
+        in application code.
+
+  \sa QwtLegend
+ */
+class QWT_EXPORT QwtAbstractLegend : public QFrame
+{
+    Q_OBJECT
+
+public:
+    explicit QwtAbstractLegend( QWidget *parent = NULL );
+    virtual ~QwtAbstractLegend();
+
+    /*!
+      Render the legend into a given rectangle.
+
+      \param painter Painter
+      \param rect Bounding rectangle
+      \param fillBackground When true, fill rect with the widget background 
+
+      \sa renderLegend() is used by QwtPlotRenderer
+    */
+    virtual void renderLegend( QPainter *painter, 
+        const QRectF &rect, bool fillBackground ) const = 0;
+
+    //! \return True, when no plot item is inserted
+    virtual bool isEmpty() const = 0;
+
+    virtual int scrollExtent( Qt::Orientation ) const;
+
+public Q_SLOTS:
+
+    /*!
+      \brief Update the entries for a plot item
+
+      \param itemInfo Info about an item
+      \param data List of legend entry attributes for the  item
+     */
+    virtual void updateLegend( const QVariant &itemInfo, 
+        const QList<QwtLegendData> &data ) = 0;
+};
+
+#endif 
diff --git a/qwt/qwt_abstract_scale.cpp b/qwt/qwt_abstract_scale.cpp
new file mode 100644
index 0000000..3018b85
--- /dev/null
+++ b/qwt/qwt_abstract_scale.cpp
@@ -0,0 +1,449 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_abstract_scale.h"
+#include "qwt_scale_engine.h"
+#include "qwt_scale_draw.h"
+#include "qwt_scale_div.h"
+#include "qwt_scale_map.h"
+#include "qwt_interval.h"
+
+class QwtAbstractScale::PrivateData
+{
+public:
+    PrivateData():
+        maxMajor( 5 ),
+        maxMinor( 3 ),
+        stepSize( 0.0 )
+    {
+        scaleEngine = new QwtLinearScaleEngine();
+        scaleDraw = new QwtScaleDraw();
+    }
+
+    ~PrivateData()
+    {
+        delete scaleEngine;
+        delete scaleDraw;
+    }
+
+    QwtScaleEngine *scaleEngine;
+    QwtAbstractScaleDraw *scaleDraw;
+
+    int maxMajor;
+    int maxMinor;
+    double stepSize;
+};
+
+/*!
+  Constructor
+
+  \param parent Parent widget
+
+  Creates a default QwtScaleDraw and a QwtLinearScaleEngine.
+  The initial scale boundaries are set to [ 0.0, 100.0 ]
+
+  The scaleStepSize() is initialized to 0.0, scaleMaxMajor() to 5
+  and scaleMaxMajor to 3.
+*/
+
+QwtAbstractScale::QwtAbstractScale( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData;
+    rescale( 0.0, 100.0, d_data->stepSize );
+}
+
+//! Destructor
+QwtAbstractScale::~QwtAbstractScale()
+{
+    delete d_data;
+}
+
+/*!
+  Set the lower bound of the scale
+
+  \param value Lower bound
+
+  \sa lowerBound(), setScale(), setUpperBound()
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setLowerBound( double value )
+{
+    setScale( value, upperBound() );
+}
+
+/*!
+  \return Lower bound of the scale
+  \sa setLowerBound(), setScale(), upperBound()
+*/
+double QwtAbstractScale::lowerBound() const
+{
+    return d_data->scaleDraw->scaleDiv().lowerBound();
+}
+
+/*!
+  Set the upper bound of the scale
+
+  \param value Upper bound
+
+  \sa upperBound(), setScale(), setLowerBound()
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setUpperBound( double value )
+{
+    setScale( lowerBound(), value );
+}
+
+/*!
+  \return Upper bound of the scale
+  \sa setUpperBound(), setScale(), lowerBound()
+*/
+double QwtAbstractScale::upperBound() const
+{
+    return d_data->scaleDraw->scaleDiv().upperBound();
+}
+
+/*!
+  \brief Specify a scale.
+
+  Define a scale by an interval 
+
+  The ticks are calculated using scaleMaxMinor(), 
+  scaleMaxMajor() and scaleStepSize().
+
+  \param lowerBound lower limit of the scale interval
+  \param upperBound upper limit of the scale interval
+
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setScale( double lowerBound, double upperBound )
+{
+    rescale( lowerBound, upperBound, d_data->stepSize );
+}
+
+/*!
+  \brief Specify a scale.
+
+  Define a scale by an interval
+
+  The ticks are calculated using scaleMaxMinor(), 
+  scaleMaxMajor() and scaleStepSize().
+
+  \param interval Interval
+*/
+void QwtAbstractScale::setScale( const QwtInterval &interval )
+{
+    setScale( interval.minValue(), interval.maxValue() );
+}
+
+/*!
+  \brief Specify a scale.
+
+  scaleMaxMinor(), scaleMaxMajor() and scaleStepSize() and have no effect.
+
+  \param scaleDiv Scale division
+  \sa setAutoScale()
+*/
+void QwtAbstractScale::setScale( const QwtScaleDiv &scaleDiv )
+{
+    if ( scaleDiv != d_data->scaleDraw->scaleDiv() )
+    {
+#if 1
+        if ( d_data->scaleEngine )
+        {
+            d_data->scaleDraw->setTransformation(
+                d_data->scaleEngine->transformation() );
+        }
+#endif
+
+        d_data->scaleDraw->setScaleDiv( scaleDiv );
+
+        scaleChange();
+    }
+}
+
+/*!
+  \brief Set the maximum number of major tick intervals.
+
+  The scale's major ticks are calculated automatically such that
+  the number of major intervals does not exceed ticks.
+
+  The default value is 5.
+
+  \param ticks Maximal number of major ticks.
+
+  \sa scaleMaxMajor(), setScaleMaxMinor(),
+      setScaleStepSize(), QwtScaleEngine::divideInterval()
+*/
+void QwtAbstractScale::setScaleMaxMajor( int ticks )
+{
+    if ( ticks != d_data->maxMajor )
+    {
+        d_data->maxMajor = ticks;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Maximal number of major tick intervals
+  \sa setScaleMaxMajor(), scaleMaxMinor()
+*/
+int QwtAbstractScale::scaleMaxMajor() const
+{
+    return d_data->maxMajor;
+}
+
+/*!
+  \brief Set the maximum number of minor tick intervals
+
+  The scale's minor ticks are calculated automatically such that
+  the number of minor intervals does not exceed ticks.
+  The default value is 3.
+
+  \param ticks Maximal number of minor ticks.
+
+  \sa scaleMaxMajor(), setScaleMaxMinor(),
+      setScaleStepSize(), QwtScaleEngine::divideInterval()
+*/
+void QwtAbstractScale::setScaleMaxMinor( int ticks )
+{
+    if ( ticks != d_data->maxMinor )
+    {
+        d_data->maxMinor = ticks;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Maximal number of minor tick intervals
+  \sa setScaleMaxMinor(), scaleMaxMajor()
+*/
+int QwtAbstractScale::scaleMaxMinor() const
+{
+    return d_data->maxMinor;
+}
+
+/*!
+   \brief Set the step size used for calculating a scale division
+
+   The step size is hint for calculating the intervals for
+   the major ticks of the scale. A value of 0.0 is interpreted
+   as no hint.
+
+   \param stepSize Hint for the step size of the scale
+
+   \sa scaleStepSize(), QwtScaleEngine::divideScale()
+
+   \note Position and distance between the major ticks also
+         depends on scaleMaxMajor().
+*/
+void QwtAbstractScale::setScaleStepSize( double stepSize )
+{
+    if ( stepSize != d_data->stepSize )
+    {
+        d_data->stepSize = stepSize;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Hint for the step size of the scale
+  \sa setScaleStepSize(), QwtScaleEngine::divideScale()
+*/
+double QwtAbstractScale::scaleStepSize() const
+{
+    return d_data->stepSize;
+}
+
+/*!
+  \brief Set a scale draw
+
+  scaleDraw has to be created with new and will be deleted in
+  the destructor or the next call of setAbstractScaleDraw().
+
+  \sa abstractScaleDraw()
+*/
+void QwtAbstractScale::setAbstractScaleDraw( QwtAbstractScaleDraw *scaleDraw )
+{
+    if ( scaleDraw == NULL || scaleDraw == d_data->scaleDraw )
+        return;
+
+    if ( d_data->scaleDraw != NULL )
+        scaleDraw->setScaleDiv( d_data->scaleDraw->scaleDiv() );
+
+    delete d_data->scaleDraw;
+    d_data->scaleDraw = scaleDraw;
+}
+
+/*!
+    \return Scale draw
+    \sa setAbstractScaleDraw()
+*/
+QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return Scale draw
+    \sa setAbstractScaleDraw()
+*/
+const QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+  \brief Set a scale engine
+
+  The scale engine is responsible for calculating the scale division
+  and provides a transformation between scale and widget coordinates.
+
+  scaleEngine has to be created with new and will be deleted in
+  the destructor or the next call of setScaleEngine.
+*/
+void QwtAbstractScale::setScaleEngine( QwtScaleEngine *scaleEngine )
+{
+    if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine )
+    {
+        delete d_data->scaleEngine;
+        d_data->scaleEngine = scaleEngine;
+    }
+}
+
+/*!
+  \return Scale engine
+  \sa setScaleEngine()
+*/
+const QwtScaleEngine *QwtAbstractScale::scaleEngine() const
+{
+    return d_data->scaleEngine;
+}
+
+/*!
+  \return Scale engine
+  \sa setScaleEngine()
+*/
+QwtScaleEngine *QwtAbstractScale::scaleEngine()
+{
+    return d_data->scaleEngine;
+}
+
+/*!
+  \return Scale boundaries and positions of the ticks
+
+  The scale division might have been assigned explicitly
+  or calculated implicitly by rescale(). 
+ */
+const QwtScaleDiv &QwtAbstractScale::scaleDiv() const
+{
+    return d_data->scaleDraw->scaleDiv();
+}
+
+/*!
+  \return Map to translate between scale and widget coordinates
+ */
+const QwtScaleMap &QwtAbstractScale::scaleMap() const
+{
+    return d_data->scaleDraw->scaleMap();
+}
+
+/*!
+  Translate a scale value into a widget coordinate
+
+  \param value Scale value 
+  \return Corresponding widget coordinate for value
+  \sa scaleMap(), invTransform()
+ */
+int QwtAbstractScale::transform( double value ) const
+{
+    return qRound( d_data->scaleDraw->scaleMap().transform( value ) );
+}
+
+/*!
+  Translate a widget coordinate into a scale value
+
+  \param value Widget coordinate
+  \return Corresponding scale coordinate for value
+  \sa scaleMap(), transform()
+ */
+double QwtAbstractScale::invTransform( int value ) const
+{
+    return d_data->scaleDraw->scaleMap().invTransform( value );
+}
+
+/*!
+  \return True, when the scale is increasing in opposite direction
+          to the widget coordinates
+ */
+bool QwtAbstractScale::isInverted() const
+{
+    return d_data->scaleDraw->scaleMap().isInverting();
+}
+
+/*!
+  \return The boundary with the smaller value
+  \sa maximum(), lowerBound(), upperBound()
+ */
+double QwtAbstractScale::minimum() const
+{
+    return qMin( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound() );
+}
+
+/*!
+  \return The boundary with the larger value
+  \sa minimum(), lowerBound(), upperBound()
+ */
+double QwtAbstractScale::maximum() const
+{
+    return qMax( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound() );
+}
+
+//! Notify changed scale
+void QwtAbstractScale::scaleChange()
+{
+}
+
+/*!
+  Recalculate the scale division and update the scale.
+
+  \param lowerBound Lower limit of the scale interval
+  \param upperBound Upper limit of the scale interval
+  \param stepSize Major step size
+
+  \sa scaleChange()
+*/
+void QwtAbstractScale::rescale( 
+    double lowerBound, double upperBound, double stepSize )
+{
+    const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale(
+        lowerBound, upperBound, d_data->maxMajor, d_data->maxMinor, stepSize );
+
+    if ( scaleDiv != d_data->scaleDraw->scaleDiv() )
+    {
+#if 1
+        d_data->scaleDraw->setTransformation(
+            d_data->scaleEngine->transformation() );
+#endif
+
+        d_data->scaleDraw->setScaleDiv( scaleDiv );
+        scaleChange();
+    }
+}
+
+void QwtAbstractScale::updateScaleDraw()
+{
+    rescale( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound(), d_data->stepSize );
+}
diff --git a/qwt/qwt_abstract_scale.h b/qwt/qwt_abstract_scale.h
new file mode 100644
index 0000000..4ed6616
--- /dev/null
+++ b/qwt/qwt_abstract_scale.h
@@ -0,0 +1,105 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SCALE_H
+#define QWT_ABSTRACT_SCALE_H
+
+#include "qwt_global.h"
+#include <qwidget.h>
+
+class QwtScaleEngine;
+class QwtAbstractScaleDraw;
+class QwtScaleDiv;
+class QwtScaleMap;
+class QwtInterval;
+
+/*!
+  \brief An abstract base class for widgets having a scale
+
+  The scale of an QwtAbstractScale is determined by a QwtScaleDiv
+  definition, that contains the boundaries and the ticks of the scale.
+  The scale is painted using a QwtScaleDraw object.
+
+  The scale division might be assigned explicitly - but usually
+  it is calculated from the boundaries using a QwtScaleEngine. 
+
+  The scale engine also decides the type of transformation of the scale 
+  ( linear, logarithmic ... ).
+*/
+
+class QWT_EXPORT QwtAbstractScale: public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double lowerBound READ lowerBound WRITE setLowerBound )
+    Q_PROPERTY( double upperBound READ upperBound WRITE setUpperBound )
+
+    Q_PROPERTY( int scaleMaxMajor READ scaleMaxMajor WRITE setScaleMaxMajor )
+    Q_PROPERTY( int scaleMaxMinor READ scaleMaxMinor WRITE setScaleMaxMinor )
+
+    Q_PROPERTY( double scaleStepSize READ scaleStepSize WRITE setScaleStepSize )
+
+public:
+    QwtAbstractScale( QWidget *parent = NULL );
+    virtual ~QwtAbstractScale();
+
+    void setScale( double lowerBound, double upperBound );
+    void setScale( const QwtInterval & );
+    void setScale( const QwtScaleDiv & );
+
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setLowerBound( double value );
+    double lowerBound() const;
+
+    void setUpperBound( double value );
+    double upperBound() const;
+
+    void setScaleStepSize( double stepSize );
+    double scaleStepSize() const;
+
+    void setScaleMaxMajor( int ticks );
+    int scaleMaxMinor() const;
+
+    void setScaleMaxMinor( int ticks );
+    int scaleMaxMajor() const;
+
+    void setScaleEngine( QwtScaleEngine * );
+    const QwtScaleEngine *scaleEngine() const;
+    QwtScaleEngine *scaleEngine();
+
+    int transform( double ) const;
+    double invTransform( int ) const;
+
+    bool isInverted() const;
+
+    double minimum() const;
+    double maximum() const;
+
+    const QwtScaleMap &scaleMap() const;
+
+protected:
+    void rescale( double lowerBound, 
+        double upperBound, double stepSize );
+
+    void setAbstractScaleDraw( QwtAbstractScaleDraw * );
+
+    const QwtAbstractScaleDraw *abstractScaleDraw() const;
+    QwtAbstractScaleDraw *abstractScaleDraw();
+
+    virtual void scaleChange();
+
+private:
+    void updateScaleDraw();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_abstract_scale_draw.cpp b/qwt/qwt_abstract_scale_draw.cpp
new file mode 100644
index 0000000..30b3ed9
--- /dev/null
+++ b/qwt/qwt_abstract_scale_draw.cpp
@@ -0,0 +1,419 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_abstract_scale_draw.h"
+#include "qwt_math.h"
+#include "qwt_text.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qmap.h>
+#include <qlocale.h>
+
+class QwtAbstractScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        spacing( 4.0 ),
+        penWidth( 0 ),
+        minExtent( 0.0 )
+    {
+        components = QwtAbstractScaleDraw::Backbone 
+            | QwtAbstractScaleDraw::Ticks 
+            | QwtAbstractScaleDraw::Labels;
+
+        tickLength[QwtScaleDiv::MinorTick] = 4.0;
+        tickLength[QwtScaleDiv::MediumTick] = 6.0;
+        tickLength[QwtScaleDiv::MajorTick] = 8.0;
+    }
+
+    ScaleComponents components;
+
+    QwtScaleMap map;
+    QwtScaleDiv scaleDiv;
+
+    double spacing;
+    double tickLength[QwtScaleDiv::NTickTypes];
+    int penWidth;
+
+    double minExtent;
+
+    QMap<double, QwtText> labelCache;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The spacing (distance between ticks and labels) is
+  set to 4, the tick lengths are set to 4,6 and 8 pixels
+*/
+QwtAbstractScaleDraw::QwtAbstractScaleDraw()
+{
+    d_data = new QwtAbstractScaleDraw::PrivateData;
+}
+
+//! Destructor
+QwtAbstractScaleDraw::~QwtAbstractScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  En/Disable a component of the scale
+
+  \param component Scale component
+  \param enable On/Off
+
+  \sa hasComponent()
+*/
+void QwtAbstractScaleDraw::enableComponent(
+    ScaleComponent component, bool enable )
+{
+    if ( enable )
+        d_data->components |= component;
+    else
+        d_data->components &= ~component;
+}
+
+/*!
+  Check if a component is enabled
+
+  \param component Component type
+  \return true, when component is enabled
+  \sa enableComponent()
+*/
+bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const
+{
+    return ( d_data->components & component );
+}
+
+/*!
+  Change the scale division
+  \param scaleDiv New scale division
+*/
+void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv )
+{
+    d_data->scaleDiv = scaleDiv;
+    d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() );
+    d_data->labelCache.clear();
+}
+
+/*!
+  Change the transformation of the scale
+  \param transformation New scale transformation
+*/
+void QwtAbstractScaleDraw::setTransformation(
+    QwtTransform *transformation )
+{
+    d_data->map.setTransformation( transformation );
+}
+
+//! \return Map how to translate between scale and pixel values
+const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const
+{
+    return d_data->map;
+}
+
+//! \return Map how to translate between scale and pixel values
+QwtScaleMap &QwtAbstractScaleDraw::scaleMap()
+{
+    return d_data->map;
+}
+
+//! \return scale division
+const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const
+{
+    return d_data->scaleDiv;
+}
+
+/*!
+  \brief Specify the width of the scale pen
+  \param width Pen width
+  \sa penWidth()
+*/
+void QwtAbstractScaleDraw::setPenWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    if ( width != d_data->penWidth )
+        d_data->penWidth = width;
+}
+
+/*!
+    \return Scale pen width
+    \sa setPenWidth()
+*/
+int QwtAbstractScaleDraw::penWidth() const
+{
+    return d_data->penWidth;
+}
+
+/*!
+  \brief Draw the scale
+
+  \param painter    The painter
+
+  \param palette    Palette, text color is used for the labels,
+                    foreground color for ticks and backbone
+*/
+void QwtAbstractScaleDraw::draw( QPainter *painter,
+    const QPalette& palette ) const
+{
+    painter->save();
+
+    QPen pen = painter->pen();
+    pen.setWidth( d_data->penWidth );
+    pen.setCosmetic( false );
+    painter->setPen( pen );
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        painter->save();
+        painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style
+
+        const QList<double> &majorTicks =
+            d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick );
+
+        for ( int i = 0; i < majorTicks.count(); i++ )
+        {
+            const double v = majorTicks[i];
+            if ( d_data->scaleDiv.contains( v ) )
+                drawLabel( painter, v );
+        }
+
+        painter->restore();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        painter->save();
+
+        QPen pen = painter->pen();
+        pen.setColor( palette.color( QPalette::WindowText ) );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter->setPen( pen );
+
+        for ( int tickType = QwtScaleDiv::MinorTick;
+            tickType < QwtScaleDiv::NTickTypes; tickType++ )
+        {
+            const QList<double> &ticks = d_data->scaleDiv.ticks( tickType );
+            for ( int i = 0; i < ticks.count(); i++ )
+            {
+                const double v = ticks[i];
+                if ( d_data->scaleDiv.contains( v ) )
+                    drawTick( painter, v, d_data->tickLength[tickType] );
+            }
+        }
+
+        painter->restore();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        painter->save();
+
+        QPen pen = painter->pen();
+        pen.setColor( palette.color( QPalette::WindowText ) );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter->setPen( pen );
+
+        drawBackbone( painter );
+
+        painter->restore();
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Set the spacing between tick and labels
+
+  The spacing is the distance between ticks and labels.
+  The default spacing is 4 pixels.
+
+  \param spacing Spacing
+
+  \sa spacing()
+*/
+void QwtAbstractScaleDraw::setSpacing( double spacing )
+{
+    if ( spacing < 0 )
+        spacing = 0;
+
+    d_data->spacing = spacing;
+}
+
+/*!
+  \brief Get the spacing
+
+  The spacing is the distance between ticks and labels.
+  The default spacing is 4 pixels.
+
+  \return Spacing
+  \sa setSpacing()
+*/
+double QwtAbstractScaleDraw::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Set a minimum for the extent
+
+  The extent is calculated from the components of the
+  scale draw. In situations, where the labels are
+  changing and the layout depends on the extent (f.e scrolling
+  a scale), setting an upper limit as minimum extent will
+  avoid jumps of the layout.
+
+  \param minExtent Minimum extent
+
+  \sa extent(), minimumExtent()
+*/
+void QwtAbstractScaleDraw::setMinimumExtent( double minExtent )
+{
+    if ( minExtent < 0.0 )
+        minExtent = 0.0;
+
+    d_data->minExtent = minExtent;
+}
+
+/*!
+  Get the minimum extent
+  \return Minimum extent
+  \sa extent(), setMinimumExtent()
+*/
+double QwtAbstractScaleDraw::minimumExtent() const
+{
+    return d_data->minExtent;
+}
+
+/*!
+  Set the length of the ticks
+
+  \param tickType Tick type
+  \param length New length
+
+  \warning the length is limited to [0..1000]
+*/
+void QwtAbstractScaleDraw::setTickLength(
+    QwtScaleDiv::TickType tickType, double length )
+{
+    if ( tickType < QwtScaleDiv::MinorTick ||
+        tickType > QwtScaleDiv::MajorTick )
+    {
+        return;
+    }
+
+    if ( length < 0.0 )
+        length = 0.0;
+
+    const double maxTickLen = 1000.0;
+    if ( length > maxTickLen )
+        length = maxTickLen;
+
+    d_data->tickLength[tickType] = length;
+}
+
+/*!
+    \return Length of the ticks
+    \sa setTickLength(), maxTickLength()
+*/
+double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const
+{
+    if ( tickType < QwtScaleDiv::MinorTick ||
+        tickType > QwtScaleDiv::MajorTick )
+    {
+        return 0;
+    }
+
+    return d_data->tickLength[tickType];
+}
+
+/*!
+   \return Length of the longest tick
+
+   Useful for layout calculations
+   \sa tickLength(), setTickLength()
+*/
+double QwtAbstractScaleDraw::maxTickLength() const
+{
+    double length = 0.0;
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+        length = qMax( length, d_data->tickLength[i] );
+
+    return length;
+}
+
+/*!
+  \brief Convert a value into its representing label
+
+  The value is converted to a plain text using
+  QLocale().toString(value).
+  This method is often overloaded by applications to have individual
+  labels.
+
+  \param value Value
+  \return Label string.
+*/
+QwtText QwtAbstractScaleDraw::label( double value ) const
+{
+    if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        value = 0.0; 
+
+    return QLocale().toString( value );
+}
+
+/*!
+   \brief Convert a value into its representing label and cache it.
+
+   The conversion between value and label is called very often
+   in the layout and painting code. Unfortunately the
+   calculation of the label sizes might be slow (really slow
+   for rich text in Qt4), so it's necessary to cache the labels.
+
+   \param font Font
+   \param value Value
+
+   \return Tick label
+*/
+const QwtText &QwtAbstractScaleDraw::tickLabel(
+    const QFont &font, double value ) const
+{
+    QMap<double, QwtText>::const_iterator it = d_data->labelCache.find( value );
+    if ( it == d_data->labelCache.end() )
+    {
+        QwtText lbl = label( value );
+        lbl.setRenderFlags( 0 );
+        lbl.setLayoutAttribute( QwtText::MinimumLayout );
+
+        ( void )lbl.textSize( font ); // initialize the internal cache
+
+        it = d_data->labelCache.insert( value, lbl );
+    }
+
+    return ( *it );
+}
+
+/*!
+   Invalidate the cache used by tickLabel()
+
+   The cache is invalidated, when a new QwtScaleDiv is set. If
+   the labels need to be changed. while the same QwtScaleDiv is set,
+   invalidateCache() needs to be called manually.
+*/
+void QwtAbstractScaleDraw::invalidateCache()
+{
+    d_data->labelCache.clear();
+}
diff --git a/qwt/qwt_abstract_scale_draw.h b/qwt/qwt_abstract_scale_draw.h
new file mode 100644
index 0000000..d0f1ec3
--- /dev/null
+++ b/qwt/qwt_abstract_scale_draw.h
@@ -0,0 +1,141 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SCALE_DRAW_H
+#define QWT_ABSTRACT_SCALE_DRAW_H
+
+#include "qwt_global.h"
+#include "qwt_scale_div.h"
+#include "qwt_text.h"
+
+class QPalette;
+class QPainter;
+class QFont;
+class QwtTransform;
+class QwtScaleMap;
+
+/*!
+  \brief A abstract base class for drawing scales
+
+  QwtAbstractScaleDraw can be used to draw linear or logarithmic scales.
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using setScaleDiv(), the scale can be drawn with the draw() member.
+*/
+class QWT_EXPORT QwtAbstractScaleDraw
+{
+public:
+
+    /*!
+       Components of a scale
+       \sa enableComponent(), hasComponent
+    */
+    enum ScaleComponent
+    {
+        //! Backbone = the line where the ticks are located
+        Backbone = 0x01,
+
+        //! Ticks
+        Ticks = 0x02,
+
+        //! Labels
+        Labels = 0x04
+    };
+
+    //! Scale components
+    typedef QFlags<ScaleComponent> ScaleComponents;
+
+    QwtAbstractScaleDraw();
+    virtual ~QwtAbstractScaleDraw();
+
+    void setScaleDiv( const QwtScaleDiv &s );
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setTransformation( QwtTransform * );
+    const QwtScaleMap &scaleMap() const;
+    QwtScaleMap &scaleMap();
+
+    void enableComponent( ScaleComponent, bool enable = true );
+    bool hasComponent( ScaleComponent ) const;
+
+    void setTickLength( QwtScaleDiv::TickType, double length );
+    double tickLength( QwtScaleDiv::TickType ) const;
+    double maxTickLength() const;
+
+    void setSpacing( double margin );
+    double spacing() const;
+
+    void setPenWidth( int width );
+    int penWidth() const;
+
+    virtual void draw( QPainter *, const QPalette & ) const;
+
+    virtual QwtText label( double ) const;
+
+    /*!
+      Calculate the extent
+
+      The extent is the distance from the baseline to the outermost
+      pixel of the scale draw in opposite to its orientation.
+      It is at least minimumExtent() pixels.
+
+      \param font Font used for drawing the tick labels
+      \return Number of pixels
+
+      \sa setMinimumExtent(), minimumExtent()
+    */
+    virtual double extent( const QFont &font ) const = 0;
+
+    void setMinimumExtent( double );
+    double minimumExtent() const;
+
+protected:
+    /*!
+       Draw a tick
+
+       \param painter Painter
+       \param value Value of the tick
+       \param len Length of the tick
+
+       \sa drawBackbone(), drawLabel()
+    */
+    virtual void drawTick( QPainter *painter, double value, double len ) const = 0;
+
+    /*!
+      Draws the baseline of the scale
+      \param painter Painter
+
+      \sa drawTick(), drawLabel()
+    */
+    virtual void drawBackbone( QPainter *painter ) const = 0;
+
+    /*!
+        Draws the label for a major scale tick
+
+        \param painter Painter
+        \param value Value
+
+        \sa drawTick(), drawBackbone()
+    */
+    virtual void drawLabel( QPainter *painter, double value ) const = 0;
+
+    void invalidateCache();
+    const QwtText &tickLabel( const QFont &, double value ) const;
+
+private:
+    QwtAbstractScaleDraw( const QwtAbstractScaleDraw & );
+    QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents )
+
+#endif
diff --git a/qwt/qwt_abstract_slider.cpp b/qwt/qwt_abstract_slider.cpp
new file mode 100644
index 0000000..ea613bd
--- /dev/null
+++ b/qwt/qwt_abstract_slider.cpp
@@ -0,0 +1,820 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_abstract_slider.h"
+#include "qwt_abstract_scale_draw.h"
+#include "qwt_math.h"
+#include "qwt_scale_map.h"
+#include <qevent.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#endif
+
+static double qwtAlignToScaleDiv( 
+    const QwtAbstractSlider *slider, double value )
+{
+    const QwtScaleDiv &sd = slider->scaleDiv();
+
+    const int tValue = slider->transform( value );
+
+    if ( tValue == slider->transform( sd.lowerBound() ) )
+        return sd.lowerBound();
+
+    if ( tValue == slider->transform( sd.lowerBound() ) )
+        return sd.upperBound();
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+    {
+        const QList<double> ticks = sd.ticks( i );
+        for ( int j = 0; j < ticks.size(); j++ )
+        {
+            if ( slider->transform( ticks[ j ] ) == tValue )
+                return ticks[ j ];
+        }
+    }
+
+    return value;
+}
+
+class QwtAbstractSlider::PrivateData
+{
+public:
+    PrivateData():
+        isScrolling( false ),
+        isTracking( true ),
+        pendingValueChanged( false ),
+        readOnly( false ),
+        totalSteps( 100 ),
+        singleSteps( 1 ),
+        pageSteps( 10 ),
+        stepAlignment( true ),
+        isValid( false ),
+        value( 0.0 ),
+        wrapping( false ),
+        invertedControls( false )
+    {
+    }
+
+    bool isScrolling;
+    bool isTracking;
+    bool pendingValueChanged;
+
+    bool readOnly;
+
+    uint totalSteps;
+    uint singleSteps;
+    uint pageSteps;
+    bool stepAlignment;
+
+    bool isValid;
+    double value;
+
+    bool wrapping;
+    bool invertedControls;
+};
+
+/*!
+  \brief Constructor
+
+  The scale is initialized to [0.0, 100.0], the
+  number of steps is set to 100 with 1 and 10 and single
+  an page step sizes. Step alignment is enabled.
+
+  The initial value is invalid.
+
+  \param parent Parent widget
+*/
+QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ):
+    QwtAbstractScale( parent )
+{
+    d_data = new QwtAbstractSlider::PrivateData;
+
+    setScale( 0.0, 100.0 );
+    setFocusPolicy( Qt::StrongFocus );
+}
+
+//! Destructor
+QwtAbstractSlider::~QwtAbstractSlider()
+{
+    delete d_data;
+}
+
+/*! 
+  Set the value to be valid/invalid
+
+  \param on When true, the value is invalidated
+
+  \sa setValue()
+*/
+void QwtAbstractSlider::setValid( bool on )
+{
+    if ( on != d_data->isValid )
+    {
+        d_data->isValid = on;
+        sliderChange();
+
+        Q_EMIT valueChanged( d_data->value );
+    }   
+}   
+
+//! \return True, when the value is invalid
+bool QwtAbstractSlider::isValid() const
+{
+    return d_data->isValid;
+}   
+
+/*!
+  En/Disable read only mode
+
+  In read only mode the slider can't be controlled by mouse
+  or keyboard.
+
+  \param on Enables in case of true
+  \sa isReadOnly()
+
+  \warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus
+*/
+void QwtAbstractSlider::setReadOnly( bool on )
+{
+    if ( d_data->readOnly != on )
+    {
+        d_data->readOnly = on;
+        setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus );
+
+        update();
+    }
+}
+
+/*!
+  In read only mode the slider can't be controlled by mouse
+  or keyboard.
+
+  \return true if read only
+  \sa setReadOnly()
+*/
+bool QwtAbstractSlider::isReadOnly() const
+{
+    return d_data->readOnly;
+}
+
+/*!
+  \brief Enables or disables tracking.
+
+  If tracking is enabled, the slider emits the valueChanged() 
+  signal while the movable part of the slider is being dragged. 
+  If tracking is disabled, the slider emits the valueChanged() signal 
+  only when the user releases the slider.
+
+  Tracking is enabled by default.
+  \param on \c true (enable) or \c false (disable) tracking.
+
+  \sa isTracking(), sliderMoved()
+*/
+void QwtAbstractSlider::setTracking( bool on )
+{
+    d_data->isTracking = on;
+}
+
+/*!
+  \return True, when tracking has been enabled
+  \sa setTracking()
+*/
+bool QwtAbstractSlider::isTracking() const
+{
+    return d_data->isTracking;
+}
+
+/*!
+   Mouse press event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mousePressEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || lowerBound() == upperBound() )
+        return;
+
+    d_data->isScrolling = isScrollPosition( event->pos() );
+
+    if ( d_data->isScrolling )
+    {
+        d_data->pendingValueChanged = false;
+
+        Q_EMIT sliderPressed();
+    }
+}
+
+/*!
+   Mouse Move Event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isValid && d_data->isScrolling )
+    {
+        double value = scrolledTo( event->pos() );
+        if ( value != d_data->value )
+        {
+            value = boundedValue( value );
+
+            if ( d_data->stepAlignment )
+            {
+                value = alignedValue( value );
+            }
+            else
+            {
+                value = qwtAlignToScaleDiv( this, value );
+            }
+
+            if ( value != d_data->value )
+            {
+                d_data->value = value;
+
+                sliderChange();
+
+                Q_EMIT sliderMoved( d_data->value );
+
+                if ( d_data->isTracking )
+                    Q_EMIT valueChanged( d_data->value );
+                else
+                    d_data->pendingValueChanged = true;
+            }
+        }
+    }
+}
+
+/*!
+   Mouse Release Event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isScrolling && d_data->isValid )
+    {
+        d_data->isScrolling = false;
+
+        if ( d_data->pendingValueChanged )
+            Q_EMIT valueChanged( d_data->value );
+
+        Q_EMIT sliderReleased();
+    }
+}
+
+/*!
+   Wheel Event handler
+
+   In/decreases the value by s number of steps. The direction 
+   depends on the invertedControls() property.
+
+   When the control or shift modifier is pressed the wheel delta
+   ( divided by 120 ) is mapped to an increment according to
+   pageSteps(). Otherwise it is mapped to singleSteps().
+
+   \param event Wheel event
+*/
+void QwtAbstractSlider::wheelEvent( QWheelEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || d_data->isScrolling )
+        return;
+
+    int numSteps = 0;
+
+    if ( ( event->modifiers() & Qt::ControlModifier) ||
+        ( event->modifiers() & Qt::ShiftModifier ) )
+    {
+        // one page regardless of delta
+        numSteps = d_data->pageSteps;
+        if ( event->delta() < 0 )
+            numSteps = -numSteps;
+    }
+    else
+    {
+        const int numTurns = ( event->delta() / 120 );
+        numSteps = numTurns * d_data->singleSteps;
+    }
+
+    if ( d_data->invertedControls )
+        numSteps = -numSteps;
+
+    const double value = incrementedValue( d_data->value, numSteps );
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+
+        Q_EMIT sliderMoved( d_data->value );
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  Handles key events
+
+  QwtAbstractSlider handles the following keys:
+
+  - Qt::Key_Left\n
+    Add/Subtract singleSteps() in direction to lowerBound();
+  - Qt::Key_Right\n
+    Add/Subtract singleSteps() in direction to upperBound();
+  - Qt::Key_Down\n
+    Subtract singleSteps(), when invertedControls() is false
+  - Qt::Key_Up\n
+    Add singleSteps(), when invertedControls() is false
+  - Qt::Key_PageDown\n
+    Subtract pageSteps(), when invertedControls() is false
+  - Qt::Key_PageUp\n
+    Add pageSteps(), when invertedControls() is false
+  - Qt::Key_Home\n
+    Set the value to the minimum()
+  - Qt::Key_End\n
+    Set the value to the maximum()
+
+  \param event Key event
+  \sa isReadOnly()
+*/
+void QwtAbstractSlider::keyPressEvent( QKeyEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || d_data->isScrolling )
+        return;
+
+    int numSteps = 0;
+    double value = d_data->value;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Left:
+        {
+            numSteps = -static_cast<int>( d_data->singleSteps );
+            if ( isInverted() )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_Right:
+        {
+            numSteps = d_data->singleSteps;
+            if ( isInverted() )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_Down:
+        {
+            numSteps = -static_cast<int>( d_data->singleSteps );
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            numSteps = d_data->singleSteps;
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_PageUp:
+        {
+            numSteps = d_data->pageSteps;
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_PageDown:
+        {
+            numSteps = -static_cast<int>( d_data->pageSteps );
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_Home:
+        {
+            value = minimum();
+            break;
+        }
+        case Qt::Key_End:
+        {
+            value = maximum();
+            break;
+        }
+        default:;
+        {
+            event->ignore();
+        }
+    }
+
+    if ( numSteps != 0 )
+    {
+        value = incrementedValue( d_data->value, numSteps );
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+
+        Q_EMIT sliderMoved( d_data->value );
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  \brief Set the number of steps
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  The default setting is 100.
+
+  \param stepCount Number of steps
+
+  \sa totalSteps(), setSingleSteps(), setPageSteps()
+ */
+void QwtAbstractSlider::setTotalSteps( uint stepCount )
+{
+    d_data->totalSteps = stepCount;
+}
+
+/*!
+  \return Number of steps
+  \sa setTotalSteps(), singleSteps(), pageSteps()
+ */
+uint QwtAbstractSlider::totalSteps() const
+{
+    return d_data->totalSteps;
+}
+
+/*!
+  \brief Set the number of steps for a single increment
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  \param stepCount Number of steps
+
+  \sa singleSteps(), setTotalSteps(), setPageSteps()
+ */
+
+void QwtAbstractSlider::setSingleSteps( uint stepCount )
+{
+    d_data->singleSteps = stepCount;
+}   
+
+/*!
+  \return Number of steps
+  \sa setSingleSteps(), totalSteps(), pageSteps()
+ */
+uint QwtAbstractSlider::singleSteps() const
+{
+    return d_data->singleSteps;
+}   
+
+/*! 
+  \brief Set the number of steps for a page increment
+    
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  \param stepCount Number of steps
+
+  \sa pageSteps(), setTotalSteps(), setSingleSteps()
+ */
+
+void QwtAbstractSlider::setPageSteps( uint stepCount )
+{
+    d_data->pageSteps = stepCount;
+}
+
+/*!
+  \return Number of steps
+  \sa setPageSteps(), totalSteps(), singleSteps()
+ */
+uint QwtAbstractSlider::pageSteps() const
+{
+    return d_data->pageSteps;
+}
+
+/*!
+  \brief Enable step alignment
+
+  When step alignment is enabled values resulting from slider
+  movements are aligned to the step size.
+
+  \param on Enable step alignment when true
+  \sa stepAlignment()
+*/
+void QwtAbstractSlider::setStepAlignment( bool on )
+{   
+    if ( on != d_data->stepAlignment )
+    {
+        d_data->stepAlignment = on;
+    }
+}   
+    
+/*!
+  \return True, when step alignment is enabled
+  \sa setStepAlignment()
+ */
+bool QwtAbstractSlider::stepAlignment() const
+{
+    return d_data->stepAlignment;
+}
+
+/*!
+  Set the slider to the specified value
+
+  \param value New value
+  \sa setValid(), sliderChange(), valueChanged()
+*/
+void QwtAbstractSlider::setValue( double value )
+{
+    value = qBound( minimum(), value, maximum() );
+
+    const bool changed = ( d_data->value != value ) || !d_data->isValid;
+
+    d_data->value = value;
+    d_data->isValid = true;
+
+    if ( changed )
+    {
+        sliderChange();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+//! Returns the current value.
+double QwtAbstractSlider::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  If wrapping is true stepping up from upperBound() value will 
+  take you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+*/
+void QwtAbstractSlider::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}   
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */ 
+bool QwtAbstractSlider::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  Invert wheel and key events
+
+  Usually scrolling the mouse wheel "up" and using keys like page 
+  up will increase the slider's value towards its maximum. 
+  When invertedControls() is enabled the value is scrolled
+  towards its minimum.
+
+  Inverting the controls might be f.e. useful for a vertical slider
+  with an inverted scale ( decreasing from top to bottom ).
+
+  \param on Invert controls, when true
+
+  \sa invertedControls(), keyEvent(), wheelEvent()
+ */
+void QwtAbstractSlider::setInvertedControls( bool on )
+{
+    d_data->invertedControls = on;
+}
+
+/*!
+ \return True, when the controls are inverted
+ \sa setInvertedControls()
+ */
+bool QwtAbstractSlider::invertedControls() const
+{
+    return d_data->invertedControls;
+}
+
+/*!
+  Increment the slider
+
+  The step size depends on the number of totalSteps()
+
+  \param stepCount Number of steps
+  \sa setTotalSteps(), incrementedValue()
+ */
+void QwtAbstractSlider::incrementValue( int stepCount )
+{
+    const double value = incrementedValue( 
+        d_data->value, stepCount );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+    }
+}
+
+/*!
+  Increment a value 
+
+  \param value Value 
+  \param stepCount Number of steps
+
+  \return Incremented value
+ */
+double QwtAbstractSlider::incrementedValue( 
+    double value, int stepCount ) const
+{
+    if ( d_data->totalSteps == 0 )
+        return value;
+
+    const QwtTransform *transformation =
+        scaleMap().transformation();
+
+    if ( transformation == NULL )
+    {
+        const double range = maximum() - minimum();
+        value += stepCount * range / d_data->totalSteps;
+    }
+    else
+    {
+        QwtScaleMap map = scaleMap();
+        map.setPaintInterval( 0, d_data->totalSteps );
+
+        // we need equidant steps according to
+        // paint device coordinates
+        const double range = transformation->transform( maximum() ) 
+            - transformation->transform( minimum() );
+
+        const double stepSize = range / d_data->totalSteps;
+
+        double v = transformation->transform( value );
+
+        v = qRound( v / stepSize ) * stepSize; 
+        v += stepCount * range / d_data->totalSteps;
+
+        value = transformation->invTransform( v );
+    }
+
+    value = boundedValue( value );
+
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    return value;
+}
+
+double QwtAbstractSlider::boundedValue( double value ) const
+{
+    const double vmin = minimum();
+    const double vmax = maximum();
+
+    if ( d_data->wrapping && vmin != vmax )
+    {
+        const int fullCircle = 360 * 16;
+
+        const double pd = scaleMap().pDist();
+        if ( int( pd / fullCircle ) * fullCircle == pd )
+        {
+            // full circle scales: min and max are the same
+            const double range = vmax - vmin;
+
+            if ( value < vmin )
+            {
+                value += ::ceil( ( vmin - value ) / range ) * range;
+            }
+            else if ( value > vmax )
+            {
+                value -= ::ceil( ( value - vmax ) / range ) * range;
+            }
+        }
+        else
+        {
+            if ( value < vmin )
+                value = vmax;
+            else if ( value > vmax )
+                value = vmin;
+        }
+    }
+    else
+    {
+        value = qBound( vmin, value, vmax );
+    }
+
+    return value;
+}
+
+double QwtAbstractSlider::alignedValue( double value ) const
+{
+    if ( d_data->totalSteps == 0 )
+        return value;
+
+    if ( scaleMap().transformation() == NULL )
+    {
+        const double stepSize = 
+            ( maximum() - minimum() ) / d_data->totalSteps;
+
+        if ( stepSize > 0.0 )
+        {
+            value = lowerBound() + 
+                qRound( ( value - lowerBound() ) / stepSize ) * stepSize;
+        }
+    }
+    else
+    {
+        const double stepSize = 
+            ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps;
+
+        if ( stepSize > 0.0 )
+        {
+            double v = scaleMap().transform( value );
+
+            v = scaleMap().p1() +
+                qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize;
+
+            value = scaleMap().invTransform( v );
+        }
+    }
+
+    // correct rounding error if value = 0
+    if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+    {
+        value = 0.0;
+    }
+    else
+    {
+        // correct rounding error at the border
+        if ( qFuzzyCompare( value, upperBound() ) )
+            value = upperBound();
+        else if ( qFuzzyCompare( value, lowerBound() ) )
+            value = lowerBound();
+    }
+
+    return value;
+}
+
+/*!
+  Update the slider according to modifications of the scale
+ */
+void QwtAbstractSlider::scaleChange()
+{
+    const double value = qBound( minimum(), d_data->value, maximum() );
+
+    const bool changed = ( value != d_data->value );
+    if ( changed )
+    {
+        d_data->value = value;
+    }
+
+    if ( d_data->isValid || changed )
+        Q_EMIT valueChanged( d_data->value );
+
+    updateGeometry();
+    update();
+}
+
+//! Calling update()
+void QwtAbstractSlider::sliderChange()
+{
+    update();
+}
diff --git a/qwt/qwt_abstract_slider.h b/qwt/qwt_abstract_slider.h
new file mode 100644
index 0000000..c91dcb6
--- /dev/null
+++ b/qwt/qwt_abstract_slider.h
@@ -0,0 +1,167 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SLIDER_H
+#define QWT_ABSTRACT_SLIDER_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_scale.h"
+
+/*!
+  \brief An abstract base class for slider widgets with a scale
+
+  A slider widget displays a value according to a scale.
+  The class is designed as a common super class for widgets like 
+  QwtKnob, QwtDial and QwtSlider.
+
+  When the slider is nor readOnly() its value can be modified 
+  by keyboard, mouse and wheel inputs. 
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+  Only for linear scales the number of steps correspond with
+  a fixed step size.
+*/
+
+class QWT_EXPORT QwtAbstractSlider: public QwtAbstractScale
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+
+    Q_PROPERTY( uint totalSteps READ totalSteps WRITE setTotalSteps )
+    Q_PROPERTY( uint singleSteps READ singleSteps WRITE setSingleSteps )
+    Q_PROPERTY( uint pageSteps READ pageSteps WRITE setPageSteps )
+    Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment )
+
+    Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+    Q_PROPERTY( bool tracking READ isTracking WRITE setTracking )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+
+    Q_PROPERTY( bool invertedControls READ invertedControls WRITE setInvertedControls )
+
+public:
+    explicit QwtAbstractSlider( QWidget *parent = NULL );
+    virtual ~QwtAbstractSlider();
+
+    void setValid( bool );
+    bool isValid() const;
+
+    double value() const;
+
+    void setWrapping( bool );
+    bool wrapping() const;
+
+    void setTotalSteps( uint );
+    uint totalSteps() const;
+
+    void setSingleSteps( uint );
+    uint singleSteps() const;
+
+    void setPageSteps( uint );
+    uint pageSteps() const;
+
+    void setStepAlignment( bool ); 
+    bool stepAlignment() const;
+
+    void setTracking( bool );
+    bool isTracking() const;
+
+    void setReadOnly( bool );
+    bool isReadOnly() const;
+
+    void setInvertedControls( bool );
+    bool invertedControls() const;
+
+public Q_SLOTS:
+    void setValue( double val );
+
+Q_SIGNALS:
+
+    /*!
+      \brief Notify a change of value.
+
+      When tracking is enabled (default setting), 
+      this signal will be emitted every time the value changes. 
+
+      \param value New value
+
+      \sa setTracking(), sliderMoved()
+    */
+    void valueChanged( double value );
+
+    /*!
+      This signal is emitted when the user presses the
+      movable part of the slider.
+    */
+    void sliderPressed();
+
+    /*!
+      This signal is emitted when the user releases the
+      movable part of the slider.
+    */
+    void sliderReleased();
+
+    /*!
+      This signal is emitted when the user moves the
+      slider with the mouse.
+
+      \param value New value
+
+      \sa valueChanged()
+    */
+    void sliderMoved( double value );
+
+protected:
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void mouseMoveEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+
+    /*!
+      \brief Determine what to do when the user presses a mouse button.
+
+      \param pos Mouse position
+
+      \retval True, when pos is a valid scroll position
+      \sa scrolledTo()
+    */
+    virtual bool isScrollPosition( const QPoint &pos ) const = 0;
+
+    /*!
+      \brief Determine the value for a new position of the
+             movable part of the slider
+
+      \param pos Mouse position
+
+      \return Value for the mouse position
+      \sa isScrollPosition()
+    */
+    virtual double scrolledTo( const QPoint &pos ) const = 0;
+
+    void incrementValue( int numSteps );
+
+    virtual void scaleChange();
+
+protected:
+    virtual void sliderChange();
+
+    double incrementedValue( 
+        double value, int stepCount ) const;
+
+private:
+    double alignedValue( double ) const;
+    double boundedValue( double ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_analog_clock.cpp b/qwt/qwt_analog_clock.cpp
new file mode 100644
index 0000000..1d44a95
--- /dev/null
+++ b/qwt/qwt_analog_clock.cpp
@@ -0,0 +1,244 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_analog_clock.h"
+#include "qwt_round_scale_draw.h"
+#include <qmath.h>
+#include <qlocale.h>
+
+class QwtAnalogClockScaleDraw: public QwtRoundScaleDraw
+{
+public:
+    QwtAnalogClockScaleDraw()
+    {
+        setSpacing( 8 );
+
+        enableComponent( QwtAbstractScaleDraw::Backbone, false );
+
+        setTickLength( QwtScaleDiv::MinorTick, 2 );
+        setTickLength( QwtScaleDiv::MediumTick, 4 );
+        setTickLength( QwtScaleDiv::MajorTick, 8 );
+
+        setPenWidth( 1 );
+    }
+
+    virtual QwtText label( double value ) const
+    {
+        if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+            value = 60.0 * 60.0 * 12.0;
+
+        return QLocale().toString( qRound( value / ( 60.0 * 60.0 ) ) );
+    }
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtAnalogClock::QwtAnalogClock( QWidget *parent ):
+    QwtDial( parent )
+{
+    setWrapping( true );
+    setReadOnly( true );
+
+    setOrigin( 270.0 );
+    setScaleDraw( new QwtAnalogClockScaleDraw() );
+
+    setTotalSteps( 60 );
+
+    const int secondsPerHour = 60.0 * 60.0; 
+
+    QList<double> majorTicks;
+    QList<double> minorTicks;
+
+    for ( int i = 0; i < 12; i++ )
+    {
+        majorTicks += i * secondsPerHour;
+
+        for ( int j = 1; j < 5; j++ )
+            minorTicks += i * secondsPerHour + j * secondsPerHour / 5.0;
+    }
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( 0.0, 12.0 * secondsPerHour );
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+    setScale( scaleDiv );
+
+    QColor knobColor = palette().color( QPalette::Active, QPalette::Text );
+    knobColor = knobColor.dark( 120 );
+
+    QColor handColor;
+    int width;
+
+    for ( int i = 0; i < NHands; i++ )
+    {
+        if ( i == SecondHand )
+        {
+            width = 2;
+            handColor = knobColor.dark( 120 );
+        }
+        else
+        {
+            width = 8;
+            handColor = knobColor;
+        }
+
+        QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle(
+            QwtDialSimpleNeedle::Arrow, true, handColor, knobColor );
+        hand->setWidth( width );
+
+        d_hand[i] = NULL;
+        setHand( static_cast<Hand>( i ), hand );
+    }
+}
+
+//! Destructor
+QwtAnalogClock::~QwtAnalogClock()
+{
+    for ( int i = 0; i < NHands; i++ )
+        delete d_hand[i];
+}
+
+/*!
+  Nop method, use setHand() instead
+  \sa setHand()
+*/
+void QwtAnalogClock::setNeedle( QwtDialNeedle * )
+{
+    // no op
+    return;
+}
+
+/*!
+   Set a clock hand
+   \param hand Specifies the type of hand
+   \param needle Hand
+   \sa hand()
+*/
+void QwtAnalogClock::setHand( Hand hand, QwtDialNeedle *needle )
+{
+    if ( hand >= 0 && hand < NHands )
+    {
+        delete d_hand[hand];
+        d_hand[hand] = needle;
+    }
+}
+
+/*!
+  \return Clock hand
+  \param hd Specifies the type of hand
+  \sa setHand()
+*/
+QwtDialNeedle *QwtAnalogClock::hand( Hand hd )
+{
+    if ( hd < 0 || hd >= NHands )
+        return NULL;
+
+    return d_hand[hd];
+}
+
+/*!
+  \return Clock hand
+  \param hd Specifies the type of hand
+  \sa setHand()
+*/
+const QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) const
+{
+    return const_cast<QwtAnalogClock *>( this )->hand( hd );
+}
+
+/*!
+  \brief Set the current time
+*/
+void QwtAnalogClock::setCurrentTime()
+{
+    setTime( QTime::currentTime() );
+}
+
+/*!
+  Set a time
+  \param time Time to display
+*/
+void QwtAnalogClock::setTime( const QTime &time )
+{
+    if ( time.isValid() )
+    {
+        setValue( ( time.hour() % 12 ) * 60.0 * 60.0
+            + time.minute() * 60.0 + time.second() );
+    }
+    else
+        setValid( false );
+}
+
+/*!
+  \brief Draw the needle
+
+  A clock has no single needle but three hands instead. drawNeedle()
+  translates value() into directions for the hands and calls
+  drawHand().
+
+  \param painter Painter
+  \param center Center of the clock
+  \param radius Maximum length for the hands
+  \param dir Dummy, not used.
+  \param colorGroup ColorGroup
+
+  \sa drawHand()
+*/
+void QwtAnalogClock::drawNeedle( QPainter *painter, const QPointF &center,
+    double radius, double dir, QPalette::ColorGroup colorGroup ) const
+{
+    Q_UNUSED( dir );
+
+    if ( isValid() )
+    {
+        const double hours = value() / ( 60.0 * 60.0 );
+        const double minutes = 
+            ( value() - qFloor(hours) * 60.0 * 60.0 ) / 60.0;
+        const double seconds = value() - qFloor(hours) * 60.0 * 60.0
+            - qFloor(minutes) * 60.0;
+
+        double angle[NHands];
+        angle[HourHand] = 360.0 * hours / 12.0;
+        angle[MinuteHand] = 360.0 * minutes / 60.0;
+        angle[SecondHand] = 360.0 * seconds / 60.0;
+
+        for ( int hand = 0; hand < NHands; hand++ )
+        {
+            const double d = 360.0 - angle[hand] - origin();
+            drawHand( painter, static_cast<Hand>( hand ), 
+                center, radius, d, colorGroup );
+        }
+    }
+}
+
+/*!
+  Draw a clock hand
+
+  \param painter Painter
+  \param hd Specify the type of hand
+  \param center Center of the clock
+  \param radius Maximum length for the hands
+  \param direction Direction of the hand in degrees, counter clockwise
+  \param cg ColorGroup
+*/
+void QwtAnalogClock::drawHand( QPainter *painter, Hand hd,
+    const QPointF &center, double radius, double direction,
+    QPalette::ColorGroup cg ) const
+{
+    const QwtDialNeedle *needle = hand( hd );
+    if ( needle )
+    {
+        if ( hd == HourHand )
+            radius = qRound( 0.8 * radius );
+
+        needle->draw( painter, center, radius, direction, cg );
+    }
+}
diff --git a/qwt/qwt_analog_clock.h b/qwt/qwt_analog_clock.h
new file mode 100644
index 0000000..ffe27e2
--- /dev/null
+++ b/qwt/qwt_analog_clock.h
@@ -0,0 +1,93 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ANALOG_CLOCK_H
+#define QWT_ANALOG_CLOCK_H
+
+#include "qwt_global.h"
+#include "qwt_dial.h"
+#include "qwt_dial_needle.h"
+#include <qdatetime.h>
+
+/*!
+  \brief An analog clock
+
+  \image html analogclock.png
+
+  \par Example
+  \code 
+  #include <qwt_analog_clock.h>
+
+  QwtAnalogClock *clock = new QwtAnalogClock(...);
+  clock->scaleDraw()->setPenWidth(3);
+  clock->setLineWidth(6);
+  clock->setFrameShadow(QwtDial::Sunken);
+  clock->setTime();
+
+  // update the clock every second
+  QTimer *timer = new QTimer(clock);
+  timer->connect(timer, SIGNAL(timeout()), clock, SLOT(setCurrentTime()));
+  timer->start(1000);
+
+  \endcode
+
+  \note The examples/dials example shows how to use QwtAnalogClock.
+*/
+
+class QWT_EXPORT QwtAnalogClock: public QwtDial
+{
+    Q_OBJECT
+
+public:
+    /*!
+        Hand type
+        \sa setHand(), hand()
+    */
+    enum Hand
+    {
+        //! Needle displaying the seconds
+        SecondHand,
+
+        //! Needle displaying the minutes
+        MinuteHand,
+
+        //! Needle displaying the hours
+        HourHand,
+
+        //! Number of needles
+        NHands
+    };
+
+    explicit QwtAnalogClock( QWidget* parent = NULL );
+    virtual ~QwtAnalogClock();
+
+    void setHand( Hand, QwtDialNeedle * );
+
+    const QwtDialNeedle *hand( Hand ) const;
+    QwtDialNeedle *hand( Hand );
+
+public Q_SLOTS:
+    void setCurrentTime();
+    void setTime( const QTime & );
+
+protected:
+    virtual void drawNeedle( QPainter *, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+    virtual void drawHand( QPainter *, Hand, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+private:
+    // use setHand instead
+    void setNeedle( QwtDialNeedle * );
+
+    QwtDialNeedle *d_hand[NHands];
+};
+
+#endif
diff --git a/qwt/qwt_arrow_button.cpp b/qwt/qwt_arrow_button.cpp
new file mode 100644
index 0000000..bd20f91
--- /dev/null
+++ b/qwt/qwt_arrow_button.cpp
@@ -0,0 +1,333 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_arrow_button.h"
+#include "qwt_math.h"
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qapplication.h>
+
+static const int MaxNum = 3;
+static const int Margin = 2;
+static const int Spacing = 1;
+
+class QwtArrowButton::PrivateData
+{
+public:
+    int num;
+    Qt::ArrowType arrowType;
+};
+
+static QStyleOptionButton styleOpt( const QwtArrowButton* btn )
+{
+    QStyleOptionButton option;
+    option.init( btn );
+    option.features = QStyleOptionButton::None;
+    if ( btn->isFlat() )
+        option.features |= QStyleOptionButton::Flat;
+    if ( btn->menu() )
+        option.features |= QStyleOptionButton::HasMenu;
+    if ( btn->autoDefault() || btn->isDefault() )
+        option.features |= QStyleOptionButton::AutoDefaultButton;
+    if ( btn->isDefault() )
+        option.features |= QStyleOptionButton::DefaultButton;
+    if ( btn->isDown() )
+        option.state |= QStyle::State_Sunken;
+    if ( !btn->isFlat() && !btn->isDown() )
+        option.state |= QStyle::State_Raised;
+
+    return option;
+}
+
+/*!
+  \param num Number of arrows
+  \param arrowType see Qt::ArrowType in the Qt docs.
+  \param parent Parent widget
+*/
+QwtArrowButton::QwtArrowButton( int num,
+        Qt::ArrowType arrowType, QWidget *parent ):
+    QPushButton( parent )
+{
+    d_data = new PrivateData;
+    d_data->num = qBound( 1, num, MaxNum );
+    d_data->arrowType = arrowType;
+
+    setAutoRepeat( true );
+    setAutoDefault( false );
+
+    switch ( d_data->arrowType )
+    {
+        case Qt::LeftArrow:
+        case Qt::RightArrow:
+            setSizePolicy( QSizePolicy::Expanding,
+                QSizePolicy::Fixed );
+            break;
+        default:
+            setSizePolicy( QSizePolicy::Fixed,
+                QSizePolicy::Expanding );
+    }
+}
+
+//! Destructor
+QwtArrowButton::~QwtArrowButton()
+{
+    delete d_data;
+    d_data = NULL;
+}
+
+/*!
+  \brief The direction of the arrows
+*/
+Qt::ArrowType QwtArrowButton::arrowType() const
+{
+    return d_data->arrowType;
+}
+
+/*!
+  \brief The number of arrows
+*/
+int QwtArrowButton::num() const
+{
+    return d_data->num;
+}
+
+/*!
+  \return the bounding rectangle for the label
+*/
+QRect QwtArrowButton::labelRect() const
+{
+    const int m = Margin;
+
+    QRect r = rect();
+    r.setRect( r.x() + m, r.y() + m,
+        r.width() - 2 * m, r.height() - 2 * m );
+
+    if ( isDown() )
+    {
+        QStyleOptionButton option = styleOpt( this );
+        const int ph = style()->pixelMetric(
+            QStyle::PM_ButtonShiftHorizontal, &option, this );
+        const int pv = style()->pixelMetric(
+            QStyle::PM_ButtonShiftVertical, &option, this );
+
+        r.translate( ph, pv );
+    }
+
+    return r;
+}
+
+/*!
+   Paint event handler
+   \param event Paint event
+*/
+void QwtArrowButton::paintEvent( QPaintEvent *event )
+{
+    QPushButton::paintEvent( event );
+    QPainter painter( this );
+    drawButtonLabel( &painter );
+}
+
+/*!
+  \brief Draw the button label
+
+  \param painter Painter
+  \sa The Qt Manual for QPushButton
+*/
+void QwtArrowButton::drawButtonLabel( QPainter *painter )
+{
+    const bool isVertical = d_data->arrowType == Qt::UpArrow ||
+        d_data->arrowType == Qt::DownArrow;
+
+    const QRect r = labelRect();
+    QSize boundingSize = labelRect().size();
+    if ( isVertical )
+        boundingSize.transpose();
+
+    const int w =
+        ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum;
+
+    QSize arrow = arrowSize( Qt::RightArrow,
+        QSize( w, boundingSize.height() ) );
+
+    if ( isVertical )
+        arrow.transpose();
+
+    QRect contentsSize; // aligned rect where to paint all arrows
+    if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow )
+    {
+        contentsSize.setWidth( d_data->num * arrow.width()
+            + ( d_data->num - 1 ) * Spacing );
+        contentsSize.setHeight( arrow.height() );
+    }
+    else
+    {
+        contentsSize.setWidth( arrow.width() );
+        contentsSize.setHeight( d_data->num * arrow.height()
+            + ( d_data->num - 1 ) * Spacing );
+    }
+
+    QRect arrowRect( contentsSize );
+    arrowRect.moveCenter( r.center() );
+    arrowRect.setSize( arrow );
+
+    painter->save();
+    for ( int i = 0; i < d_data->num; i++ )
+    {
+        drawArrow( painter, arrowRect, d_data->arrowType );
+
+        int dx = 0;
+        int dy = 0;
+
+        if ( isVertical )
+            dy = arrow.height() + Spacing;
+        else
+            dx = arrow.width() + Spacing;
+
+        arrowRect.translate( dx, dy );
+    }
+    painter->restore();
+
+    if ( hasFocus() )
+    {
+        QStyleOptionFocusRect option;
+        option.init( this );
+        option.backgroundColor = palette().color( QPalette::Window );
+
+        style()->drawPrimitive( QStyle::PE_FrameFocusRect,
+            &option, painter, this );
+    }
+}
+
+/*!
+    Draw an arrow int a bounding rectangle
+
+    \param painter Painter
+    \param r Rectangle where to paint the arrow
+    \param arrowType Arrow type
+*/
+void QwtArrowButton::drawArrow( QPainter *painter,
+    const QRect &r, Qt::ArrowType arrowType ) const
+{
+    QPolygon pa( 3 );
+
+    switch ( arrowType )
+    {
+        case Qt::UpArrow:
+            pa.setPoint( 0, r.bottomLeft() );
+            pa.setPoint( 1, r.bottomRight() );
+            pa.setPoint( 2, r.center().x(), r.top() );
+            break;
+        case Qt::DownArrow:
+            pa.setPoint( 0, r.topLeft() );
+            pa.setPoint( 1, r.topRight() );
+            pa.setPoint( 2, r.center().x(), r.bottom() );
+            break;
+        case Qt::RightArrow:
+            pa.setPoint( 0, r.topLeft() );
+            pa.setPoint( 1, r.bottomLeft() );
+            pa.setPoint( 2, r.right(), r.center().y() );
+            break;
+        case Qt::LeftArrow:
+            pa.setPoint( 0, r.topRight() );
+            pa.setPoint( 1, r.bottomRight() );
+            pa.setPoint( 2, r.left(), r.center().y() );
+            break;
+        default:
+            break;
+    }
+
+    painter->save();
+
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( palette().brush( QPalette::ButtonText ) );
+    painter->drawPolygon( pa );
+
+    painter->restore();
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtArrowButton::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \brief Return a minimum size hint
+*/
+QSize QwtArrowButton::minimumSizeHint() const
+{
+    const QSize asz = arrowSize( Qt::RightArrow, QSize() );
+
+    QSize sz(
+        2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(),
+        2 * Margin + asz.height()
+    );
+
+    if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow )
+        sz.transpose();
+
+    QStyleOption styleOption;
+    styleOption.init( this );
+
+    sz = style()->sizeFromContents( QStyle::CT_PushButton,
+        &styleOption, sz, this );
+
+    return sz;
+}
+
+/*!
+   Calculate the size for a arrow that fits into a rectangle of a given size
+
+   \param arrowType Arrow type
+   \param boundingSize Bounding size
+   \return Size of the arrow
+*/
+QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType,
+    const QSize &boundingSize ) const
+{
+    QSize bs = boundingSize;
+    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
+        bs.transpose();
+
+    const int MinLen = 2;
+    const QSize sz = bs.expandedTo(
+        QSize( MinLen, 2 * MinLen - 1 ) ); // minimum
+
+    int w = sz.width();
+    int h = 2 * w - 1;
+
+    if ( h > sz.height() )
+    {
+        h = sz.height();
+        w = ( h + 1 ) / 2;
+    }
+
+    QSize arrSize( w, h );
+    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
+        arrSize.transpose();
+
+    return arrSize;
+}
+
+/*!
+  \brief autoRepeat for the space keys
+*/
+void QwtArrowButton::keyPressEvent( QKeyEvent *event )
+{
+    if ( event->isAutoRepeat() && event->key() == Qt::Key_Space )
+        Q_EMIT clicked();
+
+    QPushButton::keyPressEvent( event );
+}
diff --git a/qwt/qwt_arrow_button.h b/qwt/qwt_arrow_button.h
new file mode 100644
index 0000000..ae436fe
--- /dev/null
+++ b/qwt/qwt_arrow_button.h
@@ -0,0 +1,52 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ARROW_BUTTON_H
+#define QWT_ARROW_BUTTON_H
+
+#include "qwt_global.h"
+#include <qpushbutton.h>
+
+/*!
+  \brief Arrow Button
+
+  A push button with one or more filled triangles on its front.
+  An Arrow button can have 1 to 3 arrows in a row, pointing
+  up, down, left or right.
+*/
+class QWT_EXPORT QwtArrowButton : public QPushButton
+{
+public:
+    explicit QwtArrowButton ( int num, Qt::ArrowType, QWidget *parent = NULL );
+    virtual ~QwtArrowButton();
+
+    Qt::ArrowType arrowType() const;
+    int num() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+protected:
+    virtual void paintEvent( QPaintEvent *event );
+
+    virtual void drawButtonLabel( QPainter *p );
+    virtual void drawArrow( QPainter *,
+        const QRect &, Qt::ArrowType ) const;
+    virtual QRect labelRect() const;
+    virtual QSize arrowSize( Qt::ArrowType,
+        const QSize &boundingSize ) const;
+
+    virtual void keyPressEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_clipper.cpp b/qwt/qwt_clipper.cpp
new file mode 100644
index 0000000..51614aa
--- /dev/null
+++ b/qwt/qwt_clipper.cpp
@@ -0,0 +1,510 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_clipper.h"
+#include "qwt_point_polar.h"
+#include <qrect.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan(x) ::atan(x)
+#endif
+
+namespace QwtClip
+{
+    // some templates used for inlining
+    template <class Point, typename T> class LeftEdge;
+    template <class Point, typename T> class RightEdge;
+    template <class Point, typename T> class TopEdge;
+    template <class Point, typename T> class BottomEdge;
+
+    template <class Point> class PointBuffer;
+}
+
+template <class Point, typename Value>
+class QwtClip::LeftEdge
+{
+public:
+    inline LeftEdge( Value x1, Value, Value, Value ):
+        d_x1( x1 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.x() >= d_x1;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
+        return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) );
+    }
+private:
+    const Value d_x1;
+};
+
+template <class Point, typename Value>
+class QwtClip::RightEdge
+{
+public:
+    inline RightEdge( Value, Value x2, Value, Value ):
+        d_x2( x2 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.x() <= d_x2;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
+        return Point( d_x2, static_cast<Value>( p2.y() + ( d_x2 - p2.x() ) * dy ) );
+    }
+
+private:
+    const Value d_x2;
+};
+
+template <class Point, typename Value>
+class QwtClip::TopEdge
+{
+public:
+    inline TopEdge( Value, Value, Value y1, Value ):
+        d_y1( y1 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.y() >= d_y1;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
+        return Point( static_cast<Value>( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 );
+    }
+
+private:
+    const Value d_y1;
+};
+
+template <class Point, typename Value>
+class QwtClip::BottomEdge
+{
+public:
+    inline BottomEdge( Value, Value, Value, Value y2 ):
+        d_y2( y2 )
+    {
+    }
+
+    inline bool isInside( const Point &p ) const
+    {
+        return p.y() <= d_y2;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
+        return Point( static_cast<Value>( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 );
+    }
+
+private:
+    const Value d_y2;
+};
+
+template<class Point>
+class QwtClip::PointBuffer
+{
+public:
+    PointBuffer( int capacity = 0 ):
+        m_capacity( 0 ),
+        m_size( 0 ),
+        m_buffer( NULL )
+    {
+        if ( capacity > 0 )
+            reserve( capacity );
+    }
+
+    ~PointBuffer()
+    {
+        if ( m_buffer )
+            ::free( m_buffer );
+    }
+
+    inline void setPoints( int numPoints, const Point *points )
+    {
+        reserve( numPoints );
+
+        m_size = numPoints;
+        ::memcpy( m_buffer, points, m_size * sizeof( Point ) );
+    }
+
+    inline void reset() 
+    { 
+        m_size = 0; 
+    }
+
+    inline int size() const 
+    { 
+        return m_size; 
+    }
+
+    inline Point *data() const 
+    { 
+        return m_buffer; 
+    }
+
+    inline Point &operator[]( int i ) 
+    { 
+        return m_buffer[i]; 
+    }
+
+    inline const Point &operator[]( int i ) const 
+    { 
+        return m_buffer[i]; 
+    }
+
+    inline void add( const Point &point )
+    {
+        if ( m_capacity <= m_size )
+            reserve( m_size + 1 );
+
+        m_buffer[m_size++] = point;
+    }
+
+private:
+    inline void reserve( int size )
+    {
+        if ( m_capacity == 0 )
+            m_capacity = 1;
+
+        while ( m_capacity < size )
+            m_capacity *= 2;
+
+        m_buffer = static_cast<Point *>( 
+            ::realloc( m_buffer, m_capacity * sizeof( Point ) ) );
+    }
+
+    int m_capacity;
+    int m_size;
+    Point *m_buffer;
+};
+
+using namespace QwtClip;
+
+template <class Polygon, class Rect, class Point, typename T>
+class QwtPolygonClipper
+{
+public:
+    QwtPolygonClipper( const Rect &clipRect ):
+        d_clipRect( clipRect )
+    {
+    }
+
+    Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const
+    {
+#if 0
+        if ( d_clipRect.contains( polygon.boundingRect() ) )
+            return polygon;
+#endif
+
+        PointBuffer<Point> points1;
+        PointBuffer<Point> points2( qMin( 256, polygon.size() ) );
+
+        points1.setPoints( polygon.size(), polygon.data() );
+
+        clipEdge< LeftEdge<Point, T> >( closePolygon, points1, points2 );
+        clipEdge< RightEdge<Point, T> >( closePolygon, points2, points1 );
+        clipEdge< TopEdge<Point, T> >( closePolygon, points1, points2 );
+        clipEdge< BottomEdge<Point, T> >( closePolygon, points2, points1 );
+
+        Polygon p;
+        p.resize( points1.size() );
+        ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) );
+
+        return p;
+    }
+
+private:
+    template <class Edge>
+    inline void clipEdge( bool closePolygon,
+        PointBuffer<Point> &points, PointBuffer<Point> &clippedPoints ) const
+    {
+        clippedPoints.reset();
+
+        if ( points.size() < 2 )
+        {
+            if ( points.size() == 1 )
+                clippedPoints.add( points[0] );
+            return;
+        }
+
+        const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(),
+            d_clipRect.y(), d_clipRect.y() + d_clipRect.height() );
+
+        int lastPos, start;
+        if ( closePolygon )
+        {
+            start = 0;
+            lastPos = points.size() - 1;
+        }
+        else
+        {
+            start = 1;
+            lastPos = 0;
+
+            if ( edge.isInside( points[0] ) )
+                clippedPoints.add( points[0] );
+        }
+
+        const uint nPoints = points.size();
+        for ( uint i = start; i < nPoints; i++ )
+        {
+            const Point &p1 = points[i];
+            const Point &p2 = points[lastPos];
+
+            if ( edge.isInside( p1 ) )
+            {
+                if ( edge.isInside( p2 ) )
+                {
+                    clippedPoints.add( p1 );
+                }
+                else
+                {
+                    clippedPoints.add( edge.intersection( p1, p2 ) );
+                    clippedPoints.add( p1 );
+                }
+            }
+            else
+            {
+                if ( edge.isInside( p2 ) )
+                {
+                    clippedPoints.add( edge.intersection( p1, p2 ) );
+                }
+            }
+            lastPos = i;
+        }
+    }
+
+    const Rect d_clipRect;
+};
+
+class QwtCircleClipper
+{
+public:
+    QwtCircleClipper( const QRectF &r );
+    QVector<QwtInterval> clipCircle( const QPointF &, double radius ) const;
+
+private:
+    enum Edge
+    {
+        Left,
+        Top,
+        Right,
+        Bottom,
+
+        NEdges
+    };
+
+    QList<QPointF> cuttingPoints(
+        Edge, const QPointF &pos, double radius ) const;
+
+    double toAngle( const QPointF &, const QPointF & ) const;
+
+    const QRectF d_rect;
+};
+
+
+QwtCircleClipper::QwtCircleClipper( const QRectF &r ):
+    d_rect( r )
+{
+}
+
+QVector<QwtInterval> QwtCircleClipper::clipCircle(
+    const QPointF &pos, double radius ) const
+{
+    QList<QPointF> points;
+    for ( int edge = 0; edge < NEdges; edge++ )
+        points += cuttingPoints( static_cast<Edge>(edge), pos, radius );
+
+    QVector<QwtInterval> intv;
+    if ( points.size() <= 0 )
+    {
+        QRectF cRect( 0, 0, 2 * radius, 2 * radius );
+        cRect.moveCenter( pos );
+        if ( d_rect.contains( cRect ) )
+            intv += QwtInterval( 0.0, 2 * M_PI );
+    }
+    else
+    {
+        QList<double> angles;
+        for ( int i = 0; i < points.size(); i++ )
+            angles += toAngle( pos, points[i] );
+        qSort( angles );
+
+        const int in = d_rect.contains( qwtPolar2Pos( pos, radius,
+            angles[0] + ( angles[1] - angles[0] ) / 2 ) );
+
+        if ( in )
+        {
+            for ( int i = 0; i < angles.size() - 1; i += 2 )
+                intv += QwtInterval( angles[i], angles[i+1] );
+        }
+        else
+        {
+            for ( int i = 1; i < angles.size() - 1; i += 2 )
+                intv += QwtInterval( angles[i], angles[i+1] );
+            intv += QwtInterval( angles.last(), angles.first() );
+        }
+    }
+
+    return intv;
+}
+
+double QwtCircleClipper::toAngle(
+    const QPointF &from, const QPointF &to ) const
+{
+    if ( from.x() == to.x() )
+        return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0;
+
+    const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) );
+
+    double angle = qAtan( m );
+    if ( to.x() > from.x() )
+    {
+        if ( to.y() > from.y() )
+            angle = 2 * M_PI - angle;
+    }
+    else
+    {
+        if ( to.y() > from.y() )
+            angle = M_PI + angle;
+        else
+            angle = M_PI - angle;
+    }
+
+    return angle;
+}
+
+QList<QPointF> QwtCircleClipper::cuttingPoints(
+    Edge edge, const QPointF &pos, double radius ) const
+{
+    QList<QPointF> points;
+
+    if ( edge == Left || edge == Right )
+    {
+        const double x = ( edge == Left ) ? d_rect.left() : d_rect.right();
+        if ( qAbs( pos.x() - x ) < radius )
+        {
+            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) );
+            const double m_y1 = pos.y() + off;
+            if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() )
+                points += QPointF( x, m_y1 );
+
+            const double m_y2 = pos.y() - off;
+            if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() )
+                points += QPointF( x, m_y2 );
+        }
+    }
+    else
+    {
+        const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom();
+        if ( qAbs( pos.y() - y ) < radius )
+        {
+            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) );
+            const double x1 = pos.x() + off;
+            if ( x1 >= d_rect.left() && x1 <= d_rect.right() )
+                points += QPointF( x1, y );
+
+            const double m_x2 = pos.x() - off;
+            if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() )
+                points += QPointF( m_x2, y );
+        }
+    }
+    return points;
+}
+
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygon QwtClipper::clipPolygon(
+    const QRectF &clipRect, const QPolygon &polygon, bool closePolygon )
+{
+    const int minX = qCeil( clipRect.left() );
+    const int maxX = qFloor( clipRect.right() );
+    const int minY = qCeil( clipRect.top() );
+    const int maxY = qFloor( clipRect.bottom() );
+
+    const QRect r( minX, minY, maxX - minX, maxY - minY );
+
+    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( r );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygon QwtClipper::clipPolygon(
+    const QRect &clipRect, const QPolygon &polygon, bool closePolygon )
+{
+    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( clipRect );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygonF QwtClipper::clipPolygonF(
+    const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon )
+{
+    QwtPolygonClipper<QPolygonF, QRectF, QPointF, double> clipper( clipRect );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+
+/*!
+   Circle clipping
+
+   clipCircle() divides a circle into intervals of angles representing arcs
+   of the circle. When the circle is completely inside the clip rectangle
+   an interval [0.0, 2 * M_PI] is returned.
+
+   \param clipRect Clip rectangle
+   \param center Center of the circle
+   \param radius Radius of the circle
+
+   \return Arcs of the circle
+*/
+QVector<QwtInterval> QwtClipper::clipCircle( const QRectF &clipRect,
+    const QPointF &center, double radius )
+{
+    QwtCircleClipper clipper( clipRect );
+    return clipper.clipCircle( center, radius );
+}
diff --git a/qwt/qwt_clipper.h b/qwt/qwt_clipper.h
new file mode 100644
index 0000000..1b1820b
--- /dev/null
+++ b/qwt/qwt_clipper.h
@@ -0,0 +1,40 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_CLIPPER_H
+#define QWT_CLIPPER_H
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qpolygon.h>
+#include <qvector.h>
+
+class QRect;
+class QRectF;
+
+/*!
+  \brief Some clipping algorithms
+*/
+
+class QWT_EXPORT QwtClipper
+{
+public:
+    static QPolygon clipPolygon( const QRect &, 
+        const QPolygon &, bool closePolygon = false );
+    static QPolygon clipPolygon( const QRectF &, 
+        const QPolygon &, bool closePolygon = false );
+
+    static QPolygonF clipPolygonF( const QRectF &, 
+        const QPolygonF &, bool closePolygon = false );
+
+    static QVector<QwtInterval> clipCircle(
+        const QRectF &, const QPointF &, double radius );
+};
+
+#endif
diff --git a/qwt/qwt_color_map.cpp b/qwt/qwt_color_map.cpp
new file mode 100644
index 0000000..571c138
--- /dev/null
+++ b/qwt/qwt_color_map.cpp
@@ -0,0 +1,444 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_color_map.h"
+#include "qwt_math.h"
+#include "qwt_interval.h"
+#include <qnumeric.h>
+
+class QwtLinearColorMap::ColorStops
+{
+public:
+    ColorStops()
+    {
+        _stops.reserve( 256 );
+    }
+
+    void insert( double pos, const QColor &color );
+    QRgb rgb( QwtLinearColorMap::Mode, double pos ) const;
+
+    QVector<double> stops() const;
+
+private:
+
+    class ColorStop
+    {
+    public:
+        ColorStop():
+            pos( 0.0 ),
+            rgb( 0 )
+        {
+        };
+
+        ColorStop( double p, const QColor &c ):
+            pos( p ),
+            rgb( c.rgb() )
+        {
+            r = qRed( rgb );
+            g = qGreen( rgb );
+            b = qBlue( rgb );
+        }
+
+        double pos;
+        QRgb rgb;
+        int r, g, b;
+    };
+
+    inline int findUpper( double pos ) const;
+    QVector<ColorStop> _stops;
+};
+
+void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color )
+{
+    // Lookups need to be very fast, insertions are not so important.
+    // Anyway, a balanced tree is what we need here. TODO ...
+
+    if ( pos < 0.0 || pos > 1.0 )
+        return;
+
+    int index;
+    if ( _stops.size() == 0 )
+    {
+        index = 0;
+        _stops.resize( 1 );
+    }
+    else
+    {
+        index = findUpper( pos );
+        if ( index == _stops.size() ||
+                qAbs( _stops[index].pos - pos ) >= 0.001 )
+        {
+            _stops.resize( _stops.size() + 1 );
+            for ( int i = _stops.size() - 1; i > index; i-- )
+                _stops[i] = _stops[i-1];
+        }
+    }
+
+    _stops[index] = ColorStop( pos, color );
+}
+
+inline QVector<double> QwtLinearColorMap::ColorStops::stops() const
+{
+    QVector<double> positions( _stops.size() );
+    for ( int i = 0; i < _stops.size(); i++ )
+        positions[i] = _stops[i].pos;
+    return positions;
+}
+
+inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const
+{
+    int index = 0;
+    int n = _stops.size();
+
+    const ColorStop *stops = _stops.data();
+
+    while ( n > 0 )
+    {
+        const int half = n >> 1;
+        const int middle = index + half;
+
+        if ( stops[middle].pos <= pos )
+        {
+            index = middle + 1;
+            n -= half + 1;
+        }
+        else
+            n = half;
+    }
+
+    return index;
+}
+
+inline QRgb QwtLinearColorMap::ColorStops::rgb(
+    QwtLinearColorMap::Mode mode, double pos ) const
+{
+    if ( pos <= 0.0 )
+        return _stops[0].rgb;
+    if ( pos >= 1.0 )
+        return _stops[ _stops.size() - 1 ].rgb;
+
+    const int index = findUpper( pos );
+    if ( mode == FixedColors )
+    {
+        return _stops[index-1].rgb;
+    }
+    else
+    {
+        const ColorStop &s1 = _stops[index-1];
+        const ColorStop &s2 = _stops[index];
+
+        const double ratio = ( pos - s1.pos ) / ( s2.pos - s1.pos );
+
+        const int r = s1.r + qRound( ratio * ( s2.r - s1.r ) );
+        const int g = s1.g + qRound( ratio * ( s2.g - s1.g ) );
+        const int b = s1.b + qRound( ratio * ( s2.b - s1.b ) );
+
+        return qRgb( r, g, b );
+    }
+}
+
+//! Constructor
+QwtColorMap::QwtColorMap( Format format ):
+    d_format( format )
+{
+}
+
+//! Destructor
+QwtColorMap::~QwtColorMap()
+{
+}
+
+/*!
+   Build and return a color map of 256 colors
+
+   The color table is needed for rendering indexed images in combination
+   with using colorIndex().
+
+   \param interval Range for the values
+   \return A color table, that can be used for a QImage
+*/
+QVector<QRgb> QwtColorMap::colorTable( const QwtInterval &interval ) const
+{
+    QVector<QRgb> table( 256 );
+
+    if ( interval.isValid() )
+    {
+        const double step = interval.width() / ( table.size() - 1 );
+        for ( int i = 0; i < table.size(); i++ )
+            table[i] = rgb( interval, interval.minValue() + step * i );
+    }
+
+    return table;
+}
+
+class QwtLinearColorMap::PrivateData
+{
+public:
+    ColorStops colorStops;
+    QwtLinearColorMap::Mode mode;
+};
+
+/*!
+   Build a color map with two stops at 0.0 and 1.0. The color
+   at 0.0 is Qt::blue, at 1.0 it is Qt::yellow.
+
+   \param format Preferred format of the color map
+*/
+QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ):
+    QwtColorMap( format )
+{
+    d_data = new PrivateData;
+    d_data->mode = ScaledColors;
+
+    setColorInterval( Qt::blue, Qt::yellow );
+}
+
+/*!
+   Build a color map with two stops at 0.0 and 1.0.
+
+   \param color1 Color used for the minimum value of the value interval
+   \param color2 Color used for the maximum value of the value interval
+   \param format Preferred format for the color map
+*/
+QwtLinearColorMap::QwtLinearColorMap( const QColor &color1,
+        const QColor &color2, QwtColorMap::Format format ):
+    QwtColorMap( format )
+{
+    d_data = new PrivateData;
+    d_data->mode = ScaledColors;
+    setColorInterval( color1, color2 );
+}
+
+//! Destructor
+QwtLinearColorMap::~QwtLinearColorMap()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Set the mode of the color map
+
+   FixedColors means the color is calculated from the next lower
+   color stop. ScaledColors means the color is calculated
+   by interpolating the colors of the adjacent stops.
+
+   \sa mode()
+*/
+void QwtLinearColorMap::setMode( Mode mode )
+{
+    d_data->mode = mode;
+}
+
+/*!
+   \return Mode of the color map
+   \sa setMode()
+*/
+QwtLinearColorMap::Mode QwtLinearColorMap::mode() const
+{
+    return d_data->mode;
+}
+
+/*!
+   Set the color range
+
+   Add stops at 0.0 and 1.0.
+
+   \param color1 Color used for the minimum value of the value interval
+   \param color2 Color used for the maximum value of the value interval
+
+   \sa color1(), color2()
+*/
+void QwtLinearColorMap::setColorInterval(
+    const QColor &color1, const QColor &color2 )
+{
+    d_data->colorStops = ColorStops();
+    d_data->colorStops.insert( 0.0, color1 );
+    d_data->colorStops.insert( 1.0, color2 );
+}
+
+/*!
+   Add a color stop
+
+   The value has to be in the range [0.0, 1.0].
+   F.e. a stop at position 17.0 for a range [10.0,20.0] must be
+   passed as: (17.0 - 10.0) / (20.0 - 10.0)
+
+   \param value Value between [0.0, 1.0]
+   \param color Color stop
+*/
+void QwtLinearColorMap::addColorStop( double value, const QColor& color )
+{
+    if ( value >= 0.0 && value <= 1.0 )
+        d_data->colorStops.insert( value, color );
+}
+
+/*!
+   \return Positions of color stops in increasing order
+*/
+QVector<double> QwtLinearColorMap::colorStops() const
+{
+    return d_data->colorStops.stops();
+}
+
+/*!
+  \return the first color of the color range
+  \sa setColorInterval()
+*/
+QColor QwtLinearColorMap::color1() const
+{
+    return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) );
+}
+
+/*!
+  \return the second color of the color range
+  \sa setColorInterval()
+*/
+QColor QwtLinearColorMap::color2() const
+{
+    return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) );
+}
+
+/*!
+  Map a value of a given interval into a RGB value
+
+  \param interval Range for all values
+  \param value Value to map into a RGB value
+
+  \return RGB value for value
+*/
+QRgb QwtLinearColorMap::rgb(
+    const QwtInterval &interval, double value ) const
+{
+    if ( qIsNaN(value) )
+        return qRgba(0, 0, 0, 0);
+
+    const double width = interval.width();
+
+    double ratio = 0.0;
+    if ( width > 0.0 )
+        ratio = ( value - interval.minValue() ) / width;
+
+    return d_data->colorStops.rgb( d_data->mode, ratio );
+}
+
+/*!
+  \brief Map a value of a given interval into a color index
+
+  \param interval Range for all values
+  \param value Value to map into a color index
+
+  \return Index, between 0 and 255
+*/
+unsigned char QwtLinearColorMap::colorIndex(
+    const QwtInterval &interval, double value ) const
+{
+    const double width = interval.width();
+
+    if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() )
+        return 0;
+
+    if ( value >= interval.maxValue() )
+        return 255;
+
+    const double ratio = ( value - interval.minValue() ) / width;
+
+    unsigned char index;
+    if ( d_data->mode == FixedColors )
+        index = static_cast<unsigned char>( ratio * 255 ); // always floor
+    else
+        index = static_cast<unsigned char>( qRound( ratio * 255 ) );
+
+    return index;
+}
+
+class QwtAlphaColorMap::PrivateData
+{
+public:
+    QColor color;
+    QRgb rgb;
+};
+
+
+/*!
+   Constructor
+   \param color Color of the map
+*/
+QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ):
+    QwtColorMap( QwtColorMap::RGB )
+{
+    d_data = new PrivateData;
+    d_data->color = color;
+    d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 );
+}
+
+//! Destructor
+QwtAlphaColorMap::~QwtAlphaColorMap()
+{
+    delete d_data;
+}
+
+/*!
+   Set the color
+
+   \param color Color
+   \sa color()
+*/
+void QwtAlphaColorMap::setColor( const QColor &color )
+{
+    d_data->color = color;
+    d_data->rgb = color.rgb();
+}
+
+/*!
+  \return the color
+  \sa setColor()
+*/
+QColor QwtAlphaColorMap::color() const
+{
+    return d_data->color;
+}
+
+/*!
+  \brief Map a value of a given interval into a alpha value
+
+  alpha := (value - interval.minValue()) / interval.width();
+
+  \param interval Range for all values
+  \param value Value to map into a RGB value
+  \return RGB value, with an alpha value
+*/
+QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const
+{
+    const double width = interval.width();
+    if ( !qIsNaN(value) && width >= 0.0 )
+    {
+        const double ratio = ( value - interval.minValue() ) / width;
+        int alpha = qRound( 255 * ratio );
+        if ( alpha < 0 )
+            alpha = 0;
+        if ( alpha > 255 )
+            alpha = 255;
+
+        return d_data->rgb | ( alpha << 24 );
+    }
+    return d_data->rgb;
+}
+
+/*!
+  Dummy function, needed to be implemented as it is pure virtual
+  in QwtColorMap. Color indices make no sense in combination with
+  an alpha channel.
+
+  \return Always 0
+*/
+unsigned char QwtAlphaColorMap::colorIndex(
+    const QwtInterval &, double ) const
+{
+    return 0;
+}
diff --git a/qwt/qwt_color_map.h b/qwt/qwt_color_map.h
new file mode 100644
index 0000000..91a92bd
--- /dev/null
+++ b/qwt/qwt_color_map.h
@@ -0,0 +1,200 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COLOR_MAP_H
+#define QWT_COLOR_MAP_H
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qcolor.h>
+#include <qvector.h>
+
+/*!
+  \brief QwtColorMap is used to map values into colors.
+
+  For displaying 3D data on a 2D plane the 3rd dimension is often
+  displayed using colors, like f.e in a spectrogram.
+
+  Each color map is optimized to return colors for only one of the
+  following image formats:
+
+  - QImage::Format_Indexed8\n
+  - QImage::Format_ARGB32\n
+
+  \sa QwtPlotSpectrogram, QwtScaleWidget
+*/
+
+class QWT_EXPORT QwtColorMap
+{
+public:
+    /*!
+        Format for color mapping
+        \sa rgb(), colorIndex(), colorTable()
+    */
+
+    enum Format
+    {
+        //! The map is intended to map into RGB values.
+        RGB,
+
+        /*!
+          The map is intended to map into 8 bit values, that
+          are indices into the color table.
+         */
+        Indexed
+    };
+
+    QwtColorMap( Format = QwtColorMap::RGB );
+    virtual ~QwtColorMap();
+
+    Format format() const;
+
+    /*!
+       Map a value of a given interval into a RGB value.
+
+       \param interval Range for the values
+       \param value Value
+       \return RGB value, corresponding to value
+    */
+    virtual QRgb rgb( const QwtInterval &interval,
+        double value ) const = 0;
+
+    /*!
+       Map a value of a given interval into a color index
+
+       \param interval Range for the values
+       \param value Value
+       \return color index, corresponding to value
+     */
+    virtual unsigned char colorIndex(
+        const QwtInterval &interval, double value ) const = 0;
+
+    QColor color( const QwtInterval &, double value ) const;
+    virtual QVector<QRgb> colorTable( const QwtInterval & ) const;
+
+private:
+    Format d_format;
+};
+
+/*!
+  \brief QwtLinearColorMap builds a color map from color stops.
+
+  A color stop is a color at a specific position. The valid
+  range for the positions is [0.0, 1.0]. When mapping a value
+  into a color it is translated into this interval according to mode().
+*/
+class QWT_EXPORT QwtLinearColorMap: public QwtColorMap
+{
+public:
+    /*!
+       Mode of color map
+       \sa setMode(), mode()
+    */
+    enum Mode
+    {
+        //! Return the color from the next lower color stop
+        FixedColors,
+
+        //! Interpolating the colors of the adjacent stops.
+        ScaledColors
+    };
+
+    QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB );
+    QwtLinearColorMap( const QColor &from, const QColor &to,
+        QwtColorMap::Format = QwtColorMap::RGB );
+
+    virtual ~QwtLinearColorMap();
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    void setColorInterval( const QColor &color1, const QColor &color2 );
+    void addColorStop( double value, const QColor& );
+    QVector<double> colorStops() const;
+
+    QColor color1() const;
+    QColor color2() const;
+
+    virtual QRgb rgb( const QwtInterval &, double value ) const;
+    virtual unsigned char colorIndex(
+        const QwtInterval &, double value ) const;
+
+    class ColorStops;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtLinearColorMap( const QwtLinearColorMap & );
+    QwtLinearColorMap &operator=( const QwtLinearColorMap & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief QwtAlphaColorMap varies the alpha value of a color
+*/
+class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap
+{
+public:
+    QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) );
+    virtual ~QwtAlphaColorMap();
+
+    void setColor( const QColor & );
+    QColor color() const;
+
+    virtual QRgb rgb( const QwtInterval &, double value ) const;
+
+private:
+    QwtAlphaColorMap( const QwtAlphaColorMap & );
+    QwtAlphaColorMap &operator=( const QwtAlphaColorMap & );
+
+    virtual unsigned char colorIndex(
+        const QwtInterval &, double value ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+
+/*!
+   Map a value into a color
+
+   \param interval Valid interval for values
+   \param value Value
+
+   \return Color corresponding to value
+
+   \warning This method is slow for Indexed color maps. If it is
+            necessary to map many values, its better to get the
+            color table once and find the color using colorIndex().
+*/
+inline QColor QwtColorMap::color(
+    const QwtInterval &interval, double value ) const
+{
+    if ( d_format == RGB )
+    {
+        return QColor( rgb( interval, value ) );
+    }
+    else
+    {
+        const unsigned int index = colorIndex( interval, value );
+        return colorTable( interval )[index]; // slow
+    }
+}
+
+/*!
+   \return Intended format of the color map
+   \sa Format
+*/
+inline QwtColorMap::Format QwtColorMap::format() const
+{
+    return d_format;
+}
+
+#endif
diff --git a/qwt/qwt_column_symbol.cpp b/qwt/qwt_column_symbol.cpp
new file mode 100644
index 0000000..d6f0f1a
--- /dev/null
+++ b/qwt/qwt_column_symbol.cpp
@@ -0,0 +1,293 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_column_symbol.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+
+static void qwtDrawBox( QPainter *p, const QRectF &rect,
+    const QPalette &pal, double lw )
+{
+    if ( lw > 0.0 )
+    {
+        if ( rect.width() == 0.0 )
+        {
+            p->setPen( pal.dark().color() );
+            p->drawLine( rect.topLeft(), rect.bottomLeft() );
+            return;
+        }
+
+        if ( rect.height() == 0.0 )
+        {
+            p->setPen( pal.dark().color() );
+            p->drawLine( rect.topLeft(), rect.topRight() );
+            return;
+        }
+
+        lw = qMin( lw, rect.height() / 2.0 - 1.0 );
+        lw = qMin( lw, rect.width() / 2.0 - 1.0 );
+
+        const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
+        QPolygonF polygon( outerRect );
+
+        if ( outerRect.width() > 2 * lw &&
+                outerRect.height() > 2 * lw )
+        {
+            const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
+            polygon = polygon.subtracted( innerRect );
+        }
+
+        p->setPen( Qt::NoPen );
+
+        p->setBrush( pal.dark() );
+        p->drawPolygon( polygon );
+    }
+
+    const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 );
+    if ( windowRect.isValid() )
+        p->fillRect( windowRect, pal.window() );
+}
+
+static void qwtDrawPanel( QPainter *painter, const QRectF &rect,
+    const QPalette &pal, double lw )
+{
+    if ( lw > 0.0 )
+    {
+        if ( rect.width() == 0.0 )
+        {
+            painter->setPen( pal.window().color() );
+            painter->drawLine( rect.topLeft(), rect.bottomLeft() );
+            return;
+        }
+
+        if ( rect.height() == 0.0 )
+        {
+            painter->setPen( pal.window().color() );
+            painter->drawLine( rect.topLeft(), rect.topRight() );
+            return;
+        }
+
+        lw = qMin( lw, rect.height() / 2.0 - 1.0 );
+        lw = qMin( lw, rect.width() / 2.0 - 1.0 );
+
+        const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
+        const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
+
+        QPolygonF lines[2];
+
+        lines[0] += outerRect.bottomLeft();
+        lines[0] += outerRect.topLeft();
+        lines[0] += outerRect.topRight();
+        lines[0] += innerRect.topRight();
+        lines[0] += innerRect.topLeft();
+        lines[0] += innerRect.bottomLeft();
+
+        lines[1] += outerRect.topRight();
+        lines[1] += outerRect.bottomRight();
+        lines[1] += outerRect.bottomLeft();
+        lines[1] += innerRect.bottomLeft();
+        lines[1] += innerRect.bottomRight();
+        lines[1] += innerRect.topRight();
+
+        painter->setPen( Qt::NoPen );
+
+        painter->setBrush( pal.light() );
+        painter->drawPolygon( lines[0] );
+        painter->setBrush( pal.dark() );
+        painter->drawPolygon( lines[1] );
+    }
+
+    painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() );
+}
+
+class QwtColumnSymbol::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtColumnSymbol::Box ),
+        frameStyle( QwtColumnSymbol::Raised ),
+        lineWidth( 2 )
+    {
+        palette = QPalette( Qt::gray );
+    }
+
+    QwtColumnSymbol::Style style;
+    QwtColumnSymbol::FrameStyle frameStyle;
+
+    QPalette palette;
+    int lineWidth;
+};
+
+/*!
+  Constructor
+
+  \param style Style of the symbol
+  \sa setStyle(), style(), Style
+*/
+QwtColumnSymbol::QwtColumnSymbol( Style style )
+{
+    d_data = new PrivateData();
+    d_data->style = style;
+}
+
+//! Destructor
+QwtColumnSymbol::~QwtColumnSymbol()
+{
+    delete d_data;
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style(), setPalette()
+*/
+void QwtColumnSymbol::setStyle( Style style )
+{
+    d_data->style = style;
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtColumnSymbol::Style QwtColumnSymbol::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Assign a palette for the symbol
+
+  \param palette Palette
+  \sa palette(), setStyle()
+*/
+void QwtColumnSymbol::setPalette( const QPalette &palette )
+{
+    d_data->palette = palette;
+}
+
+/*!
+  \return Current palette
+  \sa setPalette()
+*/
+const QPalette& QwtColumnSymbol::palette() const
+{
+    return d_data->palette;
+}
+
+/*!
+   Set the frame, that is used for the Box style.
+
+   \param frameStyle Frame style
+   \sa frameStyle(), setLineWidth(), setStyle()
+*/
+void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle )
+{
+    d_data->frameStyle = frameStyle;
+}
+
+/*!
+  \return Current frame style, that is used for the Box style.
+  \sa setFrameStyle(), lineWidth(), setStyle()
+*/
+QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const
+{
+    return d_data->frameStyle;
+}
+
+/*!
+   Set the line width of the frame, that is used for the Box style.
+
+   \param width Width
+   \sa lineWidth(), setFrameStyle()
+*/
+void QwtColumnSymbol::setLineWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    d_data->lineWidth = width;
+}
+
+/*!
+  \return Line width of the frame, that is used for the Box style.
+  \sa setLineWidth(), frameStyle(), setStyle()
+*/
+int QwtColumnSymbol::lineWidth() const
+{
+    return d_data->lineWidth;
+}
+
+/*!
+  Draw the symbol depending on its style.
+
+  \param painter Painter
+  \param rect Directed rectangle
+
+  \sa drawBox()
+*/
+void QwtColumnSymbol::draw( QPainter *painter,
+    const QwtColumnRect &rect ) const
+{
+    painter->save();
+
+    switch ( d_data->style )
+    {
+        case QwtColumnSymbol::Box:
+        {
+            drawBox( painter, rect );
+            break;
+        }
+        default:;
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw the symbol when it is in Box style.
+
+  \param painter Painter
+  \param rect Directed rectangle
+
+  \sa draw()
+*/
+void QwtColumnSymbol::drawBox( QPainter *painter,
+    const QwtColumnRect &rect ) const
+{
+    QRectF r = rect.toRect();
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        r.setLeft( qRound( r.left() ) );
+        r.setRight( qRound( r.right() ) );
+        r.setTop( qRound( r.top() ) );
+        r.setBottom( qRound( r.bottom() ) );
+    }
+
+    switch ( d_data->frameStyle )
+    {
+        case QwtColumnSymbol::Raised:
+        {
+            qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth );
+            break;
+        }
+        case QwtColumnSymbol::Plain:
+        {
+            qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth );
+            break;
+        }
+        default:
+        {
+            painter->fillRect( r, d_data->palette.window() );
+        }
+    }
+}
diff --git a/qwt/qwt_column_symbol.h b/qwt/qwt_column_symbol.h
new file mode 100644
index 0000000..918fe4a
--- /dev/null
+++ b/qwt/qwt_column_symbol.h
@@ -0,0 +1,161 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COLUMN_SYMBOL_H
+#define QWT_COLUMN_SYMBOL_H
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qpen.h>
+#include <qsize.h>
+#include <qrect.h>
+
+class QPainter;
+class QPalette;
+class QRect;
+class QwtText;
+
+/*!
+    \brief Directed rectangle representing bounding rectangle and orientation
+    of a column.
+*/
+class QWT_EXPORT QwtColumnRect
+{
+public:
+    //! Direction of the column
+    enum Direction
+    {
+        //! From left to right
+        LeftToRight,
+
+        //! From right to left
+        RightToLeft,
+
+        //! From bottom to top
+        BottomToTop,
+
+        //! From top to bottom
+        TopToBottom
+    };
+
+    //! Build an rectangle with invalid intervals directed BottomToTop.
+    QwtColumnRect():
+        direction( BottomToTop )
+    {
+    }
+
+    //! \return A normalized QRect built from the intervals
+    QRectF toRect() const
+    {
+        QRectF r( hInterval.minValue(), vInterval.minValue(),
+            hInterval.maxValue() - hInterval.minValue(),
+            vInterval.maxValue() - vInterval.minValue() );
+        r = r.normalized();
+
+        if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+            r.adjust( 1, 0, 0, 0 );
+        if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+            r.adjust( 0, 0, -1, 0 );
+        if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+            r.adjust( 0, 1, 0, 0 );
+        if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+            r.adjust( 0, 0, 0, -1 );
+
+        return r;
+    }
+
+    //! \return Orientation
+    Qt::Orientation orientation() const
+    {
+        if ( direction == LeftToRight || direction == RightToLeft )
+            return Qt::Horizontal;
+
+        return Qt::Vertical;
+    }
+
+    //! Interval for the horizontal coordinates
+    QwtInterval hInterval;
+
+    //! Interval for the vertical coordinates
+    QwtInterval vInterval;
+
+    //! Direction
+    Direction direction;
+};
+
+//! A drawing primitive for columns
+class QWT_EXPORT QwtColumnSymbol
+{
+public:
+    /*!
+      Style
+      \sa setStyle(), style()
+    */
+    enum Style
+    {
+        //! No Style, the symbol draws nothing
+        NoStyle = -1,
+
+        /*!
+          The column is painted with a frame depending on the frameStyle()
+          and lineWidth() using the palette().
+         */
+        Box,
+
+        /*!
+          Styles >= QwtColumnSymbol::UserStyle are reserved for derived
+          classes of QwtColumnSymbol that overload draw() with
+          additional application specific symbol types.
+         */
+        UserStyle = 1000
+    };
+
+    /*!
+      Frame Style used in Box style().
+      \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette()
+     */
+    enum FrameStyle
+    {
+        //! No frame
+        NoFrame,
+
+        //! A plain frame style
+        Plain,
+
+        //! A raised frame style
+        Raised
+    };
+
+public:
+    QwtColumnSymbol( Style = NoStyle );
+    virtual ~QwtColumnSymbol();
+
+    void setFrameStyle( FrameStyle style );
+    FrameStyle frameStyle() const;
+
+    void setLineWidth( int width );
+    int lineWidth() const;
+
+    void setPalette( const QPalette & );
+    const QPalette &palette() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    virtual void draw( QPainter *, const QwtColumnRect & ) const;
+
+protected:
+    void drawBox( QPainter *, const QwtColumnRect & ) const;
+
+private:
+    class PrivateData;
+    PrivateData* d_data;
+};
+
+#endif
diff --git a/qwt/qwt_compass.cpp b/qwt/qwt_compass.cpp
new file mode 100644
index 0000000..4e2c9ff
--- /dev/null
+++ b/qwt/qwt_compass.cpp
@@ -0,0 +1,308 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_compass.h"
+#include "qwt_compass_rose.h"
+#include "qwt_math.h"
+#include "qwt_scale_draw.h"
+#include "qwt_painter.h"
+#include "qwt_dial_needle.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qevent.h>
+
+/*! 
+  \brief Constructor
+
+  Initializes a label map for multiples of 45 degrees
+ */
+QwtCompassScaleDraw::QwtCompassScaleDraw()
+{
+    enableComponent( QwtAbstractScaleDraw::Backbone, false );
+    enableComponent( QwtAbstractScaleDraw::Ticks, false );
+
+    d_labelMap.insert( 0.0, QString::fromLatin1( "N" ) );
+    d_labelMap.insert( 45.0, QString::fromLatin1( "NE" ) );
+    d_labelMap.insert( 90.0, QString::fromLatin1( "E" ) );
+    d_labelMap.insert( 135.0, QString::fromLatin1( "SE" ) );
+    d_labelMap.insert( 180.0, QString::fromLatin1( "S" ) );
+    d_labelMap.insert( 225.0, QString::fromLatin1( "SW" ) );
+    d_labelMap.insert( 270.0, QString::fromLatin1( "W" ) );
+    d_labelMap.insert( 315.0, QString::fromLatin1( "NW" ) );
+
+#if 0
+    d_labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) );
+    d_labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) );
+    d_labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) );
+    d_labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) );
+    d_labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) );
+    d_labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) );
+    d_labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) );
+    d_labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) );
+#endif
+}
+
+/*! 
+  \brief Constructor
+
+  \param map Value to label map
+ */
+QwtCompassScaleDraw::QwtCompassScaleDraw( const QMap<double, QString> &map ):
+    d_labelMap( map )
+{
+    enableComponent( QwtAbstractScaleDraw::Backbone, false );
+    enableComponent( QwtAbstractScaleDraw::Ticks, false );
+}
+
+/*!
+  \brief Set a map, mapping values to labels
+  \param map Value to label map
+
+  The values of the major ticks are found by looking into this
+  map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
+
+  \warning The map will have no effect for values that are no major
+           tick values. Major ticks can be changed by QwtScaleDraw::setScale
+
+  \sa labelMap(), scaleDraw(), setScale()
+*/
+void QwtCompassScaleDraw::setLabelMap( const QMap<double, QString> &map )
+{
+    d_labelMap = map;
+}
+
+
+/*!
+  \return map, mapping values to labels
+  \sa setLabelMap()
+*/
+QMap<double, QString> QwtCompassScaleDraw::labelMap() const
+{
+    return d_labelMap;
+}
+
+/*!
+  Map a value to a corresponding label
+
+  \param value Value that will be mapped
+
+  label() looks in the labelMap() for a corresponding label for value
+  or returns an null text.
+
+  \return Label, or QString::null
+  \sa labelMap(), setLabelMap()
+*/
+
+QwtText QwtCompassScaleDraw::label( double value ) const
+{
+    if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        value = 0.0;
+
+    if ( value < 0.0 )
+        value += 360.0;
+
+    if ( d_labelMap.contains( value ) )
+        return d_labelMap[value];
+
+    return QwtText();
+}
+
+class QwtCompass::PrivateData
+{
+public:
+    PrivateData():
+        rose( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete rose;
+    }
+
+    QwtCompassRose *rose;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+
+  Create a compass widget with a scale, no needle and no rose.
+  The default origin is 270.0 with no valid value. It accepts
+  mouse and keyboard inputs and has no step size. The default mode
+  is QwtDial::RotateNeedle.
+*/
+QwtCompass::QwtCompass( QWidget* parent ):
+    QwtDial( parent )
+{
+    d_data = new PrivateData;
+
+    setScaleDraw( new QwtCompassScaleDraw() );
+
+    setOrigin( 270.0 );
+    setWrapping( true );
+
+    setScaleMaxMajor( 36 );
+    setScaleMaxMinor( 10 );
+
+    setScale( 0.0, 360.0 ); // degrees as default
+    setTotalSteps( 360 );
+}
+
+//!  Destructor
+QwtCompass::~QwtCompass()
+{
+    delete d_data;
+}
+
+
+/*!
+   Draw the contents of the scale
+
+   \param painter Painter
+   \param center Center of the content circle
+   \param radius Radius of the content circle
+*/
+void QwtCompass::drawScaleContents( QPainter *painter,
+    const QPointF &center, double radius ) const
+{
+    QPalette::ColorGroup cg;
+    if ( isEnabled() )
+        cg = hasFocus() ? QPalette::Active : QPalette::Inactive;
+    else
+        cg = QPalette::Disabled;
+
+    double north = origin();
+    if ( isValid() )
+    {
+        if ( mode() == RotateScale )
+            north -= value();
+    }
+
+    const int margin = 4;
+    drawRose( painter, center, radius - margin, 360.0 - north,  cg );
+}
+
+/*!
+  Draw the compass rose
+
+  \param painter Painter
+  \param center Center of the compass
+  \param radius of the circle, where to paint the rose
+  \param north Direction pointing north, in degrees counter clockwise
+  \param cg Color group
+*/
+void QwtCompass::drawRose( QPainter *painter, const QPointF &center,
+    double radius, double north, QPalette::ColorGroup cg ) const
+{
+    if ( d_data->rose )
+        d_data->rose->draw( painter, center, radius, north,  cg );
+}
+
+/*!
+  Set a rose for the compass
+  \param rose Compass rose
+  \warning The rose will be deleted, when a different rose is
+    set or in ~QwtCompass
+  \sa rose()
+*/
+void QwtCompass::setRose( QwtCompassRose *rose )
+{
+    if ( rose != d_data->rose )
+    {
+        if ( d_data->rose )
+            delete d_data->rose;
+
+        d_data->rose = rose;
+        update();
+    }
+}
+
+/*!
+  \return rose
+  \sa setRose()
+*/
+const QwtCompassRose *QwtCompass::rose() const
+{
+    return d_data->rose;
+}
+
+/*!
+  \return rose
+  \sa setRose()
+*/
+QwtCompassRose *QwtCompass::rose()
+{
+    return d_data->rose;
+}
+
+/*!
+  Handles key events
+
+  Beside the keys described in QwtDial::keyPressEvent numbers
+  from 1-9 (without 5) set the direction according to their
+  position on the num pad.
+
+  \sa isReadOnly()
+*/
+void QwtCompass::keyPressEvent( QKeyEvent *kev )
+{
+    if ( isReadOnly() )
+        return;
+
+#if 0
+    if ( kev->key() == Key_5 )
+    {
+        invalidate(); // signal ???
+        return;
+    }
+#endif
+
+    double newValue = value();
+
+    if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 )
+    {
+        if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 )
+            return;
+
+        switch ( kev->key() )
+        {
+            case Qt::Key_6:
+                newValue = 180.0 * 0.0;
+                break;
+            case Qt::Key_3:
+                newValue = 180.0 * 0.25;
+                break;
+            case Qt::Key_2:
+                newValue = 180.0 * 0.5;
+                break;
+            case Qt::Key_1:
+                newValue = 180.0 * 0.75;
+                break;
+            case Qt::Key_4:
+                newValue = 180.0 * 1.0;
+                break;
+            case Qt::Key_7:
+                newValue = 180.0 * 1.25;
+                break;
+            case Qt::Key_8:
+                newValue = 180.0 * 1.5;
+                break;
+            case Qt::Key_9:
+                newValue = 180.0 * 1.75;
+                break;
+        }
+        newValue -= origin();
+        setValue( newValue );
+    }
+    else
+    {
+        QwtDial::keyPressEvent( kev );
+    }
+}
diff --git a/qwt/qwt_compass.h b/qwt/qwt_compass.h
new file mode 100644
index 0000000..b9a3c95
--- /dev/null
+++ b/qwt/qwt_compass.h
@@ -0,0 +1,83 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COMPASS_H
+#define QWT_COMPASS_H 1
+
+#include "qwt_global.h"
+#include "qwt_dial.h"
+#include "qwt_round_scale_draw.h"
+#include <qstring.h>
+#include <qmap.h>
+
+class QwtCompassRose;
+
+/*!
+  \brief A special scale draw made for QwtCompass
+
+  QwtCompassScaleDraw maps values to strings using
+  a special map, that can be modified by the application
+
+  The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
+
+  \sa QwtCompass
+*/
+class QWT_EXPORT QwtCompassScaleDraw: public QwtRoundScaleDraw
+{
+public:
+    explicit QwtCompassScaleDraw();
+    explicit QwtCompassScaleDraw( const QMap<double, QString> &map );
+
+    void setLabelMap( const QMap<double, QString> &map );
+    QMap<double, QString> labelMap() const;
+
+    virtual QwtText label( double value ) const;
+
+private:
+    QMap<double, QString> d_labelMap;
+};
+
+/*!
+  \brief A Compass Widget
+
+  QwtCompass is a widget to display and enter directions. It consists
+  of a scale, an optional needle and rose.
+
+  \image html dials1.png
+
+  \note The examples/dials example shows how to use QwtCompass.
+*/
+
+class QWT_EXPORT QwtCompass: public QwtDial
+{
+    Q_OBJECT
+
+public:
+    explicit QwtCompass( QWidget* parent = NULL );
+    virtual ~QwtCompass();
+
+    void setRose( QwtCompassRose *rose );
+    const QwtCompassRose *rose() const;
+    QwtCompassRose *rose();
+
+protected:
+    virtual void drawRose( QPainter *, const QPointF &center,
+        double radius, double north, QPalette::ColorGroup ) const;
+
+    virtual void drawScaleContents( QPainter *,
+        const QPointF &center, double radius ) const;
+
+    virtual void keyPressEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_compass_rose.cpp b/qwt/qwt_compass_rose.cpp
new file mode 100644
index 0000000..21a35f2
--- /dev/null
+++ b/qwt/qwt_compass_rose.cpp
@@ -0,0 +1,269 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_compass_rose.h"
+#include "qwt_point_polar.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+
+static QPointF qwtIntersection( 
+    QPointF p11, QPointF p12, QPointF p21, QPointF p22 )
+{
+    const QLineF line1( p11, p12 );
+    const QLineF line2( p21, p22 );
+
+    QPointF pos;
+    if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection )
+        return QPointF();
+
+    return pos;
+}
+
+class QwtSimpleCompassRose::PrivateData
+{
+public:
+    PrivateData():
+        width( 0.2 ),
+        numThorns( 8 ),
+        numThornLevels( -1 ),
+        shrinkFactor( 0.9 )
+    {
+    }
+
+    double width;
+    int numThorns;
+    int numThornLevels;
+    double shrinkFactor;
+};
+
+/*!
+   Constructor
+
+   \param numThorns Number of thorns
+   \param numThornLevels Number of thorn levels
+*/
+QwtSimpleCompassRose::QwtSimpleCompassRose(
+    int numThorns, int numThornLevels )
+{
+    d_data = new PrivateData();
+    d_data->numThorns = numThorns;
+    d_data->numThornLevels = numThornLevels;
+
+    const QColor dark( 128, 128, 255 );
+    const QColor light( 192, 255, 255 );
+
+    QPalette palette;
+    palette.setColor( QPalette::Dark, dark );
+    palette.setColor( QPalette::Light, light );
+
+    setPalette( palette );
+}
+
+//! Destructor
+QwtSimpleCompassRose::~QwtSimpleCompassRose()
+{
+    delete d_data;
+}
+
+/*!
+  Set the Factor how to shrink the thorns with each level
+  The default value is 0.9.
+
+  \param factor Shrink factor
+  \sa shrinkFactor()
+*/
+void QwtSimpleCompassRose::setShrinkFactor( double factor )
+{
+    d_data->shrinkFactor = factor;
+}
+
+/*!
+  \return Factor how to shrink the thorns with each level
+  \sa setShrinkFactor()
+*/
+double QwtSimpleCompassRose::shrinkFactor() const
+{
+    return d_data->shrinkFactor;
+}
+
+/*!
+   Draw the rose
+
+   \param painter Painter
+   \param center Center point
+   \param radius Radius of the rose
+   \param north Position
+   \param cg Color group
+*/
+void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF &center,
+    double radius, double north, QPalette::ColorGroup cg ) const
+{
+    QPalette pal = palette();
+    pal.setCurrentColorGroup( cg );
+
+    drawRose( painter, pal, center, radius, north, d_data->width,
+        d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor );
+}
+
+/*!
+   Draw the rose
+
+   \param painter Painter
+   \param palette Palette
+   \param center Center of the rose
+   \param radius Radius of the rose
+   \param north Position pointing to north
+   \param width Width of the rose
+   \param numThorns Number of thorns
+   \param numThornLevels Number of thorn levels
+   \param shrinkFactor Factor to shrink the thorns with each level
+*/
+void QwtSimpleCompassRose::drawRose(
+    QPainter *painter,
+    const QPalette &palette,
+    const QPointF &center, double radius, double north, double width,
+    int numThorns, int numThornLevels, double shrinkFactor )
+{
+    if ( numThorns < 4 )
+        numThorns = 4;
+
+    if ( numThorns % 4 )
+        numThorns += 4 - numThorns % 4;
+
+    if ( numThornLevels <= 0 )
+        numThornLevels = numThorns / 4;
+
+    if ( shrinkFactor >= 1.0 )
+        shrinkFactor = 1.0;
+
+    if ( shrinkFactor <= 0.5 )
+        shrinkFactor = 0.5;
+
+    painter->save();
+
+    painter->setPen( Qt::NoPen );
+
+    for ( int j = 1; j <= numThornLevels; j++ )
+    {
+        double step =  qPow( 2.0, j ) * M_PI / numThorns;
+        if ( step > M_PI_2 )
+            break;
+
+        double r = radius;
+        for ( int k = 0; k < 3; k++ )
+        {
+            if ( j + k < numThornLevels )
+                r *= shrinkFactor;
+        }
+
+        double leafWidth = r * width;
+        if ( 2.0 * M_PI / step > 32 )
+            leafWidth = 16;
+
+        const double origin = qwtRadians( north );
+        for ( double angle = origin;
+            angle < 2.0 * M_PI + origin; angle += step )
+        {
+            const QPointF p = qwtPolar2Pos( center, r, angle );
+            const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 );
+            const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 );
+            const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 );
+            const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 );
+
+            QPainterPath darkPath;
+            darkPath.moveTo( center );
+            darkPath.lineTo( p );
+            darkPath.lineTo( qwtIntersection( center, p3, p1, p ) );
+
+            painter->setBrush( palette.brush( QPalette::Dark ) );
+            painter->drawPath( darkPath );
+
+            QPainterPath lightPath;
+            lightPath.moveTo( center );
+            lightPath.lineTo( p );
+            lightPath.lineTo( qwtIntersection( center, p4, p2, p ) );
+
+            painter->setBrush( palette.brush( QPalette::Light ) );
+            painter->drawPath( lightPath );
+        }
+    }
+    painter->restore();
+}
+
+/*!
+   Set the width of the rose heads. Lower value make thinner heads.
+   The range is limited from 0.03 to 0.4.
+
+   \param width Width
+*/
+void QwtSimpleCompassRose::setWidth( double width )
+{
+    d_data->width = width;
+    if ( d_data->width < 0.03 )
+        d_data->width = 0.03;
+
+    if ( d_data->width > 0.4 )
+        d_data->width = 0.4;
+}
+
+/*! 
+  \return Width of the rose
+  \sa setWidth()
+ */
+double QwtSimpleCompassRose::width() const
+{
+    return d_data->width;
+}
+
+/*!
+  Set the number of thorns on one level
+  The number is aligned to a multiple of 4, with a minimum of 4
+
+  \param numThorns Number of thorns
+  \sa numThorns(), setNumThornLevels()
+*/
+void QwtSimpleCompassRose::setNumThorns( int numThorns )
+{
+    if ( numThorns < 4 )
+        numThorns = 4;
+
+    if ( numThorns % 4 )
+        numThorns += 4 - numThorns % 4;
+
+    d_data->numThorns = numThorns;
+}
+
+/*!
+   \return Number of thorns
+   \sa setNumThorns(), setNumThornLevels()
+*/
+int QwtSimpleCompassRose::numThorns() const
+{
+    return d_data->numThorns;
+}
+
+/*!
+  Set the of thorns levels
+
+  \param numThornLevels Number of thorns levels
+  \sa setNumThorns(), numThornLevels()
+*/
+void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels )
+{
+    d_data->numThornLevels = numThornLevels;
+}
+
+/*!
+   \return Number of thorn levels
+   \sa setNumThorns(), setNumThornLevels()
+*/
+int QwtSimpleCompassRose::numThornLevels() const
+{
+    return d_data->numThornLevels;
+}
diff --git a/qwt/qwt_compass_rose.h b/qwt/qwt_compass_rose.h
new file mode 100644
index 0000000..9b715df
--- /dev/null
+++ b/qwt/qwt_compass_rose.h
@@ -0,0 +1,89 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COMPASS_ROSE_H
+#define QWT_COMPASS_ROSE_H 1
+
+#include "qwt_global.h"
+#include <qpalette.h>
+
+class QPainter;
+
+/*!
+  \brief Abstract base class for a compass rose
+*/
+class QWT_EXPORT QwtCompassRose
+{
+public:
+    //! Destructor
+    virtual ~QwtCompassRose() {};
+
+    //! Assign a palette
+    virtual void setPalette( const QPalette &p )
+    {
+        d_palette = p;
+    }
+
+    //! \return Current palette
+    const QPalette &palette() const
+    {
+        return d_palette;
+    }
+
+    /*!
+        Draw the rose
+
+        \param painter Painter
+        \param center Center point
+        \param radius Radius of the rose
+        \param north Position
+        \param colorGroup Color group
+     */
+    virtual void draw( QPainter *painter, 
+        const QPointF &center, double radius, double north,
+        QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0;
+
+private:
+    QPalette d_palette;
+};
+
+/*!
+  \brief A simple rose for QwtCompass
+*/
+class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose
+{
+public:
+    QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 );
+    virtual ~QwtSimpleCompassRose();
+
+    void setWidth( double w );
+    double width() const;
+
+    void setNumThorns( int count );
+    int numThorns() const;
+
+    void setNumThornLevels( int count );
+    int numThornLevels() const;
+
+    void setShrinkFactor( double factor );
+    double shrinkFactor() const;
+
+    virtual void draw( QPainter *, const QPointF &center, double radius,
+        double north, QPalette::ColorGroup = QPalette::Active ) const;
+
+    static void drawRose( QPainter *, const QPalette &,
+        const QPointF &center, double radius, double origin, double width,
+        int numThorns, int numThornLevels, double shrinkFactor );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/qwt/qwt_compat.h b/qwt/qwt_compat.h
new file mode 100644
index 0000000..c97cf6b
--- /dev/null
+++ b/qwt/qwt_compat.h
@@ -0,0 +1,42 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_COMPAT_H_
+#define _QWT_COMPAT_H_
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include "qwt_point_3d.h"
+#include <qlist.h>
+#include <qvector.h>
+#include <qpoint.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qpolygon.h>
+
+// A couple of definition for Qwt5 compatibility
+
+#define qwtMax qMax
+#define qwtMin qMin
+#define qwtAbs qAbs
+#define qwtRound qRound
+
+#define QwtArray QVector
+
+typedef QList<double> QwtValueList;
+typedef QPointF QwtDoublePoint;
+typedef QSizeF QwtDoubleSize;
+typedef QRectF QwtDoubleRect;
+
+typedef QPolygon QwtPolygon;
+typedef QPolygonF QwtPolygonF;
+typedef QwtInterval QwtDoubleInterval;
+typedef QwtPoint3D QwtDoublePoint3D;
+
+#endif
diff --git a/qwt/qwt_counter.cpp b/qwt/qwt_counter.cpp
new file mode 100644
index 0000000..192de11
--- /dev/null
+++ b/qwt/qwt_counter.cpp
@@ -0,0 +1,776 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_arrow_button.h"
+#include "qwt_math.h"
+#include "qwt_counter.h"
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qvalidator.h>
+#include <qevent.h>
+#include <qstyle.h>
+
+class QwtCounter::PrivateData
+{
+public:
+    PrivateData():
+        minimum( 0.0 ),
+        maximum( 0.0 ),
+        singleStep( 1.0 ),
+        isValid( false ),
+        value( 0.0 ),
+        wrapping( false )
+    {
+        increment[Button1] = 1;
+        increment[Button2] = 10;
+        increment[Button3] = 100;
+    }
+
+    QwtArrowButton *buttonDown[ButtonCnt];
+    QwtArrowButton *buttonUp[ButtonCnt];
+    QLineEdit *valueEdit;
+
+    int increment[ButtonCnt];
+    int numButtons;
+
+    double minimum;
+    double maximum;
+    double singleStep;
+
+    bool isValid;
+    double value;
+
+    bool wrapping;
+};
+
+/*!
+  The counter is initialized with a range is set to [0.0, 1.0] with 
+  0.01 as single step size. The value is invalid.
+
+  The default number of buttons is set to 2. The default increments are:
+  \li Button 1: 1 step
+  \li Button 2: 10 steps
+  \li Button 3: 100 steps
+
+  \param parent
+ */
+QwtCounter::QwtCounter( QWidget *parent ):
+    QWidget( parent )
+{
+    initCounter();
+}
+
+void QwtCounter::initCounter()
+{
+    d_data = new PrivateData;
+
+    QHBoxLayout *layout = new QHBoxLayout( this );
+    layout->setSpacing( 0 );
+    layout->setMargin( 0 );
+
+    for ( int i = ButtonCnt - 1; i >= 0; i-- )
+    {
+        QwtArrowButton *btn =
+            new QwtArrowButton( i + 1, Qt::DownArrow, this );
+        btn->setFocusPolicy( Qt::NoFocus );
+        btn->installEventFilter( this );
+        layout->addWidget( btn );
+
+        connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
+        connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
+
+        d_data->buttonDown[i] = btn;
+    }
+
+    d_data->valueEdit = new QLineEdit( this );
+    d_data->valueEdit->setReadOnly( false );
+    d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) );
+    layout->addWidget( d_data->valueEdit );
+
+    connect( d_data->valueEdit, SIGNAL( editingFinished() ),
+         SLOT( textChanged() ) );
+
+    layout->setStretchFactor( d_data->valueEdit, 10 );
+
+    for ( int i = 0; i < ButtonCnt; i++ )
+    {
+        QwtArrowButton *btn =
+            new QwtArrowButton( i + 1, Qt::UpArrow, this );
+        btn->setFocusPolicy( Qt::NoFocus );
+        btn->installEventFilter( this );
+        layout->addWidget( btn );
+
+        connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
+        connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
+
+        d_data->buttonUp[i] = btn;
+    }
+
+    setNumButtons( 2 );
+    setRange( 0.0, 1.0 );
+    setSingleStep( 0.001 );
+    setValue( 0.0 );
+
+    setSizePolicy(
+        QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
+
+    setFocusProxy( d_data->valueEdit );
+    setFocusPolicy( Qt::StrongFocus );
+}
+
+//! Destructor
+QwtCounter::~QwtCounter()
+{
+    delete d_data;
+}
+
+/*! 
+  Set the counter to be in valid/invalid state
+
+  When the counter is set to invalid, no numbers are displayed and
+  the buttons are disabled.
+
+  \param on If true the counter will be set as valid 
+
+  \sa setValue(), isValid()
+*/
+void QwtCounter::setValid( bool on )
+{
+    if ( on != d_data->isValid )
+    {
+        d_data->isValid = on;
+
+        updateButtons();
+
+        if ( d_data->isValid )
+        {
+            showNumber( value() );
+            Q_EMIT valueChanged( value() );
+        }
+        else
+        {
+            d_data->valueEdit->setText( QString::null );
+        }
+    }   
+}   
+
+/*! 
+  \return True, if the value is valid
+  \sa setValid(), setValue()
+ */
+bool QwtCounter::isValid() const
+{
+    return d_data->isValid;
+}   
+
+/*!
+  \brief Allow/disallow the user to manually edit the value
+
+  \param on True disable editing
+  \sa isReadOnly()
+*/
+void QwtCounter::setReadOnly( bool on )
+{
+    d_data->valueEdit->setReadOnly( on );
+}
+
+/*! 
+   \return True, when the line line edit is read only. (default is no)
+  \sa setReadOnly()
+ */
+bool QwtCounter::isReadOnly() const
+{
+    return d_data->valueEdit->isReadOnly();
+}
+
+/*!
+  \brief Set a new value without adjusting to the step raster
+
+  The state of the counter is set to be valid.
+
+  \param value New value
+
+  \sa isValid(), value(), valueChanged()
+  \warning The value is clipped when it lies outside the range.
+*/
+
+void QwtCounter::setValue( double value )
+{
+    const double vmin = qMin( d_data->minimum, d_data->maximum );
+    const double vmax = qMax( d_data->minimum, d_data->maximum );
+
+    value = qBound( vmin, value, vmax );
+
+    if ( !d_data->isValid || value != d_data->value )
+    {
+        d_data->isValid = true;
+        d_data->value = value;
+
+        showNumber( value );
+        updateButtons();
+
+        Q_EMIT valueChanged( value );
+    }
+}
+
+/*!
+  \return Current value of the counter
+  \sa setValue(), valueChanged()
+ */
+double QwtCounter::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief Set the minimum and maximum values
+
+  The maximum is adjusted if necessary to ensure that the range remains valid.
+  The value might be modified to be inside of the range.
+
+  \param min Minimum value
+  \param max Maximum value
+
+  \sa minimum(), maximum()
+ */
+void QwtCounter::setRange( double min, double max )
+{
+    max = qMax( min, max );
+
+    if ( d_data->maximum == max && d_data->minimum == min )
+        return;
+
+    d_data->minimum = min;
+    d_data->maximum = max;
+
+    setSingleStep( singleStep() );
+
+    const double value = qBound( min, d_data->value, max );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+
+        if ( d_data->isValid )
+        {
+            showNumber( value );
+            Q_EMIT valueChanged( value );
+        }
+    }
+
+    updateButtons();
+}
+
+/*!
+  Set the minimum value of the range
+
+  \param value Minimum value
+  \sa setRange(), setMaximum(), minimum()
+
+  \note The maximum is adjusted if necessary to ensure that the range remains valid.
+*/
+void QwtCounter::setMinimum( double value )
+{
+    setRange( value, maximum() );
+}
+
+/*!
+  \return The minimum of the range
+  \sa setRange(), setMinimum(), maximum()
+*/
+double QwtCounter::minimum() const
+{
+    return d_data->minimum;
+}
+
+/*!
+  Set the maximum value of the range
+
+  \param value Maximum value
+  \sa setRange(), setMinimum(), maximum()
+*/
+void QwtCounter::setMaximum( double value )
+{
+    setRange( minimum(), value );
+}
+
+/*!
+  \return The maximum of the range
+  \sa setRange(), setMaximum(), minimum()
+*/
+double QwtCounter::maximum() const
+{
+    return d_data->maximum;
+}
+
+/*!
+  \brief Set the step size of the counter
+
+  A value <= 0.0 disables stepping
+
+  \param stepSize Single step size
+  \sa singleStep()
+*/
+void QwtCounter::setSingleStep( double stepSize )
+{
+    d_data->singleStep = qMax( stepSize, 0.0 );
+}
+
+/*!
+  \return Single step size
+  \sa setSingleStep()
+ */
+double QwtCounter::singleStep() const
+{
+    return d_data->singleStep;
+}
+
+/*!
+  \brief En/Disable wrapping
+
+  If wrapping is true stepping up from maximum() value will take 
+  you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+ */
+void QwtCounter::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */
+bool QwtCounter::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  Specify the number of buttons on each side of the label
+
+  \param numButtons Number of buttons
+  \sa numButtons()
+*/
+void QwtCounter::setNumButtons( int numButtons )
+{
+    if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt )
+        return;
+
+    for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+    {
+        if ( i < numButtons )
+        {
+            d_data->buttonDown[i]->show();
+            d_data->buttonUp[i]->show();
+        }
+        else
+        {
+            d_data->buttonDown[i]->hide();
+            d_data->buttonUp[i]->hide();
+        }
+    }
+
+    d_data->numButtons = numButtons;
+}
+
+/*!
+  \return The number of buttons on each side of the widget.
+  \sa setNumButtons()
+*/
+int QwtCounter::numButtons() const
+{
+    return d_data->numButtons;
+}
+
+/*!
+  Specify the number of steps by which the value
+  is incremented or decremented when a specified button
+  is pushed.
+
+  \param button Button index
+  \param numSteps Number of steps
+
+  \sa incSteps()
+*/
+void QwtCounter::setIncSteps( QwtCounter::Button button, int numSteps )
+{
+    if ( button >= 0 && button < QwtCounter::ButtonCnt )
+        d_data->increment[ button ] = numSteps;
+}
+
+/*!
+  \return The number of steps by which a specified button increments the value
+          or 0 if the button is invalid.
+  \param button Button index
+
+  \sa setIncSteps()
+*/
+int QwtCounter::incSteps( QwtCounter::Button button ) const
+{
+    if ( button >= 0 && button < QwtCounter::ButtonCnt )
+        return d_data->increment[ button ];
+
+    return 0;
+}
+
+
+/*!
+  Set the number of increment steps for button 1
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton1( int nSteps )
+{
+    setIncSteps( QwtCounter::Button1, nSteps );
+}
+
+//! returns the number of increment steps for button 1
+int QwtCounter::stepButton1() const
+{
+    return incSteps( QwtCounter::Button1 );
+}
+
+/*!
+  Set the number of increment steps for button 2
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton2( int nSteps )
+{
+    setIncSteps( QwtCounter::Button2, nSteps );
+}
+
+//! returns the number of increment steps for button 2
+int QwtCounter::stepButton2() const
+{
+    return incSteps( QwtCounter::Button2 );
+}
+
+/*!
+  Set the number of increment steps for button 3
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton3( int nSteps )
+{
+    setIncSteps( QwtCounter::Button3, nSteps );
+}
+
+//! returns the number of increment steps for button 3
+int QwtCounter::stepButton3() const
+{
+    return incSteps( QwtCounter::Button3 );
+}
+
+//! Set from lineedit
+void QwtCounter::textChanged()
+{
+    bool converted = false;
+
+    const double value = d_data->valueEdit->text().toDouble( &converted );
+    if ( converted )
+        setValue( value );
+}
+
+/*!
+   Handle QEvent::PolishRequest events
+   \param event Event
+   \return see QWidget::event()
+*/
+bool QwtCounter::event( QEvent *event )
+{
+    if ( event->type() == QEvent::PolishRequest )
+    {
+        const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8;
+        for ( int i = 0; i < ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setMinimumWidth( w );
+            d_data->buttonUp[i]->setMinimumWidth( w );
+        }
+    }
+
+    return QWidget::event( event );
+}
+
+/*!
+  Handle key events
+
+  - Ctrl + Qt::Key_Home\n
+    Step to minimum()
+  - Ctrl + Qt::Key_End\n
+    Step to maximum()
+  - Qt::Key_Up\n
+    Increment by incSteps(QwtCounter::Button1)
+  - Qt::Key_Down\n
+    Decrement by incSteps(QwtCounter::Button1)
+  - Qt::Key_PageUp\n
+    Increment by incSteps(QwtCounter::Button2)
+  - Qt::Key_PageDown\n
+    Decrement by incSteps(QwtCounter::Button2)
+  - Shift + Qt::Key_PageUp\n
+    Increment by incSteps(QwtCounter::Button3)
+  - Shift + Qt::Key_PageDown\n
+    Decrement by incSteps(QwtCounter::Button3)
+
+  \param event Key event
+*/
+void QwtCounter::keyPressEvent ( QKeyEvent *event )
+{
+    bool accepted = true;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Home:
+        {
+            if ( event->modifiers() & Qt::ControlModifier )
+                setValue( minimum() );
+            else
+                accepted = false;
+            break;
+        }
+        case Qt::Key_End:
+        {
+            if ( event->modifiers() & Qt::ControlModifier )
+                setValue( maximum() );
+            else
+                accepted = false;
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            incrementValue( d_data->increment[0] );
+            break;
+        }
+        case Qt::Key_Down:
+        {
+            incrementValue( -d_data->increment[0] );
+            break;
+        }
+        case Qt::Key_PageUp:
+        case Qt::Key_PageDown:
+        {
+            int increment = d_data->increment[0];
+            if ( d_data->numButtons >= 2 )
+                increment = d_data->increment[1];
+            if ( d_data->numButtons >= 3 )
+            {
+                if ( event->modifiers() & Qt::ShiftModifier )
+                    increment = d_data->increment[2];
+            }
+            if ( event->key() == Qt::Key_PageDown )
+                increment = -increment;
+            incrementValue( increment );
+            break;
+        }
+        default:
+        {
+            accepted = false;
+        }
+    }
+
+    if ( accepted )
+    {
+        event->accept();
+        return;
+    }
+
+    QWidget::keyPressEvent ( event );
+}
+
+/*!
+  Handle wheel events
+  \param event Wheel event
+*/
+void QwtCounter::wheelEvent( QWheelEvent *event )
+{
+    event->accept();
+
+    if ( d_data->numButtons <= 0 )
+        return;
+
+    int increment = d_data->increment[0];
+    if ( d_data->numButtons >= 2 )
+    {
+        if ( event->modifiers() & Qt::ControlModifier )
+            increment = d_data->increment[1];
+    }
+    if ( d_data->numButtons >= 3 )
+    {
+        if ( event->modifiers() & Qt::ShiftModifier )
+            increment = d_data->increment[2];
+    }
+
+    for ( int i = 0; i < d_data->numButtons; i++ )
+    {
+        if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) ||
+            d_data->buttonUp[i]->geometry().contains( event->pos() ) )
+        {
+            increment = d_data->increment[i];
+        }
+    }
+
+    const int wheel_delta = 120;
+
+#if 1
+    int delta = event->delta();
+    if ( delta >= 2 * wheel_delta )
+        delta /= 2; // Never saw an abs(delta) < 240
+#endif
+
+    incrementValue( delta / wheel_delta * increment );
+}
+
+void QwtCounter::incrementValue( int numSteps )
+{
+    const double min = d_data->minimum;
+    const double max = d_data->maximum;
+    double stepSize = d_data->singleStep;
+
+    if ( !d_data->isValid || min >= max || stepSize <= 0.0 )
+        return;
+
+
+#if 1
+    stepSize = qMax( stepSize, 1.0e-10 * ( max - min ) );
+#endif
+
+    double value = d_data->value + numSteps * stepSize;
+
+    if ( d_data->wrapping )
+    {
+        const double range = max - min;
+
+        if ( value < min )
+        {
+            value += ::ceil( ( min - value ) / range ) * range;
+        }
+        else if ( value > max )
+        {
+            value -= ::ceil( ( value - max ) / range ) * range;
+        }
+    }
+    else
+    {
+        value = qBound( min, value, max );
+    }
+
+    value = min + qRound( ( value - min ) / stepSize ) * stepSize;
+    if ( qFuzzyCompare( value, max ) )
+        value = max;
+
+    if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        value = 0.0;
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        showNumber( d_data->value );
+        updateButtons();
+
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+
+/*!
+  \brief Update buttons according to the current value
+
+  When the QwtCounter under- or over-flows, the focus is set to the smallest
+  up- or down-button and counting is disabled.
+
+  Counting is re-enabled on a button release event (mouse or space bar).
+*/
+void QwtCounter::updateButtons()
+{
+    if ( d_data->isValid )
+    {
+        // 1. save enabled state of the smallest down- and up-button
+        // 2. change enabled state on under- or over-flow
+
+        for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setEnabled( value() > minimum() );
+            d_data->buttonUp[i]->setEnabled( value() < maximum() );
+        }
+    }
+    else
+    {
+        for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setEnabled( false );
+            d_data->buttonUp[i]->setEnabled( false );
+        }
+    }
+}
+/*!
+  Display number string
+
+  \param number Number
+*/
+void QwtCounter::showNumber( double number )
+{
+    QString text;
+    text.setNum( number );
+
+    const int cursorPos = d_data->valueEdit->cursorPosition();
+    d_data->valueEdit->setText( text );
+    d_data->valueEdit->setCursorPosition( cursorPos );
+}
+
+//!  Button clicked
+void QwtCounter::btnClicked()
+{
+    for ( int i = 0; i < ButtonCnt; i++ )
+    {
+        if ( d_data->buttonUp[i] == sender() )
+            incrementValue( d_data->increment[i] );
+
+        if ( d_data->buttonDown[i] == sender() )
+            incrementValue( -d_data->increment[i] );
+    }
+}
+
+//!  Button released
+void QwtCounter::btnReleased()
+{
+    Q_EMIT buttonReleased( value() );
+}
+
+//! A size hint
+QSize QwtCounter::sizeHint() const
+{
+    QString tmp;
+
+    int w = tmp.setNum( minimum() ).length();
+    int w1 = tmp.setNum( maximum() ).length();
+    if ( w1 > w )
+        w = w1;
+    w1 = tmp.setNum( minimum() + singleStep() ).length();
+    if ( w1 > w )
+        w = w1;
+    w1 = tmp.setNum( maximum() - singleStep() ).length();
+    if ( w1 > w )
+        w = w1;
+
+    tmp.fill( '9', w );
+
+    QFontMetrics fm( d_data->valueEdit->font() );
+    w = fm.width( tmp ) + 2;
+    if ( d_data->valueEdit->hasFrame() )
+        w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
+
+    // Now we replace default sizeHint contribution of d_data->valueEdit by
+    // what we really need.
+
+    w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width();
+
+    const int h = qMin( QWidget::sizeHint().height(),
+        d_data->valueEdit->minimumSizeHint().height() );
+    return QSize( w, h );
+}
diff --git a/qwt/qwt_counter.h b/qwt/qwt_counter.h
new file mode 100644
index 0000000..8799edd
--- /dev/null
+++ b/qwt/qwt_counter.h
@@ -0,0 +1,161 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COUNTER_H
+#define QWT_COUNTER_H
+
+#include "qwt_global.h"
+#include <qwidget.h>
+
+/*!
+  \brief The Counter Widget
+
+  A Counter consists of a label displaying a number and
+  one ore more (up to three) push buttons on each side
+  of the label which can be used to increment or decrement
+  the counter's value.
+
+  A counter has a range from a minimum value to a maximum value
+  and a step size. When the wrapping property is set
+  the counter is circular.
+ 
+  The number of steps by which a button increments or decrements the value 
+  can be specified using setIncSteps(). The number of buttons can be 
+  changed with setNumButtons().
+
+  Example:
+\code
+#include <qwt_counter.h>
+
+QwtCounter *counter = new QwtCounter(parent);
+
+counter->setRange(0.0, 100.0);                  // From 0.0 to 100
+counter->setSingleStep( 1.0 );                  // Step size 1.0
+counter->setNumButtons(2);                      // Two buttons each side
+counter->setIncSteps(QwtCounter::Button1, 1);   // Button 1 increments 1 step
+counter->setIncSteps(QwtCounter::Button2, 20);  // Button 2 increments 20 steps
+
+connect(counter, SIGNAL(valueChanged(double)), myClass, SLOT(newValue(double)));
+\endcode
+ */
+
+class QWT_EXPORT QwtCounter : public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+    Q_PROPERTY( double minimum READ minimum WRITE setMinimum )
+    Q_PROPERTY( double maximum READ maximum WRITE setMaximum )
+    Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep )
+
+    Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons )
+    Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 )
+    Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 )
+    Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 )
+
+    Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+
+public:
+    //! Button index
+    enum Button
+    {
+        //! Button intended for minor steps
+        Button1,
+
+        //! Button intended for medium steps
+        Button2,
+
+        //! Button intended for large steps
+        Button3,
+
+        //! Number of buttons
+        ButtonCnt
+    };
+
+    explicit QwtCounter( QWidget *parent = NULL );
+    virtual ~QwtCounter();
+
+    void setValid( bool );
+    bool isValid() const;
+
+    void setWrapping( bool );
+    bool wrapping() const;
+
+    bool isReadOnly() const;
+    void setReadOnly( bool );
+
+    void setNumButtons( int n );
+    int numButtons() const;
+
+    void setIncSteps( QwtCounter::Button btn, int nSteps );
+    int incSteps( QwtCounter::Button btn ) const;
+
+    virtual QSize sizeHint() const;
+
+    double singleStep() const;
+    void setSingleStep( double s );
+
+    void setRange( double min, double max );
+    
+    double minimum() const;
+    void setMinimum( double min );
+
+    double maximum() const;
+    void setMaximum( double max );
+
+    void setStepButton1( int nSteps );
+    int stepButton1() const;
+
+    void setStepButton2( int nSteps );
+    int stepButton2() const;
+
+    void setStepButton3( int nSteps );
+    int stepButton3() const;
+
+    double value() const;
+
+public Q_SLOTS:
+    void setValue( double );
+
+
+Q_SIGNALS:
+    /*!
+        This signal is emitted when a button has been released
+        \param value The new value
+    */
+    void buttonReleased ( double value );
+
+    /*!
+        This signal is emitted when the counter's value has changed
+        \param value The new value
+    */
+    void valueChanged ( double value );
+
+protected:
+    virtual bool event( QEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+
+private Q_SLOTS:
+    void btnReleased();
+    void btnClicked();
+    void textChanged();
+
+private:
+    void incrementValue( int numSteps );
+    void initCounter();
+    void updateButtons();
+    void showNumber( double );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_curve_fitter.cpp b/qwt/qwt_curve_fitter.cpp
new file mode 100644
index 0000000..5f09d5d
--- /dev/null
+++ b/qwt/qwt_curve_fitter.cpp
@@ -0,0 +1,453 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_curve_fitter.h"
+#include "qwt_math.h"
+#include "qwt_spline.h"
+#include <qstack.h>
+#include <qvector.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#endif
+
+//! Constructor
+QwtCurveFitter::QwtCurveFitter()
+{
+}
+
+//! Destructor
+QwtCurveFitter::~QwtCurveFitter()
+{
+}
+
+class QwtSplineCurveFitter::PrivateData
+{
+public:
+    PrivateData():
+        fitMode( QwtSplineCurveFitter::Auto ),
+        splineSize( 250 )
+    {
+    }
+
+    QwtSpline spline;
+    QwtSplineCurveFitter::FitMode fitMode;
+    int splineSize;
+};
+
+//! Constructor
+QwtSplineCurveFitter::QwtSplineCurveFitter()
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtSplineCurveFitter::~QwtSplineCurveFitter()
+{
+    delete d_data;
+}
+
+/*!
+  Select the algorithm used for building the spline
+
+  \param mode Mode representing a spline algorithm
+  \sa fitMode()
+*/
+void QwtSplineCurveFitter::setFitMode( FitMode mode )
+{
+    d_data->fitMode = mode;
+}
+
+/*!
+  \return Mode representing a spline algorithm
+  \sa setFitMode()
+*/
+QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const
+{
+    return d_data->fitMode;
+}
+
+/*!
+  Assign a spline
+
+  \param spline Spline
+  \sa spline()
+*/
+void QwtSplineCurveFitter::setSpline( const QwtSpline &spline )
+{
+    d_data->spline = spline;
+    d_data->spline.reset();
+}
+
+/*!
+  \return Spline
+  \sa setSpline()
+*/
+const QwtSpline &QwtSplineCurveFitter::spline() const
+{
+    return d_data->spline;
+}
+
+/*!
+  \return Spline
+  \sa setSpline()
+*/
+QwtSpline &QwtSplineCurveFitter::spline()
+{
+    return d_data->spline;
+}
+
+/*!
+   Assign a spline size ( has to be at least 10 points )
+
+   \param splineSize Spline size
+   \sa splineSize()
+*/
+void QwtSplineCurveFitter::setSplineSize( int splineSize )
+{
+    d_data->splineSize = qMax( splineSize, 10 );
+}
+
+/*!
+  \return Spline size
+  \sa setSplineSize()
+*/
+int QwtSplineCurveFitter::splineSize() const
+{
+    return d_data->splineSize;
+}
+
+/*!
+  Find a curve which has the best fit to a series of data points
+
+  \param points Series of data points
+  \return Curve points
+*/
+QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const
+{
+    const int size = points.size();
+    if ( size <= 2 )
+        return points;
+
+    FitMode fitMode = d_data->fitMode;
+    if ( fitMode == Auto )
+    {
+        fitMode = Spline;
+
+        const QPointF *p = points.data();
+        for ( int i = 1; i < size; i++ )
+        {
+            if ( p[i].x() <= p[i-1].x() )
+            {
+                fitMode = ParametricSpline;
+                break;
+            }
+        };
+    }
+
+    if ( fitMode == ParametricSpline )
+        return fitParametric( points );
+    else
+        return fitSpline( points );
+}
+
+QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const
+{
+    d_data->spline.setPoints( points );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    QPolygonF fittedPoints( d_data->splineSize );
+
+    const double x1 = points[0].x();
+    const double x2 = points[int( points.size() - 1 )].x();
+    const double dx = x2 - x1;
+    const double delta = dx / ( d_data->splineSize - 1 );
+
+    for ( int i = 0; i < d_data->splineSize; i++ )
+    {
+        QPointF &p = fittedPoints[i];
+
+        const double v = x1 + i * delta;
+        const double sv = d_data->spline.value( v );
+
+        p.setX( v );
+        p.setY( sv );
+    }
+    d_data->spline.reset();
+
+    return fittedPoints;
+}
+
+QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const
+{
+    int i;
+    const int size = points.size();
+
+    QPolygonF fittedPoints( d_data->splineSize );
+    QPolygonF splinePointsX( size );
+    QPolygonF splinePointsY( size );
+
+    const QPointF *p = points.data();
+    QPointF *spX = splinePointsX.data();
+    QPointF *spY = splinePointsY.data();
+
+    double param = 0.0;
+    for ( i = 0; i < size; i++ )
+    {
+        const double x = p[i].x();
+        const double y = p[i].y();
+        if ( i > 0 )
+        {
+            const double delta = qSqrt( qwtSqr( x - spX[i-1].y() )
+                      + qwtSqr( y - spY[i-1].y() ) );
+            param += qMax( delta, 1.0 );
+        }
+        spX[i].setX( param );
+        spX[i].setY( x );
+        spY[i].setX( param );
+        spY[i].setY( y );
+    }
+
+    d_data->spline.setPoints( splinePointsX );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    const double deltaX =
+        splinePointsX[size - 1].x() / ( d_data->splineSize - 1 );
+    for ( i = 0; i < d_data->splineSize; i++ )
+    {
+        const double dtmp = i * deltaX;
+        fittedPoints[i].setX( d_data->spline.value( dtmp ) );
+    }
+
+    d_data->spline.setPoints( splinePointsY );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    const double deltaY =
+        splinePointsY[size - 1].x() / ( d_data->splineSize - 1 );
+    for ( i = 0; i < d_data->splineSize; i++ )
+    {
+        const double dtmp = i * deltaY;
+        fittedPoints[i].setY( d_data->spline.value( dtmp ) );
+    }
+
+    return fittedPoints;
+}
+
+class QwtWeedingCurveFitter::PrivateData
+{
+public:
+    PrivateData():
+        tolerance( 1.0 ),
+        chunkSize( 0 )
+    {
+    }
+
+    double tolerance;
+    uint chunkSize;
+};
+
+class QwtWeedingCurveFitter::Line
+{
+public:
+    Line( int i1 = 0, int i2 = 0 ):
+        from( i1 ),
+        to( i2 )
+    {
+    }
+
+    int from;
+    int to;
+};
+
+/*!
+   Constructor
+
+   \param tolerance Tolerance
+   \sa setTolerance(), tolerance()
+*/
+QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance )
+{
+    d_data = new PrivateData;
+    setTolerance( tolerance );
+}
+
+//! Destructor
+QwtWeedingCurveFitter::~QwtWeedingCurveFitter()
+{
+    delete d_data;
+}
+
+/*!
+ Assign the tolerance
+
+ The tolerance is the maximum distance, that is acceptable
+ between the original curve and the smoothed curve.
+
+ Increasing the tolerance will reduce the number of the
+ resulting points.
+
+ \param tolerance Tolerance
+
+ \sa tolerance()
+*/
+void QwtWeedingCurveFitter::setTolerance( double tolerance )
+{
+    d_data->tolerance = qMax( tolerance, 0.0 );
+}
+
+/*!
+  \return Tolerance
+  \sa setTolerance()
+*/
+double QwtWeedingCurveFitter::tolerance() const
+{
+    return d_data->tolerance;
+}
+
+/*!
+ Limit the number of points passed to a run of the algorithm
+
+ The runtime of the Douglas Peucker algorithm increases non linear
+ with the number of points. For a chunk size > 0 the polygon
+ is split into pieces passed to the algorithm one by one.
+
+ \param numPoints Maximum for the number of points passed to the algorithm
+
+ \sa chunkSize()
+*/
+void QwtWeedingCurveFitter::setChunkSize( uint numPoints )
+{
+    if ( numPoints > 0 )
+        numPoints = qMax( numPoints, 3U );
+
+    d_data->chunkSize = numPoints;
+}
+
+/*!
+  
+  \return Maximum for the number of points passed to a run 
+          of the algorithm - or 0, when unlimited
+  \sa setChunkSize()
+*/
+uint QwtWeedingCurveFitter::chunkSize() const
+{
+    return d_data->chunkSize;
+}
+
+/*!
+  \param points Series of data points
+  \return Curve points
+*/
+QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const
+{
+    QPolygonF fittedPoints;
+
+    if ( d_data->chunkSize == 0 )
+    {
+        fittedPoints = simplify( points );
+    }
+    else
+    {
+        for ( int i = 0; i < points.size(); i += d_data->chunkSize )
+        {
+            const QPolygonF p = points.mid( i, d_data->chunkSize );
+            fittedPoints += simplify( p );
+        }
+    }
+
+    return fittedPoints;
+}
+
+QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const
+{
+    const double toleranceSqr = d_data->tolerance * d_data->tolerance;
+
+    QStack<Line> stack;
+    stack.reserve( 500 );
+
+    const QPointF *p = points.data();
+    const int nPoints = points.size();
+
+    QVector<bool> usePoint( nPoints, false );
+
+    stack.push( Line( 0, nPoints - 1 ) );
+
+    while ( !stack.isEmpty() )
+    {
+        const Line r = stack.pop();
+
+        // initialize line segment
+        const double vecX = p[r.to].x() - p[r.from].x();
+        const double vecY = p[r.to].y() - p[r.from].y();
+
+        const double vecLength = qSqrt( vecX * vecX + vecY * vecY );
+
+        const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0;
+        const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0;
+
+        double maxDistSqr = 0.0;
+        int nVertexIndexMaxDistance = r.from + 1;
+        for ( int i = r.from + 1; i < r.to; i++ )
+        {
+            //compare to anchor
+            const double fromVecX = p[i].x() - p[r.from].x();
+            const double fromVecY = p[i].y() - p[r.from].y();
+
+            double distToSegmentSqr;
+            if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 )
+            {
+                distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY;
+            }
+            else
+            {
+                const double toVecX = p[i].x() - p[r.to].x();
+                const double toVecY = p[i].y() - p[r.to].y();
+                const double toVecLength = toVecX * toVecX + toVecY * toVecY;
+
+                const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY );
+                if ( s < 0.0 )
+                {
+                    distToSegmentSqr = toVecLength;
+                }
+                else
+                {
+                    distToSegmentSqr = qFabs( toVecLength - s * s );
+                }
+            }
+
+            if ( maxDistSqr < distToSegmentSqr )
+            {
+                maxDistSqr = distToSegmentSqr;
+                nVertexIndexMaxDistance = i;
+            }
+        }
+        if ( maxDistSqr <= toleranceSqr )
+        {
+            usePoint[r.from] = true;
+            usePoint[r.to] = true;
+        }
+        else
+        {
+            stack.push( Line( r.from, nVertexIndexMaxDistance ) );
+            stack.push( Line( nVertexIndexMaxDistance, r.to ) );
+        }
+    }
+
+    QPolygonF stripped;
+    for ( int i = 0; i < nPoints; i++ )
+    {
+        if ( usePoint[i] )
+            stripped += p[i];
+    }
+
+    return stripped;
+}
diff --git a/qwt/qwt_curve_fitter.h b/qwt/qwt_curve_fitter.h
new file mode 100644
index 0000000..eac376a
--- /dev/null
+++ b/qwt/qwt_curve_fitter.h
@@ -0,0 +1,139 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_CURVE_FITTER_H
+#define QWT_CURVE_FITTER_H
+
+#include "qwt_global.h"
+#include <qpolygon.h>
+#include <qrect.h>
+
+class QwtSpline;
+
+/*!
+  \brief Abstract base class for a curve fitter
+*/
+class QWT_EXPORT QwtCurveFitter
+{
+public:
+    virtual ~QwtCurveFitter();
+
+    /*!
+        Find a curve which has the best fit to a series of data points
+
+        \param polygon Series of data points
+        \return Curve points
+     */
+    virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0;
+
+protected:
+    QwtCurveFitter();
+
+private:
+    QwtCurveFitter( const QwtCurveFitter & );
+    QwtCurveFitter &operator=( const QwtCurveFitter & );
+};
+
+/*!
+  \brief A curve fitter using cubic splines
+*/
+class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter
+{
+public:
+    /*!
+      Spline type
+      The default setting is Auto
+      \sa setFitMode(), FitMode()
+     */
+    enum FitMode
+    {
+        /*!
+          Use the default spline algorithm for polygons with
+          increasing x values ( p[i-1] < p[i] ), otherwise use
+          a parametric spline algorithm.
+         */
+        Auto,
+
+        //! Use a default spline algorithm
+        Spline,
+
+        //! Use a parametric spline algorithm
+        ParametricSpline
+    };
+
+    QwtSplineCurveFitter();
+    virtual ~QwtSplineCurveFitter();
+
+    void setFitMode( FitMode );
+    FitMode fitMode() const;
+
+    void setSpline( const QwtSpline& );
+    const QwtSpline &spline() const;
+    QwtSpline &spline();
+
+    void setSplineSize( int size );
+    int splineSize() const;
+
+    virtual QPolygonF fitCurve( const QPolygonF & ) const;
+
+private:
+    QPolygonF fitSpline( const QPolygonF & ) const;
+    QPolygonF fitParametric( const QPolygonF & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief A curve fitter implementing Douglas and Peucker algorithm
+
+  The purpose of the Douglas and Peucker algorithm is that given a 'curve'
+  composed of line segments to find a curve not too dissimilar but that
+  has fewer points. The algorithm defines 'too dissimilar' based on the
+  maximum distance (tolerance) between the original curve and the
+  smoothed curve.
+
+  The runtime of the algorithm increases non linear ( worst case O( n*n ) )
+  and might be very slow for huge polygons. To avoid performance issues
+  it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm
+  for these smaller parts. The disadvantage of having no interpolation
+  at the borders is for most use cases irrelevant.
+
+  The smoothed curve consists of a subset of the points that defined the
+  original curve.
+
+  In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces
+  the number of points. By adjusting the tolerance parameter according to the
+  axis scales QwtSplineCurveFitter can be used to implement different
+  level of details to speed up painting of curves of many points.
+*/
+class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter
+{
+public:
+    QwtWeedingCurveFitter( double tolerance = 1.0 );
+    virtual ~QwtWeedingCurveFitter();
+
+    void setTolerance( double );
+    double tolerance() const;
+
+    void setChunkSize( uint );
+    uint chunkSize() const;
+
+    virtual QPolygonF fitCurve( const QPolygonF & ) const;
+
+private:
+    virtual QPolygonF simplify( const QPolygonF & ) const;
+
+    class Line;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_date.cpp b/qwt/qwt_date.cpp
new file mode 100644
index 0000000..0cf5ca0
--- /dev/null
+++ b/qwt/qwt_date.cpp
@@ -0,0 +1,654 @@
+#include "qwt_date.h"
+#include <qdebug.h>
+#include <qlocale.h>
+#include <math.h>
+#include <limits>
+#include <limits.h>
+
+#if QT_VERSION >= 0x050000
+
+typedef qint64 QwtJulianDay;
+static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
+static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
+
+#else
+
+// QDate stores the Julian day as unsigned int, but
+// but it is QDate::fromJulianDay( int ). That's why
+// we have the range [ 1, INT_MAX ]
+typedef int QwtJulianDay;
+static const QwtJulianDay minJulianDayD = 1;
+static const QwtJulianDay maxJulianDayD = std::numeric_limits<int>::max();
+
+#endif
+
+static inline Qt::DayOfWeek qwtFirstDayOfWeek()
+{
+#if QT_VERSION >= 0x040800
+    return QLocale().firstDayOfWeek();
+#else
+
+    switch( QLocale().country() )
+    {
+        case QLocale::Maldives:
+            return Qt::Friday;
+
+        case QLocale::Afghanistan:
+        case QLocale::Algeria:
+        case QLocale::Bahrain:
+        case QLocale::Djibouti:
+        case QLocale::Egypt:
+        case QLocale::Eritrea:
+        case QLocale::Ethiopia:
+        case QLocale::Iran:
+        case QLocale::Iraq:
+        case QLocale::Jordan:
+        case QLocale::Kenya:
+        case QLocale::Kuwait:
+        case QLocale::LibyanArabJamahiriya:
+        case QLocale::Morocco:
+        case QLocale::Oman:
+        case QLocale::Qatar:
+        case QLocale::SaudiArabia:
+        case QLocale::Somalia:
+        case QLocale::Sudan:
+        case QLocale::Tunisia:
+        case QLocale::Yemen:
+            return Qt::Saturday;
+
+        case QLocale::AmericanSamoa:
+        case QLocale::Argentina:
+        case QLocale::Azerbaijan:
+        case QLocale::Botswana:
+        case QLocale::Canada:
+        case QLocale::China:
+        case QLocale::FaroeIslands:
+        case QLocale::Georgia:
+        case QLocale::Greenland:
+        case QLocale::Guam:
+        case QLocale::HongKong:
+        case QLocale::Iceland:
+        case QLocale::India:
+        case QLocale::Ireland:
+        case QLocale::Israel:
+        case QLocale::Jamaica:
+        case QLocale::Japan:
+        case QLocale::Kyrgyzstan:
+        case QLocale::Lao:
+        case QLocale::Malta:
+        case QLocale::MarshallIslands:
+        case QLocale::Macau:
+        case QLocale::Mongolia:
+        case QLocale::NewZealand:
+        case QLocale::NorthernMarianaIslands:
+        case QLocale::Pakistan:
+        case QLocale::Philippines:
+        case QLocale::RepublicOfKorea:
+        case QLocale::Singapore:
+        case QLocale::SyrianArabRepublic:
+        case QLocale::Taiwan:
+        case QLocale::Thailand:
+        case QLocale::TrinidadAndTobago:
+        case QLocale::UnitedStates:
+        case QLocale::UnitedStatesMinorOutlyingIslands:
+        case QLocale::USVirginIslands:
+        case QLocale::Uzbekistan:
+        case QLocale::Zimbabwe:
+            return Qt::Sunday;
+
+        default:
+            return Qt::Monday;
+    }
+#endif
+}
+
+static inline void qwtFloorTime( 
+    QwtDate::IntervalType intervalType, QDateTime &dt )
+{
+    // when dt is inside the special hour where DST is ending
+    // an hour is no unique. Therefore we have to
+    // use UTC time.
+
+    const Qt::TimeSpec timeSpec = dt.timeSpec();
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = dt.toTimeSpec( Qt::UTC );
+
+    const QTime t = dt.time();
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        {
+            dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            dt.setTime( QTime( t.hour(), 0, 0 ) );
+            break;
+        }   
+        default:
+            break;
+    }
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = dt.toTimeSpec( Qt::LocalTime );
+}
+
+static inline QDateTime qwtToTimeSpec( 
+    const QDateTime &dt, Qt::TimeSpec spec )
+{
+    if ( dt.timeSpec() == spec )
+        return dt;
+
+    const qint64 jd = dt.date().toJulianDay();
+    if ( jd < 0 || jd >= INT_MAX )
+    {
+        // the conversion between local time and UTC
+        // is internally limited. To avoid
+        // overflows we simply ignore the difference
+        // for those dates
+
+        QDateTime dt2 = dt;
+        dt2.setTimeSpec( spec );
+        return dt2;
+    }
+
+    return dt.toTimeSpec( spec );
+}
+
+static inline double qwtToJulianDay( int year, int month, int day )
+{
+    // code from QDate but using doubles to avoid overflows
+    // for large values
+
+    const int m1 = ( month - 14 ) / 12;
+    const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
+    const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
+
+    return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
+            - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
+}
+
+static inline qint64 qwtFloorDiv64( qint64 a, int b )
+{
+    if ( a < 0 )
+        a -= b - 1;
+
+    return a / b;
+}
+
+static inline qint64 qwtFloorDiv( int a, int b )
+{
+    if ( a < 0 )
+        a -= b - 1;
+        
+    return a / b;
+}   
+
+static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
+{
+#if QT_VERSION >= 0x050000
+    return QDate( year, month, day );
+#else
+    if ( year > 100000 )
+    {
+        // code from QDate but using doubles to avoid overflows
+        // for large values
+
+        const int m1 = ( month - 14 ) / 12;
+        const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
+        const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
+
+        const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
+            - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
+
+        if ( jd > maxJulianDayD )
+        {
+            qWarning() << "qwtToDate: overflow";
+            return QDate();
+        }
+
+        return QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
+    }
+    else
+    {
+        return QDate( year, month, day );
+    }
+#endif
+}
+
+/*!
+  Translate from double to QDateTime
+
+  \param value Number of milliseconds since the epoch, 
+               1970-01-01T00:00:00 UTC
+  \param timeSpec Time specification
+  \return Datetime value
+
+  \sa toDouble(), QDateTime::setMSecsSinceEpoch()
+  \note The return datetime for Qt::OffsetFromUTC will be Qt::UTC
+ */
+QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
+{
+    const int msecsPerDay = 86400000;
+
+    const double days = static_cast<qint64>( ::floor( value / msecsPerDay ) );
+
+    const double jd = QwtDate::JulianDayForEpoch + days;
+    if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
+    {
+        qWarning() << "QwtDate::toDateTime: overflow";
+        return QDateTime();
+    }
+
+    const QDate d = QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
+
+    const int msecs = static_cast<int>( value - days * msecsPerDay );
+
+    static const QTime timeNull( 0, 0, 0, 0 );
+
+    QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = qwtToTimeSpec( dt, timeSpec );
+
+    return dt;
+}
+
+/*!
+  Translate from QDateTime to double
+
+  \param dateTime Datetime value
+  \return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed.
+
+  \sa toDateTime(), QDateTime::toMSecsSinceEpoch()
+  \warning For values very far below or above 1970-01-01 UTC rounding errors
+           will happen due to the limited significance of a double.
+ */
+double QwtDate::toDouble( const QDateTime &dateTime )
+{
+    const int msecsPerDay = 86400000;
+
+    const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
+
+    const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
+
+    const QTime time = dt.time();
+    const double secs = 3600.0 * time.hour() + 
+        60.0 * time.minute() + time.second();
+
+    return days * msecsPerDay + time.msec() + 1000.0 * secs;
+}
+
+/*!
+  Ceil a datetime according the interval type
+
+  \param dateTime Datetime value
+  \param intervalType Interval type, how to ceil. 
+                      F.e. when intervalType = QwtDate::Months, the result
+                      will be ceiled to the next beginning of a month
+  \return Ceiled datetime
+  \sa floor()
+ */
+QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType )
+{
+    if ( dateTime.date() >= QwtDate::maxDate() )
+        return dateTime;
+
+    QDateTime dt = dateTime;
+
+    switch ( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            break;
+        }
+        case QwtDate::Second:
+        {
+            qwtFloorTime( QwtDate::Second, dt );
+            if ( dt < dateTime )
+                dt.addSecs( 1 );
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            qwtFloorTime( QwtDate::Minute, dt );
+            if ( dt < dateTime )
+                dt.addSecs( 60 );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            qwtFloorTime( QwtDate::Hour, dt );
+            if ( dt < dateTime )
+                dt.addSecs( 3600 );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            if ( dt < dateTime )
+                dt = dt.addDays( 1 );
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            if ( dt < dateTime )
+                dt = dt.addDays( 1 );
+
+            int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
+            if ( days < 0 )
+                days += 7;
+
+            dt = dt.addDays( days );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            dt.setDate( qwtToDate( dateTime.date().year(), 
+                dateTime.date().month() ) );
+
+            if ( dt < dateTime )
+                dt.addMonths( 1 );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate d = dateTime.date();
+
+            int year = d.year();
+            if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
+                year++;
+
+            if ( year == 0 )
+                year++; // there is no year 0
+
+            dt.setDate( qwtToDate( year ) );
+            break;
+        }
+    }
+
+    return dt;
+}
+
+/*!
+  Floor a datetime according the interval type
+
+  \param dateTime Datetime value
+  \param intervalType Interval type, how to ceil. 
+                      F.e. when intervalType = QwtDate::Months,
+                      the result will be ceiled to the next 
+                      beginning of a month
+  \return Floored datetime
+  \sa floor()
+ */
+QDateTime QwtDate::floor( const QDateTime &dateTime, 
+    IntervalType intervalType )
+{
+    if ( dateTime.date() <= QwtDate::minDate() )
+        return dateTime;
+
+    QDateTime dt = dateTime;
+
+    switch ( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            break;
+        }
+        case QwtDate::Second:
+        case QwtDate::Minute:
+        case QwtDate::Hour:
+        {
+            qwtFloorTime( intervalType, dt );
+            break;
+        }
+        case QwtDate::Day:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            break;
+        }
+        case QwtDate::Week:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
+            if ( days < 0 )
+                days += 7;
+
+            dt = dt.addDays( -days );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate date = qwtToDate( dt.date().year(), 
+                dt.date().month() );
+            dt.setDate( date );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate date = qwtToDate( dt.date().year() );
+            dt.setDate( date );
+
+            break;
+        }
+    }
+
+    return dt;
+}
+
+/*!
+  Minimum for the supported date range
+
+  The range of valid dates depends on how QDate stores the 
+  Julian day internally.
+
+  - For Qt4 it is "Tue Jan 2 -4713"
+  - For Qt5 it is "Thu Jan 1 -2147483648"
+
+  \return minimum of the date range
+  \sa maxDate()
+ */
+QDate QwtDate::minDate()
+{
+    static QDate date;
+    if ( !date.isValid() )
+        date = QDate::fromJulianDay( minJulianDayD );
+
+    return date;
+}
+
+/*!
+  Maximum for the supported date range
+
+  The range of valid dates depends on how QDate stores the 
+  Julian day internally.
+
+  - For Qt4 it is "Tue Jun 3 5874898"
+  - For Qt5 it is "Tue Dec 31 2147483647"
+
+  \return maximum of the date range
+  \sa minDate()
+  \note The maximum differs between Qt4 and Qt5
+ */
+QDate QwtDate::maxDate()
+{
+    static QDate date;
+    if ( !date.isValid() )
+        date = QDate::fromJulianDay( maxJulianDayD );
+
+    return date;
+}
+
+/*!
+  \brief Date of the first day of the first week for a year
+
+  The first day of a week depends on the current locale
+  ( QLocale::firstDayOfWeek() ). 
+
+  \param year Year
+  \param type Option how to identify the first week
+  \return First day of week 0
+
+  \sa QLocale::firstDayOfWeek(), weekNumber()
+ */ 
+QDate QwtDate::dateOfWeek0( int year, Week0Type type )
+{
+    const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
+
+    QDate dt0( year, 1, 1 );
+
+    // floor to the first day of the week
+    int days = dt0.dayOfWeek() - firstDayOfWeek;
+    if ( days < 0 )
+        days += 7;
+
+    dt0 = dt0.addDays( -days );
+
+    if ( type == QwtDate::FirstThursday )
+    {
+        // according to ISO 8601 the first week is defined
+        // by the first thursday. 
+
+        int d = Qt::Thursday - firstDayOfWeek;
+        if ( d < 0 )
+            d += 7;
+
+        if ( dt0.addDays( d ).year() < year )
+            dt0 = dt0.addDays( 7 );
+    }
+
+    return dt0;
+}
+
+/*!
+  Find the week number of a date
+
+  - QwtDate::FirstThursday\n
+    Corresponding to ISO 8601 ( see QDate::weekNumber() ). 
+
+  - QwtDate::FirstDay\n
+    Number of weeks that have begun since dateOfWeek0().
+
+  \param date Date
+  \param type Option how to identify the first week
+
+  \return Week number, starting with 1
+ */
+int QwtDate::weekNumber( const QDate &date, Week0Type type )
+{
+    int weekNo;
+
+    if ( type == QwtDate::FirstDay )
+    {
+        const QDate day0 = dateOfWeek0( date.year(), type );
+        weekNo = day0.daysTo( date ) / 7 + 1;
+    }
+    else
+    {
+        weekNo = date.weekNumber();
+    }
+
+    return weekNo;
+}
+
+/*!
+   Offset in seconds from Coordinated Universal Time
+
+   The offset depends on the time specification of dateTime:
+
+   - Qt::UTC
+     0, dateTime has no offset
+   - Qt::OffsetFromUTC
+     returns dateTime.utcOffset()
+   - Qt::LocalTime:
+     number of seconds from the UTC
+
+   For Qt::LocalTime the offset depends on the timezone and
+   daylight savings.
+
+   \param dateTime Datetime value
+   \return Offset in seconds
+ */
+int QwtDate::utcOffset( const QDateTime &dateTime )
+{
+    int seconds = 0;
+
+    switch( dateTime.timeSpec() )
+    {
+        case Qt::UTC:
+        {
+            break;
+        }
+        case Qt::OffsetFromUTC:
+        {
+            seconds = dateTime.utcOffset();
+        }
+        default:
+        {
+            const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
+            seconds = dateTime.secsTo( dt1 );
+        }
+    }
+
+    return seconds;
+}
+
+/*!
+  Translate a datetime into a string
+
+  Beside the format expressions documented in QDateTime::toString()
+  the following expressions are supported:
+
+  - w\n
+    week number: ( 1 - 53 )
+  - ww\n
+    week number with a leading zero ( 01 - 53 )
+
+  \param dateTime Datetime value
+  \param format Format string
+  \param week0Type Specification of week 0
+
+  \return Datetime string
+  \sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw
+ */
+QString QwtDate::toString( const QDateTime &dateTime,
+    const QString & format, Week0Type week0Type )
+{
+    QString weekNo;
+    weekNo.setNum( QwtDate::weekNumber( dateTime.date(), week0Type ) );
+
+    QString weekNoWW;
+    if ( weekNo.length() == 1 )
+        weekNoWW += "0";
+    weekNoWW += weekNo;
+
+    QString fmt = format;
+    fmt.replace( "ww", weekNoWW );
+    fmt.replace( "w", weekNo );
+
+    return dateTime.toString( fmt );
+}
diff --git a/qwt/qwt_date.h b/qwt/qwt_date.h
new file mode 100644
index 0000000..30422a1
--- /dev/null
+++ b/qwt/qwt_date.h
@@ -0,0 +1,128 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_DATE_H_
+#define _QWT_DATE_H_
+
+#include "qwt_global.h"
+#include <qdatetime.h>
+
+/*!
+  \brief A collection of methods around date/time values
+
+  Qt offers convenient classes for dealing with date/time values,
+  but Qwt uses coordinate systems that are based on doubles.
+  QwtDate offers methods to translate from QDateTime to double and v.v.
+
+  A double is interpreted as the number of milliseconds since
+  1970-01-01T00:00:00 Universal Coordinated Time - also known
+  as "The Epoch". 
+
+  While the range of the Julian day in Qt4 is limited to [0, MAX_INT], 
+  Qt5 stores it as qint64 offering a huge range of valid dates. 
+  As the significance of a double is below this ( assuming a 
+  fraction of 52 bits ) the translation is not 
+  bijective with rounding errors for dates very far from Epoch. 
+  For a resolution of 1 ms those start to happen for dates above the 
+  year 144683. 
+
+  An axis for a date/time interval is expected to be aligned
+  and divided in time/date units like seconds, minutes, ...
+  QwtDate offers several algorithms that are needed to
+  calculate these axes.
+
+  \sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime
+*/
+class QWT_EXPORT QwtDate
+{
+public:
+    /*! 
+       How to identify the first week of year differs between
+       countries. 
+     */
+    enum Week0Type
+    {
+        /*!
+           According to ISO 8601 the first week of a year is defined
+           as "the week with the year's first Thursday in it".
+
+           FirstThursday corresponds to the numbering that is
+           implemented in QDate::weekNumber().
+        */
+        FirstThursday,
+
+        /*!
+            "The week with January 1.1 in it."
+           
+            In the U.S. this definition is more common than
+            FirstThursday.
+        */
+        FirstDay
+    };
+
+    /*! 
+      Classification of an time interval
+
+      Time intervals needs to be classified to decide how to
+      align and divide it.
+     */
+    enum IntervalType
+    {
+        //! The interval is related to milliseconds
+        Millisecond,
+
+        //! The interval is related to seconds
+        Second,
+
+        //! The interval is related to minutes
+        Minute,
+
+        //! The interval is related to hours
+        Hour,
+
+        //! The interval is related to days
+        Day,
+
+        //! The interval is related to weeks
+        Week,
+
+        //! The interval is related to months
+        Month,
+
+        //! The interval is related to years
+        Year
+    };
+
+    enum
+    {
+        //! The Julian day of "The Epoch"
+        JulianDayForEpoch = 2440588
+    };
+
+    static QDate minDate();
+    static QDate maxDate();
+
+    static QDateTime toDateTime( double value, 
+        Qt::TimeSpec = Qt::UTC );
+
+    static double toDouble( const QDateTime & );
+
+    static QDateTime ceil( const QDateTime &, IntervalType );
+    static QDateTime floor( const QDateTime &, IntervalType );
+
+    static QDate dateOfWeek0( int year, Week0Type );
+    static int weekNumber( const QDate &, Week0Type );
+
+    static int utcOffset( const QDateTime & );
+
+    static QString toString( const QDateTime &, 
+        const QString & format, Week0Type );
+};
+
+#endif
diff --git a/qwt/qwt_date_scale_draw.cpp b/qwt/qwt_date_scale_draw.cpp
new file mode 100644
index 0000000..7cfc6de
--- /dev/null
+++ b/qwt/qwt_date_scale_draw.cpp
@@ -0,0 +1,269 @@
+#include "qwt_date_scale_draw.h"
+
+class QwtDateScaleDraw::PrivateData
+{
+public:
+    PrivateData( Qt::TimeSpec spec ):
+        timeSpec( spec ),
+        utcOffset( 0 ),
+        week0Type( QwtDate::FirstThursday )
+    {
+        dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy";
+        dateFormats[ QwtDate::Week ] = "Www yyyy";
+        dateFormats[ QwtDate::Month ] = "MMM yyyy";
+        dateFormats[ QwtDate::Year ] = "yyyy";
+    }
+
+    Qt::TimeSpec timeSpec;
+    int utcOffset;
+    QwtDate::Week0Type week0Type;
+    QString dateFormats[ QwtDate::Year + 1 ];
+};
+
+/*!
+  \brief Constructor
+
+  The default setting is to display tick labels for the 
+  given time specification. The first week of a year is defined like
+  for QwtDate::FirstThursday.
+
+  \param timeSpec Time specification
+
+  \sa setTimeSpec(), setWeek0Type()
+ */
+QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec )
+{
+    d_data = new PrivateData( timeSpec );
+}
+
+//! Destructor
+QwtDateScaleDraw::~QwtDateScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  Set the time specification used for the tick labels
+
+  \param timeSpec Time specification
+  \sa timeSpec(), setUtcOffset(), toDateTime()
+ */
+void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec )
+{
+    d_data->timeSpec = timeSpec;
+}
+
+/*!
+  \return Time specification used for the tick labels
+  \sa setTimeSpec(), utcOffset(), toDateTime()
+ */
+Qt::TimeSpec QwtDateScaleDraw::timeSpec() const
+{
+    return d_data->timeSpec;
+}
+
+/*!
+  Set the offset in seconds from Coordinated Universal Time
+
+  \param seconds Offset in seconds
+
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::utcOffset(), setTimeSpec(), toDateTime()
+ */
+void QwtDateScaleDraw::setUtcOffset( int seconds )
+{
+    d_data->utcOffset = seconds;
+}
+
+/*!
+  \return Offset in seconds from Coordinated Universal Time
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
+ */
+int QwtDateScaleDraw::utcOffset() const
+{
+    return d_data->utcOffset;
+}
+
+/*!
+  Sets how to identify the first week of a year.
+
+  \param week0Type Mode how to identify the first week of a year
+
+  \sa week0Type().
+  \note week0Type has no effect beside for intervals classified as
+        QwtDate::Week. 
+ */
+void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type )
+{
+    d_data->week0Type = week0Type;
+}
+
+/*!
+  \return Setting how to identify the first week of a year. 
+  \sa setWeek0Type()
+ */
+QwtDate::Week0Type QwtDateScaleDraw::week0Type() const
+{
+    return d_data->week0Type;
+}
+
+/*!
+  Set the default format string for an datetime interval type
+
+  \param intervalType Interval type
+  \param format Default format string
+
+  \sa dateFormat(), dateFormatOfDate(), QwtDate::toString()
+ */
+void QwtDateScaleDraw::setDateFormat( 
+    QwtDate::IntervalType intervalType, const QString &format )
+{
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        d_data->dateFormats[ intervalType ] = format;
+    }
+}
+
+/*!
+  \param intervalType Interval type
+  \return Default format string for an datetime interval type
+  \sa setDateFormat(), dateFormatOfDate()
+ */
+QString QwtDateScaleDraw::dateFormat( 
+    QwtDate::IntervalType intervalType ) const
+{
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        return d_data->dateFormats[ intervalType ];
+    }
+
+    return QString::null;
+}
+
+/*!
+  Format string for the representation of a datetime
+
+  dateFormatOfDate() is intended to be overloaded for
+  situations, where formats are individual for specific
+  datetime values.
+
+  The default setting ignores dateTime and return
+  the default format for the interval type.
+
+  \param dateTime Datetime value
+  \param intervalType Interval type
+  \return Format string
+
+  \sa setDateFormat(), QwtDate::toString()
+ */
+QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime,
+    QwtDate::IntervalType intervalType ) const
+{
+    Q_UNUSED( dateTime )
+
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        return d_data->dateFormats[ intervalType ];
+    }
+
+    return d_data->dateFormats[ QwtDate::Second ];
+}
+
+/*!
+  \brief Convert a value into its representing label
+
+  The value is converted to a datetime value using toDateTime()
+  and converted to a plain text using QwtDate::toString().
+
+  \param value Value
+  \return Label string.
+
+  \sa dateFormatOfDate()
+*/
+QwtText QwtDateScaleDraw::label( double value ) const
+{
+    const QDateTime dt = toDateTime( value );
+    const QString fmt = dateFormatOfDate( 
+        dt, intervalType( scaleDiv() ) );
+
+    return QwtDate::toString( dt, fmt, d_data->week0Type );
+}
+
+/*!
+  Find the less detailed datetime unit, where no rounding
+  errors happen.
+
+  \param scaleDiv Scale division
+  \return Interval type
+
+  \sa dateFormatOfDate()
+ */
+QwtDate::IntervalType QwtDateScaleDraw::intervalType( 
+    const QwtScaleDiv &scaleDiv ) const
+{
+    int intvType = QwtDate::Year;
+
+    bool alignedToWeeks = true;
+
+    const QList<double> ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.size(); i++ )
+    {
+        const QDateTime dt = toDateTime( ticks[i] );
+        for ( int j = QwtDate::Second; j <= intvType; j++ )
+        {
+            const QDateTime dt0 = QwtDate::floor( dt, 
+                static_cast<QwtDate::IntervalType>( j ) );
+
+            if ( dt0 != dt )
+            {
+                if ( j == QwtDate::Week )
+                {
+                    alignedToWeeks = false;
+                }
+                else
+                {
+                    intvType = j - 1;
+                    break;
+                }
+            }
+        }
+
+        if ( intvType == QwtDate::Millisecond )
+            break;
+    }
+
+    if ( intvType == QwtDate::Week && !alignedToWeeks )
+        intvType = QwtDate::Day;
+
+    return static_cast<QwtDate::IntervalType>( intvType );
+}
+
+/*!
+  Translate a double value into a QDateTime object.
+
+  \return QDateTime object initialized with timeSpec() and utcOffset().
+  \sa timeSpec(), utcOffset(), QwtDate::toDateTime()
+ */
+QDateTime QwtDateScaleDraw::toDateTime( double value ) const
+{
+    QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
+    if ( d_data->timeSpec == Qt::OffsetFromUTC )
+    {
+        dt = dt.addSecs( d_data->utcOffset );
+        dt.setUtcOffset( d_data->utcOffset );
+    }
+
+    return dt;
+}
diff --git a/qwt/qwt_date_scale_draw.h b/qwt/qwt_date_scale_draw.h
new file mode 100644
index 0000000..92589e8
--- /dev/null
+++ b/qwt/qwt_date_scale_draw.h
@@ -0,0 +1,77 @@
+#ifndef _QWT_DATE_SCALE_DRAW_H_
+#define _QWT_DATE_SCALE_DRAW_H_ 1
+
+#include "qwt_global.h"
+#include "qwt_scale_draw.h"
+#include "qwt_date.h"
+
+/*!
+  \brief A class for drawing datetime scales
+
+  QwtDateScaleDraw displays values as datetime labels.
+  The format of the labels depends on the alignment of
+  the major tick labels.
+
+  The default format strings are:
+
+  - Millisecond\n
+    "hh:mm:ss:zzz\nddd dd MMM yyyy"
+  - Second\n
+    "hh:mm:ss\nddd dd MMM yyyy"
+  - Minute\n
+    "hh:mm\nddd dd MMM yyyy"
+  - Hour\n
+    "hh:mm\nddd dd MMM yyyy"
+  - Day\n
+    "ddd dd MMM yyyy"
+  - Week\n
+    "Www yyyy"
+  - Month\n
+    "MMM yyyy"
+  - Year\n
+    "yyyy"
+
+  The format strings can be modified using setDateFormat()
+  or individually for each tick label by overloading dateFormatOfDate(),
+
+  Usually QwtDateScaleDraw is used in combination with
+  QwtDateScaleEngine, that calculates scales for datetime
+  intervals.
+
+  \sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw()
+*/
+class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw
+{
+public:
+    QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime );
+    virtual ~QwtDateScaleDraw();
+
+    void setDateFormat( QwtDate::IntervalType, const QString & );
+    QString dateFormat( QwtDate::IntervalType ) const;
+
+    void setTimeSpec( Qt::TimeSpec );
+    Qt::TimeSpec timeSpec() const;
+
+    void setUtcOffset( int seconds );
+    int utcOffset() const;
+
+    void setWeek0Type( QwtDate::Week0Type );
+    QwtDate::Week0Type week0Type() const;
+
+    virtual QwtText label( double ) const;
+
+    QDateTime toDateTime( double ) const;
+
+protected:
+    virtual QwtDate::IntervalType 
+        intervalType( const QwtScaleDiv & ) const;
+
+    virtual QString dateFormatOfDate( const QDateTime &,
+        QwtDate::IntervalType ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_date_scale_engine.cpp b/qwt/qwt_date_scale_engine.cpp
new file mode 100644
index 0000000..bff2be4
--- /dev/null
+++ b/qwt/qwt_date_scale_engine.cpp
@@ -0,0 +1,1247 @@
+#include "qwt_date_scale_engine.h"
+#include "qwt_math.h"
+#include "qwt_transform.h"
+#include <qdatetime.h>
+#include <limits.h>
+
+static inline double qwtMsecsForType( QwtDate::IntervalType type )
+{
+    static const double msecs[] =
+    {
+        1.0,
+        1000.0,
+        60.0 * 1000.0,
+        3600.0 * 1000.0,
+        24.0 * 3600.0 * 1000.0,
+        7.0 * 24.0 * 3600.0 * 1000.0,
+        30.0 * 24.0 * 3600.0 * 1000.0,
+        365.0 * 24.0 * 3600.0 * 1000.0,
+    };
+
+    if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
+        return 1.0;
+
+    return msecs[ type ];
+}
+
+static inline int qwtAlignValue(
+    double value, double stepSize, bool up )
+{
+    double d = value / stepSize;
+    d = up ? ::ceil( d ) : ::floor( d );
+
+    return static_cast<int>( d * stepSize );
+}
+
+static double qwtIntervalWidth( const QDateTime &minDate,
+    const QDateTime &maxDate, QwtDate::IntervalType intervalType ) 
+{
+    switch( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            const double msecs = maxDate.time().msec() -
+                minDate.time().msec();
+
+            return secsTo * 1000 + msecs;
+        }
+        case QwtDate::Second:
+        {
+            return minDate.secsTo( maxDate );
+        }
+        case QwtDate::Minute:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            return ::floor( secsTo / 60 );
+        }
+        case QwtDate::Hour:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            return ::floor( secsTo / 3600 );
+        }
+        case QwtDate::Day:
+        {
+            return minDate.daysTo( maxDate );
+        }
+        case QwtDate::Week:
+        {
+            return ::floor( minDate.daysTo( maxDate ) / 7.0 );
+        }
+        case QwtDate::Month:
+        {
+            const double years = 
+                double( maxDate.date().year() ) - minDate.date().year();
+
+            int months = maxDate.date().month() - minDate.date().month();
+            if ( maxDate.date().day() < minDate.date().day() )
+                months--;
+
+            return years * 12 + months;
+        }
+        case QwtDate::Year:
+        {
+            double years = 
+                double( maxDate.date().year() ) - minDate.date().year();
+
+            if ( maxDate.date().month() < minDate.date().month() )
+                years -= 1.0;
+
+            return years;
+        }
+    }
+
+    return 0.0;
+}
+
+static double qwtRoundedIntervalWidth( 
+    const QDateTime &minDate, const QDateTime &maxDate, 
+    QwtDate::IntervalType intervalType ) 
+{
+    const QDateTime minD = QwtDate::floor( minDate, intervalType );
+    const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
+
+    return qwtIntervalWidth( minD, maxD, intervalType );
+}
+
+static inline int qwtStepCount( int intervalSize, int maxSteps,
+    const int limits[], size_t numLimits )
+{
+    for ( uint i = 0; i < numLimits; i++ )
+    {
+        const int numSteps = intervalSize / limits[ i ];
+
+        if ( numSteps > 1 && numSteps <= maxSteps &&
+            numSteps * limits[ i ] == intervalSize )
+        {
+            return numSteps;
+        }
+    }
+
+    return 0;
+}
+
+static int qwtStepSize( int intervalSize, int maxSteps, uint base ) 
+{
+    if ( maxSteps <= 0 )
+        return 0;
+
+    if ( maxSteps > 2 )
+    {
+        for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
+        {
+            const double stepSize = double( intervalSize ) / numSteps;
+
+            const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
+            const double fraction = qPow( base, p );
+
+            for ( uint n = base; n >= 1; n /= 2 )
+            {
+                if ( qFuzzyCompare( stepSize, n * fraction ) )
+                    return qRound( stepSize );
+
+                if ( n == 3 && ( base % 2 ) == 0 )
+                {
+                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
+                        return qRound( stepSize );
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int qwtDivideInterval( double intervalSize, int numSteps, 
+    const int limits[], size_t numLimits )
+{
+    const int v = qCeil( intervalSize / double( numSteps ) );
+
+    for ( uint i = 0; i < numLimits - 1; i++ )
+    {
+        if ( v <= limits[i] )
+            return limits[i];
+    }
+
+    return limits[ numLimits - 1 ];
+}
+
+static double qwtDivideScale( double intervalSize, int numSteps,
+    QwtDate::IntervalType intervalType )
+{
+    if ( intervalType != QwtDate::Day )
+    {
+        if ( ( intervalSize > numSteps ) && 
+            ( intervalSize <= 2 * numSteps ) )
+        {
+            return 2.0;
+        }
+    }
+
+    double stepSize;
+
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        case QwtDate::Minute:
+        {
+            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+    
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
+    
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            const double v = intervalSize / double( numSteps );
+            if ( v <= 5.0 )
+                stepSize = qCeil( v );
+            else
+                stepSize = qCeil( v / 7 ) * 7;
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
+
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Year:
+        case QwtDate::Millisecond:
+        default:
+        {
+            stepSize = QwtScaleArithmetic::divideInterval(
+                intervalSize, numSteps, 10 );
+        }
+    }
+
+    return stepSize;
+}
+
+static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
+    QwtDate::IntervalType intervalType )
+{
+    double minStepSize = 0.0;
+
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        {
+            minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
+            if ( minStepSize == 0.0 )
+                minStepSize = 0.5 * stepSize;
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+            int numSteps;
+
+            if ( stepSize > maxMinSteps )
+            {
+                numSteps = qwtStepCount( stepSize, maxMinSteps, 
+                    limits, sizeof( limits ) / sizeof( int ) );
+
+            }
+            else
+            {
+                numSteps = qwtStepCount( stepSize * 60, maxMinSteps, 
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            int numSteps = 0;
+
+            if ( stepSize > maxMinSteps )
+            {
+                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
+
+                numSteps = qwtStepCount( stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+            else
+            {
+                static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+                numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            int numSteps = 0;
+
+            if ( stepSize > maxMinSteps )
+            {
+                static int limits[] = { 1, 2, 3, 7, 14, 28 };
+
+                numSteps = qwtStepCount( stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+            else
+            {
+                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
+
+                numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            const int daysInStep = stepSize * 7;
+
+            if ( maxMinSteps >= daysInStep )
+            {
+                // we want to have one tick per day
+                minStepSize = 1.0 / 7.0;
+            }
+            else
+            {
+                // when the stepSize is more than a week we want to
+                // have a tick for each week
+
+                const int stepSizeInWeeks = stepSize;
+
+                if ( stepSizeInWeeks <= maxMinSteps )
+                {
+                    minStepSize = 1;
+                }
+                else
+                {
+                    minStepSize = QwtScaleArithmetic::divideInterval( 
+                        stepSizeInWeeks, maxMinSteps, 10 );
+                }
+            }
+            break;
+        }
+        case QwtDate::Month:
+        {
+            // fractions of months doesn't make any sense
+
+            if ( stepSize < maxMinSteps )
+                maxMinSteps = static_cast<int>( stepSize );
+
+            static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+            int numSteps = qwtStepCount( stepSize, maxMinSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            if ( stepSize >= maxMinSteps )
+            {
+                minStepSize = QwtScaleArithmetic::divideInterval(
+                    stepSize, maxMinSteps, 10 );
+            }
+            else
+            {
+                // something in months
+
+                static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+                int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+
+                if ( numSteps > 0 )
+                    minStepSize = double( stepSize ) / numSteps;
+            }
+                
+            break;
+        }
+        default:
+            break;
+    }
+
+    if ( intervalType != QwtDate::Month
+        && minStepSize == 0.0 )
+    {
+        minStepSize = 0.5 * stepSize;
+    }
+
+    return minStepSize;
+}
+
+static QList<double> qwtDstTicks( const QDateTime &dateTime,
+    int secondsMajor, int secondsMinor )
+{
+    if ( secondsMinor <= 0 )
+        QList<double>();
+
+    QDateTime minDate = dateTime.addSecs( -secondsMajor );
+    minDate = QwtDate::floor( minDate, QwtDate::Hour );
+
+    const double utcOffset = QwtDate::utcOffset( dateTime );
+
+    // find the hours where daylight saving time happens
+
+    double dstMin = QwtDate::toDouble( minDate );
+    while ( minDate < dateTime &&
+        QwtDate::utcOffset( minDate ) != utcOffset )
+    {
+        minDate = minDate.addSecs( 3600 );
+        dstMin += 3600 * 1000.0;
+    }
+
+    QList<double> ticks;
+    for ( int i = 0; i < 3600; i += secondsMinor )
+        ticks += dstMin + i * 1000.0;
+
+    return ticks;
+}
+
+static QwtScaleDiv qwtDivideToSeconds( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps,
+    QwtDate::IntervalType intervalType ) 
+{
+    // calculate the min step size
+    double minStepSize = 0;
+
+    if ( maxMinSteps > 1 ) 
+    {
+        minStepSize = qwtDivideMajorStep( stepSize, 
+            maxMinSteps, intervalType );
+    }
+
+    bool daylightSaving = false;
+    if ( minDate.timeSpec() == Qt::LocalTime )
+    {
+        daylightSaving = intervalType > QwtDate::Hour;
+        if ( intervalType == QwtDate::Hour )
+        {
+            daylightSaving = stepSize > 1;
+        }
+    }
+
+    const double s = qwtMsecsForType( intervalType ) / 1000;
+    const int secondsMajor = static_cast<int>( stepSize * s );
+    const double secondsMinor = minStepSize * s;
+    
+    // UTC excludes daylight savings. So from the difference
+    // of a date and its UTC counterpart we can find out
+    // the daylight saving hours
+
+    const double utcOffset = QwtDate::utcOffset( minDate );
+    double dstOff = 0;
+
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    for ( QDateTime dt = minDate; dt <= maxDate; 
+        dt = dt.addSecs( secondsMajor ) )
+    {
+        if ( !dt.isValid() )
+            break;
+
+        double majorValue = QwtDate::toDouble( dt );
+
+        if ( daylightSaving )
+        {
+            const double offset = utcOffset - QwtDate::utcOffset( dt );
+            majorValue += offset * 1000.0;
+
+            if ( offset > dstOff )
+            {
+                // we add some minor ticks for the DST hour,
+                // otherwise the ticks will be unaligned: 0, 2, 3, 5 ...
+                minorTicks += qwtDstTicks( 
+                    dt, secondsMajor, qRound( secondsMinor ) );
+            }
+
+            dstOff = offset;
+        }
+
+        if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
+            majorTicks += majorValue;
+
+        if ( secondsMinor > 0.0 )
+        {
+            const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
+
+            for ( int i = 1; i < numMinorSteps; i++ )
+            {
+                const QDateTime mt = dt.addMSecs( 
+                    qRound64( i * secondsMinor * 1000 ) );
+
+                double minorValue = QwtDate::toDouble( mt );
+                if ( daylightSaving )
+                {
+                    const double offset = utcOffset - QwtDate::utcOffset( mt );
+                    minorValue += offset * 1000.0;
+                }
+
+                if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
+                {
+                    const bool isMedium = ( numMinorSteps % 2 == 0 ) 
+                        && ( i != 1 ) && ( i == numMinorSteps / 2 );
+
+                    if ( isMedium )
+                        mediumTicks += minorValue;
+                    else
+                        minorTicks += minorValue;
+                }
+            }
+        }
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+static QwtScaleDiv qwtDivideToMonths( 
+    QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps ) 
+{
+    // months are intervals with non 
+    // equidistant ( in ms ) steps: we have to build the 
+    // scale division manually
+
+    int minStepDays = 0;
+    int minStepSize = 0.0; 
+
+    if ( maxMinSteps > 1 )
+    {
+        if ( stepSize == 1 )
+        {
+            if ( maxMinSteps >= 30 )
+                minStepDays = 1;
+            else if ( maxMinSteps >= 6 )
+                minStepDays = 5;
+            else if ( maxMinSteps >= 3 )
+                minStepDays = 10;
+
+            minStepDays = 15;
+        }
+        else
+        {
+            minStepSize = qwtDivideMajorStep( 
+                stepSize, maxMinSteps, QwtDate::Month );
+        }
+    }
+
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    for ( QDateTime dt = minDate; 
+        dt <= maxDate; dt = dt.addMonths( stepSize ) )
+    {
+        if ( !dt.isValid() )
+            break;
+
+        majorTicks += QwtDate::toDouble( dt );
+
+        if ( minStepDays > 0 )
+        {
+            for ( int days = minStepDays; 
+                days < 30; days += minStepDays )
+            {
+                const double tick = QwtDate::toDouble( dt.addDays( days ) );
+
+                if ( days == 15 && minStepDays != 15 )
+                    mediumTicks += tick;
+                else
+                    minorTicks += tick;
+            }
+        }
+        else if ( minStepSize > 0.0 )
+        {
+            const int numMinorSteps = qRound( stepSize / (double) minStepSize );
+
+            for ( int i = 1; i < numMinorSteps; i++ )
+            {
+                const double minorValue =
+                    QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
+
+                if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
+                    mediumTicks += minorValue;
+                else
+                    minorTicks += minorValue;
+            }
+        }
+    }
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+static QwtScaleDiv qwtDivideToYears( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps ) 
+{
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    double minStepSize = 0.0;
+
+    if ( maxMinSteps > 1 )
+    {
+        minStepSize = qwtDivideMajorStep( 
+            stepSize, maxMinSteps, QwtDate::Year );
+    }
+
+    int numMinorSteps = 0;
+    if ( minStepSize > 0.0 )
+        numMinorSteps = qFloor( stepSize / minStepSize );
+
+    bool dateBC = minDate.date().year() < -1;
+
+    for ( QDateTime dt = minDate; dt <= maxDate;
+        dt = dt.addYears( stepSize ) )
+    {
+        if ( dateBC && dt.date().year() > 1 )
+        {
+            // there is no year 0 in the Julian calendar
+            dt = dt.addYears( -1 );
+            dateBC = false;
+        }
+
+        if ( !dt.isValid() )
+            break;
+
+        majorTicks += QwtDate::toDouble( dt );
+
+        for ( int i = 1; i < numMinorSteps; i++ )
+        {
+            QDateTime tickDate;
+
+            const double years = qRound( i * minStepSize );
+            if ( years >= INT_MAX / 12 )
+            {
+                tickDate = dt.addYears( years );
+            }
+            else
+            {
+                tickDate = dt.addMonths( qRound( years * 12 ) );
+            }
+
+            const bool isMedium = ( numMinorSteps > 2 ) &&
+                ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
+
+            const double minorValue = QwtDate::toDouble( tickDate );
+            if ( isMedium )
+                mediumTicks += minorValue;
+            else
+                minorTicks += minorValue;
+        }
+
+        if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
+        {
+            break;
+        }
+    }   
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+class QwtDateScaleEngine::PrivateData
+{
+public:
+    PrivateData( Qt::TimeSpec spec ):
+        timeSpec( spec ),
+        utcOffset( 0 ),
+        week0Type( QwtDate::FirstThursday ),
+        maxWeeks( 4 )
+    {
+    }
+
+    Qt::TimeSpec timeSpec;
+    int utcOffset;
+    QwtDate::Week0Type week0Type;
+    int maxWeeks;
+};      
+
+
+/*!
+  \brief Constructor
+
+  The engine is initialized to build scales for the 
+  given time specification. It classifies intervals > 4 weeks
+  as >= Qt::Month. The first week of a year is defined like
+  for QwtDate::FirstThursday.
+
+  \param timeSpec Time specification
+
+  \sa setTimeSpec(), setMaxWeeks(), setWeek0Type()
+ */
+QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
+    QwtLinearScaleEngine( 10 )
+{
+    d_data = new PrivateData( timeSpec );
+}
+
+//! Destructor
+QwtDateScaleEngine::~QwtDateScaleEngine()
+{
+    delete d_data;
+}
+
+/*!
+  Set the time specification used by the engine
+
+  \param timeSpec Time specification
+  \sa timeSpec(), setUtcOffset(), toDateTime()
+ */
+void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
+{
+    d_data->timeSpec = timeSpec;
+}
+
+/*!
+  \return Time specification used by the engine
+  \sa setTimeSpec(), utcOffset(), toDateTime()
+ */
+Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
+{
+    return d_data->timeSpec;
+}
+
+/*!
+  Set the offset in seconds from Coordinated Universal Time
+
+  \param seconds Offset in seconds
+
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::utcOffset(), setTimeSpec(), toDateTime()
+ */
+void QwtDateScaleEngine::setUtcOffset( int seconds )
+{
+    d_data->utcOffset = seconds;
+}
+
+/*!
+  \return Offset in seconds from Coordinated Universal Time
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
+ */
+int QwtDateScaleEngine::utcOffset() const
+{
+    return d_data->utcOffset;
+}
+
+/*!
+  Sets how to identify the first week of a year.
+
+  \param week0Type Mode how to identify the first week of a year
+
+  \sa week0Type(), setMaxWeeks()
+  \note week0Type has no effect beside for intervals classified as
+        QwtDate::Week. 
+ */
+void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
+{
+    d_data->week0Type = week0Type;
+}
+
+/*!
+  \return Setting how to identify the first week of a year. 
+  \sa setWeek0Type(), maxWeeks()
+ */
+QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
+{
+    return d_data->week0Type;
+}
+
+/*!
+  Set a upper limit for the number of weeks, when an interval
+  can be classified as Qt::Week.
+
+  The default setting is 4 weeks.
+
+  \param weeks Upper limit for the number of weeks
+
+  \note In business charts a year is often devided
+        into weeks [1-52]
+  \sa maxWeeks(), setWeek0Type() 
+ */
+void QwtDateScaleEngine::setMaxWeeks( int weeks )
+{
+    d_data->maxWeeks = qMax( weeks, 0 );
+}
+
+/*!
+  \return Upper limit for the number of weeks, when an interval
+          can be classified as Qt::Week.
+  \sa setMaxWeeks(), week0Type()
+ */
+int QwtDateScaleEngine::maxWeeks() const
+{
+    return d_data->maxWeeks;
+}
+
+/*!
+  Classification of a date/time interval division
+
+  \param minDate Minimum ( = earlier ) of the interval
+  \param maxDate Maximum ( = later ) of the interval
+  \param maxSteps Maximum for the number of steps
+
+  \return Interval classification
+ */
+QwtDate::IntervalType QwtDateScaleEngine::intervalType( 
+    const QDateTime &minDate, const QDateTime &maxDate, 
+    int maxSteps ) const
+{
+    const double jdMin = minDate.date().toJulianDay();
+    const double jdMax = maxDate.date().toJulianDay();
+
+    if ( ( jdMax - jdMin ) / 365 > maxSteps )
+        return QwtDate::Year;
+
+    const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
+    if ( months > maxSteps * 6 )
+        return QwtDate::Year;
+
+    const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
+    const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
+
+    if ( weeks > d_data->maxWeeks )
+    {
+        if ( days > 4 * maxSteps * 7 )
+            return QwtDate::Month;
+    }
+
+    if ( days > maxSteps * 7 )
+        return QwtDate::Week;
+
+    const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
+    if ( hours > maxSteps * 24 )
+        return QwtDate::Day;
+
+    const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
+
+    if ( seconds >= maxSteps * 3600 )
+        return QwtDate::Hour;
+
+    if ( seconds >= maxSteps * 60 )
+        return QwtDate::Minute;
+
+    if ( seconds >= maxSteps )
+        return QwtDate::Second;
+
+    return QwtDate::Millisecond;
+}
+
+/*!
+  Align and divide an interval
+
+  The algorithm aligns and divides the interval into steps. 
+
+  Datetime interval divisions are usually not equidistant and the
+  calculated stepSize is can only be used as an approximation
+  for the steps calculated by divideScale(). 
+
+  \param maxNumSteps Max. number of steps
+  \param x1 First limit of the interval (In/Out)
+  \param x2 Second limit of the interval (In/Out)
+  \param stepSize Step size (Out)
+
+  \sa QwtScaleEngine::setAttribute()
+*/
+void QwtDateScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    stepSize = 0.0;
+
+    QwtInterval interval( x1, x2 );
+    interval = interval.normalized();
+
+    interval.setMinValue( interval.minValue() - lowerMargin() );
+    interval.setMaxValue( interval.maxValue() + upperMargin() );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+        interval = interval.symmetrize( reference() );
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( reference() );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    const QDateTime from = toDateTime( interval.minValue() );
+    const QDateTime to = toDateTime( interval.maxValue() );
+
+    if ( from.isValid() && to.isValid() )
+    {
+        if ( maxNumSteps < 1 )
+            maxNumSteps = 1;
+
+        const QwtDate::IntervalType intvType = 
+            intervalType( from, to, maxNumSteps );
+
+        double width = qwtIntervalWidth( from, to, intvType );
+        width = QwtScaleArithmetic::divideInterval( width, maxNumSteps, 10 );
+
+        if ( width != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
+        {
+            const QDateTime d1 = alignDate( from, width, intvType, false );
+            const QDateTime d2 = alignDate( to, width, intvType, true );
+
+            interval.setMinValue( QwtDate::toDouble( d1 ) );
+            interval.setMaxValue( QwtDate::toDouble( d2 ) );
+        }
+
+        stepSize = width * qwtMsecsForType( intvType );
+    }
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for a date/time interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the scaleEngine
+                   calculates one.
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    if ( maxMajorSteps < 1 )
+        maxMajorSteps = 1;
+
+    const double min = qMin( x1, x2 );
+    const double max = qMax( x1, x2 );
+
+    const QDateTime from = toDateTime( min );
+    const QDateTime to = toDateTime( max );
+
+    if ( from == to )
+        return QwtScaleDiv();
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize > 0.0 )
+    {
+        // as interval types above hours are not equidistant
+        // ( even days might have 23/25 hours because of daylight saving )
+        // the stepSize is used as a hint only
+
+        maxMajorSteps = qCeil( ( max - min ) / stepSize );
+    }
+
+    const QwtDate::IntervalType intvType = 
+        intervalType( from, to, maxMajorSteps );
+
+    QwtScaleDiv scaleDiv;
+
+    if ( intvType == QwtDate::Millisecond )
+    {
+        // for milliseconds and below we can use the decimal system
+        scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
+            maxMajorSteps, maxMinorSteps, stepSize );
+    }
+    else
+    {
+        const QDateTime minDate = QwtDate::floor( from, intvType );
+        const QDateTime maxDate = QwtDate::ceil( to, intvType );
+
+        scaleDiv = buildScaleDiv( minDate, maxDate, 
+            maxMajorSteps, maxMinorSteps, intvType );
+
+        // scaleDiv has been calculated from an extended interval
+        // adjusted to the step size. We have to shrink it again.
+
+        scaleDiv = scaleDiv.bounded( min, max );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+QwtScaleDiv QwtDateScaleEngine::buildScaleDiv( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    int maxMajorSteps, int maxMinorSteps,
+    QwtDate::IntervalType intervalType ) const
+{
+    // calculate the step size
+    const double stepSize = qwtDivideScale( 
+        qwtIntervalWidth( minDate, maxDate, intervalType ), 
+        maxMajorSteps, intervalType );
+
+    // align minDate to the step size
+    QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
+    if ( !dt0.isValid() )
+    {
+        // the floored date is out of the range of a 
+        // QDateTime - we ceil instead.
+        dt0 = alignDate( minDate, stepSize, intervalType, true );
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    if ( intervalType <= QwtDate::Week )
+    {
+        scaleDiv = qwtDivideToSeconds( dt0, maxDate, 
+            stepSize, maxMinorSteps, intervalType );
+    }
+    else
+    {
+        if( intervalType == QwtDate::Month )
+        {
+            scaleDiv = qwtDivideToMonths( dt0, maxDate,
+                stepSize, maxMinorSteps );
+        }
+        else if ( intervalType == QwtDate::Year )
+        {
+            scaleDiv = qwtDivideToYears( dt0, maxDate,
+                stepSize, maxMinorSteps );
+        }
+    }
+
+
+    return scaleDiv;
+}
+
+/*!
+  Align a date/time value for a step size
+
+  For Qt::Day alignments there is no "natural day 0" -
+  instead the first day of the year is used to avoid jumping 
+  major ticks positions when panning a scale. For other alignments
+  ( f.e according to the first day of the month ) alignDate()
+  has to be overloaded.
+
+  \param dateTime Date/time value
+  \param stepSize Step size
+  \param intervalType Interval type
+  \param up When true dateTime is ceiled - otherwise it is floored
+
+  \return Aligned date/time value
+ */
+QDateTime QwtDateScaleEngine::alignDate( 
+    const QDateTime &dateTime, double stepSize, 
+    QwtDate::IntervalType intervalType, bool up ) const
+{
+    // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ??
+
+    QDateTime dt = dateTime;
+
+    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
+    {
+        dt.setUtcOffset( 0 );
+    }
+
+    switch( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            const int ms = qwtAlignValue( 
+                dt.time().msec(), stepSize, up ) ;
+
+            dt = QwtDate::floor( dateTime, QwtDate::Second );
+            dt = dt.addMSecs( ms );
+
+            break;
+        }
+        case QwtDate::Second:
+        {
+            const int s = qwtAlignValue( 
+                dt.time().second(), stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Minute );
+            dt = dt.addSecs( s );
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            const int m = qwtAlignValue( 
+                dt.time().minute(), stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Hour );
+            dt = dt.addSecs( m * 60 );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            const int h = qwtAlignValue( 
+                dt.time().hour(), stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            dt = dt.addSecs( h * 3600 );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            // What date do we expect f.e. from an alignment of 5 days ??
+            // Aligning them to the beginning of the year avoids at least
+            // jumping major ticks when panning
+
+            const int d = qwtAlignValue(
+                dt.date().dayOfYear(), stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Year );
+            dt = dt.addDays( d - 1 );
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            const QDate date = QwtDate::dateOfWeek0(
+                dt.date().year(), d_data->week0Type );
+
+            const int numWeeks = date.daysTo( dt.date() ) / 7;
+            const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            dt.setDate( date );
+            dt = dt.addDays( d );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            const int m = qwtAlignValue( 
+                dt.date().month() - 1, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Year );
+            dt = dt.addMonths( m );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            const int y = qwtAlignValue(
+                dateTime.date().year(), stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            if ( y == 0 )
+            {
+                // there is no year 0 in the Julian calendar
+                dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
+            }
+            else
+            {
+                dt.setDate( QDate( y, 1, 1 ) );
+            }
+
+            break;
+        }
+    }
+
+    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
+    {
+        dt.setUtcOffset( dateTime.utcOffset() );
+    }
+
+    return dt;
+}
+
+/*!
+  Translate a double value into a QDateTime object.
+
+  For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate()
+
+  \return QDateTime object initialized with timeSpec() and utcOffset().
+  \sa timeSpec(), utcOffset(), QwtDate::toDateTime()
+ */
+QDateTime QwtDateScaleEngine::toDateTime( double value ) const
+{
+    QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
+    if ( !dt.isValid() )
+    {
+        const QDate date = ( value <= 0.0 ) 
+            ? QwtDate::minDate() : QwtDate::maxDate();
+
+        dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
+    }
+
+    if ( d_data->timeSpec == Qt::OffsetFromUTC )
+    {
+        dt = dt.addSecs( d_data->utcOffset );
+        dt.setUtcOffset( d_data->utcOffset );
+    }
+
+    return dt;
+}
+
diff --git a/qwt/qwt_date_scale_engine.h b/qwt/qwt_date_scale_engine.h
new file mode 100644
index 0000000..db1d0f1
--- /dev/null
+++ b/qwt/qwt_date_scale_engine.h
@@ -0,0 +1,77 @@
+#ifndef _QWT_DATE_SCALE_ENGINE_H_
+#define _QWT_DATE_SCALE_ENGINE_H_ 1
+
+#include "qwt_date.h"
+#include "qwt_scale_engine.h"
+
+/*!
+  \brief A scale engine for date/time values
+
+  QwtDateScaleEngine builds scales from a time intervals.
+  Together with QwtDateScaleDraw it can be used for
+  axes according to date/time values.
+
+  Years, months, weeks, days, hours and minutes are organized
+  in steps with non constant intervals. QwtDateScaleEngine
+  classifies intervals and aligns the boundaries and tick positions
+  according to this classification.
+
+  QwtDateScaleEngine supports representations depending
+  on Qt::TimeSpec specifications. The valid range for scales
+  is limited by the range of QDateTime, that differs 
+  between Qt4 and Qt5.
+  
+  Datetime values are expected as the number of milliseconds since
+  1970-01-01T00:00:00 Universal Coordinated Time - also known
+  as "The Epoch", that can be converted to QDateTime using 
+  QwtDate::toDateTime().
+
+  \sa QwtDate, QwtPlot::setAxisScaleEngine(),
+      QwtAbstractScale::setScaleEngine()
+*/
+class QWT_EXPORT QwtDateScaleEngine: public QwtLinearScaleEngine
+{
+public:
+    QwtDateScaleEngine( Qt::TimeSpec = Qt::LocalTime );
+    virtual ~QwtDateScaleEngine();
+
+    void setTimeSpec( Qt::TimeSpec );
+    Qt::TimeSpec timeSpec() const;
+
+    void setUtcOffset( int seconds );
+    int utcOffset() const;
+
+    void setWeek0Type( QwtDate::Week0Type );
+    QwtDate::Week0Type week0Type() const;
+    
+    void setMaxWeeks( int );
+    int maxWeeks() const;
+
+    virtual void autoScale( int maxNumSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( 
+        double x1, double x2,
+        int maxMajorSteps, int maxMinorSteps,
+        double stepSize = 0.0 ) const;
+
+    virtual QwtDate::IntervalType intervalType( 
+        const QDateTime &, const QDateTime &, int maxSteps ) const;
+
+    QDateTime toDateTime( double ) const;
+
+protected:
+    virtual QDateTime alignDate( const QDateTime &, double stepSize,
+        QwtDate::IntervalType, bool up ) const;
+
+private:
+    QwtScaleDiv buildScaleDiv( const QDateTime &, const QDateTime &,
+        int maxMajorSteps, int maxMinorSteps, 
+        QwtDate::IntervalType ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_dial.cpp b/qwt/qwt_dial.cpp
new file mode 100644
index 0000000..1440eb1
--- /dev/null
+++ b/qwt/qwt_dial.cpp
@@ -0,0 +1,872 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_dial.h"
+#include "qwt_dial_needle.h"
+#include "qwt_math.h"
+#include "qwt_scale_engine.h"
+#include "qwt_scale_map.h"
+#include "qwt_round_scale_draw.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static inline double qwtAngleDist( double a1, double a2 )
+{
+    double dist = qAbs( a2 - a1 );
+    if ( dist > 360.0 )
+        dist -= 360.0;
+
+    return dist;
+}
+
+static inline bool qwtIsOnArc( double angle, double min, double max )
+{
+    if ( min < max )
+    {
+        return ( angle >= min ) && ( angle <= max );
+    }
+    else
+    {
+        return ( angle >= min ) || ( angle <= max );
+    }
+}
+
+static inline double qwtBoundedAngle( double min, double angle, double max )
+{
+    double from = qwtNormalizeDegrees( min );
+    double to = qwtNormalizeDegrees( max );
+
+    double a;
+
+    if ( qwtIsOnArc( angle, from, to ) )
+    {
+        a = angle;
+        if ( a < min )
+            a += 360.0;
+    }
+    else
+    {
+        if ( qwtAngleDist( angle, from ) <
+            qwtAngleDist( angle, to ) )
+        {
+            a = min;
+        }
+        else
+        {
+            a = max;
+        }
+    }
+
+    return a;
+}
+
+class QwtDial::PrivateData
+{
+public:
+    PrivateData():
+        frameShadow( Sunken ),
+        lineWidth( 0 ),
+        mode( RotateNeedle ),
+        origin( 90.0 ),
+        minScaleArc( 0.0 ),
+        maxScaleArc( 0.0 ),
+        needle( NULL ),
+        arcOffset( 0.0 ),
+        mouseOffset( 0.0 )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete needle;
+    }
+    Shadow frameShadow;
+    int lineWidth;
+
+    QwtDial::Mode mode;
+
+    double origin;
+    double minScaleArc;
+    double maxScaleArc;
+
+    double scalePenWidth;
+    QwtDialNeedle *needle;
+
+    double arcOffset;
+    double mouseOffset;
+
+    QPixmap pixmapCache;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+
+  Create a dial widget with no needle. The scale is initialized
+  to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ).
+  The origin of the scale is at 90°,
+
+  The value is set to 0.0.
+
+  The default mode is QwtDial::RotateNeedle.
+*/
+QwtDial::QwtDial( QWidget* parent ):
+    QwtAbstractSlider( parent )
+{
+    d_data = new PrivateData;
+
+    setFocusPolicy( Qt::TabFocus );
+
+    QPalette p = palette();
+    for ( int i = 0; i < QPalette::NColorGroups; i++ )
+    {
+        const QPalette::ColorGroup colorGroup =
+            static_cast<QPalette::ColorGroup>( i );
+
+        // Base: background color of the circle inside the frame.
+        // WindowText: background color of the circle inside the scale
+
+        p.setColor( colorGroup, QPalette::WindowText,
+            p.color( colorGroup, QPalette::Base ) );
+    }
+    setPalette( p );
+
+    QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
+    scaleDraw->setRadius( 0 );
+
+    setScaleDraw( scaleDraw );
+
+    setScaleArc( 0.0, 360.0 ); // scale as a full circle
+
+    setScaleMaxMajor( 10 );
+    setScaleMaxMinor( 5 );
+
+    setValue( 0.0 );
+}
+
+//!  Destructor
+QwtDial::~QwtDial()
+{
+    delete d_data;
+}
+
+/*!
+  Sets the frame shadow value from the frame style.
+
+  \param shadow Frame shadow
+  \sa setLineWidth(), QFrame::setFrameShadow()
+*/
+void QwtDial::setFrameShadow( Shadow shadow )
+{
+    if ( shadow != d_data->frameShadow )
+    {
+        invalidateCache();
+
+        d_data->frameShadow = shadow;
+        if ( lineWidth() > 0 )
+            update();
+    }
+}
+
+/*!
+  \return Frame shadow
+  /sa setFrameShadow(), lineWidth(), QFrame::frameShadow()
+*/
+QwtDial::Shadow QwtDial::frameShadow() const
+{
+    return d_data->frameShadow;
+}
+
+/*!
+  Sets the line width of the frame
+
+  \param lineWidth Line width
+  \sa setFrameShadow()
+*/
+void QwtDial::setLineWidth( int lineWidth )
+{
+    if ( lineWidth < 0 )
+        lineWidth = 0;
+
+    if ( d_data->lineWidth != lineWidth )
+    {
+        invalidateCache();
+
+        d_data->lineWidth = lineWidth;
+        update();
+    }
+}
+
+/*!
+  \return Line width of the frame
+  \sa setLineWidth(), frameShadow(), lineWidth()
+*/
+int QwtDial::lineWidth() const
+{
+    return d_data->lineWidth;
+}
+
+/*!
+  \return bounding rectangle of the circle inside the frame
+  \sa setLineWidth(), scaleInnerRect(), boundingRect()
+*/
+QRect QwtDial::innerRect() const
+{
+    const int lw = lineWidth();
+    return boundingRect().adjusted( lw, lw, -lw, -lw );
+}
+
+/*!
+  \return bounding rectangle of the dial including the frame
+  \sa setLineWidth(), scaleInnerRect(), innerRect()
+*/
+QRect QwtDial::boundingRect() const
+{
+    const QRect cr = contentsRect();
+
+    const double dim = qMin( cr.width(), cr.height() );
+
+    QRect inner( 0, 0, dim, dim );
+    inner.moveCenter( cr.center() );
+
+    return inner;
+}
+
+/*!
+  \return rectangle inside the scale
+  \sa setLineWidth(), boundingRect(), innerRect()
+*/
+QRect QwtDial::scaleInnerRect() const
+{
+    QRect rect = innerRect();
+
+    const QwtAbstractScaleDraw *sd = scaleDraw();
+    if ( sd )
+    {
+        int scaleDist = qCeil( sd->extent( font() ) );
+        scaleDist++; // margin
+
+        rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
+    }
+
+    return rect;
+}
+
+/*!
+  \brief Change the mode of the dial.
+  \param mode New mode
+
+  In case of QwtDial::RotateNeedle the needle is rotating, in case of
+  QwtDial::RotateScale, the needle points to origin()
+  and the scale is rotating.
+
+  The default mode is QwtDial::RotateNeedle.
+
+  \sa mode(), setValue(), setOrigin()
+*/
+void QwtDial::setMode( Mode mode )
+{
+    if ( mode != d_data->mode )
+    {
+        invalidateCache();
+
+        d_data->mode = mode;
+        sliderChange();
+    }
+}
+
+/*!
+  \return Mode of the dial.
+  \sa setMode(), origin(), setScaleArc(), value()
+*/
+QwtDial::Mode QwtDial::mode() const
+{
+    return d_data->mode;
+}
+
+/*!
+  Invalidate the internal caches used to speed up repainting
+ */
+void QwtDial::invalidateCache()
+{
+    d_data->pixmapCache = QPixmap();
+}
+
+/*!
+   Paint the dial
+   \param event Paint event
+*/
+void QwtDial::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    if ( d_data->mode == QwtDial::RotateScale )
+    {
+        painter.save();
+        painter.setRenderHint( QPainter::Antialiasing, true );
+
+        drawContents( &painter );
+
+        painter.restore();
+    }
+
+    const QRect r = contentsRect();
+    if ( r.size() != d_data->pixmapCache.size() )
+    {
+        d_data->pixmapCache = QwtPainter::backingStore( this, r.size() );
+        d_data->pixmapCache.fill( Qt::transparent );
+
+        QPainter p( &d_data->pixmapCache );
+        p.setRenderHint( QPainter::Antialiasing, true );
+        p.translate( -r.topLeft() );
+            
+        if ( d_data->mode != QwtDial::RotateScale )
+            drawContents( &p );
+
+        if ( lineWidth() > 0 )
+            drawFrame( &p );
+
+        if ( d_data->mode != QwtDial::RotateNeedle )
+            drawNeedle( &p );
+    }
+
+    painter.drawPixmap( r.topLeft(), d_data->pixmapCache );
+
+    if ( d_data->mode == QwtDial::RotateNeedle )
+        drawNeedle( &painter );
+
+    if ( hasFocus() )
+        drawFocusIndicator( &painter );
+}
+
+/*!
+  Draw the focus indicator
+  \param painter Painter
+*/
+void QwtDial::drawFocusIndicator( QPainter *painter ) const
+{
+    QwtPainter::drawFocusRect( painter, this, boundingRect() );
+}
+
+/*!
+  Draw the frame around the dial
+
+  \param painter Painter
+  \sa lineWidth(), frameShadow()
+*/
+void QwtDial::drawFrame( QPainter *painter )
+{
+    QwtPainter::drawRoundFrame( painter, boundingRect(),
+        palette(), lineWidth(), d_data->frameShadow );
+}
+
+/*!
+  \brief Draw the contents inside the frame
+
+  QPalette::Window is the background color outside of the frame.
+  QPalette::Base is the background color inside the frame.
+  QPalette::WindowText is the background color inside the scale.
+
+  \param painter Painter
+
+  \sa boundingRect(), innerRect(),
+    scaleInnerRect(), QWidget::setPalette()
+*/
+void QwtDial::drawContents( QPainter *painter ) const
+{
+    if ( testAttribute( Qt::WA_NoSystemBackground ) ||
+        palette().brush( QPalette::Base ) !=
+            palette().brush( QPalette::Window ) )
+    {
+        const QRectF br = boundingRect();
+
+        painter->save();
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette().brush( QPalette::Base ) );
+        painter->drawEllipse( br );
+        painter->restore();
+    }
+
+    const QRectF insideScaleRect = scaleInnerRect();
+    if ( palette().brush( QPalette::WindowText ) !=
+            palette().brush( QPalette::Base ) )
+    {
+        painter->save();
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette().brush( QPalette::WindowText ) );
+        painter->drawEllipse( insideScaleRect );
+        painter->restore();
+    }
+
+    const QPointF center = insideScaleRect.center();
+    const double radius = 0.5 * insideScaleRect.width();
+
+    painter->save();
+    drawScale( painter, center, radius );
+    painter->restore();
+
+    painter->save();
+    drawScaleContents( painter, center, radius );
+    painter->restore();
+}
+
+/*!
+  Draw the needle
+
+  \param painter Painter
+  \param center Center of the dial
+  \param radius Length for the needle
+  \param direction Direction of the needle in degrees, counter clockwise
+  \param colorGroup ColorGroup
+*/
+void QwtDial::drawNeedle( QPainter *painter, const QPointF &center,
+    double radius, double direction, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_data->needle )
+    {
+        direction = 360.0 - direction; // counter clockwise
+        d_data->needle->draw( painter, center, radius, direction, colorGroup );
+    }
+}
+
+void QwtDial::drawNeedle( QPainter *painter ) const
+{
+    if ( !isValid() )
+        return;
+
+    QPalette::ColorGroup colorGroup;
+    if ( isEnabled() )
+        colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive;
+    else
+        colorGroup = QPalette::Disabled;
+
+    const QRectF sr = scaleInnerRect();
+
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    drawNeedle( painter, sr.center(), 0.5 * sr.width(),
+        transform( value() ) + 270.0, colorGroup );
+    painter->restore();
+}
+
+/*!
+  Draw the scale
+
+  \param painter Painter
+  \param center Center of the dial
+  \param radius Radius of the scale
+*/
+void QwtDial::drawScale( QPainter *painter, 
+    const QPointF &center, double radius ) const
+{
+    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
+    if ( sd == NULL )
+        return;
+
+    sd->setRadius( radius );
+    sd->moveCenter( center );
+
+    QPalette pal = palette();
+
+    const QColor textColor = pal.color( QPalette::Text );
+    pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone
+
+    painter->setFont( font() );
+    painter->setPen( QPen( textColor, sd->penWidth() ) );
+
+    painter->setBrush( Qt::red );
+    sd->draw( painter, pal );
+}
+
+/*!
+  Draw the contents inside the scale
+
+  Paints nothing.
+
+  \param painter Painter
+  \param center Center of the contents circle
+  \param radius Radius of the contents circle
+*/
+void QwtDial::drawScaleContents( QPainter *painter,
+    const QPointF &center, double radius ) const
+{
+    Q_UNUSED(painter);
+    Q_UNUSED(center);
+    Q_UNUSED(radius);
+}
+
+/*!
+  Set a needle for the dial
+
+  \param needle Needle
+
+  \warning The needle will be deleted, when a different needle is
+           set or in ~QwtDial()
+*/
+void QwtDial::setNeedle( QwtDialNeedle *needle )
+{
+    if ( needle != d_data->needle )
+    {
+        if ( d_data->needle )
+            delete d_data->needle;
+
+        d_data->needle = needle;
+        update();
+    }
+}
+
+/*!
+  \return needle
+  \sa setNeedle()
+*/
+const QwtDialNeedle *QwtDial::needle() const
+{
+    return d_data->needle;
+}
+
+/*!
+  \return needle
+  \sa setNeedle()
+*/
+QwtDialNeedle *QwtDial::needle()
+{
+    return d_data->needle;
+}
+
+//! \return the scale draw
+QwtRoundScaleDraw *QwtDial::scaleDraw()
+{
+    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+//! \return the scale draw
+const QwtRoundScaleDraw *QwtDial::scaleDraw() const
+{
+    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Set an individual scale draw
+
+  The motivation for setting a scale draw is often
+  to overload QwtRoundScaleDraw::label() to return 
+  individual tick labels.
+  
+  \param scaleDraw Scale draw
+  \warning The previous scale draw is deleted
+*/
+void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+    sliderChange();
+}
+
+/*!
+  Change the arc of the scale
+
+  \param minArc Lower limit
+  \param maxArc Upper limit
+
+  \sa minScaleArc(), maxScaleArc()
+*/
+void QwtDial::setScaleArc( double minArc, double maxArc )
+{
+    if ( minArc != 360.0 && minArc != -360.0 )
+        minArc = ::fmod( minArc, 360.0 );
+    if ( maxArc != 360.0 && maxArc != -360.0 )
+        maxArc = ::fmod( maxArc, 360.0 );
+
+    double minScaleArc = qMin( minArc, maxArc );
+    double maxScaleArc = qMax( minArc, maxArc );
+
+    if ( maxScaleArc - minScaleArc > 360.0 )
+        maxScaleArc = minScaleArc + 360.0;
+
+    if ( ( minScaleArc != d_data->minScaleArc ) || 
+        ( maxScaleArc != d_data->maxScaleArc ) )
+    {
+        d_data->minScaleArc = minScaleArc;
+        d_data->maxScaleArc = maxScaleArc;
+
+        invalidateCache();
+        sliderChange();
+    }
+}
+
+/*! 
+  Set the lower limit for the scale arc
+
+  \param min Lower limit of the scale arc
+  \sa setScaleArc(), setMaxScaleArc()
+ */
+void QwtDial::setMinScaleArc( double min )
+{
+    setScaleArc( min, d_data->maxScaleArc );
+}
+
+/*! 
+  \return Lower limit of the scale arc
+  \sa setScaleArc()
+*/
+double QwtDial::minScaleArc() const
+{
+    return d_data->minScaleArc;
+}
+
+/*! 
+  Set the upper limit for the scale arc
+
+  \param max Upper limit of the scale arc
+  \sa setScaleArc(), setMinScaleArc()
+ */
+void QwtDial::setMaxScaleArc( double max )
+{
+    setScaleArc( d_data->minScaleArc, max );
+}
+
+/*! 
+  \return Upper limit of the scale arc
+  \sa setScaleArc()
+*/
+double QwtDial::maxScaleArc() const
+{
+    return d_data->maxScaleArc;
+}
+
+/*!
+  \brief Change the origin
+
+  The origin is the angle where scale and needle is relative to.
+
+  \param origin New origin
+  \sa origin()
+*/
+void QwtDial::setOrigin( double origin )
+{
+    invalidateCache();
+
+    d_data->origin = origin;
+    sliderChange();
+}
+
+/*!
+  The origin is the angle where scale and needle is relative to.
+
+  \return Origin of the dial
+  \sa setOrigin()
+*/
+double QwtDial::origin() const
+{
+    return d_data->origin;
+}
+
+/*!
+  \return Size hint
+  \sa minimumSizeHint()
+*/
+QSize QwtDial::sizeHint() const
+{
+    int sh = 0;
+    if ( scaleDraw() )
+        sh = qCeil( scaleDraw()->extent( font() ) );
+
+    const int d = 6 * sh + 2 * lineWidth();
+
+    QSize hint( d, d ); 
+    if ( !isReadOnly() )
+        hint = hint.expandedTo( QApplication::globalStrut() );
+
+    return hint;
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtDial::minimumSizeHint() const
+{
+    int sh = 0;
+    if ( scaleDraw() )
+        sh = qCeil( scaleDraw()->extent( font() ) );
+
+    const int d = 3 * sh + 2 * lineWidth();
+
+    return QSize( d, d );
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when the inner circle contains pos 
+  \sa scrolledTo()
+*/
+bool QwtDial::isScrollPosition( const QPoint &pos ) const
+{
+    const QRegion region( innerRect(), QRegion::Ellipse );
+    if ( region.contains( pos ) && ( pos != innerRect().center() ) )
+    {
+        double angle = QLineF( rect().center(), pos ).angle();
+        if ( d_data->mode == QwtDial::RotateScale )
+            angle = 360.0 - angle;
+
+        double valueAngle = 
+            qwtNormalizeDegrees( 90.0 - transform( value() ) );
+
+        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
+        d_data->arcOffset = scaleMap().p1();
+
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the
+         slider handle.
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtDial::scrolledTo( const QPoint &pos ) const
+{
+    double angle = QLineF( rect().center(), pos ).angle();
+    if ( d_data->mode == QwtDial::RotateScale )
+    {
+        angle += scaleMap().p1() - d_data->arcOffset;
+        angle = 360.0 - angle;
+    }
+
+    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
+    angle = qwtNormalizeDegrees( 90.0 - angle );
+
+    if ( scaleMap().pDist() >= 360.0 )
+    {
+        if ( angle < scaleMap().p1() )
+            angle += 360.0;
+
+        if ( !wrapping() )
+        {
+            double boundedAngle = angle;
+
+            const double arc = angle - transform( value() );
+            if ( qAbs( arc ) > 180.0 )
+            {
+                boundedAngle = ( arc > 0 ) 
+                    ? scaleMap().p1() : scaleMap().p2();
+            }
+
+            d_data->mouseOffset += ( boundedAngle - angle );
+
+            angle = boundedAngle;
+        }
+    }
+    else
+    {
+        const double boundedAngle =
+            qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );
+
+        if ( !wrapping() )
+            d_data->mouseOffset += ( boundedAngle - angle );
+
+        angle = boundedAngle;
+    }
+
+    return invTransform( angle );
+}
+
+/*!
+  Change Event handler
+  \param event Change event
+
+  Invalidates internal paint caches if necessary
+*/
+void QwtDial::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::EnabledChange:
+        case QEvent::FontChange:
+        case QEvent::StyleChange:
+        case QEvent::PaletteChange:
+        case QEvent::LanguageChange:
+        case QEvent::LocaleChange:
+        {
+            invalidateCache();
+            break;
+        }
+        default:
+            break;
+    }
+    
+    QwtAbstractSlider::changeEvent( event );
+}
+
+/*!
+  Wheel Event handler
+  \param event Wheel event
+*/
+void QwtDial::wheelEvent( QWheelEvent *event )
+{
+    const QRegion region( innerRect(), QRegion::Ellipse );
+    if ( region.contains( event->pos() ) )
+        QwtAbstractSlider::wheelEvent( event );
+}
+
+void QwtDial::setAngleRange( double angle, double span )
+{
+    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
+    if ( sd  )
+    {
+        angle = qwtNormalizeDegrees( angle - 270.0 );
+        sd->setAngleRange( angle, angle + span );
+    }
+}
+
+/*!
+  Invalidate the internal caches and call 
+  QwtAbstractSlider::scaleChange()
+ */
+void QwtDial::scaleChange()
+{
+    invalidateCache();
+    QwtAbstractSlider::scaleChange();
+}
+
+void QwtDial::sliderChange()
+{
+    setAngleRange( d_data->origin + d_data->minScaleArc,
+        d_data->maxScaleArc - d_data->minScaleArc );
+
+    if ( mode() == RotateScale )
+    {
+        const double arc = transform( value() ) - scaleMap().p1();
+        setAngleRange( d_data->origin - arc,
+            d_data->maxScaleArc - d_data->minScaleArc );
+    }
+
+    QwtAbstractSlider::sliderChange();
+}
diff --git a/qwt/qwt_dial.h b/qwt/qwt_dial.h
new file mode 100644
index 0000000..a409b4b
--- /dev/null
+++ b/qwt/qwt_dial.h
@@ -0,0 +1,168 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DIAL_H
+#define QWT_DIAL_H 1
+
+#include "qwt_global.h"
+#include "qwt_abstract_slider.h"
+#include "qwt_abstract_scale_draw.h"
+#include <qframe.h>
+#include <qpalette.h>
+
+class QwtDialNeedle;
+class QwtRoundScaleDraw;
+
+/*!
+  \brief QwtDial class provides a rounded range control.
+
+  QwtDial is intended as base class for dial widgets like
+  speedometers, compass widgets, clocks ...
+
+  \image html dials2.png
+
+  A dial contains a scale and a needle indicating the current value
+  of the dial. Depending on Mode one of them is fixed and the
+  other is rotating. If not isReadOnly() the
+  dial can be rotated by dragging the mouse or using keyboard inputs
+  (see QwtAbstractSlider::keyPressEvent()). A dial might be wrapping, what means
+  a rotation below/above one limit continues on the other limit (f.e compass).
+  The scale might cover any arc of the dial, its values are related to
+  the origin() of the dial.
+
+  Often dials have to be updated very often according to values from external
+  devices. For these high refresh rates QwtDial caches as much as possible.
+  For derived classes it might be necessary to clear these caches manually
+  according to attribute changes using invalidateCache().
+
+  \sa QwtCompass, QwtAnalogClock, QwtDialNeedle
+  \note The controls and dials examples shows different types of dials.
+  \note QDial is more similar to QwtKnob than to QwtDial
+*/
+
+class QWT_EXPORT QwtDial: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS( Shadow Mode Direction )
+
+    Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth )
+    Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow )
+    Q_PROPERTY( Mode mode READ mode WRITE setMode )
+    Q_PROPERTY( double origin READ origin WRITE setOrigin )
+    Q_PROPERTY( double minScaleArc READ minScaleArc WRITE setMinScaleArc )
+    Q_PROPERTY( double maxScaleArc READ maxScaleArc WRITE setMaxScaleArc )
+
+public:
+
+    /*!
+        \brief Frame shadow
+
+         Unfortunately it is not possible to use QFrame::Shadow
+         as a property of a widget that is not derived from QFrame.
+         The following enum is made for the designer only. It is safe
+         to use QFrame::Shadow instead.
+     */
+    enum Shadow
+    {
+        //! QFrame::Plain
+        Plain = QFrame::Plain,
+
+        //! QFrame::Raised
+        Raised = QFrame::Raised,
+
+        //! QFrame::Sunken
+        Sunken = QFrame::Sunken
+    };
+
+    //! Mode controlling whether the needle or the scale is rotating
+    enum Mode
+    {
+        //! The needle is rotating
+        RotateNeedle,
+
+        //! The needle is fixed, the scales are rotating
+        RotateScale
+    };
+
+    explicit QwtDial( QWidget *parent = NULL );
+    virtual ~QwtDial();
+
+    void setFrameShadow( Shadow );
+    Shadow frameShadow() const;
+
+    void setLineWidth( int );
+    int lineWidth() const;
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    void setScaleArc( double min, double max );
+
+    void setMinScaleArc( double min );
+    double minScaleArc() const;
+
+    void setMaxScaleArc( double min );
+    double maxScaleArc() const;
+
+    virtual void setOrigin( double );
+    double origin() const;
+
+    void setNeedle( QwtDialNeedle * );
+    const QwtDialNeedle *needle() const;
+    QwtDialNeedle *needle();
+
+    QRect boundingRect() const;
+    QRect innerRect() const;
+
+    virtual QRect scaleInnerRect() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtRoundScaleDraw * );
+
+    QwtRoundScaleDraw *scaleDraw();
+    const QwtRoundScaleDraw *scaleDraw() const;
+
+protected:
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void paintEvent( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+
+    virtual void drawFrame( QPainter *p );
+    virtual void drawContents( QPainter * ) const;
+    virtual void drawFocusIndicator( QPainter * ) const;
+
+    void invalidateCache();
+
+    virtual void drawScale( QPainter *, 
+        const QPointF &center, double radius ) const;
+
+    virtual void drawScaleContents( QPainter *painter, 
+        const QPointF &center, double radius ) const;
+
+    virtual void drawNeedle( QPainter *, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+    virtual void sliderChange();
+    virtual void scaleChange();
+
+private:
+    void setAngleRange( double angle, double span );
+    void drawNeedle( QPainter * ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_dial_needle.cpp b/qwt/qwt_dial_needle.cpp
new file mode 100644
index 0000000..1b53a3d
--- /dev/null
+++ b/qwt/qwt_dial_needle.cpp
@@ -0,0 +1,440 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_dial_needle.h"
+#include "qwt_global.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include <qapplication.h>
+#include <qpainter.h>
+
+#if QT_VERSION < 0x040601
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+static void qwtDrawStyle1Needle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length )
+{
+    const double r[] = { 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4 };
+    const double a[] = { -45, -20, -15, 0, 15, 20, 45 };
+
+    QPainterPath path;
+    for ( int i = 0; i < 7; i++ )
+    {
+        const double angle = a[i] / 180.0 * M_PI;
+        const double radius = r[i] * length;
+
+        const double x = radius * qFastCos( angle );
+        const double y = radius * qFastSin( angle );
+
+        path.lineTo( x, -y );
+    }
+
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( palette.brush( colorGroup, QPalette::Light ) );
+    painter->drawPath( path );
+}
+
+static void qwtDrawStyle2Needle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup, double length )
+{
+    const double ratioX = 0.7;
+    const double ratioY = 0.3;
+
+    QPainterPath path1;
+    path1.lineTo( ratioX * length, 0.0 );
+    path1.lineTo( length, ratioY * length );
+
+    QPainterPath path2;
+    path2.lineTo( ratioX * length, 0.0 );
+    path2.lineTo( length, -ratioY * length );
+
+    painter->setPen( Qt::NoPen );
+
+    painter->setBrush( palette.brush( colorGroup, QPalette::Light ) );
+    painter->drawPath( path1 );
+
+    painter->setBrush( palette.brush( colorGroup, QPalette::Dark ) );
+    painter->drawPath( path2 );
+}
+
+static void qwtDrawShadedPointer( QPainter *painter, 
+    const QColor &lightColor, const QColor &darkColor,
+    double length, double width )
+{
+    const double peak = qMax( length / 10.0, 5.0 );
+
+    const double knobWidth = width + 8;
+    QRectF knobRect( 0, 0, knobWidth, knobWidth );
+    knobRect.moveCenter( QPointF(0, 0) );
+
+    QPainterPath path1;
+    path1.lineTo( 0.0, 0.5 * width );
+    path1.lineTo( length - peak, 0.5 * width );
+    path1.lineTo( length, 0.0 );
+    path1.lineTo( 0.0, 0.0 );
+
+    QPainterPath arcPath1;
+    arcPath1.arcTo( knobRect, 0.0, -90.0 );
+
+    path1 = path1.united( arcPath1 );
+
+    QPainterPath path2;
+    path2.lineTo( 0.0, -0.5 * width );
+    path2.lineTo( length - peak, -0.5 * width );
+    path2.lineTo( length, 0.0 );
+    path2.lineTo( 0.0, 0.0 );
+
+    QPainterPath arcPath2;
+    arcPath2.arcTo( knobRect, 0.0, 90.0 );
+
+    path2 = path2.united( arcPath2 );
+
+    painter->setPen( Qt::NoPen );
+
+    painter->setBrush( lightColor );
+    painter->drawPath( path1 );
+
+    painter->setBrush( darkColor );
+    painter->drawPath( path2 );
+}
+
+static void qwtDrawArrowNeedle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length, double width )
+{
+    if ( width <= 0 )
+        width = qMax( length * 0.06, 9.0 );
+
+    const double peak = qMax( 2.0, 0.4 * width );
+
+    QPainterPath path;
+    path.moveTo( 0.0, 0.5 * width );
+    path.lineTo( length - peak, 0.3 * width );
+    path.lineTo( length, 0.0 );
+    path.lineTo( length - peak, -0.3 * width );
+    path.lineTo( 0.0, -0.5 * width );
+
+    QRectF br = path.boundingRect();
+
+    QPalette pal( palette.color( QPalette::Mid ) );
+    QColor c1 = pal.color( QPalette::Light );
+    QColor c2 = pal.color( QPalette::Dark );
+
+    QLinearGradient gradient( br.topLeft(), br.bottomLeft() );
+    gradient.setColorAt( 0.0, c1 );
+    gradient.setColorAt( 0.5, c1 );
+    gradient.setColorAt( 0.5001, c2 );
+    gradient.setColorAt( 1.0, c2 );
+
+    QPen pen( gradient, 1 );
+    pen.setJoinStyle( Qt::MiterJoin );
+
+    painter->setPen( pen );
+    painter->setBrush( palette.brush( colorGroup, QPalette::Mid ) );
+
+    painter->drawPath( path );
+}
+
+static void qwtDrawTriangleNeedle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length )
+{
+    const double width = qRound( length / 3.0 );
+
+    QPainterPath path[4];
+
+    path[0].lineTo( length, 0.0 );
+    path[0].lineTo( 0.0, width / 2 );
+
+    path[1].lineTo( length, 0.0 );
+    path[1].lineTo( 0.0, -width / 2 );
+
+    path[2].lineTo( -length, 0.0 );
+    path[2].lineTo( 0.0, width / 2 );
+
+    path[3].lineTo( -length, 0.0 );
+    path[3].lineTo( 0.0, -width / 2 );
+
+
+    const int colorOffset =  10;
+    const QColor darkColor = palette.color( colorGroup, QPalette::Dark );
+    const QColor lightColor = palette.color( colorGroup, QPalette::Light );
+
+    QColor color[4];
+    color[0] = darkColor.light( 100 + colorOffset );
+    color[1] = darkColor.dark( 100 + colorOffset );
+    color[2] = lightColor.light( 100 + colorOffset );
+    color[3] = lightColor.dark( 100 + colorOffset );
+
+    painter->setPen( Qt::NoPen );
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        painter->setBrush( color[i] );
+        painter->drawPath( path[i] );
+    }
+}
+
+//! Constructor
+QwtDialNeedle::QwtDialNeedle():
+    d_palette( QApplication::palette() )
+{
+}
+
+//! Destructor
+QwtDialNeedle::~QwtDialNeedle()
+{
+}
+
+/*!
+    Sets the palette for the needle.
+
+    \param palette New Palette
+*/
+void QwtDialNeedle::setPalette( const QPalette &palette )
+{
+    d_palette = palette;
+}
+
+/*!
+  \return the palette of the needle.
+*/
+const QPalette &QwtDialNeedle::palette() const
+{
+    return d_palette;
+}
+
+/*!
+  Draw the needle
+
+  \param painter Painter
+  \param center Center of the dial, start position for the needle
+  \param length Length of the needle
+  \param direction Direction of the needle, in degrees counter clockwise
+  \param colorGroup Color group, used for painting
+*/
+void QwtDialNeedle::draw( QPainter *painter, 
+    const QPointF &center, double length, double direction, 
+    QPalette::ColorGroup colorGroup ) const
+{
+    painter->save();
+
+    painter->translate( center );
+    painter->rotate( -direction );
+
+    drawNeedle( painter, length, colorGroup );
+
+    painter->restore();
+}
+
+//!  Draw the knob
+void QwtDialNeedle::drawKnob( QPainter *painter,
+    double width, const QBrush &brush, bool sunken ) const
+{
+    QPalette palette( brush.color() );
+
+    QColor c1 = palette.color( QPalette::Light );
+    QColor c2 = palette.color( QPalette::Dark );
+
+    if ( sunken )
+        qSwap( c1, c2 );
+
+    QRectF rect( 0.0, 0.0, width, width );
+    rect.moveCenter( painter->combinedTransform().map( QPointF() ) );
+
+    QLinearGradient gradient( rect.topLeft(), rect.bottomRight() );
+    gradient.setColorAt( 0.0, c1 );
+    gradient.setColorAt( 0.3, c1 );
+    gradient.setColorAt( 0.7, c2 );
+    gradient.setColorAt( 1.0, c2 );
+
+    painter->save();
+
+    painter->resetTransform();
+
+    painter->setPen( QPen( gradient, 1 ) );
+    painter->setBrush( brush );
+    painter->drawEllipse( rect );
+
+    painter->restore();
+}
+
+/*!
+  Constructor
+
+  \param style Style
+  \param hasKnob With/Without knob
+  \param mid Middle color
+  \param base Base color
+*/
+QwtDialSimpleNeedle::QwtDialSimpleNeedle( Style style, bool hasKnob,
+        const QColor &mid, const QColor &base ):
+    d_style( style ),
+    d_hasKnob( hasKnob ),
+    d_width( -1 )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Mid, mid );
+    palette.setColor( QPalette::Base, base );
+
+    setPalette( palette );
+}
+
+/*!
+  Set the width of the needle
+  \param width Width
+  \sa width()
+*/
+void QwtDialSimpleNeedle::setWidth( double width )
+{
+    d_width = width;
+}
+
+/*!
+  \return the width of the needle
+  \sa setWidth()
+*/
+double QwtDialSimpleNeedle::width() const
+{
+    return d_width;
+}
+
+/*!
+ Draw the needle
+
+ \param painter Painter
+ \param length Length of the needle
+ \param colorGroup Color group, used for painting
+*/
+void QwtDialSimpleNeedle::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    double knobWidth = 0.0;
+    double width = d_width;
+
+    if ( d_style == Arrow )
+    {
+        if ( width <= 0.0 )
+            width = qMax(length * 0.06, 6.0);
+
+        qwtDrawArrowNeedle( painter, 
+            palette(), colorGroup, length, width );
+
+        knobWidth = qMin( width * 2.0, 0.2 * length );
+    }
+    else
+    {
+        if ( width <= 0.0 )
+            width = 5.0;
+        
+        QPen pen ( palette().brush( colorGroup, QPalette::Mid ), width );
+        pen.setCapStyle( Qt::FlatCap );
+    
+        painter->setPen( pen );
+        painter->drawLine( QPointF( 0.0, 0.0 ), QPointF( length, 0.0 ) );
+
+        knobWidth = qMax( width * 3.0, 5.0 );
+    }
+
+    if ( d_hasKnob && knobWidth > 0.0 )
+    {
+        drawKnob( painter, knobWidth,
+            palette().brush( colorGroup, QPalette::Base ), false );
+    }
+}
+
+//! Constructor
+QwtCompassMagnetNeedle::QwtCompassMagnetNeedle( Style style,
+        const QColor &light, const QColor &dark ):
+    d_style( style )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Light, light );
+    palette.setColor( QPalette::Dark, dark );
+    palette.setColor( QPalette::Base, Qt::gray );
+
+    setPalette( palette );
+}
+
+/*!
+    Draw the needle
+
+    \param painter Painter
+    \param length Length of the needle
+    \param colorGroup Color group, used for painting
+*/
+void QwtCompassMagnetNeedle::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_style == ThinStyle )
+    {
+        const double width = qMax( length / 6.0, 3.0 );
+
+        const int colorOffset = 10;
+
+        const QColor light = palette().color( colorGroup, QPalette::Light );
+        const QColor dark = palette().color( colorGroup, QPalette::Dark );
+
+        qwtDrawShadedPointer( painter,
+            dark.light( 100 + colorOffset ),
+            dark.dark( 100 + colorOffset ),
+            length, width );
+        
+        painter->rotate( 180.0 );
+    
+        qwtDrawShadedPointer( painter,
+            light.light( 100 + colorOffset ),
+            light.dark( 100 + colorOffset ),
+            length, width );
+        
+        const QBrush baseBrush = palette().brush( colorGroup, QPalette::Base );
+        drawKnob( painter, width, baseBrush, true );
+    }
+    else
+    {
+        qwtDrawTriangleNeedle( painter, palette(), colorGroup, length );
+    }
+}
+
+/*!
+   Constructor
+
+   \param style Arrow style
+   \param light Light color
+   \param dark Dark color
+*/
+QwtCompassWindArrow::QwtCompassWindArrow( Style style,
+        const QColor &light, const QColor &dark ):
+    d_style( style )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Light, light );
+    palette.setColor( QPalette::Dark, dark );
+
+    setPalette( palette );
+}
+
+/*!
+ Draw the needle
+
+ \param painter Painter
+ \param length Length of the needle
+ \param colorGroup Color group, used for painting
+*/
+void QwtCompassWindArrow::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_style == Style1 )
+        qwtDrawStyle1Needle( painter, palette(), colorGroup, length );
+    else
+        qwtDrawStyle2Needle( painter, palette(), colorGroup, length );
+}
diff --git a/qwt/qwt_dial_needle.h b/qwt/qwt_dial_needle.h
new file mode 100644
index 0000000..d84384a
--- /dev/null
+++ b/qwt/qwt_dial_needle.h
@@ -0,0 +1,187 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DIAL_NEEDLE_H
+#define QWT_DIAL_NEEDLE_H 1
+
+#include "qwt_global.h"
+#include <qpalette.h>
+
+class QPainter;
+class QPoint;
+
+/*!
+  \brief Base class for needles that can be used in a QwtDial.
+
+  QwtDialNeedle is a pointer that indicates a value by pointing
+  to a specific direction.
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtDialNeedle
+{
+public:
+    QwtDialNeedle();
+    virtual ~QwtDialNeedle();
+
+    virtual void setPalette( const QPalette & );
+    const QPalette &palette() const;
+
+    virtual void draw( QPainter *painter, const QPointF &center,
+        double length, double direction, 
+        QPalette::ColorGroup = QPalette::Active ) const;
+
+protected:
+    /*!
+      \brief Draw the needle
+
+      The origin of the needle is at position (0.0, 0.0 )
+      pointing in direction 0.0 ( = east ). 
+
+      The painter is already initialized with translation and 
+      rotation.
+
+      \param painter Painter
+      \param length Length of the needle
+      \param colorGroup Color group, used for painting
+
+      \sa setPalette(), palette()
+    */
+    virtual void drawNeedle( QPainter *painter, 
+        double length, QPalette::ColorGroup colorGroup ) const = 0;
+
+    virtual void drawKnob( QPainter *, double width, 
+        const QBrush &, bool sunken ) const;
+
+private:
+    QPalette d_palette;
+};
+
+/*!
+  \brief A needle for dial widgets
+
+  The following colors are used:
+
+  - QPalette::Mid\n
+    Pointer
+  - QPalette::Base\n
+    Knob
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtDialSimpleNeedle: public QwtDialNeedle
+{
+public:
+    //! Style of the needle
+    enum Style
+    {
+        //! Arrow
+        Arrow,
+
+        //! A straight line from the center
+        Ray
+    };
+
+    QwtDialSimpleNeedle( Style, bool hasKnob = true,
+        const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray );
+
+    void setWidth( double width );
+    double width() const;
+
+protected:
+    virtual void drawNeedle( QPainter *, double length,
+        QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+    bool d_hasKnob;
+    double d_width;
+};
+
+/*!
+  \brief A magnet needle for compass widgets
+
+  A magnet needle points to two opposite directions indicating
+  north and south.
+
+  The following colors are used:
+  - QPalette::Light\n
+    Used for pointing south
+  - QPalette::Dark\n
+    Used for pointing north
+  - QPalette::Base\n
+    Knob (ThinStyle only)
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtCompassMagnetNeedle: public QwtDialNeedle
+{
+public:
+    //! Style of the needle
+    enum Style
+    {
+        //! A needle with a triangular shape
+        TriangleStyle,
+
+        //! A thin needle 
+        ThinStyle
+    };
+
+    QwtCompassMagnetNeedle( Style = TriangleStyle,
+        const QColor &light = Qt::white, const QColor &dark = Qt::red );
+
+protected:
+    virtual void drawNeedle( QPainter *, 
+        double length, QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+};
+
+/*!
+  \brief An indicator for the wind direction
+
+  QwtCompassWindArrow shows the direction where the wind comes from.
+
+  - QPalette::Light\n
+    Used for Style1, or the light half of Style2
+  - QPalette::Dark\n
+    Used for the dark half of Style2
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtCompassWindArrow: public QwtDialNeedle
+{
+public:
+    //! Style of the arrow
+    enum Style
+    {
+        //! A needle pointing to the center
+        Style1,
+
+        //! A needle pointing to the center
+        Style2
+    };
+
+    QwtCompassWindArrow( Style, const QColor &light = Qt::white,
+        const QColor &dark = Qt::gray );
+
+protected:
+    virtual void drawNeedle( QPainter *, 
+        double length, QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+};
+
+#endif 
diff --git a/qwt/qwt_dyngrid_layout.cpp b/qwt/qwt_dyngrid_layout.cpp
new file mode 100644
index 0000000..9e0fa28
--- /dev/null
+++ b/qwt/qwt_dyngrid_layout.cpp
@@ -0,0 +1,591 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_dyngrid_layout.h"
+#include "qwt_math.h"
+#include <qvector.h>
+#include <qlist.h>
+
+class QwtDynGridLayout::PrivateData
+{
+public:
+    PrivateData():
+        isDirty( true )
+    {
+    }
+
+    void updateLayoutCache();
+
+    mutable QList<QLayoutItem*> itemList;
+
+    uint maxColumns;
+    uint numRows;
+    uint numColumns;
+
+    Qt::Orientations expanding;
+
+    bool isDirty;
+    QVector<QSize> itemSizeHints;
+};
+
+void QwtDynGridLayout::PrivateData::updateLayoutCache()
+{
+    itemSizeHints.resize( itemList.count() );
+
+    int index = 0;
+
+    for ( QList<QLayoutItem*>::iterator it = itemList.begin();
+        it != itemList.end(); ++it, index++ )
+    {
+        itemSizeHints[ index ] = ( *it )->sizeHint();
+    }
+
+    isDirty = false;
+}
+
+/*!
+  \param parent Parent widget
+  \param margin Margin
+  \param spacing Spacing
+*/
+
+QwtDynGridLayout::QwtDynGridLayout( QWidget *parent,
+        int margin, int spacing ):
+    QLayout( parent )
+{
+    init();
+
+    setSpacing( spacing );
+    setMargin( margin );
+}
+
+/*!
+  \param spacing Spacing
+*/
+
+QwtDynGridLayout::QwtDynGridLayout( int spacing )
+{
+    init();
+    setSpacing( spacing );
+}
+
+/*!
+  Initialize the layout with default values.
+*/
+void QwtDynGridLayout::init()
+{
+    d_data = new QwtDynGridLayout::PrivateData;
+    d_data->maxColumns = d_data->numRows = d_data->numColumns = 0;
+    d_data->expanding = 0;
+}
+
+//! Destructor
+
+QwtDynGridLayout::~QwtDynGridLayout()
+{
+    for ( int i = 0; i < d_data->itemList.size(); i++ )
+        delete d_data->itemList[i];
+
+    delete d_data;
+}
+
+//! Invalidate all internal caches
+void QwtDynGridLayout::invalidate()
+{
+    d_data->isDirty = true;
+    QLayout::invalidate();
+}
+
+/*!
+  Limit the number of columns.
+  \param maxColumns upper limit, 0 means unlimited
+  \sa maxColumns()
+*/
+void QwtDynGridLayout::setMaxColumns( uint maxColumns )
+{
+    d_data->maxColumns = maxColumns;
+}
+
+/*!
+  \brief Return the upper limit for the number of columns.
+
+  0 means unlimited, what is the default.
+
+  \return Upper limit for the number of columns
+  \sa setMaxColumns()
+*/
+uint QwtDynGridLayout::maxColumns() const
+{
+    return d_data->maxColumns;
+}
+
+/*! 
+  \brief Add an item to the next free position.
+  \param item Layout item
+ */
+void QwtDynGridLayout::addItem( QLayoutItem *item )
+{
+    d_data->itemList.append( item );
+    invalidate();
+}
+
+/*!
+  \return true if this layout is empty.
+*/
+bool QwtDynGridLayout::isEmpty() const
+{
+    return d_data->itemList.isEmpty();
+}
+
+/*!
+  \return number of layout items
+*/
+uint QwtDynGridLayout::itemCount() const
+{
+    return d_data->itemList.count();
+}
+
+/*!
+  Find the item at a specific index
+
+  \param index Index
+  \return Item at a specific index
+  \sa takeAt()
+*/
+QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
+{
+    if ( index < 0 || index >= d_data->itemList.count() )
+        return NULL;
+
+    return d_data->itemList.at( index );
+}
+
+/*!
+  Find the item at a specific index and remove it from the layout
+
+  \param index Index
+  \return Layout item, removed from the layout
+  \sa itemAt()
+*/
+QLayoutItem *QwtDynGridLayout::takeAt( int index )
+{
+    if ( index < 0 || index >= d_data->itemList.count() )
+        return NULL;
+
+    d_data->isDirty = true;
+    return d_data->itemList.takeAt( index );
+}
+
+//! \return Number of items in the layout
+int QwtDynGridLayout::count() const
+{
+    return d_data->itemList.count();
+}
+
+/*!
+  Set whether this layout can make use of more space than sizeHint().
+  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
+  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
+  to grow in both dimensions. The default value is 0.
+
+  \param expanding Or'd orientations
+  \sa expandingDirections()
+*/
+void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
+{
+    d_data->expanding = expanding;
+}
+
+/*!
+  \brief Returns whether this layout can make use of more space than sizeHint().
+
+  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
+  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
+  to grow in both dimensions.
+
+  \return Orientations, where the layout expands
+  \sa setExpandingDirections()
+*/
+Qt::Orientations QwtDynGridLayout::expandingDirections() const
+{
+    return d_data->expanding;
+}
+
+/*!
+  Reorganizes columns and rows and resizes managed items within
+  a rectangle.
+
+  \param rect Layout geometry
+*/
+void QwtDynGridLayout::setGeometry( const QRect &rect )
+{
+    QLayout::setGeometry( rect );
+
+    if ( isEmpty() )
+        return;
+
+    d_data->numColumns = columnsForWidth( rect.width() );
+    d_data->numRows = itemCount() / d_data->numColumns;
+    if ( itemCount() % d_data->numColumns )
+        d_data->numRows++;
+
+    QList<QRect> itemGeometries = layoutItems( rect, d_data->numColumns );
+
+    int index = 0;
+    for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
+        it != d_data->itemList.end(); ++it )
+    {
+        ( *it )->setGeometry( itemGeometries[index] );
+        index++;
+    }
+}
+
+/*!
+  \brief Calculate the number of columns for a given width. 
+
+  The calculation tries to use as many columns as possible 
+  ( limited by maxColumns() )
+
+  \param width Available width for all columns
+  \return Number of columns for a given width
+
+  \sa maxColumns(), setMaxColumns()
+*/
+uint QwtDynGridLayout::columnsForWidth( int width ) const
+{
+    if ( isEmpty() )
+        return 0;
+
+    uint maxColumns = itemCount();
+    if ( d_data->maxColumns > 0 ) 
+        maxColumns = qMin( d_data->maxColumns, maxColumns );
+
+    if ( maxRowWidth( maxColumns ) <= width )
+        return maxColumns;
+
+    for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
+    {
+        const int rowWidth = maxRowWidth( numColumns );
+        if ( rowWidth > width )
+            return numColumns - 1;
+    }
+
+    return 1; // At least 1 column
+}
+
+/*!
+  Calculate the width of a layout for a given number of
+  columns.
+
+  \param numColumns Given number of columns
+  \param itemWidth Array of the width hints for all items
+*/
+int QwtDynGridLayout::maxRowWidth( int numColumns ) const
+{
+    int col;
+
+    QVector<int> colWidth( numColumns );
+    for ( col = 0; col < numColumns; col++ )
+        colWidth[col] = 0;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    for ( int index = 0;
+        index < d_data->itemSizeHints.count(); index++ )
+    {
+        col = index % numColumns;
+        colWidth[col] = qMax( colWidth[col],
+            d_data->itemSizeHints[int( index )].width() );
+    }
+
+    int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing();
+    for ( col = 0; col < numColumns; col++ )
+        rowWidth += colWidth[col];
+
+    return rowWidth;
+}
+
+/*!
+  \return the maximum width of all layout items
+*/
+int QwtDynGridLayout::maxItemWidth() const
+{
+    if ( isEmpty() )
+        return 0;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    int w = 0;
+    for ( int i = 0; i < d_data->itemSizeHints.count(); i++ )
+    {
+        const int itemW = d_data->itemSizeHints[i].width();
+        if ( itemW > w )
+            w = itemW;
+    }
+
+    return w;
+}
+
+/*!
+  Calculate the geometries of the layout items for a layout
+  with numColumns columns and a given rectangle.
+
+  \param rect Rect where to place the items
+  \param numColumns Number of columns
+  \return item geometries
+*/
+
+QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
+    uint numColumns ) const
+{
+    QList<QRect> itemGeometries;
+    if ( numColumns == 0 || isEmpty() )
+        return itemGeometries;
+
+    uint numRows = itemCount() / numColumns;
+    if ( numColumns % itemCount() )
+        numRows++;
+
+    if ( numRows == 0 )
+        return itemGeometries;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    bool expandH, expandV;
+    expandH = expandingDirections() & Qt::Horizontal;
+    expandV = expandingDirections() & Qt::Vertical;
+
+    if ( expandH || expandV )
+        stretchGrid( rect, numColumns, rowHeight, colWidth );
+
+    const int maxColumns = d_data->maxColumns;
+    d_data->maxColumns = numColumns;
+    const QRect alignedRect = alignmentRect( rect );
+    d_data->maxColumns = maxColumns;
+
+    const int xOffset = expandH ? 0 : alignedRect.x();
+    const int yOffset = expandV ? 0 : alignedRect.y();
+
+    QVector<int> colX( numColumns );
+    QVector<int> rowY( numRows );
+
+    const int xySpace = spacing();
+
+    rowY[0] = yOffset + margin();
+    for ( uint r = 1; r < numRows; r++ )
+        rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
+
+    colX[0] = xOffset + margin();
+    for ( uint c = 1; c < numColumns; c++ )
+        colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
+
+    const int itemCount = d_data->itemList.size();
+    for ( int i = 0; i < itemCount; i++ )
+    {
+        const int row = i / numColumns;
+        const int col = i % numColumns;
+
+        QRect itemGeometry( colX[col], rowY[row],
+            colWidth[col], rowHeight[row] );
+        itemGeometries.append( itemGeometry );
+    }
+
+    return itemGeometries;
+}
+
+
+/*!
+  Calculate the dimensions for the columns and rows for a grid
+  of numColumns columns.
+
+  \param numColumns Number of columns.
+  \param rowHeight Array where to fill in the calculated row heights.
+  \param colWidth Array where to fill in the calculated column widths.
+*/
+
+void QwtDynGridLayout::layoutGrid( uint numColumns,
+    QVector<int>& rowHeight, QVector<int>& colWidth ) const
+{
+    if ( numColumns <= 0 )
+        return;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    for ( int index = 0; index < d_data->itemSizeHints.count(); index++ )
+    {
+        const int row = index / numColumns;
+        const int col = index % numColumns;
+
+        const QSize &size = d_data->itemSizeHints[int( index )];
+
+        rowHeight[row] = ( col == 0 )
+            ? size.height() : qMax( rowHeight[row], size.height() );
+        colWidth[col] = ( row == 0 )
+            ? size.width() : qMax( colWidth[col], size.width() );
+    }
+}
+
+/*!
+  \return true: QwtDynGridLayout implements heightForWidth().
+  \sa heightForWidth()
+*/
+bool QwtDynGridLayout::hasHeightForWidth() const
+{
+    return true;
+}
+
+/*!
+  \return The preferred height for this layout, given a width.
+  \sa hasHeightForWidth()
+*/
+int QwtDynGridLayout::heightForWidth( int width ) const
+{
+    if ( isEmpty() )
+        return 0;
+
+    const uint numColumns = columnsForWidth( width );
+    uint numRows = itemCount() / numColumns;
+    if ( itemCount() % numColumns )
+        numRows++;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    int h = 2 * margin() + ( numRows - 1 ) * spacing();
+    for ( uint row = 0; row < numRows; row++ )
+        h += rowHeight[row];
+
+    return h;
+}
+
+/*!
+  Stretch columns in case of expanding() & QSizePolicy::Horizontal and
+  rows in case of expanding() & QSizePolicy::Vertical to fill the entire
+  rect. Rows and columns are stretched with the same factor.
+
+  \param rect Bounding rectangle
+  \param numColumns Number of columns
+  \param rowHeight Array to be filled with the calculated row heights
+  \param colWidth Array to be filled with the calculated column widths
+
+  \sa setExpanding(), expanding()
+*/
+void QwtDynGridLayout::stretchGrid( const QRect &rect,
+    uint numColumns, QVector<int>& rowHeight, QVector<int>& colWidth ) const
+{
+    if ( numColumns == 0 || isEmpty() )
+        return;
+
+    bool expandH, expandV;
+    expandH = expandingDirections() & Qt::Horizontal;
+    expandV = expandingDirections() & Qt::Vertical;
+
+    if ( expandH )
+    {
+        int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing();
+        for ( uint col = 0; col < numColumns; col++ )
+            xDelta -= colWidth[col];
+
+        if ( xDelta > 0 )
+        {
+            for ( uint col = 0; col < numColumns; col++ )
+            {
+                const int space = xDelta / ( numColumns - col );
+                colWidth[col] += space;
+                xDelta -= space;
+            }
+        }
+    }
+
+    if ( expandV )
+    {
+        uint numRows = itemCount() / numColumns;
+        if ( itemCount() % numColumns )
+            numRows++;
+
+        int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
+        for ( uint row = 0; row < numRows; row++ )
+            yDelta -= rowHeight[row];
+
+        if ( yDelta > 0 )
+        {
+            for ( uint row = 0; row < numRows; row++ )
+            {
+                const int space = yDelta / ( numRows - row );
+                rowHeight[row] += space;
+                yDelta -= space;
+            }
+        }
+    }
+}
+
+/*!
+   Return the size hint. If maxColumns() > 0 it is the size for
+   a grid with maxColumns() columns, otherwise it is the size for
+   a grid with only one row.
+
+   \return Size hint
+   \sa maxColumns(), setMaxColumns()
+*/
+QSize QwtDynGridLayout::sizeHint() const
+{
+    if ( isEmpty() )
+        return QSize();
+
+    uint numColumns = itemCount();
+    if ( d_data->maxColumns > 0 )
+        numColumns = qMin( d_data->maxColumns, numColumns );
+
+    uint numRows = itemCount() / numColumns;
+    if ( itemCount() % numColumns )
+        numRows++;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    int h = 2 * margin() + ( numRows - 1 ) * spacing();
+    for ( uint row = 0; row < numRows; row++ )
+        h += rowHeight[row];
+
+    int w = 2 * margin() + ( numColumns - 1 ) * spacing();
+    for ( uint col = 0; col < numColumns; col++ )
+        w += colWidth[col];
+
+    return QSize( w, h );
+}
+
+/*!
+  \return Number of rows of the current layout.
+  \sa numColumns()
+  \warning The number of rows might change whenever the geometry changes
+*/
+uint QwtDynGridLayout::numRows() const
+{
+    return d_data->numRows;
+}
+
+/*!
+  \return Number of columns of the current layout.
+  \sa numRows()
+  \warning The number of columns might change whenever the geometry changes
+*/
+uint QwtDynGridLayout::numColumns() const
+{
+    return d_data->numColumns;
+}
diff --git a/qwt/qwt_dyngrid_layout.h b/qwt/qwt_dyngrid_layout.h
new file mode 100644
index 0000000..dd20266
--- /dev/null
+++ b/qwt/qwt_dyngrid_layout.h
@@ -0,0 +1,83 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DYNGRID_LAYOUT_H
+#define QWT_DYNGRID_LAYOUT_H
+
+#include "qwt_global.h"
+#include <qlayout.h>
+#include <qsize.h>
+#include <qlist.h>
+
+/*!
+  \brief The QwtDynGridLayout class lays out widgets in a grid,
+         adjusting the number of columns and rows to the current size.
+
+  QwtDynGridLayout takes the space it gets, divides it up into rows and
+  columns, and puts each of the widgets it manages into the correct cell(s).
+  It lays out as many number of columns as possible (limited by maxColumns()).
+*/
+
+class QWT_EXPORT QwtDynGridLayout : public QLayout
+{
+    Q_OBJECT
+public:
+    explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 );
+    explicit QwtDynGridLayout( int space = -1 );
+
+    virtual ~QwtDynGridLayout();
+
+    virtual void invalidate();
+
+    void setMaxColumns( uint maxCols );
+    uint maxColumns() const;
+
+    uint numRows () const;
+    uint numColumns () const;
+
+    virtual void addItem( QLayoutItem * );
+
+    virtual QLayoutItem *itemAt( int index ) const;
+    virtual QLayoutItem *takeAt( int index );
+    virtual int count() const;
+
+    void setExpandingDirections( Qt::Orientations );
+    virtual Qt::Orientations expandingDirections() const;
+    QList<QRect> layoutItems( const QRect &, uint numCols ) const;
+
+    virtual int maxItemWidth() const;
+
+    virtual void setGeometry( const QRect &rect );
+
+    virtual bool hasHeightForWidth() const;
+    virtual int heightForWidth( int ) const;
+
+    virtual QSize sizeHint() const;
+
+    virtual bool isEmpty() const;
+    uint itemCount() const;
+
+    virtual uint columnsForWidth( int width ) const;
+
+protected:
+
+    void layoutGrid( uint numCols,
+        QVector<int>& rowHeight, QVector<int>& colWidth ) const;
+    void stretchGrid( const QRect &rect, uint numCols,
+        QVector<int>& rowHeight, QVector<int>& colWidth ) const;
+
+private:
+    void init();
+    int maxRowWidth( int numCols ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_event_pattern.cpp b/qwt/qwt_event_pattern.cpp
new file mode 100644
index 0000000..4637743
--- /dev/null
+++ b/qwt/qwt_event_pattern.cpp
@@ -0,0 +1,265 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_event_pattern.h"
+#include <qevent.h>
+
+/*!
+  Constructor
+
+  \sa MousePatternCode, KeyPatternCode
+*/
+
+QwtEventPattern::QwtEventPattern():
+    d_mousePattern( MousePatternCount ),
+    d_keyPattern( KeyPatternCount )
+{
+    initKeyPattern();
+    initMousePattern( 3 );
+}
+
+//! Destructor
+QwtEventPattern::~QwtEventPattern()
+{
+}
+
+/*!
+  Set default mouse patterns, depending on the number of mouse buttons
+
+  \param numButtons Number of mouse buttons ( <= 3 )
+  \sa MousePatternCode
+*/
+void QwtEventPattern::initMousePattern( int numButtons )
+{
+    d_mousePattern.resize( MousePatternCount );
+
+    switch ( numButtons )
+    {
+        case 1:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::LeftButton, Qt::ControlModifier );
+            setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier );
+            break;
+        }
+        case 2:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::RightButton );
+            setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier );
+            break;
+        }
+        default:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::RightButton );
+            setMousePattern( MouseSelect3, Qt::MidButton );
+        }
+    }
+
+    setMousePattern( MouseSelect4, d_mousePattern[MouseSelect1].button,
+        d_mousePattern[MouseSelect1].modifiers | Qt::ShiftModifier );
+
+    setMousePattern( MouseSelect5, d_mousePattern[MouseSelect2].button,
+        d_mousePattern[MouseSelect2].modifiers | Qt::ShiftModifier );
+
+    setMousePattern( MouseSelect6, d_mousePattern[MouseSelect3].button,
+        d_mousePattern[MouseSelect3].modifiers | Qt::ShiftModifier );
+}
+
+/*!
+  Set default mouse patterns.
+
+  \sa KeyPatternCode
+*/
+void QwtEventPattern::initKeyPattern()
+{
+    d_keyPattern.resize( KeyPatternCount );
+
+    setKeyPattern( KeySelect1, Qt::Key_Return );
+    setKeyPattern( KeySelect2, Qt::Key_Space );
+    setKeyPattern( KeyAbort, Qt::Key_Escape );
+
+    setKeyPattern( KeyLeft, Qt::Key_Left );
+    setKeyPattern( KeyRight, Qt::Key_Right );
+    setKeyPattern( KeyUp, Qt::Key_Up );
+    setKeyPattern( KeyDown, Qt::Key_Down );
+
+    setKeyPattern( KeyRedo, Qt::Key_Plus );
+    setKeyPattern( KeyUndo, Qt::Key_Minus );
+    setKeyPattern( KeyHome, Qt::Key_Escape );
+}
+
+/*!
+  Change one mouse pattern
+
+  \param pattern Index of the pattern
+  \param button Button
+  \param modifiers Keyboard modifiers
+
+  \sa QMouseEvent
+*/
+void QwtEventPattern::setMousePattern( MousePatternCode pattern, 
+    Qt::MouseButton button, Qt::KeyboardModifiers modifiers )
+{
+    if ( pattern >= 0 && pattern < MousePatternCount )
+    {
+        d_mousePattern[ pattern ].button = button;
+        d_mousePattern[ pattern ].modifiers = modifiers;
+    }
+}
+
+/*!
+  Change one key pattern
+
+  \param pattern Index of the pattern
+  \param key Key
+  \param modifiers Keyboard modifiers
+
+  \sa QKeyEvent
+*/
+void QwtEventPattern::setKeyPattern( KeyPatternCode pattern, 
+    int key, Qt::KeyboardModifiers modifiers )
+{
+    if ( pattern >= 0 && pattern < KeyPatternCount )
+    {
+        d_keyPattern[ pattern ].key = key;
+        d_keyPattern[ pattern ].modifiers = modifiers;
+    }
+}
+
+//! Change the mouse event patterns
+void QwtEventPattern::setMousePattern( const QVector<MousePattern> &pattern )
+{
+    d_mousePattern = pattern;
+}
+
+//! Change the key event patterns
+void QwtEventPattern::setKeyPattern( const QVector<KeyPattern> &pattern )
+{
+    d_keyPattern = pattern;
+}
+
+//! \return Mouse pattern
+const QVector<QwtEventPattern::MousePattern> &
+QwtEventPattern::mousePattern() const
+{
+    return d_mousePattern;
+}
+
+//! \return Key pattern
+const QVector<QwtEventPattern::KeyPattern> &
+QwtEventPattern::keyPattern() const
+{
+    return d_keyPattern;
+}
+
+//! \return Mouse pattern
+QVector<QwtEventPattern::MousePattern> &QwtEventPattern::mousePattern()
+{
+    return d_mousePattern;
+}
+
+//! \return Key pattern
+QVector<QwtEventPattern::KeyPattern> &QwtEventPattern::keyPattern()
+{
+    return d_keyPattern;
+}
+
+/*!
+  \brief Compare a mouse event with an event pattern.
+
+  A mouse event matches the pattern when both have the same button
+  value and in the state value the same key flags(Qt::KeyButtonMask)
+  are set.
+
+  \param code Index of the event pattern
+  \param event Mouse event
+  \return true if matches
+
+  \sa keyMatch()
+*/
+bool QwtEventPattern::mouseMatch( MousePatternCode code, 
+    const QMouseEvent *event ) const
+{
+    if ( code >= 0 && code < MousePatternCount )
+        return mouseMatch( d_mousePattern[ code ], event );
+
+    return false;
+}
+
+/*!
+  \brief Compare a mouse event with an event pattern.
+
+  A mouse event matches the pattern when both have the same button
+  value and in the state value the same key flags(Qt::KeyButtonMask)
+  are set.
+
+  \param pattern Mouse event pattern
+  \param event Mouse event
+  \return true if matches
+
+  \sa keyMatch()
+*/
+
+bool QwtEventPattern::mouseMatch( const MousePattern &pattern,
+    const QMouseEvent *event ) const
+{
+    if ( event == NULL )
+        return false;
+
+    const MousePattern mousePattern( event->button(), event->modifiers() );
+    return mousePattern == pattern;
+}
+
+/*!
+  \brief Compare a key event with an event pattern.
+
+  A key event matches the pattern when both have the same key
+  value and in the state value the same key flags (Qt::KeyButtonMask)
+  are set.
+
+  \param code Index of the event pattern
+  \param event Key event
+  \return true if matches
+
+  \sa mouseMatch()
+*/
+bool QwtEventPattern::keyMatch( KeyPatternCode code, 
+    const QKeyEvent *event ) const
+{
+    if ( code >= 0 && code < KeyPatternCount )
+        return keyMatch( d_keyPattern[ code ], event );
+
+    return false;
+}
+
+/*!
+  \brief Compare a key event with an event pattern.
+
+  A key event matches the pattern when both have the same key
+  value and in the state value the same key flags (Qt::KeyButtonMask)
+  are set.
+
+  \param pattern Key event pattern
+  \param event Key event
+  \return true if matches
+
+  \sa mouseMatch()
+*/
+
+bool QwtEventPattern::keyMatch(
+    const KeyPattern &pattern, const QKeyEvent *event ) const
+{
+    if ( event == NULL )
+        return false;
+
+    const KeyPattern keyPattern( event->key(), event->modifiers() );
+    return keyPattern == pattern;
+}
diff --git a/qwt/qwt_event_pattern.h b/qwt/qwt_event_pattern.h
new file mode 100644
index 0000000..7c5d1a3
--- /dev/null
+++ b/qwt/qwt_event_pattern.h
@@ -0,0 +1,240 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_EVENT_PATTERN
+#define QWT_EVENT_PATTERN 1
+
+#include "qwt_global.h"
+#include <qnamespace.h>
+#include <qvector.h>
+
+class QMouseEvent;
+class QKeyEvent;
+
+/*!
+  \brief A collection of event patterns
+
+  QwtEventPattern introduces an level of indirection for mouse and
+  keyboard inputs. Those are represented by symbolic names, so
+  the application code can be configured by individual mappings.
+
+  \sa QwtPicker, QwtPickerMachine, QwtPlotZoomer
+*/
+class QWT_EXPORT QwtEventPattern
+{
+public:
+    /*!
+      \brief Symbolic mouse input codes
+
+      QwtEventPattern implements 3 different settings for
+      mice with 1, 2, or 3 buttons that can be activated
+      using initMousePattern(). The default setting is for
+      3 button mice.
+
+      Individual settings can be configured using setMousePattern().
+
+      \sa initMousePattern(), setMousePattern(), setKeyPattern()
+    */
+    enum MousePatternCode
+    {
+        /*! 
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton 
+          - Qt::LeftButton 
+          - Qt::LeftButton 
+         */
+        MouseSelect1,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ControlModifier
+          - Qt::RightButton
+          - Qt::RightButton
+         */
+        MouseSelect2,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::AltModifier
+          - Qt::LeftButton + Qt::AltModifier
+          - Qt::MidButton
+         */
+        MouseSelect3,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::ShiftModifier
+         */
+        MouseSelect4,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ControlButton | Qt::ShiftModifier
+          - Qt::RightButton + Qt::ShiftModifier
+          - Qt::RightButton + Qt::ShiftModifier
+         */
+        MouseSelect5,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::AltModifier + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::AltModifier | Qt::ShiftModifier
+          - Qt::MidButton + Qt::ShiftModifier
+         */
+        MouseSelect6,
+
+        //! Number of mouse patterns
+        MousePatternCount
+    };
+
+    /*!
+      \brief Symbolic keyboard input codes
+
+      Individual settings can be configured using setKeyPattern()
+
+      \sa setKeyPattern(), setMousePattern()
+    */
+    enum KeyPatternCode
+    {
+        //! Qt::Key_Return
+        KeySelect1,
+
+        //! Qt::Key_Space
+        KeySelect2,
+
+        //! Qt::Key_Escape
+        KeyAbort,
+
+        //! Qt::Key_Left
+        KeyLeft,
+
+        //! Qt::Key_Right
+        KeyRight,
+
+        //! Qt::Key_Up
+        KeyUp,
+
+        //! Qt::Key_Down
+        KeyDown,
+
+        //! Qt::Key_Plus
+        KeyRedo,
+
+        //! Qt::Key_Minus
+        KeyUndo,
+
+        //! Qt::Key_Escape
+        KeyHome,
+
+        //! Number of key patterns
+        KeyPatternCount
+    };
+
+    //! A pattern for mouse events
+    class MousePattern
+    {
+    public:
+        //! Constructor
+        MousePattern( Qt::MouseButton btn = Qt::NoButton, 
+                Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ):
+            button( btn ),
+            modifiers( modifierCodes )
+        {
+        }
+
+        //! Button 
+        Qt::MouseButton button;
+
+        //! Keyboard modifier
+        Qt::KeyboardModifiers modifiers;
+    };
+
+    //! A pattern for key events
+    class KeyPattern
+    {
+    public:
+        //! Constructor
+        KeyPattern( int keyCode = Qt::Key_unknown, 
+                Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ):
+            key( keyCode ),
+            modifiers( modifierCodes )
+        {
+        }
+
+        //! Key code
+        int key;
+
+        //! Modifiers
+        Qt::KeyboardModifiers modifiers;
+    };
+
+    QwtEventPattern();
+    virtual ~QwtEventPattern();
+
+    void initMousePattern( int numButtons );
+    void initKeyPattern();
+
+    void setMousePattern( MousePatternCode, Qt::MouseButton button, 
+        Qt::KeyboardModifiers = Qt::NoModifier );
+
+    void setKeyPattern( KeyPatternCode, int keyCode, 
+        Qt::KeyboardModifiers modifierCodes = Qt::NoModifier );
+
+    void setMousePattern( const QVector<MousePattern> & );
+    void setKeyPattern( const QVector<KeyPattern> & );
+
+    const QVector<MousePattern> &mousePattern() const;
+    const QVector<KeyPattern> &keyPattern() const;
+
+    QVector<MousePattern> &mousePattern();
+    QVector<KeyPattern> &keyPattern();
+
+    bool mouseMatch( MousePatternCode, const QMouseEvent * ) const;
+    bool keyMatch( KeyPatternCode, const QKeyEvent * ) const;
+
+protected:
+    virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const;
+    virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const;
+
+private:
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+    QVector<MousePattern> d_mousePattern;
+    QVector<KeyPattern> d_keyPattern;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+};
+
+//! Compare operator
+inline bool operator==( QwtEventPattern::MousePattern b1,
+    QwtEventPattern::MousePattern  b2 )
+{
+    return b1.button == b2.button && b1.modifiers == b2.modifiers;
+}
+
+//! Compare operator
+inline bool operator==( QwtEventPattern::KeyPattern b1,
+   QwtEventPattern::KeyPattern  b2 )
+{
+    return b1.key == b2.key && b1.modifiers == b2.modifiers;
+}
+
+#endif
diff --git a/qwt/qwt_global.h b/qwt/qwt_global.h
new file mode 100644
index 0000000..71ea702
--- /dev/null
+++ b/qwt/qwt_global.h
@@ -0,0 +1,41 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_GLOBAL_H
+#define QWT_GLOBAL_H
+
+#include <qglobal.h>
+
+// QWT_VERSION is (major << 16) + (minor << 8) + patch.
+
+#define QWT_VERSION       0x060100
+#define QWT_VERSION_STR   "6.1.0"
+
+#if defined(_MSC_VER) /* MSVC Compiler */
+/* template-class specialization 'identifier' is already instantiated */
+#pragma warning(disable: 4660)
+/* inherits via dominance */
+#pragma warning(disable: 4250)
+#endif // _MSC_VER
+
+#ifdef QWT_DLL
+
+#if defined(QWT_MAKEDLL)     // create a Qwt DLL library 
+#define QWT_EXPORT Q_DECL_EXPORT
+#else                        // use a Qwt DLL library
+#define QWT_EXPORT Q_DECL_IMPORT 
+#endif
+
+#endif // QWT_DLL
+
+#ifndef QWT_EXPORT
+#define QWT_EXPORT
+#endif
+
+#endif 
diff --git a/qwt/qwt_graphic.cpp b/qwt/qwt_graphic.cpp
new file mode 100644
index 0000000..d67bcea
--- /dev/null
+++ b/qwt/qwt_graphic.cpp
@@ -0,0 +1,986 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_graphic.h"
+#include "qwt_painter_command.h"
+#include <qvector.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qpainterpath.h>
+#include <qmath.h>
+
+static bool qwtHasScalablePen( const QPainter *painter )
+{
+    const QPen pen = painter->pen();
+
+    bool scalablePen = false;
+
+    if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush )
+    {
+        scalablePen = !pen.isCosmetic();
+        if ( !scalablePen && pen.widthF() == 0.0 )
+        {
+            const QPainter::RenderHints hints = painter->renderHints();
+            if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
+                scalablePen = true;
+        }
+    }
+
+    return scalablePen;
+}
+
+static QRectF qwtStrokedPathRect( 
+    const QPainter *painter, const QPainterPath &path )
+{
+    QPainterPathStroker stroker;
+    stroker.setWidth( painter->pen().widthF() );
+    stroker.setCapStyle( painter->pen().capStyle() );
+    stroker.setJoinStyle( painter->pen().joinStyle() );
+    stroker.setMiterLimit( painter->pen().miterLimit() );
+
+    QRectF rect;
+    if ( qwtHasScalablePen( painter ) )
+    {
+        QPainterPath stroke = stroker.createStroke(path);
+        rect = painter->transform().map(stroke).boundingRect();
+    }
+    else
+    {
+        QPainterPath mappedPath = painter->transform().map(path);
+        mappedPath = stroker.createStroke( mappedPath );
+
+        rect = mappedPath.boundingRect();
+    }
+
+    return rect;
+}
+
+static inline void qwtExecCommand( 
+    QPainter *painter, const QwtPainterCommand &cmd, 
+    QwtGraphic::RenderHints renderHints,
+    const QTransform &transform )
+{
+    switch( cmd.type() )
+    {
+        case QwtPainterCommand::Path:
+        {
+            bool doMap = false;
+
+            if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled )
+                && painter->transform().isScaling() )
+            {
+                bool isCosmetic = painter->pen().isCosmetic();
+                if ( isCosmetic && painter->pen().widthF() == 0.0 )
+                {
+                    QPainter::RenderHints hints = painter->renderHints();
+                    if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
+                        isCosmetic = false;
+                }
+
+                doMap = !isCosmetic;
+            }
+
+            if ( doMap )
+            {
+                const QTransform transform = painter->transform();
+
+                painter->resetTransform();
+                painter->drawPath( transform.map( *cmd.path() ) );
+
+                painter->setTransform( transform );
+            }
+            else
+            {
+                painter->drawPath( *cmd.path() );
+            }
+            break;
+        }
+        case QwtPainterCommand::Pixmap:
+        {
+            const QwtPainterCommand::PixmapData *data = cmd.pixmapData();
+            painter->drawPixmap( data->rect, data->pixmap, data->subRect );
+            break;
+        }
+        case QwtPainterCommand::Image:
+        {
+            const QwtPainterCommand::ImageData *data = cmd.imageData();
+            painter->drawImage( data->rect, data->image, 
+                data->subRect, data->flags );
+            break;
+        }
+        case QwtPainterCommand::State:
+        {
+            const QwtPainterCommand::StateData *data = cmd.stateData();
+
+            if ( data->flags & QPaintEngine::DirtyPen ) 
+                painter->setPen( data->pen );
+
+            if ( data->flags & QPaintEngine::DirtyBrush ) 
+                painter->setBrush( data->brush );
+
+            if ( data->flags & QPaintEngine::DirtyBrushOrigin ) 
+                painter->setBrushOrigin( data->brushOrigin );
+
+            if ( data->flags & QPaintEngine::DirtyFont ) 
+                painter->setFont( data->font );
+
+            if ( data->flags & QPaintEngine::DirtyBackground ) 
+            {
+                painter->setBackgroundMode( data->backgroundMode );
+                painter->setBackground( data->backgroundBrush );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyTransform ) 
+            {
+                painter->setTransform( data->transform * transform );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyClipEnabled ) 
+                painter->setClipping( data->isClipEnabled );
+
+            if ( data->flags & QPaintEngine::DirtyClipRegion) 
+            {
+                painter->setClipRegion( data->clipRegion, 
+                    data->clipOperation );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyClipPath ) 
+            {
+                painter->setClipPath( data->clipPath, data->clipOperation );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyHints) 
+            {
+                const QPainter::RenderHints hints = data->renderHints;
+
+                painter->setRenderHint( QPainter::Antialiasing,
+                    hints.testFlag( QPainter::Antialiasing ) );
+
+                painter->setRenderHint( QPainter::TextAntialiasing,
+                    hints.testFlag( QPainter::TextAntialiasing ) );
+
+                painter->setRenderHint( QPainter::SmoothPixmapTransform,
+                    hints.testFlag( QPainter::SmoothPixmapTransform ) );
+
+                painter->setRenderHint( QPainter::HighQualityAntialiasing,
+                    hints.testFlag( QPainter::HighQualityAntialiasing ) );
+
+                painter->setRenderHint( QPainter::NonCosmeticDefaultPen,
+                    hints.testFlag( QPainter::NonCosmeticDefaultPen ) );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyCompositionMode) 
+                painter->setCompositionMode( data->compositionMode );
+
+            if ( data->flags & QPaintEngine::DirtyOpacity) 
+                painter->setOpacity( data->opacity );
+
+            break;
+        }
+        default:
+            break;
+    }
+
+}
+
+class QwtGraphic::PathInfo
+{
+public:
+    PathInfo():
+        d_scalablePen( false )
+    {
+        // QVector needs a default constructor
+    }
+
+    PathInfo( const QRectF &pointRect, 
+            const QRectF &boundingRect, bool scalablePen ):
+        d_pointRect( pointRect ),
+        d_boundingRect( boundingRect ),
+        d_scalablePen( scalablePen )
+    {
+    }
+
+    inline QRectF scaledBoundingRect( double sx, double sy,
+        bool scalePens ) const
+    {
+        if ( sx == 1.0 && sy == 1.0 )
+            return d_boundingRect;
+
+        QTransform transform;
+        transform.scale( sx, sy );
+
+        QRectF rect;
+        if ( scalePens && d_scalablePen )
+        {
+            rect = transform.mapRect( d_boundingRect );
+        }
+        else
+        {
+            rect = transform.mapRect( d_pointRect );
+
+            const double l = qAbs( d_pointRect.left() - d_boundingRect.left() );
+            const double r = qAbs( d_pointRect.right() - d_boundingRect.right() );
+            const double t = qAbs( d_pointRect.top() - d_boundingRect.top() );
+            const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() );
+
+            rect.adjust( -l, -t, r, b );
+        }
+
+        return rect;
+    }
+
+    inline double scaleFactorX( const QRectF& pathRect, 
+        const QRectF &targetRect, bool scalePens ) const
+    {
+        if ( pathRect.width() <= 0.0 )
+            return 0.0;
+
+        const QPointF p0 = d_pointRect.center();
+
+        const double l = qAbs( pathRect.left() - p0.x() );
+        const double r = qAbs( pathRect.right() - p0.x() );
+
+        const double w = 2.0 * qMin( l, r ) 
+            * targetRect.width() / pathRect.width();
+
+        double sx;
+        if ( scalePens && d_scalablePen )
+        {
+            sx = w / d_boundingRect.width();
+        }
+        else
+        {
+            const double pw = qMax( 
+                qAbs( d_boundingRect.left() - d_pointRect.left() ),
+                qAbs( d_boundingRect.right() - d_pointRect.right() ) );
+
+            sx = ( w - 2 * pw ) / d_pointRect.width();
+        }
+
+        return sx;
+    }
+
+    inline double scaleFactorY( const QRectF& pathRect, 
+        const QRectF &targetRect, bool scalePens ) const
+    {
+        if ( pathRect.height() <= 0.0 )
+            return 0.0;
+
+        const QPointF p0 = d_pointRect.center();
+
+        const double t = qAbs( pathRect.top() - p0.y() );
+        const double b = qAbs( pathRect.bottom() - p0.y() );
+
+        const double h = 2.0 * qMin( t, b ) 
+            * targetRect.height() / pathRect.height();
+
+        double sy;
+        if ( scalePens && d_scalablePen )
+        {
+            sy = h / d_boundingRect.height();
+        }
+        else
+        {
+            const double pw = 
+                qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ),
+                qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) );
+
+            sy = ( h - 2 * pw ) / d_pointRect.height();
+        }
+
+        return sy;
+    }
+
+private:
+    QRectF d_pointRect;
+    QRectF d_boundingRect;
+    bool d_scalablePen;
+};
+
+class QwtGraphic::PrivateData
+{
+public:
+    PrivateData():
+        boundingRect( 0.0, 0.0, -1.0, -1.0 ),
+        pointRect( 0.0, 0.0, -1.0, -1.0 )
+    {
+    }
+
+    QSizeF defaultSize;
+    QVector<QwtPainterCommand> commands;
+    QVector<QwtGraphic::PathInfo> pathInfos;
+
+    QRectF boundingRect;
+    QRectF pointRect;
+
+    QwtGraphic::RenderHints renderHints;
+};
+
+/*!
+  \brief Constructor
+
+  Initializes a null graphic
+  \sa isNull()
+ */
+QwtGraphic::QwtGraphic():
+    QwtNullPaintDevice()
+{
+    setMode( QwtNullPaintDevice::PathMode );
+    d_data = new PrivateData;
+}
+
+/*!
+  \brief Copy constructor
+
+  \param other Source 
+  \sa operator=()
+ */
+QwtGraphic::QwtGraphic( const QwtGraphic &other ):
+    QwtNullPaintDevice()
+{
+    setMode( other.mode() );
+    d_data = new PrivateData( *other.d_data );
+}
+
+//! Destructor
+QwtGraphic::~QwtGraphic()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Assignment operator
+
+  \param other Source 
+  \return A reference of this object
+ */
+QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other)
+{
+    setMode( other.mode() );
+    *d_data = *other.d_data;
+
+    return *this;
+}
+
+/*!
+  \brief Clear all stored commands 
+  \sa isNull()
+ */
+void QwtGraphic::reset() 
+{
+    d_data->commands.clear();
+    d_data->pathInfos.clear();
+
+    d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_data->defaultSize = QSizeF();
+
+}
+
+/*!
+  \return True, when no painter commands have been stored
+  \sa isEmpty(), commands()
+*/
+bool QwtGraphic::isNull() const
+{
+    return d_data->commands.isEmpty();
+}
+
+/*!
+  \return True, when the bounding rectangle is empty
+  \sa boundingRect(), isNull()
+*/
+bool QwtGraphic::isEmpty() const
+{
+    return d_data->boundingRect.isEmpty();
+}
+
+/*!
+  Toggle an render hint
+
+  \param hint Render hint
+  \param on true/false
+
+  \sa testRenderHint(), RenderHint
+*/
+void QwtGraphic::setRenderHint( RenderHint hint, bool on )
+{
+    if ( on )
+        d_data->renderHints |= hint;
+    else
+        d_data->renderHints &= ~hint;
+}
+
+/*!
+  Test a render hint
+
+  \param hint Render hint
+  \return true/false
+  \sa setRenderHint(), RenderHint
+*/
+bool QwtGraphic::testRenderHint( RenderHint hint ) const
+{
+    return d_data->renderHints.testFlag( hint );
+}
+
+/*!
+  The bounding rectangle is the controlPointRect()
+  extended by the areas needed for rendering the outlines
+  with unscaled pens.
+
+  \return Bounding rectangle of the graphic
+  \sa controlPointRect(), scaledBoundingRect()
+ */
+QRectF QwtGraphic::boundingRect() const
+{
+    if ( d_data->boundingRect.width() < 0 )
+        return QRectF();
+
+    return d_data->boundingRect;
+}
+
+/*!
+  The control point rectangle is the bounding rectangle 
+  of all control points of the paths and the target
+  rectangles of the images/pixmaps.
+
+  \return Control point rectangle
+  \sa boundingRect(), scaledBoundingRect()
+ */
+QRectF QwtGraphic::controlPointRect() const
+{
+    if ( d_data->pointRect.width() < 0 )
+        return QRectF();
+
+    return d_data->pointRect;
+}
+
+/*!
+  \brief Calculate the target rectangle for scaling the graphic
+
+  \param sx Horizontal scaling factor 
+  \param sy Vertical scaling factor 
+
+  \note In case of paths that are painted with a cosmetic pen 
+        ( see QPen::isCosmetic() ) the target rectangle is different to
+        multiplying the bounding rectangle.
+
+  \return Scaled bounding rectangle
+  \sa boundingRect(), controlPointRect()
+ */
+QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const
+{
+    if ( sx == 1.0 && sy == 1.0 )
+        return d_data->boundingRect;
+
+    QTransform transform;
+    transform.scale( sx, sy );
+
+    QRectF rect = transform.mapRect( d_data->pointRect );
+
+    for ( int i = 0; i < d_data->pathInfos.size(); i++ )
+    {
+        rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy, 
+            !d_data->renderHints.testFlag( RenderPensUnscaled ) );
+    }
+
+    return rect;
+}
+
+//! \return Ceiled defaultSize()
+QSize QwtGraphic::sizeMetrics() const
+{
+    const QSizeF sz = defaultSize();
+    return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
+}
+
+/*!
+  \brief Set a default size
+
+  The default size is used in all methods rendering the graphic,
+  where no size is explicitly specified. Assigning an empty size
+  means, that the default size will be calculated from the bounding 
+  rectangle.
+
+  The default setting is an empty size.
+    
+  \param size Default size
+
+  \sa defaultSize(), boundingRect()
+ */
+void QwtGraphic::setDefaultSize( const QSizeF &size )
+{
+    const double w = qMax( qreal( 0.0 ), size.width() );
+    const double h = qMax( qreal( 0.0 ), size.height() );
+
+    d_data->defaultSize = QSizeF( w, h );
+}
+
+/*!
+  \brief Default size
+
+  When a non empty size has been assigned by setDefaultSize() this
+  size will be returned. Otherwise the default size is the size
+  of the bounding rectangle.
+
+  The default size is used in all methods rendering the graphic,
+  where no size is explicitly specified. 
+
+  \return Default size
+  \sa setDefaultSize(), boundingRect()
+ */
+QSizeF QwtGraphic::defaultSize() const
+{
+    if ( !d_data->defaultSize.isEmpty() )
+        return d_data->defaultSize;
+
+    return boundingRect().size();
+}
+
+/*!
+  \brief Replay all recorded painter commands
+  \param painter Qt painter
+ */
+void QwtGraphic::render( QPainter *painter ) const
+{
+    if ( isNull() )
+        return;
+
+    const int numCommands = d_data->commands.size();
+    const QwtPainterCommand *commands = d_data->commands.constData();
+
+    const QTransform transform = painter->transform();
+
+    painter->save();
+
+    for ( int i = 0; i < numCommands; i++ )
+    {
+        qwtExecCommand( painter, commands[i], 
+            d_data->renderHints, transform );
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to fit into the rectangle
+  of the given size starting at ( 0, 0 ).
+
+  \param painter Qt painter
+  \param size Size for the scaled graphic
+  \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
+ */
+void QwtGraphic::render( QPainter *painter, const QSizeF &size, 
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    const QRectF r( 0.0, 0.0, size.width(), size.height() );
+    render( painter, r, aspectRatioMode );
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to fit into the given rectangle
+
+  \param painter Qt painter
+  \param rect Rectangle for the scaled graphic
+  \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
+ */
+void QwtGraphic::render( QPainter *painter, const QRectF &rect, 
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    if ( isEmpty() || rect.isEmpty() )
+        return;
+
+    double sx = 1.0; 
+    double sy = 1.0;
+
+    if ( d_data->pointRect.width() > 0.0 )
+        sx = rect.width() / d_data->pointRect.width();
+
+    if ( d_data->pointRect.height() > 0.0 )
+        sy = rect.height() / d_data->pointRect.height();
+
+    const bool scalePens = 
+        !d_data->renderHints.testFlag( RenderPensUnscaled );
+
+    for ( int i = 0; i < d_data->pathInfos.size(); i++ )
+    {
+        const PathInfo info = d_data->pathInfos[i];
+
+        const double ssx = info.scaleFactorX( 
+            d_data->pointRect, rect, scalePens );
+
+        if ( ssx > 0.0 )
+            sx = qMin( sx, ssx );
+
+        const double ssy = info.scaleFactorY( 
+            d_data->pointRect, rect, scalePens );
+
+        if ( ssy > 0.0 )
+            sy = qMin( sy, ssy );
+    }
+
+    if ( aspectRatioMode == Qt::KeepAspectRatio )
+    {
+        const double s = qMin( sx, sy );
+        sx = s;
+        sy = s;
+    }
+    else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding )
+    {
+        const double s = qMax( sx, sy );
+        sx = s;
+        sy = s;
+    }
+
+    QTransform tr;
+    tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(),
+        rect.center().y() - 0.5 * sy * d_data->pointRect.height() );
+    tr.scale( sx, sy );
+    tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() );
+
+    const QTransform transform = painter->transform();
+
+    painter->setTransform( tr, true );
+    render( painter );
+
+    painter->setTransform( transform );
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to the defaultSize() and aligned
+  to a position.
+
+  \param painter Qt painter
+  \param pos Reference point, where to render
+  \param alignment Flags how to align the target rectangle 
+                   to pos.
+ */
+void QwtGraphic::render( QPainter *painter, 
+    const QPointF &pos, Qt::Alignment alignment ) const
+{
+    QRectF r( pos, defaultSize() );
+
+    if ( alignment & Qt::AlignLeft )
+    {
+        r.moveLeft( pos.x() );
+    }
+    else if ( alignment & Qt::AlignHCenter )
+    {
+        r.moveCenter( QPointF( pos.x(), r.center().y() ) );
+    }
+    else if ( alignment & Qt::AlignRight )
+    {
+        r.moveRight( pos.x() );
+    }
+
+    if ( alignment & Qt::AlignTop )
+    {
+        r.moveTop( pos.y() );
+    }
+    else if ( alignment & Qt::AlignVCenter )
+    {
+        r.moveCenter( QPointF( r.center().x(), pos.y() ) );
+    }
+    else if ( alignment & Qt::AlignBottom )
+    {
+        r.moveBottom( pos.y() );
+    }
+
+    render( painter, r );
+}
+
+/*!
+  \brief Convert the graphic to a QPixmap
+    
+  All pixels of the pixmap get initialized by Qt::transparent
+  before the graphic is scaled and rendered on it.
+    
+  The size of the pixmap is the default size ( ceiled to integers )
+  of the graphic.
+
+  \return The graphic as pixmap in default size
+  \sa defaultSize(), toImage(), render()
+ */ 
+QPixmap QwtGraphic::toPixmap() const
+{
+    if ( isNull() )
+        return QPixmap();
+
+    const QSizeF sz = defaultSize();
+
+    const int w = qCeil( sz.width() );
+    const int h = qCeil( sz.height() );
+
+    QPixmap pixmap( w, h );
+    pixmap.fill( Qt::transparent );
+
+    const QRectF r( 0.0, 0.0, sz.width(), sz.height() );
+
+    QPainter painter( &pixmap );
+    render( &painter, r, Qt::KeepAspectRatio );
+    painter.end();
+
+    return pixmap;
+}
+
+/*!
+  \brief Convert the graphic to a QPixmap
+
+  All pixels of the pixmap get initialized by Qt::transparent
+  before the graphic is scaled and rendered on it.
+
+  \param size Size of the image
+  \param aspectRatioMode Aspect ratio how to scale the graphic
+
+  \return The graphic as pixmap
+  \sa toImage(), render()
+ */
+QPixmap QwtGraphic::toPixmap( const QSize &size,
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    QPixmap pixmap( size );
+    pixmap.fill( Qt::transparent );
+
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainter painter( &pixmap );
+    render( &painter, r, aspectRatioMode );
+    painter.end();
+
+    return pixmap;
+}
+
+/*!
+  \brief Convert the graphic to a QImage
+
+  All pixels of the image get initialized by 0 ( transparent )
+  before the graphic is scaled and rendered on it.
+
+  The format of the image is QImage::Format_ARGB32_Premultiplied.
+
+  \param size Size of the image
+  \param aspectRatioMode Aspect ratio how to scale the graphic
+
+  \return The graphic as image
+  \sa toPixmap(), render()
+ */
+QImage QwtGraphic::toImage( const QSize &size,
+    Qt::AspectRatioMode aspectRatioMode  ) const
+{
+    QImage image( size, QImage::Format_ARGB32_Premultiplied );
+    image.fill( 0 );
+
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainter painter( &image );
+    render( &painter, r, aspectRatioMode );
+    painter.end();
+
+    return image;
+}
+
+/*!
+  \brief Convert the graphic to a QImage
+    
+  All pixels of the image get initialized by 0 ( transparent )
+  before the graphic is scaled and rendered on it.
+
+  The format of the image is QImage::Format_ARGB32_Premultiplied.
+
+  The size of the image is the default size ( ceiled to integers )
+  of the graphic.
+    
+  \return The graphic as image in default size
+  \sa defaultSize(), toPixmap(), render()
+ */
+QImage QwtGraphic::toImage() const
+{
+    if ( isNull() )
+        return QImage();
+
+    const QSizeF sz = defaultSize();
+
+    const int w = qCeil( sz.width() );
+    const int h = qCeil( sz.height() );
+
+    QImage image( w, h, QImage::Format_ARGB32 );
+    image.fill( 0 );
+
+    const QRect r( 0, 0, sz.width(), sz.height() );
+
+    QPainter painter( &image );
+    render( &painter, r, Qt::KeepAspectRatio );
+    painter.end();
+
+    return image;
+}
+
+/*!
+  Store a path command in the command list
+
+  \param path Painter path
+  \sa QPaintEngine::drawPath()
+*/
+void QwtGraphic::drawPath( const QPainterPath &path )
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( path );
+
+    if ( !path.isEmpty() )
+    {
+        const QPainterPath scaledPath = painter->transform().map( path );
+
+        QRectF pointRect = scaledPath.boundingRect();
+        QRectF boundingRect = pointRect;
+
+        if ( painter->pen().style() != Qt::NoPen 
+            && painter->pen().brush().style() != Qt::NoBrush )
+        {
+            boundingRect = qwtStrokedPathRect( painter, path );
+        }
+
+        updateControlPointRect( pointRect );
+        updateBoundingRect( boundingRect );
+
+        d_data->pathInfos += PathInfo( pointRect, 
+            boundingRect, qwtHasScalablePen( painter ) );
+    }
+}
+
+/*!
+  \brief Store a pixmap command in the command list
+
+  \param rect target rectangle
+  \param pixmap Pixmap to be painted
+  \param subRect Reactangle of the pixmap to be painted
+
+  \sa QPaintEngine::drawPixmap()
+*/
+void QwtGraphic::drawPixmap( const QRectF &rect, 
+    const QPixmap &pixmap, const QRectF &subRect )
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( rect, pixmap, subRect );
+
+    const QRectF r = painter->transform().mapRect( rect );
+    updateControlPointRect( r );
+    updateBoundingRect( r );
+}
+
+/*!
+  \brief Store a image command in the command list
+
+  \param rect traget rectangle
+  \param image Image to be painted
+  \param subRect Reactangle of the pixmap to be painted
+  \param flags Image conversion flags
+
+  \sa QPaintEngine::drawImage()
+ */
+void QwtGraphic::drawImage( const QRectF &rect, const QImage &image,
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( rect, image, subRect, flags );
+
+    const QRectF r = painter->transform().mapRect( rect );
+
+    updateControlPointRect( r );
+    updateBoundingRect( r );
+}
+
+/*!
+  \brief Store a state command in the command list
+
+  \param state State to be stored
+  \sa QPaintEngine::updateState()
+ */
+void QwtGraphic::updateState( const QPaintEngineState &state)
+{
+    d_data->commands += QwtPainterCommand( state );
+}
+
+void QwtGraphic::updateBoundingRect( const QRectF &rect )
+{
+    QRectF br = rect;
+
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter && painter->hasClipping() )
+    {
+        QRectF cr = painter->clipRegion().boundingRect();
+        cr = painter->transform().mapRect( br );
+
+        br &= cr;
+    }
+
+    if ( d_data->boundingRect.width() < 0 )
+        d_data->boundingRect = br;
+    else
+        d_data->boundingRect |= br;
+}
+
+void QwtGraphic::updateControlPointRect( const QRectF &rect )
+{
+    if ( d_data->pointRect.width() < 0.0 )
+        d_data->pointRect = rect;
+    else
+        d_data->pointRect |= rect;
+}
+
+/*!
+  \return List of recorded paint commands
+  \sa setCommands()
+ */
+const QVector< QwtPainterCommand > &QwtGraphic::commands() const
+{
+    return d_data->commands;
+}
+
+/*!
+  \brief Append paint commands
+
+  \param commands Paint commands
+  \sa commands()
+ */
+void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands )
+{
+    reset();
+
+    const int numCommands = commands.size();
+    if ( numCommands <= 0 )
+        return;
+
+    // to calculate a proper bounding rectangle we don't simply copy 
+    // the commands. 
+
+    const QwtPainterCommand *cmds = commands.constData();
+
+    QPainter painter( this );
+    for ( int i = 0; i < numCommands; i++ )
+        qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform() );
+
+    painter.end();
+}
diff --git a/qwt/qwt_graphic.h b/qwt/qwt_graphic.h
new file mode 100644
index 0000000..ca625a8
--- /dev/null
+++ b/qwt/qwt_graphic.h
@@ -0,0 +1,174 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_GRAPHIC_H
+#define QWT_GRAPHIC_H
+
+#include "qwt_global.h"
+#include "qwt_null_paintdevice.h"
+#include <qmetatype.h>
+#include <qimage.h>
+#include <qpixmap.h>
+
+class QwtPainterCommand;
+
+/*!
+    \brief A paint device for scalable graphics
+
+    QwtGraphic is the representation of a graphic that is tailored for
+    scalability. Like QPicture it will be initialized by QPainter
+    operations and replayed later to any target paint device.
+
+    While the usual image representations QImage and QPixmap are not
+    scalable Qt offers two paint devices, that might be candidates
+    for representing a vector graphic:
+
+    - QPicture\n
+      Unfortunately QPicture had been forgotten, when Qt4
+      introduced floating point based render engines. Its API
+      is still on integers, what make it unusable for proper scaling.
+
+    - QSvgRenderer/QSvgGenerator\n
+      Unfortunately QSvgRenderer hides to much information about
+      its nodes in internal APIs, that are necessary proper 
+      layout calculations. Also it is derived from QObject and 
+      can't be copied like QImage/QPixmap.
+      Also QSvgRenderer/QSvgGenerator are no complete SVG implementations
+      with a questionable future in Qt 5.
+
+    QwtGraphic maps all scalable drawing primitives to a QPainterPath
+    and stores them together with the painter state changes 
+    ( pen, brush, transformation ... ) in a list of QwtPaintCommands. 
+    For being a complete QPaintDevice it also stores pixmaps or images, 
+    what is somehow against the idea of the class, because these objects 
+    can be scaled without a loss in quality.
+
+    The main issue about scaling a QwtGraphic object are the pens used for
+    drawing the outlines of the painter paths. While non cosmetic pens 
+    ( QPen::isCosmetic() ) are scaled with the same ratio as the path, 
+    cosmetic pens have a fixed width. A graphic might have paths with 
+    different pens - cosmetic and non-cosmetic.
+
+    QwtGraphic caches 2 different rectangles:
+
+    - control point rectangle\n
+      The control point rectangle is the bounding rectangle of all
+      control point rectangles of the painter paths, or the target 
+      rectangle of the pixmaps/images.
+
+    - bounding rectangle\n
+      The bounding rectangle extends the control point rectangle by
+      what is needed for rendering the outline with an unscaled pen.
+
+    Because the offset for drawing the outline depends on the shape 
+    of the painter path ( the peak of a triangle is different than the flat side ) 
+    scaling with a fixed aspect ratio always needs to be calculated from the 
+    control point rectangle.
+
+    \sa QwtPainterCommand
+ */
+class QWT_EXPORT QwtGraphic: public QwtNullPaintDevice
+{
+public:
+    /*! 
+        Hint how to render a graphic
+        \sa setRenderHint(), testRenderHint()
+     */
+    enum RenderHint
+    {
+        /*!
+           When RenderPensUnscaled is set non cosmetic pens are
+           painted unscaled - like cosmetic pens. The difference to
+           using cosmetic pens is, when the graphic is rendered
+           to a document in a scalable vector format ( PDF, SVG ):
+           the width of non cosmetic pens will be scaled by the
+           document viewer.
+         */
+        RenderPensUnscaled = 0x1
+    };
+
+    /*! 
+        \brief Render hints
+
+        The default setting is to disable all hints
+     */
+    typedef QFlags<RenderHint> RenderHints;
+
+    QwtGraphic();
+    QwtGraphic( const QwtGraphic & );
+
+    virtual ~QwtGraphic();
+
+    QwtGraphic& operator=( const QwtGraphic & );
+
+    void reset();
+
+    bool isNull() const;
+    bool isEmpty() const;
+
+    void render( QPainter * ) const;
+
+    void render( QPainter *, const QSizeF &, 
+            Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    void render( QPainter *, const QRectF &, 
+            Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    void render( QPainter *, const QPointF &,
+        Qt::Alignment = Qt::AlignTop | Qt::AlignLeft ) const;
+
+    QPixmap toPixmap() const; 
+    QPixmap toPixmap( const QSize &, 
+        Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    QImage toImage() const; 
+    QImage toImage( const QSize &, 
+        Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    QRectF scaledBoundingRect( double sx, double sy ) const;
+
+    QRectF boundingRect() const;
+    QRectF controlPointRect() const;
+
+    const QVector< QwtPainterCommand > &commands() const;
+    void setCommands( QVector< QwtPainterCommand > & );
+
+    void setDefaultSize( const QSizeF & );
+    QSizeF defaultSize() const;
+    
+    void setRenderHint( RenderHint, bool on = true );
+    bool testRenderHint( RenderHint ) const;
+
+protected:
+    virtual QSize sizeMetrics() const;
+
+    virtual void drawPath( const QPainterPath & );
+
+    virtual void drawPixmap( const QRectF &,
+        const QPixmap &, const QRectF & );
+
+    virtual void drawImage( const QRectF &,
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+    virtual void updateState( const QPaintEngineState &state );
+
+private:
+    void updateBoundingRect( const QRectF & );
+    void updateControlPointRect( const QRectF & );
+
+    class PathInfo;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtGraphic::RenderHints )
+Q_DECLARE_METATYPE( QwtGraphic )
+
+#endif
diff --git a/qwt/qwt_interval.cpp b/qwt/qwt_interval.cpp
new file mode 100644
index 0000000..b7c6ee9
--- /dev/null
+++ b/qwt/qwt_interval.cpp
@@ -0,0 +1,354 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_interval.h"
+#include "qwt_math.h"
+#include <qalgorithms.h>
+
+/*!
+  \brief Normalize the limits of the interval
+
+  If maxValue() < minValue() the limits will be inverted.
+  \return Normalized interval
+
+  \sa isValid(), inverted()
+*/
+QwtInterval QwtInterval::normalized() const
+{
+    if ( d_minValue > d_maxValue )
+    {
+        return inverted();
+    }
+    if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum )
+    {
+        return inverted();
+    }
+
+    return *this;
+}
+
+/*!
+  Invert the limits of the interval
+  \return Inverted interval
+  \sa normalized()
+*/
+QwtInterval QwtInterval::inverted() const
+{
+    BorderFlags borderFlags = IncludeBorders;
+    if ( d_borderFlags & ExcludeMinimum )
+        borderFlags |= ExcludeMaximum;
+    if ( d_borderFlags & ExcludeMaximum )
+        borderFlags |= ExcludeMinimum;
+
+    return QwtInterval( d_maxValue, d_minValue, borderFlags );
+}
+
+/*!
+  Test if a value is inside an interval
+
+  \param value Value
+  \return true, if value >= minValue() && value <= maxValue()
+*/
+bool QwtInterval::contains( double value ) const
+{
+    if ( !isValid() )
+        return false;
+
+    if ( value < d_minValue || value > d_maxValue )
+        return false;
+
+    if ( value == d_minValue && d_borderFlags & ExcludeMinimum )
+        return false;
+
+    if ( value == d_maxValue && d_borderFlags & ExcludeMaximum )
+        return false;
+
+    return true;
+}
+
+//! Unite 2 intervals
+QwtInterval QwtInterval::unite( const QwtInterval &other ) const
+{
+    /*
+     If one of the intervals is invalid return the other one.
+     If both are invalid return an invalid default interval
+     */
+    if ( !isValid() )
+    {
+        if ( !other.isValid() )
+            return QwtInterval();
+        else
+            return other;
+    }
+    if ( !other.isValid() )
+        return *this;
+
+    QwtInterval united;
+    BorderFlags flags = IncludeBorders;
+
+    // minimum
+    if ( d_minValue < other.minValue() )
+    {
+        united.setMinValue( d_minValue );
+        flags &= d_borderFlags & ExcludeMinimum;
+    }
+    else if ( other.minValue() < d_minValue )
+    {
+        united.setMinValue( other.minValue() );
+        flags &= other.borderFlags() & ExcludeMinimum;
+    }
+    else // d_minValue == other.minValue()
+    {
+        united.setMinValue( d_minValue );
+        flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum;
+    }
+
+    // maximum
+    if ( d_maxValue > other.maxValue() )
+    {
+        united.setMaxValue( d_maxValue );
+        flags &= d_borderFlags & ExcludeMaximum;
+    }
+    else if ( other.maxValue() > d_maxValue )
+    {
+        united.setMaxValue( other.maxValue() );
+        flags &= other.borderFlags() & ExcludeMaximum;
+    }
+    else // d_maxValue == other.maxValue() )
+    {
+        united.setMaxValue( d_maxValue );
+        flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum;
+    }
+
+    united.setBorderFlags( flags );
+    return united;
+}
+
+/*! 
+  \brief Intersect 2 intervals
+  
+  \param other Interval to be intersect with
+  \return Intersection
+ */
+QwtInterval QwtInterval::intersect( const QwtInterval &other ) const
+{
+    if ( !other.isValid() || !isValid() )
+        return QwtInterval();
+
+    QwtInterval i1 = *this;
+    QwtInterval i2 = other;
+
+    // swap i1/i2, so that the minimum of i1
+    // is smaller then the minimum of i2
+
+    if ( i1.minValue() > i2.minValue() )
+    {
+        qSwap( i1, i2 );
+    }
+    else if ( i1.minValue() == i2.minValue() )
+    {
+        if ( i1.borderFlags() & ExcludeMinimum )
+            qSwap( i1, i2 );
+    }
+
+    if ( i1.maxValue() < i2.minValue() )
+    {
+        return QwtInterval();
+    }
+
+    if ( i1.maxValue() == i2.minValue() )
+    {
+        if ( i1.borderFlags() & ExcludeMaximum ||
+            i2.borderFlags() & ExcludeMinimum )
+        {
+            return QwtInterval();
+        }
+    }
+
+    QwtInterval intersected;
+    BorderFlags flags = IncludeBorders;
+
+    intersected.setMinValue( i2.minValue() );
+    flags |= i2.borderFlags() & ExcludeMinimum;
+
+    if ( i1.maxValue() < i2.maxValue() )
+    {
+        intersected.setMaxValue( i1.maxValue() );
+        flags |= i1.borderFlags() & ExcludeMaximum;
+    }
+    else if ( i2.maxValue() < i1.maxValue() )
+    {
+        intersected.setMaxValue( i2.maxValue() );
+        flags |= i2.borderFlags() & ExcludeMaximum;
+    }
+    else // i1.maxValue() == i2.maxValue()
+    {
+        intersected.setMaxValue( i1.maxValue() );
+        flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum;
+    }
+
+    intersected.setBorderFlags( flags );
+    return intersected;
+}
+
+/*! 
+  \brief Unite this interval with the given interval.
+
+  \param other Interval to be united with
+  \return This interval
+ */
+QwtInterval& QwtInterval::operator|=( const QwtInterval &other )
+{
+    *this = *this | other;
+    return *this;
+}
+
+/*! 
+  \brief Intersect this interval with the given interval.
+
+  \param other Interval to be intersected with
+  \return This interval
+ */
+QwtInterval& QwtInterval::operator&=( const QwtInterval &other )
+{
+    *this = *this & other;
+    return *this;
+}
+
+/*!
+  \brief Test if two intervals overlap
+
+  \param other Interval
+  \return True, when the intervals are intersecting
+*/
+bool QwtInterval::intersects( const QwtInterval &other ) const
+{
+    if ( !isValid() || !other.isValid() )
+        return false;
+
+    QwtInterval i1 = *this;
+    QwtInterval i2 = other;
+
+    // swap i1/i2, so that the minimum of i1
+    // is smaller then the minimum of i2
+
+    if ( i1.minValue() > i2.minValue() )
+    {
+        qSwap( i1, i2 );
+    }
+    else if ( i1.minValue() == i2.minValue() &&
+              i1.borderFlags() & ExcludeMinimum )
+    {
+        qSwap( i1, i2 );
+    }
+
+    if ( i1.maxValue() > i2.minValue() )
+    {
+        return true;
+    }
+    if ( i1.maxValue() == i2.minValue() )
+    {
+        return !( ( i1.borderFlags() & ExcludeMaximum ) ||
+            ( i2.borderFlags() & ExcludeMinimum ) );
+    }
+    return false;
+}
+
+/*!
+  Adjust the limit that is closer to value, so that value becomes
+  the center of the interval.
+
+  \param value Center
+  \return Interval with value as center
+*/
+QwtInterval QwtInterval::symmetrize( double value ) const
+{
+    if ( !isValid() )
+        return *this;
+
+    const double delta =
+        qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) );
+
+    return QwtInterval( value - delta, value + delta );
+}
+
+/*!
+  Limit the interval, keeping the border modes
+
+  \param lowerBound Lower limit
+  \param upperBound Upper limit
+
+  \return Limited interval
+*/
+QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const
+{
+    if ( !isValid() || lowerBound > upperBound )
+        return QwtInterval();
+
+    double minValue = qMax( d_minValue, lowerBound );
+    minValue = qMin( minValue, upperBound );
+
+    double maxValue = qMax( d_maxValue, lowerBound );
+    maxValue = qMin( maxValue, upperBound );
+
+    return QwtInterval( minValue, maxValue, d_borderFlags );
+}
+
+/*!
+  \brief Extend the interval
+
+  If value is below minValue(), value becomes the lower limit.
+  If value is above maxValue(), value becomes the upper limit.
+
+  extend() has no effect for invalid intervals
+
+  \param value Value
+  \return extended interval
+
+  \sa isValid()
+*/
+QwtInterval QwtInterval::extend( double value ) const
+{
+    if ( !isValid() )
+        return *this;
+
+    return QwtInterval( qMin( value, d_minValue ),
+        qMax( value, d_maxValue ), d_borderFlags );
+}
+
+/*!
+  Extend an interval
+
+  \param value Value
+  \return Reference of the extended interval
+
+  \sa extend()
+*/
+QwtInterval& QwtInterval::operator|=( double value )
+{
+    *this = *this | value;
+    return *this;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtInterval &interval )
+{
+    const int flags = interval.borderFlags();
+
+    debug.nospace() << "QwtInterval("
+        << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" )
+        << interval.minValue() << "," << interval.maxValue()
+        << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" )
+        << ")";
+
+    return debug.space();
+}
+
+#endif
diff --git a/qwt/qwt_interval.h b/qwt/qwt_interval.h
new file mode 100644
index 0000000..68841e0
--- /dev/null
+++ b/qwt/qwt_interval.h
@@ -0,0 +1,320 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_INTERVAL_H
+#define QWT_INTERVAL_H
+
+#include "qwt_global.h"
+#include <qmetatype.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A class representing an interval
+
+  The interval is represented by 2 doubles, the lower and the upper limit.
+*/
+
+class QWT_EXPORT QwtInterval
+{
+public:
+    /*!
+      Flag indicating if a border is included or excluded 
+      \sa setBorderFlags(), borderFlags()
+    */
+    enum BorderFlag
+    {
+        //! Min/Max values are inside the interval
+        IncludeBorders = 0x00,
+
+        //! Min value is not included in the interval
+        ExcludeMinimum = 0x01,
+
+        //! Max value is not included in the interval
+        ExcludeMaximum = 0x02,
+
+        //! Min/Max values are not included in the interval
+        ExcludeBorders = ExcludeMinimum | ExcludeMaximum
+    };
+
+    //! Border flags
+    typedef QFlags<BorderFlag> BorderFlags;
+
+    QwtInterval();
+    QwtInterval( double minValue, double maxValue,
+        BorderFlags = IncludeBorders );
+
+    void setInterval( double minValue, double maxValue,
+        BorderFlags = IncludeBorders );
+
+    QwtInterval normalized() const;
+    QwtInterval inverted() const;
+    QwtInterval limited( double minValue, double maxValue ) const;
+
+    bool operator==( const QwtInterval & ) const;
+    bool operator!=( const QwtInterval & ) const;
+
+    void setBorderFlags( BorderFlags );
+    BorderFlags borderFlags() const;
+
+    double minValue() const;
+    double maxValue() const;
+
+    double width() const;
+
+    void setMinValue( double );
+    void setMaxValue( double );
+
+    bool contains( double value ) const;
+
+    bool intersects( const QwtInterval & ) const;
+    QwtInterval intersect( const QwtInterval & ) const;
+    QwtInterval unite( const QwtInterval & ) const;
+
+    QwtInterval operator|( const QwtInterval & ) const;
+    QwtInterval operator&( const QwtInterval & ) const;
+
+    QwtInterval &operator|=( const QwtInterval & );
+    QwtInterval &operator&=( const QwtInterval & );
+
+    QwtInterval extend( double value ) const;
+    QwtInterval operator|( double ) const;
+    QwtInterval &operator|=( double );
+
+    bool isValid() const;
+    bool isNull() const;
+    void invalidate();
+
+    QwtInterval symmetrize( double value ) const;
+
+private:
+    double d_minValue;
+    double d_maxValue;
+    BorderFlags d_borderFlags;
+};
+
+Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE);
+
+/*!
+  \brief Default Constructor
+
+  Creates an invalid interval [0.0, -1.0]
+  \sa setInterval(), isValid()
+*/
+inline QwtInterval::QwtInterval():
+    d_minValue( 0.0 ),
+    d_maxValue( -1.0 ),
+    d_borderFlags( IncludeBorders )
+{
+}
+
+/*!
+   Constructor
+
+   Build an interval with from min/max values
+
+   \param minValue Minimum value
+   \param maxValue Maximum value
+   \param borderFlags Include/Exclude borders
+*/
+inline QwtInterval::QwtInterval(
+        double minValue, double maxValue, BorderFlags borderFlags ):
+    d_minValue( minValue ),
+    d_maxValue( maxValue ),
+    d_borderFlags( borderFlags )
+{
+}
+
+/*!
+   Assign the limits of the interval
+
+   \param minValue Minimum value
+   \param maxValue Maximum value
+   \param borderFlags Include/Exclude borders
+*/
+inline void QwtInterval::setInterval(
+    double minValue, double maxValue, BorderFlags borderFlags )
+{
+    d_minValue = minValue;
+    d_maxValue = maxValue;
+    d_borderFlags = borderFlags;
+}
+
+/*!
+   Change the border flags
+
+   \param borderFlags Or'd BorderMode flags
+   \sa borderFlags()
+*/
+inline void QwtInterval::setBorderFlags( BorderFlags borderFlags )
+{
+    d_borderFlags = borderFlags;
+}
+
+/*!
+   \return Border flags
+   \sa setBorderFlags()
+*/
+inline QwtInterval::BorderFlags QwtInterval::borderFlags() const
+{
+    return d_borderFlags;
+}
+
+/*!
+   Assign the lower limit of the interval
+
+   \param minValue Minimum value
+*/
+inline void QwtInterval::setMinValue( double minValue )
+{
+    d_minValue = minValue;
+}
+
+/*!
+   Assign the upper limit of the interval
+
+   \param maxValue Maximum value
+*/
+inline void QwtInterval::setMaxValue( double maxValue )
+{
+    d_maxValue = maxValue;
+}
+
+//! \return Lower limit of the interval
+inline double QwtInterval::minValue() const
+{
+    return d_minValue;
+}
+
+//! \return Upper limit of the interval
+inline double QwtInterval::maxValue() const
+{
+    return d_maxValue;
+}
+
+/*!
+   A interval is valid when minValue() <= maxValue().
+   In case of QwtInterval::ExcludeBorders it is true
+   when minValue() < maxValue()
+
+   \return True, when the interval is valid
+*/
+inline bool QwtInterval::isValid() const
+{
+    if ( ( d_borderFlags & ExcludeBorders ) == 0 )
+        return d_minValue <= d_maxValue;
+    else
+        return d_minValue < d_maxValue;
+}
+
+/*!
+   \brief Return the width of an interval
+
+   The width of invalid intervals is 0.0, otherwise the result is
+   maxValue() - minValue().
+
+   \return Interval width
+   \sa isValid()
+*/
+inline double QwtInterval::width() const
+{
+    return isValid() ? ( d_maxValue - d_minValue ) : 0.0;
+}
+
+/*!
+   \brief Intersection of two intervals
+ 
+   \param other Interval to intersect with
+   \return Intersection of this and other
+
+   \sa intersect()
+*/
+inline QwtInterval QwtInterval::operator&(
+    const QwtInterval &other ) const
+{
+    return intersect( other );
+}
+
+/*!
+   Union of two intervals
+
+   \param other Interval to unite with
+   \return Union of this and other
+
+   \sa unite()
+*/
+inline QwtInterval QwtInterval::operator|(
+    const QwtInterval &other ) const
+{
+    return unite( other );
+}
+
+/*! 
+   \brief Compare two intervals
+
+   \param other Interval to compare with
+   \return True, when this and other are equal
+*/
+inline bool QwtInterval::operator==( const QwtInterval &other ) const
+{
+    return ( d_minValue == other.d_minValue ) &&
+           ( d_maxValue == other.d_maxValue ) &&
+           ( d_borderFlags == other.d_borderFlags );
+}
+/*! 
+   \brief Compare two intervals
+
+   \param other Interval to compare with
+   \return True, when this and other are not equal
+*/
+inline bool QwtInterval::operator!=( const QwtInterval &other ) const
+{
+    return ( !( *this == other ) );
+}
+
+/*!
+   Extend an interval
+
+   \param value Value
+   \return Extended interval
+   \sa extend()
+*/
+inline QwtInterval QwtInterval::operator|( double value ) const
+{
+    return extend( value );
+}
+
+//! \return true, if isValid() && (minValue() >= maxValue())
+inline bool QwtInterval::isNull() const
+{
+    return isValid() && d_minValue >= d_maxValue;
+}
+
+/*!
+  Invalidate the interval
+
+  The limits are set to interval [0.0, -1.0]
+  \sa isValid()
+*/
+inline void QwtInterval::invalidate()
+{
+    d_minValue = 0.0;
+    d_maxValue = -1.0;
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags )
+Q_DECLARE_METATYPE( QwtInterval )
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & );
+#endif
+
+#endif
diff --git a/qwt/qwt_interval_symbol.cpp b/qwt/qwt_interval_symbol.cpp
new file mode 100644
index 0000000..83c842d
--- /dev/null
+++ b/qwt/qwt_interval_symbol.cpp
@@ -0,0 +1,319 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_interval_symbol.h"
+#include "qwt_painter.h"
+#include "qwt_math.h"
+#include <qpainter.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+class QwtIntervalSymbol::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtIntervalSymbol::NoSymbol ),
+        width( 6 )
+    {
+    }
+
+    bool operator==( const PrivateData &other ) const
+    {
+        return ( style == other.style )
+            && ( width == other.width )
+            && ( brush == other.brush )
+            && ( pen == other.pen );
+    }
+
+    QwtIntervalSymbol::Style style;
+    int width;
+
+    QPen pen;
+    QBrush brush;
+};
+
+/*!
+  Constructor
+
+  \param style Style of the symbol
+  \sa setStyle(), style(), Style
+*/
+QwtIntervalSymbol::QwtIntervalSymbol( Style style )
+{
+    d_data = new PrivateData();
+    d_data->style = style;
+}
+
+//! Copy constructor
+QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other )
+{
+    d_data = new PrivateData();
+    *d_data = *other.d_data;
+}
+
+//! Destructor
+QwtIntervalSymbol::~QwtIntervalSymbol()
+{
+    delete d_data;
+}
+
+//! \brief Assignment operator
+QwtIntervalSymbol &QwtIntervalSymbol::operator=( 
+    const QwtIntervalSymbol &other )
+{
+    *d_data = *other.d_data;
+    return *this;
+}
+
+//! \brief Compare two symbols
+bool QwtIntervalSymbol::operator==( 
+    const QwtIntervalSymbol &other ) const
+{
+    return *d_data == *other.d_data;
+}
+
+//! \brief Compare two symbols
+bool QwtIntervalSymbol::operator!=( 
+    const QwtIntervalSymbol &other ) const
+{
+    return !( *d_data == *other.d_data );
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style(), Style
+*/
+void QwtIntervalSymbol::setStyle( Style style )
+{
+    d_data->style = style;
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtIntervalSymbol::Style QwtIntervalSymbol::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Specify the width of the symbol
+  It is used depending on the style.
+
+  \param width Width
+  \sa width(), setStyle()
+*/
+void QwtIntervalSymbol::setWidth( int width )
+{
+    d_data->width = width;
+}
+
+/*!
+  \return Width of the symbol.
+  \sa setWidth(), setStyle()
+*/
+int QwtIntervalSymbol::width() const
+{
+    return d_data->width;
+}
+
+/*!
+  \brief Assign a brush
+
+  The brush is used for the Box style.
+
+  \param brush Brush
+  \sa brush()
+*/
+void QwtIntervalSymbol::setBrush( const QBrush &brush )
+{
+    d_data->brush = brush;
+}
+
+/*!
+  \return Brush
+  \sa setBrush()
+*/
+const QBrush& QwtIntervalSymbol::brush() const
+{
+    return d_data->brush;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtIntervalSymbol::setPen( const QColor &color, 
+    qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtIntervalSymbol::setPen( const QPen &pen )
+{
+    d_data->pen = pen;
+}
+
+/*!
+  \return Pen
+  \sa setPen(), brush()
+*/
+const QPen& QwtIntervalSymbol::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Draw a symbol depending on its style
+
+  \param painter Painter
+  \param orientation Orientation
+  \param from Start point of the interval in target device coordinates
+  \param to End point of the interval in target device coordinates
+
+  \sa setStyle()
+*/
+void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation,
+    const QPointF &from, const QPointF &to ) const
+{
+    const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) );
+
+    QPointF p1 = from;
+    QPointF p2 = to;
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        p1 = p1.toPoint();
+        p2 = p2.toPoint();
+    }
+
+    switch ( d_data->style )
+    {
+        case QwtIntervalSymbol::Bar:
+        {
+            QwtPainter::drawLine( painter, p1, p2 );
+            if ( d_data->width > pw )
+            {
+                if ( ( orientation == Qt::Horizontal ) 
+                    && ( p1.y() == p2.y() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double y = p1.y() - sw / 2;
+                    QwtPainter::drawLine( painter,
+                        p1.x(), y, p1.x(), y + sw );
+                    QwtPainter::drawLine( painter,
+                        p2.x(), y, p2.x(), y + sw );
+                }
+                else if ( ( orientation == Qt::Vertical ) 
+                    && ( p1.x() == p2.x() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double x = p1.x() - sw / 2;
+                    QwtPainter::drawLine( painter,
+                        x, p1.y(), x + sw, p1.y() );
+                    QwtPainter::drawLine( painter,
+                        x, p2.y(), x + sw, p2.y() );
+                }
+                else
+                {
+                    const double sw = d_data->width;
+
+                    const double dx = p2.x() - p1.x();
+                    const double dy = p2.y() - p1.y();
+                    const double angle = qAtan2( dy, dx ) + M_PI_2;
+                    double dw2 = sw / 2.0;
+
+                    const double cx = qFastCos( angle ) * dw2;
+                    const double sy = qFastSin( angle ) * dw2;
+
+                    QwtPainter::drawLine( painter,
+                        p1.x() - cx, p1.y() - sy,
+                        p1.x() + cx, p1.y() + sy );
+                    QwtPainter::drawLine( painter,
+                        p2.x() - cx, p2.y() - sy,
+                        p2.x() + cx, p2.y() + sy );
+                }
+            }
+            break;
+        }
+        case QwtIntervalSymbol::Box:
+        {
+            if ( d_data->width <= pw )
+            {
+                QwtPainter::drawLine( painter, p1, p2 );
+            }
+            else
+            {
+                if ( ( orientation == Qt::Horizontal ) 
+                    && ( p1.y() == p2.y() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double y = p1.y() - d_data->width / 2;
+                    QwtPainter::drawRect( painter,
+                        p1.x(), y, p2.x() - p1.x(),  sw );
+                }
+                else if ( ( orientation == Qt::Vertical )
+                    && ( p1.x() == p2.x() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double x = p1.x() - d_data->width / 2;
+                    QwtPainter::drawRect( painter,
+                        x, p1.y(), sw, p2.y() - p1.y() );
+                }
+                else
+                {
+                    const double sw = d_data->width;
+
+                    const double dx = p2.x() - p1.x();
+                    const double dy = p2.y() - p1.y();
+                    const double angle = qAtan2( dy, dx ) + M_PI_2;
+                    double dw2 = sw / 2.0;
+
+                    const double cx = qFastCos( angle ) * dw2;
+                    const double sy = qFastSin( angle ) * dw2;
+
+                    QPolygonF polygon;
+                    polygon += QPointF( p1.x() - cx, p1.y() - sy );
+                    polygon += QPointF( p1.x() + cx, p1.y() + sy );
+                    polygon += QPointF( p2.x() + cx, p2.y() + sy );
+                    polygon += QPointF( p2.x() - cx, p2.y() - sy );
+
+                    QwtPainter::drawPolygon( painter, polygon );
+                }
+            }
+            break;
+        }
+        default:;
+    }
+}
diff --git a/qwt/qwt_interval_symbol.h b/qwt/qwt_interval_symbol.h
new file mode 100644
index 0000000..f32e1c4
--- /dev/null
+++ b/qwt/qwt_interval_symbol.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_INTERVAL_SYMBOL_H
+#define QWT_INTERVAL_SYMBOL_H
+
+#include "qwt_global.h"
+#include <qpen.h>
+#include <qsize.h>
+
+class QPainter;
+class QRect;
+class QPointF;
+
+/*!
+  \brief A drawing primitive for displaying an interval like an error bar
+
+  \sa QwtPlotIntervalCurve
+*/
+class QWT_EXPORT QwtIntervalSymbol
+{
+public:
+    //! Symbol style
+    enum Style
+    {
+        //! No Style. The symbol cannot be drawn.
+        NoSymbol = -1,
+
+        /*!
+          The symbol displays a line with caps at the beginning/end.
+          The size of the caps depends on the symbol width().
+         */
+        Bar,
+
+        /*!
+          The symbol displays a plain rectangle using pen() and brush().
+          The size of the rectangle depends on the translated interval and
+          the width(),
+         */
+        Box,
+
+        /*!
+          Styles >= UserSymbol are reserved for derived
+          classes of QwtIntervalSymbol that overload draw() with
+          additional application specific symbol types.
+         */
+        UserSymbol = 1000
+    };
+
+public:
+    QwtIntervalSymbol( Style = NoSymbol );
+    QwtIntervalSymbol( const QwtIntervalSymbol & );
+    virtual ~QwtIntervalSymbol();
+
+    QwtIntervalSymbol &operator=( const QwtIntervalSymbol & );
+    bool operator==( const QwtIntervalSymbol & ) const;
+    bool operator!=( const QwtIntervalSymbol & ) const;
+
+    void setWidth( int );
+    int width() const;
+
+    void setBrush( const QBrush& b );
+    const QBrush& brush() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen& pen() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    virtual void draw( QPainter *, Qt::Orientation,
+        const QPointF& from, const QPointF& to ) const;
+
+private:
+
+    class PrivateData;
+    PrivateData* d_data;
+};
+
+#endif
diff --git a/qwt/qwt_knob.cpp b/qwt/qwt_knob.cpp
new file mode 100644
index 0000000..5860e42
--- /dev/null
+++ b/qwt/qwt_knob.cpp
@@ -0,0 +1,845 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_knob.h"
+#include "qwt_round_scale_draw.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qmath.h>
+#include <qapplication.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#define qFabs(x) ::fabs(x)
+#define qFastCos(x) qCos(x)
+#define qFastSin(x) qSin(x)
+#endif
+
+static QSize qwtKnobSizeHint( const QwtKnob *knob, int min )
+{
+    int knobWidth = knob->knobWidth();
+    if ( knobWidth <= 0 )
+        knobWidth = qMax( 3 * knob->markerSize(), min );
+
+    // Add the scale radial thickness to the knobWidth
+    const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) );
+    const int d = 2 * ( extent + 4 ) + knobWidth;
+
+    int left, right, top, bottom;
+    knob->getContentsMargins( &left, &top, &right, &bottom );
+
+    return QSize( d + left + right, d + top + bottom );
+}
+
+static inline double qwtToScaleAngle( double angle )
+{
+    // the map is counter clockwise with the origin
+    // at 90° using angles from -180° -> 180°
+
+    double a = 90.0 - angle;
+    if ( a <= -180.0 )
+        a += 360.0;
+    else if ( a >= 180.0 )
+        a -= 360.0;
+
+    return a;
+}
+
+static double qwtToDegrees( double value )
+{
+    return qwtNormalizeDegrees( 90.0 - value );
+}
+
+class QwtKnob::PrivateData
+{
+public:
+    PrivateData():
+        knobStyle( QwtKnob::Raised ),
+        markerStyle( QwtKnob::Notch ),
+        borderWidth( 2 ),
+        borderDist( 4 ),
+        scaleDist( 4 ),
+        maxScaleTicks( 11 ),
+        knobWidth( 0 ),
+        alignment( Qt::AlignCenter ),
+        markerSize( 8 ),
+        totalAngle( 270.0 ),
+        mouseOffset( 0.0 )
+    {
+    }
+
+    QwtKnob::KnobStyle knobStyle;
+    QwtKnob::MarkerStyle markerStyle;
+
+    int borderWidth;
+    int borderDist;
+    int scaleDist;
+    int maxScaleTicks;
+    int knobWidth;
+    Qt::Alignment alignment;
+    int markerSize;
+
+    double totalAngle;
+
+    double mouseOffset;
+};
+
+/*!
+  \brief Constructor
+
+  Construct a knob with an angle of 270°. The style is
+  QwtKnob::Raised and the marker style is QwtKnob::Notch.
+  The width of the knob is set to 50 pixels.
+
+  \param parent Parent widget
+
+  \sa setTotalAngle()
+*/
+QwtKnob::QwtKnob( QWidget* parent ):
+    QwtAbstractSlider( parent )
+{
+    d_data = new PrivateData;
+
+    setScaleDraw( new QwtRoundScaleDraw() );
+
+    setTotalAngle( 270.0 );
+
+    setScale( 0.0, 10.0 );
+    setValue( 0.0 );
+
+    setSizePolicy( QSizePolicy::MinimumExpanding, 
+        QSizePolicy::MinimumExpanding );
+}
+
+//! Destructor
+QwtKnob::~QwtKnob()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Set the knob type 
+
+  \param knobStyle Knob type
+  \sa knobStyle(), setBorderWidth()
+*/
+void QwtKnob::setKnobStyle( KnobStyle knobStyle )
+{
+    if ( d_data->knobStyle != knobStyle )
+    {
+        d_data->knobStyle = knobStyle;
+        update();
+    }
+}
+
+/*!
+    \return Marker type of the knob
+    \sa setKnobStyle(), setBorderWidth()
+*/
+QwtKnob::KnobStyle QwtKnob::knobStyle() const
+{
+    return d_data->knobStyle;
+}
+
+/*!
+  \brief Set the marker type of the knob
+
+  \param markerStyle Marker type
+  \sa markerStyle(), setMarkerSize()
+*/
+void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
+{
+    if ( d_data->markerStyle != markerStyle )
+    {
+        d_data->markerStyle = markerStyle;
+        update();
+    }
+}
+
+/*!
+  \return Marker type of the knob
+  \sa setMarkerStyle(), setMarkerSize()
+*/
+QwtKnob::MarkerStyle QwtKnob::markerStyle() const
+{
+    return d_data->markerStyle;
+}
+
+/*!
+  \brief Set the total angle by which the knob can be turned
+  \param angle Angle in degrees.
+
+  The angle has to be between [10, 360] degrees. Angles above
+  360 ( so that the knob can be turned several times around its axis )
+  have to be set using setNumTurns().
+
+  The default angle is 270 degrees. 
+
+  \sa totalAngle(), setNumTurns()
+*/
+void QwtKnob::setTotalAngle ( double angle )
+{
+    angle = qBound( 10.0, angle, 360.0 );
+
+    if ( angle != d_data->totalAngle )
+    {
+        d_data->totalAngle = angle;
+
+        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
+            0.5 * d_data->totalAngle );
+
+        updateGeometry();
+        update();
+    }
+}
+
+/*! 
+  \return the total angle
+  \sa setTotalAngle(), setNumTurns(), numTurns()
+ */
+double QwtKnob::totalAngle() const
+{
+    return d_data->totalAngle;
+}
+
+/*!
+  \brief Set the number of turns
+
+  When numTurns > 1 the knob can be turned several times around its axis
+  - otherwise the total angle is floored to 360°.
+
+  \sa numTurns(), totalAngle(), setTotalAngle()
+*/
+  
+void QwtKnob::setNumTurns( int numTurns )
+{
+    numTurns = qMax( numTurns, 1 );
+
+    if ( numTurns == 1 && d_data->totalAngle <= 360.0 )
+        return;
+
+    const double angle = numTurns * 360.0;
+    if ( angle != d_data->totalAngle )
+    {
+        d_data->totalAngle = angle;
+
+        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
+            0.5 * d_data->totalAngle );
+
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  \return Number of turns. 
+
+  When the total angle is below 360° numTurns() is ceiled to 1.
+  \sa setNumTurns(), setTotalAngle(), totalAngle()
+ */
+int QwtKnob::numTurns() const
+{
+    return qCeil( d_data->totalAngle / 360.0 );
+}
+
+/*!
+   Change the scale draw of the knob
+
+   For changing the labels of the scales, it
+   is necessary to derive from QwtRoundScaleDraw and
+   overload QwtRoundScaleDraw::label().
+
+   \sa scaleDraw()
+*/
+void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+    setTotalAngle( d_data->totalAngle );
+}
+
+/*!
+   \return the scale draw of the knob
+   \sa setScaleDraw()
+*/
+const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
+{
+    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+   \return the scale draw of the knob
+   \sa setScaleDraw()
+*/
+QwtRoundScaleDraw *QwtKnob::scaleDraw()
+{
+    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Calculate the bounding rectangle of the knob without the scale
+
+  \return Bounding rectangle of the knob
+  \sa knobWidth(), alignment(), QWidget::contentsRect()
+ */
+QRect QwtKnob::knobRect() const
+{
+    const QRect cr = contentsRect();
+
+    const int extent = qCeil( scaleDraw()->extent( font() ) );
+    const int d = extent + d_data->scaleDist;
+
+    int w = d_data->knobWidth;
+    if ( w <= 0 )
+    {
+        const int dim = qMin( cr.width(), cr.height() );
+
+        w = dim - 2 * ( d );
+        w = qMax( 0, w );
+    }
+
+    QRect r( 0, 0, w, w );
+
+    if ( d_data->alignment & Qt::AlignLeft )
+    {
+        r.moveLeft( cr.left() + d );
+    }
+    else if ( d_data->alignment & Qt::AlignRight )
+    {
+        r.moveRight( cr.right() - d );
+    }
+    else
+    {
+        r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
+    }
+
+    if ( d_data->alignment & Qt::AlignTop )
+    {
+        r.moveTop( cr.top() + d );
+    }
+    else if ( d_data->alignment & Qt::AlignBottom )
+    {
+        r.moveBottom( cr.bottom() - d );
+    }
+    else 
+    {
+        r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
+    }
+
+    return r;
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when pos is inside the circle of the knob.
+  \sa scrolledTo()
+*/
+bool QwtKnob::isScrollPosition( const QPoint &pos ) const
+{
+    const QRect kr = knobRect();
+
+    const QRegion region( kr, QRegion::Ellipse );
+    if ( region.contains( pos ) && ( pos != kr.center() ) )
+    {
+        const double angle = QLineF( kr.center(), pos ).angle();
+        const double valueAngle = qwtToDegrees( transform( value() ) );
+
+        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
+
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the mouse
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtKnob::scrolledTo( const QPoint &pos ) const
+{
+    double angle = QLineF( rect().center(), pos ).angle();
+    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
+
+    if ( scaleMap().pDist() > 360.0 )
+    {
+        angle = qwtToDegrees( angle );
+
+        const double v = transform( value() );
+
+        int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 );
+
+        double valueAngle = qwtNormalizeDegrees( v );
+        if ( qAbs( valueAngle - angle ) > 180.0 )
+        {
+            numTurns += ( angle > valueAngle ) ? -1 : 1;
+        }
+
+        angle += scaleMap().p1() + numTurns * 360.0;
+
+        if ( !wrapping() )
+        {
+            const double boundedAngle = 
+                qBound( scaleMap().p1(), angle, scaleMap().p2() );
+
+            d_data->mouseOffset += ( boundedAngle - angle );
+            angle = boundedAngle;
+        }
+    }
+    else
+    {
+        angle = qwtToScaleAngle( angle );
+
+        const double boundedAngle = 
+            qBound( scaleMap().p1(), angle, scaleMap().p2() );
+
+        if ( !wrapping() )
+            d_data->mouseOffset += ( boundedAngle - angle );
+
+        angle = boundedAngle;
+    }
+
+    return invTransform( angle );
+}
+
+/*! 
+  Handle QEvent::StyleChange and QEvent::FontChange;
+  \param event Change event
+*/
+void QwtKnob::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::StyleChange:
+        case QEvent::FontChange:
+        {
+            updateGeometry();
+            update();
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Repaint the knob
+  \param event Paint event
+*/
+void QwtKnob::paintEvent( QPaintEvent *event )
+{
+    const QRectF knobRect = this->knobRect();
+
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    painter.setRenderHint( QPainter::Antialiasing, true );
+
+    if ( !knobRect.contains( event->region().boundingRect() ) )
+    {
+        scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist );
+        scaleDraw()->moveCenter( knobRect.center() );
+
+        scaleDraw()->draw( &painter, palette() );
+    }
+
+    drawKnob( &painter, knobRect );
+
+    drawMarker( &painter, knobRect, 
+        qwtNormalizeDegrees( transform( value() ) ) );
+
+    painter.setRenderHint( QPainter::Antialiasing, false );
+
+    if ( hasFocus() )
+        drawFocusIndicator( &painter );
+}
+
+/*!
+  \brief Draw the knob
+
+  \param painter painter
+  \param knobRect Bounding rectangle of the knob (without scale)
+*/
+void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const
+{
+    double dim = qMin( knobRect.width(), knobRect.height() );
+    dim -= d_data->borderWidth * 0.5;
+
+    QRectF aRect( 0, 0, dim, dim );
+    aRect.moveCenter( knobRect.center() );
+
+    QPen pen( Qt::NoPen );
+    if ( d_data->borderWidth > 0 )
+    {
+        QColor c1 = palette().color( QPalette::Light );
+        QColor c2 = palette().color( QPalette::Dark );
+
+        QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
+        gradient.setColorAt( 0.0, c1 );
+        gradient.setColorAt( 0.3, c1 );
+        gradient.setColorAt( 0.7, c2 );
+        gradient.setColorAt( 1.0, c2 );
+
+        pen = QPen( gradient, d_data->borderWidth ); 
+    }
+
+    QBrush brush;
+    switch( d_data->knobStyle )
+    {
+        case QwtKnob::Raised:
+        {
+            double off = 0.3 * knobRect.width();
+            QRadialGradient gradient( knobRect.center(),
+                knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
+            
+            gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
+            gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
+
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Styled:
+        {
+            QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
+                knobRect.center().y() - knobRect.height() / 2,
+                knobRect.width() * 1.3,
+                knobRect.center().x(),
+                knobRect.center().y() - knobRect.height() / 2);
+
+            const QColor c = palette().color( QPalette::Button );
+            gradient.setColorAt(0, c.lighter(110));
+            gradient.setColorAt(qreal(0.5), c);
+            gradient.setColorAt(qreal(0.501), c.darker(102));
+            gradient.setColorAt(1, c.darker(115));
+
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Sunken:
+        {
+            QLinearGradient gradient( 
+                knobRect.topLeft(), knobRect.bottomRight() );
+            gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
+            gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
+            gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Flat:
+        default:
+            brush = palette().brush( QPalette::Button );
+    }
+
+    painter->setPen( pen );
+    painter->setBrush( brush );
+    painter->drawEllipse( aRect );
+}
+
+
+/*!
+  \brief Draw the marker at the knob's front
+
+  \param painter Painter
+  \param rect Bounding rectangle of the knob without scale
+  \param angle Angle of the marker in degrees 
+               ( clockwise, 0 at the 12 o'clock position )
+*/
+void QwtKnob::drawMarker( QPainter *painter, 
+    const QRectF &rect, double angle ) const
+{
+    if ( d_data->markerStyle == NoMarker || !isValid() )
+        return;
+
+    const double radians = qwtRadians( angle );
+    const double sinA = -qFastSin( radians );
+    const double cosA = qFastCos( radians );
+
+    const double xm = rect.center().x();
+    const double ym = rect.center().y();
+    const double margin = 4.0;
+
+    double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin;
+    if ( radius < 1.0 )
+        radius = 1.0;
+
+    int markerSize = d_data->markerSize;
+    if ( markerSize <= 0 )
+        markerSize = qRound( 0.4 * radius );
+
+    switch ( d_data->markerStyle )
+    {
+        case Notch:
+        case Nub:
+        {
+            const double dotWidth = 
+                qMin( double( markerSize ), radius);
+
+            const double dotCenterDist = radius - 0.5 * dotWidth;
+            if ( dotCenterDist > 0.0 )
+            {
+                const QPointF center( xm - sinA * dotCenterDist, 
+                    ym - cosA * dotCenterDist );
+
+                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
+                ellipse.moveCenter( center );
+
+                QColor c1 = palette().color( QPalette::Light );
+                QColor c2 = palette().color( QPalette::Mid );
+
+                if ( d_data->markerStyle == Notch )
+                    qSwap( c1, c2 );
+
+                QLinearGradient gradient( 
+                    ellipse.topLeft(), ellipse.bottomRight() );
+                gradient.setColorAt( 0.0, c1 );
+                gradient.setColorAt( 1.0, c2 );
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( gradient );
+
+                painter->drawEllipse( ellipse );
+            }
+            break;
+        }
+        case Dot:
+        {
+            const double dotWidth = 
+                qMin( double( markerSize ), radius);
+
+            const double dotCenterDist = radius - 0.5 * dotWidth;
+            if ( dotCenterDist > 0.0 )
+            {
+                const QPointF center( xm - sinA * dotCenterDist, 
+                    ym - cosA * dotCenterDist );
+
+                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
+                ellipse.moveCenter( center );
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( palette().color( QPalette::ButtonText ) );
+                painter->drawEllipse( ellipse );
+            }
+
+            break;
+        }
+        case Tick:
+        {
+            const double rb = qMax( radius - markerSize, 1.0 );
+            const double re = radius;
+
+            const QLineF line( xm - sinA * rb, ym - cosA * rb,
+                xm - sinA * re, ym - cosA * re );
+
+            QPen pen( palette().color( QPalette::ButtonText ), 0 );
+            pen.setCapStyle( Qt::FlatCap );
+            painter->setPen( pen );
+            painter->drawLine ( line );
+
+            break;
+        }
+        case Triangle:
+        {
+            const double rb = qMax( radius - markerSize, 1.0 );
+            const double re = radius;
+
+            painter->translate( rect.center() );
+            painter->rotate( angle - 90.0 );
+            
+            QPolygonF polygon;
+            polygon += QPointF( re, 0.0 );
+            polygon += QPointF( rb, 0.5 * ( re - rb ) );
+            polygon += QPointF( rb, -0.5 * ( re - rb ) );
+
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( palette().color( QPalette::ButtonText ) );
+            painter->drawPolygon( polygon );
+
+            painter->resetTransform();
+
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Draw the focus indicator
+  \param painter Painter
+*/
+void QwtKnob::drawFocusIndicator( QPainter *painter ) const
+{       
+    const QRect cr = contentsRect();
+
+    int w = d_data->knobWidth;
+    if ( w <= 0 )
+    {
+        w = qMin( cr.width(), cr.height() );
+    }
+    else
+    {
+        const int extent = qCeil( scaleDraw()->extent( font() ) );
+        w += 2 * ( extent + d_data->scaleDist );
+    }
+
+    QRect focusRect( 0, 0, w, w );
+    focusRect.moveCenter( cr.center() );
+
+    QwtPainter::drawFocusRect( painter, this, focusRect );
+}  
+
+/*!
+  \brief Set the alignment of the knob
+
+  Similar to a QLabel::alignment() the flags decide how
+  to align the knob inside of contentsRect(). 
+
+  The default setting is Qt::AlignCenter
+
+  \param alignment Or'd alignment flags
+
+  \sa alignment(), setKnobWidth(), knobRect()
+ */
+void QwtKnob::setAlignment( Qt::Alignment alignment )
+{
+    if ( d_data->alignment != alignment )
+    {
+        d_data->alignment = alignment;
+        update();
+    }
+}
+
+/*!
+  \return Alignment of the knob inside of contentsRect()
+  \sa setAlignment(), knobWidth(), knobRect()
+ */
+Qt::Alignment QwtKnob::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+  \brief Change the knob's width.
+
+  Setting a fixed value for the diameter of the knob 
+  is helpful for aligning several knobs in a row.
+
+  \param width New width
+
+  \sa knobWidth(), setAlignment()
+  \note Modifies the sizePolicy() 
+*/
+void QwtKnob::setKnobWidth( int width )
+{
+    width = qMax( width, 0 );
+
+    if ( width != d_data->knobWidth )
+    {
+        QSizePolicy::Policy policy;
+        if ( width > 0 )
+            policy = QSizePolicy::Minimum;
+        else
+            policy = QSizePolicy::MinimumExpanding;
+
+        setSizePolicy( policy, policy );
+
+        d_data->knobWidth = width;
+
+        updateGeometry();
+        update();
+    }
+}
+
+//! Return the width of the knob
+int QwtKnob::knobWidth() const
+{
+    return d_data->knobWidth;
+}
+
+/*!
+  \brief Set the knob's border width
+  \param borderWidth new border width
+*/
+void QwtKnob::setBorderWidth( int borderWidth )
+{
+    d_data->borderWidth = qMax( borderWidth, 0 );
+
+    updateGeometry();
+    update();
+
+}
+
+//! Return the border width
+int QwtKnob::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Set the size of the marker
+
+  When setting a size <= 0 the marker will
+  automatically scaled to 40% of the radius of the knob.
+
+  \sa markerSize(), markerStyle()
+*/
+void QwtKnob::setMarkerSize( int size )
+{
+    if ( d_data->markerSize != size )
+    {
+        d_data->markerSize = size;
+        update();
+    }
+}
+
+/*! 
+  \return Marker size
+  \sa setMarkerSize()
+ */
+int QwtKnob::markerSize() const
+{
+    return d_data->markerSize;
+}
+
+/*!
+  \return sizeHint()
+*/
+QSize QwtKnob::sizeHint() const
+{
+    const QSize hint = qwtKnobSizeHint( this, 50 );
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtKnob::minimumSizeHint() const
+{
+    return qwtKnobSizeHint( this, 20 );
+}
diff --git a/qwt/qwt_knob.h b/qwt/qwt_knob.h
new file mode 100644
index 0000000..852374c
--- /dev/null
+++ b/qwt/qwt_knob.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_KNOB_H
+#define QWT_KNOB_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_slider.h"
+
+class QwtRoundScaleDraw;
+
+/*!
+  \brief The Knob Widget
+
+  The QwtKnob widget imitates look and behavior of a volume knob on a radio.
+  It looks similar to QDial - not to QwtDial.
+
+  The value range of a knob might be divided into several turns.
+
+  The layout of the knob depends on the knobWidth().
+
+  - width > 0 
+    The diameter of the knob is fixed and the knob is aligned
+    according to the alignment() flags inside of the contentsRect(). 
+
+  - width <= 0
+    The knob is extended to the minimum of width/height of the contentsRect()
+    and aligned in the other direction according to alignment().
+
+  Setting a fixed knobWidth() is helpful to align several knobs with different
+  scale labels.
+  
+  \image html knob.png
+*/
+
+class QWT_EXPORT QwtKnob: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS ( KnobStyle MarkerStyle )
+
+    Q_PROPERTY( KnobStyle knobStyle READ knobStyle WRITE setKnobStyle )
+    Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth )
+    Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment )
+    Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle )
+    Q_PROPERTY( int numTurns READ numTurns WRITE setNumTurns )
+    Q_PROPERTY( MarkerStyle markerStyle READ markerStyle WRITE setMarkerStyle )
+    Q_PROPERTY( int markerSize READ markerSize WRITE setMarkerSize )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+
+public:
+    /*! 
+       \brief Style of the knob surface
+
+       Depending on the KnobStyle the surface of the knob is
+       filled from the brushes of the widget palette().
+
+       \sa setKnobStyle(), knobStyle()
+     */
+    enum KnobStyle
+    {
+        //! Fill the knob with a brush from QPalette::Button.
+        Flat,
+
+        //! Build a gradient from QPalette::Midlight and QPalette::Button
+        Raised,
+
+        /*! 
+          Build a gradient from QPalette::Midlight, QPalette::Button
+          and QPalette::Midlight
+         */
+        Sunken,
+
+        /*! 
+          Build a radial gradient from QPalette::Button
+          like it is used for QDial in various Qt styles.
+         */
+        Styled
+    };
+
+    /*!
+        \brief Marker type
+ 
+        The marker indicates the current value on the knob
+        The default setting is a Notch marker.
+
+        \sa setMarkerStyle(), setMarkerSize()
+    */
+    enum MarkerStyle 
+    { 
+        //! Don't paint any marker
+        NoMarker = -1,
+
+        //! Paint a single tick in QPalette::ButtonText color
+        Tick, 
+
+        //! Paint a triangle in QPalette::ButtonText color
+        Triangle, 
+
+        //! Paint a circle in QPalette::ButtonText color
+        Dot, 
+
+        /*! 
+          Draw a raised ellipse with a gradient build from
+          QPalette::Light and QPalette::Mid
+         */ 
+        Nub, 
+
+        /*! 
+          Draw a sunken ellipse with a gradient build from
+          QPalette::Light and QPalette::Mid
+         */ 
+        Notch 
+    };
+
+    explicit QwtKnob( QWidget* parent = NULL );
+    virtual ~QwtKnob();
+
+    void setAlignment( Qt::Alignment );
+    Qt::Alignment alignment() const;
+
+    void setKnobWidth( int );
+    int knobWidth() const;
+
+    void setNumTurns( int );
+    int numTurns() const;
+
+    void setTotalAngle ( double angle );
+    double totalAngle() const;
+
+    void setKnobStyle( KnobStyle );
+    KnobStyle knobStyle() const;
+
+    void setBorderWidth( int bw );
+    int borderWidth() const;
+
+    void setMarkerStyle( MarkerStyle );
+    MarkerStyle markerStyle() const;
+
+    void setMarkerSize( int );
+    int markerSize() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtRoundScaleDraw * );
+
+    const QwtRoundScaleDraw *scaleDraw() const;
+    QwtRoundScaleDraw *scaleDraw();
+
+    QRect knobRect() const;
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+
+    virtual void drawKnob( QPainter *, const QRectF & ) const;
+
+    virtual void drawFocusIndicator( QPainter * ) const;
+
+    virtual void drawMarker( QPainter *, 
+        const QRectF &, double arc ) const;
+
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_legend.cpp b/qwt/qwt_legend.cpp
new file mode 100644
index 0000000..01cef89
--- /dev/null
+++ b/qwt/qwt_legend.cpp
@@ -0,0 +1,801 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_legend.h"
+#include "qwt_legend_label.h"
+#include "qwt_dyngrid_layout.h"
+#include "qwt_math.h"
+#include "qwt_plot_item.h"
+#include "qwt_painter.h"
+#include <qapplication.h>
+#include <qscrollbar.h>
+#include <qscrollarea.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+class QwtLegendMap
+{
+public:
+    inline bool isEmpty() const { return d_entries.isEmpty(); }
+
+    void insert( const QVariant &, const QList<QWidget *> & );
+    void remove( const QVariant & );
+
+    void removeWidget( const QWidget * );
+
+    QList<QWidget *> legendWidgets( const QVariant & ) const;
+    QVariant itemInfo( const QWidget * ) const;
+
+private:
+    // we don't know anything about itemInfo and therefore don't have
+    // any key that can be used for a map or hashtab.
+    // But a simple linear list is o.k. here, as we will never have
+    // more than a few entries.
+
+    class Entry
+    {
+    public:
+        QVariant itemInfo;
+        QList<QWidget *> widgets;
+    };
+
+    QList< Entry > d_entries;
+};
+
+void QwtLegendMap::insert( const QVariant &itemInfo, 
+    const QList<QWidget *> &widgets )
+{
+    for ( int i = 0; i < d_entries.size(); i++ )
+    {
+        Entry &entry = d_entries[i];
+        if ( entry.itemInfo == itemInfo )
+        {
+            entry.widgets = widgets;
+            return;
+        }
+    }
+
+    Entry newEntry;
+    newEntry.itemInfo = itemInfo;
+    newEntry.widgets = widgets;
+
+    d_entries += newEntry;
+}
+
+void QwtLegendMap::remove( const QVariant &itemInfo )
+{
+    for ( int i = 0; i < d_entries.size(); i++ )
+    {
+        Entry &entry = d_entries[i];
+        if ( entry.itemInfo == itemInfo )
+        {
+            d_entries.removeAt( i );
+            return;
+        }
+    }
+}
+
+void QwtLegendMap::removeWidget( const QWidget *widget )
+{
+    QWidget *w = const_cast<QWidget *>( widget );
+
+    for ( int i = 0; i < d_entries.size(); i++ )
+        d_entries[ i ].widgets.removeAll( w );
+}
+
+QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
+{
+    if ( widget != NULL )
+    {
+        QWidget *w = const_cast<QWidget *>( widget );
+
+        for ( int i = 0; i < d_entries.size(); i++ )
+        {
+            const Entry &entry = d_entries[i];
+            if ( entry.widgets.indexOf( w ) >= 0 )
+                return entry.itemInfo;
+        }
+    }
+
+    return QVariant();
+}
+
+QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
+{
+    if ( itemInfo.isValid() )
+    {
+        for ( int i = 0; i < d_entries.size(); i++ )
+        {
+            const Entry &entry = d_entries[i];
+            if ( entry.itemInfo == itemInfo )
+                return entry.widgets;
+        }
+    }
+
+    return QList<QWidget *>();
+}
+
+class QwtLegend::PrivateData
+{
+public:
+    PrivateData():
+        itemMode( QwtLegendData::ReadOnly ),
+        view( NULL )
+    {
+    }
+
+    QwtLegendData::Mode itemMode;
+    QwtLegendMap itemMap;
+
+    class LegendView;
+    LegendView *view;
+};
+
+class QwtLegend::PrivateData::LegendView: public QScrollArea
+{
+public:
+    LegendView( QWidget *parent ):
+        QScrollArea( parent )
+    {
+        contentsWidget = new QWidget( this );
+        contentsWidget->setObjectName( "QwtLegendViewContents" );
+
+        setWidget( contentsWidget );
+        setWidgetResizable( false );
+
+        viewport()->setObjectName( "QwtLegendViewport" );
+
+        // QScrollArea::setWidget internally sets autoFillBackground to true
+        // But we don't want a background.
+        contentsWidget->setAutoFillBackground( false );
+        viewport()->setAutoFillBackground( false );
+    }
+
+    virtual bool event( QEvent *event )
+    {
+        if ( event->type() == QEvent::PolishRequest )
+        {
+            setFocusPolicy( Qt::NoFocus );
+        }
+
+        if ( event->type() == QEvent::Resize )
+        {
+            // adjust the size to en/disable the scrollbars
+            // before QScrollArea adjusts the viewport size
+
+            const QRect cr = contentsRect();
+
+            int w = cr.width();
+            int h = contentsWidget->heightForWidth( cr.width() );
+            if ( h > w )
+            {
+                w -= verticalScrollBar()->sizeHint().width();
+                h = contentsWidget->heightForWidth( w );
+            }
+
+            contentsWidget->resize( w, h );
+        }
+
+        return QScrollArea::event( event );
+    }
+
+    virtual bool viewportEvent( QEvent *event )
+    {
+        bool ok = QScrollArea::viewportEvent( event );
+
+        if ( event->type() == QEvent::Resize )
+        {
+            layoutContents();
+        }
+        return ok;
+    }
+
+    QSize viewportSize( int w, int h ) const
+    {
+        const int sbHeight = horizontalScrollBar()->sizeHint().height();
+        const int sbWidth = verticalScrollBar()->sizeHint().width();
+
+        const int cw = contentsRect().width();
+        const int ch = contentsRect().height();
+
+        int vw = cw;
+        int vh = ch;
+
+        if ( w > vw )
+            vh -= sbHeight;
+
+        if ( h > vh )
+        {
+            vw -= sbWidth;
+            if ( w > vw && vh == ch )
+                vh -= sbHeight;
+        }
+        return QSize( vw, vh );
+    }
+
+    void layoutContents()
+    {
+        const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
+            contentsWidget->layout() );
+        if ( tl == NULL )
+            return;
+
+        const QSize visibleSize = viewport()->contentsRect().size();
+
+        const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
+
+        int w = qMax( visibleSize.width(), minW );
+        int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
+
+        const int vpWidth = viewportSize( w, h ).width();
+        if ( w > vpWidth )
+        {
+            w = qMax( vpWidth, minW );
+            h = qMax( tl->heightForWidth( w ), visibleSize.height() );
+        }
+
+        contentsWidget->resize( w, h );
+    }
+
+    QWidget *contentsWidget;
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtLegend::QwtLegend( QWidget *parent ):
+    QwtAbstractLegend( parent )
+{
+    setFrameStyle( NoFrame );
+
+    d_data = new QwtLegend::PrivateData;
+
+    d_data->view = new QwtLegend::PrivateData::LegendView( this );
+    d_data->view->setObjectName( "QwtLegendView" );
+    d_data->view->setFrameStyle( NoFrame );
+
+    QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
+        d_data->view->contentsWidget );
+    gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
+
+    d_data->view->contentsWidget->installEventFilter( this );
+
+    QVBoxLayout *layout = new QVBoxLayout( this );
+    layout->setContentsMargins( 0, 0, 0, 0 );
+    layout->addWidget( d_data->view );
+}
+
+//! Destructor
+QwtLegend::~QwtLegend()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Set the maximum number of entries in a row
+
+  F.e when the maximum is set to 1 all items are aligned
+  vertically. 0 means unlimited
+
+  \param numColums Maximum number of entries in a row
+
+  \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
+ */
+void QwtLegend::setMaxColumns( uint numColums )
+{
+    QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
+        d_data->view->contentsWidget->layout() );
+    if ( tl )
+        tl->setMaxColumns( numColums );
+}
+
+/*!
+  \return Maximum number of entries in a row
+  \sa setMaxColumns(), QwtDynGridLayout::maxColumns()
+ */
+uint QwtLegend::maxColumns() const
+{
+    uint maxCols = 0;
+
+    const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
+        d_data->view->contentsWidget->layout() );
+    if ( tl )
+        maxCols = tl->maxColumns();
+
+    return maxCols;
+}
+
+/*!
+  \brief Set the default mode for legend labels
+
+  Legend labels will be constructed according to the
+  attributes in a QwtLegendData object. When it doesn't
+  contain a value for the QwtLegendData::ModeRole the
+  label will be initialized with the default mode of the legend.
+
+  \param mode Default item mode
+
+  \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData()
+  \note Changing the mode doesn't have any effect on existing labels.
+ */
+void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode )
+{
+    d_data->itemMode = mode;
+}
+
+/*!
+  \return Default item mode
+  \sa setDefaultItemMode()
+*/
+QwtLegendData::Mode QwtLegend::defaultItemMode() const
+{
+    return d_data->itemMode;
+}
+
+/*!
+  The contents widget is the only child of the viewport of 
+  the internal QScrollArea and the parent widget of all legend items.
+
+  \return Container widget of the legend items
+*/
+QWidget *QwtLegend::contentsWidget()
+{
+    return d_data->view->contentsWidget;
+}
+
+/*!
+  \return Horizontal scrollbar
+  \sa verticalScrollBar()
+*/
+QScrollBar *QwtLegend::horizontalScrollBar() const
+{
+    return d_data->view->horizontalScrollBar();
+}
+
+/*!
+  \return Vertical scrollbar
+  \sa horizontalScrollBar()
+*/
+QScrollBar *QwtLegend::verticalScrollBar() const
+{
+    return d_data->view->verticalScrollBar();
+}
+
+/*!
+  The contents widget is the only child of the viewport of 
+  the internal QScrollArea and the parent widget of all legend items.
+
+  \return Container widget of the legend items
+
+*/
+const QWidget *QwtLegend::contentsWidget() const
+{
+    return d_data->view->contentsWidget;
+}
+
+/*!
+  \brief Update the entries for an item
+
+  \param itemInfo Info for an item
+  \param data List of legend entry attributes for the item
+ */
+void QwtLegend::updateLegend( const QVariant &itemInfo, 
+    const QList<QwtLegendData> &data )
+{
+    QList<QWidget *> widgetList = legendWidgets( itemInfo );
+
+    if ( widgetList.size() != data.size() )
+    {
+        QLayout *contentsLayout = d_data->view->contentsWidget->layout();
+
+        while ( widgetList.size() > data.size() )
+        {
+            QWidget *w = widgetList.takeLast();
+
+            contentsLayout->removeWidget( w );
+
+            // updates might be triggered by signals from the legend widget
+            // itself. So we better don't delete it here.
+
+            w->hide();
+            w->deleteLater();
+        }
+
+        for ( int i = widgetList.size(); i < data.size(); i++ )
+        {
+            QWidget *widget = createWidget( data[i] );
+
+            if ( contentsLayout )
+                contentsLayout->addWidget( widget );
+
+            widgetList += widget;
+        }
+
+        if ( widgetList.isEmpty() )
+        {
+            d_data->itemMap.remove( itemInfo );
+        }
+        else
+        {
+            d_data->itemMap.insert( itemInfo, widgetList );
+        }
+
+        updateTabOrder();
+    }
+    
+    for ( int i = 0; i < data.size(); i++ )
+        updateWidget( widgetList[i], data[i] );
+}
+
+/*!
+  \brief Create a widget to be inserted into the legend
+
+  The default implementation returns a QwtLegendLabel.
+
+  \param data Attributes of the legend entry
+  \return Widget representing data on the legend
+  
+  \note updateWidget() will called soon after createWidget()
+        with the same attributes.
+ */
+QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
+{
+    Q_UNUSED( data );
+
+    QwtLegendLabel *label = new QwtLegendLabel();
+    label->setItemMode( defaultItemMode() );
+
+    connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
+    connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );
+
+    return label;
+}
+
+/*!
+  \brief Update the widget 
+
+  \param widget Usually a QwtLegendLabel
+  \param data Attributes to be displayed
+
+  \sa createWidget()
+  \note When widget is no QwtLegendLabel updateWidget() does nothing.
+ */
+void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
+{
+    QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
+    if ( label )
+    {
+        label->setData( data );
+        if ( !data.value( QwtLegendData::ModeRole ).isValid() )
+        {
+            // use the default mode, when there is no specific
+            // hint from the legend data
+
+            label->setItemMode( defaultItemMode() );
+        }
+    }
+}
+
+void QwtLegend::updateTabOrder()
+{
+    QLayout *contentsLayout = d_data->view->contentsWidget->layout();
+    if ( contentsLayout )
+    {
+        // set tab focus chain
+
+        QWidget *w = NULL;
+
+        for ( int i = 0; i < contentsLayout->count(); i++ )
+        {
+            QLayoutItem *item = contentsLayout->itemAt( i );
+            if ( w && item->widget() )
+                QWidget::setTabOrder( w, item->widget() );
+
+            w = item->widget();
+        }
+    }
+}
+
+//! Return a size hint.
+QSize QwtLegend::sizeHint() const
+{
+    QSize hint = d_data->view->contentsWidget->sizeHint();
+    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
+
+    return hint;
+}
+
+/*!
+  \return The preferred height, for a width.
+  \param width Width
+*/
+int QwtLegend::heightForWidth( int width ) const
+{
+    width -= 2 * frameWidth();
+
+    int h = d_data->view->contentsWidget->heightForWidth( width );
+    if ( h >= 0 )
+        h += 2 * frameWidth();
+
+    return h;
+}
+
+
+/*!
+  Handle QEvent::ChildRemoved andQEvent::LayoutRequest events 
+  for the contentsWidget().
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Forwarded to QwtAbstractLegend::eventFilter()
+*/
+bool QwtLegend::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == d_data->view->contentsWidget )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::ChildRemoved:
+            {
+                const QChildEvent *ce = 
+                    static_cast<const QChildEvent *>(event);
+                if ( ce->child()->isWidgetType() )
+                {
+                    QWidget *w = static_cast< QWidget * >( ce->child() );
+                    d_data->itemMap.removeWidget( w );
+                }
+                break;
+            }
+            case QEvent::LayoutRequest:
+            {
+                d_data->view->layoutContents();
+
+                if ( parentWidget() && parentWidget()->layout() == NULL )
+                {
+                    /*
+                       We want the parent widget ( usually QwtPlot ) to recalculate
+                       its layout, when the contentsWidget has changed. But
+                       because of the scroll view we have to forward the LayoutRequest
+                       event manually.
+
+                       We don't use updateGeometry() because it doesn't post LayoutRequest
+                       events when the legend is hidden. But we want the
+                       parent widget notified, so it can show/hide the legend
+                       depending on its items.
+                     */
+                    QApplication::postEvent( parentWidget(),
+                        new QEvent( QEvent::LayoutRequest ) );
+                }                
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    return QwtAbstractLegend::eventFilter( object, event );
+}
+
+/*!
+  Called internally when the legend has been clicked on.
+  Emits a clicked() signal.
+*/
+void QwtLegend::itemClicked()
+{
+    QWidget *w = qobject_cast<QWidget *>( sender() );
+    if ( w )
+    {
+        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
+        if ( itemInfo.isValid() )
+        {
+            const QList<QWidget *> widgetList =
+                d_data->itemMap.legendWidgets( itemInfo );
+
+            const int index = widgetList.indexOf( w );
+            if ( index >= 0 )
+                Q_EMIT clicked( itemInfo, index );
+        }
+    }
+}
+
+/*!
+  Called internally when the legend has been checked
+  Emits a checked() signal.
+*/
+void QwtLegend::itemChecked( bool on )
+{
+    QWidget *w = qobject_cast<QWidget *>( sender() );
+    if ( w )
+    {
+        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
+        if ( itemInfo.isValid() )
+        {
+            const QList<QWidget *> widgetList =
+                d_data->itemMap.legendWidgets( itemInfo );
+
+            const int index = widgetList.indexOf( w );
+            if ( index >= 0 )
+                Q_EMIT checked( itemInfo, on, index );
+        }
+    }
+}
+
+/*!
+  Render the legend into a given rectangle.
+
+  \param painter Painter
+  \param rect Bounding rectangle
+  \param fillBackground When true, fill rect with the widget background 
+
+  \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself
+*/
+void QwtLegend::renderLegend( QPainter *painter, 
+    const QRectF &rect, bool fillBackground ) const
+{
+    if ( d_data->itemMap.isEmpty() )
+        return;
+
+    if ( fillBackground )
+    {
+        if ( autoFillBackground() ||
+            testAttribute( Qt::WA_StyledBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, rect, this );
+        }
+    }
+
+    const QwtDynGridLayout *legendLayout = 
+        qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
+    if ( legendLayout == NULL )
+        return;
+
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+
+    QRect layoutRect; 
+    layoutRect.setLeft( qCeil( rect.left() ) + left );
+    layoutRect.setTop( qCeil( rect.top() ) + top );
+    layoutRect.setRight( qFloor( rect.right() ) - right );
+    layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
+
+    uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
+    QList<QRect> itemRects =
+        legendLayout->layoutItems( layoutRect, numCols );
+
+    int index = 0;
+
+    for ( int i = 0; i < legendLayout->count(); i++ )
+    {
+        QLayoutItem *item = legendLayout->itemAt( i );
+        QWidget *w = item->widget();
+        if ( w )
+        {
+            painter->save();
+
+            painter->setClipRect( itemRects[index] );
+            renderItem( painter, w, itemRects[index], fillBackground );
+
+            index++;
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  Render a legend entry into a given rectangle.
+
+  \param painter Painter
+  \param widget Widget representing a legend entry
+  \param rect Bounding rectangle
+  \param fillBackground When true, fill rect with the widget background 
+
+  \note When widget is not derived from QwtLegendLabel renderItem
+        does nothing beside the background
+*/
+void QwtLegend::renderItem( QPainter *painter, 
+    const QWidget *widget, const QRectF &rect, bool fillBackground ) const
+{
+    if ( fillBackground )
+    {
+        if ( widget->autoFillBackground() ||
+            widget->testAttribute( Qt::WA_StyledBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, rect, widget );
+        }
+    }
+
+    const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
+    if ( label )
+    {
+        // icon
+
+        const QwtGraphic &icon = label->data().icon();
+        const QSizeF sz = icon.defaultSize();
+
+        const QRectF iconRect( rect.x() + label->margin(),
+            rect.center().y() - 0.5 * sz.height(), 
+            sz.width(), sz.height() );
+
+        icon.render( painter, iconRect, Qt::KeepAspectRatio );
+
+        // title
+
+        QRectF titleRect = rect;
+        titleRect.setX( iconRect.right() + 2 * label->spacing() );
+
+        painter->setFont( label->font() );
+        painter->setPen( label->palette().color( QPalette::Text ) );
+        const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
+    }
+}
+
+/*!
+  \return List of widgets associated to a item
+  \param itemInfo Info about an item
+  \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo()
+ */
+QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
+{
+    return d_data->itemMap.legendWidgets( itemInfo );
+}
+
+/*!
+  \return First widget in the list of widgets associated to an item
+  \param itemInfo Info about an item
+  \sa itemInfo(), QwtPlot::itemToInfo()
+  \note Almost all types of items have only one widget
+*/
+QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
+{
+    const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
+    if ( list.isEmpty() )
+        return NULL;
+
+    return list[0];
+}
+
+/*!
+  Find the item that is associated to a widget
+
+  \param widget Widget on the legend
+  \return Associated item info
+  \sa legendWidget()
+ */
+QVariant QwtLegend::itemInfo( const QWidget *widget ) const
+{
+    return d_data->itemMap.itemInfo( widget );
+}
+
+//! \return True, when no item is inserted
+bool QwtLegend::isEmpty() const
+{
+    return d_data->itemMap.isEmpty();
+}
+
+/*!
+    Return the extent, that is needed for the scrollbars
+
+    \param orientation Orientation ( 
+    \return The width of the vertical scrollbar for Qt::Horizontal and v.v.
+ */
+int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
+{
+    int extent = 0;
+
+    if ( orientation == Qt::Horizontal )
+        extent = verticalScrollBar()->sizeHint().width();
+    else
+        extent = horizontalScrollBar()->sizeHint().height();
+
+    return extent;
+}
+
diff --git a/qwt/qwt_legend.h b/qwt/qwt_legend.h
new file mode 100644
index 0000000..3d8fca6
--- /dev/null
+++ b/qwt/qwt_legend.h
@@ -0,0 +1,117 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_H
+#define QWT_LEGEND_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_legend.h"
+#include <qvariant.h>
+
+class QScrollBar;
+
+/*!
+  \brief The legend widget
+
+  The QwtLegend widget is a tabular arrangement of legend items. Legend
+  items might be any type of widget, but in general they will be
+  a QwtLegendLabel.
+
+  \sa QwtLegendLabel, QwtPlotItem, QwtPlot
+*/
+
+class QWT_EXPORT QwtLegend : public QwtAbstractLegend
+{
+    Q_OBJECT
+
+public:
+    explicit QwtLegend( QWidget *parent = NULL );
+    virtual ~QwtLegend();
+
+    void setMaxColumns( uint numColums );
+    uint maxColumns() const;
+
+    void setDefaultItemMode( QwtLegendData::Mode );
+    QwtLegendData::Mode defaultItemMode() const;
+
+    QWidget *contentsWidget();
+    const QWidget *contentsWidget() const;
+
+    QWidget *legendWidget( const QVariant &  ) const;
+    QList<QWidget *> legendWidgets( const QVariant & ) const;
+
+    QVariant itemInfo( const QWidget * ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    virtual QSize sizeHint() const;
+    virtual int heightForWidth( int w ) const;
+
+    QScrollBar *horizontalScrollBar() const;
+    QScrollBar *verticalScrollBar() const;
+
+    virtual void renderLegend( QPainter *, 
+        const QRectF &, bool fillBackground ) const;
+
+    virtual void renderItem( QPainter *, 
+        const QWidget *, const QRectF &, bool fillBackground ) const;
+
+    virtual bool isEmpty() const;
+    virtual int scrollExtent( Qt::Orientation ) const;
+
+Q_SIGNALS:
+    /*!
+      A signal which is emitted when the user has clicked on
+      a legend label, which is in QwtLegendData::Clickable mode.
+
+      \param itemInfo Info for the item item of the
+                      selected legend item
+      \param index Index of the legend label in the list of widgets
+                   that are associated with the plot item
+
+      \note clicks are disabled as default
+      \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo()
+     */
+    void clicked( const QVariant &itemInfo, int index );
+
+    /*!
+      A signal which is emitted when the user has clicked on
+      a legend label, which is in QwtLegendData::Checkable mode
+
+      \param itemInfo Info for the item of the
+                      selected legend label
+      \param index Index of the legend label in the list of widgets
+                   that are associated with the plot item
+      \param on True when the legend label is checked
+
+      \note clicks are disabled as default
+      \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo()
+     */
+    void checked( const QVariant &itemInfo, bool on, int index );
+
+public Q_SLOTS:
+    virtual void updateLegend( const QVariant &,
+        const QList<QwtLegendData> & );
+
+protected Q_SLOTS:
+    void itemClicked();
+    void itemChecked( bool );
+
+protected:
+    virtual QWidget *createWidget( const QwtLegendData & ) const;
+    virtual void updateWidget( QWidget *widget, const QwtLegendData &data );
+
+private:
+    void updateTabOrder();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/qwt/qwt_legend_data.cpp b/qwt/qwt_legend_data.cpp
new file mode 100644
index 0000000..cf0cb2c
--- /dev/null
+++ b/qwt/qwt_legend_data.cpp
@@ -0,0 +1,129 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_legend_data.h"
+
+//! Constructor
+QwtLegendData::QwtLegendData()
+{
+}
+
+//! Destructor
+QwtLegendData::~QwtLegendData()
+{
+}
+
+/*!
+  Set the legend attributes
+
+  QwtLegendData actually is a QMap<int, QVariant> with some
+  convenience interfaces
+
+  \param map Values
+  \sa values()
+ */
+void QwtLegendData::setValues( const QMap<int, QVariant> &map )
+{
+    d_map = map;
+}
+
+/*!
+  \return Legend attributes
+  \sa setValues()
+ */
+const QMap<int, QVariant> &QwtLegendData::values() const
+{
+    return d_map;
+}
+
+/*!
+  \param role Attribute role
+  \return True, when the internal map has an entry for role
+ */
+bool QwtLegendData::hasRole( int role ) const
+{
+    return d_map.contains( role );
+}
+
+/*!
+  Set an attribute value
+
+  \param role Attribute role
+  \param data Attribute value
+
+  \sa value()
+ */
+void QwtLegendData::setValue( int role, const QVariant &data )
+{
+    d_map[role] = data;
+}
+
+/*!
+  \param role Attribute role
+  \return Attribute value for a specific role
+ */
+QVariant QwtLegendData::value( int role ) const
+{
+    if ( !d_map.contains( role ) )
+        return QVariant();
+
+    return d_map[role];
+}
+
+//! \return True, when the internal map is empty
+bool QwtLegendData::isValid() const
+{
+    return !d_map.isEmpty();
+}
+
+//! \return Value of the TitleRole attribute
+QwtText QwtLegendData::title() const
+{
+    QwtText text;
+
+    const QVariant titleValue = value( QwtLegendData::TitleRole );
+    if ( titleValue.canConvert<QwtText>() )
+    {
+        text = qvariant_cast<QwtText>( titleValue );
+    }
+    else if ( titleValue.canConvert<QString>() )
+    {
+        text.setText( qvariant_cast<QString>( titleValue ) );
+    }
+
+    return text;
+}
+
+//! \return Value of the IconRole attribute
+QwtGraphic QwtLegendData::icon() const
+{
+    const QVariant iconValue = value( QwtLegendData::IconRole );
+
+    QwtGraphic graphic;
+    if ( iconValue.canConvert<QwtGraphic>() )
+    {
+        graphic = qvariant_cast<QwtGraphic>( iconValue );
+    }
+
+    return graphic;
+}
+
+//! \return Value of the ModeRole attribute
+QwtLegendData::Mode QwtLegendData::mode() const
+{
+    const QVariant modeValue = value( QwtLegendData::ModeRole );
+    if ( modeValue.canConvert<int>() )
+    {
+        const int mode = qvariant_cast<int>( modeValue );
+        return static_cast<QwtLegendData::Mode>( mode );
+    }
+    
+    return QwtLegendData::ReadOnly;
+}
+
diff --git a/qwt/qwt_legend_data.h b/qwt/qwt_legend_data.h
new file mode 100644
index 0000000..d83e132
--- /dev/null
+++ b/qwt/qwt_legend_data.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_DATA_H
+#define QWT_LEGEND_DATA_H
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include "qwt_graphic.h"
+#include <qvariant.h>
+#include <qpixmap.h>
+#include <qmap.h>
+
+/*!
+  \brief Attributes of an entry on a legend
+
+  QwtLegendData is an abstract container ( like QAbstractModel )
+  to exchange attributes, that are only known between to 
+  the plot item and the legend. 
+  
+  By overloading QwtPlotItem::legendData() any other set of attributes
+  could be used, that can be handled by a modified ( or completely 
+  different ) implementation of a legend.
+
+  \sa QwtLegend, QwtPlotLegendItem
+  \note The stockchart example implements a legend as a tree
+        with checkable items
+ */
+class QWT_EXPORT QwtLegendData
+{
+public:
+    //! Mode defining how a legend entry interacts
+    enum Mode
+    {
+        //! The legend item is not interactive, like a label
+        ReadOnly,
+
+        //! The legend item is clickable, like a push button
+        Clickable,
+
+        //! The legend item is checkable, like a checkable button
+        Checkable
+    };
+
+    //! Identifier how to interprete a QVariant
+    enum Role
+    {
+        // The value is a Mode
+        ModeRole, 
+
+        // The value is a title
+        TitleRole, 
+
+        // The value is an icon
+        IconRole, 
+
+        // Values < UserRole are reserved for internal use
+        UserRole  = 32
+    };
+
+    QwtLegendData();
+    ~QwtLegendData();
+
+    void setValues( const QMap<int, QVariant> & );
+    const QMap<int, QVariant> &values() const;
+
+    void setValue( int role, const QVariant & );
+    QVariant value( int role ) const;
+
+    bool hasRole( int role ) const;
+    bool isValid() const;
+
+    QwtGraphic icon() const;
+    QwtText title() const;
+    Mode mode() const;
+
+private:
+    QMap<int, QVariant> d_map;
+};
+
+#endif
diff --git a/qwt/qwt_legend_label.cpp b/qwt/qwt_legend_label.cpp
new file mode 100644
index 0000000..19a7eb9
--- /dev/null
+++ b/qwt/qwt_legend_label.cpp
@@ -0,0 +1,421 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_legend_label.h"
+#include "qwt_legend_data.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include "qwt_symbol.h"
+#include "qwt_graphic.h"
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+#include <qpen.h>
+#include <qevent.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static const int ButtonFrame = 2;
+static const int Margin = 2;
+
+static QSize buttonShift( const QwtLegendLabel *w )
+{
+    QStyleOption option;
+    option.init( w );
+
+    const int ph = w->style()->pixelMetric(
+        QStyle::PM_ButtonShiftHorizontal, &option, w );
+    const int pv = w->style()->pixelMetric(
+        QStyle::PM_ButtonShiftVertical, &option, w );
+    return QSize( ph, pv );
+}
+
+class QwtLegendLabel::PrivateData
+{
+public:
+    PrivateData():
+        itemMode( QwtLegendData::ReadOnly ),
+        isDown( false ),
+        spacing( Margin )
+    {
+    }
+
+    QwtLegendData::Mode itemMode;
+    QwtLegendData legendData;
+    bool isDown;
+
+    QPixmap icon;
+
+    int spacing;
+};
+
+/*!
+  Set the attributes of the legend label
+
+  \param legendData Attributes of the label
+  \sa data()
+ */
+void QwtLegendLabel::setData( const QwtLegendData &legendData )
+{
+    d_data->legendData = legendData;
+
+    const bool doUpdate = updatesEnabled();
+    setUpdatesEnabled( false );
+
+    setText( legendData.title() );
+    setIcon( legendData.icon().toPixmap() );
+
+    if ( legendData.hasRole( QwtLegendData::ModeRole ) )
+        setItemMode( legendData.mode() );
+
+    if ( doUpdate )
+    {
+        setUpdatesEnabled( true );
+        update();
+    }
+}
+
+/*!
+  \return Attributes of the label
+  \sa setData(), QwtPlotItem::legendData()
+ */
+const QwtLegendData &QwtLegendLabel::data() const
+{
+    return d_data->legendData;
+}
+
+/*!
+  \param parent Parent widget
+*/
+QwtLegendLabel::QwtLegendLabel( QWidget *parent ):
+    QwtTextLabel( parent )
+{
+    d_data = new PrivateData;
+    setMargin( Margin );
+    setIndent( Margin );
+}
+
+//! Destructor
+QwtLegendLabel::~QwtLegendLabel()
+{
+    delete d_data;
+    d_data = NULL;
+}
+
+/*!
+   Set the text to the legend item
+
+   \param text Text label
+    \sa QwtTextLabel::text()
+*/
+void QwtLegendLabel::setText( const QwtText &text )
+{
+    const int flags = Qt::AlignLeft | Qt::AlignVCenter
+        | Qt::TextExpandTabs | Qt::TextWordWrap;
+
+    QwtText txt = text;
+    txt.setRenderFlags( flags );
+
+    QwtTextLabel::setText( txt );
+}
+
+/*!
+   Set the item mode
+   The default is QwtLegendData::ReadOnly
+
+   \param mode Item mode
+   \sa itemMode()
+*/
+void QwtLegendLabel::setItemMode( QwtLegendData::Mode mode )
+{
+    if ( mode != d_data->itemMode )
+    {
+        d_data->itemMode = mode;
+        d_data->isDown = false;
+
+        setFocusPolicy( ( mode != QwtLegendData::ReadOnly ) 
+            ? Qt::TabFocus : Qt::NoFocus );
+        setMargin( ButtonFrame + Margin );
+
+        updateGeometry();
+    }
+}
+
+/*!
+   \return Item mode
+   \sa setItemMode()
+*/
+QwtLegendData::Mode QwtLegendLabel::itemMode() const
+{
+    return d_data->itemMode;
+}
+
+/*!
+  Assign the icon
+
+  \param icon Pixmap representing a plot item
+
+  \sa icon(), QwtPlotItem::legendIcon()
+*/
+void QwtLegendLabel::setIcon( const QPixmap &icon )
+{
+    d_data->icon = icon;
+
+    int indent = margin() + d_data->spacing;
+    if ( icon.width() > 0 )
+        indent += icon.width() + d_data->spacing;
+
+    setIndent( indent );
+}
+
+/*!
+  \return Pixmap representing a plot item
+  \sa setIcon()
+*/
+QPixmap QwtLegendLabel::icon() const
+{
+    return d_data->icon;
+}
+
+/*!
+   \brief Change the spacing between icon and text
+
+   \param spacing Spacing
+   \sa spacing(), QwtTextLabel::margin()
+*/
+void QwtLegendLabel::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+
+        int indent = margin() + d_data->spacing;
+        if ( d_data->icon.width() > 0 )
+            indent += d_data->icon.width() + d_data->spacing;
+
+        setIndent( indent );
+    }
+}
+
+/*!
+   \return Spacing between icon and text
+   \sa setSpacing(), QwtTextLabel::margin()
+*/
+int QwtLegendLabel::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+    Check/Uncheck a the item
+
+    \param on check/uncheck
+    \sa setItemMode()
+*/
+void QwtLegendLabel::setChecked( bool on )
+{
+    if ( d_data->itemMode == QwtLegendData::Checkable )
+    {
+        const bool isBlocked = signalsBlocked();
+        blockSignals( true );
+
+        setDown( on );
+
+        blockSignals( isBlocked );
+    }
+}
+
+//! Return true, if the item is checked
+bool QwtLegendLabel::isChecked() const
+{
+    return d_data->itemMode == QwtLegendData::Checkable && isDown();
+}
+
+//! Set the item being down
+void QwtLegendLabel::setDown( bool down )
+{
+    if ( down == d_data->isDown )
+        return;
+
+    d_data->isDown = down;
+    update();
+
+    if ( d_data->itemMode == QwtLegendData::Clickable )
+    {
+        if ( d_data->isDown )
+            Q_EMIT pressed();
+        else
+        {
+            Q_EMIT released();
+            Q_EMIT clicked();
+        }
+    }
+
+    if ( d_data->itemMode == QwtLegendData::Checkable )
+        Q_EMIT checked( d_data->isDown );
+}
+
+//! Return true, if the item is down
+bool QwtLegendLabel::isDown() const
+{
+    return d_data->isDown;
+}
+
+//! Return a size hint
+QSize QwtLegendLabel::sizeHint() const
+{
+    QSize sz = QwtTextLabel::sizeHint();
+    sz.setHeight( qMax( sz.height(), d_data->icon.height() + 4 ) );
+
+    if ( d_data->itemMode != QwtLegendData::ReadOnly )
+    {
+        sz += buttonShift( this );
+        sz = sz.expandedTo( QApplication::globalStrut() );
+    }
+
+    return sz;
+}
+
+//! Paint event
+void QwtLegendLabel::paintEvent( QPaintEvent *e )
+{
+    const QRect cr = contentsRect();
+
+    QPainter painter( this );
+    painter.setClipRegion( e->region() );
+
+    if ( d_data->isDown )
+    {
+        qDrawWinButton( &painter, 0, 0, width(), height(),
+            palette(), true );
+    }
+
+    painter.save();
+
+    if ( d_data->isDown )
+    {
+        const QSize shiftSize = buttonShift( this );
+        painter.translate( shiftSize.width(), shiftSize.height() );
+    }
+
+    painter.setClipRect( cr );
+
+    drawContents( &painter );
+
+    if ( !d_data->icon.isNull() )
+    {
+        QRect iconRect = cr;
+        iconRect.setX( iconRect.x() + margin() );
+        if ( d_data->itemMode != QwtLegendData::ReadOnly )
+            iconRect.setX( iconRect.x() + ButtonFrame );
+
+        iconRect.setSize( d_data->icon.size() );
+        iconRect.moveCenter( QPoint( iconRect.center().x(), cr.center().y() ) );
+
+        painter.drawPixmap( iconRect, d_data->icon );
+    }
+
+    painter.restore();
+}
+
+//! Handle mouse press events
+void QwtLegendLabel::mousePressEvent( QMouseEvent *e )
+{
+    if ( e->button() == Qt::LeftButton )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                setDown( true );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                setDown( !isDown() );
+                return;
+            }
+            default:;
+        }
+    }
+    QwtTextLabel::mousePressEvent( e );
+}
+
+//! Handle mouse release events
+void QwtLegendLabel::mouseReleaseEvent( QMouseEvent *e )
+{
+    if ( e->button() == Qt::LeftButton )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                setDown( false );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                return; // do nothing, but accept
+            }
+            default:;
+        }
+    }
+    QwtTextLabel::mouseReleaseEvent( e );
+}
+
+//! Handle key press events
+void QwtLegendLabel::keyPressEvent( QKeyEvent *e )
+{
+    if ( e->key() == Qt::Key_Space )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( true );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( !isDown() );
+                return;
+            }
+            default:;
+        }
+    }
+
+    QwtTextLabel::keyPressEvent( e );
+}
+
+//! Handle key release events
+void QwtLegendLabel::keyReleaseEvent( QKeyEvent *e )
+{
+    if ( e->key() == Qt::Key_Space )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( false );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                return; // do nothing, but accept
+            }
+            default:;
+        }
+    }
+
+    QwtTextLabel::keyReleaseEvent( e );
+}
diff --git a/qwt/qwt_legend_label.h b/qwt/qwt_legend_label.h
new file mode 100644
index 0000000..f0a1584
--- /dev/null
+++ b/qwt/qwt_legend_label.h
@@ -0,0 +1,80 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_LABEL_H
+#define QWT_LEGEND_LABEL_H
+
+#include "qwt_global.h"
+#include "qwt_legend_data.h"
+#include "qwt_text.h"
+#include "qwt_text_label.h"
+#include <qpixmap.h>
+
+class QwtLegendData;
+
+/*!
+  \brief A widget representing something on a QwtLegend.
+*/
+class QWT_EXPORT QwtLegendLabel: public QwtTextLabel
+{
+    Q_OBJECT
+public:
+    explicit QwtLegendLabel( QWidget *parent = 0 );
+    virtual ~QwtLegendLabel();
+
+    void setData( const QwtLegendData & );
+    const QwtLegendData &data() const;
+
+    void setItemMode( QwtLegendData::Mode );
+    QwtLegendData::Mode itemMode() const;
+
+    void setSpacing( int spacing );
+    int spacing() const;
+
+    virtual void setText( const QwtText & );
+
+    void setIcon( const QPixmap & );
+    QPixmap icon() const;
+
+    virtual QSize sizeHint() const;
+
+    bool isChecked() const;
+
+public Q_SLOTS:
+    void setChecked( bool on );
+
+Q_SIGNALS:
+    //! Signal, when the legend item has been clicked
+    void clicked();
+
+    //! Signal, when the legend item has been pressed
+    void pressed();
+
+    //! Signal, when the legend item has been released
+    void released();
+
+    //! Signal, when the legend item has been toggled
+    void checked( bool );
+
+protected:
+    void setDown( bool );
+    bool isDown() const;
+
+    virtual void paintEvent( QPaintEvent * );
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void keyReleaseEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/qwt/qwt_magnifier.cpp b/qwt/qwt_magnifier.cpp
new file mode 100644
index 0000000..55e7bb5
--- /dev/null
+++ b/qwt/qwt_magnifier.cpp
@@ -0,0 +1,492 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_magnifier.h"
+#include "qwt_math.h"
+#include <qevent.h>
+#include <qwidget.h>
+
+class QwtMagnifier::PrivateData
+{
+public:
+    PrivateData():
+        isEnabled( false ),
+        wheelFactor( 0.9 ),
+        wheelModifiers( Qt::NoModifier ),
+        mouseFactor( 0.95 ),
+        mouseButton( Qt::RightButton ),
+        mouseButtonModifiers( Qt::NoModifier ),
+        keyFactor( 0.9 ),
+        zoomInKey( Qt::Key_Plus ),
+        zoomInKeyModifiers( Qt::NoModifier ),
+        zoomOutKey( Qt::Key_Minus ),
+        zoomOutKeyModifiers( Qt::NoModifier ),
+        mousePressed( false )
+    {
+    }
+
+    bool isEnabled;
+
+    double wheelFactor;
+    Qt::KeyboardModifiers wheelModifiers;
+
+    double mouseFactor;
+
+    Qt::MouseButton mouseButton;
+    Qt::KeyboardModifiers mouseButtonModifiers;
+
+    double keyFactor;
+
+    int zoomInKey;
+    Qt::KeyboardModifiers zoomInKeyModifiers;
+
+    int zoomOutKey;
+    Qt::KeyboardModifiers  zoomOutKeyModifiers;
+
+    bool mousePressed;
+    bool hasMouseTracking;
+    QPoint mousePos;
+};
+
+/*!
+   Constructor
+   \param parent Widget to be magnified
+*/
+QwtMagnifier::QwtMagnifier( QWidget *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData();
+    setEnabled( true );
+}
+
+//! Destructor
+QwtMagnifier::~QwtMagnifier()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/disable the magnifier
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtMagnifier::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QObject *o = parent();
+        if ( o )
+        {
+            if ( d_data->isEnabled )
+                o->installEventFilter( this );
+            else
+                o->removeEventFilter( this );
+        }
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled(), eventFilter()
+*/
+bool QwtMagnifier::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+   \brief Change the wheel factor
+
+   The wheel factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each step of the wheel.
+
+   Use values > 1 for magnification (i.e. 2.0) and values < 1 for
+   scaling down (i.e. 1/2.0 = 0.5). You can use this feature for
+   inverting the direction of the wheel.
+
+   The default value is 0.9.
+
+   \param factor Wheel factor
+   \sa wheelFactor(), setWheelButtonState(),
+       setMouseFactor(), setKeyFactor()
+*/
+void QwtMagnifier::setWheelFactor( double factor )
+{
+    d_data->wheelFactor = factor;
+}
+
+/*!
+   \return Wheel factor
+   \sa setWheelFactor()
+*/
+double QwtMagnifier::wheelFactor() const
+{
+    return d_data->wheelFactor;
+}
+
+/*!
+   Assign keyboard modifiers for zooming in/out using the wheel.
+   The default modifiers are Qt::NoModifiers.
+
+   \param modifiers Keyboard modifiers
+   \sa wheelModifiers()
+*/
+void QwtMagnifier::setWheelModifiers( Qt::KeyboardModifiers modifiers )
+{
+    d_data->wheelModifiers = modifiers;
+}
+
+/*!
+   \return Wheel modifiers
+   \sa setWheelModifiers()
+*/
+Qt::KeyboardModifiers QwtMagnifier::wheelModifiers() const
+{
+    return d_data->wheelModifiers;
+}
+
+/*!
+   \brief Change the mouse factor
+
+   The mouse factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each vertical mouse movement.
+   The default value is 0.95.
+
+   \param factor Wheel factor
+   \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor()
+*/
+void QwtMagnifier::setMouseFactor( double factor )
+{
+    d_data->mouseFactor = factor;
+}
+
+/*!
+   \return Mouse factor
+   \sa setMouseFactor()
+*/
+double QwtMagnifier::mouseFactor() const
+{
+    return d_data->mouseFactor;
+}
+
+/*!
+   Assign the mouse button, that is used for zooming in/out.
+   The default value is Qt::RightButton.
+
+   \param button Button
+   \param modifiers Keyboard modifiers
+
+   \sa getMouseButton()
+*/
+void QwtMagnifier::setMouseButton( 
+    Qt::MouseButton button, Qt::KeyboardModifiers modifiers )
+{
+    d_data->mouseButton = button;
+    d_data->mouseButtonModifiers = modifiers;
+}
+
+//! \sa setMouseButton()
+void QwtMagnifier::getMouseButton(
+    Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const
+{
+    button = d_data->mouseButton;
+    modifiers = d_data->mouseButtonModifiers;
+}
+
+/*!
+   \brief Change the key factor
+
+   The key factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each key press of
+   the zoom in/out keys. The default value is 0.9.
+
+   \param factor Key factor
+   \sa keyFactor(), setZoomInKey(), setZoomOutKey(),
+       setWheelFactor, setMouseFactor()
+*/
+void QwtMagnifier::setKeyFactor( double factor )
+{
+    d_data->keyFactor = factor;
+}
+
+/*!
+   \return Key factor
+   \sa setKeyFactor()
+*/
+double QwtMagnifier::keyFactor() const
+{
+    return d_data->keyFactor;
+}
+
+/*!
+   Assign the key, that is used for zooming in.
+   The default combination is Qt::Key_Plus + Qt::NoModifier.
+
+   \param key
+   \param modifiers
+   \sa getZoomInKey(), setZoomOutKey()
+*/
+void QwtMagnifier::setZoomInKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->zoomInKey = key;
+    d_data->zoomInKeyModifiers = modifiers;
+}
+
+/*! 
+   \brief Retrieve the settings of the zoom in key
+
+   \param key Key code, see Qt::Key
+   \param modifiers Keyboard modifiers
+
+   \sa setZoomInKey()
+*/
+void QwtMagnifier::getZoomInKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->zoomInKey;
+    modifiers = d_data->zoomInKeyModifiers;
+}
+
+/*!
+   Assign the key, that is used for zooming out.
+   The default combination is Qt::Key_Minus + Qt::NoModifier.
+
+   \param key
+   \param modifiers
+   \sa getZoomOutKey(), setZoomOutKey()
+*/
+void QwtMagnifier::setZoomOutKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->zoomOutKey = key;
+    d_data->zoomOutKeyModifiers = modifiers;
+}
+
+/*! 
+   \brief Retrieve the settings of the zoom out key
+
+   \param key Key code, see Qt::Key
+   \param modifiers Keyboard modifiers
+
+   \sa setZoomOutKey()
+*/
+void QwtMagnifier::getZoomOutKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->zoomOutKey;
+    modifiers = d_data->zoomOutKeyModifiers;
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true, the mouse events of the
+  observed widget are filtered.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Forwarded to QObject::eventFilter()
+
+  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent()
+      widgetKeyReleaseEvent()
+*/
+bool QwtMagnifier::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == parent() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::MouseButtonPress:
+            {
+                widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseMove:
+            {
+                widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonRelease:
+            {
+                widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::Wheel:
+            {
+                widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyPress:
+            {
+                widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyRelease:
+            {
+                widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            default:;
+        }
+    }
+    return QObject::eventFilter( object, event );
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent()
+*/
+void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    if ( parentWidget() == NULL )
+        return;
+
+    if ( ( mouseEvent->button() != d_data->mouseButton ) ||
+        ( mouseEvent->modifiers() != d_data->mouseButtonModifiers ) )
+    {
+        return;
+    }
+
+    d_data->hasMouseTracking = parentWidget()->hasMouseTracking();
+
+    parentWidget()->setMouseTracking( true );
+    d_data->mousePos = mouseEvent->pos();
+    d_data->mousePressed = true;
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(),
+*/
+void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    Q_UNUSED( mouseEvent );
+
+    if ( d_data->mousePressed && parentWidget() )
+    {
+        d_data->mousePressed = false;
+        parentWidget()->setMouseTracking( d_data->hasMouseTracking );
+    }
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+*/
+void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( !d_data->mousePressed )
+        return;
+
+    const int dy = mouseEvent->pos().y() - d_data->mousePos.y();
+    if ( dy != 0 )
+    {
+        double f = d_data->mouseFactor;
+        if ( dy < 0 )
+            f = 1 / f;
+
+        rescale( f );
+    }
+
+    d_data->mousePos = mouseEvent->pos();
+}
+
+/*!
+  Handle a wheel event for the observed widget.
+
+  \param wheelEvent Wheel event
+  \sa eventFilter()
+*/
+void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent )
+{
+    if ( wheelEvent->modifiers() != d_data->wheelModifiers )
+    {
+        return;
+    }
+
+    if ( d_data->wheelFactor != 0.0 )
+    {
+        /*
+            A positive delta indicates that the wheel was
+            rotated forwards away from the user; a negative
+            value indicates that the wheel was rotated
+            backwards toward the user.
+            Most mouse types work in steps of 15 degrees,
+            in which case the delta value is a multiple
+            of 120 (== 15 * 8).
+         */
+        double f = qPow( d_data->wheelFactor, 
+            qAbs( wheelEvent->delta() / 120.0 ) );
+
+        if ( wheelEvent->delta() > 0 )
+            f = 1 / f;
+
+        rescale( f );
+    }
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    if ( keyEvent->key() == d_data->zoomInKey &&
+        keyEvent->modifiers() == d_data->zoomInKeyModifiers )
+    {
+        rescale( d_data->keyFactor );
+    }
+    else if ( keyEvent->key() == d_data->zoomOutKey &&
+        keyEvent->modifiers() == d_data->zoomOutKeyModifiers )
+    {
+        rescale( 1.0 / d_data->keyFactor );
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    Q_UNUSED( keyEvent );
+}
+
+//! \return Parent widget, where the rescaling happens
+QWidget *QwtMagnifier::parentWidget()
+{
+    return qobject_cast<QWidget *>( parent() );
+}
+
+//! \return Parent widget, where the rescaling happens
+const QWidget *QwtMagnifier::parentWidget() const
+{
+    return qobject_cast<const QWidget *>( parent() );
+}
+
diff --git a/qwt/qwt_magnifier.h b/qwt/qwt_magnifier.h
new file mode 100644
index 0000000..48e8ed8
--- /dev/null
+++ b/qwt/qwt_magnifier.h
@@ -0,0 +1,86 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MAGNIFIER_H
+#define QWT_MAGNIFIER_H 1
+
+#include "qwt_global.h"
+#include <qobject.h>
+
+class QWidget;
+class QMouseEvent;
+class QWheelEvent;
+class QKeyEvent;
+
+/*!
+  \brief QwtMagnifier provides zooming, by magnifying in steps.
+
+  Using QwtMagnifier a plot can be zoomed in/out in steps using
+  keys, the mouse wheel or moving a mouse button in vertical direction.
+*/
+class QWT_EXPORT QwtMagnifier: public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit QwtMagnifier( QWidget * );
+    virtual ~QwtMagnifier();
+
+    QWidget *parentWidget();
+    const QWidget *parentWidget() const;
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    // mouse
+    void setMouseFactor( double );
+    double mouseFactor() const;
+
+    void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getMouseButton( Qt::MouseButton &, Qt::KeyboardModifiers & ) const;
+
+    // mouse wheel
+    void setWheelFactor( double );
+    double wheelFactor() const;
+
+    void setWheelModifiers( Qt::KeyboardModifiers );
+    Qt::KeyboardModifiers wheelModifiers() const;
+
+    // keyboard
+    void setKeyFactor( double );
+    double keyFactor() const;
+
+    void setZoomInKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getZoomInKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    void setZoomOutKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getZoomOutKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+protected:
+    /*!
+       Rescale the parent widget
+       \param factor Scale factor
+     */
+    virtual void rescale( double factor ) = 0;
+
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetWheelEvent( QWheelEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_math.cpp b/qwt/qwt_math.cpp
new file mode 100644
index 0000000..9e898c1
--- /dev/null
+++ b/qwt/qwt_math.cpp
@@ -0,0 +1,74 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_math.h"
+
+/*!
+  \brief Find the smallest value in an array
+  \param array Pointer to an array
+  \param size Array size
+*/
+double qwtGetMin( const double *array, int size )
+{
+    if ( size <= 0 )
+        return 0.0;
+
+    double rv = array[0];
+    for ( int i = 1; i < size; i++ )
+        rv = qMin( rv, array[i] );
+
+    return rv;
+}
+
+
+/*!
+  \brief Find the largest value in an array
+  \param array Pointer to an array
+  \param size Array size
+*/
+double qwtGetMax( const double *array, int size )
+{
+    if ( size <= 0 )
+        return 0.0;
+
+    double rv = array[0];
+    for ( int i = 1; i < size; i++ )
+        rv = qMax( rv, array[i] );
+
+    return rv;
+}
+
+/*!
+  \brief Normalize an angle to be int the range [0.0, 2 * PI[
+  \param radians Angle in radians
+  \return Normalized angle in radians
+*/
+double qwtNormalizeRadians( double radians )
+{
+    double a = ::fmod( radians, 2.0 * M_PI );
+    if ( a < 0.0 )
+        a += 2.0 * M_PI;
+
+    return a;
+
+}
+
+/*!
+  \brief Normalize an angle to be int the range [0.0, 360.0[
+  \param radians Angle in degrees
+  \return Normalized angle in degrees
+*/
+double qwtNormalizeDegrees( double degrees )
+{
+    double a = ::fmod( degrees, 360.0 );
+    if ( a < 0.0 )
+        a += 360.0;
+
+    return a;
+}
diff --git a/qwt/qwt_math.h b/qwt/qwt_math.h
new file mode 100644
index 0000000..23ad205
--- /dev/null
+++ b/qwt/qwt_math.h
@@ -0,0 +1,149 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MATH_H
+#define QWT_MATH_H
+
+#include "qwt_global.h"
+
+#if defined(_MSC_VER)
+/*
+  Microsoft says:
+
+  Define _USE_MATH_DEFINES before including math.h to expose these macro
+  definitions for common math constants.  These are placed under an #ifdef
+  since these commonly-defined names are not part of the C/C++ standards.
+*/
+#define _USE_MATH_DEFINES 1
+#endif
+
+#include <qmath.h>
+#include "qwt_global.h"
+
+#ifndef M_PI_2
+// For Qt <= 4.8.4 M_PI_2 is not known by MinGW-w64 
+// when compiling with -std=c++11
+#define M_PI_2 (1.57079632679489661923)
+#endif
+
+#ifndef LOG_MIN
+//! Minimum value for logarithmic scales
+#define LOG_MIN 1.0e-100
+#endif
+
+#ifndef LOG_MAX
+//! Maximum value for logarithmic scales
+#define LOG_MAX 1.0e100
+#endif
+
+QWT_EXPORT double qwtGetMin( const double *array, int size );
+QWT_EXPORT double qwtGetMax( const double *array, int size );
+
+QWT_EXPORT double qwtNormalizeRadians( double radians );
+QWT_EXPORT double qwtNormalizeDegrees( double degrees );
+
+/*!
+  \brief Compare 2 values, relative to an interval
+
+  Values are "equal", when :
+  \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$
+
+  \param value1 First value to compare
+  \param value2 Second value to compare
+  \param intervalSize interval size
+
+  \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2
+*/
+inline int qwtFuzzyCompare( double value1, double value2, double intervalSize )
+{
+    const double eps = qAbs( 1.0e-6 * intervalSize );
+
+    if ( value2 - value1 > eps )
+        return -1;
+
+    if ( value1 - value2 > eps )
+        return 1;
+
+    return 0;
+}
+
+
+inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 )
+{
+    return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 );
+}
+
+inline bool qwtFuzzyLessOrEqual( double d1, double d2 )
+{
+    return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 );
+}
+
+//! Return the sign
+inline int qwtSign( double x )
+{
+    if ( x > 0.0 )
+        return 1;
+    else if ( x < 0.0 )
+        return ( -1 );
+    else
+        return 0;
+}
+
+//! Return the square of a number
+inline double qwtSqr( double x )
+{
+    return x * x;
+}
+
+//! Approximation of arc tangent ( error below 0,005 radians )
+inline double qwtFastAtan( double x )
+{
+    if ( x < -1.0 )
+        return -M_PI_2 - x / ( x * x + 0.28 );
+
+    if ( x > 1.0 )
+        return M_PI_2 - x / ( x * x + 0.28 );
+
+    return x / ( 1.0 + x * x * 0.28 );
+}
+
+//! Approximation of arc tangent ( error below 0,005 radians )
+inline double qwtFastAtan2( double y, double x )
+{
+    if ( x > 0 )
+        return qwtFastAtan( y / x );
+
+    if ( x < 0 )
+    {
+        const double d = qwtFastAtan( y / x );
+        return ( y >= 0 ) ? d + M_PI : d - M_PI;
+    }
+
+    if ( y < 0.0 )
+        return -M_PI_2;
+
+    if ( y > 0.0 )
+        return M_PI_2;
+
+    return 0.0;
+}
+
+// Translate degrees into radians
+inline double qwtRadians( double degrees )
+{
+    return degrees * M_PI / 180.0;
+}
+
+// Translate radians into degrees
+inline double qwtDegrees( double degrees )
+{
+    return degrees * 180.0 / M_PI;
+}
+
+#endif
diff --git a/qwt/qwt_matrix_raster_data.cpp b/qwt/qwt_matrix_raster_data.cpp
new file mode 100644
index 0000000..69355ad
--- /dev/null
+++ b/qwt/qwt_matrix_raster_data.cpp
@@ -0,0 +1,298 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_matrix_raster_data.h"
+#include <qnumeric.h>
+#include <qmath.h>
+
+class QwtMatrixRasterData::PrivateData
+{
+public:
+    PrivateData():
+        resampleMode(QwtMatrixRasterData::NearestNeighbour),
+        numColumns(0)
+    {
+    }
+
+    inline double value(int row, int col) const
+    {
+        return values.data()[ row * numColumns + col ];
+    }
+
+    QwtMatrixRasterData::ResampleMode resampleMode;
+
+    QVector<double> values;
+    int numColumns;
+    int numRows;
+
+    double dx;
+    double dy;
+};
+
+//! Constructor
+QwtMatrixRasterData::QwtMatrixRasterData()
+{
+    d_data = new PrivateData();
+    update();
+}
+
+//! Destructor
+QwtMatrixRasterData::~QwtMatrixRasterData()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Set the resampling algorithm
+
+   \param mode Resampling mode
+   \sa resampleMode(), value()
+*/
+void QwtMatrixRasterData::setResampleMode( ResampleMode mode )
+{
+    d_data->resampleMode = mode;
+}
+
+/*!
+   \return resampling algorithm
+   \sa setResampleMode(), value()
+*/
+QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const
+{
+    return d_data->resampleMode;
+}
+
+/*!
+   \brief Assign the bounding interval for an axis
+
+   Setting the bounding intervals for the X/Y axis is mandatory
+   to define the positions for the values of the value matrix.
+   The interval in Z direction defines the possible range for
+   the values in the matrix, what is f.e used by QwtPlotSpectrogram
+   to map values to colors. The Z-interval might be the bounding
+   interval of the values in the matrix, but usually it isn't.
+   ( f.e a interval of 0.0-100.0 for values in percentage )
+
+   \param axis X, Y or Z axis
+   \param interval Interval
+   
+   \sa QwtRasterData::interval(), setValueMatrix()
+*/
+void QwtMatrixRasterData::setInterval( 
+    Qt::Axis axis, const QwtInterval &interval )
+{
+    QwtRasterData::setInterval( axis, interval );
+    update();
+}
+
+/*!
+   \brief Assign a value matrix
+
+   The positions of the values are calculated by dividing
+   the bounding rectangle of the X/Y intervals into equidistant
+   rectangles ( pixels ). Each value corresponds to the center of 
+   a pixel.
+
+   \param values Vector of values
+   \param numColumns Number of columns
+
+   \sa valueMatrix(), numColumns(), numRows(), setInterval()()
+*/
+void QwtMatrixRasterData::setValueMatrix( 
+    const QVector<double> &values, int numColumns )
+{
+    d_data->values = values;
+    d_data->numColumns = qMax( numColumns, 0 );
+    update();
+}
+
+/*!
+   \return Value matrix
+   \sa setValueMatrix(), numColumns(), numRows(), setInterval()
+*/
+const QVector<double> QwtMatrixRasterData::valueMatrix() const
+{
+    return d_data->values;
+}
+
+/*!
+  \brief Change a single value in the matrix
+
+  \param row Row index
+  \param col Column index
+  \param value New value
+
+  \sa value(), setValueMatrix()
+*/
+void QwtMatrixRasterData::setValue( int row, int col, double value )
+{
+    if ( row >= 0 && row < d_data->numRows &&
+        col >= 0 && col < d_data->numColumns )
+    {
+        const int index = row * d_data->numColumns + col;
+        d_data->values.data()[ index ] = value;
+    }
+}
+
+/*!
+   \return Number of columns of the value matrix
+   \sa valueMatrix(), numRows(), setValueMatrix()
+*/
+int QwtMatrixRasterData::numColumns() const
+{
+    return d_data->numColumns;
+}
+
+/*!
+   \return Number of rows of the value matrix
+   \sa valueMatrix(), numColumns(), setValueMatrix()
+*/
+int QwtMatrixRasterData::numRows() const
+{
+    return d_data->numRows;
+}
+
+/*!
+   \brief Calculate the pixel hint
+
+   pixelHint() returns the geometry of a pixel, that can be used 
+   to calculate the resolution and alignment of the plot item, that is
+   representing the data. 
+
+   - NearestNeighbour\n
+     pixelHint() returns the surrounding pixel of the top left value 
+     in the matrix.
+
+   - BilinearInterpolation\n
+     Returns an empty rectangle recommending
+     to render in target device ( f.e. screen ) resolution. 
+
+   \param area Requested area, ignored
+   \return Calculated hint
+
+   \sa ResampleMode, setMatrix(), setInterval()
+*/
+QRectF QwtMatrixRasterData::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area )
+
+    QRectF rect;
+    if ( d_data->resampleMode == NearestNeighbour )
+    {
+        const QwtInterval intervalX = interval( Qt::XAxis );
+        const QwtInterval intervalY = interval( Qt::YAxis );
+        if ( intervalX.isValid() && intervalY.isValid() )
+        {
+            rect = QRectF( intervalX.minValue(), intervalY.minValue(),
+                d_data->dx, d_data->dy );
+        }
+    }
+
+    return rect;
+}
+
+/*!
+   \return the value at a raster position
+
+   \param x X value in plot coordinates
+   \param y Y value in plot coordinates
+
+   \sa ResampleMode
+*/
+double QwtMatrixRasterData::value( double x, double y ) const
+{
+    const QwtInterval xInterval = interval( Qt::XAxis );
+    const QwtInterval yInterval = interval( Qt::YAxis );
+
+    if ( !( xInterval.contains(x) && yInterval.contains(y) ) )
+        return qQNaN();
+
+    double value;
+
+    switch( d_data->resampleMode )
+    {
+        case BilinearInterpolation:
+        {
+            int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1;
+            int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1;
+            int col2 = col1 + 1;
+            int row2 = row1 + 1;
+
+            if ( col1 < 0 )
+                col1 = col2;
+            else if ( col2 >= static_cast<int>( d_data->numColumns ) )
+                col2 = col1;
+
+            if ( row1 < 0 )
+                row1 = row2;
+            else if ( row2 >= static_cast<int>( d_data->numRows ) )
+                row2 = row1;
+
+            const double v11 = d_data->value( row1, col1 );
+            const double v21 = d_data->value( row1, col2 );
+            const double v12 = d_data->value( row2, col1 );
+            const double v22 = d_data->value( row2, col2 );
+
+            const double x2 = xInterval.minValue() + 
+                ( col2 + 0.5 ) * d_data->dx;
+            const double y2 = yInterval.minValue() + 
+                ( row2 + 0.5 ) * d_data->dy;
+                
+            const double rx = ( x2 - x ) / d_data->dx;
+            const double ry = ( y2 - y ) / d_data->dy;
+
+            const double vr1 = rx * v11 + ( 1.0 - rx ) * v21;
+            const double vr2 = rx * v12 + ( 1.0 - rx ) * v22;
+
+            value = ry * vr1 + ( 1.0 - ry ) * vr2;
+
+            break;
+        }
+        case NearestNeighbour:
+        default:
+        {
+            int row = int( (y - yInterval.minValue() ) / d_data->dy );
+            int col = int( (x - xInterval.minValue() ) / d_data->dx );
+
+            // In case of intervals, where the maximum is included
+            // we get out of bound for row/col, when the value for the
+            // maximum is requested. Instead we return the value
+            // from the last row/col
+
+            if ( row >= d_data->numRows )
+                row = d_data->numRows - 1;
+
+            if ( col >= d_data->numColumns )
+                col = d_data->numColumns - 1;
+
+            value = d_data->value( row, col );
+        }
+    }
+
+    return value;
+}
+
+void QwtMatrixRasterData::update()
+{
+    d_data->numRows = 0;
+    d_data->dx = 0.0;
+    d_data->dy = 0.0;
+
+    if ( d_data->numColumns > 0 )
+    {
+        d_data->numRows = d_data->values.size() / d_data->numColumns;
+
+        const QwtInterval xInterval = interval( Qt::XAxis );
+        const QwtInterval yInterval = interval( Qt::YAxis );
+        if ( xInterval.isValid() )
+            d_data->dx = xInterval.width() / d_data->numColumns;
+        if ( yInterval.isValid() )
+            d_data->dy = yInterval.width() / d_data->numRows;
+    }
+}
diff --git a/qwt/qwt_matrix_raster_data.h b/qwt/qwt_matrix_raster_data.h
new file mode 100644
index 0000000..0b107c9
--- /dev/null
+++ b/qwt/qwt_matrix_raster_data.h
@@ -0,0 +1,74 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MATRIX_RASTER_DATA_H
+#define QWT_MATRIX_RASTER_DATA_H 1
+
+#include "qwt_global.h"
+#include "qwt_raster_data.h"
+#include <qvector.h>
+
+/*!
+  \brief A class representing a matrix of values as raster data
+
+  QwtMatrixRasterData implements an interface for a matrix of
+  equidistant values, that can be used by a QwtPlotRasterItem. 
+  It implements a couple of resampling algorithms, to provide
+  values for positions, that or not on the value matrix.
+*/
+class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData
+{
+public:
+    /*!
+      \brief Resampling algorithm
+      The default setting is NearestNeighbour;
+    */
+    enum ResampleMode
+    {
+        /*!
+          Return the value from the matrix, that is nearest to the
+          the requested position.
+         */
+        NearestNeighbour,
+
+        /*!
+          Interpolate the value from the distances and values of the 
+          4 surrounding values in the matrix,
+         */
+        BilinearInterpolation
+    };
+
+    QwtMatrixRasterData();
+    virtual ~QwtMatrixRasterData();
+
+    void setResampleMode(ResampleMode mode);
+    ResampleMode resampleMode() const;
+
+    virtual void setInterval( Qt::Axis, const QwtInterval & );
+
+    void setValueMatrix( const QVector<double> &values, int numColumns );
+    const QVector<double> valueMatrix() const;
+
+    void setValue( int row, int col, double value );
+
+    int numColumns() const;
+    int numRows() const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual double value( double x, double y ) const;
+
+private:
+    void update();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_null_paintdevice.cpp b/qwt/qwt_null_paintdevice.cpp
new file mode 100644
index 0000000..db1611d
--- /dev/null
+++ b/qwt/qwt_null_paintdevice.cpp
@@ -0,0 +1,593 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_null_paintdevice.h"
+#include <qpaintengine.h>
+#include <qpixmap.h>
+
+class QwtNullPaintDevice::PrivateData
+{
+public:
+    PrivateData():
+        mode( QwtNullPaintDevice::NormalMode )
+    {
+    }
+
+    QwtNullPaintDevice::Mode mode;
+};
+
+class QwtNullPaintDevice::PaintEngine: public QPaintEngine
+{
+public:
+    PaintEngine();
+
+    virtual bool begin( QPaintDevice * );
+    virtual bool end();
+
+    virtual Type type () const;
+    virtual void updateState(const QPaintEngineState &);
+
+    virtual void drawRects(const QRect *, int );
+    virtual void drawRects(const QRectF *, int );
+
+    virtual void drawLines(const QLine *, int );
+    virtual void drawLines(const QLineF *, int );
+
+    virtual void drawEllipse(const QRectF &);
+    virtual void drawEllipse(const QRect &);
+
+    virtual void drawPath(const QPainterPath &);
+
+    virtual void drawPoints(const QPointF *, int );
+    virtual void drawPoints(const QPoint *, int );
+
+    virtual void drawPolygon(const QPointF *, int , PolygonDrawMode );
+    virtual void drawPolygon(const QPoint *, int , PolygonDrawMode );
+
+    virtual void drawPixmap(const QRectF &, 
+        const QPixmap &, const QRectF &);
+
+    virtual void drawTextItem(const QPointF &, const QTextItem &);
+
+    virtual void drawTiledPixmap(const QRectF &, 
+        const QPixmap &, const QPointF &s);
+
+    virtual void drawImage(const QRectF &, 
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+private:
+    QwtNullPaintDevice *nullDevice();
+};
+    
+QwtNullPaintDevice::PaintEngine::PaintEngine():
+    QPaintEngine( QPaintEngine::AllFeatures )
+{
+}
+
+bool QwtNullPaintDevice::PaintEngine::begin( QPaintDevice * )
+{
+    setActive( true );
+    return true;
+}
+
+bool QwtNullPaintDevice::PaintEngine::end()
+{
+    setActive( false );
+    return true;
+}
+
+QPaintEngine::Type QwtNullPaintDevice::PaintEngine::type() const
+{
+    return QPaintEngine::User;
+}
+
+void QwtNullPaintDevice::PaintEngine::drawRects(
+    const QRect *rects, int rectCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawRects( rects, rectCount );
+        return;
+    }
+
+    device->drawRects( rects, rectCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawRects(
+    const QRectF *rects, int rectCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawRects( rects, rectCount );
+        return;
+    }
+
+    device->drawRects( rects, rectCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawLines(
+    const QLine *lines, int lineCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawLines( lines, lineCount );
+        return;
+    }
+
+    device->drawLines( lines, lineCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawLines(
+    const QLineF *lines, int lineCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawLines( lines, lineCount );
+        return;
+    }
+
+    device->drawLines( lines, lineCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawEllipse(
+    const QRectF &rect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawEllipse( rect );
+        return;
+    }
+
+    device->drawEllipse( rect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawEllipse(
+    const QRect &rect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawEllipse( rect );
+        return;
+    }
+
+    device->drawEllipse( rect );
+}
+
+
+void QwtNullPaintDevice::PaintEngine::drawPath(
+    const QPainterPath &path)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawPath( path );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPoints(
+    const QPointF *points, int pointCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawPoints( points, pointCount );
+        return;
+    }
+
+    device->drawPoints( points, pointCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPoints(
+    const QPoint *points, int pointCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawPoints( points, pointCount );
+        return;
+    }
+
+    device->drawPoints( points, pointCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPolygon(
+    const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() == QwtNullPaintDevice::PathMode )
+    {
+        QPainterPath path;
+
+        if ( pointCount > 0 )
+        {
+            path.moveTo( points[0] );
+            for ( int i = 1; i < pointCount; i++ )
+                path.lineTo( points[i] );
+
+            if ( mode != PolylineMode )
+                path.closeSubpath();
+        }
+
+        device->drawPath( path );
+        return;
+    }
+
+    device->drawPolygon( points, pointCount, mode );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPolygon(
+    const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() == QwtNullPaintDevice::PathMode )
+    {
+        QPainterPath path;
+
+        if ( pointCount > 0 )
+        {
+            path.moveTo( points[0] );
+            for ( int i = 1; i < pointCount; i++ )
+                path.lineTo( points[i] );
+
+            if ( mode != PolylineMode )
+                path.closeSubpath();
+        }
+
+        device->drawPath( path );
+        return;
+    }
+
+    device->drawPolygon( points, pointCount, mode );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPixmap( 
+    const QRectF &rect, const QPixmap &pm, const QRectF &subRect )
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawPixmap( rect, pm, subRect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawTextItem(
+    const QPointF &pos, const QTextItem &textItem)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawTextItem( pos, textItem );
+        return;
+    }
+
+    device->drawTextItem( pos, textItem );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawTiledPixmap(
+    const QRectF &rect, const QPixmap &pixmap, 
+    const QPointF &subRect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawTiledPixmap( rect, pixmap, subRect );
+        return;
+    }   
+
+    device->drawTiledPixmap( rect, pixmap, subRect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawImage(
+    const QRectF &rect, const QImage &image, 
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawImage( rect, image, subRect, flags );
+}
+
+void QwtNullPaintDevice::PaintEngine::updateState(
+    const QPaintEngineState &state)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->updateState( state );
+}
+
+inline QwtNullPaintDevice *QwtNullPaintDevice::PaintEngine::nullDevice()
+{
+    if ( !isActive() )
+        return NULL;
+
+    return static_cast<QwtNullPaintDevice *>( paintDevice() );
+}
+
+//! Constructor
+QwtNullPaintDevice::QwtNullPaintDevice():
+    d_engine( NULL )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtNullPaintDevice::~QwtNullPaintDevice()
+{
+    delete d_engine;
+    delete d_data;
+}
+
+/*!
+    Set the render mode
+
+    \param mode New mode
+    \sa mode()
+ */
+void QwtNullPaintDevice::setMode( Mode mode )
+{
+    d_data->mode = mode;
+}
+
+/*! 
+    \return Render mode
+    \sa setMode()
+*/
+QwtNullPaintDevice::Mode QwtNullPaintDevice::mode() const
+{
+    return d_data->mode;
+}
+
+//! See QPaintDevice::paintEngine()
+QPaintEngine *QwtNullPaintDevice::paintEngine() const
+{
+    if ( d_engine == NULL )
+    {
+        QwtNullPaintDevice *that = 
+            const_cast< QwtNullPaintDevice * >( this );
+
+        that->d_engine = new PaintEngine();
+    }
+
+    return d_engine;
+}
+
+/*! 
+    See QPaintDevice::metric()
+
+    \param deviceMetric Type of metric
+    \return Metric information for the given paint device metric.
+
+    \sa sizeMetrics()
+*/
+int QwtNullPaintDevice::metric( PaintDeviceMetric deviceMetric ) const
+{
+    int value;
+
+    switch ( deviceMetric ) 
+    {
+        case PdmWidth:
+        {
+            value = sizeMetrics().width();
+            break;
+        }
+        case PdmHeight:
+        {
+            value = sizeMetrics().height();
+            break;
+        }
+        case PdmNumColors:
+        {
+            value = 0xffffffff;
+            break;
+        }
+        case PdmDepth:
+        {
+            value = 32;
+            break;
+        }
+        case PdmPhysicalDpiX:
+        case PdmPhysicalDpiY:
+        case PdmDpiY:
+        case PdmDpiX:
+        {
+            value = 72;
+            break;
+        }
+        case PdmWidthMM:
+        {
+            value = qRound( metric( PdmWidth ) * 25.4 / metric( PdmDpiX ) );
+            break;
+        }
+        case PdmHeightMM:
+        {
+            value = qRound( metric( PdmHeight ) * 25.4 / metric( PdmDpiY ) );
+            break;
+        }
+        default:
+            value = 0;
+    }
+    return value;
+
+}
+
+//! See QPaintEngine::drawRects()
+void QwtNullPaintDevice::drawRects(
+    const QRect *rects, int rectCount)
+{
+    Q_UNUSED(rects);
+    Q_UNUSED(rectCount);
+}
+
+//! See QPaintEngine::drawRects()
+void QwtNullPaintDevice::drawRects(
+    const QRectF *rects, int rectCount)
+{
+    Q_UNUSED(rects);
+    Q_UNUSED(rectCount);
+}
+
+//! See QPaintEngine::drawLines()
+void QwtNullPaintDevice::drawLines(
+    const QLine *lines, int lineCount)
+{
+    Q_UNUSED(lines);
+    Q_UNUSED(lineCount);
+}
+
+//! See QPaintEngine::drawLines()
+void QwtNullPaintDevice::drawLines(
+    const QLineF *lines, int lineCount)
+{
+    Q_UNUSED(lines);
+    Q_UNUSED(lineCount);
+}
+
+//! See QPaintEngine::drawEllipse()
+void QwtNullPaintDevice::drawEllipse( const QRectF &rect )
+{
+    Q_UNUSED(rect);
+}
+
+//! See QPaintEngine::drawEllipse()
+void QwtNullPaintDevice::drawEllipse( const QRect &rect )
+{
+    Q_UNUSED(rect);
+}
+
+//! See QPaintEngine::drawPath()
+void QwtNullPaintDevice::drawPath( const QPainterPath &path )
+{
+    Q_UNUSED(path);
+}
+
+//! See QPaintEngine::drawPoints()
+void QwtNullPaintDevice::drawPoints(
+    const QPointF *points, int pointCount)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+}
+
+//! See QPaintEngine::drawPoints()
+void QwtNullPaintDevice::drawPoints(
+    const QPoint *points, int pointCount)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+}
+
+//! See QPaintEngine::drawPolygon()
+void QwtNullPaintDevice::drawPolygon(
+    const QPointF *points, int pointCount, 
+    QPaintEngine::PolygonDrawMode mode)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+    Q_UNUSED(mode);
+}
+
+//! See QPaintEngine::drawPolygon()
+void QwtNullPaintDevice::drawPolygon(
+    const QPoint *points, int pointCount, 
+    QPaintEngine::PolygonDrawMode mode)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+    Q_UNUSED(mode);
+}
+
+//! See QPaintEngine::drawPixmap()
+void QwtNullPaintDevice::drawPixmap( const QRectF &rect, 
+    const QPixmap &pm, const QRectF &subRect )
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(pm);
+    Q_UNUSED(subRect);
+}
+
+//! See QPaintEngine::drawTextItem()
+void QwtNullPaintDevice::drawTextItem(
+    const QPointF &pos, const QTextItem &textItem)
+{
+    Q_UNUSED(pos);
+    Q_UNUSED(textItem);
+}
+
+//! See QPaintEngine::drawTiledPixmap()
+void QwtNullPaintDevice::drawTiledPixmap(
+    const QRectF &rect, const QPixmap &pixmap, 
+    const QPointF &subRect)
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(pixmap);
+    Q_UNUSED(subRect);
+}
+
+//! See QPaintEngine::drawImage()
+void QwtNullPaintDevice::drawImage(
+    const QRectF &rect, const QImage &image, 
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(image);
+    Q_UNUSED(subRect);
+    Q_UNUSED(flags);
+}
+
+//! See QPaintEngine::updateState()
+void QwtNullPaintDevice::updateState( 
+    const QPaintEngineState &state )
+{
+    Q_UNUSED(state);
+}
diff --git a/qwt/qwt_null_paintdevice.h b/qwt/qwt_null_paintdevice.h
new file mode 100644
index 0000000..d7f03be
--- /dev/null
+++ b/qwt/qwt_null_paintdevice.h
@@ -0,0 +1,126 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_NULL_PAINT_DEVICE_H
+#define QWT_NULL_PAINT_DEVICE_H 1
+
+#include "qwt_global.h"
+#include <qpaintdevice.h>
+#include <qpaintengine.h>
+
+/*!
+  \brief A null paint device doing nothing
+
+  Sometimes important layout/rendering geometries are not 
+  available or changeable from the public Qt class interface. 
+  ( f.e hidden in the style implementation ).
+
+  QwtNullPaintDevice can be used to manipulate or filter out 
+  this information by analyzing the stream of paint primitives.
+
+  F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify
+  styled backgrounds with rounded corners.
+*/
+
+class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice
+{
+public:
+    /*!
+      \brief Render mode
+
+      \sa setMode(), mode()
+     */
+    enum Mode
+    {
+        /*!
+           All vector graphic primitives are painted by
+           the corresponding draw methods
+         */
+        NormalMode, 
+
+        /*!
+           Vector graphic primitives ( beside polygons ) are mapped to a QPainterPath
+           and are painted by drawPath. In PathMode mode
+           only a few draw methods are called:
+
+           - drawPath()
+           - drawPixmap()
+           - drawImage()
+           - drawPolygon()
+         */
+        PolygonPathMode,
+
+        /*!
+           Vector graphic primitives are mapped to a QPainterPath
+           and are painted by drawPath. In PathMode mode
+           only a few draw methods are called:
+
+           - drawPath()
+           - drawPixmap()
+           - drawImage()
+         */
+        PathMode
+    };
+
+    QwtNullPaintDevice();
+    virtual ~QwtNullPaintDevice();
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    virtual QPaintEngine *paintEngine() const;
+
+    virtual int metric( PaintDeviceMetric metric ) const;
+
+    virtual void drawRects(const QRect *, int );
+    virtual void drawRects(const QRectF *, int );
+
+    virtual void drawLines(const QLine *, int );
+    virtual void drawLines(const QLineF *, int );
+
+    virtual void drawEllipse(const QRectF &);
+    virtual void drawEllipse(const QRect &);
+
+    virtual void drawPath(const QPainterPath &);
+
+    virtual void drawPoints(const QPointF *, int );
+    virtual void drawPoints(const QPoint *, int );
+
+    virtual void drawPolygon(
+        const QPointF *, int , QPaintEngine::PolygonDrawMode );
+
+    virtual void drawPolygon(
+        const QPoint *, int , QPaintEngine::PolygonDrawMode );
+
+    virtual void drawPixmap(const QRectF &,
+        const QPixmap &, const QRectF &);
+
+    virtual void drawTextItem(const QPointF &, const QTextItem &);
+
+    virtual void drawTiledPixmap(const QRectF &,
+        const QPixmap &, const QPointF &s);
+
+    virtual void drawImage(const QRectF &,
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+    virtual void updateState( const QPaintEngineState &state );
+
+protected:
+    //! \return Size needed to implement metric()
+    virtual QSize sizeMetrics() const = 0;
+
+private:
+    class PaintEngine;
+    PaintEngine *d_engine;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_painter.cpp b/qwt/qwt_painter.cpp
new file mode 100644
index 0000000..1509a7e
--- /dev/null
+++ b/qwt/qwt_painter.cpp
@@ -0,0 +1,1266 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_painter.h"
+#include "qwt_math.h"
+#include "qwt_clipper.h"
+#include "qwt_color_map.h"
+#include "qwt_scale_map.h"
+#include <qwindowdefs.h>
+#include <qwidget.h>
+#include <qframe.h>
+#include <qrect.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpaintdevice.h>
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <qtextdocument.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qstyleoption.h>
+#include <qpaintengine.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+
+#if QT_VERSION >= 0x050000
+#include <qwindow.h>
+#endif
+
+#if QT_VERSION < 0x050000 
+
+#ifdef Q_WS_X11
+#include <qx11info_x11.h>
+#endif
+
+#endif
+
+bool QwtPainter::d_polylineSplitting = true;
+bool QwtPainter::d_roundingAlignment = true;
+
+static inline bool qwtIsClippingNeeded( 
+    const QPainter *painter, QRectF &clipRect )
+{
+    bool doClipping = false;
+    const QPaintEngine *pe = painter->paintEngine();
+    if ( pe && pe->type() == QPaintEngine::SVG )
+    {
+        // The SVG paint engine ignores any clipping,
+
+        if ( painter->hasClipping() )
+        {
+            doClipping = true;
+            clipRect = painter->clipRegion().boundingRect();
+        }
+    }
+
+    return doClipping;
+}
+
+template <class T>
+static inline void qwtDrawPolyline( QPainter *painter,
+    const T *points, int pointCount, bool polylineSplitting )
+{
+    bool doSplit = false;
+    if ( polylineSplitting )
+    {
+        const QPaintEngine *pe = painter->paintEngine();
+        if ( pe && pe->type() == QPaintEngine::Raster )
+        {
+            /*
+                The raster paint engine seems to use some algo with O(n*n).
+                ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
+                To work around this problem, we have to split the polygon into
+                smaller pieces.
+             */
+            doSplit = true;
+        }
+    }
+
+    if ( doSplit )
+    {
+        const int splitSize = 20;
+        for ( int i = 0; i < pointCount; i += splitSize )
+        {
+            const int n = qMin( splitSize + 1, pointCount - i );
+            painter->drawPolyline( points + i, n );
+        }
+    }
+    else
+        painter->drawPolyline( points, pointCount );
+}
+
+static inline void qwtUnscaleFont( QPainter *painter )
+{
+    if ( painter->font().pixelSize() >= 0 )
+        return;
+
+    static QSize screenResolution;
+    if ( !screenResolution.isValid() )
+    {
+        QDesktopWidget *desktop = QApplication::desktop();
+        if ( desktop )
+        {
+            screenResolution.setWidth( desktop->logicalDpiX() );
+            screenResolution.setHeight( desktop->logicalDpiY() );
+        }
+    }
+
+    const QPaintDevice *pd = painter->device();
+    if ( pd->logicalDpiX() != screenResolution.width() ||
+        pd->logicalDpiY() != screenResolution.height() )
+    {
+        QFont pixelFont( painter->font(), QApplication::desktop() );
+        pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
+
+        painter->setFont( pixelFont );
+    }
+}
+
+/*!
+  Check is the application is running with the X11 graphics system
+  that has some special capabilities that can be used for incremental
+  painting to a widget.
+
+  \return True, when the graphics system is X11
+*/
+bool QwtPainter::isX11GraphicsSystem()
+{
+    static int onX11 = -1;
+    if ( onX11 < 0 )
+    {
+        QPixmap pm( 1, 1 );
+        QPainter painter( &pm );
+
+        onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0;
+    }
+
+    return onX11 == 1;
+}
+
+/*!
+  Check if the painter is using a paint engine, that aligns
+  coordinates to integers. Today these are all paint engines
+  beside QPaintEngine::Pdf and QPaintEngine::SVG.
+
+  If we have an integer based paint engine it is also
+  checked if the painter has a transformation matrix,
+  that rotates or scales.
+
+  \param  painter Painter
+  \return true, when the painter is aligning
+
+  \sa setRoundingAlignment()
+*/
+bool QwtPainter::isAligning( QPainter *painter )
+{
+    if ( painter && painter->isActive() )
+    {
+        switch ( painter->paintEngine()->type() )
+        {
+            case QPaintEngine::Pdf:
+            case QPaintEngine::SVG:
+                return false;
+
+            default:;
+        }
+
+        const QTransform tr = painter->transform();
+        if ( tr.isRotating() || tr.isScaling() )
+        {
+            // we might have to check translations too
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*!
+  Enable whether coordinates should be rounded, before they are painted
+  to a paint engine that floors to integer values. For other paint engines
+  this ( PDF, SVG ), this flag has no effect.
+  QwtPainter stores this flag only, the rounding itself is done in 
+  the painting code ( f.e the plot items ).
+
+  The default setting is true. 
+
+  \sa roundingAlignment(), isAligning()
+*/
+void QwtPainter::setRoundingAlignment( bool enable )
+{
+    d_roundingAlignment = enable;
+}
+
+/*!
+  \brief En/Disable line splitting for the raster paint engine
+
+  In some Qt versions the raster paint engine paints polylines of many points
+  much faster when they are split in smaller chunks: f.e all supported Qt versions
+  >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2.
+
+  The default setting is true.
+
+  \sa polylineSplitting()
+*/
+void QwtPainter::setPolylineSplitting( bool enable )
+{
+    d_polylineSplitting = enable;
+}
+
+//! Wrapper for QPainter::drawPath()
+void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
+{
+    painter->drawPath( path );
+}
+
+//! Wrapper for QPainter::drawRect()
+void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
+{
+    drawRect( painter, QRectF( x, y, w, h ) );
+}
+
+//! Wrapper for QPainter::drawRect()
+void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
+{
+    const QRectF r = rect;
+
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        if ( !clipRect.intersects( r ) )
+            return;
+
+        if ( !clipRect.contains( r ) )
+        {
+            fillRect( painter, r & clipRect, painter->brush() );
+
+            painter->save();
+            painter->setBrush( Qt::NoBrush );
+            drawPolyline( painter, QPolygonF( r ) );
+            painter->restore();
+
+            return;
+        }
+    }
+
+    painter->drawRect( r );
+}
+
+//! Wrapper for QPainter::fillRect()
+void QwtPainter::fillRect( QPainter *painter,
+    const QRectF &rect, const QBrush &brush )
+{
+    if ( !rect.isValid() )
+        return;
+
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    /*
+      Performance of Qt4 is horrible for a non trivial brush. Without
+      clipping expect minutes or hours for repainting large rectangles
+      (might result from zooming)
+    */
+
+    if ( deviceClipping )
+        clipRect &= painter->window();
+    else
+        clipRect = painter->window();
+
+    if ( painter->hasClipping() )
+        clipRect &= painter->clipRegion().boundingRect();
+
+    QRectF r = rect;
+    if ( deviceClipping )
+        r = r.intersected( clipRect );
+
+    if ( r.isValid() )
+        painter->fillRect( r, brush );
+}
+
+//! Wrapper for QPainter::drawPie()
+void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
+    int a, int alen )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+    if ( deviceClipping && !clipRect.contains( rect ) )
+        return;
+
+    painter->drawPie( rect, a, alen );
+}
+
+//! Wrapper for QPainter::drawEllipse()
+void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( rect ) )
+        return;
+
+    painter->drawEllipse( rect );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, double x, double y,
+        const QString &text )
+{
+    drawText( painter, QPointF( x, y ), text );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
+        const QString &text )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( pos ) )
+        return;
+
+
+    painter->save();
+    qwtUnscaleFont( painter );
+    painter->drawText( pos, text );
+    painter->restore();
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter,
+    double x, double y, double w, double h,
+    int flags, const QString &text )
+{
+    drawText( painter, QRectF( x, y, w, h ), flags, text );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text )
+{
+    painter->save();
+    qwtUnscaleFont( painter );
+    painter->drawText( rect, flags, text );
+    painter->restore();
+}
+
+#ifndef QT_NO_RICHTEXT
+
+/*!
+  Draw a text document into a rectangle
+
+  \param painter Painter
+  \param rect Traget rectangle
+  \param flags Alignments/Text flags, see QPainter::drawText()
+  \param text Text document
+*/
+void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
+    int flags, const QTextDocument &text )
+{
+    QTextDocument *txt = text.clone();
+
+    painter->save();
+
+    painter->setFont( txt->defaultFont() );
+    qwtUnscaleFont( painter );
+
+    txt->setDefaultFont( painter->font() );
+    txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) );
+
+    QAbstractTextDocumentLayout* layout = txt->documentLayout();
+
+    const double height = layout->documentSize().height();
+    double y = rect.y();
+    if ( flags & Qt::AlignBottom )
+        y += ( rect.height() - height );
+    else if ( flags & Qt::AlignVCenter )
+        y += ( rect.height() - height ) / 2;
+
+    QAbstractTextDocumentLayout::PaintContext context;
+    context.palette.setColor( QPalette::Text, painter->pen().color() );
+
+    painter->translate( rect.x(), y );
+    layout->draw( painter, context );
+
+    painter->restore();
+    delete txt;
+}
+
+#endif // !QT_NO_RICHTEXT
+
+
+//! Wrapper for QPainter::drawLine()
+void QwtPainter::drawLine( QPainter *painter,
+    const QPointF &p1, const QPointF &p2 )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping &&
+        !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
+    {
+        QPolygonF polygon;
+        polygon += p1;
+        polygon += p2;
+        drawPolyline( painter, polygon );
+        return;
+    }
+
+    painter->drawLine( p1, p2 );
+}
+
+//! Wrapper for QPainter::drawPolygon()
+void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygonF cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygonF( clipRect, polygon );
+
+    painter->drawPolygon( cpa );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygonF cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygonF( clipRect, cpa );
+
+    qwtDrawPolyline<QPointF>( painter,
+        cpa.constData(), cpa.size(), d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter,
+    const QPointF *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygonF polygon( pointCount );
+        ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) );
+
+        polygon = QwtClipper::clipPolygonF( clipRect, polygon );
+        qwtDrawPolyline<QPointF>( painter,
+            polygon.constData(), polygon.size(), d_polylineSplitting );
+    }
+    else
+    {
+        qwtDrawPolyline<QPointF>( painter, points, pointCount, d_polylineSplitting );
+    }
+}
+
+//! Wrapper for QPainter::drawPolygon()
+void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygon cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygon( clipRect, polygon );
+
+    painter->drawPolygon( cpa );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygon cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygon( clipRect, cpa );
+
+    qwtDrawPolyline<QPoint>( painter,
+        cpa.constData(), cpa.size(), d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter,
+    const QPoint *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygon polygon( pointCount );
+        ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) );
+
+        polygon = QwtClipper::clipPolygon( clipRect, polygon );
+        qwtDrawPolyline<QPoint>( painter,
+            polygon.constData(), polygon.size(), d_polylineSplitting );
+    }
+    else
+        qwtDrawPolyline<QPoint>( painter, points, pointCount, d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPoint()
+void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( pos ) )
+        return;
+
+    painter->drawPoint( pos );
+}
+
+//! Wrapper for QPainter::drawPoint()
+void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        const int minX = qCeil( clipRect.left() );
+        const int maxX = qFloor( clipRect.right() );
+        const int minY = qCeil( clipRect.top() );
+        const int maxY = qFloor( clipRect.bottom() );
+
+        if ( pos.x() < minX || pos.x() > maxX 
+            || pos.y() < minY || pos.y() > maxY )
+        {
+            return;
+        }
+    }
+
+    painter->drawPoint( pos );
+}
+
+//! Wrapper for QPainter::drawPoints()
+void QwtPainter::drawPoints( QPainter *painter, 
+    const QPoint *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        const int minX = qCeil( clipRect.left() );
+        const int maxX = qFloor( clipRect.right() );
+        const int minY = qCeil( clipRect.top() );
+        const int maxY = qFloor( clipRect.bottom() );
+
+        const QRect r( minX, minY, maxX - minX, maxY - minY );
+
+        QPolygon clippedPolygon( pointCount );
+        QPoint *clippedData = clippedPolygon.data();
+
+        int numClippedPoints = 0;
+        for ( int i = 0; i < pointCount; i++ )
+        {
+            if ( r.contains( points[i] ) )
+                clippedData[ numClippedPoints++ ] = points[i];
+        }
+        painter->drawPoints( clippedData, numClippedPoints );
+    }
+    else
+    {
+        painter->drawPoints( points, pointCount );
+    }
+}
+
+//! Wrapper for QPainter::drawPoints()
+void QwtPainter::drawPoints( QPainter *painter, 
+    const QPointF *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygonF clippedPolygon( pointCount );
+        QPointF *clippedData = clippedPolygon.data();
+
+        int numClippedPoints = 0;
+        for ( int i = 0; i < pointCount; i++ )
+        {
+            if ( clipRect.contains( points[i] ) )
+                clippedData[ numClippedPoints++ ] = points[i];
+        }
+        painter->drawPoints( clippedData, numClippedPoints );
+    }
+    else
+    {
+        painter->drawPoints( points, pointCount );
+    }
+}
+
+//! Wrapper for QPainter::drawImage()
+void QwtPainter::drawImage( QPainter *painter,
+    const QRectF &rect, const QImage &image )
+{
+    const QRect alignedRect = rect.toAlignedRect();
+
+    if ( alignedRect != rect )
+    {
+        const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+        painter->save();
+        painter->setClipRect( clipRect, Qt::IntersectClip );
+        painter->drawImage( alignedRect, image );
+        painter->restore();
+    }
+    else
+    {
+        painter->drawImage( alignedRect, image );
+    }
+}
+
+//! Wrapper for QPainter::drawPixmap()
+void QwtPainter::drawPixmap( QPainter *painter,
+    const QRectF &rect, const QPixmap &pixmap )
+{
+    const QRect alignedRect = rect.toAlignedRect();
+
+    if ( alignedRect != rect )
+    {
+        const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+        painter->save();
+        painter->setClipRect( clipRect, Qt::IntersectClip );
+        painter->drawPixmap( alignedRect, pixmap );
+        painter->restore();
+    }
+    else
+    {
+        painter->drawPixmap( alignedRect, pixmap );
+    }
+}
+
+//! Draw a focus rectangle on a widget using its style.
+void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget )
+{
+    drawFocusRect( painter, widget, widget->rect() );
+}
+
+//! Draw a focus rectangle on a widget using its style.
+void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget,
+    const QRect &rect )
+{
+    QStyleOptionFocusRect opt;
+    opt.init( widget );
+    opt.rect = rect;
+    opt.state |= QStyle::State_HasFocus;
+
+    widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
+        &opt, painter, widget );
+}
+
+/*!
+  Draw a round frame 
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param palette QPalette::WindowText is used for plain borders
+                 QPalette::Dark and QPalette::Light for raised
+                 or sunken borders
+  \param lineWidth Line width
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+void QwtPainter::drawRoundFrame( QPainter *painter,
+    const QRectF &rect, const QPalette &palette, 
+    int lineWidth, int frameStyle )
+{
+    enum Style
+    {
+        Plain,
+        Sunken,
+        Raised
+    };
+
+    Style style = Plain;
+    if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
+        style = Sunken;
+    else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
+        style = Raised;
+
+    const double lw2 = 0.5 * lineWidth;
+    QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
+
+    QBrush brush;
+
+    if ( style != Plain )
+    {
+        QColor c1 = palette.color( QPalette::Light );
+        QColor c2 = palette.color( QPalette::Dark );
+
+        if ( style == Sunken )
+            qSwap( c1, c2 );
+
+        QLinearGradient gradient( r.topLeft(), r.bottomRight() );
+        gradient.setColorAt( 0.0, c1 );
+#if 0
+        gradient.setColorAt( 0.3, c1 );
+        gradient.setColorAt( 0.7, c2 );
+#endif
+        gradient.setColorAt( 1.0, c2 );
+
+        brush = QBrush( gradient );
+    }
+    else // Plain
+    {
+        brush = palette.brush( QPalette::WindowText );
+    }
+
+    painter->save();
+
+    painter->setPen( QPen( brush, lineWidth ) );
+    painter->setBrush( Qt::NoBrush );
+
+    painter->drawEllipse( r );
+
+    painter->restore();
+}
+
+/*!
+  Draw a rectangular frame
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param palette Palette
+  \param foregroundRole Foreground role used for QFrame::Plain
+  \param frameWidth Frame width
+  \param midLineWidth Used for QFrame::Box
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect,
+    const QPalette &palette, QPalette::ColorRole foregroundRole,
+    int frameWidth, int midLineWidth, int frameStyle )
+{
+    if ( frameWidth <= 0 || rect.isEmpty() )
+        return;
+
+    const int shadow = frameStyle & QFrame::Shadow_Mask;
+
+    painter->save();
+
+    if ( shadow == QFrame::Plain )
+    {
+        const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+        const QRectF innerRect = outerRect.adjusted( 
+            frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+        QPainterPath path;
+        path.addRect( outerRect );
+        path.addRect( innerRect );
+
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette.color( foregroundRole ) );
+
+        painter->drawPath( path );
+    }
+    else
+    {
+        const int shape = frameStyle & QFrame::Shape_Mask;
+
+        if ( shape == QFrame::Box )
+        {
+            const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+            const QRectF midRect1 = outerRect.adjusted( 
+                frameWidth, frameWidth, -frameWidth, -frameWidth );
+            const QRectF midRect2 = midRect1.adjusted( 
+                midLineWidth, midLineWidth, -midLineWidth, -midLineWidth );
+
+            const QRectF innerRect = midRect2.adjusted( 
+                frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+            QPainterPath path1;
+            path1.moveTo( outerRect.bottomLeft() );
+            path1.lineTo( outerRect.topLeft() );
+            path1.lineTo( outerRect.topRight() );
+            path1.lineTo( midRect1.topRight() );
+            path1.lineTo( midRect1.topLeft() );
+            path1.lineTo( midRect1.bottomLeft() );
+
+            QPainterPath path2;
+            path2.moveTo( outerRect.bottomLeft() );
+            path2.lineTo( outerRect.bottomRight() );
+            path2.lineTo( outerRect.topRight() );
+            path2.lineTo( midRect1.topRight() );
+            path2.lineTo( midRect1.bottomRight() );
+            path2.lineTo( midRect1.bottomLeft() );
+
+            QPainterPath path3;
+            path3.moveTo( midRect2.bottomLeft() );
+            path3.lineTo( midRect2.topLeft() );
+            path3.lineTo( midRect2.topRight() );
+            path3.lineTo( innerRect.topRight() );
+            path3.lineTo( innerRect.topLeft() );
+            path3.lineTo( innerRect.bottomLeft() );
+
+            QPainterPath path4;
+            path4.moveTo( midRect2.bottomLeft() );
+            path4.lineTo( midRect2.bottomRight() );
+            path4.lineTo( midRect2.topRight() );
+            path4.lineTo( innerRect.topRight() );
+            path4.lineTo( innerRect.bottomRight() );
+            path4.lineTo( innerRect.bottomLeft() );
+
+            QPainterPath path5;
+            path5.addRect( midRect1 );
+            path5.addRect( midRect2 );
+
+            painter->setPen( Qt::NoPen );
+
+            QBrush brush1 = palette.dark().color();
+            QBrush brush2 = palette.light().color();
+
+            if ( shadow == QFrame::Raised )
+                qSwap( brush1, brush2 );
+
+            painter->setBrush( brush1 );
+            painter->drawPath( path1 );
+            painter->drawPath( path4 );
+
+            painter->setBrush( brush2 );
+            painter->drawPath( path2 );
+            painter->drawPath( path3 );
+
+            painter->setBrush( palette.mid() );
+            painter->drawPath( path5 );
+        }
+#if 0
+        // qDrawWinPanel doesn't result in something nice
+        // on a scalable document like PDF. Better draw a
+        // Panel.
+
+        else if ( shape == QFrame::WinPanel )
+        {
+            painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true );
+            qDrawWinPanel ( painter, rect.toRect(), palette,
+                frameStyle & QFrame::Sunken );
+        }
+        else if ( shape == QFrame::StyledPanel )
+        {
+        }
+#endif
+        else
+        {
+            const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+            const QRectF innerRect = outerRect.adjusted( 
+                frameWidth - 1.0, frameWidth - 1.0, 
+                -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) );
+
+            QPainterPath path1;
+            path1.moveTo( outerRect.bottomLeft() );
+            path1.lineTo( outerRect.topLeft() );
+            path1.lineTo( outerRect.topRight() );
+            path1.lineTo( innerRect.topRight() );
+            path1.lineTo( innerRect.topLeft() );
+            path1.lineTo( innerRect.bottomLeft() );
+
+
+            QPainterPath path2;
+            path2.moveTo( outerRect.bottomLeft() );
+            path2.lineTo( outerRect.bottomRight() );
+            path2.lineTo( outerRect.topRight() );
+            path2.lineTo( innerRect.topRight() );
+            path2.lineTo( innerRect.bottomRight() );
+            path2.lineTo( innerRect.bottomLeft() );
+
+            painter->setPen( Qt::NoPen );
+
+            QBrush brush1 = palette.dark().color();
+            QBrush brush2 = palette.light().color();
+
+            if ( shadow == QFrame::Raised )
+                qSwap( brush1, brush2 );
+
+            painter->setBrush( brush1 );
+            painter->drawPath( path1 );
+
+            painter->setBrush( brush2 );
+            painter->drawPath( path2 );
+        }
+
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a rectangular frame with rounded borders
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param xRadius x-radius of the ellipses defining the corners
+  \param yRadius y-radius of the ellipses defining the corners
+  \param palette QPalette::WindowText is used for plain borders
+                 QPalette::Dark and QPalette::Light for raised
+                 or sunken borders
+  \param lineWidth Line width
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+
+void QwtPainter::drawRoundedFrame( QPainter *painter, 
+    const QRectF &rect, double xRadius, double yRadius, 
+    const QPalette &palette, int lineWidth, int frameStyle )
+{
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    painter->setBrush( Qt::NoBrush );
+
+    double lw2 = lineWidth * 0.5;
+    QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
+
+    QPainterPath path;
+    path.addRoundedRect( r, xRadius, yRadius );
+
+    enum Style
+    {
+        Plain,
+        Sunken,
+        Raised
+    };
+
+    Style style = Plain;
+    if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
+        style = Sunken;
+    else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
+        style = Raised;
+
+    if ( style != Plain && path.elementCount() == 17 )
+    {
+        // move + 4 * ( cubicTo + lineTo )
+        QPainterPath pathList[8];
+        
+        for ( int i = 0; i < 4; i++ )
+        {
+            const int j = i * 4 + 1;
+            
+            pathList[ 2 * i ].moveTo(
+                path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
+            );  
+            
+            pathList[ 2 * i ].cubicTo(
+                path.elementAt(j + 0).x, path.elementAt(j + 0).y,
+                path.elementAt(j + 1).x, path.elementAt(j + 1).y,
+                path.elementAt(j + 2).x, path.elementAt(j + 2).y );
+                
+            pathList[ 2 * i + 1 ].moveTo(
+                path.elementAt(j + 2).x, path.elementAt(j + 2).y
+            );  
+            pathList[ 2 * i + 1 ].lineTo(
+                path.elementAt(j + 3).x, path.elementAt(j + 3).y
+            );  
+        }   
+
+        QColor c1( palette.color( QPalette::Dark ) );
+        QColor c2( palette.color( QPalette::Light ) );
+
+        if ( style == Raised )
+            qSwap( c1, c2 );
+
+        for ( int i = 0; i < 4; i++ )
+        {
+            QRectF r = pathList[2 * i].controlPointRect();
+
+            QPen arcPen;
+            arcPen.setCapStyle( Qt::FlatCap );
+            arcPen.setWidth( lineWidth );
+
+            QPen linePen;
+            linePen.setCapStyle( Qt::FlatCap );
+            linePen.setWidth( lineWidth );
+
+            switch( i )
+            {
+                case 0:
+                {
+                    arcPen.setColor( c1 );
+                    linePen.setColor( c1 );
+                    break;
+                }
+                case 1:
+                {
+                    QLinearGradient gradient;
+                    gradient.setStart( r.topLeft() );
+                    gradient.setFinalStop( r.bottomRight() );
+                    gradient.setColorAt( 0.0, c1 );
+                    gradient.setColorAt( 1.0, c2 );
+
+                    arcPen.setBrush( gradient );
+                    linePen.setColor( c2 );
+                    break;
+                }
+                case 2:
+                {
+                    arcPen.setColor( c2 );
+                    linePen.setColor( c2 );
+                    break;
+                }
+                case 3:
+                {
+                    QLinearGradient gradient;
+
+                    gradient.setStart( r.bottomRight() );
+                    gradient.setFinalStop( r.topLeft() );
+                    gradient.setColorAt( 0.0, c2 );
+                    gradient.setColorAt( 1.0, c1 );
+
+                    arcPen.setBrush( gradient );
+                    linePen.setColor( c1 );
+                    break;
+                }
+            }
+
+
+            painter->setPen( arcPen );
+            painter->drawPath( pathList[ 2 * i] );
+
+            painter->setPen( linePen );
+            painter->drawPath( pathList[ 2 * i + 1] );
+        }
+    }
+    else
+    {
+        QPen pen( palette.color( QPalette::WindowText ), lineWidth );
+        painter->setPen( pen );
+        painter->drawPath( path );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a color bar into a rectangle
+
+  \param painter Painter
+  \param colorMap Color map
+  \param interval Value range
+  \param scaleMap Scale map
+  \param orientation Orientation
+  \param rect Traget rectangle
+*/
+void QwtPainter::drawColorBar( QPainter *painter,
+        const QwtColorMap &colorMap, const QwtInterval &interval,
+        const QwtScaleMap &scaleMap, Qt::Orientation orientation,
+        const QRectF &rect )
+{
+    QVector<QRgb> colorTable;
+    if ( colorMap.format() == QwtColorMap::Indexed )
+        colorTable = colorMap.colorTable( interval );
+
+    QColor c;
+
+    const QRect devRect = rect.toAlignedRect();
+
+    /*
+      We paint to a pixmap first to have something scalable for printing
+      ( f.e. in a Pdf document )
+     */
+
+    QPixmap pixmap( devRect.size() );
+    QPainter pmPainter( &pixmap );
+    pmPainter.translate( -devRect.x(), -devRect.y() );
+
+    if ( orientation == Qt::Horizontal )
+    {
+        QwtScaleMap sMap = scaleMap;
+        sMap.setPaintInterval( rect.left(), rect.right() );
+
+        for ( int x = devRect.left(); x <= devRect.right(); x++ )
+        {
+            const double value = sMap.invTransform( x );
+
+            if ( colorMap.format() == QwtColorMap::RGB )
+                c.setRgb( colorMap.rgb( interval, value ) );
+            else
+                c = colorTable[colorMap.colorIndex( interval, value )];
+
+            pmPainter.setPen( c );
+            pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
+        }
+    }
+    else // Vertical
+    {
+        QwtScaleMap sMap = scaleMap;
+        sMap.setPaintInterval( rect.bottom(), rect.top() );
+
+        for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
+        {
+            const double value = sMap.invTransform( y );
+
+            if ( colorMap.format() == QwtColorMap::RGB )
+                c.setRgb( colorMap.rgb( interval, value ) );
+            else
+                c = colorTable[colorMap.colorIndex( interval, value )];
+
+            pmPainter.setPen( c );
+            pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
+        }
+    }
+    pmPainter.end();
+
+    drawPixmap( painter, rect, pixmap );
+}
+
+static inline void qwtFillRect( const QWidget *widget, QPainter *painter, 
+    const QRect &rect, const QBrush &brush)
+{
+    if ( brush.style() == Qt::TexturePattern ) 
+    {
+        painter->save();
+
+        painter->setClipRect( rect );
+        painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
+
+        painter->restore();
+    } 
+    else if ( brush.gradient() )
+    {
+        painter->save();
+
+        painter->setClipRect( rect );
+        painter->fillRect(0, 0, widget->width(), 
+            widget->height(), brush);
+
+        painter->restore();
+    } 
+    else 
+    {
+        painter->fillRect(rect, brush);
+    }
+}
+
+/*!
+  Fill a pixmap with the content of a widget
+
+  In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy
+  for backgrounds with gradients. Thus fillPixmap() offers 
+  an alternative implementation.
+
+  \param widget Widget
+  \param pixmap Pixmap to be filled
+  \param offset Offset 
+
+  \sa QPixmap::fill()
+ */
+void QwtPainter::fillPixmap( const QWidget *widget, 
+    QPixmap &pixmap, const QPoint &offset )
+{
+    const QRect rect( offset, pixmap.size() );
+
+    QPainter painter( &pixmap );
+    painter.translate( -offset );
+
+    const QBrush autoFillBrush = 
+        widget->palette().brush( widget->backgroundRole() );
+
+    if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) 
+    {
+        const QBrush bg = widget->palette().brush( QPalette::Window );
+        qwtFillRect( widget, &painter, rect, bg);
+    }
+
+    if ( widget->autoFillBackground() )
+        qwtFillRect( widget, &painter, rect, autoFillBrush);
+
+    if ( widget->testAttribute(Qt::WA_StyledBackground) ) 
+    {
+        painter.setClipRegion( rect );
+
+        QStyleOption opt;
+        opt.initFrom( widget );
+        widget->style()->drawPrimitive( QStyle::PE_Widget, 
+            &opt, &painter, widget );
+    }
+}
+
+/*!
+  Fill rect with the background of a widget
+
+  \param painter Painter
+  \param rect Rectangle to be filled
+  \param widget Widget
+
+  \sa QStyle::PE_Widget, QWidget::backgroundRole()
+ */
+void QwtPainter::drawBackgound( QPainter *painter,
+    const QRectF &rect, const QWidget *widget )
+{
+    if ( widget->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QStyleOption opt;
+        opt.initFrom( widget );
+        opt.rect = rect.toAlignedRect();
+
+        widget->style()->drawPrimitive(
+            QStyle::PE_Widget, &opt, painter, widget);
+    }
+    else
+    {
+        const QBrush brush =
+            widget->palette().brush( widget->backgroundRole() );
+
+        painter->fillRect( rect, brush );
+    }
+}
+
+/*!
+  \return A pixmap that can be used as backing store
+
+  \param widget Widget, for which the backinstore is intended
+  \param size Size of the pixmap
+ */
+QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size )
+{
+    QPixmap pm;
+
+#define QWT_HIGH_DPI 1
+
+#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI
+    qreal pixelRatio = 1.0;
+
+    if ( widget && widget->windowHandle() )
+    {
+        pixelRatio = widget->windowHandle()->devicePixelRatio();
+    }
+    else
+    {
+        if ( qApp )
+            pixelRatio = qApp->devicePixelRatio();
+    }
+
+    pm = QPixmap( size * pixelRatio );
+    pm.setDevicePixelRatio( pixelRatio );
+#else
+    Q_UNUSED( widget )
+    pm = QPixmap( size );
+#endif
+
+#if QT_VERSION < 0x050000 
+#ifdef Q_WS_X11
+    if ( widget && isX11GraphicsSystem() )
+    {
+        if ( pm.x11Info().screen() != widget->x11Info().screen() )
+            pm.x11SetScreen( widget->x11Info().screen() );
+    }
+#endif
+#endif
+
+    return pm;
+}
+
diff --git a/qwt/qwt_painter.h b/qwt/qwt_painter.h
new file mode 100644
index 0000000..9609b69
--- /dev/null
+++ b/qwt/qwt_painter.h
@@ -0,0 +1,188 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PAINTER_H
+#define QWT_PAINTER_H
+
+#include "qwt_global.h"
+
+#include <qpoint.h>
+#include <qrect.h>
+#include <qpen.h>
+#include <qline.h>
+#include <qpalette.h>
+
+class QPainter;
+class QBrush;
+class QColor;
+class QWidget;
+class QPolygonF;
+class QRectF;
+class QImage;
+class QPixmap;
+class QwtScaleMap;
+class QwtColorMap;
+class QwtInterval;
+
+class QTextDocument;
+class QPainterPath;
+
+/*!
+  \brief A collection of QPainter workarounds
+*/
+class QWT_EXPORT QwtPainter
+{
+public:
+    static void setPolylineSplitting( bool );
+    static bool polylineSplitting();
+
+    static void setRoundingAlignment( bool );
+    static bool roundingAlignment();
+    static bool roundingAlignment(QPainter *);
+
+    static void drawText( QPainter *, double x, double y, const QString & );
+    static void drawText( QPainter *, const QPointF &, const QString & );
+    static void drawText( QPainter *, double x, double y, double w, double h,
+        int flags, const QString & );
+    static void drawText( QPainter *, const QRectF &, 
+        int flags, const QString & );
+
+#ifndef QT_NO_RICHTEXT
+    static void drawSimpleRichText( QPainter *, const QRectF &,
+        int flags, const QTextDocument & );
+#endif
+
+    static void drawRect( QPainter *, double x, double y, double w, double h );
+    static void drawRect( QPainter *, const QRectF &rect );
+    static void fillRect( QPainter *, const QRectF &, const QBrush & );
+
+    static void drawEllipse( QPainter *, const QRectF & );
+    static void drawPie( QPainter *, const QRectF & r, int a, int alen );
+
+    static void drawLine( QPainter *, double x1, double y1, double x2, double y2 );
+    static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 );
+    static void drawLine( QPainter *, const QLineF & );
+
+    static void drawPolygon( QPainter *, const QPolygonF & );
+    static void drawPolyline( QPainter *, const QPolygonF & );
+    static void drawPolyline( QPainter *, const QPointF *, int pointCount );
+
+    static void drawPolygon( QPainter *, const QPolygon & );
+    static void drawPolyline( QPainter *, const QPolygon & );
+    static void drawPolyline( QPainter *, const QPoint *, int pointCount );
+
+    static void drawPoint( QPainter *, const QPoint & );
+    static void drawPoints( QPainter *, const QPolygon & );
+    static void drawPoints( QPainter *, const QPoint *, int pointCount );
+
+    static void drawPoint( QPainter *, double x, double y );
+    static void drawPoint( QPainter *, const QPointF & );
+    static void drawPoints( QPainter *, const QPolygonF & );
+    static void drawPoints( QPainter *, const QPointF *, int pointCount );
+
+    static void drawPath( QPainter *, const QPainterPath & );
+    static void drawImage( QPainter *, const QRectF &, const QImage & );
+    static void drawPixmap( QPainter *, const QRectF &, const QPixmap & );
+
+    static void drawRoundFrame( QPainter *,
+        const QRectF &, const QPalette &, int lineWidth, int frameStyle );
+
+    static void drawRoundedFrame( QPainter *, 
+        const QRectF &, double xRadius, double yRadius,
+        const QPalette &, int lineWidth, int frameStyle );
+
+    static void drawFrame( QPainter *, const QRectF &rect,
+        const QPalette &palette, QPalette::ColorRole foregroundRole,
+        int lineWidth, int midLineWidth, int frameStyle ); 
+
+    static void drawFocusRect( QPainter *, const QWidget * );
+    static void drawFocusRect( QPainter *, const QWidget *, const QRect & );
+
+    static void drawColorBar( QPainter *painter,
+        const QwtColorMap &, const QwtInterval &,
+        const QwtScaleMap &, Qt::Orientation, const QRectF & );
+
+    static bool isAligning( QPainter *painter );
+    static bool isX11GraphicsSystem();
+
+    static void fillPixmap( const QWidget *, 
+        QPixmap &, const QPoint &offset = QPoint() );
+
+    static void drawBackgound( QPainter *painter,
+        const QRectF &rect, const QWidget *widget );
+
+    static QPixmap backingStore( QWidget *, const QSize & );
+
+private:
+    static bool d_polylineSplitting;
+    static bool d_roundingAlignment;
+};
+
+//!  Wrapper for QPainter::drawPoint()
+inline void QwtPainter::drawPoint( QPainter *painter, double x, double y )
+{
+    QwtPainter::drawPoint( painter, QPointF( x, y ) );
+}
+
+//! Wrapper for QPainter::drawPoints()
+inline void QwtPainter::drawPoints( QPainter *painter, const QPolygon &polygon )
+{
+    drawPoints( painter, polygon.data(), polygon.size() );
+}
+
+//! Wrapper for QPainter::drawPoints()
+inline void QwtPainter::drawPoints( QPainter *painter, const QPolygonF &polygon )
+{
+    drawPoints( painter, polygon.data(), polygon.size() );
+}
+
+//!  Wrapper for QPainter::drawLine()
+inline void QwtPainter::drawLine( QPainter *painter,
+    double x1, double y1, double x2, double y2 )
+{
+    QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) );
+}
+
+//!  Wrapper for QPainter::drawLine()
+inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line )
+{
+    QwtPainter::drawLine( painter, line.p1(), line.p2() );
+}
+
+/*!
+  \return True, when line splitting for the raster paint engine is enabled.
+  \sa setPolylineSplitting()
+*/
+inline bool QwtPainter::polylineSplitting()
+{
+    return d_polylineSplitting;
+}
+
+/*!
+  Check whether coordinates should be rounded, before they are painted
+  to a paint engine that rounds to integer values. For other paint engines
+  ( PDF, SVG ), this flag has no effect.
+
+  \return True, when rounding is enabled
+  \sa setRoundingAlignment(), isAligning()
+*/
+inline bool QwtPainter::roundingAlignment()
+{
+    return d_roundingAlignment;
+}
+
+/*!
+  \return roundingAlignment() && isAligning(painter);
+  \param painter Painter
+*/
+inline bool QwtPainter::roundingAlignment(QPainter *painter)
+{
+    return d_roundingAlignment && isAligning(painter);
+}
+#endif
diff --git a/qwt/qwt_painter_command.cpp b/qwt/qwt_painter_command.cpp
new file mode 100644
index 0000000..f6affae
--- /dev/null
+++ b/qwt/qwt_painter_command.cpp
@@ -0,0 +1,237 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_painter_command.h"
+
+//! Construct an invalid command
+QwtPainterCommand::QwtPainterCommand():
+    d_type( Invalid )
+{
+}
+
+//! Copy constructor
+QwtPainterCommand::QwtPainterCommand( const QPainterPath &path ):
+    d_type( Path )
+{
+    d_path = new QPainterPath( path );
+}
+
+/*!
+  Constructor for Pixmap paint operation
+
+  \param rect Target rectangle
+  \param pixmap Pixmap
+  \param subRect Rectangle inside the pixmap
+
+  \sa QPainter::drawPixmap()
+ */
+QwtPainterCommand::QwtPainterCommand( const QRectF &rect,
+        const QPixmap &pixmap, const QRectF& subRect ):
+    d_type( Pixmap )
+{
+    d_pixmapData = new PixmapData();
+    d_pixmapData->rect = rect;
+    d_pixmapData->pixmap = pixmap;
+    d_pixmapData->subRect = subRect;
+}
+
+/*!
+  Constructor for Image paint operation
+
+  \param rect Target rectangle
+  \param image Image
+  \param subRect Rectangle inside the image
+  \param flags Conversion flags
+
+  \sa QPainter::drawImage()
+ */
+QwtPainterCommand::QwtPainterCommand( const QRectF &rect,
+        const QImage &image, const QRectF& subRect,
+        Qt::ImageConversionFlags flags ):
+    d_type( Image )
+{
+    d_imageData = new ImageData();
+    d_imageData->rect = rect;
+    d_imageData->image = image;
+    d_imageData->subRect = subRect;
+    d_imageData->flags = flags;
+}
+
+/*! 
+  Constructor for State paint operation
+  \param state Paint engine state
+ */  
+QwtPainterCommand::QwtPainterCommand( const QPaintEngineState &state ):
+    d_type( State )
+{
+    d_stateData = new StateData();
+
+    d_stateData->flags = state.state();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyPen ) 
+        d_stateData->pen = state.pen();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBrush ) 
+        d_stateData->brush = state.brush();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBrushOrigin ) 
+        d_stateData->brushOrigin = state.brushOrigin();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyFont ) 
+        d_stateData->font = state.font();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBackground ) 
+    {
+        d_stateData->backgroundMode = state.backgroundMode();
+        d_stateData->backgroundBrush = state.backgroundBrush();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyTransform ) 
+        d_stateData->transform = state.transform();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipEnabled ) 
+        d_stateData->isClipEnabled = state.isClipEnabled();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipRegion ) 
+    {
+        d_stateData->clipRegion = state.clipRegion();
+        d_stateData->clipOperation = state.clipOperation();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipPath ) 
+    {
+        d_stateData->clipPath = state.clipPath();
+        d_stateData->clipOperation = state.clipOperation();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyHints ) 
+        d_stateData->renderHints = state.renderHints();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyCompositionMode ) 
+        d_stateData->compositionMode = state.compositionMode();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyOpacity ) 
+        d_stateData->opacity = state.opacity();
+}
+
+/*!
+  Copy constructor
+  \param other Command to be copied
+  
+ */
+QwtPainterCommand::QwtPainterCommand(const QwtPainterCommand &other)
+{
+    copy( other );
+}
+
+//! Destructor
+QwtPainterCommand::~QwtPainterCommand()
+{
+    reset();
+}
+
+/*!
+  Assignment operator
+
+  \param other Command to be copied
+  \return Modified command
+ */
+QwtPainterCommand &QwtPainterCommand::operator=(const QwtPainterCommand &other)
+{
+    reset();
+    copy( other );
+
+    return *this;
+}
+
+void QwtPainterCommand::copy( const QwtPainterCommand &other )
+{
+    d_type = other.d_type;
+
+    switch( other.d_type )
+    {
+        case Path:
+        {
+            d_path = new QPainterPath( *other.d_path );
+            break;
+        }
+        case Pixmap:
+        {
+            d_pixmapData = new PixmapData( *other.d_pixmapData );
+            break;
+        }
+        case Image:
+        {
+            d_imageData = new ImageData( *other.d_imageData );
+            break;
+        }
+        case State:
+        {
+            d_stateData = new StateData( *other.d_stateData );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+void QwtPainterCommand::reset()
+{
+    switch( d_type )
+    {
+        case Path:
+        {
+            delete d_path;
+            break;
+        }
+        case Pixmap:
+        {
+            delete d_pixmapData;
+            break;
+        }
+        case Image:
+        {
+            delete d_imageData;
+            break;
+        }
+        case State:
+        {
+            delete d_stateData;
+            break;
+        }
+        default:
+            break;
+    }
+
+    d_type = Invalid;
+}
+
+//! \return Painter path to be painted
+QPainterPath *QwtPainterCommand::path() 
+{
+    return d_path;
+}
+
+//! \return Attributes how to paint a QPixmap
+QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() 
+{
+    return d_pixmapData;
+}
+
+//! \return Attributes how to paint a QImage
+QwtPainterCommand::ImageData* QwtPainterCommand::imageData() 
+{
+    return d_imageData;
+}
+
+//! \return Attributes of a state change
+QwtPainterCommand::StateData* QwtPainterCommand::stateData() 
+{
+    return d_stateData;
+}
diff --git a/qwt/qwt_painter_command.h b/qwt/qwt_painter_command.h
new file mode 100644
index 0000000..2da597a
--- /dev/null
+++ b/qwt/qwt_painter_command.h
@@ -0,0 +1,173 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PAINTER_COMMAND_H
+#define QWT_PAINTER_COMMAND_H
+
+#include "qwt_global.h"
+#include <qpaintengine.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qpolygon.h>
+
+class QPainterPath;
+
+/*!
+  QwtPainterCommand represents the attributes of a paint operation
+  how it is used between QPainter and QPaintDevice
+
+  It is used by QwtGraphic to record and replay paint operations
+
+  \sa QwtGraphic::commands()
+ */
+  
+class QWT_EXPORT QwtPainterCommand
+{
+public:
+    //! Type of the paint command
+    enum Type
+    {
+        //! Invalid command
+        Invalid = -1,
+
+        //! Draw a QPainterPath
+        Path,
+
+        //! Draw a QPixmap
+        Pixmap,
+
+        //! Draw a QImage
+        Image,
+
+        //! QPainter state change
+        State
+    };
+
+    //! Attributes how to paint a QPixmap 
+    struct PixmapData
+    {
+        QRectF rect;
+        QPixmap pixmap;
+        QRectF subRect;
+    };
+
+    //! Attributes how to paint a QImage 
+    struct ImageData
+    {
+        QRectF rect;
+        QImage image;
+        QRectF subRect;
+        Qt::ImageConversionFlags flags;
+    };
+
+    //! Attributes of a state change
+    struct StateData
+    {
+        QPaintEngine::DirtyFlags flags;
+
+        QPen pen;
+        QBrush brush;
+        QPointF brushOrigin;
+        QBrush backgroundBrush;
+        Qt::BGMode backgroundMode;
+        QFont font;
+        QMatrix matrix;
+        QTransform transform;
+
+        Qt::ClipOperation clipOperation;
+        QRegion clipRegion;
+        QPainterPath clipPath;
+        bool isClipEnabled;
+
+        QPainter::RenderHints renderHints;
+        QPainter::CompositionMode compositionMode;
+        qreal opacity;
+    };
+
+    QwtPainterCommand();
+    QwtPainterCommand(const QwtPainterCommand &);
+
+    QwtPainterCommand( const QPainterPath & );
+
+    QwtPainterCommand( const QRectF &rect,
+            const QPixmap &, const QRectF& subRect );
+
+    QwtPainterCommand( const QRectF &rect,
+            const QImage &, const QRectF& subRect,
+            Qt::ImageConversionFlags );
+
+    QwtPainterCommand( const QPaintEngineState & );
+
+    ~QwtPainterCommand();
+
+    QwtPainterCommand &operator=(const QwtPainterCommand & );
+
+    Type type() const;
+
+    QPainterPath *path();
+    const QPainterPath *path() const;
+
+    PixmapData* pixmapData();
+    const PixmapData* pixmapData() const;
+
+    ImageData* imageData();
+    const ImageData* imageData() const;
+
+    StateData* stateData();
+    const StateData* stateData() const;
+
+private:
+    void copy( const QwtPainterCommand & );
+    void reset();
+
+    Type d_type;
+
+    union
+    {
+        QPainterPath *d_path;
+        PixmapData *d_pixmapData;
+        ImageData *d_imageData;
+        StateData *d_stateData;
+    };
+};
+
+//! \return Type of the command
+inline QwtPainterCommand::Type QwtPainterCommand::type() const
+{
+    return d_type;
+}
+
+//! \return Painter path to be painted
+inline const QPainterPath *QwtPainterCommand::path() const
+{
+    return d_path;
+}
+
+//! \return Attributes how to paint a QPixmap
+inline const QwtPainterCommand::PixmapData* 
+QwtPainterCommand::pixmapData() const
+{
+    return d_pixmapData;
+}
+
+//! \return Attributes how to paint a QImage
+inline const QwtPainterCommand::ImageData * 
+QwtPainterCommand::imageData() const
+{
+    return d_imageData;
+}
+
+//! \return Attributes of a state change
+inline const QwtPainterCommand::StateData * 
+QwtPainterCommand::stateData() const
+{
+    return d_stateData;
+}
+
+#endif
diff --git a/qwt/qwt_panner.cpp b/qwt/qwt_panner.cpp
new file mode 100644
index 0000000..18497a9
--- /dev/null
+++ b/qwt/qwt_panner.cpp
@@ -0,0 +1,538 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_panner.h"
+#include "qwt_picker.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <qcursor.h>
+#include <qbitmap.h>
+
+static QVector<QwtPicker *> qwtActivePickers( QWidget *w )
+{
+    QVector<QwtPicker *> pickers;
+
+    QObjectList children = w->children();
+    for ( int i = 0; i < children.size(); i++ )
+    {
+        QwtPicker *picker = qobject_cast<QwtPicker *>( children[i] );
+        if ( picker && picker->isEnabled() )
+            pickers += picker;
+    }
+
+    return pickers;
+}
+
+class QwtPanner::PrivateData
+{
+public:
+    PrivateData():
+        button( Qt::LeftButton ),
+        buttonModifiers( Qt::NoModifier ),
+        abortKey( Qt::Key_Escape ),
+        abortKeyModifiers( Qt::NoModifier ),
+#ifndef QT_NO_CURSOR
+        cursor( NULL ),
+        restoreCursor( NULL ),
+        hasCursor( false ),
+#endif
+        isEnabled( false )
+    {
+        orientations = Qt::Vertical | Qt::Horizontal;
+    }
+
+    ~PrivateData()
+    {
+#ifndef QT_NO_CURSOR
+        delete cursor;
+        delete restoreCursor;
+#endif
+    }
+
+    Qt::MouseButton button;
+    Qt::KeyboardModifiers  buttonModifiers;
+
+    int abortKey;
+    Qt::KeyboardModifiers abortKeyModifiers;
+
+    QPoint initialPos;
+    QPoint pos;
+
+    QPixmap pixmap;
+    QBitmap contentsMask;
+
+#ifndef QT_NO_CURSOR
+    QCursor *cursor;
+    QCursor *restoreCursor;
+    bool hasCursor;
+#endif
+    bool isEnabled;
+    Qt::Orientations orientations;
+};
+
+/*!
+  Creates an panner that is enabled for the left mouse button.
+
+  \param parent Parent widget to be panned
+*/
+QwtPanner::QwtPanner( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData();
+
+    setAttribute( Qt::WA_TransparentForMouseEvents );
+    setAttribute( Qt::WA_NoSystemBackground );
+    setFocusPolicy( Qt::NoFocus );
+    hide();
+
+    setEnabled( true );
+}
+
+//! Destructor
+QwtPanner::~QwtPanner()
+{
+    delete d_data;
+}
+
+/*!
+   Change the mouse button and modifiers used for panning
+   The defaults are Qt::LeftButton and Qt::NoModifier
+*/
+void QwtPanner::setMouseButton( Qt::MouseButton button,
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->button = button;
+    d_data->buttonModifiers = modifiers;
+}
+
+//! Get mouse button and modifiers used for panning
+void QwtPanner::getMouseButton( Qt::MouseButton &button,
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    button = d_data->button;
+    modifiers = d_data->buttonModifiers;
+}
+
+/*!
+   Change the abort key
+   The defaults are Qt::Key_Escape and Qt::NoModifiers
+
+   \param key Key ( See Qt::Keycode )
+   \param modifiers Keyboard modifiers
+*/
+void QwtPanner::setAbortKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->abortKey = key;
+    d_data->abortKeyModifiers = modifiers;
+}
+
+//! Get the abort key and modifiers
+void QwtPanner::getAbortKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->abortKey;
+    modifiers = d_data->abortKeyModifiers;
+}
+
+/*!
+   Change the cursor, that is active while panning
+   The default is the cursor of the parent widget.
+
+   \param cursor New cursor
+
+   \sa setCursor()
+*/
+#ifndef QT_NO_CURSOR
+void QwtPanner::setCursor( const QCursor &cursor )
+{
+    d_data->cursor = new QCursor( cursor );
+}
+#endif
+
+/*!
+   \return Cursor that is active while panning
+   \sa setCursor()
+*/
+#ifndef QT_NO_CURSOR
+const QCursor QwtPanner::cursor() const
+{
+    if ( d_data->cursor )
+        return *d_data->cursor;
+
+    if ( parentWidget() )
+        return parentWidget()->cursor();
+
+    return QCursor();
+}
+#endif
+
+/*!
+  \brief En/disable the panner
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPanner::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QWidget *w = parentWidget();
+        if ( w )
+        {
+            if ( d_data->isEnabled )
+            {
+                w->installEventFilter( this );
+            }
+            else
+            {
+                w->removeEventFilter( this );
+                hide();
+            }
+        }
+    }
+}
+
+/*!
+   Set the orientations, where panning is enabled
+   The default value is in both directions: Qt::Horizontal | Qt::Vertical
+
+   /param o Orientation
+*/
+void QwtPanner::setOrientations( Qt::Orientations o )
+{
+    d_data->orientations = o;
+}
+
+//! Return the orientation, where paning is enabled
+Qt::Orientations QwtPanner::orientations() const
+{
+    return d_data->orientations;
+}
+
+/*!
+   \return True if an orientation is enabled
+   \sa orientations(), setOrientations()
+*/
+bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const
+{
+    return d_data->orientations & o;
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled, eventFilter()
+*/
+bool QwtPanner::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+   \brief Paint event
+
+   Repaint the grabbed pixmap on its current position and
+   fill the empty spaces by the background of the parent widget.
+
+   \param pe Paint event
+*/
+void QwtPanner::paintEvent( QPaintEvent *pe )
+{
+    int dx = d_data->pos.x() - d_data->initialPos.x();
+    int dy = d_data->pos.y() - d_data->initialPos.y();
+
+    QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() );
+    r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) );
+
+    QPixmap pm( size() );
+    QwtPainter::fillPixmap( parentWidget(), pm );
+
+    QPainter painter( &pm );
+
+    if ( !d_data->contentsMask.isNull() )
+    {
+        QPixmap masked = d_data->pixmap;
+        masked.setMask( d_data->contentsMask );
+        painter.drawPixmap( r, masked );
+    }
+    else
+    {
+        painter.drawPixmap( r, d_data->pixmap );
+    }
+
+    painter.end();
+
+    if ( !d_data->contentsMask.isNull() )
+        pm.setMask( d_data->contentsMask );
+
+    painter.begin( this );
+    painter.setClipRegion( pe->region() );
+    painter.drawPixmap( 0, 0, pm );
+}
+
+/*!
+  \brief Calculate a mask for the contents of the panned widget
+
+  Sometimes only parts of the contents of a widget should be
+  panned. F.e. for a widget with a styled background with rounded borders
+  only the area inside of the border should be panned.
+
+  \return An empty bitmap, indicating no mask
+*/
+QBitmap QwtPanner::contentsMask() const
+{
+    return QBitmap();
+}
+
+/*!
+  Grab the widget into a pixmap.
+  \return Grabbed pixmap
+*/
+QPixmap QwtPanner::grab() const
+{
+#if QT_VERSION >= 0x050000
+    return parentWidget()->grab( parentWidget()->rect() );
+#else
+    return QPixmap::grabWidget( parentWidget() );
+#endif
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true mouse events of the
+  observed widget are filtered.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Always false, beside for paint events for the
+          parent widget.
+
+  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent()
+*/
+bool QwtPanner::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == NULL || object != parentWidget() )
+        return false;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::MouseMove:
+        {
+            widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+            break;
+        }
+        case QEvent::KeyRelease:
+        {
+            widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+            break;
+        }
+        case QEvent::Paint:
+        {
+            if ( isVisible() )
+                return true;
+            break;
+        }
+        default:;
+    }
+
+    return false;
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(),
+*/
+void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    if ( ( mouseEvent->button() != d_data->button )
+        || ( mouseEvent->modifiers() != d_data->buttonModifiers ) )
+    {
+        return;
+    }
+
+    QWidget *w = parentWidget();
+    if ( w == NULL )
+        return;
+
+#ifndef QT_NO_CURSOR
+    showCursor( true );
+#endif
+
+    d_data->initialPos = d_data->pos = mouseEvent->pos();
+
+    setGeometry( parentWidget()->rect() );
+
+    // We don't want to grab the picker !
+    QVector<QwtPicker *> pickers = qwtActivePickers( parentWidget() );
+    for ( int i = 0; i < pickers.size(); i++ )
+        pickers[i]->setEnabled( false );
+
+    d_data->pixmap = grab();
+    d_data->contentsMask = contentsMask();
+
+    for ( int i = 0; i < pickers.size(); i++ )
+        pickers[i]->setEnabled( true );
+
+    show();
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent()
+*/
+void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( !isVisible() )
+        return;
+
+    QPoint pos = mouseEvent->pos();
+    if ( !isOrientationEnabled( Qt::Horizontal ) )
+        pos.setX( d_data->initialPos.x() );
+    if ( !isOrientationEnabled( Qt::Vertical ) )
+        pos.setY( d_data->initialPos.y() );
+
+    if ( pos != d_data->pos && rect().contains( pos ) )
+    {
+        d_data->pos = pos;
+        update();
+
+        Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(),
+            d_data->pos.y() - d_data->initialPos.y() );
+    }
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(),
+      widgetMouseMoveEvent(),
+*/
+void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    if ( isVisible() )
+    {
+        hide();
+#ifndef QT_NO_CURSOR
+        showCursor( false );
+#endif
+
+        QPoint pos = mouseEvent->pos();
+        if ( !isOrientationEnabled( Qt::Horizontal ) )
+            pos.setX( d_data->initialPos.x() );
+        if ( !isOrientationEnabled( Qt::Vertical ) )
+            pos.setY( d_data->initialPos.y() );
+
+        d_data->pixmap = QPixmap();
+        d_data->contentsMask = QBitmap();
+        d_data->pos = pos;
+
+        if ( d_data->pos != d_data->initialPos )
+        {
+            Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(),
+                d_data->pos.y() - d_data->initialPos.y() );
+        }
+    }
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    if ( ( keyEvent->key() == d_data->abortKey )
+        && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) )
+    {
+        hide();
+
+#ifndef QT_NO_CURSOR
+        showCursor( false );
+#endif
+        d_data->pixmap = QPixmap();
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    Q_UNUSED( keyEvent );
+}
+
+#ifndef QT_NO_CURSOR
+void QwtPanner::showCursor( bool on )
+{
+    if ( on == d_data->hasCursor )
+        return;
+
+    QWidget *w = parentWidget();
+    if ( w == NULL || d_data->cursor == NULL )
+        return;
+
+    d_data->hasCursor = on;
+
+    if ( on )
+    {
+        if ( w->testAttribute( Qt::WA_SetCursor ) )
+        {
+            delete d_data->restoreCursor;
+            d_data->restoreCursor = new QCursor( w->cursor() );
+        }
+        w->setCursor( *d_data->cursor );
+    }
+    else
+    {
+        if ( d_data->restoreCursor )
+        {
+            w->setCursor( *d_data->restoreCursor );
+            delete d_data->restoreCursor;
+            d_data->restoreCursor = NULL;
+        }
+        else
+            w->unsetCursor();
+    }
+}
+#endif
diff --git a/qwt/qwt_panner.h b/qwt/qwt_panner.h
new file mode 100644
index 0000000..a0c6873
--- /dev/null
+++ b/qwt/qwt_panner.h
@@ -0,0 +1,103 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PANNER_H
+#define QWT_PANNER_H 1
+
+#include "qwt_global.h"
+#include <qwidget.h>
+#include <qpixmap.h>
+
+class QCursor;
+
+/*!
+  \brief QwtPanner provides panning of a widget
+
+  QwtPanner grabs the contents of a widget, that can be dragged
+  in all directions. The offset between the start and the end position
+  is emitted by the panned signal.
+
+  QwtPanner grabs the content of the widget into a pixmap and moves
+  the pixmap around, without initiating any repaint events for the widget.
+  Areas, that are not part of content are not painted  while panning.
+  This makes panning fast enough for widgets, where
+  repaints are too slow for mouse movements.
+
+  For widgets, where repaints are very fast it might be better to
+  implement panning manually by mapping mouse events into paint events.
+*/
+class QWT_EXPORT QwtPanner: public QWidget
+{
+    Q_OBJECT
+
+public:
+    QwtPanner( QWidget* parent );
+    virtual ~QwtPanner();
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    void setMouseButton( Qt::MouseButton, 
+        Qt::KeyboardModifiers = Qt::NoModifier );
+    void getMouseButton( Qt::MouseButton &button, 
+        Qt::KeyboardModifiers & ) const;
+
+    void setAbortKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getAbortKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    void setCursor( const QCursor & );
+    const QCursor cursor() const;
+
+    void setOrientations( Qt::Orientations );
+    Qt::Orientations orientations() const;
+
+    bool isOrientationEnabled( Qt::Orientation ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+Q_SIGNALS:
+    /*!
+      Signal emitted, when panning is done
+
+      \param dx Offset in horizontal direction
+      \param dy Offset in vertical direction
+    */
+    void panned( int dx, int dy );
+
+    /*!
+      Signal emitted, while the widget moved, but panning
+      is not finished.
+
+      \param dx Offset in horizontal direction
+      \param dy Offset in vertical direction
+    */
+    void moved( int dx, int dy );
+
+protected:
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+
+    virtual void paintEvent( QPaintEvent * );
+
+    virtual QBitmap contentsMask() const;
+    virtual QPixmap grab() const;
+
+private:
+#ifndef QT_NO_CURSOR
+    void showCursor( bool );
+#endif
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_picker.cpp b/qwt/qwt_picker.cpp
new file mode 100644
index 0000000..335ddf0
--- /dev/null
+++ b/qwt/qwt_picker.cpp
@@ -0,0 +1,1577 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_picker.h"
+#include "qwt_picker_machine.h"
+#include "qwt_painter.h"
+#include "qwt_math.h"
+#include "qwt_widget_overlay.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qframe.h>
+#include <qcursor.h>
+#include <qbitmap.h>
+#include <qpointer.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+
+static inline QRegion qwtMaskRegion( const QRect &r, int penWidth )
+{
+    const int pw = qMax( penWidth, 1 );
+    const int pw2 = penWidth / 2;
+
+    int x1 = r.left() - pw2;
+    int x2 = r.right() + 1 + pw2 + ( pw % 2 );
+
+    int y1 = r.top() - pw2;
+    int y2 = r.bottom() + 1 + pw2 + ( pw % 2 );
+
+    QRegion region;
+
+    region += QRect( x1, y1, x2 - x1, pw );
+    region += QRect( x1, y1, pw, y2 - y1 );
+    region += QRect( x1, y2 - pw, x2 - x1, pw );
+    region += QRect( x2 - pw, y1, pw, y2 - y1 );
+
+    return region;
+}
+
+static inline QRegion qwtMaskRegion( const QLine &l, int penWidth )
+{
+    const int pw = qMax( penWidth, 1 );
+    const int pw2 = penWidth / 2;
+
+    QRegion region;
+
+    if ( l.x1() == l.x2() )
+    {
+        region += QRect( l.x1() - pw2, l.y1(), 
+            pw, l.y2() ).normalized();
+    }
+    else if ( l.y1() == l.y2() )
+    {
+        region += QRect( l.x1(), l.y1() - pw2, 
+            l.x2(), pw ).normalized();
+    }
+
+    return region;
+}
+
+class QwtPickerRubberband: public QwtWidgetOverlay
+{
+public:
+    QwtPickerRubberband( QwtPicker *, QWidget * );
+
+protected:
+    virtual void drawOverlay( QPainter * ) const;
+    virtual QRegion maskHint() const;
+
+    QwtPicker *d_picker;
+};
+
+class QwtPickerTracker: public QwtWidgetOverlay
+{                                  
+public:
+    QwtPickerTracker( QwtPicker *, QWidget * );
+    
+protected:
+    virtual void drawOverlay( QPainter * ) const;
+    virtual QRegion maskHint() const;
+    
+    QwtPicker *d_picker;
+};  
+
+
+class QwtPicker::PrivateData
+{
+public:
+    PrivateData():
+        enabled( false ),
+        stateMachine( NULL ),
+        resizeMode( QwtPicker::Stretch ),
+        rubberBand( QwtPicker::NoRubberBand ),
+        trackerMode( QwtPicker::AlwaysOff ),
+        isActive( false ),
+        trackerPosition( -1, -1 ),
+        mouseTracking( false ),
+        openGL( false )
+    {
+    }
+        
+    bool enabled;
+
+    QwtPickerMachine *stateMachine;
+
+    QwtPicker::ResizeMode resizeMode;
+
+    QwtPicker::RubberBand rubberBand;
+    QPen rubberBandPen;
+
+    QwtPicker::DisplayMode trackerMode;
+    QPen trackerPen;
+    QFont trackerFont;
+
+    QPolygon pickedPoints;
+    bool isActive;
+    QPoint trackerPosition;
+
+    bool mouseTracking; // used to save previous value
+
+    QPointer< QwtPickerRubberband > rubberBandOverlay;
+    QPointer< QwtPickerTracker> trackerOverlay;
+
+    bool openGL;
+};
+
+QwtPickerRubberband::QwtPickerRubberband(
+        QwtPicker *picker, QWidget *parent ):
+    QwtWidgetOverlay( parent ),
+    d_picker( picker )
+{
+    setMaskMode( QwtWidgetOverlay::MaskHint );
+}
+
+QRegion QwtPickerRubberband::maskHint() const
+{
+    return d_picker->rubberBandMask();
+}
+
+void QwtPickerRubberband::drawOverlay( QPainter *painter ) const
+{
+    painter->setPen( d_picker->rubberBandPen() );
+    d_picker->drawRubberBand( painter );
+}
+
+QwtPickerTracker::QwtPickerTracker(
+        QwtPicker *picker, QWidget *parent ):
+    QwtWidgetOverlay( parent ),
+    d_picker( picker )
+{
+    setMaskMode( QwtWidgetOverlay::MaskHint );
+}
+
+QRegion QwtPickerTracker::maskHint() const
+{
+    return d_picker->trackerRect( font() );
+}
+
+void QwtPickerTracker::drawOverlay( QPainter *painter ) const
+{
+    painter->setPen( d_picker->trackerPen() );
+    d_picker->drawTracker( painter );
+}
+
+/*!
+  Constructor
+
+  Creates an picker that is enabled, but without a state machine.
+  rubber band and tracker are disabled.
+
+  \param parent Parent widget, that will be observed
+ */
+
+QwtPicker::QwtPicker( QWidget *parent ):
+    QObject( parent )
+{
+    init( parent, NoRubberBand, AlwaysOff );
+}
+
+/*!
+  Constructor
+
+  \param rubberBand Rubber band style
+  \param trackerMode Tracker mode
+  \param parent Parent widget, that will be observed
+ */
+QwtPicker::QwtPicker( RubberBand rubberBand,
+        DisplayMode trackerMode, QWidget *parent ):
+    QObject( parent )
+{
+    init( parent, rubberBand, trackerMode );
+}
+
+//! Destructor
+QwtPicker::~QwtPicker()
+{
+    setMouseTracking( false );
+
+    delete d_data->stateMachine;
+    delete d_data->rubberBandOverlay;
+    delete d_data->trackerOverlay;
+
+    delete d_data;
+}
+
+//! Initialize the picker - used by the constructors
+void QwtPicker::init( QWidget *parent,
+    RubberBand rubberBand, DisplayMode trackerMode )
+{
+    d_data = new PrivateData;
+
+    d_data->rubberBand = rubberBand;
+
+    if ( parent )
+    {
+        if ( parent->focusPolicy() == Qt::NoFocus )
+            parent->setFocusPolicy( Qt::WheelFocus );
+
+        d_data->openGL = parent->inherits( "QGLWidget" );
+        d_data->trackerFont = parent->font();
+        d_data->mouseTracking = parent->hasMouseTracking();
+
+        setEnabled( true );
+    }
+
+    setTrackerMode( trackerMode );
+}
+
+/*!
+  Set a state machine and delete the previous one
+
+  \param stateMachine State machine
+  \sa stateMachine()
+*/
+void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine )
+{
+    if ( d_data->stateMachine != stateMachine )
+    {
+        reset();
+
+        delete d_data->stateMachine;
+        d_data->stateMachine = stateMachine;
+
+        if ( d_data->stateMachine )
+            d_data->stateMachine->reset();
+    }
+}
+
+/*!
+  \return Assigned state machine
+  \sa setStateMachine()
+*/
+QwtPickerMachine *QwtPicker::stateMachine()
+{
+    return d_data->stateMachine;
+}
+
+/*!
+  \return Assigned state machine
+  \sa setStateMachine()
+*/
+const QwtPickerMachine *QwtPicker::stateMachine() const
+{
+    return d_data->stateMachine;
+}
+
+//! Return the parent widget, where the selection happens
+QWidget *QwtPicker::parentWidget()
+{
+    QObject *obj = parent();
+    if ( obj && obj->isWidgetType() )
+        return static_cast<QWidget *>( obj );
+
+    return NULL;
+}
+
+//! Return the parent widget, where the selection happens
+const QWidget *QwtPicker::parentWidget() const
+{
+    QObject *obj = parent();
+    if ( obj && obj->isWidgetType() )
+        return static_cast< const QWidget *>( obj );
+
+    return NULL;
+}
+
+/*!
+  Set the rubber band style
+
+  \param rubberBand Rubber band style
+         The default value is NoRubberBand.
+
+  \sa rubberBand(), RubberBand, setRubberBandPen()
+*/
+void QwtPicker::setRubberBand( RubberBand rubberBand )
+{
+    d_data->rubberBand = rubberBand;
+}
+
+/*!
+  \return Rubber band style
+  \sa setRubberBand(), RubberBand, rubberBandPen()
+*/
+QwtPicker::RubberBand QwtPicker::rubberBand() const
+{
+    return d_data->rubberBand;
+}
+
+/*!
+  \brief Set the display mode of the tracker.
+
+  A tracker displays information about current position of
+  the cursor as a string. The display mode controls
+  if the tracker has to be displayed whenever the observed
+  widget has focus and cursor (AlwaysOn), never (AlwaysOff), or
+  only when the selection is active (ActiveOnly).
+
+  \param mode Tracker display mode
+
+  \warning In case of AlwaysOn, mouseTracking will be enabled
+           for the observed widget.
+  \sa trackerMode(), DisplayMode
+*/
+
+void QwtPicker::setTrackerMode( DisplayMode mode )
+{
+    if ( d_data->trackerMode != mode )
+    {
+        d_data->trackerMode = mode;
+        setMouseTracking( d_data->trackerMode == AlwaysOn );
+    }
+}
+
+/*!
+  \return Tracker display mode
+  \sa setTrackerMode(), DisplayMode
+*/
+QwtPicker::DisplayMode QwtPicker::trackerMode() const
+{
+    return d_data->trackerMode;
+}
+
+/*!
+  \brief Set the resize mode.
+
+  The resize mode controls what to do with the selected points of an active
+  selection when the observed widget is resized.
+
+  Stretch means the points are scaled according to the new
+  size, KeepSize means the points remain unchanged.
+
+  The default mode is Stretch.
+
+  \param mode Resize mode
+  \sa resizeMode(), ResizeMode
+*/
+void QwtPicker::setResizeMode( ResizeMode mode )
+{
+    d_data->resizeMode = mode;
+}
+
+/*!
+  \return Resize mode
+  \sa setResizeMode(), ResizeMode
+*/
+
+QwtPicker::ResizeMode QwtPicker::resizeMode() const
+{
+    return d_data->resizeMode;
+}
+
+/*!
+  \brief En/disable the picker
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param enabled true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPicker::setEnabled( bool enabled )
+{
+    if ( d_data->enabled != enabled )
+    {
+        d_data->enabled = enabled;
+
+        QWidget *w = parentWidget();
+        if ( w )
+        {
+            if ( enabled )
+                w->installEventFilter( this );
+            else
+                w->removeEventFilter( this );
+        }
+
+        updateDisplay();
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled(), eventFilter()
+*/
+
+bool QwtPicker::isEnabled() const
+{
+    return d_data->enabled;
+}
+
+/*!
+  Set the font for the tracker
+
+  \param font Tracker font
+  \sa trackerFont(), setTrackerMode(), setTrackerPen()
+*/
+void QwtPicker::setTrackerFont( const QFont &font )
+{
+    if ( font != d_data->trackerFont )
+    {
+        d_data->trackerFont = font;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Tracker font
+  \sa setTrackerFont(), trackerMode(), trackerPen()
+*/
+
+QFont QwtPicker::trackerFont() const
+{
+    return d_data->trackerFont;
+}
+
+/*!
+  Set the pen for the tracker
+
+  \param pen Tracker pen
+  \sa trackerPen(), setTrackerMode(), setTrackerFont()
+*/
+void QwtPicker::setTrackerPen( const QPen &pen )
+{
+    if ( pen != d_data->trackerPen )
+    {
+        d_data->trackerPen = pen;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Tracker pen
+  \sa setTrackerPen(), trackerMode(), trackerFont()
+*/
+QPen QwtPicker::trackerPen() const
+{
+    return d_data->trackerPen;
+}
+
+/*!
+  Set the pen for the rubberband
+
+  \param pen Rubber band pen
+  \sa rubberBandPen(), setRubberBand()
+*/
+void QwtPicker::setRubberBandPen( const QPen &pen )
+{
+    if ( pen != d_data->rubberBandPen )
+    {
+        d_data->rubberBandPen = pen;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Rubber band pen
+  \sa setRubberBandPen(), rubberBand()
+*/
+QPen QwtPicker::rubberBandPen() const
+{
+    return d_data->rubberBandPen;
+}
+
+/*!
+   \brief Return the label for a position
+
+   In case of HLineRubberBand the label is the value of the
+   y position, in case of VLineRubberBand the value of the x position.
+   Otherwise the label contains x and y position separated by a ',' .
+
+   The format for the string conversion is "%d".
+
+   \param pos Position
+   \return Converted position as string
+*/
+
+QwtText QwtPicker::trackerText( const QPoint &pos ) const
+{
+    QString label;
+
+    switch ( rubberBand() )
+    {
+        case HLineRubberBand:
+            label.sprintf( "%d", pos.y() );
+            break;
+        case VLineRubberBand:
+            label.sprintf( "%d", pos.x() );
+            break;
+        default:
+            label.sprintf( "%d, %d", pos.x(), pos.y() );
+    }
+    return label;
+}
+
+/*!
+  Calculate the mask for the rubber band overlay
+
+  \return Region for the mask
+  \sa QWidget::setMask()
+ */
+QRegion QwtPicker::rubberBandMask() const
+{
+    QRegion mask;
+
+    if ( !isActive() || rubberBand() == NoRubberBand ||
+        rubberBandPen().style() == Qt::NoPen )
+    {
+        return mask;
+    }
+
+    const QPolygon pa = adjustedPoints( d_data->pickedPoints );
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( d_data->stateMachine )
+        selectionType = d_data->stateMachine->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::NoSelection:
+        case QwtPickerMachine::PointSelection:
+        {
+            if ( pa.count() < 1 )
+                return mask;
+
+            const QPoint pos = pa[0];
+            const int pw = rubberBandPen().width();
+
+            const QRect pRect = pickArea().boundingRect().toRect();
+            switch ( rubberBand() )
+            {
+                case VLineRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
+                        pos.x(), pRect.bottom() ), pw );
+                    break;
+                }
+                case HLineRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
+                        pRect.right(), pos.y() ), pw );
+                    break;
+                }
+                case CrossRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
+                        pos.x(), pRect.bottom() ), pw );
+                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
+                        pRect.right(), pos.y() ), pw );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( pa.count() < 2 )
+                return mask;
+
+            const int pw = rubberBandPen().width();
+
+            switch ( rubberBand() )
+            {
+                case RectRubberBand:
+                {
+                    const QRect r = QRect( pa.first(), pa.last() );
+                    mask = qwtMaskRegion( r.normalized(), pw );
+                    break;
+                }
+                case EllipseRubberBand:
+                {
+                    const QRect r = QRect( pa.first(), pa.last() );
+                    mask += r.adjusted( -pw, -pw, pw, pw );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            const int pw = rubberBandPen().width();
+            if ( pw <= 1 )
+            {
+                // because of the join style we better
+                // return a mask for a pen width <= 1 only
+
+                const int off = 2 * pw;
+                const QRect r = pa.boundingRect();
+                mask += r.adjusted( -off, -off, off, off );
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return mask;
+}
+
+/*!
+   Draw a rubber band, depending on rubberBand()
+
+   \param painter Painter, initialized with a clip region
+
+   \sa rubberBand(), RubberBand
+*/
+
+void QwtPicker::drawRubberBand( QPainter *painter ) const
+{
+    if ( !isActive() || rubberBand() == NoRubberBand ||
+        rubberBandPen().style() == Qt::NoPen )
+    {
+        return;
+    }
+
+    const QPolygon pa = adjustedPoints( d_data->pickedPoints );
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( d_data->stateMachine )
+        selectionType = d_data->stateMachine->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::NoSelection:
+        case QwtPickerMachine::PointSelection:
+        {
+            if ( pa.count() < 1 )
+                return;
+
+            const QPoint pos = pa[0];
+
+            const QRect pRect = pickArea().boundingRect().toRect();
+            switch ( rubberBand() )
+            {
+                case VLineRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pos.x(),
+                        pRect.top(), pos.x(), pRect.bottom() );
+                    break;
+                }
+                case HLineRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pRect.left(),
+                        pos.y(), pRect.right(), pos.y() );
+                    break;
+                }
+                case CrossRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pos.x(),
+                        pRect.top(), pos.x(), pRect.bottom() );
+                    QwtPainter::drawLine( painter, pRect.left(),
+                        pos.y(), pRect.right(), pos.y() );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( pa.count() < 2 )
+                return;
+
+            const QRect rect = QRect( pa.first(), pa.last() ).normalized();
+            switch ( rubberBand() )
+            {
+                case EllipseRubberBand:
+                {
+                    QwtPainter::drawEllipse( painter, rect );
+                    break;
+                }
+                case RectRubberBand:
+                {
+                    QwtPainter::drawRect( painter, rect );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            if ( rubberBand() == PolygonRubberBand )
+                painter->drawPolyline( pa );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+   Draw the tracker
+
+   \param painter Painter
+   \sa trackerRect(), trackerText()
+*/
+
+void QwtPicker::drawTracker( QPainter *painter ) const
+{
+    const QRect textRect = trackerRect( painter->font() );
+    if ( !textRect.isEmpty() )
+    {
+        const QwtText label = trackerText( d_data->trackerPosition );
+        if ( !label.isEmpty() )
+            label.draw( painter, textRect );
+    }
+}
+
+/*!
+   \brief Map the pickedPoints() into a selection()
+
+   adjustedPoints() maps the points, that have been collected on
+   the parentWidget() into a selection(). The default implementation
+   simply returns the points unmodified.
+
+   The reason, why a selection() differs from the picked points
+   depends on the application requirements. F.e. :
+
+   - A rectangular selection might need to have a specific aspect ratio only.\n
+   - A selection could accept non intersecting polygons only.\n
+   - ...\n
+
+   The example below is for a rectangular selection, where the first
+   point is the center of the selected rectangle.
+  \par Example
+  \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const
+{
+    QPolygon adjusted;
+    if ( points.size() == 2 )
+    {
+        const int width = qAbs(points[1].x() - points[0].x());
+        const int height = qAbs(points[1].y() - points[0].y());
+
+        QRect rect(0, 0, 2 * width, 2 * height);
+        rect.moveCenter(points[0]);
+
+        adjusted += rect.topLeft();
+        adjusted += rect.bottomRight();
+    }
+    return adjusted;
+}\endverbatim\n
+
+  \param points Selected points
+  \return Selected points unmodified
+*/
+QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const
+{
+    return points;
+}
+
+/*!
+  \return Selected points
+  \sa pickedPoints(), adjustedPoints()
+*/
+QPolygon QwtPicker::selection() const
+{
+    return adjustedPoints( d_data->pickedPoints );
+}
+
+//! \return Current position of the tracker
+QPoint QwtPicker::trackerPosition() const
+{
+    return d_data->trackerPosition;
+}
+
+/*!
+   Calculate the bounding rectangle for the tracker text
+   from the current position of the tracker
+
+   \param font Font of the tracker text
+   \return Bounding rectangle of the tracker text
+
+   \sa trackerPosition()
+*/
+QRect QwtPicker::trackerRect( const QFont &font ) const
+{
+    if ( trackerMode() == AlwaysOff ||
+        ( trackerMode() == ActiveOnly && !isActive() ) )
+    {
+        return QRect();
+    }
+
+    if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
+        return QRect();
+
+    QwtText text = trackerText( d_data->trackerPosition );
+    if ( text.isEmpty() )
+        return QRect();
+
+    const QSizeF textSize = text.textSize( font );
+    QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) );
+
+    const QPoint &pos = d_data->trackerPosition;
+
+    int alignment = 0;
+    if ( isActive() && d_data->pickedPoints.count() > 1
+        && rubberBand() != NoRubberBand )
+    {
+        const QPoint last =
+            d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2];
+
+        alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft;
+        alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop;
+    }
+    else
+        alignment = Qt::AlignTop | Qt::AlignRight;
+
+    const int margin = 5;
+
+    int x = pos.x();
+    if ( alignment & Qt::AlignLeft )
+        x -= textRect.width() + margin;
+    else if ( alignment & Qt::AlignRight )
+        x += margin;
+
+    int y = pos.y();
+    if ( alignment & Qt::AlignBottom )
+        y += margin;
+    else if ( alignment & Qt::AlignTop )
+        y -= textRect.height() + margin;
+
+    textRect.moveTopLeft( QPoint( x, y ) );
+
+    const QRect pickRect = pickArea().boundingRect().toRect();
+
+    int right = qMin( textRect.right(), pickRect.right() - margin );
+    int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin );
+    textRect.moveBottomRight( QPoint( right, bottom ) );
+
+    int left = qMax( textRect.left(), pickRect.left() + margin );
+    int top = qMax( textRect.top(), pickRect.top() + margin );
+    textRect.moveTopLeft( QPoint( left, top ) );
+
+    return textRect;
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true all events of the observed widget are filtered.
+  Mouse and keyboard events are translated into widgetMouse- and widgetKey-
+  and widgetWheel-events. Paint and Resize events are handled to keep
+  rubber band and tracker up to date.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Always false.
+
+  \sa widgetEnterEvent(), widgetLeaveEvent(),
+      widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(),
+      QObject::installEventFilter(), QObject::event()
+*/
+bool QwtPicker::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == parentWidget() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::Resize:
+            {
+                const QResizeEvent *re = static_cast<QResizeEvent *>( event );
+                if ( d_data->resizeMode == Stretch )
+                    stretchSelection( re->oldSize(), re->size() );
+
+                break;
+            }
+            case QEvent::Enter:
+            {
+                widgetEnterEvent( event );
+                break;
+            }
+            case QEvent::Leave:
+            {
+                widgetLeaveEvent( event );
+                break;
+            }
+            case QEvent::MouseButtonPress:
+            {
+                widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonRelease:
+            {
+                widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonDblClick:
+            {
+                widgetMouseDoubleClickEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseMove:
+            {
+                widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyPress:
+            {
+                widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyRelease:
+            {
+                widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::Wheel:
+            {
+                widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return false;
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( pickArea().contains( mouseEvent->pos() ) )
+        d_data->trackerPosition = mouseEvent->pos();
+    else
+        d_data->trackerPosition = QPoint( -1, -1 );
+
+    if ( !isActive() )
+        updateDisplay();
+
+    transition( mouseEvent );
+}
+
+/*!
+  Handle a enter event for the observed widget.
+
+  \param event Qt event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetEnterEvent( QEvent *event )
+{
+    transition( event );
+}
+
+/*!
+  Handle a leave event for the observed widget.
+
+  \param event Qt event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetLeaveEvent( QEvent *event )
+{
+    transition( event );
+
+    d_data->trackerPosition = QPoint( -1, -1 );
+    if ( !isActive() )
+        updateDisplay();
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+/*!
+  Handle mouse double click event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+
+/*!
+  Handle a wheel event for the observed widget.
+
+  Move the last point of the selection in case of isActive() == true
+
+  \param wheelEvent Wheel event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent )
+{
+    if ( pickArea().contains( wheelEvent->pos() ) )
+        d_data->trackerPosition = wheelEvent->pos();
+    else
+        d_data->trackerPosition = QPoint( -1, -1 );
+
+    updateDisplay();
+
+    transition( wheelEvent );
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  Selections can be completely done by the keyboard. The arrow keys
+  move the cursor, the abort key aborts a selection. All other keys
+  are handled by the current state machine.
+
+  \param keyEvent Key event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(),
+      QwtEventPattern::KeyPatternCode
+*/
+void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    int dx = 0;
+    int dy = 0;
+
+    int offset = 1;
+    if ( keyEvent->isAutoRepeat() )
+        offset = 5;
+
+    if ( keyMatch( KeyLeft, keyEvent ) )
+        dx = -offset;
+    else if ( keyMatch( KeyRight, keyEvent ) )
+        dx = offset;
+    else if ( keyMatch( KeyUp, keyEvent ) )
+        dy = -offset;
+    else if ( keyMatch( KeyDown, keyEvent ) )
+        dy = offset;
+    else if ( keyMatch( KeyAbort, keyEvent ) )
+    {
+        reset();
+    }
+    else
+        transition( keyEvent );
+
+    if ( dx != 0 || dy != 0 )
+    {
+        const QRect rect = pickArea().boundingRect().toRect();
+        const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() );
+
+        int x = pos.x() + dx;
+        x = qMax( rect.left(), x );
+        x = qMin( rect.right(), x );
+
+        int y = pos.y() + dy;
+        y = qMax( rect.top(), y );
+        y = qMin( rect.bottom(), y );
+
+        QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) );
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  Passes the event to the state machine.
+
+  \param keyEvent Key event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), stateMachine()
+*/
+void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    transition( keyEvent );
+}
+
+/*!
+  Passes an event to the state machine and executes the resulting
+  commands. Append and Move commands use the current position
+  of the cursor ( QCursor::pos() ).
+
+  \param event Event
+*/
+void QwtPicker::transition( const QEvent *event )
+{
+    if ( !d_data->stateMachine )
+        return;
+
+    const QList<QwtPickerMachine::Command> commandList =
+        d_data->stateMachine->transition( *this, event );
+
+    QPoint pos;
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonDblClick:
+        case QEvent::MouseButtonPress:
+        case QEvent::MouseButtonRelease:
+        case QEvent::MouseMove:
+        {
+            const QMouseEvent *me = 
+                static_cast< const QMouseEvent * >( event );
+            pos = me->pos();
+            break;
+        }
+        default:
+            pos = parentWidget()->mapFromGlobal( QCursor::pos() );
+    }
+
+    for ( int i = 0; i < commandList.count(); i++ )
+    {
+        switch ( commandList[i] )
+        {
+            case QwtPickerMachine::Begin:
+            {
+                begin();
+                break;
+            }
+            case QwtPickerMachine::Append:
+            {
+                append( pos );
+                break;
+            }
+            case QwtPickerMachine::Move:
+            {
+                move( pos );
+                break;
+            }
+            case QwtPickerMachine::Remove:
+            {
+                remove();
+                break;
+            }
+            case QwtPickerMachine::End:
+            {
+                end();
+                break;
+            }
+        }
+    }
+}
+
+/*!
+  Open a selection setting the state to active
+
+  \sa isActive(), end(), append(), move()
+*/
+void QwtPicker::begin()
+{
+    if ( d_data->isActive )
+        return;
+
+    d_data->pickedPoints.resize( 0 );
+    d_data->isActive = true;
+    Q_EMIT activated( true );
+
+    if ( trackerMode() != AlwaysOff )
+    {
+        if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
+        {
+            QWidget *w = parentWidget();
+            if ( w )
+                d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() );
+        }
+    }
+
+    updateDisplay();
+    setMouseTracking( true );
+}
+
+/*!
+  \brief Close a selection setting the state to inactive.
+
+  The selection is validated and maybe fixed by accept().
+
+  \param ok If true, complete the selection and emit a selected signal
+            otherwise discard the selection.
+  \return true if the selection is accepted, false otherwise
+  \sa isActive(), begin(), append(), move(), selected(), accept()
+*/
+bool QwtPicker::end( bool ok )
+{
+    if ( d_data->isActive )
+    {
+        setMouseTracking( false );
+
+        d_data->isActive = false;
+        Q_EMIT activated( false );
+
+        if ( trackerMode() == ActiveOnly )
+            d_data->trackerPosition = QPoint( -1, -1 );
+
+        if ( ok )
+            ok = accept( d_data->pickedPoints );
+
+        if ( ok )
+            Q_EMIT selected( d_data->pickedPoints );
+        else
+            d_data->pickedPoints.resize( 0 );
+
+        updateDisplay();
+    }
+    else
+        ok = false;
+
+    return ok;
+}
+
+/*!
+   Reset the state machine and terminate ( end(false) ) the selection
+*/
+void QwtPicker::reset()
+{
+    if ( d_data->stateMachine )
+        d_data->stateMachine->reset();
+
+    if ( isActive() )
+        end( false );
+}
+
+/*!
+  Append a point to the selection and update rubber band and tracker.
+  The appended() signal is emitted.
+
+  \param pos Additional point
+
+  \sa isActive(), begin(), end(), move(), appended()
+*/
+void QwtPicker::append( const QPoint &pos )
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count();
+        d_data->pickedPoints.resize( idx + 1 );
+        d_data->pickedPoints[idx] = pos;
+
+        updateDisplay();
+        Q_EMIT appended( pos );
+    }
+}
+
+/*!
+  Move the last point of the selection
+  The moved() signal is emitted.
+
+  \param pos New position
+  \sa isActive(), begin(), end(), append()
+*/
+void QwtPicker::move( const QPoint &pos )
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count() - 1;
+        if ( idx >= 0 )
+        {
+            if ( d_data->pickedPoints[idx] != pos )
+            {
+                d_data->pickedPoints[idx] = pos;
+
+                updateDisplay();
+                Q_EMIT moved( pos );
+            }
+        }
+    }
+}
+
+/*!
+  Remove the last point of the selection
+  The removed() signal is emitted.
+
+  \sa isActive(), begin(), end(), append(), move()
+*/
+void QwtPicker::remove()
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count() - 1;
+        if ( idx > 0 )
+        {
+            const int idx = d_data->pickedPoints.count();
+
+            const QPoint pos = d_data->pickedPoints[idx - 1];
+            d_data->pickedPoints.resize( idx - 1 );
+
+            updateDisplay();
+            Q_EMIT removed( pos );
+        }
+    }
+}
+
+/*!
+  \brief Validate and fix up the selection
+
+  Accepts all selections unmodified
+
+  \param selection Selection to validate and fix up
+  \return true, when accepted, false otherwise
+*/
+bool QwtPicker::accept( QPolygon &selection ) const
+{
+    Q_UNUSED( selection );
+    return true;
+}
+
+/*!
+  A picker is active between begin() and end().
+  \return true if the selection is active.
+*/
+bool QwtPicker::isActive() const
+{
+    return d_data->isActive;
+}
+
+/*!
+  Return the points, that have been collected so far. The selection()
+  is calculated from the pickedPoints() in adjustedPoints().
+  \return Picked points
+*/
+const QPolygon &QwtPicker::pickedPoints() const
+{
+    return d_data->pickedPoints;
+}
+
+/*!
+  Scale the selection by the ratios of oldSize and newSize
+  The changed() signal is emitted.
+
+  \param oldSize Previous size
+  \param newSize Current size
+
+  \sa ResizeMode, setResizeMode(), resizeMode()
+*/
+void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize )
+{
+    if ( oldSize.isEmpty() )
+    {
+        // avoid division by zero. But scaling for small sizes also
+        // doesn't make much sense, because of rounding losses. TODO ...
+        return;
+    }
+
+    const double xRatio =
+        double( newSize.width() ) / double( oldSize.width() );
+    const double yRatio =
+        double( newSize.height() ) / double( oldSize.height() );
+
+    for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ )
+    {
+        QPoint &p = d_data->pickedPoints[i];
+        p.setX( qRound( p.x() * xRatio ) );
+        p.setY( qRound( p.y() * yRatio ) );
+
+        Q_EMIT changed( d_data->pickedPoints );
+    }
+}
+
+/*!
+  Set mouse tracking for the observed widget.
+
+  In case of enable is true, the previous value
+  is saved, that is restored when enable is false.
+
+  \warning Even when enable is false, mouse tracking might be restored
+           to true. When mouseTracking for the observed widget
+           has been changed directly by QWidget::setMouseTracking
+           while mouse tracking has been set to true, this value can't
+           be restored.
+*/
+
+void QwtPicker::setMouseTracking( bool enable )
+{
+    QWidget *widget = parentWidget();
+    if ( !widget )
+        return;
+
+    if ( enable )
+    {
+        d_data->mouseTracking = widget->hasMouseTracking();
+        widget->setMouseTracking( true );
+    }
+    else
+    {
+        widget->setMouseTracking( d_data->mouseTracking );
+    }
+}
+
+/*!
+  Find the area of the observed widget, where selection might happen.
+
+  \return parentWidget()->contentsRect() 
+*/
+QPainterPath QwtPicker::pickArea() const
+{
+    QPainterPath path;
+
+    const QWidget *widget = parentWidget();
+    if ( widget )
+        path.addRect( widget->contentsRect() );
+
+    return path;
+}
+
+//! Update the state of rubber band and tracker label
+void QwtPicker::updateDisplay()
+{
+    QWidget *w = parentWidget();
+
+    bool showRubberband = false;
+    bool showTracker = false;
+
+    if ( w && w->isVisible() && d_data->enabled )
+    {
+        if ( rubberBand() != NoRubberBand && isActive() &&
+            rubberBandPen().style() != Qt::NoPen )
+        {
+            showRubberband = true;
+        }
+
+        if ( trackerMode() == AlwaysOn ||
+            ( trackerMode() == ActiveOnly && isActive() ) )
+        {
+            if ( trackerPen() != Qt::NoPen 
+                && !trackerRect( QFont() ).isEmpty() )
+            {
+                showTracker = true;
+            }
+        }
+    }
+
+    QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay;
+    if ( showRubberband )
+    {
+        if ( rw.isNull() )
+        {
+            rw = new QwtPickerRubberband( this, w );
+            rw->setObjectName( "PickerRubberBand" );
+            rw->resize( w->size() );
+        }
+
+        if ( d_data->rubberBand <= RectRubberBand )
+            rw->setMaskMode( QwtWidgetOverlay::MaskHint );
+        else
+            rw->setMaskMode( QwtWidgetOverlay::AlphaMask );
+
+        rw->updateOverlay();
+    }
+    else
+    {
+        if ( d_data->openGL )
+        {
+            // Qt 4.8 crashes for a delete
+            if ( !rw.isNull() )
+            {
+                rw->hide();
+                rw->deleteLater();
+                rw = NULL;
+            }
+        }
+        else
+        {
+            delete rw;
+        }
+    }
+
+    QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay;
+    if ( showTracker )
+    {
+        if ( tw.isNull() )
+        {
+            tw = new QwtPickerTracker( this, w );
+            tw->setObjectName( "PickerTracker" );
+            tw->resize( w->size() );
+        }
+        tw->setFont( d_data->trackerFont );
+        tw->updateOverlay();
+    }
+    else
+    {
+        if ( d_data->openGL )
+        {
+            // Qt 4.8 crashes for a delete
+            if ( !tw.isNull() )
+            {
+                tw->hide();
+                tw->deleteLater();
+                tw = NULL;
+            }
+        }
+        else
+        {
+            delete tw;
+        }
+    }
+}
+
+//! \return Overlay displaying the rubber band
+const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const
+{
+    return d_data->rubberBandOverlay;
+}
+
+//! \return Overlay displaying the tracker text
+const QwtWidgetOverlay *QwtPicker::trackerOverlay() const
+{
+    return d_data->trackerOverlay;
+}
+
diff --git a/qwt/qwt_picker.h b/qwt/qwt_picker.h
new file mode 100644
index 0000000..87d6805
--- /dev/null
+++ b/qwt/qwt_picker.h
@@ -0,0 +1,329 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PICKER
+#define QWT_PICKER 1
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include "qwt_event_pattern.h"
+#include <qobject.h>
+#include <qpen.h>
+#include <qfont.h>
+#include <qrect.h>
+#include <qpainterpath.h>
+
+class QWidget;
+class QMouseEvent;
+class QWheelEvent;
+class QKeyEvent;
+class QwtPickerMachine;
+class QwtWidgetOverlay;
+
+/*!
+  \brief QwtPicker provides selections on a widget
+
+  QwtPicker filters all enter, leave, mouse and keyboard events of a widget
+  and translates them into an array of selected points.
+
+  The way how the points are collected depends on type of state machine
+  that is connected to the picker. Qwt offers a couple of predefined
+  state machines for selecting:
+
+  - Nothing\n
+    QwtPickerTrackerMachine
+  - Single points\n
+    QwtPickerClickPointMachine, QwtPickerDragPointMachine
+  - Rectangles\n
+    QwtPickerClickRectMachine, QwtPickerDragRectMachine
+  - Polygons\n
+    QwtPickerPolygonMachine
+
+  While these state machines cover the most common ways to collect points
+  it is also possible to implement individual machines as well.
+
+  QwtPicker translates the picked points into a selection using the
+  adjustedPoints() method. adjustedPoints() is intended to be reimplemented
+  to fix up the selection according to application specific requirements.
+  (F.e. when an application accepts rectangles of a fixed aspect ratio only.)
+
+  Optionally QwtPicker support the process of collecting points by a
+  rubber band and tracker displaying a text for the current mouse
+  position.
+
+  \par Example
+  \verbatim #include <qwt_picker.h>
+#include <qwt_picker_machine.h>
+
+QwtPicker *picker = new QwtPicker(widget);
+picker->setStateMachine(new QwtPickerDragRectMachine);
+picker->setTrackerMode(QwtPicker::ActiveOnly);
+picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n
+
+  The state machine triggers the following commands:
+
+  - begin()\n
+    Activate/Initialize the selection.
+  - append()\n
+    Add a new point
+  - move() \n
+    Change the position of the last point.
+  - remove()\n
+    Remove the last point.
+  - end()\n
+    Terminate the selection and call accept to validate the picked points.
+
+  The picker is active (isActive()), between begin() and end().
+  In active state the rubber band is displayed, and the tracker is visible
+  in case of trackerMode is ActiveOnly or AlwaysOn.
+
+  The cursor can be moved using the arrow keys. All selections can be aborted
+  using the abort key. (QwtEventPattern::KeyPatternCode)
+
+  \warning In case of QWidget::NoFocus the focus policy of the observed
+           widget is set to QWidget::WheelFocus and mouse tracking
+           will be manipulated while the picker is active,
+           or if trackerMode() is AlwayOn.
+*/
+
+class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern
+{
+    Q_OBJECT
+
+    Q_ENUMS( RubberBand DisplayMode ResizeMode )
+
+    Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled )
+    Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode )
+
+    Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode )
+    Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen )
+    Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont )
+
+    Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand )
+    Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen )
+
+public:
+    /*!
+      Rubber band style
+
+      The default value is QwtPicker::NoRubberBand.
+      \sa setRubberBand(), rubberBand()
+    */
+
+    enum RubberBand
+    {
+        //! No rubberband.
+        NoRubberBand = 0,
+
+        //! A horizontal line ( only for QwtPickerMachine::PointSelection )
+        HLineRubberBand,
+
+        //! A vertical line ( only for QwtPickerMachine::PointSelection )
+        VLineRubberBand,
+
+        //! A crosshair ( only for QwtPickerMachine::PointSelection )
+        CrossRubberBand,
+
+        //! A rectangle ( only for QwtPickerMachine::RectSelection )
+        RectRubberBand,
+
+        //! An ellipse ( only for QwtPickerMachine::RectSelection )
+        EllipseRubberBand,
+
+        //! A polygon ( only for QwtPickerMachine::PolygonSelection )
+        PolygonRubberBand,
+
+        /*!
+          Values >= UserRubberBand can be used to define additional
+          rubber bands.
+         */
+        UserRubberBand = 100
+    };
+
+    /*!
+      \brief Display mode
+      \sa setTrackerMode(), trackerMode(), isActive()
+    */
+    enum DisplayMode
+    {
+        //! Display never
+        AlwaysOff,
+
+        //! Display always
+        AlwaysOn,
+
+        //! Display only when the selection is active
+        ActiveOnly
+    };
+
+    /*!
+      Controls what to do with the selected points of an active
+         selection when the observed widget is resized.
+
+      The default value is QwtPicker::Stretch.
+      \sa setResizeMode()
+    */
+
+    enum ResizeMode
+    {
+        //! All points are scaled according to the new size,
+        Stretch,
+
+        //! All points remain unchanged.
+        KeepSize
+    };
+
+    explicit QwtPicker( QWidget *parent );
+    explicit QwtPicker( RubberBand rubberBand,
+                        DisplayMode trackerMode, QWidget * );
+
+    virtual ~QwtPicker();
+
+    void setStateMachine( QwtPickerMachine * );
+    const QwtPickerMachine *stateMachine() const;
+    QwtPickerMachine *stateMachine();
+
+    void setRubberBand( RubberBand );
+    RubberBand rubberBand() const;
+
+    void setTrackerMode( DisplayMode );
+    DisplayMode trackerMode() const;
+
+    void setResizeMode( ResizeMode );
+    ResizeMode resizeMode() const;
+
+    void setRubberBandPen( const QPen & );
+    QPen rubberBandPen() const;
+
+    void setTrackerPen( const QPen & );
+    QPen trackerPen() const;
+
+    void setTrackerFont( const QFont & );
+    QFont trackerFont() const;
+
+    bool isEnabled() const;
+    bool isActive() const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    QWidget *parentWidget();
+    const QWidget *parentWidget() const;
+
+    virtual QPainterPath pickArea() const;
+
+    virtual void drawRubberBand( QPainter * ) const;
+    virtual void drawTracker( QPainter * ) const;
+
+    virtual QRegion rubberBandMask() const;
+
+    virtual QwtText trackerText( const QPoint &pos ) const;
+    QPoint trackerPosition() const;
+    virtual QRect trackerRect( const QFont & ) const;
+
+    QPolygon selection() const;
+
+public Q_SLOTS:
+    void setEnabled( bool );
+
+Q_SIGNALS:
+    /*!
+      A signal indicating, when the picker has been activated.
+      Together with setEnabled() it can be used to implement
+      selections with more than one picker.
+
+      \param on True, when the picker has been activated
+    */
+    void activated( bool on );
+
+    /*!
+      A signal emitting the selected points,
+      at the end of a selection.
+
+      \param polygon Selected points
+    */
+    void selected( const QPolygon &polygon );
+
+    /*!
+      A signal emitted when a point has been appended to the selection
+
+      \param pos Position of the appended point.
+      \sa append(). moved()
+    */
+    void appended( const QPoint &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been moved.
+
+      \param pos Position of the moved last point of the selection.
+      \sa move(), appended()
+    */
+    void moved( const QPoint &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been removed.
+
+      \param pos Position of the point, that has been removed
+      \sa remove(), appended()
+    */
+    void removed( const QPoint &pos );
+    /*!
+      A signal emitted when the active selection has been changed.
+      This might happen when the observed widget is resized.
+
+      \param selection Changed selection
+      \sa stretchSelection()
+    */
+    void changed( const QPolygon &selection );
+
+protected:
+    virtual QPolygon adjustedPoints( const QPolygon & ) const;
+
+    virtual void transition( const QEvent * );
+
+    virtual void begin();
+    virtual void append( const QPoint & );
+    virtual void move( const QPoint & );
+    virtual void remove();
+    virtual bool end( bool ok = true );
+
+    virtual bool accept( QPolygon & ) const;
+    virtual void reset();
+
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseDoubleClickEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetWheelEvent( QWheelEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+    virtual void widgetEnterEvent( QEvent * );
+    virtual void widgetLeaveEvent( QEvent * );
+
+    virtual void stretchSelection( const QSize &oldSize,
+                                   const QSize &newSize );
+
+    virtual void updateDisplay();
+
+    const QwtWidgetOverlay *rubberBandOverlay() const;
+    const QwtWidgetOverlay *trackerOverlay() const;
+
+    const QPolygon &pickedPoints() const;
+
+private:
+    void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode );
+
+    void setMouseTracking( bool );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_picker_machine.cpp b/qwt/qwt_picker_machine.cpp
new file mode 100644
index 0000000..299624e
--- /dev/null
+++ b/qwt/qwt_picker_machine.cpp
@@ -0,0 +1,526 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_picker_machine.h"
+#include "qwt_event_pattern.h"
+#include <qevent.h>
+
+//! Constructor
+QwtPickerMachine::QwtPickerMachine( SelectionType type ):
+    d_selectionType( type ),
+    d_state( 0 )
+{
+}
+
+//! Destructor
+QwtPickerMachine::~QwtPickerMachine()
+{
+}
+
+//! Return the selection type
+QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const
+{
+    return d_selectionType;
+}
+
+//! Return the current state
+int QwtPickerMachine::state() const
+{
+    return d_state;
+}
+
+//! Change the current state
+void QwtPickerMachine::setState( int state )
+{
+    d_state = state;
+}
+
+//! Set the current state to 0.
+void QwtPickerMachine::reset()
+{
+    setState( 0 );
+}
+
+//! Constructor
+QwtPickerTrackerMachine::QwtPickerTrackerMachine():
+    QwtPickerMachine( NoSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerTrackerMachine::transition(
+    const QwtEventPattern &, const QEvent *e )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( e->type() )
+    {
+        case QEvent::Enter:
+        case QEvent::MouseMove:
+        {
+            if ( state() == 0 )
+            {
+                cmdList += Begin;
+                cmdList += Append;
+                setState( 1 );
+            }
+            else
+            {
+                cmdList += Move;
+            }
+            break;
+        }
+        case QEvent::Leave:
+        {
+            cmdList += Remove;
+            cmdList += End;
+            setState( 0 );
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerClickPointMachine::QwtPickerClickPointMachine():
+    QwtPickerMachine( PointSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerClickPointMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                cmdList += Begin;
+                cmdList += Append;
+                cmdList += End;
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                cmdList += Begin;
+                cmdList += Append;
+                cmdList += End;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragPointMachine::QwtPickerDragPointMachine():
+    QwtPickerMachine( PointSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragPointMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() != 0 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerClickRectMachine::QwtPickerClickRectMachine():
+    QwtPickerMachine( RectSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerClickRectMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                switch ( state() )
+                {
+                    case 0:
+                    {
+                        cmdList += Begin;
+                        cmdList += Append;
+                        setState( 1 );
+                        break;
+                    }
+                    case 1:
+                    {
+                        // Uh, strange we missed the MouseButtonRelease
+                        break;
+                    }
+                    default:
+                    {
+                        cmdList += End;
+                        setState( 0 );
+                    }
+                }
+            }
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 1 )
+                {
+                    cmdList += Append;
+                    setState( 2 );
+                }
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    if ( state() == 1 )
+                    {
+                        cmdList += Append;
+                        setState( 2 );
+                    }
+                    else if ( state() == 2 )
+                    {
+                        cmdList += End;
+                        setState( 0 );
+                    }
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragRectMachine::QwtPickerDragRectMachine():
+    QwtPickerMachine( RectSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragRectMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 2 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() == 2 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 2 );
+                }
+                else
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerPolygonMachine::QwtPickerPolygonMachine():
+    QwtPickerMachine( PolygonSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerPolygonMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += Append;
+                }
+            }
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect2, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 1 )
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += Append;
+                }
+            }
+            else if ( eventPattern.keyMatch( QwtEventPattern::KeySelect2, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 1 )
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragLineMachine::QwtPickerDragLineMachine():
+    QwtPickerMachine( PolygonSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragLineMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1,
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1,
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() != 0 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
diff --git a/qwt/qwt_picker_machine.h b/qwt/qwt_picker_machine.h
new file mode 100644
index 0000000..6164b93
--- /dev/null
+++ b/qwt/qwt_picker_machine.h
@@ -0,0 +1,214 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PICKER_MACHINE
+#define QWT_PICKER_MACHINE 1
+
+#include "qwt_global.h"
+#include <qlist.h>
+
+class QEvent;
+class QwtEventPattern;
+
+/*!
+  \brief A state machine for QwtPicker selections
+
+  QwtPickerMachine accepts key and mouse events and translates them
+  into selection commands.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerMachine
+{
+public:
+    /*!
+      Type of a selection.
+      \sa selectionType()
+    */
+    enum SelectionType
+    {
+        //! The state machine not usable for any type of selection.
+        NoSelection = -1,
+
+        //! The state machine is for selecting a single point.
+        PointSelection,
+
+        //! The state machine is for selecting a rectangle (2 points).
+        RectSelection,
+
+        //! The state machine is for selecting a polygon (many points).
+        PolygonSelection
+    };
+
+    //! Commands - the output of a state machine
+    enum Command
+    {
+        Begin,
+        Append,
+        Move,
+        Remove,
+        End
+    };
+
+    QwtPickerMachine( SelectionType );
+    virtual ~QwtPickerMachine();
+
+    //! Transition
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * ) = 0;
+    void reset();
+
+    int state() const;
+    void setState( int );
+
+    SelectionType selectionType() const;
+
+private:
+    const SelectionType d_selectionType;
+    int d_state;
+};
+
+/*!
+  \brief A state machine for indicating mouse movements
+
+  QwtPickerTrackerMachine supports displaying information
+  corresponding to mouse movements, but is not intended for
+  selecting anything. Begin/End are related to Enter/Leave events.
+*/
+class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerTrackerMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for point selections
+
+  Pressing QwtEventPattern::MouseSelect1 or
+  QwtEventPattern::KeySelect1 selects a point.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerClickPointMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for point selections
+
+  Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1
+  starts the selection, releasing QwtEventPattern::MouseSelect1 or
+  a second press of QwtEventPattern::KeySelect1 terminates it.
+*/
+class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragPointMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for rectangle selections
+
+  Pressing QwtEventPattern::MouseSelect1 starts
+  the selection, releasing it selects the first point. Pressing it
+  again selects the second point and terminates the selection.
+  Pressing QwtEventPattern::KeySelect1 also starts the
+  selection, a second press selects the first point. A third one selects
+  the second point and terminates the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerClickRectMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for rectangle selections
+
+  Pressing QwtEventPattern::MouseSelect1 selects
+  the first point, releasing it the second point.
+  Pressing QwtEventPattern::KeySelect1 also selects the
+  first point, a second press selects the second point and terminates
+  the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragRectMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for line selections
+    
+  Pressing QwtEventPattern::MouseSelect1 selects
+  the first point, releasing it the second point.
+  Pressing QwtEventPattern::KeySelect1 also selects the
+  first point, a second press selects the second point and terminates
+  the selection.
+
+  A common use case of QwtPickerDragLineMachine are pickers for
+  distance measurements.
+  
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/              
+                    
+class QWT_EXPORT QwtPickerDragLineMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragLineMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for polygon selections
+
+  Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1
+  starts the selection and selects the first point, or appends a point.
+  Pressing QwtEventPattern::MouseSelect2 or QwtEventPattern::KeySelect2
+  appends the last point and terminates the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerPolygonMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+#endif
diff --git a/qwt/qwt_pixel_matrix.cpp b/qwt/qwt_pixel_matrix.cpp
new file mode 100644
index 0000000..627992c
--- /dev/null
+++ b/qwt/qwt_pixel_matrix.cpp
@@ -0,0 +1,51 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_pixel_matrix.h"
+
+/*!
+  \brief Constructor
+
+  \param rect Bounding rectangle for the matrix
+*/
+QwtPixelMatrix::QwtPixelMatrix( const QRect& rect ):
+    QBitArray( qMax( rect.width() * rect.height(), 0 ) ),
+    d_rect( rect )
+{
+}
+
+//! Destructor
+QwtPixelMatrix::~QwtPixelMatrix()
+{
+}
+
+/*!
+    Set the bounding rectangle of the matrix
+
+    \param rect Bounding rectangle
+
+    \note All bits are cleared
+ */
+void QwtPixelMatrix::setRect( const QRect& rect )
+{
+    if ( rect != d_rect )
+    {
+        d_rect = rect;
+        const int sz = qMax( rect.width() * rect.height(), 0 );
+        resize( sz );
+    }
+
+    fill( false );
+}
+
+//! \return Bounding rectangle
+QRect QwtPixelMatrix::rect() const
+{
+    return d_rect;
+}
diff --git a/qwt/qwt_pixel_matrix.h b/qwt/qwt_pixel_matrix.h
new file mode 100644
index 0000000..0fe9daf
--- /dev/null
+++ b/qwt/qwt_pixel_matrix.h
@@ -0,0 +1,98 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PIXEL_MATRIX_H
+#define QWT_PIXEL_MATRIX_H
+
+#include "qwt_global.h"
+#include <qbitarray.h>
+#include <qrect.h>
+
+/*!
+  \brief A bit field corresponding to the pixels of a rectangle
+
+  QwtPixelMatrix is intended to filter out duplicates in an
+  unsorted array of points.
+*/
+class QWT_EXPORT QwtPixelMatrix: public QBitArray
+{
+public:
+    QwtPixelMatrix( const QRect& rect );
+    ~QwtPixelMatrix();
+
+    void setRect( const QRect& rect );
+    QRect rect() const;
+
+    bool testPixel( int x, int y ) const;
+    bool testAndSetPixel( int x, int y, bool on );
+
+    int index( int x, int y ) const;
+
+private:
+    QRect d_rect;
+};
+
+/*!
+  \brief Test if a pixel has been set
+
+  \param x X-coordinate
+  \param y Y-coordinate
+
+  \return true, when pos is outside of rect(), or when the pixel
+          has already been set.
+ */
+inline bool QwtPixelMatrix::testPixel( int x, int y ) const
+{
+    const int idx = index( x, y );
+    return ( idx >= 0 ) ? testBit( idx ) : true;
+}
+
+/*!
+  \brief Set a pixel and test if a pixel has been set before
+
+  \param x X-coordinate
+  \param y Y-coordinate
+  \param on Set/Clear the pixel
+
+  \return true, when pos is outside of rect(), or when the pixel
+          was set before.
+ */
+inline bool QwtPixelMatrix::testAndSetPixel( int x, int y, bool on )
+{
+    const int idx = index( x, y );
+    if ( idx < 0 )
+        return true;
+
+    const bool onBefore = testBit( idx );
+    setBit( idx, on );
+
+    return onBefore;
+}
+
+/*!
+  \brief Calculate the index in the bit field corresponding to a position
+
+  \param x X-coordinate
+  \param y Y-coordinate
+  \return Index, when rect() contains pos - otherwise -1.
+ */
+inline int QwtPixelMatrix::index( int x, int y ) const
+{
+    const int dx = x - d_rect.x();
+    if ( dx < 0 || dx >= d_rect.width() )
+        return -1;
+
+    const int dy = y - d_rect.y();
+    if ( dy < 0 || dy >= d_rect.height() )
+        return -1;
+
+    return dy * d_rect.width() + dx;
+}
+
+#endif
diff --git a/qwt/qwt_plot.cpp b/qwt/qwt_plot.cpp
new file mode 100644
index 0000000..dc63a3b
--- /dev/null
+++ b/qwt/qwt_plot.cpp
@@ -0,0 +1,1163 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot.h"
+#include "qwt_plot_dict.h"
+#include "qwt_plot_layout.h"
+#include "qwt_scale_widget.h"
+#include "qwt_scale_engine.h"
+#include "qwt_text_label.h"
+#include "qwt_legend.h"
+#include "qwt_legend_data.h"
+#include "qwt_plot_canvas.h"
+#include <qmath.h>
+#include <qpainter.h>
+#include <qpointer.h>
+#include <qpaintengine.h>
+#include <qapplication.h>
+#include <qevent.h>
+
+static inline void qwtEnableLegendItems( QwtPlot *plot, bool on )
+{
+    if ( on )
+    {
+        QObject::connect( 
+            plot, SIGNAL( legendDataChanged(
+                const QVariant &, const QList<QwtLegendData> & ) ),
+            plot, SLOT( updateLegendItems( 
+                const QVariant &, const QList<QwtLegendData> & ) ) );
+    }
+    else
+    {
+        QObject::disconnect( 
+            plot, SIGNAL( legendDataChanged(
+                const QVariant &, const QList<QwtLegendData> & ) ),
+            plot, SLOT( updateLegendItems( 
+                const QVariant &, const QList<QwtLegendData> & ) ) );
+    }
+}
+
+static void qwtSetTabOrder( 
+    QWidget *first, QWidget *second, bool withChildren )
+{
+    QList<QWidget *> tabChain;
+    tabChain += first;
+    tabChain += second;
+
+    if ( withChildren )
+    {
+        QList<QWidget *> children = second->findChildren<QWidget *>();
+
+        QWidget *w = second->nextInFocusChain();
+        while ( children.contains( w ) )
+        {
+            children.removeAll( w );
+
+            tabChain += w;
+            w = w->nextInFocusChain();
+        }
+    }
+
+    for ( int i = 0; i < tabChain.size() - 1; i++ )
+    {
+        QWidget *from = tabChain[i];
+        QWidget *to = tabChain[i+1];
+
+        const Qt::FocusPolicy policy1 = from->focusPolicy();
+        const Qt::FocusPolicy policy2 = to->focusPolicy();
+
+        QWidget *proxy1 = from->focusProxy();
+        QWidget *proxy2 = to->focusProxy();
+
+        from->setFocusPolicy( Qt::TabFocus );
+        from->setFocusProxy( NULL);
+
+        to->setFocusPolicy( Qt::TabFocus );
+        to->setFocusProxy( NULL);
+
+        QWidget::setTabOrder( from, to );
+
+        from->setFocusPolicy( policy1 );
+        from->setFocusProxy( proxy1);
+
+        to->setFocusPolicy( policy2 );
+        to->setFocusProxy( proxy2 );
+    }
+}
+
+class QwtPlot::PrivateData
+{
+public:
+    QPointer<QwtTextLabel> titleLabel;
+    QPointer<QwtTextLabel> footerLabel;
+    QPointer<QWidget> canvas;
+    QPointer<QwtAbstractLegend> legend;
+    QwtPlotLayout *layout;
+
+    bool autoReplot;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+ */
+QwtPlot::QwtPlot( QWidget *parent ):
+    QFrame( parent )
+{
+    initPlot( QwtText() );
+}
+
+/*!
+  \brief Constructor
+  \param title Title text
+  \param parent Parent widget
+ */
+QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
+    QFrame( parent )
+{
+    initPlot( title );
+}
+
+//! Destructor
+QwtPlot::~QwtPlot()
+{
+    detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
+
+    delete d_data->layout;
+    deleteAxesData();
+    delete d_data;
+}
+
+/*!
+  \brief Initializes a QwtPlot instance
+  \param title Title text
+ */
+void QwtPlot::initPlot( const QwtText &title )
+{
+    d_data = new PrivateData;
+
+    d_data->layout = new QwtPlotLayout;
+    d_data->autoReplot = false;
+
+    // title
+    d_data->titleLabel = new QwtTextLabel( this );
+    d_data->titleLabel->setObjectName( "QwtPlotTitle" );
+    d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
+
+    QwtText text( title );
+    text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
+    d_data->titleLabel->setText( text );
+
+    // footer
+    d_data->footerLabel = new QwtTextLabel( this );
+    d_data->footerLabel->setObjectName( "QwtPlotFooter" );
+
+    QwtText footer;
+    footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
+    d_data->footerLabel->setText( footer );
+
+    // legend
+    d_data->legend = NULL;
+
+    // axis
+    initAxesData();
+
+    // canvas
+    d_data->canvas = new QwtPlotCanvas( this );
+    d_data->canvas->setObjectName( "QwtPlotCanvas" );
+    d_data->canvas->installEventFilter( this );
+
+    setSizePolicy( QSizePolicy::MinimumExpanding,
+        QSizePolicy::MinimumExpanding );
+
+    resize( 200, 200 );
+
+    QList<QWidget *> focusChain;
+    focusChain << this << d_data->titleLabel << axisWidget( xTop )
+        << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight )
+        << axisWidget( xBottom ) << d_data->footerLabel;
+
+    for ( int i = 0; i < focusChain.size() - 1; i++ )
+        qwtSetTabOrder( focusChain[i], focusChain[i+1], false );
+
+    qwtEnableLegendItems( this, true );
+}
+
+/*!
+  \brief Set the drawing canvas of the plot widget
+
+  QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ).
+  In opposite to using conventional C++ techniques like virtual methods
+  they allow to use canvas implementations that are derived from 
+  QWidget or QGLWidget.
+
+  The following meta methods could be implemented:
+
+  - replot()
+    When the canvas doesn't offer a replot method, QwtPlot calls
+    update() instead.
+
+  - borderPath()
+    The border path is necessary to clip the content of the canvas
+    When the canvas doesn't have any special border ( f.e rounded corners )
+    it is o.k. not to implement this method.
+
+  The default canvas is a QwtPlotCanvas 
+
+  \param canvas Canvas Widget
+  \sa canvas()
+ */
+void QwtPlot::setCanvas( QWidget *canvas )
+{
+    if ( canvas == d_data->canvas )
+        return;
+
+    delete d_data->canvas;
+    d_data->canvas = canvas;
+
+    if ( canvas )
+    {
+        canvas->setParent( this );
+        canvas->installEventFilter( this );
+
+        if ( isVisible() )
+            canvas->show();
+    }
+}
+
+/*!
+  \brief Adds handling of layout requests
+  \param event Event
+
+  \return See QFrame::event()
+*/
+bool QwtPlot::event( QEvent *event )
+{
+    bool ok = QFrame::event( event );
+    switch ( event->type() )
+    {
+        case QEvent::LayoutRequest:
+            updateLayout();
+            break;
+        case QEvent::PolishRequest:
+            replot();
+            break;
+        default:;
+    }
+    return ok;
+}
+
+/*!
+  \brief Event filter
+
+  The plot handles the following events for the canvas:
+
+  - QEvent::Resize
+    The canvas margins might depend on its size
+
+  - QEvent::ContentsRectChange
+    The layout needs to be recalculated
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return See QFrame::eventFilter()
+
+  \sa updateCanvasMargins(), updateLayout()
+*/
+bool QwtPlot::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == d_data->canvas )
+    {
+        if ( event->type() == QEvent::Resize )
+        {
+            updateCanvasMargins();
+        }
+        else if ( event->type() == QEvent::ContentsRectChange )
+        {
+            updateLayout();
+        }
+    }
+
+    return QFrame::eventFilter( object, event );
+}
+
+//! Replots the plot if autoReplot() is \c true.
+void QwtPlot::autoRefresh()
+{
+    if ( d_data->autoReplot )
+        replot();
+}
+
+/*!
+  \brief Set or reset the autoReplot option
+
+  If the autoReplot option is set, the plot will be
+  updated implicitly by manipulating member functions.
+  Since this may be time-consuming, it is recommended
+  to leave this option switched off and call replot()
+  explicitly if necessary.
+
+  The autoReplot option is set to false by default, which
+  means that the user has to call replot() in order to make
+  changes visible.
+  \param tf \c true or \c false. Defaults to \c true.
+  \sa replot()
+*/
+void QwtPlot::setAutoReplot( bool tf )
+{
+    d_data->autoReplot = tf;
+}
+
+/*! 
+  \return true if the autoReplot option is set.
+  \sa setAutoReplot()
+*/
+bool QwtPlot::autoReplot() const
+{
+    return d_data->autoReplot;
+}
+
+/*!
+  Change the plot's title
+  \param title New title
+*/
+void QwtPlot::setTitle( const QString &title )
+{
+    if ( title != d_data->titleLabel->text().text() )
+    {
+        d_data->titleLabel->setText( title );
+        updateLayout();
+    }
+}
+
+/*!
+  Change the plot's title
+  \param title New title
+*/
+void QwtPlot::setTitle( const QwtText &title )
+{
+    if ( title != d_data->titleLabel->text() )
+    {
+        d_data->titleLabel->setText( title );
+        updateLayout();
+    }
+}
+
+//! \return Title of the plot
+QwtText QwtPlot::title() const
+{
+    return d_data->titleLabel->text();
+}
+
+//! \return Title label widget.
+QwtTextLabel *QwtPlot::titleLabel()
+{
+    return d_data->titleLabel;
+}
+
+//! \return Title label widget.
+const QwtTextLabel *QwtPlot::titleLabel() const
+{
+    return d_data->titleLabel;
+}
+
+/*!
+  Change the text the footer 
+  \param text New text of the footer
+*/
+void QwtPlot::setFooter( const QString &text )
+{
+    if ( text != d_data->footerLabel->text().text() )
+    {
+        d_data->footerLabel->setText( text );
+        updateLayout();
+    }
+}
+
+/*!
+  Change the text the footer 
+  \param text New text of the footer
+*/
+void QwtPlot::setFooter( const QwtText &text )
+{
+    if ( text != d_data->footerLabel->text() )
+    {
+        d_data->footerLabel->setText( text );
+        updateLayout();
+    }
+}
+
+//! \return Text of the footer
+QwtText QwtPlot::footer() const
+{
+    return d_data->footerLabel->text();
+}
+
+//! \return Footer label widget.
+QwtTextLabel *QwtPlot::footerLabel()
+{
+    return d_data->footerLabel;
+}
+
+//! \return Footer label widget.
+const QwtTextLabel *QwtPlot::footerLabel() const
+{
+    return d_data->footerLabel;
+}
+
+/*!
+   \brief Assign a new plot layout
+
+   \param layout Layout()
+   \sa plotLayout()
+ */
+void QwtPlot::setPlotLayout( QwtPlotLayout *layout )
+{
+    if ( layout != d_data->layout )
+    {
+        delete d_data->layout;
+        layout = d_data->layout;
+
+        updateLayout();
+    }
+}
+
+//! \return the plot's layout
+QwtPlotLayout *QwtPlot::plotLayout()
+{
+    return d_data->layout;
+}
+
+//! \return the plot's layout
+const QwtPlotLayout *QwtPlot::plotLayout() const
+{
+    return d_data->layout;
+}
+
+/*!
+  \return the plot's legend
+  \sa insertLegend()
+*/
+QwtAbstractLegend *QwtPlot::legend()
+{
+    return d_data->legend;
+}
+
+/*!
+  \return the plot's legend
+  \sa insertLegend()
+*/
+const QwtAbstractLegend *QwtPlot::legend() const
+{
+    return d_data->legend;
+}
+
+
+/*!
+  \return the plot's canvas
+*/
+QWidget *QwtPlot::canvas()
+{
+    return d_data->canvas;
+}
+
+/*!
+  \return the plot's canvas
+*/
+const QWidget *QwtPlot::canvas() const
+{
+    return d_data->canvas;
+}
+
+/*!
+  \return Size hint for the plot widget
+  \sa minimumSizeHint()
+*/
+QSize QwtPlot::sizeHint() const
+{
+    int dw = 0;
+    int dh = 0;
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( axisEnabled( axisId ) )
+        {
+            const int niceDist = 40;
+            const QwtScaleWidget *scaleWidget = axisWidget( axisId );
+            const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
+            const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
+
+            if ( axisId == yLeft || axisId == yRight )
+            {
+                int hDiff = ( majCnt - 1 ) * niceDist
+                    - scaleWidget->minimumSizeHint().height();
+                if ( hDiff > dh )
+                    dh = hDiff;
+            }
+            else
+            {
+                int wDiff = ( majCnt - 1 ) * niceDist
+                    - scaleWidget->minimumSizeHint().width();
+                if ( wDiff > dw )
+                    dw = wDiff;
+            }
+        }
+    }
+    return minimumSizeHint() + QSize( dw, dh );
+}
+
+/*!
+  \brief Return a minimum size hint
+*/
+QSize QwtPlot::minimumSizeHint() const
+{
+    QSize hint = d_data->layout->minimumSizeHint( this );
+    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
+
+    return hint;
+}
+
+/*!
+  Resize and update internal layout
+  \param e Resize event
+*/
+void QwtPlot::resizeEvent( QResizeEvent *e )
+{
+    QFrame::resizeEvent( e );
+    updateLayout();
+}
+
+/*!
+  \brief Redraw the plot
+
+  If the autoReplot option is not set (which is the default)
+  or if any curves are attached to raw data, the plot has to
+  be refreshed explicitly in order to make changes visible.
+
+  \sa updateAxes(), setAutoReplot()
+*/
+void QwtPlot::replot()
+{
+    bool doAutoReplot = autoReplot();
+    setAutoReplot( false );
+
+    updateAxes();
+
+    /*
+      Maybe the layout needs to be updated, because of changed
+      axes labels. We need to process them here before painting
+      to avoid that scales and canvas get out of sync.
+     */
+    QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
+
+    if ( d_data->canvas )
+    {
+        const bool ok = QMetaObject::invokeMethod( 
+            d_data->canvas, "replot", Qt::DirectConnection );
+        if ( !ok )
+        {
+            // fallback, when canvas has no a replot method
+            d_data->canvas->update( d_data->canvas->contentsRect() );
+        }
+    }
+
+    setAutoReplot( doAutoReplot );
+}
+
+/*!
+  \brief Adjust plot content to its current size.
+  \sa resizeEvent()
+*/
+void QwtPlot::updateLayout()
+{
+    d_data->layout->activate( this, contentsRect() );
+
+    QRect titleRect = d_data->layout->titleRect().toRect();
+    QRect footerRect = d_data->layout->footerRect().toRect();
+    QRect scaleRect[QwtPlot::axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect();
+    QRect legendRect = d_data->layout->legendRect().toRect();
+    QRect canvasRect = d_data->layout->canvasRect().toRect();
+
+    // resize and show the visible widgets
+
+    if ( !d_data->titleLabel->text().isEmpty() )
+    {
+        d_data->titleLabel->setGeometry( titleRect );
+        if ( !d_data->titleLabel->isVisibleTo( this ) )
+            d_data->titleLabel->show();
+    }
+    else
+        d_data->titleLabel->hide();
+
+    if ( !d_data->footerLabel->text().isEmpty() )
+    {
+        d_data->footerLabel->setGeometry( footerRect );
+        if ( !d_data->footerLabel->isVisibleTo( this ) )
+            d_data->footerLabel->show();
+    }
+    else
+        d_data->footerLabel->hide();
+
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( axisEnabled( axisId ) )
+        {
+            axisWidget( axisId )->setGeometry( scaleRect[axisId] );
+
+#if 1
+            if ( axisId == xBottom || axisId == xTop )
+            {
+                // do we need this code any longer ???
+
+                QRegion r( scaleRect[axisId] );
+                if ( axisEnabled( yLeft ) )
+                    r = r.subtracted( QRegion( scaleRect[yLeft] ) );
+                if ( axisEnabled( yRight ) )
+                    r = r.subtracted( QRegion( scaleRect[yRight] ) );
+                r.translate( -scaleRect[ axisId ].x(),
+                    -scaleRect[axisId].y() );
+
+                axisWidget( axisId )->setMask( r );
+            }
+#endif
+            if ( !axisWidget( axisId )->isVisibleTo( this ) )
+                axisWidget( axisId )->show();
+        }
+        else
+            axisWidget( axisId )->hide();
+    }
+
+    if ( d_data->legend )
+    {
+        if ( d_data->legend->isEmpty() )
+        {
+            d_data->legend->hide();
+        }
+        else
+        {
+            d_data->legend->setGeometry( legendRect );
+            d_data->legend->show();
+        }
+    }
+
+    d_data->canvas->setGeometry( canvasRect );
+}
+
+/*!
+  \brief Calculate the canvas margins
+
+  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
+  \param canvasRect Bounding rectangle where to paint
+  \param left Return parameter for the left margin
+  \param top Return parameter for the top margin
+  \param right Return parameter for the right margin
+  \param bottom Return parameter for the bottom margin
+
+  Plot items might indicate, that they need some extra space
+  at the borders of the canvas by the QwtPlotItem::Margins flag.
+
+  updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint()
+ */
+void QwtPlot::getCanvasMarginsHint(
+    const QwtScaleMap maps[], const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom) const
+{
+    left = top = right = bottom = -1.0;
+
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        const QwtPlotItem *item = *it;
+        if ( item->testItemAttribute( QwtPlotItem::Margins ) )
+        {
+            double m[ QwtPlot::axisCnt ];
+            item->getCanvasMarginHint(
+                maps[ item->xAxis() ], maps[ item->yAxis() ],
+                canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] );
+
+            left = qMax( left, m[yLeft] );
+            top = qMax( top, m[xTop] );
+            right = qMax( right, m[yRight] );
+            bottom = qMax( bottom, m[xBottom] );
+        }
+    }
+}
+
+/*!
+  \brief Update the canvas margins
+
+  Plot items might indicate, that they need some extra space
+  at the borders of the canvas by the QwtPlotItem::Margins flag.
+
+  getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint()
+ */
+void QwtPlot::updateCanvasMargins()
+{
+    QwtScaleMap maps[axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        maps[axisId] = canvasMap( axisId );
+
+    double margins[axisCnt];
+    getCanvasMarginsHint( maps, canvas()->contentsRect(),
+        margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] );
+    
+    bool doUpdate = false;
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( margins[axisId] >= 0.0 )
+        {
+            const int m = qCeil( margins[axisId] );
+            plotLayout()->setCanvasMargin( m, axisId);
+            doUpdate = true;
+        }
+    }
+
+    if ( doUpdate )
+        updateLayout();
+}
+
+/*!
+  Redraw the canvas.
+  \param painter Painter used for drawing
+
+  \warning drawCanvas calls drawItems what is also used
+           for printing. Applications that like to add individual
+           plot items better overload drawItems()
+  \sa drawItems()
+*/
+void QwtPlot::drawCanvas( QPainter *painter )
+{
+    QwtScaleMap maps[axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        maps[axisId] = canvasMap( axisId );
+
+    drawItems( painter, d_data->canvas->contentsRect(), maps );
+}
+
+/*!
+  Redraw the canvas items.
+
+  \param painter Painter used for drawing
+  \param canvasRect Bounding rectangle where to paint
+  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
+
+  \note Usually canvasRect is contentsRect() of the plot canvas.
+        Due to a bug in Qt this rectangle might be wrong for certain 
+        frame styles ( f.e QFrame::Box ) and it might be necessary to 
+        fix the margins manually using QWidget::setContentsMargins()
+*/
+
+void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
+        const QwtScaleMap maps[axisCnt] ) const
+{
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item && item->isVisible() )
+        {
+            painter->save();
+
+            painter->setRenderHint( QPainter::Antialiasing,
+                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+            painter->setRenderHint( QPainter::HighQualityAntialiasing,
+                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+            item->draw( painter,
+                maps[item->xAxis()], maps[item->yAxis()],
+                canvasRect );
+
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  \param axisId Axis
+  \return Map for the axis on the canvas. With this map pixel coordinates can
+          translated to plot coordinates and vice versa.
+  \sa QwtScaleMap, transform(), invTransform()
+
+*/
+QwtScaleMap QwtPlot::canvasMap( int axisId ) const
+{
+    QwtScaleMap map;
+    if ( !d_data->canvas )
+        return map;
+
+    map.setTransformation( axisScaleEngine( axisId )->transformation() );
+
+    const QwtScaleDiv &sd = axisScaleDiv( axisId );
+    map.setScaleInterval( sd.lowerBound(), sd.upperBound() );
+
+    if ( axisEnabled( axisId ) )
+    {
+        const QwtScaleWidget *s = axisWidget( axisId );
+        if ( axisId == yLeft || axisId == yRight )
+        {
+            double y = s->y() + s->startBorderDist() - d_data->canvas->y();
+            double h = s->height() - s->startBorderDist() - s->endBorderDist();
+            map.setPaintInterval( y + h, y );
+        }
+        else
+        {
+            double x = s->x() + s->startBorderDist() - d_data->canvas->x();
+            double w = s->width() - s->startBorderDist() - s->endBorderDist();
+            map.setPaintInterval( x, x + w );
+        }
+    }
+    else
+    {
+        int margin = 0;
+        if ( !plotLayout()->alignCanvasToScale( axisId ) )
+            margin = plotLayout()->canvasMargin( axisId );
+
+        const QRect &canvasRect = d_data->canvas->contentsRect();
+        if ( axisId == yLeft || axisId == yRight )
+        {
+            map.setPaintInterval( canvasRect.bottom() - margin,
+                canvasRect.top() + margin );
+        }
+        else
+        {
+            map.setPaintInterval( canvasRect.left() + margin,
+                canvasRect.right() - margin );
+        }
+    }
+    return map;
+}
+
+/*!
+  \brief Change the background of the plotting area
+
+  Sets brush to QPalette::Window of all color groups of
+  the palette of the canvas. Using canvas()->setPalette()
+  is a more powerful way to set these colors.
+
+  \param brush New background brush
+  \sa canvasBackground()
+*/
+void QwtPlot::setCanvasBackground( const QBrush &brush )
+{
+    QPalette pal = d_data->canvas->palette();
+    pal.setBrush( QPalette::Window, brush );
+
+    canvas()->setPalette( pal );
+}
+
+/*!
+  Nothing else than: canvas()->palette().brush(
+        QPalette::Normal, QPalette::Window);
+
+  \return Background brush of the plotting area.
+  \sa setCanvasBackground()
+*/
+QBrush QwtPlot::canvasBackground() const
+{
+    return canvas()->palette().brush(
+        QPalette::Normal, QPalette::Window );
+}
+
+/*!
+  \return \c true if the specified axis exists, otherwise \c false
+  \param axisId axis index
+ */
+bool QwtPlot::axisValid( int axisId )
+{
+    return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
+}
+
+/*!
+  \brief Insert a legend
+
+  If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
+  the legend will be organized in one column from top to down.
+  Otherwise the legend items will be placed in a table
+  with a best fit number of columns from left to right.
+
+  insertLegend() will set the plot widget as parent for the legend.
+  The legend will be deleted in the destructor of the plot or when 
+  another legend is inserted.
+
+  Legends, that are not inserted into the layout of the plot widget
+  need to connect to the legendDataChanged() signal. Calling updateLegend()
+  initiates this signal for an initial update. When the application code
+  wants to implement its own layout this also needs to be done for
+  rendering plots to a document ( see QwtPlotRenderer ).
+
+  \param legend Legend
+  \param pos The legend's position. For top/left position the number
+             of columns will be limited to 1, otherwise it will be set to
+             unlimited.
+
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+
+  \sa legend(), QwtPlotLayout::legendPosition(),
+      QwtPlotLayout::setLegendPosition()
+*/
+void QwtPlot::insertLegend( QwtAbstractLegend *legend,
+    QwtPlot::LegendPosition pos, double ratio )
+{
+    d_data->layout->setLegendPosition( pos, ratio );
+
+    if ( legend != d_data->legend )
+    {
+        if ( d_data->legend && d_data->legend->parent() == this )
+            delete d_data->legend;
+
+        d_data->legend = legend;
+
+        if ( d_data->legend )
+        {
+            connect( this, 
+                SIGNAL( legendDataChanged( 
+                    const QVariant &, const QList<QwtLegendData> & ) ),
+                d_data->legend, 
+                SLOT( updateLegend( 
+                    const QVariant &, const QList<QwtLegendData> & ) ) 
+            );
+
+            if ( d_data->legend->parent() != this )
+                d_data->legend->setParent( this );
+
+            qwtEnableLegendItems( this, false );
+            updateLegend();
+            qwtEnableLegendItems( this, true );
+
+            QwtLegend *lgd = qobject_cast<QwtLegend *>( legend );
+            if ( lgd )
+            {
+                switch ( d_data->layout->legendPosition() )
+                {
+                    case LeftLegend:
+                    case RightLegend:
+                    {
+                        if ( lgd->maxColumns() == 0     )
+                            lgd->setMaxColumns( 1 ); // 1 column: align vertical
+                        break;
+                    }
+                    case TopLegend:
+                    case BottomLegend:
+                    {
+                        lgd->setMaxColumns( 0 ); // unlimited
+                        break;
+                    }
+                    default:
+                        break;
+                }
+            }
+
+            QWidget *previousInChain = NULL;
+            switch ( d_data->layout->legendPosition() )
+            {
+                case LeftLegend:
+                {
+                    previousInChain = axisWidget( QwtPlot::xTop );
+                    break;
+                }
+                case TopLegend:
+                {
+                    previousInChain = this;
+                    break;
+                }
+                case RightLegend:
+                {
+                    previousInChain = axisWidget( QwtPlot::yRight );
+                    break;
+                }
+                case BottomLegend:
+                {
+                    previousInChain = footerLabel();
+                    break;
+                }
+            }
+
+            if ( previousInChain )
+                qwtSetTabOrder( previousInChain, legend, true );
+        }
+    }
+
+    updateLayout();
+}
+
+/*!
+  Emit legendDataChanged() for all plot item
+
+  \sa QwtPlotItem::legendData(), legendDataChanged()
+ */
+void QwtPlot::updateLegend()
+{
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        updateLegend( *it );
+    }
+}
+
+/*!
+  Emit legendDataChanged() for a plot item
+
+  \param plotItem Plot item
+  \sa QwtPlotItem::legendData(), legendDataChanged()
+ */
+void QwtPlot::updateLegend( const QwtPlotItem *plotItem )
+{
+    if ( plotItem == NULL )
+        return;
+
+    QList<QwtLegendData> legendData;
+
+    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
+        legendData = plotItem->legendData();
+
+    const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) );
+    Q_EMIT legendDataChanged( itemInfo, legendData );
+}
+
+/*!
+  \brief Update all plot items interested in legend attributes
+
+  Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest
+  flag is set.
+
+  \param itemInfo Info about the plot item
+  \param legendData Entries to be displayed for the plot item ( usually 1 )
+
+  \sa QwtPlotItem::LegendInterest,
+      QwtPlotLegendItem, QwtPlotItem::updateLegend()
+ */
+void QwtPlot::updateLegendItems( const QVariant &itemInfo,
+    const QList<QwtLegendData> &legendData )
+{
+    QwtPlotItem *plotItem = infoToItem( itemInfo );
+    if ( plotItem )
+    {
+        const QwtPlotItemList& itmList = itemList();
+        for ( QwtPlotItemIterator it = itmList.begin();
+            it != itmList.end(); ++it )
+        {
+            QwtPlotItem *item = *it;
+            if ( item->testItemInterest( QwtPlotItem::LegendInterest ) )
+                item->updateLegend( plotItem, legendData );
+        }
+    }
+}
+
+/*!
+  \brief Attach/Detach a plot item 
+
+  \param plotItem Plot item
+  \param on When true attach the item, otherwise detach it
+ */
+void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on )
+{
+    if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) )
+    {
+        // plotItem is some sort of legend
+
+        const QwtPlotItemList& itmList = itemList();
+        for ( QwtPlotItemIterator it = itmList.begin();
+            it != itmList.end(); ++it )
+        {
+            QwtPlotItem *item = *it;
+
+            QList<QwtLegendData> legendData;
+            if ( on && item->testItemAttribute( QwtPlotItem::Legend ) )
+            {
+                legendData = item->legendData();
+                plotItem->updateLegend( item, legendData );
+            }
+        }
+    }
+
+    if ( on )
+        insertItem( plotItem );
+    else 
+        removeItem( plotItem );
+
+    Q_EMIT itemAttached( plotItem, on );
+
+    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
+    {
+        // the item wants to be represented on the legend
+
+        if ( on )
+        {
+            updateLegend( plotItem );
+        }
+        else
+        {
+            const QVariant itemInfo = itemToInfo( plotItem );
+            Q_EMIT legendDataChanged( itemInfo, QList<QwtLegendData>() );
+        }
+    }
+
+    if ( autoReplot() )
+        update();
+}
+
+/*!
+  \brief Build an information, that can be used to identify
+         a plot item on the legend.
+
+  The default implementation simply wraps the plot item
+  into a QVariant object. When overloading itemToInfo()
+  usually infoToItem() needs to reimplemeted too.
+
+\code
+    QVariant itemInfo;
+    qVariantSetValue( itemInfo, plotItem );
+\endcode
+
+  \param plotItem Plot item
+  \return Plot item embedded in a QVariant
+  \sa infoToItem()
+ */
+QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const
+{
+    QVariant itemInfo;
+    qVariantSetValue( itemInfo, plotItem );
+
+    return itemInfo;
+}
+
+/*!
+  \brief Identify the plot item according to an item info object,
+         that has bee generated from itemToInfo().
+
+  The default implementation simply tries to unwrap a QwtPlotItem 
+  pointer:
+
+\code
+    if ( itemInfo.canConvert<QwtPlotItem *>() )
+        return qvariant_cast<QwtPlotItem *>( itemInfo );
+\endcode
+  \param itemInfo Plot item
+  \return A plot item, when successful, otherwise a NULL pointer.
+  \sa itemToInfo()
+*/
+QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const
+{
+    if ( itemInfo.canConvert<QwtPlotItem *>() )
+        return qvariant_cast<QwtPlotItem *>( itemInfo );
+
+    return NULL;
+}
+
+
diff --git a/qwt/qwt_plot.h b/qwt/qwt_plot.h
new file mode 100644
index 0000000..d662613
--- /dev/null
+++ b/qwt/qwt_plot.h
@@ -0,0 +1,312 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_H
+#define QWT_PLOT_H
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include "qwt_plot_dict.h"
+#include "qwt_scale_map.h"
+#include "qwt_interval.h"
+#include <qframe.h>
+#include <qlist.h>
+#include <qvariant.h>
+
+class QwtPlotLayout;
+class QwtAbstractLegend;
+class QwtScaleWidget;
+class QwtScaleEngine;
+class QwtScaleDiv;
+class QwtScaleDraw;
+class QwtTextLabel;
+
+/*!
+  \brief A 2-D plotting widget
+
+  QwtPlot is a widget for plotting two-dimensional graphs.
+  An unlimited number of plot items can be displayed on
+  its canvas. Plot items might be curves (QwtPlotCurve), markers
+  (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived
+  from QwtPlotItem.
+  A plot can have up to four axes, with each plot item attached to an x- and
+  a y axis. The scales at the axes can be explicitly set (QwtScaleDiv), or
+  are calculated from the plot items, using algorithms (QwtScaleEngine) which
+  can be configured separately for each axis.
+
+  The simpleplot example is a good starting point to see how to set up a 
+  plot widget.
+
+  \image html plot.png
+
+  \par Example
+  The following example shows (schematically) the most simple
+  way to use QwtPlot. By default, only the left and bottom axes are
+  visible and their scales are computed automatically.
+  \verbatim
+#include <qwt_plot.h>
+#include <qwt_plot_curve.h>
+
+QwtPlot *myPlot = new QwtPlot("Two Curves", parent);
+
+// add curves
+QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1");
+QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2");
+
+// connect or copy the data to the curves
+curve1->setData(...);
+curve2->setData(...);
+
+curve1->attach(myPlot);
+curve2->attach(myPlot);
+
+// finally, refresh the plot
+myPlot->replot();
+\endverbatim
+*/
+
+class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict
+{
+    Q_OBJECT
+
+    Q_PROPERTY( QBrush canvasBackground 
+        READ canvasBackground WRITE setCanvasBackground )
+    Q_PROPERTY( bool autoReplot READ autoReplot WRITE setAutoReplot )
+
+#if 0
+    // This property is intended to configure the plot
+    // widget from a special dialog in the deigner plugin.
+    // Disabled until such a dialog has been implemented.
+
+    Q_PROPERTY( QString propertiesDocument
+        READ grabProperties WRITE applyProperties )
+#endif
+
+public:
+    //! \brief Axis index
+    enum Axis
+    {
+        //! Y axis left of the canvas
+        yLeft,
+
+        //! Y axis right of the canvas
+        yRight,
+
+        //! X axis below the canvas
+        xBottom,
+
+        //! X axis above the canvas
+        xTop,
+
+        //! Number of axes
+        axisCnt
+    };
+
+    /*!
+        Position of the legend, relative to the canvas.
+
+        \sa insertLegend()
+     */
+    enum LegendPosition
+    {
+        //! The legend will be left from the QwtPlot::yLeft axis.
+        LeftLegend,
+
+        //! The legend will be right from the QwtPlot::yRight axis.
+        RightLegend,
+
+        //! The legend will be below the footer 
+        BottomLegend,
+
+        //! The legend will be above the title
+        TopLegend
+    };
+
+    explicit QwtPlot( QWidget * = NULL );
+    explicit QwtPlot( const QwtText &title, QWidget * = NULL );
+
+    virtual ~QwtPlot();
+
+    void applyProperties( const QString & );
+    QString grabProperties() const;
+
+    void setAutoReplot( bool = true );
+    bool autoReplot() const;
+
+    // Layout
+
+    void setPlotLayout( QwtPlotLayout * );
+
+    QwtPlotLayout *plotLayout();
+    const QwtPlotLayout *plotLayout() const;
+
+    // Title
+
+    void setTitle( const QString & );
+    void setTitle( const QwtText &t );
+    QwtText title() const;
+
+    QwtTextLabel *titleLabel();
+    const QwtTextLabel *titleLabel() const;
+
+    // Footer
+
+    void setFooter( const QString & );
+    void setFooter( const QwtText &t );
+    QwtText footer() const;
+
+    QwtTextLabel *footerLabel();
+    const QwtTextLabel *footerLabel() const;
+
+    // Canvas
+
+    void setCanvas( QWidget * );
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    void setCanvasBackground( const QBrush & );
+    QBrush canvasBackground() const;
+
+    virtual QwtScaleMap canvasMap( int axisId ) const;
+
+    double invTransform( int axisId, int pos ) const;
+    double transform( int axisId, double value ) const;
+
+    // Axes
+
+    QwtScaleEngine *axisScaleEngine( int axisId );
+    const QwtScaleEngine *axisScaleEngine( int axisId ) const;
+    void setAxisScaleEngine( int axisId, QwtScaleEngine * );
+
+    void setAxisAutoScale( int axisId, bool on = true );
+    bool axisAutoScale( int axisId ) const;
+
+    void enableAxis( int axisId, bool tf = true );
+    bool axisEnabled( int axisId ) const;
+
+    void setAxisFont( int axisId, const QFont &f );
+    QFont axisFont( int axisId ) const;
+
+    void setAxisScale( int axisId, double min, double max, double step = 0 );
+    void setAxisScaleDiv( int axisId, const QwtScaleDiv & );
+    void setAxisScaleDraw( int axisId, QwtScaleDraw * );
+
+    double axisStepSize( int axisId ) const;
+    QwtInterval axisInterval( int axisId ) const;
+
+    const QwtScaleDiv &axisScaleDiv( int axisId ) const;
+
+    const QwtScaleDraw *axisScaleDraw( int axisId ) const;
+    QwtScaleDraw *axisScaleDraw( int axisId );
+
+    const QwtScaleWidget *axisWidget( int axisId ) const;
+    QwtScaleWidget *axisWidget( int axisId );
+
+    void setAxisLabelAlignment( int axisId, Qt::Alignment );
+    void setAxisLabelRotation( int axisId, double rotation );
+
+    void setAxisTitle( int axisId, const QString & );
+    void setAxisTitle( int axisId, const QwtText & );
+    QwtText axisTitle( int axisId ) const;
+
+    void setAxisMaxMinor( int axisId, int maxMinor );
+    int axisMaxMinor( int axisId ) const;
+
+    void setAxisMaxMajor( int axisId, int maxMajor );
+    int axisMaxMajor( int axisId ) const;
+
+    // Legend
+
+    void insertLegend( QwtAbstractLegend *, 
+        LegendPosition = QwtPlot::RightLegend, double ratio = -1.0 );
+
+    QwtAbstractLegend *legend();
+    const QwtAbstractLegend *legend() const;
+
+    void updateLegend();
+    void updateLegend( const QwtPlotItem * );
+
+    // Misc
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    virtual void updateLayout();
+    virtual void drawCanvas( QPainter * );
+
+    void updateAxes();
+    void updateCanvasMargins();
+
+    virtual void getCanvasMarginsHint( 
+        const QwtScaleMap maps[], const QRectF &canvasRect,
+        double &left, double &top, double &right, double &bottom) const;
+
+    virtual bool event( QEvent * );
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    virtual void drawItems( QPainter *, const QRectF &,
+        const QwtScaleMap maps[axisCnt] ) const;
+
+    virtual QVariant itemToInfo( QwtPlotItem * ) const;
+    virtual QwtPlotItem *infoToItem( const QVariant & ) const;
+
+Q_SIGNALS:
+    /*!
+      A signal indicating, that an item has been attached/detached
+
+      \param plotItem Plot item
+      \param on Attached/Detached
+     */
+    void itemAttached( QwtPlotItem *plotItem, bool on );
+
+    /*!
+      A signal with the attributes how to update 
+      the legend entries for a plot item.
+
+      \param itemInfo Info about a plot item, build from itemToInfo()
+      \param data Attributes of the entries ( usually <= 1 ) for
+                  the plot item.
+
+      \sa itemToInfo(), infoToItem(), QwtAbstractLegend::updateLegend()
+     */
+    void legendDataChanged( const QVariant &itemInfo, 
+        const QList<QwtLegendData> &data );
+
+public Q_SLOTS:
+    virtual void replot();
+    void autoRefresh();
+
+protected:
+    static bool axisValid( int axisId );
+
+    virtual void resizeEvent( QResizeEvent *e );
+
+private Q_SLOTS:
+    void updateLegendItems( const QVariant &itemInfo,
+        const QList<QwtLegendData> &data );
+
+private:
+    friend class QwtPlotItem;
+    void attachItem( QwtPlotItem *, bool );
+
+    void initAxesData();
+    void deleteAxesData();
+    void updateScaleDiv();
+
+    void initPlot( const QwtText &title );
+
+    class AxisData;
+    AxisData *d_axisData[axisCnt];
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_abstract_barchart.cpp b/qwt/qwt_plot_abstract_barchart.cpp
new file mode 100644
index 0000000..9a72fdd
--- /dev/null
+++ b/qwt/qwt_plot_abstract_barchart.cpp
@@ -0,0 +1,367 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_abstract_barchart.h"
+#include "qwt_scale_map.h"
+
+static inline double qwtTransformWidth(
+    const QwtScaleMap &map, double value, double width )
+{
+    const double w2 = 0.5 * width;
+
+    const double v1 = map.transform( value - w2 );
+    const double v2 = map.transform( value + w2 );
+
+    return qAbs( v2 - v1 );
+}
+
+class QwtPlotAbstractBarChart::PrivateData
+{
+public:
+    PrivateData():
+        layoutPolicy( QwtPlotAbstractBarChart::AutoAdjustSamples ),
+        layoutHint( 0.5 ),
+        spacing( 10 ),
+        margin( 5 ),
+        baseline( 0.0 )
+    {
+    }
+
+    QwtPlotAbstractBarChart::LayoutPolicy layoutPolicy;
+    double layoutHint;
+    int spacing;
+    int margin;
+    double baseline;
+};
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotAbstractBarChart::QwtPlotAbstractBarChart( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Margins, true );
+    setZ( 19.0 );
+}
+
+//! Destructor
+QwtPlotAbstractBarChart::~QwtPlotAbstractBarChart()
+{
+    delete d_data;
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \param policy Layout policy
+
+  \sa layoutPolicy(), layoutHint()
+ */
+void QwtPlotAbstractBarChart::setLayoutPolicy( LayoutPolicy policy )
+{
+    if ( policy != d_data->layoutPolicy )
+    {
+        d_data->layoutPolicy = policy;
+        itemChanged();
+    }
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \return Layout policy of the chart item
+  \sa setLayoutPolicy(), layoutHint()
+ */
+QwtPlotAbstractBarChart::LayoutPolicy QwtPlotAbstractBarChart::layoutPolicy() const
+{
+    return d_data->layoutPolicy;
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \param hint Layout hint
+
+  \sa LayoutPolicy, layoutPolicy(), layoutHint()
+ */
+void QwtPlotAbstractBarChart::setLayoutHint( double hint )
+{
+    hint = qMax( 0.0, hint );
+    if ( hint != d_data->layoutHint )
+    {
+        d_data->layoutHint = hint;
+        itemChanged();
+    }
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \return Layout policy of the chart item
+  \sa LayoutPolicy, setLayoutHint(), layoutPolicy()
+*/
+double QwtPlotAbstractBarChart::layoutHint() const
+{
+    return d_data->layoutHint;
+}
+
+/*!
+  \brief Set the spacing
+
+  The spacing is the distance between 2 samples ( bars for QwtPlotBarChart or
+  a group of bars for QwtPlotMultiBarChart ) in paint device coordinates.
+
+  \sa spacing()
+ */
+void QwtPlotAbstractBarChart::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spacing between 2 samples ( bars or groups of bars )
+  \sa setSpacing(), margin()
+ */
+int QwtPlotAbstractBarChart::spacing() const
+{
+    return d_data->spacing;
+}
+/*!
+  \brief Set the margin
+
+  The margin is the distance between the outmost bars and the contentsRect()
+  of the canvas. The default setting is 5 pixels.
+
+  \param margin Margin
+
+  \sa spacing(), margin()
+ */
+void QwtPlotAbstractBarChart::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != d_data->margin )
+    {
+        d_data->margin = margin;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin between the outmost bars and the contentsRect()
+  of the canvas.
+
+  \sa setMargin(), spacing()
+ */
+int QwtPlotAbstractBarChart::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+   \brief Set the baseline
+
+   The baseline is the origin for the chart. Each bar is 
+   painted from the baseline in the direction of the sample 
+   value. In case of a horizontal orientation() the baseline
+   is interpreted as x - otherwise as y - value.
+
+   The default value for the baseline is 0.
+
+   \param value Value for the baseline
+
+   \sa baseline(), QwtPlotSeriesItem::orientation()
+*/
+void QwtPlotAbstractBarChart::setBaseline( double value )
+{
+    if ( value != d_data->baseline )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*! 
+   \return Value for the origin of the bar chart
+   \sa setBaseline(), QwtPlotSeriesItem::orientation()
+ */
+double QwtPlotAbstractBarChart::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+   Calculate the width for a sample in paint device coordinates
+
+   \param map Scale map for the corresponding scale
+   \param canvasSize Size of the canvas in paint device coordinates
+   \param boundingSize Bounding size of the chart in plot coordinates
+                       ( used in AutoAdjustSamples mode )
+   \param value Value of the sample
+
+   \return Sample width
+   \sa layoutPolicy(), layoutHint()
+*/
+double QwtPlotAbstractBarChart::sampleWidth( const QwtScaleMap &map,
+    double canvasSize, double boundingSize, double value ) const
+{
+    double width;
+
+    switch( d_data->layoutPolicy )
+    {
+        case ScaleSamplesToAxes:
+        {
+            width = qwtTransformWidth( map, value, d_data->layoutHint );
+            break;
+        }
+        case ScaleSampleToCanvas:
+        {
+            width = canvasSize * d_data->layoutHint;
+            break;
+        }
+        case FixedSampleSize:
+        {
+            width = d_data->layoutHint;
+            break;
+        }
+        case AutoAdjustSamples:
+        default:
+        {
+            const size_t numSamples = dataSize();
+
+            double w = 1.0;
+            if ( numSamples > 1 )
+            {
+                w = qAbs( boundingSize / ( numSamples - 1 ) );
+            }
+
+            width = qwtTransformWidth( map, value, w );
+            width -= d_data->spacing;
+        }
+    }
+
+    return width;
+}
+
+/*!
+   \brief Calculate a hint for the canvas margin
+
+   Bar charts need to reserve some space for displaying the bars
+   for the first and the last sample.  The hint is calculated
+   from the layoutHint() depending on the layoutPolicy().
+
+   The margins are in target device coordinates ( pixels on screen )
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param canvasRect Contents rectangle of the canvas in painter coordinates
+   \param left Returns the left margin
+   \param top Returns the top margin
+   \param right Returns the right margin
+   \param bottom Returns the bottom margin
+
+   \return Margin
+
+   \sa layoutPolicy(), layoutHint(), QwtPlotItem::Margins
+       QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins()
+ */
+void QwtPlotAbstractBarChart::getCanvasMarginHint( const QwtScaleMap &xMap, 
+    const QwtScaleMap &yMap, const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom ) const
+{
+    double hint = -1.0;
+
+    switch( layoutPolicy() )
+    {
+        case ScaleSampleToCanvas:
+        {
+            if ( orientation() == Qt::Vertical )
+                hint = 0.5 * canvasRect.width() * d_data->layoutHint;
+            else
+                hint = 0.5 * canvasRect.height() * d_data->layoutHint;
+
+            break;
+        }
+        case FixedSampleSize:
+        {
+            hint = 0.5 * d_data->layoutHint;
+            break;
+        }
+        case AutoAdjustSamples:
+        case ScaleSamplesToAxes:
+        default:
+        {
+            const size_t numSamples = dataSize();
+            if ( numSamples <= 0 )
+                break;
+
+            // doesn't work for nonlinear scales
+
+            const QRectF br = dataRect();
+            double spacing = 0.0;
+            double sampleWidthS = 1.0;
+
+            if ( layoutPolicy() == ScaleSamplesToAxes )
+            {
+                sampleWidthS = qMax( d_data->layoutHint, 0.0 );
+            }
+            else
+            {
+                spacing = d_data->spacing;
+
+                if ( numSamples > 1 )
+                {
+                    sampleWidthS = qAbs( br.width() / ( numSamples - 1 ) );
+                }
+            }
+
+            double ds, w;
+            if ( orientation() == Qt::Vertical )
+            {
+                ds = qAbs( xMap.sDist() );
+                w = canvasRect.width();
+            }
+            else
+            {
+                ds = qAbs( yMap.sDist() );
+                w = canvasRect.height();
+            }
+
+            const double sampleWidthP = ( w - spacing * ds ) 
+                * sampleWidthS / ( ds + sampleWidthS );
+
+            hint = 0.5 * sampleWidthP;
+            hint += qMax( d_data->margin, 0 );
+        }
+    }
+
+    if ( orientation() == Qt::Vertical )
+    {
+        left = right = hint;
+        top = bottom = -1.0; // no hint
+    }
+    else
+    {
+        left = right = -1.0; // no hint
+        top = bottom = hint;
+    }
+}
diff --git a/qwt/qwt_plot_abstract_barchart.h b/qwt/qwt_plot_abstract_barchart.h
new file mode 100644
index 0000000..78b98a5
--- /dev/null
+++ b/qwt/qwt_plot_abstract_barchart.h
@@ -0,0 +1,97 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ABSTRACT_BAR_CHART_H
+#define QWT_PLOT_ABSTRACT_BAR_CHART_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_series_data.h"
+
+/*!
+  \brief Abstract base class for bar chart items
+
+  In opposite to almost all other plot items bar charts can't be 
+  displayed inside of their bounding rectangle and need a special
+  API  how to calculate the width of the bars and how they affect
+  the layout of the attached plot.
+ */
+class QWT_EXPORT QwtPlotAbstractBarChart: public QwtPlotSeriesItem
+{
+public:
+    /*!
+        \brief Mode how to calculate the bar width
+
+        setLayoutPolicy(), setLayoutHint(), barWidthHint()
+     */
+    enum LayoutPolicy
+    {
+        /*!
+          The sample width is calculated by dividing the bounding rectangle
+          by the number of samples.
+
+          \sa boundingRectangle()
+          \note The layoutHint() is ignored
+         */
+        AutoAdjustSamples,
+
+        /*!
+          layoutHint() defines an interval in axis coordinates
+         */
+        ScaleSamplesToAxes,
+
+        /*!
+          The bar width is calculated by multiplying layoutHint()
+          with the height or width of the canvas.
+
+          \sa boundingRectangle()
+         */
+        ScaleSampleToCanvas,
+
+        /*!
+          layoutHint() defines a fixed width in paint device coordinates.
+         */
+        FixedSampleSize
+    };
+
+    explicit QwtPlotAbstractBarChart( const QwtText &title );
+    virtual ~QwtPlotAbstractBarChart();
+
+    void setLayoutPolicy( LayoutPolicy );
+    LayoutPolicy layoutPolicy() const;
+
+    void setLayoutHint( double );
+    double layoutHint() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setMargin( int );
+    int margin() const;
+
+    void setBaseline( double );
+    double baseline() const;
+
+    virtual void getCanvasMarginHint( 
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect,
+        double &left, double &top, double &right, double &bottom) const;
+
+
+protected:
+    double sampleWidth( const QwtScaleMap &map,
+        double canvasSize, double dataSize,
+        double value ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_axis.cpp b/qwt/qwt_plot_axis.cpp
new file mode 100644
index 0000000..e3802f6
--- /dev/null
+++ b/qwt/qwt_plot_axis.cpp
@@ -0,0 +1,719 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot.h"
+#include "qwt_math.h"
+#include "qwt_scale_widget.h"
+#include "qwt_scale_div.h"
+#include "qwt_scale_engine.h"
+
+class QwtPlot::AxisData
+{
+public:
+    bool isEnabled;
+    bool doAutoScale;
+
+    double minValue;
+    double maxValue;
+    double stepSize;
+
+    int maxMajor;
+    int maxMinor;
+
+    bool isValid;
+
+    QwtScaleDiv scaleDiv;
+    QwtScaleEngine *scaleEngine;
+    QwtScaleWidget *scaleWidget;
+};
+
+//! Initialize axes
+void QwtPlot::initAxesData()
+{
+    int axisId;
+
+    for ( axisId = 0; axisId < axisCnt; axisId++ )
+        d_axisData[axisId] = new AxisData;
+
+    d_axisData[yLeft]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::LeftScale, this );
+    d_axisData[yRight]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::RightScale, this );
+    d_axisData[xTop]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::TopScale, this );
+    d_axisData[xBottom]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::BottomScale, this );
+
+    d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" );
+    d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" );
+    d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" );
+    d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" );
+
+#if 1
+    // better find the font sizes from the application font
+    QFont fscl( fontInfo().family(), 10 );
+    QFont fttl( fontInfo().family(), 12, QFont::Bold );
+#endif
+
+    for ( axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.scaleEngine = new QwtLinearScaleEngine;
+
+        d.scaleWidget->setTransformation( 
+            d.scaleEngine->transformation() );
+
+        d.scaleWidget->setFont( fscl );
+        d.scaleWidget->setMargin( 2 );
+
+        QwtText text = d.scaleWidget->title();
+        text.setFont( fttl );
+        d.scaleWidget->setTitle( text );
+
+        d.doAutoScale = true;
+
+        d.minValue = 0.0;
+        d.maxValue = 1000.0;
+        d.stepSize = 0.0;
+
+        d.maxMinor = 5;
+        d.maxMajor = 8;
+
+
+        d.isValid = false;
+    }
+
+    d_axisData[yLeft]->isEnabled = true;
+    d_axisData[yRight]->isEnabled = false;
+    d_axisData[xBottom]->isEnabled = true;
+    d_axisData[xTop]->isEnabled = false;
+}
+
+void QwtPlot::deleteAxesData()
+{
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        delete d_axisData[axisId]->scaleEngine;
+        delete d_axisData[axisId];
+        d_axisData[axisId] = NULL;
+    }
+}
+
+/*!
+  \return Scale widget of the specified axis, or NULL if axisId is invalid.
+  \param axisId Axis index
+*/
+const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleWidget;
+
+    return NULL;
+}
+
+/*!
+  \return Scale widget of the specified axis, or NULL if axisId is invalid.
+  \param axisId Axis index
+*/
+QwtScaleWidget *QwtPlot::axisWidget( int axisId )
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleWidget;
+
+    return NULL;
+}
+
+/*!
+  Change the scale engine for an axis
+
+  \param axisId Axis index
+  \param scaleEngine Scale engine
+
+  \sa axisScaleEngine()
+*/
+void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine )
+{
+    if ( axisValid( axisId ) && scaleEngine != NULL )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        delete d.scaleEngine;
+        d.scaleEngine = scaleEngine;
+
+        d_axisData[axisId]->scaleWidget->setTransformation( 
+            scaleEngine->transformation() );
+
+        d.isValid = false;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \param axisId Axis index
+  \return Scale engine for a specific axis
+*/
+QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId )
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleEngine;
+    else
+        return NULL;
+}
+
+/*!
+  \param axisId Axis index
+  \return Scale engine for a specific axis
+*/
+const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleEngine;
+    else
+        return NULL;
+}
+/*!
+  \return \c True, if autoscaling is enabled
+  \param axisId Axis index
+*/
+bool QwtPlot::axisAutoScale( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->doAutoScale;
+    else
+        return false;
+
+}
+
+/*!
+  \return \c True, if a specified axis is enabled
+  \param axisId Axis index
+*/
+bool QwtPlot::axisEnabled( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->isEnabled;
+    else
+        return false;
+}
+
+/*!
+  \return The font of the scale labels for a specified axis
+  \param axisId Axis index
+*/
+QFont QwtPlot::axisFont( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return axisWidget( axisId )->font();
+    else
+        return QFont();
+
+}
+
+/*!
+  \return The maximum number of major ticks for a specified axis
+  \param axisId Axis index
+  \sa setAxisMaxMajor(), QwtScaleEngine::divideScale()
+*/
+int QwtPlot::axisMaxMajor( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->maxMajor;
+    else
+        return 0;
+}
+
+/*!
+  \return the maximum number of minor ticks for a specified axis
+  \param axisId Axis index
+  \sa setAxisMaxMinor(), QwtScaleEngine::divideScale()
+*/
+int QwtPlot::axisMaxMinor( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->maxMinor;
+    else
+        return 0;
+}
+
+/*!
+  \brief Return the scale division of a specified axis
+
+  axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound()
+  are the current limits of the axis scale.
+
+  \param axisId Axis index
+  \return Scale division
+
+  \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale()
+*/
+const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const
+{
+    return d_axisData[axisId]->scaleDiv;
+}
+
+/*!
+  \brief Return the scale draw of a specified axis
+
+  \param axisId Axis index
+  \return Specified scaleDraw for axis, or NULL if axis is invalid.
+*/
+const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return NULL;
+
+    return axisWidget( axisId )->scaleDraw();
+}
+
+/*!
+  \brief Return the scale draw of a specified axis
+
+  \param axisId Axis index
+  \return Specified scaleDraw for axis, or NULL if axis is invalid.
+*/
+QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId )
+{
+    if ( !axisValid( axisId ) )
+        return NULL;
+
+    return axisWidget( axisId )->scaleDraw();
+}
+
+/*!
+  \brief Return the step size parameter that has been set in setAxisScale. 
+
+  This doesn't need to be the step size of the current scale.
+
+  \param axisId Axis index
+  \return step size parameter value
+
+   \sa setAxisScale(), QwtScaleEngine::divideScale()
+*/
+double QwtPlot::axisStepSize( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return 0;
+
+    return d_axisData[axisId]->stepSize;
+}
+
+/*!
+  \brief Return the current interval of the specified axis
+
+  This is only a convenience function for axisScaleDiv( axisId )->interval();
+  
+  \param axisId Axis index
+  \return Scale interval
+
+  \sa QwtScaleDiv, axisScaleDiv()
+*/
+QwtInterval QwtPlot::axisInterval( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return QwtInterval();
+
+    return d_axisData[axisId]->scaleDiv.interval();
+}
+
+/*!
+  \return Title of a specified axis
+  \param axisId Axis index
+*/
+QwtText QwtPlot::axisTitle( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return axisWidget( axisId )->title();
+    else
+        return QwtText();
+}
+
+/*!
+  \brief Enable or disable a specified axis
+
+  When an axis is disabled, this only means that it is not
+  visible on the screen. Curves, markers and can be attached
+  to disabled axes, and transformation of screen coordinates
+  into values works as normal.
+
+  Only xBottom and yLeft are enabled by default.
+
+  \param axisId Axis index
+  \param tf \c true (enabled) or \c false (disabled)
+*/
+void QwtPlot::enableAxis( int axisId, bool tf )
+{
+    if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled )
+    {
+        d_axisData[axisId]->isEnabled = tf;
+        updateLayout();
+    }
+}
+
+/*!
+  Transform the x or y coordinate of a position in the
+  drawing region into a value.
+
+  \param axisId Axis index
+  \param pos position
+
+  \return Position as axis coordinate
+
+  \warning The position can be an x or a y coordinate,
+           depending on the specified axis.
+*/
+double QwtPlot::invTransform( int axisId, int pos ) const
+{
+    if ( axisValid( axisId ) )
+        return( canvasMap( axisId ).invTransform( pos ) );
+    else
+        return 0.0;
+}
+
+
+/*!
+  \brief Transform a value into a coordinate in the plotting region
+
+  \param axisId Axis index
+  \param value value
+  \return X or Y coordinate in the plotting region corresponding
+          to the value.
+*/
+double QwtPlot::transform( int axisId, double value ) const
+{
+    if ( axisValid( axisId ) )
+        return( canvasMap( axisId ).transform( value ) );
+    else
+        return 0.0;
+}
+
+/*!
+  \brief Change the font of an axis
+
+  \param axisId Axis index
+  \param font Font
+  \warning This function changes the font of the tick labels,
+           not of the axis title.
+*/
+void QwtPlot::setAxisFont( int axisId, const QFont &font )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setFont( font );
+}
+
+/*!
+  \brief Enable autoscaling for a specified axis
+
+  This member function is used to switch back to autoscaling mode
+  after a fixed scale has been set. Autoscaling is enabled by default.
+
+  \param axisId Axis index
+  \param on On/Off
+  \sa setAxisScale(), setAxisScaleDiv(), updateAxes()
+
+  \note The autoscaling flag has no effect until updateAxes() is executed
+        ( called by replot() ).
+*/
+void QwtPlot::setAxisAutoScale( int axisId, bool on )
+{
+    if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) )
+    {
+        d_axisData[axisId]->doAutoScale = on;
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Disable autoscaling and specify a fixed scale for a selected axis.
+
+  In updateAxes() the scale engine calculates a scale division from the 
+  specified parameters, that will be assigned to the scale widget. So 
+  updates of the scale widget usually happen delayed with the next replot.
+
+  \param axisId Axis index
+  \param min Minimum of the scale
+  \param max Maximum of the scale
+  \param stepSize Major step size. If <code>step == 0</code>, the step size is
+                  calculated automatically using the maxMajor setting.
+
+  \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale()
+*/
+void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize )
+{
+    if ( axisValid( axisId ) )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.doAutoScale = false;
+        d.isValid = false;
+
+        d.minValue = min;
+        d.maxValue = max;
+        d.stepSize = stepSize;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Disable autoscaling and specify a fixed scale for a selected axis.
+
+  The scale division will be stored locally only until the next call
+  of updateAxes(). So updates of the scale widget usually happen delayed with 
+  the next replot.
+
+  \param axisId Axis index
+  \param scaleDiv Scale division
+
+  \sa setAxisScale(), setAxisAutoScale()
+*/
+void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv )
+{
+    if ( axisValid( axisId ) )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.doAutoScale = false;
+        d.scaleDiv = scaleDiv;
+        d.isValid = true;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Set a scale draw
+
+  \param axisId Axis index
+  \param scaleDraw Object responsible for drawing scales.
+
+  By passing scaleDraw it is possible to extend QwtScaleDraw
+  functionality and let it take place in QwtPlot. Please note
+  that scaleDraw has to be created with new and will be deleted
+  by the corresponding QwtScale member ( like a child object ).
+
+  \sa QwtScaleDraw, QwtScaleWidget
+  \warning The attributes of scaleDraw will be overwritten by those of the
+           previous QwtScaleDraw.
+*/
+
+void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw )
+{
+    if ( axisValid( axisId ) )
+    {
+        axisWidget( axisId )->setScaleDraw( scaleDraw );
+        autoRefresh();
+    }
+}
+
+/*!
+  Change the alignment of the tick labels
+
+  \param axisId Axis index
+  \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
+
+  \sa QwtScaleDraw::setLabelAlignment()
+*/
+void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setLabelAlignment( alignment );
+}
+
+/*!
+  Rotate all tick labels
+
+  \param axisId Axis index
+  \param rotation Angle in degrees. When changing the label rotation,
+                  the label alignment might be adjusted too.
+
+  \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment()
+*/
+void QwtPlot::setAxisLabelRotation( int axisId, double rotation )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setLabelRotation( rotation );
+}
+
+/*!
+  Set the maximum number of minor scale intervals for a specified axis
+
+  \param axisId Axis index
+  \param maxMinor Maximum number of minor steps
+
+  \sa axisMaxMinor()
+*/
+void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor )
+{
+    if ( axisValid( axisId ) )
+    {
+        maxMinor = qBound( 0, maxMinor, 100 );
+
+        AxisData &d = *d_axisData[axisId];
+        if ( maxMinor != d.maxMinor )
+        {
+            d.maxMinor = maxMinor;
+            d.isValid = false;
+            autoRefresh();
+        }
+    }
+}
+
+/*!
+  Set the maximum number of major scale intervals for a specified axis
+
+  \param axisId Axis index
+  \param maxMajor Maximum number of major steps
+
+  \sa axisMaxMajor()
+*/
+void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor )
+{
+    if ( axisValid( axisId ) )
+    {
+        maxMajor = qBound( 1, maxMajor, 10000 );
+
+        AxisData &d = *d_axisData[axisId];
+        if ( maxMajor != d.maxMajor )
+        {
+            d.maxMajor = maxMajor;
+            d.isValid = false;
+            autoRefresh();
+        }
+    }
+}
+
+/*!
+  \brief Change the title of a specified axis
+
+  \param axisId Axis index
+  \param title axis title
+*/
+void QwtPlot::setAxisTitle( int axisId, const QString &title )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setTitle( title );
+}
+
+/*!
+  \brief Change the title of a specified axis
+
+  \param axisId Axis index
+  \param title Axis title
+*/
+void QwtPlot::setAxisTitle( int axisId, const QwtText &title )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setTitle( title );
+}
+
+/*! 
+  \brief Rebuild the axes scales
+
+  In case of autoscaling the boundaries of a scale are calculated 
+  from the bounding rectangles of all plot items, having the 
+  QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ). 
+  Then a scale division is calculated ( QwtScaleEngine::didvideScale() ) 
+  and assigned to scale widget.
+
+  When the scale boundaries have been assigned with setAxisScale() a 
+  scale division is calculated ( QwtScaleEngine::didvideScale() )
+  for this interval and assigned to the scale widget.
+
+  When the scale has been set explicitly by setAxisScaleDiv() the 
+  locally stored scale division gets assigned to the scale widget.
+
+  The scale widget indicates modifications by emitting a 
+  QwtScaleWidget::scaleDivChanged() signal.
+
+  updateAxes() is usually called by replot(). 
+
+  \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot()
+      QwtPlotItem::boundingRect()
+ */
+void QwtPlot::updateAxes()
+{
+    // Find bounding interval of the item data
+    // for all axes, where autoscaling is enabled
+
+    QwtInterval intv[axisCnt];
+
+    const QwtPlotItemList& itmList = itemList();
+
+    QwtPlotItemIterator it;
+    for ( it = itmList.begin(); it != itmList.end(); ++it )
+    {
+        const QwtPlotItem *item = *it;
+
+        if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) )
+            continue;
+
+        if ( !item->isVisible() )
+            continue;
+
+        if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) )
+        {
+            const QRectF rect = item->boundingRect();
+
+            if ( rect.width() >= 0.0 )
+                intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() );
+
+            if ( rect.height() >= 0.0 )
+                intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() );
+        }
+    }
+
+    // Adjust scales
+
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        double minValue = d.minValue;
+        double maxValue = d.maxValue;
+        double stepSize = d.stepSize;
+
+        if ( d.doAutoScale && intv[axisId].isValid() )
+        {
+            d.isValid = false;
+
+            minValue = intv[axisId].minValue();
+            maxValue = intv[axisId].maxValue();
+
+            d.scaleEngine->autoScale( d.maxMajor,
+                minValue, maxValue, stepSize );
+        }
+        if ( !d.isValid )
+        {
+            d.scaleDiv = d.scaleEngine->divideScale(
+                minValue, maxValue,
+                d.maxMajor, d.maxMinor, stepSize );
+            d.isValid = true;
+        }
+
+        QwtScaleWidget *scaleWidget = axisWidget( axisId );
+        scaleWidget->setScaleDiv( d.scaleDiv );
+
+        int startDist, endDist;
+        scaleWidget->getBorderDistHint( startDist, endDist );
+        scaleWidget->setBorderDist( startDist, endDist );
+    }
+
+    for ( it = itmList.begin(); it != itmList.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) )
+        {
+            item->updateScaleDiv( axisScaleDiv( item->xAxis() ),
+                axisScaleDiv( item->yAxis() ) );
+        }
+    }
+}
+
diff --git a/qwt/qwt_plot_barchart.cpp b/qwt/qwt_plot_barchart.cpp
new file mode 100644
index 0000000..b3455af
--- /dev/null
+++ b/qwt/qwt_plot_barchart.cpp
@@ -0,0 +1,455 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_barchart.h"
+#include "qwt_scale_map.h"
+#include "qwt_column_symbol.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+
+class QwtPlotBarChart::PrivateData
+{
+public:
+    PrivateData():
+        symbol( NULL ),
+        legendMode( QwtPlotBarChart::LegendChartTitle )
+    {
+    }
+ 
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtColumnSymbol *symbol;
+    QwtPlotBarChart::LegendMode legendMode;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ):
+    QwtPlotAbstractBarChart( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotBarChart::QwtPlotBarChart( const QString &title ):
+    QwtPlotAbstractBarChart( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotBarChart::~QwtPlotBarChart()
+{
+    delete d_data;
+}
+
+void QwtPlotBarChart::init()
+{
+    d_data = new PrivateData;
+    setData( new QwtPointSeriesData() );
+}
+
+//! \return QwtPlotItem::Rtti_PlotBarChart
+int QwtPlotBarChart::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotBarChart;
+}
+
+/*!
+  Initialize data with an array of points
+
+  \param samples Vector of points
+  \note QVector is implicitly shared
+  \note QPolygonF is derived from QVector<QPointF>
+*/
+void QwtPlotBarChart::setSamples(
+    const QVector<QPointF> &samples )
+{
+    setData( new QwtPointSeriesData( samples ) );
+}
+
+/*!
+  Initialize data with an array of doubles
+
+  The indices in the array are taken as x coordinate,
+  while the doubles are interpreted as y values.
+
+  \param samples Vector of y coordinates
+  \note QVector is implicitly shared
+*/
+void QwtPlotBarChart::setSamples(
+    const QVector<double> &samples )
+{
+    QVector<QPointF> points;
+    for ( int i = 0; i < samples.size(); i++ )
+        points += QPointF( i, samples[ i ] );
+
+    setData( new QwtPointSeriesData( points ) );
+}
+
+/*!
+  Assign a series of samples
+
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotBarChart::setSamples( QwtSeriesData<QPointF> *data )
+{
+    setData( data );
+}
+
+/*!
+  \brief Assign a symbol
+
+  The bar chart will take the ownership of the symbol, hence the previously
+  set symbol will be delete by setting a new one. If \p symbol is 
+  \c NULL no symbol will be drawn.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtColumnSymbol *QwtPlotBarChart::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Set the mode that decides what to display on the legend
+
+  In case of LegendBarTitles barTitle() needs to be overloaded
+  to return individual titles for each bar.
+
+  \param mode New mode
+  \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute
+ */
+void QwtPlotBarChart::setLegendMode( LegendMode mode )
+{
+    if ( mode != d_data->legendMode )
+    {
+        d_data->legendMode = mode;
+        legendChanged();
+    }
+}
+
+/*!
+  \return Legend mode
+  \sa setLegendMode()
+ */
+QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const
+{
+    return d_data->legendMode;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotBarChart::boundingRect() const
+{
+    const size_t numSamples = dataSize();
+    if ( numSamples == 0 )
+        return QwtPlotSeriesItem::boundingRect();
+
+    const double baseLine = baseline();
+
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.bottom() < baseLine )
+        rect.setBottom( baseLine );
+    if ( rect.top() > baseLine )
+        rect.setTop( baseLine );
+
+    if ( rect.isValid() && ( orientation() == Qt::Horizontal ) )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the bar chart
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rect of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotBarChart::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+
+    const QRectF br = data()->boundingRect();
+    const QwtInterval interval( br.left(), br.right() );
+
+    painter->save();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        drawSample( painter, xMap, yMap,
+                    canvasRect, interval, i, sample( i ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rect of the canvas
+  \param boundingInterval Bounding interval of sample values
+  \param index Index of the sample
+  \param sample Value of the sample
+
+  \sa drawSeries()
+*/
+void QwtPlotBarChart::drawSample( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, const QwtInterval &boundingInterval,
+    int index, const QPointF &sample ) const
+{
+    QwtColumnRect barRect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        const double barHeight = sampleWidth( yMap, canvasRect.height(),
+            boundingInterval.width(), sample.y() );
+
+        const double x1 = xMap.transform( baseline() );
+        const double x2 = xMap.transform( sample.y() );
+
+        const double y = yMap.transform( sample.x() );
+        const double y1 = y - 0.5 * barHeight;
+        const double y2 = y + 0.5 * barHeight;
+
+        barRect.direction = ( x1 < x2 ) ?
+            QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+
+        barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+        barRect.vInterval = QwtInterval( y1, y2 );
+    }
+    else
+    {
+        const double barWidth = sampleWidth( xMap, canvasRect.width(),
+            boundingInterval.width(), sample.y() );
+
+        const double x = xMap.transform( sample.x() );
+        const double x1 = x - 0.5 * barWidth;
+        const double x2 = x + 0.5 * barWidth;
+
+        const double y1 = yMap.transform( baseline() );
+        const double y2 = yMap.transform( sample.y() );
+
+        barRect.direction = ( y1 < y2 ) ?
+            QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+        barRect.hInterval = QwtInterval( x1, x2 );
+        barRect.vInterval = QwtInterval( y1, y2 ).normalized();
+    }
+
+    drawBar( painter, index, sample, barRect );
+}
+
+/*!
+  Draw a bar 
+
+  \param painter Painter
+  \param sampleIndex Index of the sample represented by the bar
+  \param sample Value of the sample
+  \param rect Bounding rectangle of the bar
+ */
+void QwtPlotBarChart::drawBar( QPainter *painter,
+    int sampleIndex, const QPointF &sample, 
+    const QwtColumnRect &rect ) const
+{
+    const QwtColumnSymbol *specialSym = 
+        specialSymbol( sampleIndex, sample );
+
+    const QwtColumnSymbol *sym = specialSym;
+    if ( sym == NULL )
+        sym = d_data->symbol;
+
+    if ( sym )
+    {
+        sym->draw( painter, rect );
+    }
+    else
+    {
+        // we build a temporary default symbol
+        QwtColumnSymbol sym( QwtColumnSymbol::Box );
+        sym.setLineWidth( 1 );
+        sym.setFrameStyle( QwtColumnSymbol::Plain );
+        sym.draw( painter, rect );
+    }
+
+    delete specialSym;
+}
+
+/*!
+  Needs to be overloaded to return a 
+  non default symbol for a specific sample
+
+  \param sampleIndex Index of the sample represented by the bar
+  \param sample Value of the sample
+
+  \return NULL, indicating to use the default symbol
+ */
+QwtColumnSymbol *QwtPlotBarChart::specialSymbol( 
+    int sampleIndex, const QPointF &sample ) const
+{
+    Q_UNUSED( sampleIndex );
+    Q_UNUSED( sample );
+
+    return NULL;
+}
+
+/*!
+  \brief Return the title of a bar
+
+  In LegendBarTitles mode the title is displayed on
+  the legend entry corresponding to a bar.
+
+  The default implementation is a dummy, that is intended
+  to be overloaded.
+
+  \param sampleIndex Index of the bar
+  \return An empty text
+  \sa LegendBarTitles
+ */
+QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const
+{
+    Q_UNUSED( sampleIndex );
+    return QwtText();
+}
+
+/*!
+   \brief Return all information, that is needed to represent
+          the item on the legend
+
+   In case of LegendBarTitles an entry for each bar is returned,
+   otherwise the chart is represented like any other plot item
+   from its title() and the legendIcon().
+
+   \return Information, that is needed to represent the item on the legend
+   \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem
+ */
+QList<QwtLegendData> QwtPlotBarChart::legendData() const
+{
+    QList<QwtLegendData> list;
+
+    if ( d_data->legendMode == LegendBarTitles )
+    {
+        const size_t numSamples = dataSize();
+        for ( size_t i = 0; i < numSamples; i++ )
+        {
+            QwtLegendData data;
+
+            QVariant titleValue;
+            qVariantSetValue( titleValue, barTitle( i ) );
+            data.setValue( QwtLegendData::TitleRole, titleValue );
+
+            if ( !legendIconSize().isEmpty() )
+            {
+                QVariant iconValue;
+                qVariantSetValue( iconValue,
+                    legendIcon( i, legendIconSize() ) );
+
+                data.setValue( QwtLegendData::IconRole, iconValue );
+            }
+
+            list += data;
+        }
+    }
+    else
+    {
+        return QwtPlotAbstractBarChart::legendData();
+    }
+
+    return list;
+}
+
+/*!
+   \return Icon representing a bar or the chart on the legend
+
+   When the legendMode() is LegendBarTitles the icon shows
+   the bar corresponding to index - otherwise the bar
+   displays the default symbol.
+
+   \param index Index of the legend entry 
+   \param size Icon size
+
+   \sa setLegendMode(), drawBar(), 
+       QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+ */
+QwtGraphic QwtPlotBarChart::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    QwtColumnRect column;
+    column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
+    column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    int barIndex = -1;
+    if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles )
+        barIndex = index;
+        
+    drawBar( &painter, barIndex, QPointF(), column );
+
+    return icon;
+}
diff --git a/qwt/qwt_plot_barchart.h b/qwt/qwt_plot_barchart.h
new file mode 100644
index 0000000..d47bfb9
--- /dev/null
+++ b/qwt/qwt_plot_barchart.h
@@ -0,0 +1,118 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_BAR_CHART_H
+#define QWT_PLOT_BAR_CHART_H
+
+#include "qwt_global.h"
+#include "qwt_plot_abstract_barchart.h"
+#include "qwt_series_data.h"
+
+class QwtColumnRect;
+class QwtColumnSymbol;
+
+/*!
+  \brief QwtPlotBarChart displays a series of a values as bars.
+
+  Each bar might be customized individually by implementing
+  a specialSymbol(). Otherwise it is rendered using a default symbol.
+
+  Depending on its orientation() the bars are displayed horizontally 
+  or vertically. The bars cover the interval between the baseline() 
+  and the value.
+
+  By activating the LegendBarTitles mode each sample will have
+  its own entry on the legend.
+
+  The most common use case of a bar chart is to display a
+  list of y coordinates, where the x coordinate is simply the index
+  in the list. But for other situations ( f.e. when values are related
+  to dates ) it is also possible to set x coordinates explicitly.
+
+  \sa QwtPlotMultiBarChart, QwtPlotHistogram, QwtPlotCurve::Sticks,
+      QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline()
+ */
+class QWT_EXPORT QwtPlotBarChart:
+    public QwtPlotAbstractBarChart, public QwtSeriesStore<QPointF>
+{
+public:
+    /*!
+      \brief Legend modes.
+
+      The default setting is QwtPlotBarChart::LegendChartTitle.
+      \sa setLegendMode(), legendMode()
+    */
+    enum LegendMode
+    {
+        /*! 
+          One entry on the legend showing the default symbol
+          and the title() of the chart
+
+          \sa QwtPlotItem::title()
+         */
+        LegendChartTitle,
+
+        /*!
+          One entry for each value showing the individual symbol
+          of the corresponding bar and the bar title.
+
+          \sa specialSymbol(), barTitle()
+         */
+        LegendBarTitles
+    };
+
+    explicit QwtPlotBarChart( const QString &title = QString::null );
+    explicit QwtPlotBarChart( const QwtText &title );
+
+    virtual ~QwtPlotBarChart();
+
+    virtual int rtti() const;
+
+    void setSamples( const QVector<QPointF> & );
+    void setSamples( const QVector<double> & );
+    void setSamples( QwtSeriesData<QPointF> *series );
+
+    void setSymbol( QwtColumnSymbol * );
+    const QwtColumnSymbol *symbol() const;
+
+    void setLegendMode( LegendMode );
+    LegendMode legendMode() const;
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtColumnSymbol *specialSymbol( 
+        int sampleIndex, const QPointF& ) const;
+
+    virtual QwtText barTitle( int sampleIndex ) const;
+
+protected:
+    virtual void drawSample( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, const QwtInterval &boundingInterval,
+        int index, const QPointF& sample ) const;
+
+    virtual void drawBar( QPainter *,
+        int sampleIndex, const QPointF& point, 
+        const QwtColumnRect & ) const;
+
+    QList<QwtLegendData> legendData() const;
+    QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_canvas.cpp b/qwt/qwt_plot_canvas.cpp
new file mode 100644
index 0000000..0271713
--- /dev/null
+++ b/qwt/qwt_plot_canvas.cpp
@@ -0,0 +1,1097 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_canvas.h"
+#include "qwt_painter.h"
+#include "qwt_null_paintdevice.h"
+#include "qwt_math.h"
+#include "qwt_plot.h"
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qpaintengine.h>
+#include <qevent.h>
+
+class QwtStyleSheetRecorder: public QwtNullPaintDevice
+{
+public:
+    QwtStyleSheetRecorder( const QSize &size ):
+        d_size( size )
+    {
+    }
+
+    virtual void updateState( const QPaintEngineState &state )
+    {
+        if ( state.state() & QPaintEngine::DirtyPen )
+        {
+            d_pen = state.pen();
+        }
+        if ( state.state() & QPaintEngine::DirtyBrush )
+        {
+            d_brush = state.brush();
+        }
+        if ( state.state() & QPaintEngine::DirtyBrushOrigin )
+        {
+            d_origin = state.brushOrigin();
+        }
+    }
+
+    virtual void drawRects(const QRectF *rects, int count )
+    {
+        for ( int i = 0; i < count; i++ )
+            border.rectList += rects[i];
+    }
+
+    virtual void drawPath( const QPainterPath &path )
+    {
+        const QRectF rect( QPointF( 0.0, 0.0 ), d_size );
+        if ( path.controlPointRect().contains( rect.center() ) )
+        {
+            setCornerRects( path );
+            alignCornerRects( rect );
+
+            background.path = path;
+            background.brush = d_brush;
+            background.origin = d_origin;
+        }
+        else
+        {
+            border.pathList += path;
+        }
+    }
+
+    void setCornerRects( const QPainterPath &path )
+    {
+        QPointF pos( 0.0, 0.0 );
+
+        for ( int i = 0; i < path.elementCount(); i++ )
+        {
+            QPainterPath::Element el = path.elementAt(i); 
+            switch( el.type )
+            {
+                case QPainterPath::MoveToElement:
+                case QPainterPath::LineToElement:
+                {
+                    pos.setX( el.x );
+                    pos.setY( el.y );
+                    break;
+                }
+                case QPainterPath::CurveToElement:
+                {
+                    QRectF r( pos, QPointF( el.x, el.y ) );
+                    clipRects += r.normalized();
+
+                    pos.setX( el.x );
+                    pos.setY( el.y );
+
+                    break;
+                }
+                case QPainterPath::CurveToDataElement:
+                {
+                    if ( clipRects.size() > 0 )
+                    {
+                        QRectF r = clipRects.last();
+                        r.setCoords( 
+                            qMin( r.left(), el.x ),
+                            qMin( r.top(), el.y ),
+                            qMax( r.right(), el.x ),
+                            qMax( r.bottom(), el.y )
+                        );
+                        clipRects.last() = r.normalized();
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+protected:
+    virtual QSize sizeMetrics() const
+    {
+        return d_size;
+    }
+
+private:
+    void alignCornerRects( const QRectF &rect )
+    {
+        for ( int i = 0; i < clipRects.size(); i++ )
+        {
+            QRectF &r = clipRects[i];
+            if ( r.center().x() < rect.center().x() )
+                r.setLeft( rect.left() );
+            else
+                r.setRight( rect.right() );
+
+            if ( r.center().y() < rect.center().y() )
+                r.setTop( rect.top() );
+            else
+                r.setBottom( rect.bottom() );
+        }
+    }
+
+
+public:
+    QVector<QRectF> clipRects;
+
+    struct Border
+    {
+        QList<QPainterPath> pathList;
+        QList<QRectF> rectList;
+        QRegion clipRegion;
+    } border;
+
+    struct Background
+    {
+        QPainterPath path;
+        QBrush brush;
+        QPointF origin;
+    } background;
+
+private:
+    const QSize d_size;
+
+    QPen d_pen;
+    QBrush d_brush;
+    QPointF d_origin;
+};
+
+static void qwtDrawBackground( QPainter *painter, QwtPlotCanvas *canvas )
+{
+    painter->save();
+
+    const QPainterPath borderClip = canvas->borderPath( canvas->rect() );
+    if ( !borderClip.isEmpty() )
+        painter->setClipPath( borderClip, Qt::IntersectClip );
+
+    const QBrush &brush = 
+        canvas->palette().brush( canvas->backgroundRole() );
+
+    if ( brush.style() == Qt::TexturePattern )
+    {
+        QPixmap pm( canvas->size() );
+        QwtPainter::fillPixmap( canvas, pm );
+        painter->drawPixmap( 0, 0, pm );
+    }
+    else if ( brush.gradient() )
+    {
+        QVector<QRect> rects;
+
+        if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode )
+        {
+            rects += canvas->rect();
+        } 
+        else 
+        {
+            rects = painter->clipRegion().rects();
+        }
+
+#if 1
+        bool useRaster = false;
+
+        if ( painter->paintEngine()->type() == QPaintEngine::X11 )
+        {
+            // Qt 4.7.1: gradients on X11 are broken ( subrects + 
+            // QGradient::StretchToDeviceMode ) and horrible slow.
+            // As workaround we have to use the raster paintengine.
+            // Even if the QImage -> QPixmap translation is slow
+            // it is three times faster, than using X11 directly
+
+            useRaster = true;
+        }
+#endif
+        if ( useRaster )
+        {
+            QImage::Format format = QImage::Format_RGB32;
+
+            const QGradientStops stops = brush.gradient()->stops();
+            for ( int i = 0; i < stops.size(); i++ )
+            {
+                if ( stops[i].second.alpha() != 255 )
+                {
+                    // don't use Format_ARGB32_Premultiplied. It's
+                    // recommended by the Qt docs, but QPainter::drawImage()
+                    // is horrible slow on X11.
+
+                    format = QImage::Format_ARGB32;
+                    break;
+                }
+            }
+            
+            QImage image( canvas->size(), format );
+
+            QPainter p( &image );
+            p.setPen( Qt::NoPen );
+            p.setBrush( brush );
+
+            p.drawRects( rects );
+
+            p.end();
+
+            painter->drawImage( 0, 0, image );
+        }
+        else
+        {
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( brush );
+
+            painter->drawRects( rects );
+        }
+    }
+    else
+    {
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( brush );
+
+        painter->drawRects( painter->clipRegion().rects() );
+
+    }
+
+    painter->restore();
+}
+
+static inline void qwtRevertPath( QPainterPath &path )
+{
+    if ( path.elementCount() == 4 )
+    {
+        QPainterPath::Element el0 = path.elementAt(0);
+        QPainterPath::Element el3 = path.elementAt(3);
+
+        path.setElementPositionAt( 0, el3.x, el3.y );
+        path.setElementPositionAt( 3, el0.x, el0.y );
+    }
+}
+
+static QPainterPath qwtCombinePathList( const QRectF &rect, 
+    const QList<QPainterPath> &pathList )
+{
+    if ( pathList.isEmpty() )
+        return QPainterPath();
+
+    QPainterPath ordered[8]; // starting top left
+
+    for ( int i = 0; i < pathList.size(); i++ )
+    {
+        int index = -1;
+        QPainterPath subPath = pathList[i];
+
+        const QRectF br = pathList[i].controlPointRect();
+        if ( br.center().x() < rect.center().x() )
+        {
+            if ( br.center().y() < rect.center().y() )
+            {
+                if ( qAbs( br.top() - rect.top() ) < 
+                    qAbs( br.left() - rect.left() ) )
+                {
+                    index = 1;
+                }
+                else
+                {
+                    index = 0;
+                }
+            }
+            else
+            {
+                if ( qAbs( br.bottom() - rect.bottom() ) < 
+                    qAbs( br.left() - rect.left() ) )
+                {
+                    index = 6;
+                }
+                else
+                {
+                    index = 7;
+                }
+            }
+
+            if ( subPath.currentPosition().y() > br.center().y() )
+                qwtRevertPath( subPath );
+        }
+        else
+        {
+            if ( br.center().y() < rect.center().y() )
+            {
+                if ( qAbs( br.top() - rect.top() ) < 
+                    qAbs( br.right() - rect.right() ) )
+                {
+                    index = 2;
+                }
+                else
+                {
+                    index = 3;
+                }
+            }
+            else
+            {
+                if ( qAbs( br.bottom() - rect.bottom() ) < 
+                    qAbs( br.right() - rect.right() ) )
+                {
+                    index = 5;
+                }
+                else
+                {
+                    index = 4;
+                }
+            }
+            if ( subPath.currentPosition().y() < br.center().y() )
+                qwtRevertPath( subPath );
+        }   
+        ordered[index] = subPath;
+    }
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() )
+        {
+            // we don't accept incomplete rounded borders
+            return QPainterPath();
+        }
+    }
+
+
+    const QPolygonF corners( rect );
+
+    QPainterPath path;
+    //path.moveTo( rect.topLeft() );
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        if ( ordered[2 * i].isEmpty() )
+        {
+            path.lineTo( corners[i] );
+        }
+        else
+        {
+            path.connectPath( ordered[2 * i] );
+            path.connectPath( ordered[2 * i + 1] );
+        }
+    }
+
+    path.closeSubpath();
+
+#if 0
+    return path.simplified();
+#else
+    return path;
+#endif
+}
+
+static inline void qwtDrawStyledBackground( 
+    QWidget *w, QPainter *painter )
+{
+    QStyleOption opt;
+    opt.initFrom(w);
+    w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w);
+}
+
+static QWidget *qwtBackgroundWidget( QWidget *w )
+{
+    if ( w->parentWidget() == NULL )
+        return w;
+
+    if ( w->autoFillBackground() )
+    {
+        const QBrush brush = w->palette().brush( w->backgroundRole() );
+        if ( brush.color().alpha() > 0 )
+            return w;
+    }
+
+    if ( w->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QImage image( 1, 1, QImage::Format_ARGB32 );
+        image.fill( Qt::transparent );
+
+        QPainter painter( &image );
+        painter.translate( -w->rect().center() );
+        qwtDrawStyledBackground( w, &painter );
+        painter.end();
+
+        if ( qAlpha( image.pixel( 0, 0 ) ) != 0 )
+            return w;
+    }
+
+    return qwtBackgroundWidget( w->parentWidget() );
+}
+
+static void qwtFillBackground( QPainter *painter, 
+    QWidget *widget, const QVector<QRectF> &fillRects )
+{
+    if ( fillRects.isEmpty() )
+        return;
+
+    QRegion clipRegion;
+    if ( painter->hasClipping() )
+        clipRegion = painter->transform().map( painter->clipRegion() );
+    else
+        clipRegion = widget->contentsRect();
+
+    // Try to find out which widget fills
+    // the unfilled areas of the styled background
+
+    QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() );
+
+    for ( int i = 0; i < fillRects.size(); i++ )
+    {
+        const QRect rect = fillRects[i].toAlignedRect();
+        if ( clipRegion.intersects( rect ) )
+        {
+            QPixmap pm( rect.size() );
+            QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) );
+            painter->drawPixmap( rect, pm );
+        }
+    }
+}
+
+static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas )
+{
+    QVector<QRectF> rects;
+
+    if ( canvas->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QwtStyleSheetRecorder recorder( canvas->size() );
+
+        QPainter p( &recorder );
+        qwtDrawStyledBackground( canvas, &p );
+        p.end();
+
+        if ( recorder.background.brush.isOpaque() )
+            rects = recorder.clipRects;
+        else
+            rects += canvas->rect();
+    }
+    else
+    {
+        const QRectF r = canvas->rect();
+        const double radius = canvas->borderRadius();
+        if ( radius > 0.0 )
+        {
+            QSizeF sz( radius, radius );
+
+            rects += QRectF( r.topLeft(), sz );
+            rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz );
+            rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz );
+            rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz );
+        }
+    }
+
+    qwtFillBackground( painter, canvas, rects);
+}
+
+
+class QwtPlotCanvas::PrivateData
+{
+public:
+    PrivateData():
+        focusIndicator( NoFocusIndicator ),
+        borderRadius( 0 ),
+        paintAttributes( 0 ),
+        backingStore( NULL )
+    {
+        styleSheet.hasBorder = false;
+    }
+
+    ~PrivateData()
+    {
+        delete backingStore;
+    }
+
+    FocusIndicator focusIndicator;
+    double borderRadius;
+    QwtPlotCanvas::PaintAttributes paintAttributes;
+    QPixmap *backingStore;
+
+    struct StyleSheet
+    {
+        bool hasBorder;
+        QPainterPath borderPath;
+        QVector<QRectF> cornerRects;
+
+        struct StyleSheetBackground
+        {
+            QBrush brush;
+            QPointF origin;
+        } background;
+
+    } styleSheet;
+
+};
+
+/*! 
+  \brief Constructor
+
+  \param plot Parent plot widget
+  \sa QwtPlot::setCanvas()
+*/
+QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ):
+    QFrame( plot )
+{
+    setFrameStyle( QFrame::Panel | QFrame::Sunken );
+    setLineWidth( 2 );
+
+    d_data = new PrivateData;
+
+#ifndef QT_NO_CURSOR
+    setCursor( Qt::CrossCursor );
+#endif
+
+    setAutoFillBackground( true );
+    setPaintAttribute( QwtPlotCanvas::BackingStore, true );
+    setPaintAttribute( QwtPlotCanvas::Opaque, true );
+    setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true );
+}
+
+//! Destructor
+QwtPlotCanvas::~QwtPlotCanvas()
+{
+    delete d_data;
+}
+
+//! Return parent plot widget
+QwtPlot *QwtPlotCanvas::plot()
+{
+    return qobject_cast<QwtPlot *>( parent() );
+}
+
+//! Return parent plot widget
+const QwtPlot *QwtPlotCanvas::plot() const
+{
+    return qobject_cast<const QwtPlot *>( parent() );
+}
+
+/*!
+  \brief Changing the paint attributes
+
+  \param attribute Paint attribute
+  \param on On/Off
+
+  \sa testPaintAttribute(), backingStore()
+*/
+void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( bool( d_data->paintAttributes & attribute ) == on )
+        return;
+
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+
+    switch ( attribute )
+    {
+        case BackingStore:
+        {
+            if ( on )
+            {
+                if ( d_data->backingStore == NULL )
+                    d_data->backingStore = new QPixmap();
+
+                if ( isVisible() )
+                {
+#if QT_VERSION >= 0x050000
+                    *d_data->backingStore = grab( rect() );
+#else
+                    *d_data->backingStore = 
+                        QPixmap::grabWidget( this, rect() );
+#endif
+                }
+            }
+            else
+            {
+                delete d_data->backingStore;
+                d_data->backingStore = NULL;
+            }
+            break;
+        }
+        case Opaque:
+        {
+            if ( on )
+                setAttribute( Qt::WA_OpaquePaintEvent, true );
+
+            break;
+        }
+        case HackStyledBackground:
+        case ImmediatePaint:
+        {
+            break;
+        }
+    }
+}
+
+/*!
+  Test whether a paint attribute is enabled
+
+  \param attribute Paint attribute
+  \return true, when attribute is enabled
+  \sa setPaintAttribute()
+*/
+bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return d_data->paintAttributes & attribute;
+}
+
+//! \return Backing store, might be null
+const QPixmap *QwtPlotCanvas::backingStore() const
+{
+    return d_data->backingStore;
+}
+
+//! Invalidate the internal backing store
+void QwtPlotCanvas::invalidateBackingStore()
+{
+    if ( d_data->backingStore )
+        *d_data->backingStore = QPixmap();
+}
+
+/*!
+  Set the focus indicator
+
+  \sa FocusIndicator, focusIndicator()
+*/
+void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator )
+{
+    d_data->focusIndicator = focusIndicator;
+}
+
+/*!
+  \return Focus indicator
+
+  \sa FocusIndicator, setFocusIndicator()
+*/
+QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const
+{
+    return d_data->focusIndicator;
+}
+
+/*!
+  Set the radius for the corners of the border frame
+
+  \param radius Radius of a rounded corner
+  \sa borderRadius()
+*/
+void QwtPlotCanvas::setBorderRadius( double radius )
+{
+    d_data->borderRadius = qMax( 0.0, radius );
+}
+
+/*!
+  \return Radius for the corners of the border frame
+  \sa setBorderRadius()
+*/
+double QwtPlotCanvas::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+  Qt event handler for QEvent::PolishRequest and QEvent::StyleChange
+
+  \param event Qt Event
+  \return See QFrame::event()
+*/
+bool QwtPlotCanvas::event( QEvent *event )
+{
+    if ( event->type() == QEvent::PolishRequest ) 
+    {
+        if ( testPaintAttribute( QwtPlotCanvas::Opaque ) )
+        {
+            // Setting a style sheet changes the 
+            // Qt::WA_OpaquePaintEvent attribute, but we insist
+            // on painting the background.
+            
+            setAttribute( Qt::WA_OpaquePaintEvent, true );
+        }
+    }
+
+    if ( event->type() == QEvent::PolishRequest || 
+        event->type() == QEvent::StyleChange )
+    {
+        updateStyleSheetInfo();
+    }
+
+    return QFrame::event( event );
+}
+
+/*!
+  Paint event
+  \param event Paint event
+*/
+void QwtPlotCanvas::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) &&
+        d_data->backingStore != NULL )
+    {
+        QPixmap &bs = *d_data->backingStore;
+        if ( bs.size() != size() )
+        {
+            bs = QwtPainter::backingStore( this, size() );
+
+            if ( testAttribute(Qt::WA_StyledBackground) )
+            {
+                QPainter p( &bs );
+                qwtFillBackground( &p, this );
+                drawCanvas( &p, true );
+            }
+            else
+            {
+                QPainter p;
+                if ( d_data->borderRadius <= 0.0 )
+                {
+                    QwtPainter::fillPixmap( this, bs );
+                    p.begin( &bs );
+                    drawCanvas( &p, false );
+                }
+                else
+                {
+                    p.begin( &bs );
+                    qwtFillBackground( &p, this );
+                    drawCanvas( &p, true );
+                }
+
+                if ( frameWidth() > 0 )
+                    drawBorder( &p );
+            }
+        }
+
+        painter.drawPixmap( 0, 0, *d_data->backingStore );
+    }
+    else
+    {
+        if ( testAttribute(Qt::WA_StyledBackground ) )
+        {
+            if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
+            {
+                qwtFillBackground( &painter, this );
+                drawCanvas( &painter, true );
+            }
+            else
+            {
+                drawCanvas( &painter, false );
+            }
+        }
+        else
+        {
+            if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
+            {
+                if ( autoFillBackground() )
+                {
+                    qwtFillBackground( &painter, this );
+                    qwtDrawBackground( &painter, this );
+                }
+            }
+            else
+            {
+                if ( borderRadius() > 0.0 )
+                {
+                    QPainterPath clipPath;
+                    clipPath.addRect( rect() );
+                    clipPath = clipPath.subtracted( borderPath( rect() ) );
+
+                    painter.save();
+
+                    painter.setClipPath( clipPath, Qt::IntersectClip );
+                    qwtFillBackground( &painter, this );
+                    qwtDrawBackground( &painter, this );
+
+                    painter.restore();
+                }
+            }
+
+            drawCanvas( &painter, false );
+
+            if ( frameWidth() > 0 ) 
+                drawBorder( &painter );
+        }
+    }
+
+    if ( hasFocus() && focusIndicator() == CanvasFocusIndicator )
+        drawFocusIndicator( &painter );
+}
+
+void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) 
+{
+    bool hackStyledBackground = false;
+
+    if ( withBackground && testAttribute( Qt::WA_StyledBackground ) 
+        && testPaintAttribute( HackStyledBackground ) )
+    {
+        // Antialiasing rounded borders is done by
+        // inserting pixels with colors between the 
+        // border color and the color on the canvas,
+        // When the border is painted before the plot items
+        // these colors are interpolated for the canvas
+        // and the plot items need to be clipped excluding
+        // the anialiased pixels. In situations, where
+        // the plot items fill the area at the rounded
+        // borders this is noticeable.
+        // The only way to avoid these annoying "artefacts"
+        // is to paint the border on top of the plot items.
+
+        if ( d_data->styleSheet.hasBorder &&
+            !d_data->styleSheet.borderPath.isEmpty() )
+        {
+            // We have a border with at least one rounded corner
+            hackStyledBackground = true;
+        }
+    }
+
+    if ( withBackground )
+    {
+        painter->save();
+
+        if ( testAttribute( Qt::WA_StyledBackground ) )
+        {
+            if ( hackStyledBackground )
+            {
+                // paint background without border
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( d_data->styleSheet.background.brush ); 
+                painter->setBrushOrigin( d_data->styleSheet.background.origin );
+                painter->setClipPath( d_data->styleSheet.borderPath );
+                painter->drawRect( contentsRect() );
+            }
+            else
+            {
+                qwtDrawStyledBackground( this, painter );
+            }
+        }
+        else if ( autoFillBackground() )
+        {
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( palette().brush( backgroundRole() ) );
+
+            if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) )
+            {
+                if ( frameWidth() > 0 )
+                {
+                    painter->setClipPath( borderPath( rect() ) );
+                    painter->drawRect( rect() );
+                }
+                else
+                {
+                    painter->setRenderHint( QPainter::Antialiasing, true );
+                    painter->drawPath( borderPath( rect() ) );
+                }
+            }
+            else
+            {
+                painter->drawRect( rect() );
+            }
+        }
+
+        painter->restore();
+    }
+
+    painter->save();
+
+    if ( !d_data->styleSheet.borderPath.isEmpty() )
+    {
+        painter->setClipPath( 
+            d_data->styleSheet.borderPath, Qt::IntersectClip );
+    }
+    else
+    {
+        if ( d_data->borderRadius > 0.0 )
+            painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip );
+        else
+            painter->setClipRect( contentsRect(), Qt::IntersectClip );
+    }
+
+    plot()->drawCanvas( painter );
+
+    painter->restore();
+
+    if ( withBackground && hackStyledBackground )
+    {
+        // Now paint the border on top
+        QStyleOptionFrame opt;
+        opt.initFrom(this);
+        style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this);
+    }
+}
+
+/*!
+  Draw the border of the plot canvas
+
+  \param painter Painter
+  \sa setBorderRadius()
+*/
+void QwtPlotCanvas::drawBorder( QPainter *painter )
+{
+    if ( d_data->borderRadius > 0 )
+    {
+        if ( frameWidth() > 0 )
+        {
+            QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ), 
+                d_data->borderRadius, d_data->borderRadius,
+                palette(), frameWidth(), frameStyle() );
+        }
+    }
+    else
+    {
+#if QT_VERSION >= 0x040500
+        QStyleOptionFrameV3 opt;
+        opt.init(this);
+
+        int frameShape  = frameStyle() & QFrame::Shape_Mask;
+        int frameShadow = frameStyle() & QFrame::Shadow_Mask;
+
+        opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape );
+#if 0
+        opt.rect = frameRect();
+#endif
+
+        switch (frameShape) 
+        {
+            case QFrame::Box:
+            case QFrame::HLine:
+            case QFrame::VLine:
+            case QFrame::StyledPanel:
+            case QFrame::Panel:
+            {
+                opt.lineWidth = lineWidth();
+                opt.midLineWidth = midLineWidth();
+                break; 
+            }
+            default: 
+            {
+                opt.lineWidth = frameWidth();
+                break;
+            }
+        }
+    
+        if ( frameShadow == Sunken )
+            opt.state |= QStyle::State_Sunken;
+        else if ( frameShadow == Raised )
+            opt.state |= QStyle::State_Raised;
+
+        style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this);
+#else
+        drawFrame( painter );
+#endif
+    }
+}
+
+/*!
+  Resize event
+  \param event Resize event
+*/
+void QwtPlotCanvas::resizeEvent( QResizeEvent *event )
+{
+    QFrame::resizeEvent( event );
+    updateStyleSheetInfo();
+}
+
+/*!
+  Draw the focus indication
+  \param painter Painter
+*/
+void QwtPlotCanvas::drawFocusIndicator( QPainter *painter )
+{
+    const int margin = 1;
+
+    QRect focusRect = contentsRect();
+    focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin,
+        focusRect.width() - 2 * margin, focusRect.height() - 2 * margin );
+
+    QwtPainter::drawFocusRect( painter, this, focusRect );
+}
+
+/*!
+   Invalidate the paint cache and repaint the canvas
+   \sa invalidatePaintCache()
+*/
+void QwtPlotCanvas::replot()
+{
+    invalidateBackingStore();
+
+    if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) )
+        repaint( contentsRect() );
+    else
+        update( contentsRect() );
+}
+
+//! Update the cached information about the current style sheet
+void QwtPlotCanvas::updateStyleSheetInfo()
+{
+    if ( !testAttribute(Qt::WA_StyledBackground ) )
+        return;
+
+    QwtStyleSheetRecorder recorder( size() );
+    
+    QPainter painter( &recorder );
+    
+    QStyleOption opt;
+    opt.initFrom(this);
+    style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
+    
+    painter.end();
+
+    d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty();
+    d_data->styleSheet.cornerRects = recorder.clipRects;
+
+    if ( recorder.background.path.isEmpty() )
+    {
+        if ( !recorder.border.rectList.isEmpty() )
+        {
+            d_data->styleSheet.borderPath = 
+                qwtCombinePathList( rect(), recorder.border.pathList );
+        }
+    }
+    else
+    {
+        d_data->styleSheet.borderPath = recorder.background.path;
+        d_data->styleSheet.background.brush = recorder.background.brush;
+        d_data->styleSheet.background.origin = recorder.background.origin;
+    }
+}
+
+/*!
+   Calculate the painter path for a styled or rounded border
+
+   When the canvas has no styled background or rounded borders
+   the painter path is empty.
+
+   \param rect Bounding rectangle of the canvas
+   \return Painter path, that can be used for clipping
+*/
+QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const
+{
+    if ( testAttribute(Qt::WA_StyledBackground ) )
+    {
+        QwtStyleSheetRecorder recorder( rect.size() );
+
+        QPainter painter( &recorder );
+
+        QStyleOption opt;
+        opt.initFrom(this);
+        opt.rect = rect;
+        style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
+
+        painter.end();
+
+        if ( !recorder.background.path.isEmpty() )
+            return recorder.background.path;
+
+        if ( !recorder.border.rectList.isEmpty() )
+            return qwtCombinePathList( rect, recorder.border.pathList );
+    }
+    else if ( d_data->borderRadius > 0.0 )
+    {
+        double fw2 = frameWidth() * 0.5;
+        QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 );
+
+        QPainterPath path;
+        path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius );
+        return path;
+    }
+    
+    return QPainterPath();
+}
diff --git a/qwt/qwt_plot_canvas.h b/qwt/qwt_plot_canvas.h
new file mode 100644
index 0000000..daea8a0
--- /dev/null
+++ b/qwt/qwt_plot_canvas.h
@@ -0,0 +1,171 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CANVAS_H
+#define QWT_PLOT_CANVAS_H
+
+#include "qwt_global.h"
+#include <qframe.h>
+#include <qpainterpath.h>
+
+class QwtPlot;
+class QPixmap;
+
+/*!
+  \brief Canvas of a QwtPlot.
+  
+   Canvas is the widget where all plot items are displayed
+
+  \sa QwtPlot::setCanvas(), QwtPlotGLCanvas
+*/
+class QWT_EXPORT QwtPlotCanvas : public QFrame
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius )
+
+public:
+
+    /*!
+      \brief Paint attributes
+
+      The default setting enables BackingStore and Opaque.
+
+      \sa setPaintAttribute(), testPaintAttribute()
+     */
+    enum PaintAttribute
+    {
+        /*!
+          \brief Paint double buffered reusing the content 
+                 of the pixmap buffer when possible. 
+
+          Using a backing store might improve the performance
+          significantly, when working with widget overlays ( like rubber bands ).
+          Disabling the cache might improve the performance for
+          incremental paints (using QwtPlotDirectPainter ).
+
+          \sa backingStore(), invalidateBackingStore()
+         */
+        BackingStore = 1,
+
+        /*!
+          \brief Try to fill the complete contents rectangle
+                 of the plot canvas
+
+          When using styled backgrounds Qt assumes, that the
+          canvas doesn't fill its area completely 
+          ( f.e because of rounded borders ) and fills the area
+          below the canvas. When this is done with gradients it might
+          result in a serious performance bottleneck - depending on the size.
+
+          When the Opaque attribute is enabled the canvas tries to
+          identify the gaps with some heuristics and to fill those only. 
+
+          \warning Will not work for semitransparent backgrounds 
+         */
+        Opaque       = 2,
+
+        /*!
+          \brief Try to improve painting of styled backgrounds
+
+          QwtPlotCanvas supports the box model attributes for
+          customizing the layout with style sheets. Unfortunately
+          the design of Qt style sheets has no concept how to
+          handle backgrounds with rounded corners - beside of padding.
+
+          When HackStyledBackground is enabled the plot canvas tries
+          to separate the background from the background border
+          by reverse engineering to paint the background before and
+          the border after the plot items. In this order the border
+          gets perfectly antialiased and you can avoid some pixel
+          artifacts in the corners.
+         */
+        HackStyledBackground = 4,
+
+        /*!
+          When ImmediatePaint is set replot() calls repaint()
+          instead of update().
+
+          \sa replot(), QWidget::repaint(), QWidget::update()
+         */
+        ImmediatePaint = 8
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    /*!
+      \brief Focus indicator
+      The default setting is NoFocusIndicator
+      \sa setFocusIndicator(), focusIndicator(), paintFocus()
+    */
+
+    enum FocusIndicator
+    {
+        //! Don't paint a focus indicator
+        NoFocusIndicator,
+
+        /*!
+          The focus is related to the complete canvas.
+          Paint the focus indicator using paintFocus()
+         */
+        CanvasFocusIndicator,
+
+        /*!
+          The focus is related to an item (curve, point, ...) on
+          the canvas. It is up to the application to display a
+          focus indication using f.e. highlighting.
+         */
+        ItemFocusIndicator
+    };
+
+    explicit QwtPlotCanvas( QwtPlot * = NULL );
+    virtual ~QwtPlotCanvas();
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    void setFocusIndicator( FocusIndicator );
+    FocusIndicator focusIndicator() const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    const QPixmap *backingStore() const;
+    void invalidateBackingStore();
+
+    virtual bool event( QEvent * );
+
+    Q_INVOKABLE QPainterPath borderPath( const QRect & ) const;
+
+public Q_SLOTS:
+    void replot();
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+
+    virtual void drawFocusIndicator( QPainter * );
+    virtual void drawBorder( QPainter * );
+
+    void updateStyleSheetInfo();
+
+private:
+    void drawCanvas( QPainter *, bool withBackground );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_curve.cpp b/qwt/qwt_plot_curve.cpp
new file mode 100644
index 0000000..140cc59
--- /dev/null
+++ b/qwt/qwt_plot_curve.cpp
@@ -0,0 +1,1191 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_curve.h"
+#include "qwt_point_data.h"
+#include "qwt_math.h"
+#include "qwt_clipper.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include "qwt_plot.h"
+#include "qwt_curve_fitter.h"
+#include "qwt_symbol.h"
+#include "qwt_point_mapper.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+
+static void qwtUpdateLegendIconSize( QwtPlotCurve *curve )
+{
+    if ( curve->symbol() && 
+        curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) )
+    {
+        QSize sz = curve->symbol()->boundingRect().size();
+        sz += QSize( 2, 2 ); // margin
+
+        if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) )
+        {
+            // Avoid, that the line is completely covered by the symbol
+
+            int w = qCeil( 1.5 * sz.width() );
+            if ( w % 2 )
+                w++;
+
+            sz.setWidth( qMax( 8, w ) );
+        }
+
+        curve->setLegendIconSize( sz );
+    }
+}
+
+static int qwtVerifyRange( int size, int &i1, int &i2 )
+{
+    if ( size < 1 )
+        return 0;
+
+    i1 = qBound( 0, i1, size - 1 );
+    i2 = qBound( 0, i2, size - 1 );
+
+    if ( i1 > i2 )
+        qSwap( i1, i2 );
+
+    return ( i2 - i1 + 1 );
+}
+
+class QwtPlotCurve::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotCurve::Lines ),
+        baseline( 0.0 ),
+        symbol( NULL ),
+        attributes( 0 ),
+        paintAttributes( 
+            QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ),
+        legendAttributes( 0 )
+    {
+        pen = QPen( Qt::black );
+        curveFitter = new QwtSplineCurveFitter;
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+        delete curveFitter;
+    }
+
+    QwtPlotCurve::CurveStyle style;
+    double baseline;
+
+    const QwtSymbol *symbol;
+    QwtCurveFitter *curveFitter;
+
+    QPen pen;
+    QBrush brush;
+
+    QwtPlotCurve::CurveAttributes attributes;
+    QwtPlotCurve::PaintAttributes paintAttributes;
+
+    QwtPlotCurve::LegendAttributes legendAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotCurve::QwtPlotCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotCurve::QwtPlotCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotCurve::~QwtPlotCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend );
+    setItemAttribute( QwtPlotItem::AutoScale );
+
+    d_data = new PrivateData;
+    setData( new QwtPointSeriesData() );
+
+    setZ( 20.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotCurve
+int QwtPlotCurve::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa setPaintAttribute()
+*/
+bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Specify an attribute how to draw the legend icon
+
+  \param attribute Attribute
+  \param on On/Off
+  /sa testLegendAttribute(). legendIcon()
+*/
+void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on )
+{
+    if ( on != testLegendAttribute( attribute ) )
+    {
+        if ( on )
+            d_data->legendAttributes |= attribute;
+        else
+            d_data->legendAttributes &= ~attribute;
+
+        qwtUpdateLegendIconSize( this );
+        legendChanged();
+    }
+}
+
+/*!
+  \return True, when attribute is enabled
+  \sa setLegendAttribute()
+*/
+bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const
+{
+    return ( d_data->legendAttributes & attribute );
+}
+
+/*!
+  Set the curve's drawing style
+
+  \param style Curve style
+  \sa style()
+*/
+void QwtPlotCurve::setStyle( CurveStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Style of the curve
+  \sa setStyle()
+*/
+QwtPlotCurve::CurveStyle QwtPlotCurve::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  \brief Assign a symbol
+
+  The curve will take the ownership of the symbol, hence the previously
+  set symbol will be delete by setting a new one. If \p symbol is 
+  \c NULL no symbol will be drawn.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotCurve::setSymbol( QwtSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        qwtUpdateLegendIconSize( this );
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtSymbol *QwtPlotCurve::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Build and assign a pen
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotCurve::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used to draw the lines
+  \sa setPen(), brush()
+*/
+const QPen& QwtPlotCurve::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  \brief Assign a brush.
+
+   In case of brush.style() != QBrush::NoBrush
+   and style() != QwtPlotCurve::Sticks
+   the area between the curve and the baseline will be filled.
+
+   In case !brush.color().isValid() the area will be filled by
+   pen.color(). The fill algorithm simply connects the first and the
+   last curve point to the baseline. So the curve data has to be sorted
+   (ascending or descending).
+
+  \param brush New brush
+  \sa brush(), setBaseline(), baseline()
+*/
+void QwtPlotCurve::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the area between lines and the baseline
+  \sa setBrush(), setBaseline(), baseline()
+*/
+const QBrush& QwtPlotCurve::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  Draw an interval of the curve
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawCurve(), drawSymbols(),
+*/
+void QwtPlotCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const size_t numSamples = dataSize();
+
+    if ( !painter || numSamples <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = numSamples - 1;
+
+    if ( qwtVerifyRange( numSamples, from, to ) > 0 )
+    {
+        painter->save();
+        painter->setPen( d_data->pen );
+
+        /*
+          Qt 4.0.0 is slow when drawing lines, but it's even
+          slower when the painter has a brush. So we don't
+          set the brush before we really need it.
+         */
+
+        drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to );
+        painter->restore();
+
+        if ( d_data->symbol &&
+            ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+        {
+            painter->save();
+            drawSymbols( painter, *d_data->symbol,
+                xMap, yMap, canvasRect, from, to );
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  \brief Draw the line part (without symbols) of a curve interval.
+  \param painter Painter
+  \param style curve style, see QwtPlotCurve::CurveStyle
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+  \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks()
+*/
+void QwtPlotCurve::drawCurve( QPainter *painter, int style,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    switch ( style )
+    {
+        case Lines:
+            if ( testCurveAttribute( Fitted ) )
+            {
+                // we always need the complete
+                // curve for fitting
+                from = 0;
+                to = dataSize() - 1;
+            }
+            drawLines( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Sticks:
+            drawSticks( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Steps:
+            drawSteps( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Dots:
+            drawDots( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case NoCurve:
+        default:
+            break;
+    }
+}
+
+/*!
+  \brief Draw lines
+
+  If the CurveAttribute Fitted is enabled a QwtCurveFitter tries
+  to interpolate/smooth the curve, before it is painted.
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa setCurveAttribute(), setCurveFitter(), draw(),
+      drawLines(), drawDots(), drawSteps(), drawSticks()
+*/
+void QwtPlotCurve::drawLines( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( from > to )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+    const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter;
+    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
+            && ( d_data->brush.color().alpha() > 0 );
+
+    QRectF clipRect;
+    if ( d_data->paintAttributes & ClipPolygons )
+    {
+        qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
+        clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
+    }
+
+    bool doIntegers = false;
+
+#if QT_VERSION < 0x040800
+
+    // For Qt <= 4.7 the raster paint engine is significantly faster
+    // for rendering QPolygon than for QPolygonF. So let's
+    // see if we can use it.
+
+    if ( painter->paintEngine()->type() == QPaintEngine::Raster )
+    {
+        // In case of filling or fitting performance doesn't count
+        // because both operations are much more expensive
+        // then drawing the polyline itself
+
+        if ( !doFit && !doFill )
+            doIntegers = true; 
+    }
+#endif
+
+    const bool noDuplicates = d_data->paintAttributes & FilterPoints;
+
+    QwtPointMapper mapper;
+    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
+    mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
+    mapper.setBoundingRect( canvasRect );
+
+    if ( doIntegers )
+    {
+        const QPolygon polyline = mapper.toPolygon( 
+            xMap, yMap, data(), from, to );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            const QPolygon clipped = QwtClipper::clipPolygon( 
+                clipRect.toAlignedRect(), polyline, false );
+
+            QwtPainter::drawPolyline( painter, clipped );
+        }
+        else
+        {
+            QwtPainter::drawPolyline( painter, polyline );
+        }
+    }
+    else
+    {
+        QPolygonF polyline = mapper.toPolygonF( xMap, yMap,
+            data(), from, to );
+
+        if ( doFit )
+            polyline = d_data->curveFitter->fitCurve( polyline );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            const QPolygonF clipped = QwtClipper::clipPolygonF( 
+                clipRect, polyline, false );
+
+            QwtPainter::drawPolyline( painter, clipped );
+        }
+        else
+        {
+            QwtPainter::drawPolyline( painter, polyline );
+        }
+
+        if ( doFill )
+        {
+            fillCurve( painter, xMap, yMap, canvasRect, polyline );
+        }
+    }
+}
+
+/*!
+  Draw sticks
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps()
+*/
+void QwtPlotCurve::drawSticks( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &, int from, int to ) const
+{
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double x0 = xMap.transform( d_data->baseline );
+    double y0 = yMap.transform( d_data->baseline );
+    if ( doAlign )
+    {
+        x0 = qRound( x0 );
+        y0 = qRound( y0 );
+    }
+
+    const Qt::Orientation o = orientation();
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( o == Qt::Horizontal )
+            QwtPainter::drawLine( painter, x0, yi, xi, yi );
+        else
+            QwtPainter::drawLine( painter, xi, y0, xi, yi );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw dots
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps()
+*/
+void QwtPlotCurve::drawDots( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const QColor color = painter->pen().color();
+
+    if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
+    {
+        return;
+    }
+
+    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
+            && ( d_data->brush.color().alpha() > 0 );
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    QwtPointMapper mapper;
+    mapper.setBoundingRect( canvasRect );
+    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
+
+    if ( d_data->paintAttributes & FilterPoints )
+    {
+        if ( ( color.alpha() == 255 )
+            && !( painter->renderHints() & QPainter::Antialiasing ) )
+        {
+            mapper.setFlag( QwtPointMapper::WeedOutPoints, true );
+        }
+    }
+
+    if ( doFill )
+    {
+        mapper.setFlag( QwtPointMapper::WeedOutPoints, false );
+
+        QPolygonF points = mapper.toPointsF( 
+            xMap, yMap, data(), from, to );
+
+        QwtPainter::drawPoints( painter, points );
+        fillCurve( painter, xMap, yMap, canvasRect, points );
+    }
+    else if ( d_data->paintAttributes & ImageBuffer )
+    {
+        const QImage image = mapper.toImage( xMap, yMap,
+            data(), from, to, d_data->pen, 
+            painter->testRenderHint( QPainter::Antialiasing ),
+            renderThreadCount() );
+
+        painter->drawImage( canvasRect.toAlignedRect(), image );
+    }
+    else if ( d_data->paintAttributes & MinimizeMemory )
+    {
+        const QwtSeriesData<QPointF> *series = data();
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            double xi = xMap.transform( sample.x() );
+            double yi = yMap.transform( sample.y() );
+
+            if ( doAlign )
+            {
+                xi = qRound( xi );
+                yi = qRound( yi );
+            }
+
+            QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
+        }
+    }
+    else
+    {
+        if ( doAlign )
+        {
+            const QPolygon points = mapper.toPoints(
+                xMap, yMap, data(), from, to ); 
+
+            QwtPainter::drawPoints( painter, points );
+        }
+        else
+        {
+            const QPolygonF points = mapper.toPointsF( 
+                xMap, yMap, data(), from, to );
+
+            QwtPainter::drawPoints( painter, points );
+        }
+    }
+}
+
+/*!
+  Draw step function
+
+  The direction of the steps depends on Inverted attribute.
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa CurveAttribute, setCurveAttribute(),
+      draw(), drawCurve(), drawDots(), drawLines(), drawSticks()
+*/
+void QwtPlotCurve::drawSteps( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    QPolygonF polygon( 2 * ( to - from ) + 1 );
+    QPointF *points = polygon.data();
+
+    bool inverted = orientation() == Qt::Vertical;
+    if ( d_data->attributes & Inverted )
+        inverted = !inverted;
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    int i, ip;
+    for ( i = from, ip = 0; i <= to; i++, ip += 2 )
+    {
+        const QPointF sample = series->sample( i );
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( ip > 0 )
+        {
+            const QPointF &p0 = points[ip - 2];
+            QPointF &p = points[ip - 1];
+
+            if ( inverted )
+            {
+                p.rx() = p0.x();
+                p.ry() = yi;
+            }
+            else
+            {
+                p.rx() = xi;
+                p.ry() = p0.y();
+            }
+        }
+
+        points[ip].rx() = xi;
+        points[ip].ry() = yi;
+    }
+
+    if ( d_data->paintAttributes & ClipPolygons )
+    {
+        const QPolygonF clipped = QwtClipper::clipPolygonF( 
+            canvasRect, polygon, false );
+
+        QwtPainter::drawPolyline( painter, clipped );
+    }
+    else
+    {
+        QwtPainter::drawPolyline( painter, polygon );
+    }
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+        fillCurve( painter, xMap, yMap, canvasRect, polygon );
+}
+
+
+/*!
+  Specify an attribute for drawing the curve
+
+  \param attribute Curve attribute
+  \param on On/Off
+
+  /sa testCurveAttribute(), setCurveFitter()
+*/
+void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on )
+{
+    if ( bool( d_data->attributes & attribute ) == on )
+        return;
+
+    if ( on )
+        d_data->attributes |= attribute;
+    else
+        d_data->attributes &= ~attribute;
+
+    itemChanged();
+}
+
+/*!
+    \return true, if attribute is enabled
+    \sa setCurveAttribute()
+*/
+bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const
+{
+    return d_data->attributes & attribute;
+}
+
+/*!
+  Assign a curve fitter
+
+  The curve fitter "smooths" the curve points, when the Fitted
+  CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting.
+
+  The curve fitter operates on the translated points ( = widget coordinates)
+  to be functional for logarithmic scales. Obviously this is less performant
+  for fitting algorithms, that reduce the number of points.
+
+  For situations, where curve fitting is used to improve the performance
+  of painting huge series of points it might be better to execute the fitter
+  on the curve points once and to cache the result in the QwtSeriesData object.
+
+  \param curveFitter() Curve fitter
+  \sa Fitted
+*/
+void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter )
+{
+    delete d_data->curveFitter;
+    d_data->curveFitter = curveFitter;
+
+    itemChanged();
+}
+
+/*!
+  Get the curve fitter. If curve fitting is disabled NULL is returned.
+
+  \return Curve fitter
+  \sa setCurveFitter(), Fitted
+*/
+QwtCurveFitter *QwtPlotCurve::curveFitter() const
+{
+    return d_data->curveFitter;
+}
+
+/*!
+  Fill the area between the curve and the baseline with
+  the curve brush
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param polygon Polygon - will be modified !
+
+  \sa setBrush(), setBaseline(), setStyle()
+*/
+void QwtPlotCurve::fillCurve( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, QPolygonF &polygon ) const
+{
+    if ( d_data->brush.style() == Qt::NoBrush )
+        return;
+
+    closePolyline( painter, xMap, yMap, polygon );
+    if ( polygon.count() <= 2 ) // a line can't be filled
+        return;
+
+    QBrush brush = d_data->brush;
+    if ( !brush.color().isValid() )
+        brush.setColor( d_data->pen.color() );
+
+    if ( d_data->paintAttributes & ClipPolygons )
+        polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true );
+
+    painter->save();
+
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( brush );
+
+    QwtPainter::drawPolygon( painter, polygon );
+
+    painter->restore();
+}
+
+/*!
+  \brief Complete a polygon to be a closed polygon including the 
+         area between the original polygon and the baseline.
+
+  \param painter Painter
+  \param xMap X map
+  \param yMap Y map
+  \param polygon Polygon to be completed
+*/
+void QwtPlotCurve::closePolyline( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    QPolygonF &polygon ) const
+{
+    if ( polygon.size() < 2 )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double baseline = d_data->baseline;
+    
+    if ( orientation() == Qt::Vertical )
+    {
+        if ( yMap.transformation() )
+            baseline = yMap.transformation()->bounded( baseline );
+
+        double refY = yMap.transform( baseline );
+        if ( doAlign )
+            refY = qRound( refY );
+
+        polygon += QPointF( polygon.last().x(), refY );
+        polygon += QPointF( polygon.first().x(), refY );
+    }
+    else
+    {
+        if ( xMap.transformation() )
+            baseline = xMap.transformation()->bounded( baseline );
+
+        double refX = xMap.transform( baseline );
+        if ( doAlign )
+            refX = qRound( refX );
+
+        polygon += QPointF( refX, polygon.last().y() );
+        polygon += QPointF( refX, polygon.first().y() );
+    }
+}
+
+/*!
+  Draw symbols
+
+  \param painter Painter
+  \param symbol Curve symbol
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \sa setSymbol(), drawSeries(), drawCurve()
+*/
+void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    QwtPointMapper mapper;
+    mapper.setFlag( QwtPointMapper::RoundPoints, 
+        QwtPainter::roundingAlignment( painter ) );
+    mapper.setFlag( QwtPointMapper::WeedOutPoints, 
+        testPaintAttribute( QwtPlotCurve::FilterPoints ) );
+    mapper.setBoundingRect( canvasRect );
+
+    const int chunkSize = 500;
+
+    for ( int i = from; i <= to; i += chunkSize )
+    {
+        const int n = qMin( chunkSize, to - i + 1 );
+
+        const QPolygonF points = mapper.toPointsF( xMap, yMap,
+            data(), i, i + n - 1 );
+
+        if ( points.size() > 0 )
+            symbol.drawSymbols( painter, points );
+    }
+}
+
+/*!
+  \brief Set the value of the baseline
+
+  The baseline is needed for filling the curve with a brush or
+  the Sticks drawing style.
+
+  The interpretation of the baseline depends on the orientation().
+  With Qt::Horizontal, the baseline is interpreted as a horizontal line
+  at y = baseline(), with Qt::Vertical, it is interpreted as a vertical
+  line at x = baseline().
+
+  The default value is 0.0.
+
+  \param value Value of the baseline
+  \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation()
+*/
+void QwtPlotCurve::setBaseline( double value )
+{
+    if ( d_data->baseline != value )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value of the baseline
+  \sa setBaseline()
+*/
+double QwtPlotCurve::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+  Find the closest curve point for a specific position
+
+  \param pos Position, where to look for the closest curve point
+  \param dist If dist != NULL, closestPoint() returns the distance between
+              the position and the closest curve point
+  \return Index of the closest curve point, or -1 if none can be found
+          ( f.e when the curve has no points )
+  \note closestPoint() implements a dumb algorithm, that iterates
+        over all points
+*/
+int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const
+{
+    const size_t numSamples = dataSize();
+
+    if ( plot() == NULL || numSamples <= 0 )
+        return -1;
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    const QwtScaleMap xMap = plot()->canvasMap( xAxis() );
+    const QwtScaleMap yMap = plot()->canvasMap( yAxis() );
+
+    int index = -1;
+    double dmin = 1.0e10;
+
+    for ( uint i = 0; i < numSamples; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const double cx = xMap.transform( sample.x() ) - pos.x();
+        const double cy = yMap.transform( sample.y() ) - pos.y();
+
+        const double f = qwtSqr( cx ) + qwtSqr( cy );
+        if ( f < dmin )
+        {
+            index = i;
+            dmin = f;
+        }
+    }
+    if ( dist )
+        *dist = qSqrt( dmin );
+
+    return index;
+}
+
+/*!
+   \return Icon representing the curve on the legend
+
+   \param index Index of the legend entry 
+                ( ignored as there is only one )
+   \param size Icon size
+
+   \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+ */
+QwtGraphic QwtPlotCurve::legendIcon( int index, 
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic graphic;
+    graphic.setDefaultSize( size );
+    graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &graphic );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->legendAttributes == 0 ||
+        d_data->legendAttributes & QwtPlotCurve::LegendShowBrush )
+    {
+        QBrush brush = d_data->brush;
+
+        if ( brush.style() == Qt::NoBrush &&
+            d_data->legendAttributes == 0 )
+        {
+            if ( style() != QwtPlotCurve::NoCurve )
+            {
+                brush = QBrush( pen().color() );
+            }
+            else if ( d_data->symbol &&
+                ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+            {
+                brush = QBrush( d_data->symbol->pen().color() );
+            }
+        }
+
+        if ( brush.style() != Qt::NoBrush )
+        {
+            QRectF r( 0, 0, size.width(), size.height() );
+            painter.fillRect( r, brush );
+        }
+    }
+
+    if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine )
+    {
+        if ( pen() != Qt::NoPen )
+        {
+            QPen pn = pen();
+            pn.setCapStyle( Qt::FlatCap );
+
+            painter.setPen( pn );
+
+            const double y = 0.5 * size.height();
+            QwtPainter::drawLine( &painter, 0.0, y, size.width(), y );
+        }
+    }
+
+    if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol )
+    {
+        if ( d_data->symbol )
+        {
+            QRectF r( 0, 0, size.width(), size.height() );
+            d_data->symbol->drawSymbol( &painter, r );
+        }
+    }
+
+    return graphic;
+}
+
+/*!
+  Initialize data with an array of points.
+
+  \param samples Vector of points
+  \note QVector is implicitly shared
+  \note QPolygonF is derived from QVector<QPointF>
+*/
+void QwtPlotCurve::setSamples( const QVector<QPointF> &samples )
+{
+    setData( new QwtPointSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of points
+
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotCurve::setSamples( QwtSeriesData<QPointF> *data )
+{
+    setData( data );
+}
+
+#ifndef QWT_NO_COMPAT
+
+/*!
+  \brief Initialize the data by pointing to memory blocks which 
+         are not managed by QwtPlotCurve.
+
+  setRawSamples is provided for efficiency. 
+  It is important to keep the pointers
+  during the lifetime of the underlying QwtCPointerData class.
+
+  \param xData pointer to x data
+  \param yData pointer to y data
+  \param size size of x and y
+
+  \sa QwtCPointerData
+*/
+void QwtPlotCurve::setRawSamples( 
+    const double *xData, const double *yData, int size )
+{
+    setData( new QwtCPointerData( xData, yData, size ) );
+}
+
+/*!
+  Set data by copying x- and y-values from specified memory blocks.
+  Contrary to setRawSamples(), this function makes a 'deep copy' of
+  the data.
+
+  \param xData pointer to x values
+  \param yData pointer to y values
+  \param size size of xData and yData
+
+  \sa QwtPointArrayData
+*/
+void QwtPlotCurve::setSamples( 
+    const double *xData, const double *yData, int size )
+{
+    setData( new QwtPointArrayData( xData, yData, size ) );
+}
+
+/*!
+  \brief Initialize data with x- and y-arrays (explicitly shared)
+
+  \param xData x data
+  \param yData y data
+
+  \sa QwtPointArrayData
+*/
+void QwtPlotCurve::setSamples( const QVector<double> &xData,
+    const QVector<double> &yData )
+{
+    setData( new QwtPointArrayData( xData, yData ) );
+}
+
+#endif // !QWT_NO_COMPAT
+
diff --git a/qwt/qwt_plot_curve.h b/qwt/qwt_plot_curve.h
new file mode 100644
index 0000000..3421abf
--- /dev/null
+++ b/qwt/qwt_plot_curve.h
@@ -0,0 +1,337 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CURVE_H
+#define QWT_PLOT_CURVE_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_series_data.h"
+#include "qwt_text.h"
+#include <qpen.h>
+#include <qstring.h>
+
+class QPainter;
+class QPolygonF;
+class QwtScaleMap;
+class QwtSymbol;
+class QwtCurveFitter;
+
+/*!
+  \brief A plot item, that represents a series of points
+
+  A curve is the representation of a series of points in the x-y plane.
+  It supports different display styles, interpolation ( f.e. spline )
+  and symbols.
+
+  \par Usage
+  <dl><dt>a) Assign curve properties</dt>
+  <dd>When a curve is created, it is configured to draw black solid lines
+  with in QwtPlotCurve::Lines style and no symbols. 
+  You can change this by calling
+  setPen(), setStyle() and setSymbol().</dd>
+  <dt>b) Connect/Assign data.</dt>
+  <dd>QwtPlotCurve gets its points using a QwtSeriesData object offering
+  a bridge to the real storage of the points ( like QAbstractItemModel ).
+  There are several convenience classes derived from QwtSeriesData, that also store
+  the points inside ( like QStandardItemModel ). QwtPlotCurve also offers
+  a couple of variations of setSamples(), that build QwtSeriesData objects from
+  arrays internally.</dd>
+  <dt>c) Attach the curve to a plot</dt>
+  <dd>See QwtPlotItem::attach()
+  </dd></dl>
+
+  \par Example:
+  see examples/bode
+
+  \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap
+*/
+class QWT_EXPORT QwtPlotCurve: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QPointF>
+{
+public:
+    /*!
+        Curve styles.
+        \sa setStyle(), style()
+    */
+    enum CurveStyle
+    {
+        /*!
+           Don't draw a curve. Note: This doesn't affect the symbols.
+        */
+        NoCurve = -1,
+
+        /*!
+           Connect the points with straight lines. The lines might
+           be interpolated depending on the 'Fitted' attribute. Curve
+           fitting can be configured using setCurveFitter().
+        */
+        Lines,
+
+        /*!
+           Draw vertical or horizontal sticks ( depending on the 
+           orientation() ) from a baseline which is defined by setBaseline().
+        */
+        Sticks,
+
+        /*!
+           Connect the points with a step function. The step function
+           is drawn from the left to the right or vice versa,
+           depending on the QwtPlotCurve::Inverted attribute.
+        */
+        Steps,
+
+        /*!
+           Draw dots at the locations of the data points. Note:
+           This is different from a dotted line (see setPen()), and faster
+           as a curve in QwtPlotCurve::NoStyle style and a symbol 
+           painting a point.
+        */
+        Dots,
+
+        /*!
+           Styles >= QwtPlotCurve::UserCurve are reserved for derived
+           classes of QwtPlotCurve that overload drawCurve() with
+           additional application specific curve types.
+        */
+        UserCurve = 100
+    };
+
+    /*!
+      Attribute for drawing the curve
+      \sa setCurveAttribute(), testCurveAttribute(), curveFitter()
+    */
+    enum CurveAttribute
+    {
+        /*!
+           For QwtPlotCurve::Steps only. 
+           Draws a step function from the right to the left.
+         */
+        Inverted = 0x01,
+
+        /*!
+          Only in combination with QwtPlotCurve::Lines
+          A QwtCurveFitter tries to
+          interpolate/smooth the curve, before it is painted.
+
+          \note Curve fitting requires temporary memory
+          for calculating coefficients and additional points.
+          If painting in QwtPlotCurve::Fitted mode is slow it might be better
+          to fit the points, before they are passed to QwtPlotCurve.
+         */
+        Fitted = 0x02
+    };
+
+    //! Curve attributes
+    typedef QFlags<CurveAttribute> CurveAttributes;
+
+    /*!
+        Attributes how to represent the curve on the legend
+
+        \sa setLegendAttribute(), testLegendAttribute(),
+            QwtPlotItem::legendData(), legendIcon()
+     */
+
+    enum LegendAttribute
+    {
+        /*!
+          QwtPlotCurve tries to find a color representing the curve 
+          and paints a rectangle with it.
+         */
+        LegendNoAttribute = 0x00,
+
+        /*!
+          If the style() is not QwtPlotCurve::NoCurve a line 
+          is painted with the curve pen().
+         */
+        LegendShowLine = 0x01,
+
+        /*!
+          If the curve has a valid symbol it is painted.
+         */
+        LegendShowSymbol = 0x02,
+
+        /*!
+          If the curve has a brush a rectangle filled with the
+          curve brush() is painted.
+         */
+        LegendShowBrush = 0x04
+    };
+
+    //! Legend attributes
+    typedef QFlags<LegendAttribute> LegendAttributes;
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        The default setting enables ClipPolygons | FilterPoints
+
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance
+         */
+        ClipPolygons = 0x01,
+
+        /*!
+          Tries to reduce the data that has to be painted, by sorting out
+          duplicates, or paintings outside the visible area. Might have a
+          notable impact on curves with many close points.
+          Only a couple of very basic filtering algorithms are implemented.
+         */
+        FilterPoints = 0x02,
+
+        /*!
+          Minimize memory usage that is temporarily needed for the 
+          translated points, before they get painted.
+          This might slow down the performance of painting 
+         */
+        MinimizeMemory = 0x04,
+
+        /*!
+          Render the points to a temporary image and paint the image.
+          This is a very special optimization for Dots style, when
+          having a huge amount of points. 
+          With a reasonable number of points QPainter::drawPoints()
+          will be faster.
+         */
+        ImageBuffer = 0x08
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotCurve( const QString &title = QString::null );
+    explicit QwtPlotCurve( const QwtText &title );
+
+    virtual ~QwtPlotCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLegendAttribute( LegendAttribute, bool on = true );
+    bool testLegendAttribute( LegendAttribute ) const;
+
+#ifndef QWT_NO_COMPAT
+    void setRawSamples( const double *xData, const double *yData, int size );
+    void setSamples( const double *xData, const double *yData, int size );
+    void setSamples( const QVector<double> &xData, const QVector<double> &yData );
+#endif
+    void setSamples( const QVector<QPointF> & );
+    void setSamples( QwtSeriesData<QPointF> * );
+
+    int closestPoint( const QPoint &pos, double *dist = NULL ) const;
+
+    double minXValue() const;
+    double maxXValue() const;
+    double minYValue() const;
+    double maxYValue() const;
+
+    void setCurveAttribute( CurveAttribute, bool on = true );
+    bool testCurveAttribute( CurveAttribute ) const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setBaseline( double );
+    double baseline() const;
+
+    void setStyle( CurveStyle style );
+    CurveStyle style() const;
+
+    void setSymbol( QwtSymbol * );
+    const QwtSymbol *symbol() const;
+
+    void setCurveFitter( QwtCurveFitter * );
+    QwtCurveFitter *curveFitter() const;
+
+    virtual void drawSeries( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawCurve( QPainter *p, int style,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSymbols( QPainter *p, const QwtSymbol &,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawLines( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSticks( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawDots( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSteps( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void fillCurve( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &, 
+        const QRectF &canvasRect, QPolygonF & ) const;
+
+    void closePolyline( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+//! boundingRect().left()
+inline double QwtPlotCurve::minXValue() const
+{
+    return boundingRect().left();
+}
+
+//! boundingRect().right()
+inline double QwtPlotCurve::maxXValue() const
+{
+    return boundingRect().right();
+}
+
+//! boundingRect().top()
+inline double QwtPlotCurve::minYValue() const
+{
+    return boundingRect().top();
+}
+
+//! boundingRect().bottom()
+inline double QwtPlotCurve::maxYValue() const
+{
+    return boundingRect().bottom();
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_dict.cpp b/qwt/qwt_plot_dict.cpp
new file mode 100644
index 0000000..17c61ed
--- /dev/null
+++ b/qwt/qwt_plot_dict.cpp
@@ -0,0 +1,191 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_dict.h"
+
+class QwtPlotDict::PrivateData
+{
+public:
+
+    class ItemList: public QList<QwtPlotItem *>
+    {
+    public:
+        void insertItem( QwtPlotItem *item )
+        {
+            if ( item == NULL )
+                return;
+
+            QList<QwtPlotItem *>::iterator it =
+                qUpperBound( begin(), end(), item, LessZThan() );
+            insert( it, item );
+        }
+
+        void removeItem( QwtPlotItem *item )
+        {
+            if ( item == NULL )
+                return;
+
+            QList<QwtPlotItem *>::iterator it =
+                qLowerBound( begin(), end(), item, LessZThan() );
+
+            for ( ; it != end(); ++it )
+            {
+                if ( item == *it )
+                {
+                    erase( it );
+                    break;
+                }
+            }
+        }
+    private:
+        class LessZThan
+        {
+        public:
+            inline bool operator()( const QwtPlotItem *item1,
+                const QwtPlotItem *item2 ) const
+            {
+                return item1->z() < item2->z();
+            }
+        };
+    };
+
+    ItemList itemList;
+    bool autoDelete;
+};
+
+/*!
+   Constructor
+
+   Auto deletion is enabled.
+   \sa setAutoDelete(), QwtPlotItem::attach()
+*/
+QwtPlotDict::QwtPlotDict()
+{
+    d_data = new QwtPlotDict::PrivateData;
+    d_data->autoDelete = true;
+}
+
+/*!
+   Destructor
+
+   If autoDelete() is on, all attached items will be deleted
+   \sa setAutoDelete(), autoDelete(), QwtPlotItem::attach()
+*/
+QwtPlotDict::~QwtPlotDict()
+{
+    detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete );
+    delete d_data;
+}
+
+/*!
+   En/Disable Auto deletion
+
+   If Auto deletion is on all attached plot items will be deleted
+   in the destructor of QwtPlotDict. The default value is on.
+
+   \sa autoDelete(), insertItem()
+*/
+void QwtPlotDict::setAutoDelete( bool autoDelete )
+{
+    d_data->autoDelete = autoDelete;
+}
+
+/*!
+   \return true if auto deletion is enabled
+   \sa setAutoDelete(), insertItem()
+*/
+bool QwtPlotDict::autoDelete() const
+{
+    return d_data->autoDelete;
+}
+
+/*!
+  Insert a plot item
+
+  \param item PlotItem
+  \sa removeItem()
+ */
+void QwtPlotDict::insertItem( QwtPlotItem *item )
+{
+    d_data->itemList.insertItem( item );
+}
+
+/*!
+  Remove a plot item
+
+  \param item PlotItem
+  \sa insertItem()
+ */
+void QwtPlotDict::removeItem( QwtPlotItem *item )
+{
+    d_data->itemList.removeItem( item );
+}
+
+/*!
+   Detach items from the dictionary
+
+   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
+               otherwise only those items of the type rtti.
+   \param autoDelete If true, delete all detached items
+*/
+void QwtPlotDict::detachItems( int rtti, bool autoDelete )
+{
+    PrivateData::ItemList list = d_data->itemList;
+    QwtPlotItemIterator it = list.begin();
+    while ( it != list.end() )
+    {
+        QwtPlotItem *item = *it;
+
+        ++it; // increment before removing item from the list
+
+        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
+        {
+            item->attach( NULL );
+            if ( autoDelete )
+                delete item;
+        }
+    }
+}
+
+/*!
+  \brief A QwtPlotItemList of all attached plot items.
+
+  Use caution when iterating these lists, as removing/detaching an item will
+  invalidate the iterator. Instead you can place pointers to objects to be
+  removed in a removal list, and traverse that list later.
+
+  \return List of all attached plot items.
+*/
+const QwtPlotItemList &QwtPlotDict::itemList() const
+{
+    return d_data->itemList;
+}
+
+/*!
+  \return List of all attached plot items of a specific type.
+  \param rtti See QwtPlotItem::RttiValues
+  \sa QwtPlotItem::rtti()
+*/
+QwtPlotItemList QwtPlotDict::itemList( int rtti ) const
+{
+    if ( rtti == QwtPlotItem::Rtti_PlotItem )
+        return d_data->itemList;
+
+    QwtPlotItemList items;
+
+    PrivateData::ItemList list = d_data->itemList;
+    for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item->rtti() == rtti )
+            items += item;
+    }
+
+    return items;
+}
diff --git a/qwt/qwt_plot_dict.h b/qwt/qwt_plot_dict.h
new file mode 100644
index 0000000..5d34f0c
--- /dev/null
+++ b/qwt/qwt_plot_dict.h
@@ -0,0 +1,58 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file !*/
+#ifndef QWT_PLOT_DICT
+#define QWT_PLOT_DICT
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include <qlist.h>
+
+/// \var typedef QList< QwtPlotItem *> QwtPlotItemList
+/// \brief See QT 4.x assistant documentation for QList
+typedef QList<QwtPlotItem *> QwtPlotItemList;
+typedef QList<QwtPlotItem *>::ConstIterator QwtPlotItemIterator;
+
+/*!
+  \brief A dictionary for plot items
+
+  QwtPlotDict organizes plot items in increasing z-order.
+  If autoDelete() is enabled, all attached items will be deleted
+  in the destructor of the dictionary.
+  QwtPlotDict can be used to get access to all QwtPlotItem items - or all
+  items of a specific type -  that are currently on the plot.
+
+  \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z()
+*/
+class QWT_EXPORT QwtPlotDict
+{
+public:
+    explicit QwtPlotDict();
+    virtual ~QwtPlotDict();
+
+    void setAutoDelete( bool );
+    bool autoDelete() const;
+
+    const QwtPlotItemList& itemList() const;
+    QwtPlotItemList itemList( int rtti ) const;
+
+    void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem,
+        bool autoDelete = true );
+
+protected:
+    void insertItem( QwtPlotItem * );
+    void removeItem( QwtPlotItem * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_directpainter.cpp b/qwt/qwt_plot_directpainter.cpp
new file mode 100644
index 0000000..d783527
--- /dev/null
+++ b/qwt/qwt_plot_directpainter.cpp
@@ -0,0 +1,317 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_directpainter.h"
+#include "qwt_scale_map.h"
+#include "qwt_plot.h"
+#include "qwt_plot_canvas.h"
+#include "qwt_plot_seriesitem.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qapplication.h>
+#include <qpixmap.h>
+
+static inline void qwtRenderItem( 
+    QPainter *painter, const QRect &canvasRect,
+    QwtPlotSeriesItem *seriesItem, int from, int to )
+{
+    // A minor performance improvement is possible
+    // with caching the maps. TODO ...
+
+    QwtPlot *plot = seriesItem->plot();
+    const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() );
+    const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() );
+
+    painter->setRenderHint( QPainter::Antialiasing,
+        seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+    seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to );
+}
+
+static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas )
+{
+    return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore )
+        && canvas->backingStore() && !canvas->backingStore()->isNull();
+}
+
+class QwtPlotDirectPainter::PrivateData
+{
+public:
+    PrivateData():
+        attributes( 0 ),
+        hasClipping(false),
+        seriesItem( NULL )
+    {
+    }
+
+    QwtPlotDirectPainter::Attributes attributes;
+
+    bool hasClipping;
+    QRegion clipRegion;
+
+    QPainter painter;
+
+    QwtPlotSeriesItem *seriesItem;
+    int from;
+    int to;
+};
+
+//! Constructor
+QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlotDirectPainter::~QwtPlotDirectPainter()
+{
+    delete d_data;
+}
+
+/*!
+  Change an attribute
+
+  \param attribute Attribute to change
+  \param on On/Off
+
+  \sa Attribute, testAttribute()
+*/
+void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on )
+{
+    if ( bool( d_data->attributes & attribute ) != on )
+    {
+        if ( on )
+            d_data->attributes |= attribute;
+        else
+            d_data->attributes &= ~attribute;
+
+        if ( ( attribute == AtomicPainter ) && on )
+            reset();
+    }
+}
+
+/*!
+  \return True, when attribute is enabled
+  \param attribute Attribute to be tested
+  \sa Attribute, setAttribute()
+*/
+bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const
+{
+    return d_data->attributes & attribute;
+}
+
+/*!
+  En/Disables clipping 
+
+  \param enable Enables clipping is true, disable it otherwise
+  \sa hasClipping(), clipRegion(), setClipRegion()
+*/
+void QwtPlotDirectPainter::setClipping( bool enable )
+{
+    d_data->hasClipping = enable;
+}
+
+/*!
+  \return true, when clipping is enabled
+  \sa setClipping(), clipRegion(), setClipRegion()
+*/
+bool QwtPlotDirectPainter::hasClipping() const
+{
+    return d_data->hasClipping;
+}
+
+/*!
+   \brief Assign a clip region and enable clipping
+
+   Depending on the environment setting a proper clip region might improve 
+   the performance heavily. F.e. on Qt embedded only the clipped part of
+   the backing store will be copied to a ( maybe unaccelerated ) frame buffer
+   device.
+   
+   \param region Clip region
+   \sa clipRegion(), hasClipping(), setClipping()
+*/
+void QwtPlotDirectPainter::setClipRegion( const QRegion &region )
+{
+    d_data->clipRegion = region;
+    d_data->hasClipping = true;
+}
+
+/*!
+   \return Currently set clip region.
+   \sa setClipRegion(), setClipping(), hasClipping()
+*/
+QRegion QwtPlotDirectPainter::clipRegion() const
+{
+    return d_data->clipRegion;
+}
+
+/*!
+  \brief Draw a set of points of a seriesItem.
+
+  When observing an measurement while it is running, new points have to be
+  added to an existing seriesItem. drawSeries() can be used to display them avoiding
+  a complete redraw of the canvas.
+
+  Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);
+  will result in faster painting, if the paint engine of the canvas widget
+  supports this feature.
+
+  \param seriesItem Item to be painted
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         series will be painted to its last point.
+*/
+void QwtPlotDirectPainter::drawSeries(
+    QwtPlotSeriesItem *seriesItem, int from, int to )
+{
+    if ( seriesItem == NULL || seriesItem->plot() == NULL )
+        return;
+
+    QWidget *canvas = seriesItem->plot()->canvas();
+    const QRect canvasRect = canvas->contentsRect();
+
+    QwtPlotCanvas *plotCanvas = qobject_cast<QwtPlotCanvas *>( canvas );
+
+    if ( plotCanvas && qwtHasBackingStore( plotCanvas ) )
+    {
+        QPainter painter( const_cast<QPixmap *>( plotCanvas->backingStore() ) );
+
+        if ( d_data->hasClipping )
+            painter.setClipRegion( d_data->clipRegion );
+
+        qwtRenderItem( &painter, canvasRect, seriesItem, from, to );
+
+        if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) )
+        {
+            plotCanvas->repaint();
+            return;
+        }
+    }
+
+    bool immediatePaint = true;
+    if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) 
+    {
+#if QT_VERSION < 0x050000
+        if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) )
+#endif
+            immediatePaint = false;
+    }
+
+    if ( immediatePaint )
+    {
+        if ( !d_data->painter.isActive() )
+        {
+            reset();
+
+            d_data->painter.begin( canvas );
+            canvas->installEventFilter( this );
+        }
+
+        if ( d_data->hasClipping )
+        {
+            d_data->painter.setClipRegion( 
+                QRegion( canvasRect ) & d_data->clipRegion );
+        }
+        else
+        {
+            if ( !d_data->painter.hasClipping() )
+                d_data->painter.setClipRect( canvasRect );
+        }
+
+        qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to );
+
+        if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter )
+        {
+            reset();
+        }
+        else
+        {
+            if ( d_data->hasClipping )
+                d_data->painter.setClipping( false );
+        }
+    }
+    else
+    {
+        reset();
+
+        d_data->seriesItem = seriesItem;
+        d_data->from = from;
+        d_data->to = to;
+
+        QRegion clipRegion = canvasRect;
+        if ( d_data->hasClipping )
+            clipRegion &= d_data->clipRegion;
+
+        canvas->installEventFilter( this );
+        canvas->repaint(clipRegion);
+        canvas->removeEventFilter( this );
+
+        d_data->seriesItem = NULL;
+    }
+}
+
+//! Close the internal QPainter
+void QwtPlotDirectPainter::reset()
+{
+    if ( d_data->painter.isActive() )
+    {
+        QWidget *w = static_cast<QWidget *>( d_data->painter.device() );
+        if ( w )
+            w->removeEventFilter( this );
+
+        d_data->painter.end();
+    }
+}
+
+//! Event filter
+bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event )
+{
+    if ( event->type() == QEvent::Paint )
+    {
+        reset();
+
+        if ( d_data->seriesItem )
+        {
+            const QPaintEvent *pe = static_cast< QPaintEvent *>( event );
+
+            QWidget *canvas = d_data->seriesItem->plot()->canvas();
+
+            QPainter painter( canvas );
+            painter.setClipRegion( pe->region() );
+
+            bool doCopyCache = testAttribute( CopyBackingStore );
+
+            if ( doCopyCache )
+            {
+                QwtPlotCanvas *plotCanvas = 
+                    qobject_cast<QwtPlotCanvas *>( canvas );
+                if ( plotCanvas )
+                {
+                    doCopyCache = qwtHasBackingStore( plotCanvas );
+                    if ( doCopyCache )
+                    {
+                        painter.drawPixmap( plotCanvas->contentsRect().topLeft(), 
+                            *plotCanvas->backingStore() );
+                    }
+                }
+            }
+
+            if ( !doCopyCache )
+            {
+                qwtRenderItem( &painter, canvas->contentsRect(),
+                    d_data->seriesItem, d_data->from, d_data->to );
+            }
+
+            return true; // don't call QwtPlotCanvas::paintEvent()
+        }
+    }
+
+    return false;
+}
diff --git a/qwt/qwt_plot_directpainter.h b/qwt/qwt_plot_directpainter.h
new file mode 100644
index 0000000..b555c87
--- /dev/null
+++ b/qwt/qwt_plot_directpainter.h
@@ -0,0 +1,100 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_DIRECT_PAINTER_H
+#define QWT_PLOT_DIRECT_PAINTER_H
+
+#include "qwt_global.h"
+#include <qobject.h>
+
+class QRegion;
+class QwtPlotSeriesItem;
+
+/*!
+    \brief Painter object trying to paint incrementally
+
+    Often applications want to display samples while they are
+    collected. When there are too many samples complete replots
+    will be expensive to be processed in a collection cycle.
+
+    QwtPlotDirectPainter offers an API to paint
+    subsets ( f.e all additions points ) without erasing/repainting
+    the plot canvas.
+
+    On certain environments it might be important to calculate a proper
+    clip region before painting. F.e. for Qt Embedded only the clipped part
+    of the backing store will be copied to a ( maybe unaccelerated ) 
+    frame buffer.
+
+    \warning Incremental painting will only help when no replot is triggered
+             by another operation ( like changing scales ) and nothing needs
+             to be erased.
+*/
+class QWT_EXPORT QwtPlotDirectPainter: public QObject
+{
+public:
+    /*!
+      \brief Paint attributes
+      \sa setAttribute(), testAttribute(), drawSeries()
+    */
+    enum Attribute
+    {
+        /*!
+          Initializing a QPainter is an expensive operation.
+          When AtomicPainter is set each call of drawSeries() opens/closes
+          a temporary QPainter. Otherwise QwtPlotDirectPainter tries to
+          use the same QPainter as long as possible.
+         */
+        AtomicPainter = 0x01,
+
+        /*!
+          When FullRepaint is set the plot canvas is explicitly repainted
+          after the samples have been rendered.
+         */
+        FullRepaint = 0x02,
+
+        /*!
+          When QwtPlotCanvas::BackingStore is enabled the painter
+          has to paint to the backing store and the widget. In certain 
+          situations/environments it might be faster to paint to 
+          the backing store only and then copy the backing store to the canvas.
+          This flag can also be useful for settings, where Qt fills the
+          the clip region with the widget background.
+         */
+        CopyBackingStore = 0x04
+    };
+
+    //! Paint attributes
+    typedef QFlags<Attribute> Attributes;
+
+    QwtPlotDirectPainter( QObject *parent = NULL );
+    virtual ~QwtPlotDirectPainter();
+
+    void setAttribute( Attribute, bool on );
+    bool testAttribute( Attribute ) const;
+
+    void setClipping( bool );
+    bool hasClipping() const;
+
+    void setClipRegion( const QRegion & );
+    QRegion clipRegion() const;
+
+    void drawSeries( QwtPlotSeriesItem *, int from, int to );
+    void reset();
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes )
+
+#endif
diff --git a/qwt/qwt_plot_grid.cpp b/qwt/qwt_plot_grid.cpp
new file mode 100644
index 0000000..4375e53
--- /dev/null
+++ b/qwt/qwt_plot_grid.cpp
@@ -0,0 +1,438 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_grid.h"
+#include "qwt_painter.h"
+#include "qwt_text.h"
+#include "qwt_scale_map.h"
+#include "qwt_scale_div.h"
+#include "qwt_math.h"
+#include <qpainter.h>
+#include <qpen.h>
+
+class QwtPlotGrid::PrivateData
+{
+public:
+    PrivateData():
+        xEnabled( true ),
+        yEnabled( true ),
+        xMinEnabled( false ),
+        yMinEnabled( false )
+    {
+    }
+
+    bool xEnabled;
+    bool yEnabled;
+    bool xMinEnabled;
+    bool yMinEnabled;
+
+    QwtScaleDiv xScaleDiv;
+    QwtScaleDiv yScaleDiv;
+
+    QPen majorPen;
+    QPen minorPen;
+};
+
+//! Enables major grid, disables minor grid
+QwtPlotGrid::QwtPlotGrid():
+    QwtPlotItem( QwtText( "Grid" ) )
+{
+    d_data = new PrivateData;
+
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+    setZ( 10.0 );
+}
+
+//! Destructor
+QwtPlotGrid::~QwtPlotGrid()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotGrid
+int QwtPlotGrid::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotGrid;
+}
+
+/*!
+  \brief Enable or disable vertical grid lines
+  \param on Enable (true) or disable
+
+  \sa Minor grid lines can be enabled or disabled with
+      enableXMin()
+*/
+void QwtPlotGrid::enableX( bool on )
+{
+    if ( d_data->xEnabled != on )
+    {
+        d_data->xEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable horizontal grid lines
+  \param on Enable (true) or disable
+  \sa Minor grid lines can be enabled or disabled with enableYMin()
+*/
+void QwtPlotGrid::enableY( bool on )
+{
+    if ( d_data->yEnabled != on )
+    {
+        d_data->yEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable  minor vertical grid lines.
+  \param on Enable (true) or disable
+  \sa enableX()
+*/
+void QwtPlotGrid::enableXMin( bool on )
+{
+    if ( d_data->xMinEnabled != on )
+    {
+        d_data->xMinEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable minor horizontal grid lines
+  \param on Enable (true) or disable
+  \sa enableY()
+*/
+void QwtPlotGrid::enableYMin( bool on )
+{
+    if ( d_data->yMinEnabled != on )
+    {
+        d_data->yMinEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Assign an x axis scale division
+
+  \param scaleDiv Scale division
+*/
+void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv )
+{
+    if ( d_data->xScaleDiv != scaleDiv )
+    {
+        d_data->xScaleDiv = scaleDiv;
+        itemChanged();
+    }
+}
+
+/*!
+  Assign a y axis division
+
+  \param scaleDiv Scale division
+*/
+void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv )
+{
+    if ( d_data->yScaleDiv != scaleDiv )
+    {
+        d_data->yScaleDiv = scaleDiv;
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for both major and minor grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for both major and minor grid lines
+
+  \param pen Pen
+  \sa setMajorPen(), setMinorPen()
+*/
+void QwtPlotGrid::setPen( const QPen &pen )
+{
+    if ( d_data->majorPen != pen || d_data->minorPen != pen )
+    {
+        d_data->majorPen = pen;
+        d_data->minorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for both major grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setMajorPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setMajorPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for the major grid lines
+
+  \param pen Pen
+  \sa majorPen(), setMinorPen(), setPen()
+*/
+void QwtPlotGrid::setMajorPen( const QPen &pen )
+{
+    if ( d_data->majorPen != pen )
+    {
+        d_data->majorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for the minor grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setMinorPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setMinorPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for the minor grid lines
+
+  \param pen Pen
+  \sa minorPen(), setMajorPen(), setPen()
+*/
+void QwtPlotGrid::setMinorPen( const QPen &pen )
+{
+    if ( d_data->minorPen != pen )
+    {
+        d_data->minorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Draw the grid
+
+  The grid is drawn into the bounding rectangle such that
+  grid lines begin and end at the rectangle's borders. The X and Y
+  maps are used to map the scale divisions into the drawing region
+  screen.
+
+  \param painter  Painter
+  \param xMap X axis map
+  \param yMap Y axis
+  \param canvasRect Contents rectangle of the plot canvas
+*/
+void QwtPlotGrid::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    //  draw minor grid lines
+    QPen minorPen = d_data->minorPen;
+    minorPen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( minorPen );
+
+    if ( d_data->xEnabled && d_data->xMinEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) );
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) );
+    }
+
+    if ( d_data->yEnabled && d_data->yMinEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) );
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) );
+    }
+
+    //  draw major grid lines
+    QPen majorPen = d_data->majorPen;
+    majorPen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( majorPen );
+
+    if ( d_data->xEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) );
+    }
+
+    if ( d_data->yEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) );
+    }
+}
+
+void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect,
+    Qt::Orientation orientation, const QwtScaleMap &scaleMap,
+    const QList<double> &values ) const
+{
+    const double x1 = canvasRect.left();
+    const double x2 = canvasRect.right() - 1.0;
+    const double y1 = canvasRect.top();
+    const double y2 = canvasRect.bottom() - 1.0;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < values.count(); i++ )
+    {
+        double value = scaleMap.transform( values[i] );
+        if ( doAlign )
+            value = qRound( value );
+
+        if ( orientation == Qt::Horizontal )
+        {
+            if ( qwtFuzzyGreaterOrEqual( value, y1 ) &&
+                qwtFuzzyLessOrEqual( value, y2 ) )
+            {
+                QwtPainter::drawLine( painter, x1, value, x2, value );
+            }
+        }
+        else
+        {
+            if ( qwtFuzzyGreaterOrEqual( value, x1 ) &&
+                qwtFuzzyLessOrEqual( value, x2 ) )
+            {
+                QwtPainter::drawLine( painter, value, y1, value, y2 );
+            }
+        }
+    }
+}
+
+/*!
+  \return the pen for the major grid lines
+  \sa setMajorPen(), setMinorPen(), setPen()
+*/
+const QPen &QwtPlotGrid::majorPen() const
+{
+    return d_data->majorPen;
+}
+
+/*!
+  \return the pen for the minor grid lines
+  \sa setMinorPen(), setMajorPen(), setPen()
+*/
+const QPen &QwtPlotGrid::minorPen() const
+{
+    return d_data->minorPen;
+}
+
+/*!
+  \return true if vertical grid lines are enabled
+  \sa enableX()
+*/
+bool QwtPlotGrid::xEnabled() const
+{
+    return d_data->xEnabled;
+}
+
+/*!
+  \return true if minor vertical grid lines are enabled
+  \sa enableXMin()
+*/
+bool QwtPlotGrid::xMinEnabled() const
+{
+    return d_data->xMinEnabled;
+}
+
+/*!
+  \return true if horizontal grid lines are enabled
+  \sa enableY()
+*/
+bool QwtPlotGrid::yEnabled() const
+{
+    return d_data->yEnabled;
+}
+
+/*!
+  \return true if minor horizontal grid lines are enabled
+  \sa enableYMin()
+*/
+bool QwtPlotGrid::yMinEnabled() const
+{
+    return d_data->yMinEnabled;
+}
+
+
+/*! \return the scale division of the x axis */
+const QwtScaleDiv &QwtPlotGrid::xScaleDiv() const
+{
+    return d_data->xScaleDiv;
+}
+
+/*! \return the scale division of the y axis */
+const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const
+{
+    return d_data->yScaleDiv;
+}
+
+/*!
+   Update the grid to changes of the axes scale division
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes()
+*/
+void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv,
+    const QwtScaleDiv& yScaleDiv )
+{
+    setXDiv( xScaleDiv );
+    setYDiv( yScaleDiv );
+}
diff --git a/qwt/qwt_plot_grid.h b/qwt/qwt_plot_grid.h
new file mode 100644
index 0000000..16d984c
--- /dev/null
+++ b/qwt/qwt_plot_grid.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_GRID_H
+#define QWT_PLOT_GRID_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_scale_div.h"
+
+class QPainter;
+class QPen;
+class QwtScaleMap;
+class QwtScaleDiv;
+
+/*!
+  \brief A class which draws a coordinate grid
+
+  The QwtPlotGrid class can be used to draw a coordinate grid.
+  A coordinate grid consists of major and minor vertical
+  and horizontal grid lines. The locations of the grid lines
+  are determined by the X and Y scale divisions which can
+  be assigned with setXDiv() and setYDiv().
+  The draw() member draws the grid within a bounding
+  rectangle.
+*/
+
+class QWT_EXPORT QwtPlotGrid: public QwtPlotItem
+{
+public:
+    explicit QwtPlotGrid();
+    virtual ~QwtPlotGrid();
+
+    virtual int rtti() const;
+
+    void enableX( bool tf );
+    bool xEnabled() const;
+
+    void enableY( bool tf );
+    bool yEnabled() const;
+
+    void enableXMin( bool tf );
+    bool xMinEnabled() const;
+
+    void enableYMin( bool tf );
+    bool yMinEnabled() const;
+
+    void setXDiv( const QwtScaleDiv &sx );
+    const QwtScaleDiv &xScaleDiv() const;
+
+    void setYDiv( const QwtScaleDiv &sy );
+    const QwtScaleDiv &yScaleDiv() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+
+    void setMajorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setMajorPen( const QPen & );
+    const QPen& majorPen() const;
+
+    void setMinorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setMinorPen( const QPen &p );
+    const QPen& minorPen() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv &xMap, const QwtScaleDiv &yMap );
+
+private:
+    void drawLines( QPainter *painter, const QRectF &,
+        Qt::Orientation orientation, const QwtScaleMap &,
+        const QList<double> & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_histogram.cpp b/qwt/qwt_plot_histogram.cpp
new file mode 100644
index 0000000..4464f03
--- /dev/null
+++ b/qwt/qwt_plot_histogram.cpp
@@ -0,0 +1,690 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_histogram.h"
+#include "qwt_plot.h"
+#include "qwt_painter.h"
+#include "qwt_column_symbol.h"
+#include "qwt_scale_map.h"
+#include <qstring.h>
+#include <qpainter.h>
+
+static inline bool qwtIsCombinable( const QwtInterval &d1,
+    const QwtInterval &d2 )
+{
+    if ( d1.isValid() && d2.isValid() )
+    {
+        if ( d1.maxValue() == d2.minValue() )
+        {
+            if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum
+                && d2.borderFlags() & QwtInterval::ExcludeMinimum ) )
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+class QwtPlotHistogram::PrivateData
+{
+public:
+    PrivateData():
+        baseline( 0.0 ),
+        style( Columns ),
+        symbol( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    double baseline;
+
+    QPen pen;
+    QBrush brush;
+    QwtPlotHistogram::HistogramStyle style;
+    const QwtColumnSymbol *symbol;
+};
+
+/*!
+  Constructor
+  \param title Title of the histogram.
+*/
+QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the histogram.
+*/
+QwtPlotHistogram::QwtPlotHistogram( const QString &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotHistogram::~QwtPlotHistogram()
+{
+    delete d_data;
+}
+
+//! Initialize data members
+void QwtPlotHistogram::init()
+{
+    d_data = new PrivateData();
+    setData( new QwtIntervalSeriesData() );
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, true );
+
+    setZ( 20.0 );
+}
+
+/*!
+  Set the histogram's drawing style
+
+  \param style Histogram style
+  \sa HistogramStyle, style()
+*/
+void QwtPlotHistogram::setStyle( HistogramStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Style of the histogram
+    \sa HistogramStyle, setStyle()
+*/
+QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */
+void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}   
+
+/*!
+  Assign a pen, that is used in a style() depending way.
+
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotHistogram::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used in a style() depending way.
+  \sa setPen(), brush()
+*/
+const QPen &QwtPlotHistogram::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush, that is used in a style() depending way.
+
+  \param brush New brush
+  \sa pen(), brush()
+*/
+void QwtPlotHistogram::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used in a style() depending way.
+  \sa setPen(), brush()
+*/
+const QBrush &QwtPlotHistogram::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Assign a symbol
+
+  In Column style an optional symbol can be assigned, that is responsible
+  for displaying the rectangle that is defined by the interval and
+  the distance between baseline() and value. When no symbol has been
+  defined the area is displayed as plain rectangle using pen() and brush().
+
+  \sa style(), symbol(), drawColumn(), pen(), brush()
+
+  \note In applications, where different intervals need to be displayed
+        in a different way ( f.e different colors or even using different symbols)
+        it is recommended to overload drawColumn().
+*/
+void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtColumnSymbol *QwtPlotHistogram::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  \brief Set the value of the baseline
+
+  Each column representing an QwtIntervalSample is defined by its
+  interval and the interval between baseline and the value of the sample.
+
+  The default value of the baseline is 0.0.
+
+  \param value Value of the baseline
+  \sa baseline()
+*/
+void QwtPlotHistogram::setBaseline( double value )
+{
+    if ( d_data->baseline != value )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value of the baseline
+  \sa setBaseline()
+*/
+double QwtPlotHistogram::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotHistogram::boundingRect() const
+{
+    QRectF rect = data()->boundingRect();
+    if ( !rect.isValid() )
+        return rect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        rect = QRectF( rect.y(), rect.x(),
+            rect.height(), rect.width() );
+
+        if ( rect.left() > d_data->baseline )
+            rect.setLeft( d_data->baseline );
+        else if ( rect.right() < d_data->baseline )
+            rect.setRight( d_data->baseline );
+    }
+    else
+    {
+        if ( rect.bottom() < d_data->baseline )
+            rect.setBottom( d_data->baseline );
+        else if ( rect.top() > d_data->baseline )
+            rect.setTop( d_data->baseline );
+    }
+
+    return rect;
+}
+
+//! \return QwtPlotItem::Rtti_PlotHistogram
+int QwtPlotHistogram::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotHistogram;
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotHistogram::setSamples(
+    const QVector<QwtIntervalSample> &samples )
+{
+    setData( new QwtIntervalSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotHistogram::setSamples( 
+    QwtSeriesData<QwtIntervalSample> *data )
+{
+    setData( data );
+}
+
+/*!
+  Draw a subset of the histogram samples
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawOutline(), drawLines(), drawColumns
+*/
+void QwtPlotHistogram::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &, int from, int to ) const
+{
+    if ( !painter || dataSize() <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    switch ( d_data->style )
+    {
+        case Outline:
+            drawOutline( painter, xMap, yMap, from, to );
+            break;
+        case Lines:
+            drawLines( painter, xMap, yMap, from, to );
+            break;
+        case Columns:
+            drawColumns( painter, xMap, yMap, from, to );
+            break;
+        default:
+            break;
+    }
+}
+
+/*!
+  Draw a histogram in Outline style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style()
+  \warning The outline style requires, that the intervals are in increasing
+           order and not overlapping.
+*/
+void QwtPlotHistogram::drawOutline( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double v0 = ( orientation() == Qt::Horizontal ) ?
+        xMap.transform( baseline() ) : yMap.transform( baseline() );
+    if ( doAlign )
+        v0 = qRound( v0 );
+
+    QwtIntervalSample previous;
+
+    QPolygonF polygon;
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = this->sample( i );
+
+        if ( !sample.interval.isValid() )
+        {
+            flushPolygon( painter, v0, polygon );
+            previous = sample;
+            continue;
+        }
+
+        if ( previous.interval.isValid() )
+        {
+            if ( !qwtIsCombinable( previous.interval, sample.interval ) )
+                flushPolygon( painter, v0, polygon );
+        }
+
+        if ( orientation() == Qt::Vertical )
+        {
+            double x1 = xMap.transform( sample.interval.minValue() );
+            double x2 = xMap.transform( sample.interval.maxValue() );
+            double y = yMap.transform( sample.value );
+            if ( doAlign )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+                y = qRound( y );
+            }
+
+            if ( polygon.size() == 0 )
+                polygon += QPointF( x1, v0 );
+
+            polygon += QPointF( x1, y );
+            polygon += QPointF( x2, y );
+        }
+        else
+        {
+            double y1 = yMap.transform( sample.interval.minValue() );
+            double y2 = yMap.transform( sample.interval.maxValue() );
+            double x = xMap.transform( sample.value );
+            if ( doAlign )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+                x = qRound( x );
+            }
+
+            if ( polygon.size() == 0 )
+                polygon += QPointF( v0, y1 );
+
+            polygon += QPointF( x, y1 );
+            polygon += QPointF( x, y2 );
+        }
+        previous = sample;
+    }
+
+    flushPolygon( painter, v0, polygon );
+}
+
+/*!
+  Draw a histogram in Columns style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style(), setSymbol(), drawColumn()
+*/
+void QwtPlotHistogram::drawColumns( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    painter->setPen( d_data->pen );
+    painter->setBrush( d_data->brush );
+
+    const QwtSeriesData<QwtIntervalSample> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = series->sample( i );
+        if ( !sample.interval.isNull() )
+        {
+            const QwtColumnRect rect = columnRect( sample, xMap, yMap );
+            drawColumn( painter, rect, sample );
+        }
+    }
+}
+
+/*!
+  Draw a histogram in Lines style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style(), setPen()
+*/
+void QwtPlotHistogram::drawLines( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->setPen( d_data->pen );
+    painter->setBrush( Qt::NoBrush );
+
+    const QwtSeriesData<QwtIntervalSample> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = series->sample( i );
+        if ( !sample.interval.isNull() )
+        {
+            const QwtColumnRect rect = columnRect( sample, xMap, yMap );
+
+            QRectF r = rect.toRect();
+            if ( doAlign )
+            {
+                r.setLeft( qRound( r.left() ) );
+                r.setRight( qRound( r.right() ) );
+                r.setTop( qRound( r.top() ) );
+                r.setBottom( qRound( r.bottom() ) );
+            }
+
+            switch ( rect.direction )
+            {
+                case QwtColumnRect::LeftToRight:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topRight(), r.bottomRight() );
+                    break;
+                }
+                case QwtColumnRect::RightToLeft:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topLeft(), r.bottomLeft() );
+                    break;
+                }
+                case QwtColumnRect::TopToBottom:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.bottomRight(), r.bottomLeft() );
+                    break;
+                }
+                case QwtColumnRect::BottomToTop:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topRight(), r.topLeft() );
+                    break;
+                }
+            }
+        }
+    }
+}
+
+//! Internal, used by the Outline style.
+void QwtPlotHistogram::flushPolygon( QPainter *painter,
+    double baseLine, QPolygonF &polygon ) const
+{
+    if ( polygon.size() == 0 )
+        return;
+
+    if ( orientation() == Qt::Horizontal )
+        polygon += QPointF( baseLine, polygon.last().y() );
+    else
+        polygon += QPointF( polygon.last().x(), baseLine );
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+    {
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( d_data->brush );
+
+        if ( orientation() == Qt::Horizontal )
+        {
+            polygon += QPointF( polygon.last().x(), baseLine );
+            polygon += QPointF( polygon.first().x(), baseLine );
+        }
+        else
+        {
+            polygon += QPointF( baseLine, polygon.last().y() );
+            polygon += QPointF( baseLine, polygon.first().y() );
+        }
+
+        QwtPainter::drawPolygon( painter, polygon );
+
+        polygon.pop_back();
+        polygon.pop_back();
+    }
+    if ( d_data->pen.style() != Qt::NoPen )
+    {
+        painter->setBrush( Qt::NoBrush );
+        painter->setPen( d_data->pen );
+        QwtPainter::drawPolyline( painter, polygon );
+    }
+    polygon.clear();
+}
+
+/*!
+  Calculate the area that is covered by a sample
+
+  \param sample Sample
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+
+  \return Rectangle, that is covered by a sample
+*/
+QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
+{
+    QwtColumnRect rect;
+
+    const QwtInterval &iv = sample.interval;
+    if ( !iv.isValid() )
+        return rect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        const double x0 = xMap.transform( baseline() );
+        const double x  = xMap.transform( sample.value );
+        const double y1 = yMap.transform( iv.minValue() );
+        const double y2 = yMap.transform( iv.maxValue() );
+
+        rect.hInterval.setInterval( x0, x );
+        rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
+        rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
+                         QwtColumnRect::LeftToRight;
+    }
+    else
+    {
+        const double x1 = xMap.transform( iv.minValue() );
+        const double x2 = xMap.transform( iv.maxValue() );
+        const double y0 = yMap.transform( baseline() );
+        const double y = yMap.transform( sample.value );
+
+        rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
+        rect.vInterval.setInterval( y0, y );
+        rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
+            QwtColumnRect::TopToBottom;
+    }
+
+    return rect;
+}
+
+/*!
+  Draw a column for a sample in Columns style().
+
+  When a symbol() has been set the symbol is used otherwise the
+  column is displayed as plain rectangle using pen() and brush().
+
+  \param painter Painter
+  \param rect Rectangle where to paint the column in paint device coordinates
+  \param sample Sample to be displayed
+
+  \note In applications, where different intervals need to be displayed
+        in a different way ( f.e different colors or even using different symbols)
+        it is recommended to overload drawColumn().
+*/
+void QwtPlotHistogram::drawColumn( QPainter *painter,
+    const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
+{
+    Q_UNUSED( sample );
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
+    {
+        d_data->symbol->draw( painter, rect );
+    }
+    else
+    {
+        QRectF r = rect.toRect();
+        if ( QwtPainter::roundingAlignment( painter ) )
+        {
+            r.setLeft( qRound( r.left() ) );
+            r.setRight( qRound( r.right() ) );
+            r.setTop( qRound( r.top() ) );
+            r.setBottom( qRound( r.bottom() ) );
+        }
+
+        QwtPainter::drawRect( painter, r );
+    }
+}
+
+/*!
+  A plain rectangle without pen using the brush()
+
+  \param index Index of the legend entry 
+                ( ignored as there is only one )
+  \param size Icon size
+  \return A graphic displaying the icon
+    
+  \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+*/
+QwtGraphic QwtPlotHistogram::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+    return defaultIcon( d_data->brush, size );
+}
diff --git a/qwt/qwt_plot_histogram.h b/qwt/qwt_plot_histogram.h
new file mode 100644
index 0000000..b96bddd
--- /dev/null
+++ b/qwt/qwt_plot_histogram.h
@@ -0,0 +1,139 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_HISTOGRAM_H
+#define QWT_PLOT_HISTOGRAM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_column_symbol.h"
+#include <qcolor.h>
+#include <qvector.h>
+
+class QwtIntervalData;
+class QString;
+class QPolygonF;
+
+/*!
+  \brief QwtPlotHistogram represents a series of samples, where an interval
+         is associated with a value ( \f$y = f([x1,x2])\f$ ).
+
+  The representation depends on the style() and an optional symbol()
+  that is displayed for each interval.
+
+  \note The term "histogram" is used in a different way in the areas of
+        digital image processing and statistics. Wikipedia introduces the
+        terms "image histogram" and "color histogram" to avoid confusions.
+        While "image histograms" can be displayed by a QwtPlotCurve there
+        is no applicable plot item for a "color histogram" yet.
+
+  \sa QwtPlotBarChart, QwtPlotMultiBarChart
+*/
+
+class QWT_EXPORT QwtPlotHistogram: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QwtIntervalSample>
+{
+public:
+    /*!
+        Histogram styles.
+        The default style is QwtPlotHistogram::Columns.
+
+        \sa setStyle(), style(), setSymbol(), symbol(), setBaseline()
+    */
+    enum HistogramStyle
+    {
+        /*!
+           Draw an outline around the area, that is build by all intervals
+           using the pen() and fill it with the brush(). The outline style
+           requires, that the intervals are in increasing order and
+           not overlapping.
+         */
+        Outline,
+
+        /*!
+           Draw a column for each interval. When a symbol() has been set
+           the symbol is used otherwise the column is displayed as 
+           plain rectangle using pen() and brush().
+         */
+        Columns,
+
+        /*!
+           Draw a simple line using the pen() for each interval.
+         */
+        Lines,
+
+        /*!
+           Styles >= UserStyle are reserved for derived
+           classes that overload drawSeries() with
+           additional application specific ways to display a histogram.
+         */
+        UserStyle = 100
+    };
+
+    explicit QwtPlotHistogram( const QString &title = QString::null );
+    explicit QwtPlotHistogram( const QwtText &title );
+    virtual ~QwtPlotHistogram();
+
+    virtual int rtti() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setSamples( const QVector<QwtIntervalSample> & );
+    void setSamples( QwtSeriesData<QwtIntervalSample> * );
+
+    void setBaseline( double reference );
+    double baseline() const;
+
+    void setStyle( HistogramStyle style );
+    HistogramStyle style() const;
+
+    void setSymbol( const QwtColumnSymbol * );
+    const QwtColumnSymbol *symbol() const;
+
+    virtual void drawSeries( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    virtual QwtColumnRect columnRect( const QwtIntervalSample &,
+        const QwtScaleMap &, const QwtScaleMap & ) const;
+
+    virtual void drawColumn( QPainter *, const QwtColumnRect &,
+        const QwtIntervalSample & ) const;
+
+    void drawColumns( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        int from, int to ) const;
+
+    void drawOutline( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        int from, int to ) const;
+
+    void drawLines( QPainter *,
+         const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+         int from, int to ) const;
+
+private:
+    void init();
+    void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_intervalcurve.cpp b/qwt/qwt_plot_intervalcurve.cpp
new file mode 100644
index 0000000..200ea39
--- /dev/null
+++ b/qwt/qwt_plot_intervalcurve.cpp
@@ -0,0 +1,603 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_intervalcurve.h"
+#include "qwt_interval_symbol.h"
+#include "qwt_scale_map.h"
+#include "qwt_clipper.h"
+#include "qwt_painter.h"
+#include <string.h>
+
+#include <qpainter.h>
+
+static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample,
+    double xMin, double xMax, double yMin, double yMax )
+{
+    const double y = sample.value;
+    const double x1 = sample.interval.minValue();
+    const double x2 = sample.interval.maxValue();
+
+    const bool isOffScreen = ( y < yMin ) || ( y > yMax )
+        || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax );
+
+    return !isOffScreen;
+}
+
+static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample,
+    double xMin, double xMax, double yMin, double yMax )
+{
+    const double x = sample.value;
+    const double y1 = sample.interval.minValue();
+    const double y2 = sample.interval.maxValue();
+
+    const bool isOffScreen = ( x < xMin ) || ( x > xMax )
+        || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax );
+
+    return !isOffScreen;
+}
+
+class QwtPlotIntervalCurve::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotIntervalCurve::Tube ),
+        symbol( NULL ),
+        pen( Qt::black ),
+        brush( Qt::white )
+    {
+        paintAttributes = QwtPlotIntervalCurve::ClipPolygons;
+        paintAttributes |= QwtPlotIntervalCurve::ClipSymbol;
+
+        pen.setCapStyle( Qt::FlatCap );
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtPlotIntervalCurve::CurveStyle style;
+    const QwtIntervalSymbol *symbol;
+
+    QPen pen;
+    QBrush brush;
+
+    QwtPlotIntervalCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotIntervalCurve::~QwtPlotIntervalCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotIntervalCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+
+    d_data = new PrivateData;
+    setData( new QwtIntervalSeriesData() );
+
+    setZ( 19.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotIntervalCurve
+int QwtPlotIntervalCurve::rtti() const
+{
+    return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotIntervalCurve::setPaintAttribute(
+    PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotIntervalCurve::testPaintAttribute(
+    PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of samples
+*/
+void QwtPlotIntervalCurve::setSamples(
+    const QVector<QwtIntervalSample> &samples )
+{
+    setData( new QwtIntervalSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotIntervalCurve::setSamples( 
+    QwtSeriesData<QwtIntervalSample> *data )
+{
+    setData( data );
+}
+
+/*!
+  Set the curve's drawing style
+
+  \param style Curve style
+  \sa CurveStyle, style()
+*/
+void QwtPlotIntervalCurve::setStyle( CurveStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Style of the curve
+    \sa setStyle()
+*/
+QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Assign a symbol.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */
+void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}   
+
+/*!
+  \brief Assign a pen
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotIntervalCurve::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Pen used to draw the lines
+    \sa setPen(), brush()
+*/
+const QPen& QwtPlotIntervalCurve::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush.
+
+  The brush is used to fill the area in Tube style().
+
+  \param brush Brush
+  \sa brush(), pen(), setStyle(), CurveStyle
+*/
+void QwtPlotIntervalCurve::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the area in Tube style()
+  \sa setBrush(), setStyle(), CurveStyle
+*/
+const QBrush& QwtPlotIntervalCurve::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotIntervalCurve::boundingRect() const
+{
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.isValid() && orientation() == Qt::Vertical )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw a subset of the samples
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawTube(), drawSymbols()
+*/
+void QwtPlotIntervalCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    switch ( d_data->style )
+    {
+        case Tube:
+            drawTube( painter, xMap, yMap, canvasRect, from, to );
+            break;
+
+        case NoCurve:
+        default:
+            break;
+    }
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
+    {
+        drawSymbols( painter, *d_data->symbol,
+            xMap, yMap, canvasRect, from, to );
+    }
+}
+
+/*!
+  Draw a tube
+
+  Builds 2 curves from the upper and lower limits of the intervals
+  and draws them with the pen(). The area between the curves is
+  filled with the brush().
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawSeries(), drawSymbols()
+*/
+void QwtPlotIntervalCurve::drawTube( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->save();
+
+    const size_t size = to - from + 1;
+    QPolygonF polygon( 2 * size );
+    QPointF *points = polygon.data();
+
+    for ( uint i = 0; i < size; i++ )
+    {
+        QPointF &minValue = points[i];
+        QPointF &maxValue = points[2 * size - 1 - i];
+
+        const QwtIntervalSample intervalSample = sample( from + i );
+        if ( orientation() == Qt::Vertical )
+        {
+            double x = xMap.transform( intervalSample.value );
+            double y1 = yMap.transform( intervalSample.interval.minValue() );
+            double y2 = yMap.transform( intervalSample.interval.maxValue() );
+            if ( doAlign )
+            {
+                x = qRound( x );
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            minValue.rx() = x;
+            minValue.ry() = y1;
+            maxValue.rx() = x;
+            maxValue.ry() = y2;
+        }
+        else
+        {
+            double y = yMap.transform( intervalSample.value );
+            double x1 = xMap.transform( intervalSample.interval.minValue() );
+            double x2 = xMap.transform( intervalSample.interval.maxValue() );
+            if ( doAlign )
+            {
+                y = qRound( y );
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            minValue.rx() = x1;
+            minValue.ry() = y;
+            maxValue.rx() = x2;
+            maxValue.ry() = y;
+        }
+    }
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+    {
+        painter->setPen( QPen( Qt::NoPen ) );
+        painter->setBrush( d_data->brush );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            const qreal m = 1.0;
+            const QPolygonF p = QwtClipper::clipPolygonF(
+               canvasRect.adjusted( -m, -m, m, m ), polygon, true );
+
+            QwtPainter::drawPolygon( painter, p );
+        }
+        else
+        {
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+
+    if ( d_data->pen.style() != Qt::NoPen )
+    {
+        painter->setPen( d_data->pen );
+        painter->setBrush( Qt::NoBrush );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() );
+            const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );
+
+            QPolygonF p;
+
+            p.resize( size );
+            ::memcpy( p.data(), points, size * sizeof( QPointF ) );
+            p = QwtClipper::clipPolygonF( clipRect, p );
+            QwtPainter::drawPolyline( painter, p );
+
+            p.resize( size );
+            ::memcpy( p.data(), points + size, size * sizeof( QPointF ) );
+            p = QwtClipper::clipPolygonF( clipRect, p );
+            QwtPainter::drawPolyline( painter, p );
+        }
+        else
+        {
+            QwtPainter::drawPolyline( painter, points, size );
+            QwtPainter::drawPolyline( painter, points + size, size );
+        }
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw symbols for a subset of the samples
+
+  \param painter Painter
+  \param symbol Interval symbol
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted
+
+  \sa setSymbol(), drawSeries(), drawTube()
+*/
+void QwtPlotIntervalCurve::drawSymbols(
+    QPainter *painter, const QwtIntervalSymbol &symbol,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    painter->save();
+
+    QPen pen = symbol.pen();
+    pen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+
+    const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect );
+
+    const double xMin = tr.left();
+    const double xMax = tr.right();
+    const double yMin = tr.top();
+    const double yMax = tr.bottom();
+
+    const bool doClip = d_data->paintAttributes & ClipSymbol;
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample s = sample( i );
+
+        if ( orientation() == Qt::Vertical )
+        {
+            if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) )
+            {
+                const double x = xMap.transform( s.value );
+                const double y1 = yMap.transform( s.interval.minValue() );
+                const double y2 = yMap.transform( s.interval.maxValue() );
+
+                symbol.draw( painter, orientation(),
+                    QPointF( x, y1 ), QPointF( x, y2 ) );
+            }
+        }
+        else
+        {
+            if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) )
+            {
+                const double y = yMap.transform( s.value );
+                const double x1 = xMap.transform( s.interval.minValue() );
+                const double x2 = xMap.transform( s.interval.maxValue() );
+
+                symbol.draw( painter, orientation(),
+                    QPointF( x1, y ), QPointF( x2, y ) );
+            }
+        }
+    }
+
+    painter->restore();
+}
+
+/*!
+  \return Icon for the legend
+
+  In case of Tube style() the icon is a plain rectangle filled with the brush().
+  If a symbol is assigned it is scaled to size.
+
+  \param index Index of the legend entry 
+               ( ignored as there is only one )
+  \param size Icon size
+    
+  \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+*/
+QwtGraphic QwtPlotIntervalCurve::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->style == Tube )
+    {
+        QRectF r( 0, 0, size.width(), size.height() );
+        painter.fillRect( r, d_data->brush );
+    }
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
+    {
+        QPen pen = d_data->symbol->pen();
+        pen.setWidthF( pen.widthF() );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter.setPen( pen );
+        painter.setBrush( d_data->symbol->brush() );
+
+        if ( orientation() == Qt::Vertical )
+        {
+            const double x = 0.5 * size.width();
+
+            d_data->symbol->draw( &painter, orientation(),
+                QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) );
+        }
+        else
+        {
+            const double y = 0.5 * size.height();
+
+            d_data->symbol->draw( &painter, orientation(),
+                QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) );
+        }
+    }
+
+    return icon;
+}
diff --git a/qwt/qwt_plot_intervalcurve.h b/qwt/qwt_plot_intervalcurve.h
new file mode 100644
index 0000000..624d82f
--- /dev/null
+++ b/qwt/qwt_plot_intervalcurve.h
@@ -0,0 +1,132 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_INTERVAL_CURVE_H
+#define QWT_PLOT_INTERVAL_CURVE_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_series_data.h"
+
+class QwtIntervalSymbol;
+
+/*!
+  \brief QwtPlotIntervalCurve represents a series of samples, where each value
+         is associated with an interval ( \f$[y1,y2] = f(x)\f$ ).
+
+  The representation depends on the style() and an optional symbol()
+  that is displayed for each interval. QwtPlotIntervalCurve might be used
+  to display error bars or the area between 2 curves.
+*/
+class QWT_EXPORT QwtPlotIntervalCurve: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QwtIntervalSample>
+{
+public:
+    /*!
+        \brief Curve styles.
+        The default setting is QwtPlotIntervalCurve::Tube.
+
+        \sa setStyle(), style()
+    */
+    enum CurveStyle
+    {
+        /*!
+           Don't draw a curve. Note: This doesn't affect the symbols.
+         */
+        NoCurve,
+
+        /*!
+           Build 2 curves from the upper and lower limits of the intervals
+           and draw them with the pen(). The area between the curves is
+           filled with the brush().
+         */
+        Tube,
+
+        /*!
+           Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived
+           classes that overload drawSeries() with
+           additional application specific curve types.
+         */
+        UserCurve = 100
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance.
+         */
+        ClipPolygons = 0x01,
+
+        //! Check if a symbol is on the plot canvas before painting it.
+        ClipSymbol   = 0x02
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotIntervalCurve( const QString &title = QString::null );
+    explicit QwtPlotIntervalCurve( const QwtText &title );
+
+    virtual ~QwtPlotIntervalCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtIntervalSample> & );
+    void setSamples( QwtSeriesData<QwtIntervalSample> * );
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setStyle( CurveStyle style );
+    CurveStyle style() const;
+
+    void setSymbol( const QwtIntervalSymbol * );
+    const QwtIntervalSymbol *symbol() const;
+
+    virtual void drawSeries( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawTube( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_item.cpp b/qwt/qwt_plot_item.cpp
new file mode 100644
index 0000000..4cb03bb
--- /dev/null
+++ b/qwt/qwt_plot_item.cpp
@@ -0,0 +1,698 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_item.h"
+#include "qwt_text.h"
+#include "qwt_plot.h"
+#include "qwt_legend_data.h"
+#include "qwt_scale_div.h"
+#include "qwt_graphic.h"
+#include <qpainter.h>
+
+class QwtPlotItem::PrivateData
+{
+public:
+    PrivateData():
+        plot( NULL ),
+        isVisible( true ),
+        attributes( 0 ),
+        interests( 0 ),
+        renderHints( 0 ),
+        renderThreadCount( 1 ),
+        z( 0.0 ),
+        xAxis( QwtPlot::xBottom ),
+        yAxis( QwtPlot::yLeft ),
+        legendIconSize( 8, 8 )
+    {
+    }
+
+    mutable QwtPlot *plot;
+
+    bool isVisible;
+
+    QwtPlotItem::ItemAttributes attributes;
+    QwtPlotItem::ItemInterests interests;
+
+    QwtPlotItem::RenderHints renderHints;
+    uint renderThreadCount;
+
+    double z;
+
+    int xAxis;
+    int yAxis;
+
+    QwtText title;
+    QSize legendIconSize;
+};
+
+/*!
+   Constructor
+   \param title Title of the item
+*/
+QwtPlotItem::QwtPlotItem( const QwtText &title )
+{
+    d_data = new PrivateData;
+    d_data->title = title;
+}
+
+//! Destroy the QwtPlotItem
+QwtPlotItem::~QwtPlotItem()
+{
+    attach( NULL );
+    delete d_data;
+}
+
+/*!
+  \brief Attach the item to a plot.
+
+  This method will attach a QwtPlotItem to the QwtPlot argument. It will first
+  detach the QwtPlotItem from any plot from a previous call to attach (if
+  necessary). If a NULL argument is passed, it will detach from any QwtPlot it
+  was attached to.
+
+  \param plot Plot widget
+  \sa detach()
+*/
+void QwtPlotItem::attach( QwtPlot *plot )
+{
+    if ( plot == d_data->plot )
+        return;
+
+    if ( d_data->plot )
+        d_data->plot->attachItem( this, false );
+
+    d_data->plot = plot;
+
+    if ( d_data->plot )
+        d_data->plot->attachItem( this, true );
+}
+
+/*!
+   \brief This method detaches a QwtPlotItem from any 
+          QwtPlot it has been associated with.
+
+   detach() is equivalent to calling attach( NULL )
+   \sa attach()
+*/
+void QwtPlotItem::detach()
+{
+    attach( NULL );
+}
+
+/*!
+   Return rtti for the specific class represented. QwtPlotItem is simply
+   a virtual interface class, and base classes will implement this method
+   with specific rtti values so a user can differentiate them.
+
+   The rtti value is useful for environments, where the
+   runtime type information is disabled and it is not possible
+   to do a dynamic_cast<...>.
+
+   \return rtti value
+   \sa RttiValues
+*/
+int QwtPlotItem::rtti() const
+{
+    return Rtti_PlotItem;
+}
+
+//! Return attached plot
+QwtPlot *QwtPlotItem::plot() const
+{
+    return d_data->plot;
+}
+
+/*!
+   Plot items are painted in increasing z-order.
+
+   \return setZ(), QwtPlotDict::itemList()
+*/
+double QwtPlotItem::z() const
+{
+    return d_data->z;
+}
+
+/*!
+   \brief Set the z value
+
+   Plot items are painted in increasing z-order.
+
+   \param z Z-value
+   \sa z(), QwtPlotDict::itemList()
+*/
+void QwtPlotItem::setZ( double z )
+{
+    if ( d_data->z != z )
+    {
+        if ( d_data->plot ) // update the z order
+            d_data->plot->attachItem( this, false );
+
+        d_data->z = z;
+
+        if ( d_data->plot )
+            d_data->plot->attachItem( this, true );
+
+        itemChanged();
+    }
+}
+
+/*!
+   Set a new title
+
+   \param title Title
+   \sa title()
+*/
+void QwtPlotItem::setTitle( const QString &title )
+{
+    setTitle( QwtText( title ) );
+}
+
+/*!
+   Set a new title
+
+   \param title Title
+   \sa title()
+*/
+void QwtPlotItem::setTitle( const QwtText &title )
+{
+    if ( d_data->title != title )
+    {
+        d_data->title = title;
+
+        legendChanged();
+#if 0
+        itemChanged();
+#endif
+    }
+}
+
+/*!
+   \return Title of the item
+   \sa setTitle()
+*/
+const QwtText &QwtPlotItem::title() const
+{
+    return d_data->title;
+}
+
+/*!
+   Toggle an item attribute
+
+   \param attribute Attribute type
+   \param on true/false
+
+   \sa testItemAttribute(), ItemInterest
+*/
+void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on )
+{
+    if ( d_data->attributes.testFlag( attribute ) != on )
+    {
+        if ( on )
+            d_data->attributes |= attribute;
+        else
+            d_data->attributes &= ~attribute;
+
+        if ( attribute == QwtPlotItem::Legend )
+            legendChanged();
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test an item attribute
+
+   \param attribute Attribute type
+   \return true/false
+   \sa setItemAttribute(), ItemInterest
+*/
+bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const
+{
+    return d_data->attributes.testFlag( attribute );
+}
+
+/*!
+   Toggle an item interest
+
+   \param interest Interest type
+   \param on true/false
+
+   \sa testItemInterest(), ItemAttribute
+*/
+void QwtPlotItem::setItemInterest( ItemInterest interest, bool on )
+{
+    if ( d_data->interests.testFlag( interest ) != on )
+    {
+        if ( on )
+            d_data->interests |= interest;
+        else
+            d_data->interests &= ~interest;
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test an item interest
+
+   \param interest Interest type
+   \return true/false
+   \sa setItemInterest(), ItemAttribute
+*/
+bool QwtPlotItem::testItemInterest( ItemInterest interest ) const
+{
+    return d_data->interests.testFlag( interest );
+}
+
+/*!
+   Toggle an render hint
+
+   \param hint Render hint
+   \param on true/false
+
+   \sa testRenderHint(), RenderHint
+*/
+void QwtPlotItem::setRenderHint( RenderHint hint, bool on )
+{
+    if ( d_data->renderHints.testFlag( hint ) != on )
+    {
+        if ( on )
+            d_data->renderHints |= hint;
+        else
+            d_data->renderHints &= ~hint;
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test a render hint
+
+   \param hint Render hint
+   \return true/false
+   \sa setRenderHint(), RenderHint
+*/
+bool QwtPlotItem::testRenderHint( RenderHint hint ) const
+{
+    return d_data->renderHints.testFlag( hint );
+}
+
+/*!
+   On multi core systems rendering of certain plot item 
+   ( f.e QwtPlotRasterItem ) can be done in parallel in 
+   several threads.
+
+   The default setting is set to 1.
+
+   \param numThreads Number of threads to be used for rendering.
+                     If numThreads is set to 0, the system specific
+                     ideal thread count is used.
+
+   The default thread count is 1 ( = no additional threads )
+*/
+void QwtPlotItem::setRenderThreadCount( uint numThreads )
+{
+    d_data->renderThreadCount = numThreads;
+}
+
+/*!
+   \return Number of threads to be used for rendering.
+           If numThreads() is set to 0, the system specific
+           ideal thread count is used.
+*/
+uint QwtPlotItem::renderThreadCount() const
+{
+    return d_data->renderThreadCount;
+}
+
+/*!
+   Set the size of the legend icon
+
+   The default setting is 8x8 pixels
+
+   \param size Size
+   \sa legendIconSize(), legendIcon()
+*/
+void QwtPlotItem::setLegendIconSize( const QSize &size )
+{
+    if ( d_data->legendIconSize != size )
+    {
+        d_data->legendIconSize = size;
+        legendChanged();
+    }
+}
+
+/*!
+   \return Legend icon size
+   \sa setLegendIconSize(), legendIcon()
+*/
+QSize QwtPlotItem::legendIconSize() const
+{
+    return d_data->legendIconSize;
+}
+
+/*!
+   \return Icon representing the item on the legend
+
+   The default implementation returns an invalid icon
+
+   \param index Index of the legend entry 
+                ( usually there is only one )
+   \param size Icon size
+
+   \sa setLegendIconSize(), legendData()
+ */
+QwtGraphic QwtPlotItem::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    Q_UNUSED( index )
+    Q_UNUSED( size )
+
+    return QwtGraphic();
+}
+
+/*!
+   \brief Return a default icon from a brush
+
+   The default icon is a filled rectangle used
+   in several derived classes as legendIcon().
+
+   \param brush Fill brush
+   \param size Icon size
+
+   \return A filled rectangle
+ */
+QwtGraphic QwtPlotItem::defaultIcon( 
+    const QBrush &brush, const QSizeF &size ) const
+{   
+    QwtGraphic icon;
+    if ( !size.isEmpty() )
+    {
+        icon.setDefaultSize( size );
+        
+        QRectF r( 0, 0, size.width(), size.height() );
+        
+        QPainter painter( &icon );
+        painter.fillRect( r, brush );
+    }   
+    
+    return icon;
+}   
+
+//! Show the item
+void QwtPlotItem::show()
+{
+    setVisible( true );
+}
+
+//! Hide the item
+void QwtPlotItem::hide()
+{
+    setVisible( false );
+}
+
+/*!
+    Show/Hide the item
+
+    \param on Show if true, otherwise hide
+    \sa isVisible(), show(), hide()
+*/
+void QwtPlotItem::setVisible( bool on )
+{
+    if ( on != d_data->isVisible )
+    {
+        d_data->isVisible = on;
+        itemChanged();
+    }
+}
+
+/*!
+    \return true if visible
+    \sa setVisible(), show(), hide()
+*/
+bool QwtPlotItem::isVisible() const
+{
+    return d_data->isVisible;
+}
+
+/*!
+   Update the legend and call QwtPlot::autoRefresh() for the
+   parent plot.
+
+   \sa QwtPlot::legendChanged(), QwtPlot::autoRefresh()
+*/
+void QwtPlotItem::itemChanged()
+{
+    if ( d_data->plot )
+        d_data->plot->autoRefresh();
+}
+
+/*!
+   Update the legend of the parent plot.
+   \sa QwtPlot::updateLegend(), itemChanged()
+*/
+void QwtPlotItem::legendChanged()
+{
+    if ( testItemAttribute( QwtPlotItem::Legend ) && d_data->plot )
+        d_data->plot->updateLegend( this );
+}
+
+/*!
+   Set X and Y axis
+
+   The item will painted according to the coordinates of its Axes.
+
+   \param xAxis X Axis ( QwtPlot::xBottom or QwtPlot::xTop )
+   \param yAxis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight )
+
+   \sa setXAxis(), setYAxis(), xAxis(), yAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setAxes( int xAxis, int yAxis )
+{
+    if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop )
+        d_data->xAxis = xAxis;
+
+    if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight )
+        d_data->yAxis = yAxis;
+
+    itemChanged();
+}
+
+/*!
+   Set the X axis
+
+   The item will painted according to the coordinates its Axes.
+
+   \param axis X Axis ( QwtPlot::xBottom or QwtPlot::xTop )
+   \sa setAxes(), setYAxis(), xAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setXAxis( int axis )
+{
+    if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+    {
+        d_data->xAxis = axis;
+        itemChanged();
+    }
+}
+
+/*!
+   Set the Y axis
+
+   The item will painted according to the coordinates its Axes.
+
+   \param axis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight )
+   \sa setAxes(), setXAxis(), yAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setYAxis( int axis )
+{
+    if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
+    {
+        d_data->yAxis = axis;
+        itemChanged();
+    }
+}
+
+//! Return xAxis
+int QwtPlotItem::xAxis() const
+{
+    return d_data->xAxis;
+}
+
+//! Return yAxis
+int QwtPlotItem::yAxis() const
+{
+    return d_data->yAxis;
+}
+
+/*!
+   \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0)
+   \note A width or height < 0.0 is ignored by the autoscaler
+*/
+QRectF QwtPlotItem::boundingRect() const
+{
+    return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid
+}
+
+/*!
+   \brief Calculate a hint for the canvas margin
+
+   When the QwtPlotItem::Margins flag is enabled the plot item
+   indicates, that it needs some margins at the borders of the canvas.
+   This is f.e. used by bar charts to reserve space for displaying
+   the bars.
+
+   The margins are in target device coordinates ( pixels on screen )
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param canvasRect Contents rectangle of the canvas in painter coordinates
+   \param left Returns the left margin
+   \param top Returns the top margin
+   \param right Returns the right margin
+   \param bottom Returns the bottom margin
+
+   \return The default implementation returns 0 for all margins
+
+   \sa QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins()
+ */
+void QwtPlotItem::getCanvasMarginHint( const QwtScaleMap &xMap, 
+    const QwtScaleMap &yMap, const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+    Q_UNUSED( canvasRect );
+
+    // use QMargins, when we don't need to support Qt < 4.6 anymore
+    left = top = right = bottom = 0.0;
+}
+
+/*!
+   \brief Return all information, that is needed to represent
+          the item on the legend
+
+   Most items are represented by one entry on the legend
+   showing an icon and a text, but f.e. QwtPlotMultiBarChart
+   displays one entry for each bar.
+
+   QwtLegendData is basically a list of QVariants that makes it
+   possible to overload and reimplement legendData() to 
+   return almost any type of information, that is understood
+   by the receiver that acts as the legend.
+
+   The default implementation returns one entry with 
+   the title() of the item and the legendIcon().
+
+   \return Data, that is needed to represent the item on the legend
+   \sa title(), legendIcon(), QwtLegend, QwtPlotLegendItem
+ */
+QList<QwtLegendData> QwtPlotItem::legendData() const
+{
+    QwtLegendData data;
+
+    QwtText label = title();
+    label.setRenderFlags( label.renderFlags() & Qt::AlignLeft );
+            
+    QVariant titleValue;
+    qVariantSetValue( titleValue, label );
+    data.setValue( QwtLegendData::TitleRole, titleValue );
+        
+    const QwtGraphic graphic = legendIcon( 0, legendIconSize() );
+    if ( !graphic.isNull() )
+    {   
+        QVariant iconValue;
+        qVariantSetValue( iconValue, graphic );
+        data.setValue( QwtLegendData::IconRole, iconValue );
+    }   
+        
+    QList<QwtLegendData> list;
+    list += data;
+
+    return list;
+}
+
+/*!
+   \brief Update the item to changes of the axes scale division
+
+   Update the item, when the axes of plot have changed.
+   The default implementation does nothing, but items that depend
+   on the scale division (like QwtPlotGrid()) have to reimplement
+   updateScaleDiv()
+
+   updateScaleDiv() is only called when the ScaleInterest interest
+   is enabled. The default implementation does nothing.
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes(), ScaleInterest
+*/
+void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv,
+    const QwtScaleDiv &yScaleDiv )
+{
+    Q_UNUSED( xScaleDiv );
+    Q_UNUSED( yScaleDiv );
+}
+
+/*!
+   \brief Update the item to changes of the legend info
+
+   Plot items that want to display a legend ( not those, that want to
+   be displayed on a legend ! ) will have to implement updateLegend().
+
+   updateLegend() is only called when the LegendInterest interest
+   is enabled. The default implementation does nothing.
+
+   \param item Plot item to be displayed on a legend
+   \param data Attributes how to display item on the legend
+
+   \sa QwtPlotLegendItem
+
+   \note Plot items, that want to be displayed on a legend
+         need to enable the QwtPlotItem::Legend flag and to implement
+         legendData() and legendIcon()
+ */
+void QwtPlotItem::updateLegend( const QwtPlotItem *item, 
+    const QList<QwtLegendData> &data )
+{
+    Q_UNUSED( item );
+    Q_UNUSED( data );
+}
+
+/*!
+   \brief Calculate the bounding scale rectangle of 2 maps
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+
+   \return Bounding scale rect of the scale maps, not normalized
+*/
+QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap ) const
+{
+    return QRectF( xMap.s1(), yMap.s1(),
+        xMap.sDist(), yMap.sDist() );
+}
+
+/*!
+   \brief Calculate the bounding paint rectangle of 2 maps
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+
+   \return Bounding paint rectangle of the scale maps, not normalized
+*/
+QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap ) const
+{
+    const QRectF rect( xMap.p1(), yMap.p1(),
+        xMap.pDist(), yMap.pDist() );
+
+    return rect;
+}
diff --git a/qwt/qwt_plot_item.h b/qwt/qwt_plot_item.h
new file mode 100644
index 0000000..c76634e
--- /dev/null
+++ b/qwt/qwt_plot_item.h
@@ -0,0 +1,307 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ITEM_H
+#define QWT_PLOT_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include "qwt_legend_data.h"
+#include "qwt_graphic.h"
+#include <qrect.h>
+#include <qlist.h>
+#include <qmetatype.h>
+
+class QPainter;
+class QwtScaleMap;
+class QwtScaleDiv;
+class QwtPlot;
+
+/*!
+  \brief Base class for items on the plot canvas
+
+  A plot item is "something", that can be painted on the plot canvas,
+  or only affects the scales of the plot widget. They can be categorized as:
+
+  - Representator\n
+    A "Representator" is an item that represents some sort of data
+    on the plot canvas. The different representator classes are organized
+    according to the characteristics of the data:
+    - QwtPlotMarker
+      Represents a point or a horizontal/vertical coordinate
+    - QwtPlotCurve
+      Represents a series of points
+    - QwtPlotSpectrogram ( QwtPlotRasterItem )
+      Represents raster data
+    - ...
+
+  - Decorators\n
+    A "Decorator" is an item, that displays additional information, that
+    is not related to any data:
+    - QwtPlotGrid
+    - QwtPlotScaleItem
+    - QwtPlotSvgItem
+    - ...
+
+  Depending on the QwtPlotItem::ItemAttribute flags, an item is included
+  into autoscaling or has an entry on the legend.
+
+  Before misusing the existing item classes it might be better to
+  implement a new type of plot item
+  ( don't implement a watermark as spectrogram ).
+  Deriving a new type of QwtPlotItem primarily means to implement
+  the YourPlotItem::draw() method.
+
+  \sa The cpuplot example shows the implementation of additional plot items.
+*/
+
+class QWT_EXPORT QwtPlotItem
+{
+public:
+    /*!
+        \brief Runtime type information
+
+        RttiValues is used to cast plot items, without
+        having to enable runtime type information of the compiler.
+     */
+    enum RttiValues
+    {
+        //! Unspecific value, that can be used, when it doesn't matter
+        Rtti_PlotItem = 0,
+
+        //! For QwtPlotGrid
+        Rtti_PlotGrid,
+
+        //! For QwtPlotScaleItem
+        Rtti_PlotScale,
+
+        //! For QwtPlotLegendItem
+        Rtti_PlotLegend,
+
+        //! For QwtPlotMarker
+        Rtti_PlotMarker,
+
+        //! For QwtPlotCurve
+        Rtti_PlotCurve,
+
+        //! For QwtPlotSpectroCurve
+        Rtti_PlotSpectroCurve,
+
+        //! For QwtPlotIntervalCurve
+        Rtti_PlotIntervalCurve,
+
+        //! For QwtPlotHistogram
+        Rtti_PlotHistogram,
+
+        //! For QwtPlotSpectrogram
+        Rtti_PlotSpectrogram,
+
+        //! For QwtPlotSvgItem
+        Rtti_PlotSVG,
+
+        //! For QwtPlotTradingCurve
+        Rtti_PlotTradingCurve,
+
+        //! For QwtPlotBarChart
+        Rtti_PlotBarChart,
+
+        //! For QwtPlotMultiBarChart
+        Rtti_PlotMultiBarChart,
+
+        //! For QwtPlotShapeItem
+        Rtti_PlotShape,
+
+        //! For QwtPlotTextLabel
+        Rtti_PlotTextLabel,
+
+        //! For QwtPlotZoneItem
+        Rtti_PlotZone,
+
+        /*! 
+           Values >= Rtti_PlotUserItem are reserved for plot items
+           not implemented in the Qwt library.
+         */
+        Rtti_PlotUserItem = 1000
+    };
+
+    /*!
+       \brief Plot Item Attributes
+
+       Various aspects of a plot widget depend on the attributes of
+       the attached plot items. If and how a single plot item 
+       participates in these updates depends on its attributes.
+       
+       \sa setItemAttribute(), testItemAttribute(), ItemInterest
+     */
+    enum ItemAttribute
+    {
+        //! The item is represented on the legend.
+        Legend = 0x01,
+
+        /*!
+           The boundingRect() of the item is included in the
+           autoscaling calculation as long as its width or height
+           is >= 0.0.
+         */
+        AutoScale = 0x02,
+
+        /*!
+           The item needs extra space to display something outside
+           its bounding rectangle. 
+           \sa getCanvasMarginHint()
+         */
+        Margins = 0x04
+    };
+
+    //! Plot Item Attributes
+    typedef QFlags<ItemAttribute> ItemAttributes;
+
+    /*!
+       \brief Plot Item Interests
+
+       Plot items might depend on the situation of the corresponding
+       plot widget. By enabling an interest the plot item will be
+       notified, when the corresponding attribute of the plot widgets
+       has changed.
+
+       \sa setItemAttribute(), testItemAttribute(), ItemInterest
+     */
+    enum ItemInterest
+    {
+        /*! 
+           The item is interested in updates of the scales
+           \sa updateScaleDiv()
+         */
+        ScaleInterest = 0x01,
+
+        /*! 
+           The item is interested in updates of the legend ( of other items )
+           This flag is intended for items, that want to implement a legend
+           for displaying entries of other plot item.
+
+           \note If the plot item wants to be represented on a legend
+                 enable QwtPlotItem::Legend instead.
+
+           \sa updateLegend()
+         */
+        LegendInterest = 0x02
+    };
+
+    //! Plot Item Interests
+    typedef QFlags<ItemInterest> ItemInterests;
+
+    //! Render hints
+    enum RenderHint
+    {
+        //! Enable antialiasing
+        RenderAntialiased = 0x1
+    };
+
+    //! Render hints
+    typedef QFlags<RenderHint> RenderHints;
+
+    explicit QwtPlotItem( const QwtText &title = QwtText() );
+    virtual ~QwtPlotItem();
+
+    void attach( QwtPlot *plot );
+    void detach();
+
+    QwtPlot *plot() const;
+
+    void setTitle( const QString &title );
+    void setTitle( const QwtText &title );
+    const QwtText &title() const;
+
+    virtual int rtti() const;
+
+    void setItemAttribute( ItemAttribute, bool on = true );
+    bool testItemAttribute( ItemAttribute ) const;
+
+    void setItemInterest( ItemInterest, bool on = true );
+    bool testItemInterest( ItemInterest ) const;
+
+    void setRenderHint( RenderHint, bool on = true );
+    bool testRenderHint( RenderHint ) const;
+
+    void setRenderThreadCount( uint numThreads );
+    uint renderThreadCount() const;
+
+    void setLegendIconSize( const QSize & );
+    QSize legendIconSize() const;
+
+    double z() const;
+    void setZ( double z );
+
+    void show();
+    void hide();
+    virtual void setVisible( bool );
+    bool isVisible () const;
+
+    void setAxes( int xAxis, int yAxis );
+
+    void setXAxis( int axis );
+    int xAxis() const;
+
+    void setYAxis( int axis );
+    int yAxis() const;
+
+    virtual void itemChanged();
+    virtual void legendChanged();
+
+    /*!
+      \brief Draw the item
+
+      \param painter Painter
+      \param xMap Maps x-values into pixel coordinates.
+      \param yMap Maps y-values into pixel coordinates.
+      \param canvasRect Contents rect of the canvas in painter coordinates
+    */
+    virtual void draw( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const = 0;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void getCanvasMarginHint( 
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasSize,
+        double &left, double &top, double &right, double &bottom) const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv&, const QwtScaleDiv& );
+
+    virtual void updateLegend( const QwtPlotItem *,
+        const QList<QwtLegendData> & );
+
+    QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const;
+    QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const;
+
+    virtual QList<QwtLegendData> legendData() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF  & ) const;
+
+protected:
+    QwtGraphic defaultIcon( const QBrush &, const QSizeF & ) const;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtPlotItem( const QwtPlotItem & );
+    QwtPlotItem &operator=( const QwtPlotItem & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemInterests )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints )
+
+Q_DECLARE_METATYPE( QwtPlotItem * )
+
+#endif
diff --git a/qwt/qwt_plot_layout.cpp b/qwt/qwt_plot_layout.cpp
new file mode 100644
index 0000000..1c143e2
--- /dev/null
+++ b/qwt/qwt_plot_layout.cpp
@@ -0,0 +1,1444 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_layout.h"
+#include "qwt_text.h"
+#include "qwt_text_label.h"
+#include "qwt_scale_widget.h"
+#include "qwt_abstract_legend.h"
+#include <qscrollbar.h>
+#include <qmath.h>
+
+class QwtPlotLayout::LayoutData
+{
+public:
+    void init( const QwtPlot *, const QRectF &rect );
+
+    struct t_legendData
+    {
+        int frameWidth;
+        int hScrollExtent;
+        int vScrollExtent;
+        QSize hint;
+    } legend;
+
+    struct t_titleData
+    {
+        QwtText text;
+        int frameWidth;
+    } title;
+
+    struct t_footerData
+    {
+        QwtText text;
+        int frameWidth;
+    } footer;
+
+    struct t_scaleData
+    {
+        bool isEnabled;
+        const QwtScaleWidget *scaleWidget;
+        QFont scaleFont;
+        int start;
+        int end;
+        int baseLineOffset;
+        double tickOffset;
+        int dimWithoutTitle;
+    } scale[QwtPlot::axisCnt];
+
+    struct t_canvasData
+    {
+        int contentsMargins[ QwtPlot::axisCnt ];
+
+    } canvas;
+};
+
+/*
+  Extract all layout relevant data from the plot components
+*/
+void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect )
+{
+    // legend
+
+    if ( plot->legend() )
+    {
+        legend.frameWidth = plot->legend()->frameWidth();
+        legend.hScrollExtent =
+            plot->legend()->scrollExtent( Qt::Horizontal );
+        legend.vScrollExtent =
+            plot->legend()->scrollExtent( Qt::Vertical );
+
+        const QSize hint = plot->legend()->sizeHint();
+
+        int w = qMin( hint.width(), qFloor( rect.width() ) );
+        int h = plot->legend()->heightForWidth( w );
+        if ( h <= 0 )
+            h = hint.height();
+
+        if ( h > rect.height() )
+            w += legend.hScrollExtent;
+
+        legend.hint = QSize( w, h );
+    }
+
+    // title
+
+    title.frameWidth = 0;
+    title.text = QwtText();
+
+    if ( plot->titleLabel() )
+    {
+        const QwtTextLabel *label = plot->titleLabel();
+        title.text = label->text();
+        if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
+            title.text.setFont( label->font() );
+
+        title.frameWidth = plot->titleLabel()->frameWidth();
+    }
+
+    // footer
+
+    footer.frameWidth = 0;
+    footer.text = QwtText();
+
+    if ( plot->footerLabel() )
+    {
+        const QwtTextLabel *label = plot->footerLabel();
+        footer.text = label->text();
+        if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
+            footer.text.setFont( label->font() );
+
+        footer.frameWidth = plot->footerLabel()->frameWidth();
+    }
+
+    // scales
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( plot->axisEnabled( axis ) )
+        {
+            const QwtScaleWidget *scaleWidget = plot->axisWidget( axis );
+
+            scale[axis].isEnabled = true;
+
+            scale[axis].scaleWidget = scaleWidget;
+
+            scale[axis].scaleFont = scaleWidget->font();
+
+            scale[axis].start = scaleWidget->startBorderDist();
+            scale[axis].end = scaleWidget->endBorderDist();
+
+            scale[axis].baseLineOffset = scaleWidget->margin();
+            scale[axis].tickOffset = scaleWidget->margin();
+            if ( scaleWidget->scaleDraw()->hasComponent(
+                QwtAbstractScaleDraw::Ticks ) )
+            {
+                scale[axis].tickOffset +=
+                    scaleWidget->scaleDraw()->maxTickLength();
+            }
+
+            scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
+                QWIDGETSIZE_MAX, scale[axis].scaleFont );
+
+            if ( !scaleWidget->title().isEmpty() )
+            {
+                scale[axis].dimWithoutTitle -=
+                    scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX );
+            }
+        }
+        else
+        {
+            scale[axis].isEnabled = false;
+            scale[axis].start = 0;
+            scale[axis].end = 0;
+            scale[axis].baseLineOffset = 0;
+            scale[axis].tickOffset = 0.0;
+            scale[axis].dimWithoutTitle = 0;
+        }
+    }
+
+    // canvas
+
+    plot->canvas()->getContentsMargins( 
+        &canvas.contentsMargins[ QwtPlot::yLeft ], 
+        &canvas.contentsMargins[ QwtPlot::xTop ],
+        &canvas.contentsMargins[ QwtPlot::yRight ],
+        &canvas.contentsMargins[ QwtPlot::xBottom ] );
+}
+
+class QwtPlotLayout::PrivateData
+{
+public:
+    PrivateData():
+        spacing( 5 )
+    {
+    }
+
+    QRectF titleRect;
+    QRectF footerRect;
+    QRectF legendRect;
+    QRectF scaleRect[QwtPlot::axisCnt];
+    QRectF canvasRect;
+
+    QwtPlotLayout::LayoutData layoutData;
+
+    QwtPlot::LegendPosition legendPos;
+    double legendRatio;
+    unsigned int spacing;
+    unsigned int canvasMargin[QwtPlot::axisCnt];
+    bool alignCanvasToScales[QwtPlot::axisCnt];
+};
+
+/*!
+  \brief Constructor
+ */
+
+QwtPlotLayout::QwtPlotLayout()
+{
+    d_data = new PrivateData;
+
+    setLegendPosition( QwtPlot::BottomLegend );
+    setCanvasMargin( 4 );
+    setAlignCanvasToScales( false );
+
+    invalidate();
+}
+
+//! Destructor
+QwtPlotLayout::~QwtPlotLayout()
+{
+    delete d_data;
+}
+
+/*!
+  Change a margin of the canvas. The margin is the space
+  above/below the scale ticks. A negative margin will
+  be set to -1, excluding the borders of the scales.
+
+  \param margin New margin
+  \param axis One of QwtPlot::Axis. Specifies where the position of the margin.
+              -1 means margin at all borders.
+  \sa canvasMargin()
+
+  \warning The margin will have no effect when alignCanvasToScale() is true
+*/
+
+void QwtPlotLayout::setCanvasMargin( int margin, int axis )
+{
+    if ( margin < -1 )
+        margin = -1;
+
+    if ( axis == -1 )
+    {
+        for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            d_data->canvasMargin[axis] = margin;
+    }
+    else if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->canvasMargin[axis] = margin;
+}
+
+/*!
+    \param axisId Axis index
+    \return Margin around the scale tick borders
+    \sa setCanvasMargin()
+*/
+int QwtPlotLayout::canvasMargin( int axisId ) const
+{
+    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
+        return 0;
+
+    return d_data->canvasMargin[axisId];
+}
+
+/*!
+  \brief Set the align-canvas-to-axis-scales flag for all axes
+
+  \param on True/False
+  \sa setAlignCanvasToScale(), alignCanvasToScale()
+*/
+void QwtPlotLayout::setAlignCanvasToScales( bool on )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        d_data->alignCanvasToScales[axis] = on;
+}
+
+/*!
+  Change the align-canvas-to-axis-scales setting. The canvas may:
+
+  - extend beyond the axis scale ends to maximize its size,
+  - align with the axis scale ends to control its size.
+
+  The axisId parameter is somehow confusing as it identifies a border
+  of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft
+  is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom )
+  is aligned.
+
+  \param axisId Axis index
+  \param on New align-canvas-to-axis-scales setting
+
+  \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales()
+  \warning In case of on == true canvasMargin() will have no effect
+*/
+void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on )
+{
+    if ( axisId >= 0 && axisId < QwtPlot::axisCnt )
+        d_data->alignCanvasToScales[axisId] = on;
+}
+
+/*!
+  Return the align-canvas-to-axis-scales setting. The canvas may:
+  - extend beyond the axis scale ends to maximize its size
+  - align with the axis scale ends to control its size.
+
+  \param axisId Axis index
+  \return align-canvas-to-axis-scales setting
+  \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin()
+*/
+bool QwtPlotLayout::alignCanvasToScale( int axisId ) const
+{
+    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
+        return false;
+
+    return d_data->alignCanvasToScales[ axisId ];
+}
+
+/*!
+  Change the spacing of the plot. The spacing is the distance
+  between the plot components.
+
+  \param spacing New spacing
+  \sa setCanvasMargin(), spacing()
+*/
+void QwtPlotLayout::setSpacing( int spacing )
+{
+    d_data->spacing = qMax( 0, spacing );
+}
+
+/*!
+  \return Spacing
+  \sa margin(), setSpacing()
+*/
+int QwtPlotLayout::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Specify the position of the legend
+  \param pos The legend's position.
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, footer, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+
+  \sa QwtPlot::setLegendPosition()
+*/
+
+void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio )
+{
+    if ( ratio > 1.0 )
+        ratio = 1.0;
+
+    switch ( pos )
+    {
+        case QwtPlot::TopLegend:
+        case QwtPlot::BottomLegend:
+            if ( ratio <= 0.0 )
+                ratio = 0.33;
+            d_data->legendRatio = ratio;
+            d_data->legendPos = pos;
+            break;
+        case QwtPlot::LeftLegend:
+        case QwtPlot::RightLegend:
+            if ( ratio <= 0.0 )
+                ratio = 0.5;
+            d_data->legendRatio = ratio;
+            d_data->legendPos = pos;
+            break;
+        default:
+            break;
+    }
+}
+
+/*!
+  \brief Specify the position of the legend
+  \param pos The legend's position. Valid values are
+      \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend,
+      \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend.
+
+  \sa QwtPlot::setLegendPosition()
+*/
+void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos )
+{
+    setLegendPosition( pos, 0.0 );
+}
+
+/*!
+  \return Position of the legend
+  \sa setLegendPosition(), QwtPlot::setLegendPosition(),
+      QwtPlot::legendPosition()
+*/
+QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
+{
+    return d_data->legendPos;
+}
+
+/*!
+  Specify the relative size of the legend in the plot
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, footer, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+*/
+void QwtPlotLayout::setLegendRatio( double ratio )
+{
+    setLegendPosition( legendPosition(), ratio );
+}
+
+/*!
+  \return The relative size of the legend in the plot.
+  \sa setLegendPosition()
+*/
+double QwtPlotLayout::legendRatio() const
+{
+    return d_data->legendRatio;
+}
+
+/*!
+  \brief Set the geometry for the title
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa titleRect(), activate()
+ */
+void QwtPlotLayout::setTitleRect( const QRectF &rect )
+{
+    d_data->titleRect = rect;
+}
+
+/*!
+  \return Geometry for the title
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::titleRect() const
+{
+    return d_data->titleRect;
+}
+
+/*!
+  \brief Set the geometry for the footer
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa footerRect(), activate()
+ */
+void QwtPlotLayout::setFooterRect( const QRectF &rect )
+{
+    d_data->footerRect = rect;
+}
+
+/*!
+  \return Geometry for the footer
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::footerRect() const
+{
+    return d_data->footerRect;
+}
+
+/*!
+  \brief Set the geometry for the legend
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \param rect Rectangle for the legend
+
+  \sa legendRect(), activate()
+ */
+void QwtPlotLayout::setLegendRect( const QRectF &rect )
+{
+    d_data->legendRect = rect;
+}
+
+/*!
+  \return Geometry for the legend
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::legendRect() const
+{
+    return d_data->legendRect;
+}
+
+/*!
+  \brief Set the geometry for an axis
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \param axis Axis index
+  \param rect Rectangle for the scale
+
+  \sa scaleRect(), activate()
+ */
+void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->scaleRect[axis] = rect;
+}
+
+/*!
+  \param axis Axis index
+  \return Geometry for the scale
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::scaleRect( int axis ) const
+{
+    if ( axis < 0 || axis >= QwtPlot::axisCnt )
+    {
+        static QRectF dummyRect;
+        return dummyRect;
+    }
+    return d_data->scaleRect[axis];
+}
+
+/*!
+  \brief Set the geometry for the canvas
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa canvasRect(), activate()
+ */
+void QwtPlotLayout::setCanvasRect( const QRectF &rect )
+{
+    d_data->canvasRect = rect;
+}
+
+/*!
+  \return Geometry for the canvas
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::canvasRect() const
+{
+    return d_data->canvasRect;
+}
+
+/*!
+  Invalidate the geometry of all components.
+  \sa activate()
+*/
+void QwtPlotLayout::invalidate()
+{
+    d_data->titleRect = d_data->footerRect
+        = d_data->legendRect = d_data->canvasRect = QRect();
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        d_data->scaleRect[axis] = QRect();
+}
+
+/*!
+  \return Minimum size hint
+  \param plot Plot widget
+
+  \sa QwtPlot::minimumSizeHint()
+*/
+
+QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const
+{
+    class ScaleData
+    {
+    public:
+        ScaleData()
+        {
+            w = h = minLeft = minRight = tickOffset = 0;
+        }
+
+        int w;
+        int h;
+        int minLeft;
+        int minRight;
+        int tickOffset;
+    } scaleData[QwtPlot::axisCnt];
+
+    int canvasBorder[QwtPlot::axisCnt];
+
+    int fw;
+    plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL );
+
+    int axis;
+    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( plot->axisEnabled( axis ) )
+        {
+            const QwtScaleWidget *scl = plot->axisWidget( axis );
+            ScaleData &sd = scaleData[axis];
+
+            const QSize hint = scl->minimumSizeHint();
+            sd.w = hint.width();
+            sd.h = hint.height();
+            scl->getBorderDistHint( sd.minLeft, sd.minRight );
+            sd.tickOffset = scl->margin();
+            if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) )
+                sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() );
+        }
+
+        canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1;
+    }
+
+
+    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        ScaleData &sd = scaleData[axis];
+        if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) )
+        {
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] )
+                && scaleData[QwtPlot::yLeft].w )
+            {
+                int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
+                if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
+                    shiftLeft = scaleData[QwtPlot::yLeft].w;
+
+                sd.w -= shiftLeft;
+            }
+            if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] )
+                && scaleData[QwtPlot::yRight].w )
+            {
+                int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
+                if ( shiftRight > scaleData[QwtPlot::yRight].w )
+                    shiftRight = scaleData[QwtPlot::yRight].w;
+
+                sd.w -= shiftRight;
+            }
+        }
+
+        if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) )
+        {
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) &&
+                scaleData[QwtPlot::xBottom].h )
+            {
+                int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
+                if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
+                    shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
+
+                sd.h -= shiftBottom;
+            }
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) &&
+                scaleData[QwtPlot::xTop].h )
+            {
+                int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
+                if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
+                    shiftTop = scaleData[QwtPlot::xTop].tickOffset;
+
+                sd.h -= shiftTop;
+            }
+        }
+    }
+
+    const QWidget *canvas = plot->canvas();
+
+    int left, top, right, bottom;
+    canvas->getContentsMargins( &left, &top, &right, &bottom );
+
+    const QSize minCanvasSize = canvas->minimumSize();
+
+    int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
+    int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w )
+        + left + 1 + right + 1;
+    w += qMax( cw, minCanvasSize.width() );
+
+    int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
+    int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h )
+        + top + 1 + bottom + 1;
+    h += qMax( ch, minCanvasSize.height() );
+
+    const QwtTextLabel *labels[2];
+    labels[0] = plot->titleLabel();
+    labels[1] = plot->footerLabel();
+
+    for ( int i = 0; i < 2; i++ )
+    {
+        const QwtTextLabel *label   = labels[i];
+        if ( label && !label->text().isEmpty() )
+        {
+            // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
+            // we center on the plot canvas.
+            const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft )
+                && plot->axisEnabled( QwtPlot::yRight ) );
+
+            int labelW = w;
+            if ( centerOnCanvas )
+            {
+                labelW -= scaleData[QwtPlot::yLeft].w
+                    + scaleData[QwtPlot::yRight].w;
+            }
+
+            int labelH = label->heightForWidth( labelW );
+            if ( labelH > labelW ) // Compensate for a long title
+            {
+                w = labelW = labelH;
+                if ( centerOnCanvas )
+                {
+                    w += scaleData[QwtPlot::yLeft].w
+                        + scaleData[QwtPlot::yRight].w;
+                }
+
+                labelH = label->heightForWidth( labelW );
+            }
+            h += labelH + d_data->spacing;
+        }
+    }
+
+    // Compute the legend contribution
+
+    const QwtAbstractLegend *legend = plot->legend();
+    if ( legend && !legend->isEmpty() )
+    {
+        if ( d_data->legendPos == QwtPlot::LeftLegend
+            || d_data->legendPos == QwtPlot::RightLegend )
+        {
+            int legendW = legend->sizeHint().width();
+            int legendH = legend->heightForWidth( legendW );
+
+            if ( legend->frameWidth() > 0 )
+                w += d_data->spacing;
+
+            if ( legendH > h )
+                legendW += legend->scrollExtent( Qt::Horizontal );
+
+            if ( d_data->legendRatio < 1.0 )
+                legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) );
+
+            w += legendW + d_data->spacing;
+        }
+        else // QwtPlot::Top, QwtPlot::Bottom
+        {
+            int legendW = qMin( legend->sizeHint().width(), w );
+            int legendH = legend->heightForWidth( legendW );
+
+            if ( legend->frameWidth() > 0 )
+                h += d_data->spacing;
+
+            if ( d_data->legendRatio < 1.0 )
+                legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) );
+
+            h += legendH + d_data->spacing;
+        }
+    }
+
+    return QSize( w, h );
+}
+
+/*!
+  Find the geometry for the legend
+
+  \param options Options how to layout the legend
+  \param rect Rectangle where to place the legend
+
+  \return Geometry for the legend
+  \sa Options
+*/
+
+QRectF QwtPlotLayout::layoutLegend( Options options,
+    const QRectF &rect ) const
+{
+    const QSize hint( d_data->layoutData.legend.hint );
+
+    int dim;
+    if ( d_data->legendPos == QwtPlot::LeftLegend
+        || d_data->legendPos == QwtPlot::RightLegend )
+    {
+        // We don't allow vertical legends to take more than
+        // half of the available space.
+
+        dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) );
+
+        if ( !( options & IgnoreScrollbars ) )
+        {
+            if ( hint.height() > rect.height() )
+            {
+                // The legend will need additional
+                // space for the vertical scrollbar.
+
+                dim += d_data->layoutData.legend.hScrollExtent;
+            }
+        }
+    }
+    else
+    {
+        dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) );
+        dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
+    }
+
+    QRectF legendRect = rect;
+    switch ( d_data->legendPos )
+    {
+        case QwtPlot::LeftLegend:
+            legendRect.setWidth( dim );
+            break;
+        case QwtPlot::RightLegend:
+            legendRect.setX( rect.right() - dim );
+            legendRect.setWidth( dim );
+            break;
+        case QwtPlot::TopLegend:
+            legendRect.setHeight( dim );
+            break;
+        case QwtPlot::BottomLegend:
+            legendRect.setY( rect.bottom() - dim );
+            legendRect.setHeight( dim );
+            break;
+    }
+
+    return legendRect;
+}
+
+/*!
+  Align the legend to the canvas
+
+  \param canvasRect Geometry of the canvas
+  \param legendRect Maximum geometry for the legend
+
+  \return Geometry for the aligned legend
+*/
+QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect,
+    const QRectF &legendRect ) const
+{
+    QRectF alignedRect = legendRect;
+
+    if ( d_data->legendPos == QwtPlot::BottomLegend
+        || d_data->legendPos == QwtPlot::TopLegend )
+    {
+        if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
+        {
+            alignedRect.setX( canvasRect.x() );
+            alignedRect.setWidth( canvasRect.width() );
+        }
+    }
+    else
+    {
+        if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
+        {
+            alignedRect.setY( canvasRect.y() );
+            alignedRect.setHeight( canvasRect.height() );
+        }
+    }
+
+    return alignedRect;
+}
+
+/*!
+  Expand all line breaks in text labels, and calculate the height
+  of their widgets in orientation of the text.
+
+  \param options Options how to layout the legend
+  \param rect Bounding rectangle for title, footer, axes and canvas.
+  \param dimTitle Expanded height of the title widget
+  \param dimFooter Expanded height of the footer widget
+  \param dimAxis Expanded heights of the axis in axis orientation.
+
+  \sa Options
+*/
+void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect,
+    int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const
+{
+    dimTitle = dimFooter = 0;
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        dimAxis[axis] = 0;
+
+    int backboneOffset[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        backboneOffset[axis] = 0;
+        if ( !( options & IgnoreFrames ) )
+            backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ];
+
+        if ( !d_data->alignCanvasToScales[axis] )
+            backboneOffset[axis] += d_data->canvasMargin[axis];
+    }
+
+    bool done = false;
+    while ( !done )
+    {
+        done = true;
+
+        // the size for the 4 axis depend on each other. Expanding
+        // the height of a horizontal axis will shrink the height
+        // for the vertical axis, shrinking the height of a vertical
+        // axis will result in a line break what will expand the
+        // width and results in shrinking the width of a horizontal
+        // axis what might result in a line break of a horizontal
+        // axis ... . So we loop as long until no size changes.
+
+        if ( !( ( options & IgnoreTitle ) ||
+            d_data->layoutData.title.text.isEmpty() ) )
+        {
+            double w = rect.width();
+
+            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
+                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+            {
+                // center to the canvas
+                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
+            }
+
+            int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) );
+            if ( !( options & IgnoreFrames ) )
+                d += 2 * d_data->layoutData.title.frameWidth;
+
+            if ( d > dimTitle )
+            {
+                dimTitle = d;
+                done = false;
+            }
+        }
+
+        if ( !( ( options & IgnoreFooter ) ||
+            d_data->layoutData.footer.text.isEmpty() ) )
+        {
+            double w = rect.width();
+
+            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
+                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+            {
+                // center to the canvas
+                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
+            }
+
+            int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) );
+            if ( !( options & IgnoreFrames ) )
+                d += 2 * d_data->layoutData.footer.frameWidth;
+
+            if ( d > dimFooter )
+            {
+                dimFooter = d;
+                done = false;
+            }
+        }
+
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        {
+            const struct LayoutData::t_scaleData &scaleData =
+                d_data->layoutData.scale[axis];
+
+            if ( scaleData.isEnabled )
+            {
+                double length;
+                if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
+                {
+                    length = rect.width() - dimAxis[QwtPlot::yLeft]
+                        - dimAxis[QwtPlot::yRight];
+                    length -= scaleData.start + scaleData.end;
+
+                    if ( dimAxis[QwtPlot::yRight] > 0 )
+                        length -= 1;
+
+                    length += qMin( dimAxis[QwtPlot::yLeft],
+                        scaleData.start - backboneOffset[QwtPlot::yLeft] );
+                    length += qMin( dimAxis[QwtPlot::yRight],
+                        scaleData.end - backboneOffset[QwtPlot::yRight] );
+                }
+                else // QwtPlot::yLeft, QwtPlot::yRight
+                {
+                    length = rect.height() - dimAxis[QwtPlot::xTop]
+                        - dimAxis[QwtPlot::xBottom];
+                    length -= scaleData.start + scaleData.end;
+                    length -= 1;
+
+                    if ( dimAxis[QwtPlot::xBottom] <= 0 )
+                        length -= 1;
+                    if ( dimAxis[QwtPlot::xTop] <= 0 )
+                        length -= 1;
+
+                    if ( dimAxis[QwtPlot::xBottom] > 0 )
+                    {
+                        length += qMin(
+                            d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
+                            double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) );
+                    }
+                    if ( dimAxis[QwtPlot::xTop] > 0 )
+                    {
+                        length += qMin(
+                            d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
+                            double( scaleData.end - backboneOffset[QwtPlot::xTop] ) );
+                    }
+
+                    if ( dimTitle > 0 )
+                        length -= dimTitle + d_data->spacing;
+                }
+
+                int d = scaleData.dimWithoutTitle;
+                if ( !scaleData.scaleWidget->title().isEmpty() )
+                {
+                    d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) );
+                }
+
+
+                if ( d > dimAxis[axis] )
+                {
+                    dimAxis[axis] = d;
+                    done = false;
+                }
+            }
+        }
+    }
+}
+
+/*!
+  Align the ticks of the axis to the canvas borders using
+  the empty corners.
+
+  \param options Layout options
+  \param canvasRect Geometry of the canvas ( IN/OUT )
+  \param scaleRect Geometries of the scales ( IN/OUT )
+
+  \sa Options
+*/
+
+void QwtPlotLayout::alignScales( Options options,
+    QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const
+{
+    int backboneOffset[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        backboneOffset[axis] = 0;
+
+        if ( !d_data->alignCanvasToScales[axis] )
+        {
+            backboneOffset[axis] += d_data->canvasMargin[axis];
+        }
+
+        if ( !( options & IgnoreFrames ) )
+        {
+            backboneOffset[axis] += 
+                d_data->layoutData.canvas.contentsMargins[axis];
+        }
+    }
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( !scaleRect[axis].isValid() )
+            continue;
+
+        const int startDist = d_data->layoutData.scale[axis].start;
+        const int endDist = d_data->layoutData.scale[axis].end;
+
+        QRectF &axisRect = scaleRect[axis];
+
+        if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
+        {
+            const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft];
+            const int leftOffset =
+                backboneOffset[QwtPlot::yLeft] - startDist;
+
+            if ( leftScaleRect.isValid() )
+            {
+                const double dx = leftOffset + leftScaleRect.width();
+                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 )
+                {
+                    /*
+                      The axis needs more space than the width
+                      of the left scale.
+                     */
+                    const double cLeft = canvasRect.left(); // qreal -> double
+                    canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) );
+                }
+                else
+                {
+                    const double minLeft = leftScaleRect.left();
+                    const double left = axisRect.left() + leftOffset;
+                    axisRect.setLeft( qMax( left, minLeft ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 )
+                {
+                    canvasRect.setLeft( qMax( canvasRect.left(),
+                        axisRect.left() - leftOffset ) );
+                }
+                else
+                {
+                    if ( leftOffset > 0 )
+                        axisRect.setLeft( axisRect.left() + leftOffset );
+                }
+            }
+
+            const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight];
+            const int rightOffset =
+                backboneOffset[QwtPlot::yRight] - endDist + 1;
+
+            if ( rightScaleRect.isValid() )
+            {
+                const double dx = rightOffset + rightScaleRect.width();
+                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 )
+                {
+                    /*
+                      The axis needs more space than the width
+                      of the right scale.
+                     */
+                    const double cRight = canvasRect.right(); // qreal -> double
+                    canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) );
+                }   
+
+                const double maxRight = rightScaleRect.right();
+                const double right = axisRect.right() - rightOffset;
+                axisRect.setRight( qMin( right, maxRight ) );
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 )
+                {
+                    canvasRect.setRight( qMin( canvasRect.right(),
+                        axisRect.right() + rightOffset ) );
+                }
+                else
+                {
+                    if ( rightOffset > 0 )
+                        axisRect.setRight( axisRect.right() - rightOffset );
+                }
+            }
+        }
+        else // QwtPlot::yLeft, QwtPlot::yRight
+        {
+            const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom];
+            const int bottomOffset =
+                backboneOffset[QwtPlot::xBottom] - endDist + 1;
+
+            if ( bottomScaleRect.isValid() )
+            {
+                const double dy = bottomOffset + bottomScaleRect.height();
+                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 )
+                {
+                    /*
+                      The axis needs more space than the height
+                      of the bottom scale.
+                     */
+                    const double cBottom = canvasRect.bottom(); // qreal -> double
+                    canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) );
+                }
+                else
+                {
+                    const double maxBottom = bottomScaleRect.top() +
+                        d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
+                    const double bottom = axisRect.bottom() - bottomOffset;
+                    axisRect.setBottom( qMin( bottom, maxBottom ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 )
+                {
+                    canvasRect.setBottom( qMin( canvasRect.bottom(),
+                        axisRect.bottom() + bottomOffset ) );
+                }
+                else
+                {
+                    if ( bottomOffset > 0 )
+                        axisRect.setBottom( axisRect.bottom() - bottomOffset );
+                }
+            }
+
+            const QRectF &topScaleRect = scaleRect[QwtPlot::xTop];
+            const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
+
+            if ( topScaleRect.isValid() )
+            {
+                const double dy = topOffset + topScaleRect.height();
+                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 )
+                {
+                    /*
+                      The axis needs more space than the height
+                      of the top scale.
+                     */
+                    const double cTop = canvasRect.top(); // qreal -> double
+                    canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) );
+                }
+                else
+                {
+                    const double minTop = topScaleRect.bottom() -
+                        d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
+                    const double top = axisRect.top() + topOffset;
+                    axisRect.setTop( qMax( top, minTop ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 )
+                {
+                    canvasRect.setTop( qMax( canvasRect.top(),
+                        axisRect.top() - topOffset ) );
+                }
+                else
+                {
+                    if ( topOffset > 0 )
+                        axisRect.setTop( axisRect.top() + topOffset );
+                }
+            }
+        }
+    }
+
+    /*
+      The canvas has been aligned to the scale with largest
+      border distances. Now we have to realign the other scale.
+     */
+
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        QRectF &sRect = scaleRect[axis];
+
+        if ( !sRect.isValid() )
+            continue;
+
+        if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+        {
+            if ( d_data->alignCanvasToScales[QwtPlot::yLeft] )
+            {
+                double y = canvasRect.left() - d_data->layoutData.scale[axis].start;
+                if ( !( options & IgnoreFrames ) )
+                    y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ];
+
+                sRect.setLeft( y );
+            }
+            if ( d_data->alignCanvasToScales[QwtPlot::yRight] )
+            {
+                double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end;
+                if ( !( options & IgnoreFrames ) )
+                    y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ];
+
+                sRect.setRight( y );
+            }
+
+            if ( d_data->alignCanvasToScales[ axis ] )
+            {
+                if ( axis == QwtPlot::xTop )
+                    sRect.setBottom( canvasRect.top() );
+                else
+                    sRect.setTop( canvasRect.bottom() );
+            }
+        }
+        else
+        {
+            if ( d_data->alignCanvasToScales[QwtPlot::xTop] )
+            {
+                double x = canvasRect.top() - d_data->layoutData.scale[axis].start;
+                if ( !( options & IgnoreFrames ) )
+                    x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ];
+
+                sRect.setTop( x );
+            }
+            if ( d_data->alignCanvasToScales[QwtPlot::xBottom] )
+            {
+                double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end;
+                if ( !( options & IgnoreFrames ) )
+                    x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ];
+
+                sRect.setBottom( x );
+            }
+
+            if ( d_data->alignCanvasToScales[ axis ] )
+            {
+                if ( axis == QwtPlot::yLeft )
+                    sRect.setRight( canvasRect.left() );
+                else
+                    sRect.setLeft( canvasRect.right() );
+            }
+        }
+    }
+}
+
+/*!
+  \brief Recalculate the geometry of all components.
+
+  \param plot Plot to be layout
+  \param plotRect Rectangle where to place the components
+  \param options Layout options
+
+  \sa invalidate(), titleRect(), footerRect()
+      legendRect(), scaleRect(), canvasRect()
+*/
+void QwtPlotLayout::activate( const QwtPlot *plot,
+    const QRectF &plotRect, Options options )
+{
+    invalidate();
+
+    QRectF rect( plotRect );  // undistributed rest of the plot rect
+
+    // We extract all layout relevant parameters from the widgets,
+    // and save them to d_data->layoutData.
+
+    d_data->layoutData.init( plot, rect );
+
+    if ( !( options & IgnoreLegend )
+        && plot->legend() && !plot->legend()->isEmpty() )
+    {
+        d_data->legendRect = layoutLegend( options, rect );
+
+        // subtract d_data->legendRect from rect
+
+        const QRegion region( rect.toRect() );
+        rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect();
+
+        switch ( d_data->legendPos )
+        {
+            case QwtPlot::LeftLegend:
+                rect.setLeft( rect.left() + d_data->spacing );
+                break;
+            case QwtPlot::RightLegend:
+                rect.setRight( rect.right() - d_data->spacing );
+                break;
+            case QwtPlot::TopLegend:
+                rect.setTop( rect.top() + d_data->spacing );
+                break;
+            case QwtPlot::BottomLegend:
+                rect.setBottom( rect.bottom() - d_data->spacing );
+                break;
+        }
+    }
+
+    /*
+     +---+-----------+---+
+     |       Title       |
+     +---+-----------+---+
+     |   |   Axis    |   |
+     +---+-----------+---+
+     | A |           | A |
+     | x |  Canvas   | x |
+     | i |           | i |
+     | s |           | s |
+     +---+-----------+---+
+     |   |   Axis    |   |
+     +---+-----------+---+
+     |      Footer       |
+     +---+-----------+---+
+    */
+
+    // title, footer and axes include text labels. The height of each
+    // label depends on its line breaks, that depend on the width
+    // for the label. A line break in a horizontal text will reduce
+    // the available width for vertical texts and vice versa.
+    // expandLineBreaks finds the height/width for title, footer and axes
+    // including all line breaks.
+
+    int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt];
+    expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes );
+
+    if ( dimTitle > 0 )
+    {
+        d_data->titleRect.setRect(
+            rect.left(), rect.top(), rect.width(), dimTitle );
+
+        rect.setTop( d_data->titleRect.bottom() + d_data->spacing );
+
+        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
+            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+        {
+            // if only one of the y axes is missing we align
+            // the title centered to the canvas
+
+            d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
+            d_data->titleRect.setWidth( rect.width()
+                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
+        }
+    }
+
+    if ( dimFooter > 0 )
+    {
+        d_data->footerRect.setRect(
+            rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter );
+
+        rect.setBottom( d_data->footerRect.top() - d_data->spacing );
+
+        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
+            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+        {
+            // if only one of the y axes is missing we align
+            // the footer centered to the canvas
+
+            d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
+            d_data->footerRect.setWidth( rect.width()
+                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
+        }
+    }
+
+    d_data->canvasRect.setRect(
+        rect.x() + dimAxes[QwtPlot::yLeft],
+        rect.y() + dimAxes[QwtPlot::xTop],
+        rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
+        rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        // set the rects for the axes
+
+        if ( dimAxes[axis] )
+        {
+            int dim = dimAxes[axis];
+            QRectF &scaleRect = d_data->scaleRect[axis];
+
+            scaleRect = d_data->canvasRect;
+            switch ( axis )
+            {
+                case QwtPlot::yLeft:
+                    scaleRect.setX( d_data->canvasRect.left() - dim );
+                    scaleRect.setWidth( dim );
+                    break;
+                case QwtPlot::yRight:
+                    scaleRect.setX( d_data->canvasRect.right() );
+                    scaleRect.setWidth( dim );
+                    break;
+                case QwtPlot::xBottom:
+                    scaleRect.setY( d_data->canvasRect.bottom() );
+                    scaleRect.setHeight( dim );
+                    break;
+                case QwtPlot::xTop:
+                    scaleRect.setY( d_data->canvasRect.top() - dim );
+                    scaleRect.setHeight( dim );
+                    break;
+            }
+            scaleRect = scaleRect.normalized();
+        }
+    }
+
+    // +---+-----------+---+
+    // |  <-   Axis   ->   |
+    // +-^-+-----------+-^-+
+    // | | |           | | |
+    // |   |           |   |
+    // | A |           | A |
+    // | x |  Canvas   | x |
+    // | i |           | i |
+    // | s |           | s |
+    // |   |           |   |
+    // | | |           | | |
+    // +-V-+-----------+-V-+
+    // |   <-  Axis   ->   |
+    // +---+-----------+---+
+
+    // The ticks of the axes - not the labels above - should
+    // be aligned to the canvas. So we try to use the empty
+    // corners to extend the axes, so that the label texts
+    // left/right of the min/max ticks are moved into them.
+
+    alignScales( options, d_data->canvasRect, d_data->scaleRect );
+
+    if ( !d_data->legendRect.isEmpty() )
+    {
+        // We prefer to align the legend to the canvas - not to
+        // the complete plot - if possible.
+
+        d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect );
+    }
+}
diff --git a/qwt/qwt_plot_layout.h b/qwt/qwt_plot_layout.h
new file mode 100644
index 0000000..c72c04f
--- /dev/null
+++ b/qwt/qwt_plot_layout.h
@@ -0,0 +1,122 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_LAYOUT_H
+#define QWT_PLOT_LAYOUT_H
+
+#include "qwt_global.h"
+#include "qwt_plot.h"
+
+/*!
+  \brief Layout engine for QwtPlot.
+
+  It is used by the QwtPlot widget to organize its internal widgets
+  or by QwtPlot::print() to render its content to a QPaintDevice like
+  a QPrinter, QPixmap/QImage or QSvgRenderer.
+
+  \sa QwtPlot::setPlotLayout()
+*/
+
+class QWT_EXPORT QwtPlotLayout
+{
+public:
+    /*!
+      Options to configure the plot layout engine
+      \sa activate(), QwtPlotRenderer
+     */
+    enum Option
+    {
+        //! Unused
+        AlignScales = 0x01,
+
+        /*!
+          Ignore the dimension of the scrollbars. There are no
+          scrollbars, when the plot is not rendered to widgets.
+         */
+        IgnoreScrollbars = 0x02,
+
+        //! Ignore all frames. 
+        IgnoreFrames = 0x04,
+
+        //! Ignore the legend.
+        IgnoreLegend = 0x08,
+
+        //! Ignore the title.
+        IgnoreTitle = 0x10,
+
+        //! Ignore the footer.
+        IgnoreFooter = 0x20
+    };
+
+    //! Layout options
+    typedef QFlags<Option> Options;
+
+    explicit QwtPlotLayout();
+    virtual ~QwtPlotLayout();
+
+    void setCanvasMargin( int margin, int axis = -1 );
+    int canvasMargin( int axis ) const;
+
+    void setAlignCanvasToScales( bool );
+
+    void setAlignCanvasToScale( int axisId, bool );
+    bool alignCanvasToScale( int axisId ) const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setLegendPosition( QwtPlot::LegendPosition pos, double ratio );
+    void setLegendPosition( QwtPlot::LegendPosition pos );
+    QwtPlot::LegendPosition legendPosition() const;
+
+    void setLegendRatio( double ratio );
+    double legendRatio() const;
+
+    virtual QSize minimumSizeHint( const QwtPlot * ) const;
+
+    virtual void activate( const QwtPlot *,
+        const QRectF &rect, Options options = 0x00 );
+
+    virtual void invalidate();
+
+    QRectF titleRect() const;
+    QRectF footerRect() const;
+    QRectF legendRect() const;
+    QRectF scaleRect( int axis ) const;
+    QRectF canvasRect() const;
+
+    class LayoutData;
+
+protected:
+
+    void setTitleRect( const QRectF & );
+    void setFooterRect( const QRectF & );
+    void setLegendRect( const QRectF & );
+    void setScaleRect( int axis, const QRectF & );
+    void setCanvasRect( const QRectF & );
+
+    QRectF layoutLegend( Options options, const QRectF & ) const;
+    QRectF alignLegend( const QRectF &canvasRect,
+        const QRectF &legendRect ) const;
+
+    void expandLineBreaks( Options options, const QRectF &rect,
+        int &dimTitle, int &dimFooter, int dimAxes[QwtPlot::axisCnt] ) const;
+
+    void alignScales( Options options, QRectF &canvasRect,
+        QRectF scaleRect[QwtPlot::axisCnt] ) const;
+
+private:
+    class PrivateData;
+
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotLayout::Options )
+
+#endif
diff --git a/qwt/qwt_plot_legenditem.cpp b/qwt/qwt_plot_legenditem.cpp
new file mode 100644
index 0000000..5bb8e4c
--- /dev/null
+++ b/qwt/qwt_plot_legenditem.cpp
@@ -0,0 +1,865 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_legenditem.h"
+#include "qwt_dyngrid_layout.h"
+#include "qwt_scale_map.h"
+#include "qwt_painter.h"
+#include <qlayoutitem.h>
+#include <qpen.h>
+#include <qbrush.h>
+#include <qpainter.h>
+#include <qmath.h>
+
+class QwtLegendLayoutItem: public QLayoutItem
+{
+public:
+    QwtLegendLayoutItem( const QwtPlotLegendItem *, const QwtPlotItem * );
+    virtual ~QwtLegendLayoutItem();
+
+    const QwtPlotItem *plotItem() const;
+
+    void setData( const QwtLegendData & );
+    const QwtLegendData &data() const;
+
+    virtual Qt::Orientations expandingDirections() const;
+    virtual QRect geometry() const;
+    virtual bool hasHeightForWidth() const;
+    virtual int heightForWidth( int w ) const;
+    virtual bool isEmpty() const;
+    virtual QSize maximumSize() const;
+    virtual int minimumHeightForWidth( int w ) const;
+    virtual QSize minimumSize() const;
+    virtual void setGeometry( const QRect & r );
+    virtual QSize sizeHint() const;
+
+private:
+
+    const QwtPlotLegendItem *d_legendItem;
+    const QwtPlotItem *d_plotItem;
+    QwtLegendData d_data;
+
+    QRect d_rect;
+};
+
+QwtLegendLayoutItem::QwtLegendLayoutItem( 
+        const QwtPlotLegendItem *legendItem, const QwtPlotItem *plotItem ):
+    d_legendItem( legendItem ),
+    d_plotItem( plotItem)
+{
+}
+
+QwtLegendLayoutItem::~QwtLegendLayoutItem()
+{
+}
+
+const QwtPlotItem *QwtLegendLayoutItem::plotItem() const
+{
+    return d_plotItem;
+}
+
+void QwtLegendLayoutItem::setData( const QwtLegendData &data )
+{
+    d_data = data;
+}
+
+const QwtLegendData &QwtLegendLayoutItem::data() const
+{
+    return d_data;
+}
+
+Qt::Orientations QwtLegendLayoutItem::expandingDirections() const
+{
+    return Qt::Horizontal;
+}
+
+bool QwtLegendLayoutItem::hasHeightForWidth() const
+{
+    return !d_data.title().isEmpty();
+}
+
+int QwtLegendLayoutItem::minimumHeightForWidth( int w ) const
+{
+    return d_legendItem->heightForWidth( d_data, w );
+}
+
+int QwtLegendLayoutItem::heightForWidth( int w ) const
+{
+    return d_legendItem->heightForWidth( d_data, w );
+}
+
+bool QwtLegendLayoutItem::isEmpty() const
+{
+    return false;
+}
+
+QSize QwtLegendLayoutItem::maximumSize() const
+{
+    return QSize( QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX );
+}
+
+QSize QwtLegendLayoutItem::minimumSize() const
+{
+    return d_legendItem->minimumSize( d_data );
+}
+
+QSize QwtLegendLayoutItem::sizeHint() const
+{
+    return minimumSize();
+}
+
+void QwtLegendLayoutItem::setGeometry( const QRect &rect )
+{
+    d_rect = rect;
+}
+
+QRect QwtLegendLayoutItem::geometry() const
+{
+    return d_rect;
+}
+
+class QwtPlotLegendItem::PrivateData
+{
+public:
+    PrivateData():
+        itemMargin( 4 ),
+        itemSpacing( 4 ),
+        borderRadius( 0.0 ),
+        borderPen( Qt::NoPen ),
+        backgroundBrush( Qt::NoBrush ),
+        backgroundMode( QwtPlotLegendItem::LegendBackground ),
+        borderDistance( 10 ),
+        alignment( Qt::AlignRight | Qt::AlignBottom )
+    {
+        layout = new QwtDynGridLayout();
+        layout->setMaxColumns( 2 );
+
+        layout->setSpacing( 0 );
+        layout->setContentsMargins( 0, 0, 0, 0 );
+    }
+
+    ~PrivateData()
+    {
+        delete layout;
+    }
+
+    QFont font;
+    QPen textPen;
+    int itemMargin;
+    int itemSpacing;
+
+    double borderRadius;
+    QPen borderPen;
+    QBrush backgroundBrush;
+    QwtPlotLegendItem::BackgroundMode backgroundMode;
+
+    int borderDistance;
+    Qt::Alignment alignment;
+
+    QMap< const QwtPlotItem *, QList<QwtLegendLayoutItem *> > map;
+    QwtDynGridLayout *layout;
+};
+
+//! Constructor 
+QwtPlotLegendItem::QwtPlotLegendItem():
+    QwtPlotItem( QwtText( "Legend" ) )
+{
+    d_data = new PrivateData;
+
+    setItemInterest( QwtPlotItem::LegendInterest, true );
+    setZ( 100.0 );
+}
+
+//! Destructor
+QwtPlotLegendItem::~QwtPlotLegendItem()
+{
+    clearLegend();
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotLegend
+int QwtPlotLegendItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotLegend;
+}
+
+/*!
+  \brief Set the alignmnet
+
+  Alignment means the position of the legend relative
+  to the geometry of the plot canvas. 
+
+  \param alignment Alignment flags
+
+  \sa alignment(), setMaxColumns()
+
+  \note To align a legend with many items horizontally 
+        the number of columns need to be limited
+ */
+void QwtPlotLegendItem::setAlignment( Qt::Alignment alignment )
+{
+    if ( d_data->alignment != alignment )
+    {
+        d_data->alignment = alignment;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Alignment flags
+  \sa setAlignment()
+ */
+Qt::Alignment QwtPlotLegendItem::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+  \brief Limit the number of columns
+
+  When aligning the legend horizontally ( Qt::AlignLeft, Qt::AlignRight )
+  the number of columns needs to be limited to avoid, that
+  the width of the legend grows with an increasing number of entries.
+
+  \param maxColumns Maximum number of columns. 0 means unlimited.
+  \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
+ */
+void QwtPlotLegendItem::setMaxColumns( uint maxColumns )
+{
+    if ( maxColumns != d_data->layout->maxColumns() )
+    {
+        d_data->layout->setMaxColumns( maxColumns );
+        itemChanged();
+    }
+}
+
+/*!
+  \return Maximum number of columns
+  \sa maxColumns(), QwtDynGridLayout::maxColumns()
+ */
+uint QwtPlotLegendItem::maxColumns() const
+{
+    return d_data->layout->maxColumns();
+}
+
+/*!
+  \brief Set the margin around legend items
+
+  The default setting for the margin is 0.
+
+  \param margin Margin in pixels
+  \sa margin(), setSpacing(), setItemMargin(), setItemSpacing
+ */
+void QwtPlotLegendItem::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != this->margin() )
+    {
+        d_data->layout->setContentsMargins( 
+            margin, margin, margin, margin );
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin around the legend items
+  \sa setMargin(), spacing(), itemMargin(), itemSpacing()
+ */
+int QwtPlotLegendItem::margin() const
+{
+    int left;
+    d_data->layout->getContentsMargins( &left, NULL, NULL, NULL );
+
+    return left;
+}
+
+/*!
+  \brief Set the spacing between the legend items
+
+  \param spacing Spacing in pixels
+  \sa spacing(), setMargin()
+*/
+void QwtPlotLegendItem::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->layout->spacing() )
+    {
+        d_data->layout->setSpacing( spacing );
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spacing between the legend items
+  \sa setSpacing(), margin(), itemSpacing(), itemMargin()
+ */
+int QwtPlotLegendItem::spacing() const
+{
+    return d_data->layout->spacing();
+}
+
+/*!
+  Set the margin around each item
+
+  \param margin Margin
+  \sa itemMargin(), setItemSpacing(), setMargin(), setSpacing()
+ */
+void QwtPlotLegendItem::setItemMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != d_data->itemMargin )
+    {
+        d_data->itemMargin = margin;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin around each item
+  \sa setItemMargin(), itemSpacing(), margin(), spacing()
+*/
+int QwtPlotLegendItem::itemMargin() const
+{
+    return d_data->itemMargin;
+}
+
+/*!
+  Set the spacing inside of each item
+
+  \param spacing Spacing
+  \sa itemSpacing(), setItemMargin(), setMargin(), setSpacing()
+ */
+void QwtPlotLegendItem::setItemSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->itemSpacing )
+    {
+        d_data->itemSpacing = spacing;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+
+}
+
+/*!
+  \return Spacing inside of each item
+  \sa setItemSpacing(), itemMargin(), margin(), spacing()
+*/
+int QwtPlotLegendItem::itemSpacing() const
+{
+    return d_data->itemSpacing;
+}
+
+/*!
+   Change the font used for drawing the text label
+
+   \param font Legend font
+   \sa font()
+*/
+void QwtPlotLegendItem::setFont( const QFont &font )
+{
+    if ( font != d_data->font )
+    {
+        d_data->font = font;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+/*!
+   \return Font used for drawing the text label
+   \sa setFont()
+*/
+QFont QwtPlotLegendItem::font() const
+{
+    return d_data->font;
+}
+
+/*!
+  \brief Set the margin between the legend and the canvas border
+
+  The default setting for the margin is 10 pixels.
+
+  \param distance Margin in pixels
+  \sa setMargin()
+ */
+void QwtPlotLegendItem::setBorderDistance( int distance )
+{
+    if ( distance < 0 )
+        distance = -1;
+
+    if ( distance != d_data->borderDistance )
+    {
+        d_data->borderDistance = distance;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin between the legend and the canvas border
+  \sa margin()
+ */
+int QwtPlotLegendItem::borderDistance() const
+{
+    return d_data->borderDistance;
+}
+
+/*!
+  Set the radius for the border
+  
+  \param radius A value <= 0 defines a rectangular border
+  \sa borderRadius(), setBorderPen()
+ */
+void QwtPlotLegendItem::setBorderRadius( double radius )
+{
+    radius = qMax( 0.0, radius );
+
+    if ( radius != d_data->borderRadius )
+    {
+        d_data->borderRadius = radius;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Radius of the border
+  \sa setBorderRadius(), setBorderPen()
+ */
+double QwtPlotLegendItem::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+  Set the pen for drawing the border
+
+  \param pen Border pen
+  \sa borderPen(), setBackgroundBrush()
+ */
+void QwtPlotLegendItem::setBorderPen( const QPen &pen )
+{
+    if ( d_data->borderPen != pen )
+    {
+        d_data->borderPen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen for drawing the border
+  \sa setBorderPen(), backgroundBrush()
+ */
+QPen QwtPlotLegendItem::borderPen() const
+{
+    return d_data->borderPen;
+}
+
+/*!
+  \brief Set the background brush
+
+  The brush is used to fill the background
+
+  \param brush Brush
+  \sa backgroundBrush(), setBackgroundMode(), drawBackground()
+ */
+void QwtPlotLegendItem::setBackgroundBrush( const QBrush &brush )
+{
+    if ( d_data->backgroundBrush != brush )
+    {
+        d_data->backgroundBrush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush is used to fill the background
+  \sa setBackgroundBrush(), backgroundMode(), drawBackground()
+ */
+QBrush QwtPlotLegendItem::backgroundBrush() const
+{
+    return d_data->backgroundBrush;
+}
+
+/*!
+  \brief Set the background mode
+
+  Depending on the mode the complete legend or each item 
+  might have an background.
+
+  The default setting is LegendBackground.
+
+   \sa backgroundMode(), setBackgroundBrush(), drawBackground()
+ */
+void QwtPlotLegendItem::setBackgroundMode( BackgroundMode mode )
+{
+    if ( mode != d_data->backgroundMode )
+    {
+        d_data->backgroundMode = mode;
+        itemChanged();
+    }
+}
+
+/*! 
+  \return backgroundMode
+  \sa setBackgroundMode(), backgroundBrush(), drawBackground()
+ */
+QwtPlotLegendItem::BackgroundMode QwtPlotLegendItem::backgroundMode() const
+{
+    return d_data->backgroundMode;
+}
+
+/*!
+  \brief Set the pen for drawing text labels
+
+  \param pen Text pen
+  \sa textPen(), setFont()
+ */
+void QwtPlotLegendItem::setTextPen( const QPen &pen )
+{
+    if ( d_data->textPen != pen )
+    {
+        d_data->textPen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen for drawing text labels
+  \sa setTextPen(), font()
+ */
+QPen QwtPlotLegendItem::textPen() const
+{
+    return d_data->textPen;
+}
+
+/*!
+  Draw the legend
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+void QwtPlotLegendItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+
+    d_data->layout->setGeometry( geometry( canvasRect ) );
+
+    if ( d_data->backgroundMode == QwtPlotLegendItem::LegendBackground )
+        drawBackground( painter, d_data->layout->geometry() );
+    
+    for ( int i = 0; i <  d_data->layout->count(); i++ )
+    {
+        const QwtLegendLayoutItem *layoutItem = 
+            static_cast<QwtLegendLayoutItem *>( d_data->layout->itemAt( i ) );
+
+        if ( d_data->backgroundMode == QwtPlotLegendItem::ItemBackground )
+            drawBackground( painter, layoutItem->geometry() );
+
+        painter->save();
+
+        drawLegendData( painter, layoutItem->plotItem(),
+            layoutItem->data(), layoutItem->geometry() );
+
+        painter->restore();
+    }
+}
+
+/*!
+  Draw a rounded rect
+
+  \param painter Painter
+  \param rect Bounding rectangle
+
+  \sa setBorderRadius(), setBorderPen(),
+      setBackgroundBrush(), setBackgroundMode()
+ */
+void QwtPlotLegendItem::drawBackground( 
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->save();
+
+    painter->setPen( d_data->borderPen );
+    painter->setBrush( d_data->backgroundBrush );
+    
+    const double radius = d_data->borderRadius;
+    painter->drawRoundedRect( rect, radius, radius );
+    
+    painter->restore();
+}
+
+/*!
+  Calculate the geometry of the legend on the canvas
+
+  \param canvasRect Geometry of the canvas
+  \return Geometry of the legend
+*/
+QRect QwtPlotLegendItem::geometry( const QRectF &canvasRect ) const
+{
+    QRect rect;
+    rect.setSize( d_data->layout->sizeHint() );
+
+    int margin = d_data->borderDistance;
+    if ( d_data->alignment & Qt::AlignHCenter )
+    {
+        int x = qRound( canvasRect.center().x() );
+        rect.moveCenter( QPoint( x, rect.center().y() ) ); 
+    }
+    else if ( d_data->alignment & Qt::AlignRight )
+    {
+        rect.moveRight( qFloor( canvasRect.right() - margin ) );
+    }
+    else 
+    {
+        rect.moveLeft( qCeil( canvasRect.left() + margin ) );
+    }
+
+    if ( d_data->alignment & Qt::AlignVCenter )
+    {
+        int y = qRound( canvasRect.center().y() );
+        rect.moveCenter( QPoint( rect.center().x(), y ) );
+    }
+    else if ( d_data->alignment & Qt::AlignBottom )
+    {
+        rect.moveBottom( qFloor( canvasRect.bottom() - margin ) );
+    }
+    else 
+    {
+        rect.moveTop( qCeil( canvasRect.top() + margin ) ); 
+    }
+
+    return rect;
+}
+
+/*!
+  Update the legend items according to modifications of a 
+  plot item
+
+  \param plotItem Plot item
+  \param data Attributes of the legend entries
+ */
+void QwtPlotLegendItem::updateLegend( const QwtPlotItem *plotItem,
+        const QList<QwtLegendData> &data )
+{
+    if ( plotItem == NULL )
+        return;
+
+    QList<QwtLegendLayoutItem *> layoutItems;
+
+    QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it = 
+        d_data->map.find( plotItem );
+    if ( it != d_data->map.end() )
+        layoutItems = it.value();
+
+    bool changed = false;
+
+    if ( data.size() != layoutItems.size() )
+    {
+        changed = true;
+
+        for ( int i = 0; i < layoutItems.size(); i++ )
+        {
+            d_data->layout->removeItem( layoutItems[i] );
+            delete layoutItems[i];
+        }
+        if ( it != d_data->map.end() )
+            d_data->map.remove( plotItem );
+
+        if ( !data.isEmpty() )
+        {
+            for ( int i = 0; i < data.size(); i++ )
+            {
+                QwtLegendLayoutItem *layoutItem = 
+                    new QwtLegendLayoutItem( this, plotItem );
+                d_data->layout->addItem( layoutItem );
+                layoutItems += layoutItem;
+            }
+
+            d_data->map.insert( plotItem, layoutItems );
+        }
+    }
+
+    for ( int i = 0; i < data.size(); i++ )
+    {
+        if ( layoutItems[i]->data().values() != data[i].values() )
+        {
+            layoutItems[i]->setData( data[i] );
+            changed = true;
+        }
+    }
+
+    if ( changed )
+    {
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+//! Remove all items from the legend
+void QwtPlotLegendItem::clearLegend()
+{
+    if ( !d_data->map.isEmpty() )
+    {
+        d_data->map.clear();
+
+        for ( int i = d_data->layout->count() - 1; i >= 0; i-- )
+            delete d_data->layout->takeAt( i );
+
+        itemChanged();
+    }
+}
+
+/*!
+  Draw an entry on the legend
+
+  \param painter Qt Painter
+  \param plotItem Plot item, represented by the entry
+  \param data Attributes of the legend entry
+  \param rect Bounding rectangle for the entry
+ */
+void QwtPlotLegendItem::drawLegendData( QPainter *painter,
+    const QwtPlotItem *plotItem, const QwtLegendData &data, 
+    const QRectF &rect ) const
+{
+    Q_UNUSED( plotItem );
+
+    const int m = d_data->itemMargin;
+    const QRectF r = rect.toRect().adjusted( m, m, -m, -m );
+
+    painter->setClipRect( r, Qt::IntersectClip );
+
+    int titleOff = 0;
+
+    const QwtGraphic graphic = data.icon();
+    if ( !graphic.isEmpty() )
+    {
+        QRectF iconRect( r.topLeft(), graphic.defaultSize() );
+
+        iconRect.moveCenter( 
+            QPoint( iconRect.center().x(), rect.center().y() ) );
+
+        graphic.render( painter, iconRect, Qt::KeepAspectRatio );
+
+        titleOff += iconRect.width() + d_data->itemSpacing;
+    }
+
+    const QwtText text = data.title();
+    if ( !text.isEmpty() )
+    {
+        painter->setPen( textPen() );
+        painter->setFont( font() );
+
+        const QRectF textRect = r.adjusted( titleOff, 0, 0, 0 );
+        text.draw( painter, textRect );
+    }
+}
+
+/*!
+  Minimum size hint needed to display an entry
+
+  \param data Attributes of the legend entry
+  \return Minimum size
+ */
+QSize QwtPlotLegendItem::minimumSize( const QwtLegendData &data ) const
+{
+    QSize size( 2 * d_data->itemMargin, 2 * d_data->itemMargin );
+
+    if ( !data.isValid() )
+        return size;
+
+    const QwtGraphic graphic = data.icon();
+    const QwtText text = data.title();
+
+    int w = 0;
+    int h = 0;
+
+    if ( !graphic.isNull() )
+    {
+        w = graphic.width();
+        h = graphic.height();
+    }
+
+    if ( !text.isEmpty() )
+    {
+        const QSizeF sz = text.textSize( font() );
+
+        w += qCeil( sz.width() );
+        h = qMax( h, qCeil( sz.height() ) );
+    }
+
+    if ( graphic.width() > 0 && !text.isEmpty() )
+        w += d_data->itemSpacing;
+
+    size += QSize( w, h );
+    return size;
+}
+
+/*!
+  \return The preferred height, for a width.
+  \param data Attributes of the legend entry
+  \param width Width
+*/
+int QwtPlotLegendItem::heightForWidth( 
+    const QwtLegendData &data, int width ) const
+{
+    width -= 2 * d_data->itemMargin;
+
+    const QwtGraphic graphic = data.icon();
+    const QwtText text = data.title();
+
+    if ( text.isEmpty() )
+        return graphic.height();
+
+    if ( graphic.width() > 0 )
+        width -= graphic.width() + d_data->itemSpacing;
+
+    int h = text.heightForWidth( width, font() );
+    h += 2 * d_data->itemMargin;
+
+    return qMax( graphic.height(), h );
+}
+
+/*! 
+  \return All plot items with an entry on the legend
+  \note A plot item might have more than one entry on the legend
+ */
+QList< const QwtPlotItem * > QwtPlotLegendItem::plotItems() const
+{
+    return d_data->map.keys();
+}
+
+/*!
+  \return Geometries of the items of a plot item
+  \note Usually a plot item has only one entry on the legend
+*/
+QList< QRect > QwtPlotLegendItem::legendGeometries( 
+    const QwtPlotItem *plotItem ) const
+{
+    QList<QwtLegendLayoutItem *> layoutItems;
+
+    QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it =
+        d_data->map.find( plotItem );
+    if ( it != d_data->map.end() )
+        layoutItems = it.value();
+
+    QList<QRect> geometries;
+    for ( int i = 0; i < layoutItems.size(); i++ )
+        geometries += layoutItems[i]->geometry();
+
+    return geometries;
+}
diff --git a/qwt/qwt_plot_legenditem.h b/qwt/qwt_plot_legenditem.h
new file mode 100644
index 0000000..378551e
--- /dev/null
+++ b/qwt/qwt_plot_legenditem.h
@@ -0,0 +1,136 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_LEGEND_ITEM_H
+#define QWT_PLOT_LEGEND_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_legend_data.h"
+
+class QFont;
+
+/*!
+  \brief A class which draws a legend inside the plot canvas
+
+  QwtPlotLegendItem can be used to draw a inside the plot canvas.
+  It can be used together with a QwtLegend or instead of it
+  to have more space for the plot canvas.
+
+  In opposite to QwtLegend the legend item is not interactive. 
+  To identify mouse clicks on a legend item an event filter
+  needs to be installed catching mouse events ob the plot canvas.
+  The geometries of the legend items are available using
+  legendGeometries().
+  
+  The legend item is aligned to plot canvas according to 
+  its alignment() flags. It might have a background for the
+  complete legend ( usually semi transparent ) or for
+  each legend item.
+
+  \note An external QwtLegend with a transparent background 
+        on top the plot canvas might be another option 
+        with a similar effect.
+*/
+
+class QWT_EXPORT QwtPlotLegendItem: public QwtPlotItem
+{
+public:
+    /*!
+      \brief Background mode
+
+      Depending on the mode the complete legend or each item 
+      might have an background.
+
+      The default setting is LegendBackground.
+
+       \sa setBackgroundMode(), setBackgroundBrush(), drawBackground()
+     */
+    enum BackgroundMode
+    {
+        //! The legend has a background
+        LegendBackground,
+
+        //! Each item has a background
+        ItemBackground
+    };
+
+    explicit QwtPlotLegendItem();
+    virtual ~QwtPlotLegendItem();
+
+    virtual int rtti() const;
+
+    void setAlignment( Qt::Alignment );
+    Qt::Alignment alignment() const;
+
+    void setMaxColumns( uint );
+    uint maxColumns() const;
+
+    void setMargin( int );
+    int margin() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setItemMargin( int );
+    int itemMargin() const;
+
+    void setItemSpacing( int );
+    int itemSpacing() const;
+    
+    void setFont( const QFont& );
+    QFont font() const;
+
+    void setBorderDistance( int numPixels );
+    int borderDistance() const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setBorderPen( const QPen & );
+    QPen borderPen() const;
+
+    void setBackgroundBrush( const QBrush & );
+    QBrush backgroundBrush() const;
+
+    void setBackgroundMode( BackgroundMode );
+    BackgroundMode backgroundMode() const;
+
+    void setTextPen( const QPen & );
+    QPen textPen() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    void clearLegend();
+
+    virtual void updateLegend( const QwtPlotItem *,
+        const QList<QwtLegendData> & );
+
+    virtual QRect geometry( const QRectF &canvasRect ) const;
+
+    virtual QSize minimumSize( const QwtLegendData & ) const;
+    virtual int heightForWidth( const QwtLegendData &, int w ) const;
+
+    QList< const QwtPlotItem * > plotItems() const;
+    QList< QRect > legendGeometries( const QwtPlotItem * ) const;
+
+protected:
+    virtual void drawLegendData( QPainter *painter, 
+        const QwtPlotItem *, const QwtLegendData &, const QRectF & ) const;
+
+    virtual void drawBackground( QPainter *, const QRectF &rect ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_magnifier.cpp b/qwt/qwt_plot_magnifier.cpp
new file mode 100644
index 0000000..43a6b9a
--- /dev/null
+++ b/qwt/qwt_plot_magnifier.cpp
@@ -0,0 +1,145 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot.h"
+#include "qwt_scale_div.h"
+#include "qwt_plot_magnifier.h"
+#include <qevent.h>
+
+class QwtPlotMagnifier::PrivateData
+{
+public:
+    PrivateData()
+    {
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            isAxisEnabled[axis] = true;
+    }
+
+    bool isAxisEnabled[QwtPlot::axisCnt];
+};
+
+/*!
+   Constructor
+   \param canvas Plot canvas to be magnified
+*/
+QwtPlotMagnifier::QwtPlotMagnifier( QWidget *canvas ):
+    QwtMagnifier( canvas )
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPlotMagnifier::~QwtPlotMagnifier()
+{
+    delete d_data;
+}
+
+/*!
+   \brief En/Disable an axis
+
+   Only Axes that are enabled will be zoomed.
+   All other axes will remain unchanged.
+
+   \param axis Axis, see QwtPlot::Axis
+   \param on On/Off
+
+   \sa isAxisEnabled()
+*/
+void QwtPlotMagnifier::setAxisEnabled( int axis, bool on )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->isAxisEnabled[axis] = on;
+}
+
+/*!
+   Test if an axis is enabled
+
+   \param axis Axis, see QwtPlot::Axis
+   \return True, if the axis is enabled
+
+   \sa setAxisEnabled()
+*/
+bool QwtPlotMagnifier::isAxisEnabled( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->isAxisEnabled[axis];
+
+    return true;
+}
+
+//! Return observed plot canvas
+QWidget *QwtPlotMagnifier::canvas()
+{
+    return parentWidget();
+}
+
+//! Return Observed plot canvas
+const QWidget *QwtPlotMagnifier::canvas() const
+{
+    return parentWidget();
+}
+
+//! Return plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotMagnifier::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! Return plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotMagnifier::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+   Zoom in/out the axes scales
+   \param factor A value < 1.0 zooms in, a value > 1.0 zooms out.
+*/
+void QwtPlotMagnifier::rescale( double factor )
+{
+    QwtPlot* plt = plot();
+    if ( plt == NULL )
+        return;
+
+    factor = qAbs( factor );
+    if ( factor == 1.0 || factor == 0.0 )
+        return;
+
+    bool doReplot = false;
+
+    const bool autoReplot = plt->autoReplot();
+    plt->setAutoReplot( false );
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        const QwtScaleDiv &scaleDiv = plt->axisScaleDiv( axisId );
+        if ( isAxisEnabled( axisId ) )
+        {
+            const double center =
+                scaleDiv.lowerBound() + scaleDiv.range() / 2;
+            const double width_2 = scaleDiv.range() / 2 * factor;
+
+            plt->setAxisScale( axisId, center - width_2, center + width_2 );
+            doReplot = true;
+        }
+    }
+
+    plt->setAutoReplot( autoReplot );
+
+    if ( doReplot )
+        plt->replot();
+}
diff --git a/qwt/qwt_plot_magnifier.h b/qwt/qwt_plot_magnifier.h
new file mode 100644
index 0000000..7bce3fa
--- /dev/null
+++ b/qwt/qwt_plot_magnifier.h
@@ -0,0 +1,54 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_MAGNIFIER_H
+#define QWT_PLOT_MAGNIFIER_H 1
+
+#include "qwt_global.h"
+#include "qwt_magnifier.h"
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotMagnifier provides zooming, by magnifying in steps.
+
+  Using QwtPlotMagnifier a plot can be zoomed in/out in steps using
+  keys, the mouse wheel or moving a mouse button in vertical direction.
+
+  Together with QwtPlotZoomer and QwtPlotPanner it is possible to implement
+  individual and powerful navigation of the plot canvas.
+
+  \sa QwtPlotZoomer, QwtPlotPanner, QwtPlot
+*/
+class QWT_EXPORT QwtPlotMagnifier: public QwtMagnifier
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotMagnifier( QWidget * );
+    virtual ~QwtPlotMagnifier();
+
+    void setAxisEnabled( int axis, bool on );
+    bool isAxisEnabled( int axis ) const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+protected:
+    virtual void rescale( double factor );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_marker.cpp b/qwt/qwt_plot_marker.cpp
new file mode 100644
index 0000000..d88debf
--- /dev/null
+++ b/qwt/qwt_plot_marker.cpp
@@ -0,0 +1,610 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_marker.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include "qwt_symbol.h"
+#include "qwt_text.h"
+#include "qwt_math.h"
+#include <qpainter.h>
+
+class QwtPlotMarker::PrivateData
+{
+public:
+    PrivateData():
+        labelAlignment( Qt::AlignCenter ),
+        labelOrientation( Qt::Horizontal ),
+        spacing( 2 ),
+        symbol( NULL ),
+        style( QwtPlotMarker::NoLine ),
+        xValue( 0.0 ),
+        yValue( 0.0 )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtText label;
+    Qt::Alignment labelAlignment;
+    Qt::Orientation labelOrientation;
+    int spacing;
+
+    QPen pen;
+    const QwtSymbol *symbol;
+    LineStyle style;
+
+    double xValue;
+    double yValue;
+};
+
+//! Sets alignment to Qt::AlignCenter, and style to QwtPlotMarker::NoLine
+QwtPlotMarker::QwtPlotMarker( const QString &title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    d_data = new PrivateData;
+    setZ( 30.0 );
+}
+
+//! Sets alignment to Qt::AlignCenter, and style to QwtPlotMarker::NoLine
+QwtPlotMarker::QwtPlotMarker( const QwtText &title ):
+    QwtPlotItem( title )
+{
+    d_data = new PrivateData;
+    setZ( 30.0 );
+}
+
+//! Destructor
+QwtPlotMarker::~QwtPlotMarker()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotMarker
+int QwtPlotMarker::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotMarker;
+}
+
+//! Return Value
+QPointF QwtPlotMarker::value() const
+{
+    return QPointF( d_data->xValue, d_data->yValue );
+}
+
+//! Return x Value
+double QwtPlotMarker::xValue() const
+{
+    return d_data->xValue;
+}
+
+//! Return y Value
+double QwtPlotMarker::yValue() const
+{
+    return d_data->yValue;
+}
+
+//! Set Value
+void QwtPlotMarker::setValue( const QPointF& pos )
+{
+    setValue( pos.x(), pos.y() );
+}
+
+//! Set Value
+void QwtPlotMarker::setValue( double x, double y )
+{
+    if ( x != d_data->xValue || y != d_data->yValue )
+    {
+        d_data->xValue = x;
+        d_data->yValue = y;
+        itemChanged();
+    }
+}
+
+//! Set X Value
+void QwtPlotMarker::setXValue( double x )
+{
+    setValue( x, d_data->yValue );
+}
+
+//! Set Y Value
+void QwtPlotMarker::setYValue( double y )
+{
+    setValue( d_data->xValue, y );
+}
+
+/*!
+  Draw the marker
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+void QwtPlotMarker::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    const QPointF pos( xMap.transform( d_data->xValue ), 
+        yMap.transform( d_data->yValue ) );
+
+    // draw lines
+
+    drawLines( painter, canvasRect, pos );
+
+    // draw symbol
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+    {
+        const QSizeF sz = d_data->symbol->size();
+
+        const QRectF clipRect = canvasRect.adjusted( 
+            -sz.width(), -sz.height(), sz.width(), sz.height() );
+
+        if ( clipRect.contains( pos ) )
+            d_data->symbol->drawSymbol( painter, pos );
+    }
+
+    drawLabel( painter, canvasRect, pos );
+}
+
+/*!
+  Draw the lines marker
+
+  \param painter Painter
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+  \param pos Position of the marker, translated into widget coordinates
+
+  \sa drawLabel(), QwtSymbol::drawSymbol()
+*/
+void QwtPlotMarker::drawLines( QPainter *painter,
+    const QRectF &canvasRect, const QPointF &pos ) const
+{
+    if ( d_data->style == NoLine )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->setPen( d_data->pen );
+    if ( d_data->style == QwtPlotMarker::HLine ||
+        d_data->style == QwtPlotMarker::Cross )
+    {
+        double y = pos.y();
+        if ( doAlign )
+            y = qRound( y );
+
+        QwtPainter::drawLine( painter, canvasRect.left(),
+            y, canvasRect.right() - 1.0, y );
+    }
+    if ( d_data->style == QwtPlotMarker::VLine ||
+        d_data->style == QwtPlotMarker::Cross )
+    {
+        double x = pos.x();
+        if ( doAlign )
+            x = qRound( x );
+
+        QwtPainter::drawLine( painter, x,
+            canvasRect.top(), x, canvasRect.bottom() - 1.0 );
+    }
+}
+
+/*!
+  Align and draw the text label of the marker
+
+  \param painter Painter
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+  \param pos Position of the marker, translated into widget coordinates
+
+  \sa drawLabel(), QwtSymbol::drawSymbol()
+*/
+void QwtPlotMarker::drawLabel( QPainter *painter,
+    const QRectF &canvasRect, const QPointF &pos ) const
+{
+    if ( d_data->label.isEmpty() )
+        return;
+
+    Qt::Alignment align = d_data->labelAlignment;
+    QPointF alignPos = pos;
+
+    QSizeF symbolOff( 0, 0 );
+
+    switch ( d_data->style )
+    {
+        case QwtPlotMarker::VLine:
+        {
+            // In VLine-style the y-position is pointless and
+            // the alignment flags are relative to the canvas
+
+            if ( d_data->labelAlignment & Qt::AlignTop )
+            {
+                alignPos.setY( canvasRect.top() );
+                align &= ~Qt::AlignTop;
+                align |= Qt::AlignBottom;
+            }
+            else if ( d_data->labelAlignment & Qt::AlignBottom )
+            {
+                // In HLine-style the x-position is pointless and
+                // the alignment flags are relative to the canvas
+
+                alignPos.setY( canvasRect.bottom() - 1 );
+                align &= ~Qt::AlignBottom;
+                align |= Qt::AlignTop;
+            }
+            else
+            {
+                alignPos.setY( canvasRect.center().y() );
+            }
+            break;
+        }
+        case QwtPlotMarker::HLine:
+        {
+            if ( d_data->labelAlignment & Qt::AlignLeft )
+            {
+                alignPos.setX( canvasRect.left() );
+                align &= ~Qt::AlignLeft;
+                align |= Qt::AlignRight;
+            }
+            else if ( d_data->labelAlignment & Qt::AlignRight )
+            {
+                alignPos.setX( canvasRect.right() - 1 );
+                align &= ~Qt::AlignRight;
+                align |= Qt::AlignLeft;
+            }
+            else
+            {
+                alignPos.setX( canvasRect.center().x() );
+            }
+            break;
+        }
+        default:
+        {
+            if ( d_data->symbol &&
+                ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+            {
+                symbolOff = d_data->symbol->size() + QSizeF( 1, 1 );
+                symbolOff /= 2;
+            }
+        }
+    }
+
+    qreal pw2 = d_data->pen.widthF() / 2.0;
+    if ( pw2 == 0.0 )
+        pw2 = 0.5;
+
+    const int spacing = d_data->spacing;
+
+    const qreal xOff = qMax( pw2, symbolOff.width() );
+    const qreal yOff = qMax( pw2, symbolOff.height() );
+
+    const QSizeF textSize = d_data->label.textSize( painter->font() );
+
+    if ( align & Qt::AlignLeft )
+    {
+        alignPos.rx() -= xOff + spacing;
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.rx() -= textSize.height();
+        else
+            alignPos.rx() -= textSize.width();
+    }
+    else if ( align & Qt::AlignRight )
+    {
+        alignPos.rx() += xOff + spacing;
+    }
+    else
+    {
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.rx() -= textSize.height() / 2;
+        else
+            alignPos.rx() -= textSize.width() / 2;
+    }
+
+    if ( align & Qt::AlignTop )
+    {
+        alignPos.ry() -= yOff + spacing;
+        if ( d_data->labelOrientation != Qt::Vertical )
+            alignPos.ry() -= textSize.height();
+    }
+    else if ( align & Qt::AlignBottom )
+    {
+        alignPos.ry() += yOff + spacing;
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.ry() += textSize.width();
+    }
+    else
+    {
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.ry() += textSize.width() / 2;
+        else
+            alignPos.ry() -= textSize.height() / 2;
+    }
+
+    painter->translate( alignPos.x(), alignPos.y() );
+    if ( d_data->labelOrientation == Qt::Vertical )
+        painter->rotate( -90.0 );
+
+    const QRectF textRect( 0, 0, textSize.width(), textSize.height() );
+    d_data->label.draw( painter, textRect );
+}
+
+/*!
+  \brief Set the line style
+  \param style Line style. 
+  \sa lineStyle()
+*/
+void QwtPlotMarker::setLineStyle( LineStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the line style
+  \sa setLineStyle()
+*/
+QwtPlotMarker::LineStyle QwtPlotMarker::lineStyle() const
+{
+    return d_data->style;
+}
+
+/*!
+  \brief Assign a symbol
+  \param symbol New symbol
+  \sa symbol()
+*/
+void QwtPlotMarker::setSymbol( const QwtSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        if ( symbol )
+            setLegendIconSize( symbol->boundingRect().size() );
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the symbol
+  \sa setSymbol(), QwtSymbol
+*/
+const QwtSymbol *QwtPlotMarker::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  \brief Set the label
+  \param label Label text
+  \sa label()
+*/
+void QwtPlotMarker::setLabel( const QwtText& label )
+{
+    if ( label != d_data->label )
+    {
+        d_data->label = label;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label
+  \sa setLabel()
+*/
+QwtText QwtPlotMarker::label() const
+{
+    return d_data->label;
+}
+
+/*!
+  \brief Set the alignment of the label
+
+  In case of QwtPlotMarker::HLine the alignment is relative to the
+  y position of the marker, but the horizontal flags correspond to the
+  canvas rectangle. In case of QwtPlotMarker::VLine the alignment is
+  relative to the x position of the marker, but the vertical flags
+  correspond to the canvas rectangle.
+
+  In all other styles the alignment is relative to the marker's position.
+
+  \param align Alignment. 
+  \sa labelAlignment(), labelOrientation()
+*/
+void QwtPlotMarker::setLabelAlignment( Qt::Alignment align )
+{
+    if ( align != d_data->labelAlignment )
+    {
+        d_data->labelAlignment = align;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label alignment
+  \sa setLabelAlignment(), setLabelOrientation()
+*/
+Qt::Alignment QwtPlotMarker::labelAlignment() const
+{
+    return d_data->labelAlignment;
+}
+
+/*!
+  \brief Set the orientation of the label
+
+  When orientation is Qt::Vertical the label is rotated by 90.0 degrees
+  ( from bottom to top ).
+
+  \param orientation Orientation of the label
+
+  \sa labelOrientation(), setLabelAlignment()
+*/
+void QwtPlotMarker::setLabelOrientation( Qt::Orientation orientation )
+{
+    if ( orientation != d_data->labelOrientation )
+    {
+        d_data->labelOrientation = orientation;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label orientation
+  \sa setLabelOrientation(), labelAlignment()
+*/
+Qt::Orientation QwtPlotMarker::labelOrientation() const
+{
+    return d_data->labelOrientation;
+}
+
+/*!
+  \brief Set the spacing
+
+  When the label is not centered on the marker position, the spacing
+  is the distance between the position and the label.
+
+  \param spacing Spacing
+  \sa spacing(), setLabelAlignment()
+*/
+void QwtPlotMarker::setSpacing( int spacing )
+{
+    if ( spacing < 0 )
+        spacing = 0;
+
+    if ( spacing == d_data->spacing )
+        return;
+
+    d_data->spacing = spacing;
+    itemChanged();
+}
+
+/*!
+  \return the spacing
+  \sa setSpacing()
+*/
+int QwtPlotMarker::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*! 
+  Build and assign a line pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotMarker::setLinePen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setLinePen( QPen( color, width, style ) );
+}
+
+/*!
+  Specify a pen for the line.
+
+  \param pen New pen
+  \sa linePen()
+*/
+void QwtPlotMarker::setLinePen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the line pen
+  \sa setLinePen()
+*/
+const QPen &QwtPlotMarker::linePen() const
+{
+    return d_data->pen;
+}
+
+QRectF QwtPlotMarker::boundingRect() const
+{
+    return QRectF( d_data->xValue, d_data->yValue, 0.0, 0.0 );
+}
+
+/*!
+   \return Icon representing the marker on the legend
+
+   \param index Index of the legend entry 
+                ( usually there is only one )
+   \param size Icon size
+
+   \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotMarker::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->style != QwtPlotMarker::NoLine )
+    {
+        painter.setPen( d_data->pen );
+
+        if ( d_data->style == QwtPlotMarker::HLine ||
+            d_data->style == QwtPlotMarker::Cross )
+        {
+            const double y = 0.5 * size.height();
+
+            QwtPainter::drawLine( &painter, 
+                0.0, y, size.width(), y );
+        }
+
+        if ( d_data->style == QwtPlotMarker::VLine ||
+            d_data->style == QwtPlotMarker::Cross )
+        {
+            const double x = 0.5 * size.width();
+
+            QwtPainter::drawLine( &painter, 
+                x, 0.0, x, size.height() );
+        }
+    }
+
+    if ( d_data->symbol )
+    {
+        const QRect r( 0.0, 0.0, size.width(), size.height() );
+        d_data->symbol->drawSymbol( &painter, r );
+    }
+
+    return icon;
+}
+
diff --git a/qwt/qwt_plot_marker.h b/qwt/qwt_plot_marker.h
new file mode 100644
index 0000000..2c726ce
--- /dev/null
+++ b/qwt/qwt_plot_marker.h
@@ -0,0 +1,130 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_MARKER_H
+#define QWT_PLOT_MARKER_H
+
+#include <qpen.h>
+#include <qfont.h>
+#include <qstring.h>
+#include <qbrush.h>
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+
+class QRectF;
+class QwtText;
+class QwtSymbol;
+
+/*!
+  \brief A class for drawing markers
+
+  A marker can be a horizontal line, a vertical line,
+  a symbol, a label or any combination of them, which can
+  be drawn around a center point inside a bounding rectangle.
+
+  The setSymbol() member assigns a symbol to the marker.
+  The symbol is drawn at the specified point.
+
+  With setLabel(), a label can be assigned to the marker.
+  The setLabelAlignment() member specifies where the label is
+  drawn. All the Align*-constants in Qt::AlignmentFlags (see Qt documentation)
+  are valid. The interpretation of the alignment depends on the marker's
+  line style. The alignment refers to the center point of
+  the marker, which means, for example, that the label would be printed
+  left above the center point if the alignment was set to 
+  Qt::AlignLeft | Qt::AlignTop.
+
+  \note QwtPlotTextLabel is intended to align a text label
+        according to the geometry of canvas 
+        ( unrelated to plot coordinates )
+*/
+
+class QWT_EXPORT QwtPlotMarker: public QwtPlotItem
+{
+public:
+
+    /*!
+        Line styles.
+        \sa setLineStyle(), lineStyle()
+    */
+    enum LineStyle
+    {
+        //! No line
+        NoLine,
+
+        //! A horizontal line
+        HLine,
+
+        //! A vertical line
+        VLine,
+
+        //! A crosshair
+        Cross
+    };
+
+    explicit QwtPlotMarker( const QString &title = QString::null );
+    explicit QwtPlotMarker( const QwtText &title );
+
+    virtual ~QwtPlotMarker();
+
+    virtual int rtti() const;
+
+    double xValue() const;
+    double yValue() const;
+    QPointF value() const;
+
+    void setXValue( double );
+    void setYValue( double );
+    void setValue( double, double );
+    void setValue( const QPointF & );
+
+    void setLineStyle( LineStyle st );
+    LineStyle lineStyle() const;
+
+    void setLinePen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setLinePen( const QPen &p );
+    const QPen &linePen() const;
+
+    void setSymbol( const QwtSymbol * );
+    const QwtSymbol *symbol() const;
+
+    void setLabel( const QwtText& );
+    QwtText label() const;
+
+    void setLabelAlignment( Qt::Alignment );
+    Qt::Alignment labelAlignment() const;
+
+    void setLabelOrientation( Qt::Orientation );
+    Qt::Orientation labelOrientation() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF & ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    virtual void drawLines( QPainter *, 
+        const QRectF &, const QPointF & ) const;
+
+    virtual void drawLabel( QPainter *, 
+        const QRectF &, const QPointF & ) const;
+
+private:
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_multi_barchart.cpp b/qwt/qwt_plot_multi_barchart.cpp
new file mode 100644
index 0000000..9c8c5e0
--- /dev/null
+++ b/qwt/qwt_plot_multi_barchart.cpp
@@ -0,0 +1,740 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_multi_barchart.h"
+#include "qwt_scale_map.h"
+#include "qwt_column_symbol.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qmap.h>
+
+inline static bool qwtIsIncreasing(
+    const QwtScaleMap &map, const QVector<double> &values )
+{
+    bool isInverting = map.isInverting();
+
+    for ( int i = 0; i < values.size(); i++ )
+    {
+        const double y = values[ i ];
+        if ( y != 0.0 )
+            return ( map.isInverting() != ( y > 0.0 ) );
+    }
+
+    return !isInverting;
+}
+
+class QwtPlotMultiBarChart::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotMultiBarChart::Grouped )
+    {
+    }
+
+    QwtPlotMultiBarChart::ChartStyle style;
+    QList<QwtText> barTitles;
+    QMap<int, QwtColumnSymbol *> symbolMap;
+};
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QwtText &title ):
+    QwtPlotAbstractBarChart( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QString &title ):
+    QwtPlotAbstractBarChart( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotMultiBarChart::~QwtPlotMultiBarChart()
+{
+    resetSymbolMap();
+    delete d_data;
+}
+
+void QwtPlotMultiBarChart::init()
+{
+    d_data = new PrivateData;
+    setData( new QwtSetSeriesData() );
+}
+
+//! \return QwtPlotItem::Rtti_PlotBarChart
+int QwtPlotMultiBarChart::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotMultiBarChart;
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotMultiBarChart::setSamples(
+    const QVector<QwtSetSample> &samples )
+{
+    setData( new QwtSetSeriesData( samples ) );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotMultiBarChart::setSamples(
+    const QVector< QVector<double> > &samples )
+{
+    QVector<QwtSetSample> s;
+    for ( int i = 0; i < samples.size(); i++ )
+        s += QwtSetSample( i, samples[ i ] );
+
+    setData( new QwtSetSeriesData( s ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/  
+void QwtPlotMultiBarChart::setSamples( 
+    QwtSeriesData<QwtSetSample> *data )
+{       
+    setData( data );
+}       
+
+/*!
+  \brief Set the titles for the bars
+
+  The titles are used for the legend.
+
+  \param titles Bar titles
+
+  \sa barTitles(), legendData()
+ */
+void QwtPlotMultiBarChart::setBarTitles( const QList<QwtText> &titles )
+{
+    d_data->barTitles = titles;
+    itemChanged();
+}
+
+/*! 
+  \return Bar titles
+  \sa setBarTitles(), legendData()
+ */
+QList<QwtText> QwtPlotMultiBarChart::barTitles() const
+{
+    return d_data->barTitles;
+}
+
+/*!
+  \brief Add a symbol to the symbol map
+
+  Assign a default symbol for drawing the bar representing all values
+  with the same index in a set.
+
+  \param valueIndex Index of a value in a set
+  \param symbol Symbol used for drawing a bar
+
+  \sa symbol(), resetSymbolMap(), specialSymbol()
+*/
+void QwtPlotMultiBarChart::setSymbol( int valueIndex, QwtColumnSymbol *symbol )
+{
+    if ( valueIndex < 0 )
+        return;
+
+    QMap<int, QwtColumnSymbol *>::iterator it = 
+        d_data->symbolMap.find(valueIndex);
+    if ( it == d_data->symbolMap.end() )
+    {
+        if ( symbol != NULL )
+        {
+            d_data->symbolMap.insert( valueIndex, symbol );
+
+            legendChanged();
+            itemChanged();
+        }
+    }
+    else
+    {
+        if ( symbol != it.value() )
+        {
+            delete it.value();
+
+            if ( symbol == NULL )
+            {
+                d_data->symbolMap.remove( valueIndex );
+            }
+            else
+            {
+                it.value() = symbol;
+            }
+
+            legendChanged();
+            itemChanged();
+        }
+    }
+}
+
+/*!
+  Find a symbol in the symbol map
+
+  \param valueIndex Index of a value in a set
+  \return The symbol, that had been set by setSymbol() or NULL.
+
+  \sa setSymbol(), specialSymbol(), drawBar()
+*/
+const QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) const
+{
+    QMap<int, QwtColumnSymbol *>::const_iterator it =
+        d_data->symbolMap.find( valueIndex );
+
+    return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
+}
+
+/*!
+  Find a symbol in the symbol map
+
+  \param valueIndex Index of a value in a set
+  \return The symbol, that had been set by setSymbol() or NULL.
+
+  \sa setSymbol(), specialSymbol(), drawBar()
+*/
+QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) 
+{
+    QMap<int, QwtColumnSymbol *>::iterator it =
+        d_data->symbolMap.find( valueIndex );
+
+    return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
+}
+
+/*!
+  Remove all symbols from the symbol map
+ */
+void QwtPlotMultiBarChart::resetSymbolMap()
+{
+    for ( QMap<int, QwtColumnSymbol *>::iterator it 
+        = d_data->symbolMap.begin(); it != d_data->symbolMap.end(); ++it )
+    {
+        delete it.value();
+    }
+
+    d_data->symbolMap.clear();
+}
+
+/*!
+  \brief Create a symbol for special values
+
+  Usually the symbols for displaying a bar are set by setSymbols() and
+  common for all sets. By overloading specialSymbol() it is possible to
+  create a temporary symbol() for displaying a special value.
+
+  The symbol has to be created by new each time specialSymbol() is
+  called. As soon as the symbol is painted this symbol gets deleted.
+
+  When no symbol ( NULL ) is returned, the value will be displayed
+  with the standard symbol that is used for all symbols with the same 
+  valueIndex.
+
+  \param sampleIndex Index of the sample
+  \param valueIndex Index of the value in the set
+
+  \return NULL, meaning that the value is not special
+    
+ */
+QwtColumnSymbol *QwtPlotMultiBarChart::specialSymbol( 
+    int sampleIndex, int valueIndex ) const
+{
+    Q_UNUSED( sampleIndex );
+    Q_UNUSED( valueIndex );
+
+    return NULL;
+}
+
+/*!
+  Set the style of the chart
+
+  \param style Chart style
+  \sa style()
+ */
+void QwtPlotMultiBarChart::setStyle( ChartStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Style of the chart
+  \sa setStyle()
+ */
+QwtPlotMultiBarChart::ChartStyle QwtPlotMultiBarChart::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotMultiBarChart::boundingRect() const
+{
+    const size_t numSamples = dataSize();
+
+    if ( numSamples == 0 )
+        return QwtPlotSeriesItem::boundingRect();
+
+    const double baseLine = baseline();
+
+    QRectF rect;
+
+    if ( d_data->style != QwtPlotMultiBarChart::Stacked )
+    {
+        rect = QwtPlotSeriesItem::boundingRect();
+        if ( rect.bottom() < baseLine )
+            rect.setBottom( baseLine );
+        if ( rect.top() > baseLine )
+            rect.setTop( baseLine );
+    }
+    else
+    {
+        double xMin, xMax, yMin, yMax;
+
+        xMin = xMax = 0.0;
+        yMin = yMax = baseLine;
+
+        const QwtSeriesData<QwtSetSample> *series = data();
+
+        for ( size_t i = 0; i < numSamples; i++ )
+        {
+            const QwtSetSample sample = series->sample( i );
+            if ( i == 0 )
+            {
+                xMin = xMax = sample.value;
+            }
+            else
+            {
+                xMin = qMin( xMin, sample.value );
+                xMax = qMax( xMax, sample.value );
+            }
+
+            const double y = baseLine + sample.added();
+
+            yMin = qMin( yMin, y );
+            yMax = qMax( yMax, y );
+        }
+        rect.setRect( xMin, yMin, xMax - xMin, yMax - yMin );
+    }
+
+    if ( rect.isValid() && ( orientation() == Qt::Horizontal ) )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the bar chart
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotMultiBarChart::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+
+    const QRectF br = data()->boundingRect();
+    const QwtInterval interval( br.left(), br.right() );
+
+    painter->save();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        drawSample( painter, xMap, yMap,
+            canvasRect, interval, i, sample( i ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param boundingInterval Bounding interval of sample values
+  \param index Index of the sample to be painted
+  \param sample Sample value
+
+  \sa drawSeries()
+*/
+void QwtPlotMultiBarChart::drawSample( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, const QwtInterval &boundingInterval,
+    int index, const QwtSetSample& sample ) const
+{
+    if ( sample.set.size() <= 0 )
+        return;
+
+    double sampleW;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        sampleW = sampleWidth( yMap, canvasRect.height(),
+            boundingInterval.width(), sample.value );
+    }
+    else
+    {
+        sampleW = sampleWidth( xMap, canvasRect.width(),
+            boundingInterval.width(), sample.value );
+    }
+
+    if ( d_data->style == Stacked )
+    {
+        drawStackedBars( painter, xMap, yMap,
+            canvasRect, index, sampleW, sample );
+    }
+    else
+    {
+        drawGroupedBars( painter, xMap, yMap,
+            canvasRect, index, sampleW, sample );
+    }
+}
+
+/*!
+  Draw a grouped sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param index Index of the sample to be painted
+  \param sampleWidth Boundng width for all bars of the smaple
+  \param sample Sample 
+
+  \sa drawSeries(), sampleWidth()
+*/
+void QwtPlotMultiBarChart::drawGroupedBars( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int index, double sampleWidth,
+    const QwtSetSample& sample ) const
+{
+    Q_UNUSED( canvasRect );
+
+    const int numBars = sample.set.size();
+    if ( numBars == 0 )
+        return;
+
+    if ( orientation() == Qt::Vertical )
+    {
+        const double barWidth = sampleWidth / numBars;
+
+        const double y1 = yMap.transform( baseline() );
+        const double x0 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
+
+        for ( int i = 0; i < numBars; i++ )
+        {
+            const double x1 = x0 + i * barWidth;
+            const double x2 = x1 + barWidth;
+
+            const double y2 = yMap.transform( sample.set[i] );
+
+            QwtColumnRect barRect;
+            barRect.direction = ( y1 < y2 ) ?
+                QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+            barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+            if ( i != 0 )
+                barRect.hInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
+
+            barRect.vInterval = QwtInterval( y1, y2 ).normalized();
+
+            drawBar( painter, index, i, barRect );
+        }
+    }
+    else
+    {
+        const double barHeight = sampleWidth / numBars;
+
+        const double x1 = xMap.transform( baseline() );
+        const double y0 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
+
+        for ( int i = 0; i < numBars; i++ )
+        {
+            double y1 = y0 + i * barHeight;
+            double y2 = y1 + barHeight;
+
+            double x2 = xMap.transform( sample.set[i] );
+
+            QwtColumnRect barRect;
+            barRect.direction = x1 < x2 ?
+                QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+
+            barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+
+            barRect.vInterval = QwtInterval( y1, y2 );
+            if ( i != 0 )
+                barRect.vInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
+
+            drawBar( painter, index, i, barRect );
+        }
+    }
+}
+
+/*!
+  Draw a stacked sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param index Index of the sample to be painted
+  \param sampleWidth Width of the bars
+  \param sample Sample 
+
+  \sa drawSeries(), sampleWidth()
+*/
+void QwtPlotMultiBarChart::drawStackedBars( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int index, 
+    double sampleWidth, const QwtSetSample& sample ) const
+{
+    Q_UNUSED( canvasRect ); // clipping the bars ?
+
+    const int numBars = sample.set.size();
+    if ( numBars == 0 )
+        return;
+
+    QwtInterval::BorderFlag borderFlags = QwtInterval::IncludeBorders;
+
+    if ( orientation() == Qt::Vertical )
+    {
+        const double x1 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
+        const double x2 = x1 + sampleWidth;
+
+        const bool increasing = qwtIsIncreasing( yMap, sample.set );
+
+        QwtColumnRect bar;
+        bar.direction = increasing ?
+            QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+        bar.hInterval = QwtInterval( x1, x2 ).normalized();
+
+        double sum = baseline();
+
+        const int numBars = sample.set.size();
+        for ( int i = 0; i < numBars; i++ )
+        {
+            const double si = sample.set[ i ];
+            if ( si == 0.0 )
+                continue;
+
+            const double y1 = yMap.transform( sum );
+            const double y2 = yMap.transform( sum + si );
+
+            if ( ( y2 > y1 ) != increasing )
+            {
+                // stacked bars need to be in the same direction
+                continue;
+            }
+
+            bar.vInterval = QwtInterval( y1, y2 ).normalized();
+            bar.vInterval.setBorderFlags( borderFlags );
+
+            drawBar( painter, index, i, bar );
+
+            sum += si;
+
+            if ( increasing )
+                borderFlags = QwtInterval::ExcludeMinimum;
+            else
+                borderFlags = QwtInterval::ExcludeMaximum;
+        }
+    }
+    else
+    {
+        const double y1 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
+        const double y2 = y1 + sampleWidth;
+
+        const bool increasing = qwtIsIncreasing( xMap, sample.set );
+
+        QwtColumnRect bar;
+        bar.direction = increasing ?
+            QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+        bar.vInterval = QwtInterval( y1, y2 ).normalized();
+
+        double sum = baseline();
+
+        for ( int i = 0; i < sample.set.size(); i++ )
+        {
+            const double si = sample.set[ i ];
+            if ( si == 0.0 )
+                continue;
+
+            const double x1 = xMap.transform( sum );
+            const double x2 = xMap.transform( sum + si );
+
+            if ( ( x2 > x1 ) != increasing )
+            {
+                // stacked bars need to be in the same direction
+                continue;
+            }
+
+            bar.hInterval = QwtInterval( x1, x2 ).normalized();
+            bar.hInterval.setBorderFlags( borderFlags );
+
+            drawBar( painter, index, i, bar );
+
+            sum += si;
+
+            if ( increasing )
+                borderFlags = QwtInterval::ExcludeMinimum;
+            else
+                borderFlags = QwtInterval::ExcludeMaximum;
+        }
+    }
+}
+
+/*!
+  Draw a bar
+
+  \param painter Painter
+  \param sampleIndex Index of the sample - might be -1 when the
+                     bar is painted for the legend
+  \param valueIndex Index of a value in a set
+  \param rect Directed target rectangle for the bar
+
+  \sa drawSeries()
+*/
+void QwtPlotMultiBarChart::drawBar( QPainter *painter,
+    int sampleIndex, int valueIndex, const QwtColumnRect &rect ) const
+{
+    const QwtColumnSymbol *specialSym = NULL;
+    if ( sampleIndex >= 0 )
+        specialSym = specialSymbol( sampleIndex, valueIndex );
+
+    const QwtColumnSymbol *sym = specialSym;
+    if ( sym == NULL )
+        sym = symbol( valueIndex );
+
+    if ( sym )
+    {
+        sym->draw( painter, rect );
+    }
+    else
+    {
+        // we build a temporary default symbol
+        QwtColumnSymbol sym( QwtColumnSymbol::Box );
+        sym.setLineWidth( 1 );
+        sym.setFrameStyle( QwtColumnSymbol::Plain );
+        sym.draw( painter, rect );
+    }
+
+    delete specialSym;
+}
+
+/*!
+  \return Information to be displayed on the legend
+
+  The chart is represented by a list of entries - one for each bar title.
+  Each element contains a bar title and an icon showing its corresponding bar.
+
+  \sa barTitles(), legendIcon(), legendIconSize()
+*/
+QList<QwtLegendData> QwtPlotMultiBarChart::legendData() const
+{
+    QList<QwtLegendData> list;
+
+    for ( int i = 0; i < d_data->barTitles.size(); i++ )
+    {
+        QwtLegendData data;
+
+        QVariant titleValue;
+        qVariantSetValue( titleValue, d_data->barTitles[i] );
+        data.setValue( QwtLegendData::TitleRole, titleValue );
+
+        if ( !legendIconSize().isEmpty() )
+        {
+            QVariant iconValue;
+            qVariantSetValue( iconValue, 
+                legendIcon( i, legendIconSize() ) );
+
+            data.setValue( QwtLegendData::IconRole, iconValue );
+        }
+
+        list += data;
+    }
+
+    return list;
+}
+
+/*!
+  \return Icon for representing a bar on the legend
+
+  \param index Index of the bar
+  \param size Icon size
+  
+  \return An icon showing a bar
+  \sa drawBar(), legendData()
+ */
+QwtGraphic QwtPlotMultiBarChart::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    QwtColumnRect column;
+    column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
+    column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    drawBar( &painter, -1, index, column );
+
+    return icon;
+}
+
diff --git a/qwt/qwt_plot_multi_barchart.h b/qwt/qwt_plot_multi_barchart.h
new file mode 100644
index 0000000..03e3db5
--- /dev/null
+++ b/qwt/qwt_plot_multi_barchart.h
@@ -0,0 +1,127 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_BAR_CHART_H
+#define QWT_PLOT_BAR_CHART_H
+
+#include "qwt_global.h"
+#include "qwt_plot_abstract_barchart.h"
+#include "qwt_series_data.h"
+
+class QwtColumnRect;
+class QwtColumnSymbol;
+
+/*!
+  \brief QwtPlotMultiBarChart displays a series of a samples that consist
+         each of a set of values. 
+
+  Each value is displayed as a bar, the bars of each set can be organized 
+  side by side or accumulated.
+
+  Each bar of a set is rendered by a QwtColumnSymbol, that is set by setSymbol().
+  The bars of different sets use the same symbols. Exceptions are possible
+  by overloading specialSymbol() or overloading drawBar().
+
+  Depending on its orientation() the bars are displayed horizontally 
+  or vertically. The bars cover the interval between the baseline() 
+  and the value.
+
+  In opposite to most other plot items, QwtPlotMultiBarChart returns more
+  than one entry for the legend - one for each symbol.
+   
+  \sa QwtPlotBarChart, QwtPlotHistogram
+      QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline()
+ */
+class QWT_EXPORT QwtPlotMultiBarChart: 
+    public QwtPlotAbstractBarChart, public QwtSeriesStore<QwtSetSample>
+{
+public:
+    /*!
+        \brief Chart styles.
+
+        The default setting is QwtPlotMultiBarChart::Grouped.
+        \sa setStyle(), style()
+    */
+    enum ChartStyle
+    {
+        //! The bars of a set are displayed side by side
+        Grouped,
+
+        /*!
+            The bars are displayed on top of each other accumulating
+            to a single bar. All values of a set need to have the same
+            sign.
+         */
+        Stacked
+    };
+
+    explicit QwtPlotMultiBarChart( const QString &title = QString::null );
+    explicit QwtPlotMultiBarChart( const QwtText &title );
+
+    virtual ~QwtPlotMultiBarChart();
+
+    virtual int rtti() const;
+
+    void setBarTitles( const QList<QwtText> & );
+    QList<QwtText> barTitles() const;
+
+    void setSamples( const QVector<QwtSetSample> & );
+    void setSamples( const QVector< QVector<double> > & );
+    void setSamples( QwtSeriesData<QwtSetSample> * );
+
+    void setStyle( ChartStyle style );
+    ChartStyle style() const;
+
+    void setSymbol( int barIndex, QwtColumnSymbol *symbol );
+    const QwtColumnSymbol *symbol( int barIndex ) const;
+
+    void resetSymbolMap();
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QList<QwtLegendData> legendData() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    QwtColumnSymbol *symbol( int barIndex );
+
+    virtual QwtColumnSymbol *specialSymbol( 
+        int sampleIndex, int valueIndex ) const;
+
+    virtual void drawSample( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, const QwtInterval &boundingInterval,
+        int index, const QwtSetSample& sample ) const;
+
+    virtual void drawBar( QPainter *, int sampleIndex,
+        int barIndex, const QwtColumnRect & ) const;
+
+    void drawStackedBars( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int index,
+        double sampleWidth, const QwtSetSample& sample ) const;
+
+    void drawGroupedBars( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int index,
+        double sampleWidth, const QwtSetSample& sample ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_panner.cpp b/qwt/qwt_plot_panner.cpp
new file mode 100644
index 0000000..62a75a2
--- /dev/null
+++ b/qwt/qwt_plot_panner.cpp
@@ -0,0 +1,275 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_panner.h"
+#include "qwt_scale_div.h"
+#include "qwt_plot.h"
+#include "qwt_painter.h"
+#include <qbitmap.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+static QBitmap qwtBorderMask( const QWidget *canvas, const QSize &size )
+{
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainterPath borderPath;
+
+    ( void )QMetaObject::invokeMethod( 
+        const_cast< QWidget *>( canvas ), "borderPath", Qt::DirectConnection,
+        Q_RETURN_ARG( QPainterPath, borderPath ), Q_ARG( QRect, r ) );
+
+    if ( borderPath.isEmpty() )
+    {
+        if ( canvas->contentsRect() == canvas->rect() )
+            return QBitmap();
+
+        QBitmap mask( size );
+        mask.fill( Qt::color0 );
+
+        QPainter painter( &mask );
+        painter.fillRect( canvas->contentsRect(), Qt::color1 );
+
+        return mask;
+    }
+
+    QImage image( size, QImage::Format_ARGB32_Premultiplied );
+    image.fill( Qt::color0 );
+
+    QPainter painter( &image );
+    painter.setClipPath( borderPath );
+    painter.fillRect( r, Qt::color1 );
+
+    // now erase the frame
+
+    painter.setCompositionMode( QPainter::CompositionMode_DestinationOut );
+
+    if ( canvas->testAttribute(Qt::WA_StyledBackground ) )
+    {
+        QStyleOptionFrame opt;
+        opt.initFrom(canvas);
+        opt.rect = r;
+        canvas->style()->drawPrimitive( QStyle::PE_Frame, &opt, &painter, canvas );
+    }
+    else
+    {
+        const QVariant borderRadius = canvas->property( "borderRadius" );
+        const QVariant frameWidth = canvas->property( "frameWidth" );
+
+        if ( borderRadius.type() == QVariant::Double 
+            && frameWidth.type() == QVariant::Int )
+        {
+            const double br = borderRadius.toDouble();
+            const int fw = frameWidth.toInt();
+        
+            if ( br > 0.0 && fw > 0 )
+            {
+                painter.setPen( QPen( Qt::color1, fw ) );
+                painter.setBrush( Qt::NoBrush );
+                painter.setRenderHint( QPainter::Antialiasing, true );
+
+                painter.drawPath( borderPath );
+            }
+        }
+    }
+
+    painter.end();
+
+    const QImage mask = image.createMaskFromColor(
+        QColor( Qt::color1 ).rgb(), Qt::MaskOutColor );
+
+    return QBitmap::fromImage( mask );
+}
+
+class QwtPlotPanner::PrivateData
+{
+public:
+    PrivateData()
+    {
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            isAxisEnabled[axis] = true;
+    }
+
+    bool isAxisEnabled[QwtPlot::axisCnt];
+};
+
+/*!
+  \brief A panner for the canvas of a QwtPlot
+
+  The panner is enabled for all axes
+
+  \param canvas Plot canvas to pan, also the parent object
+
+  \sa setAxisEnabled()
+*/
+QwtPlotPanner::QwtPlotPanner( QWidget *canvas ):
+    QwtPanner( canvas )
+{
+    d_data = new PrivateData();
+
+    connect( this, SIGNAL( panned( int, int ) ),
+        SLOT( moveCanvas( int, int ) ) );
+}
+
+//! Destructor
+QwtPlotPanner::~QwtPlotPanner()
+{
+    delete d_data;
+}
+
+/*!
+   \brief En/Disable an axis
+
+   Axes that are enabled will be synchronized to the
+   result of panning. All other axes will remain unchanged.
+
+   \param axis Axis, see QwtPlot::Axis
+   \param on On/Off
+
+   \sa isAxisEnabled(), moveCanvas()
+*/
+void QwtPlotPanner::setAxisEnabled( int axis, bool on )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->isAxisEnabled[axis] = on;
+}
+
+/*!
+   Test if an axis is enabled
+
+   \param axis Axis, see QwtPlot::Axis
+   \return True, if the axis is enabled
+
+   \sa setAxisEnabled(), moveCanvas()
+*/
+bool QwtPlotPanner::isAxisEnabled( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->isAxisEnabled[axis];
+
+    return true;
+}
+
+//! Return observed plot canvas
+QWidget *QwtPlotPanner::canvas()
+{
+    return parentWidget();
+}
+
+//! Return Observed plot canvas
+const QWidget *QwtPlotPanner::canvas() const
+{
+    return parentWidget();
+}
+
+//! Return plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotPanner::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! Return plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotPanner::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+   Adjust the enabled axes according to dx/dy
+
+   \param dx Pixel offset in x direction
+   \param dy Pixel offset in y direction
+
+   \sa QwtPanner::panned()
+*/
+void QwtPlotPanner::moveCanvas( int dx, int dy )
+{
+    if ( dx == 0 && dy == 0 )
+        return;
+
+    QwtPlot *plot = this->plot();
+    if ( plot == NULL )
+        return;
+
+    const bool doAutoReplot = plot->autoReplot();
+    plot->setAutoReplot( false );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( !d_data->isAxisEnabled[axis] )
+            continue;
+
+        const QwtScaleMap map = plot->canvasMap( axis );
+
+        const double p1 = map.transform( plot->axisScaleDiv( axis ).lowerBound() );
+        const double p2 = map.transform( plot->axisScaleDiv( axis ).upperBound() );
+
+        double d1, d2;
+        if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+        {
+            d1 = map.invTransform( p1 - dx );
+            d2 = map.invTransform( p2 - dx );
+        }
+        else
+        {
+            d1 = map.invTransform( p1 - dy );
+            d2 = map.invTransform( p2 - dy );
+        }
+
+        plot->setAxisScale( axis, d1, d2 );
+    }
+
+    plot->setAutoReplot( doAutoReplot );
+    plot->replot();
+}
+
+/*!
+   Calculate a mask from the border path of the canvas
+
+   \return Mask as bitmap
+   \sa QwtPlotCanvas::borderPath()
+*/
+QBitmap QwtPlotPanner::contentsMask() const
+{
+    if ( canvas() )
+        return qwtBorderMask( canvas(), size() );
+
+    return QwtPanner::contentsMask();
+}
+
+/*!
+   \return Pixmap with the content of the canvas
+ */
+QPixmap QwtPlotPanner::grab() const
+{   
+    const QWidget *cv = canvas();
+    if ( cv && cv->inherits( "QGLWidget" ) )
+    {
+        // we can't grab from a QGLWidget
+
+        QPixmap pm( cv->size() );
+        QwtPainter::fillPixmap( cv, pm );
+
+        QPainter painter( &pm );
+        const_cast<QwtPlot *>( plot() )->drawCanvas( &painter );
+
+        return pm;
+    }
+
+    return QwtPanner::grab();
+}   
+
diff --git a/qwt/qwt_plot_panner.h b/qwt/qwt_plot_panner.h
new file mode 100644
index 0000000..517c23d
--- /dev/null
+++ b/qwt/qwt_plot_panner.h
@@ -0,0 +1,60 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_PANNER_H
+#define QWT_PLOT_PANNER_H 1
+
+#include "qwt_global.h"
+#include "qwt_panner.h"
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotPanner provides panning of a plot canvas
+
+  QwtPlotPanner is a panner for a plot canvas, that
+  adjusts the scales of the axes after dropping
+  the canvas on its new position.
+
+  Together with QwtPlotZoomer and QwtPlotMagnifier powerful ways
+  of navigating on a QwtPlot widget can be implemented easily.
+
+  \note The axes are not updated, while dragging the canvas
+  \sa QwtPlotZoomer, QwtPlotMagnifier
+*/
+class QWT_EXPORT QwtPlotPanner: public QwtPanner
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotPanner( QWidget * );
+    virtual ~QwtPlotPanner();
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    void setAxisEnabled( int axis, bool on );
+    bool isAxisEnabled( int axis ) const;
+
+protected Q_SLOTS:
+    virtual void moveCanvas( int dx, int dy );
+
+protected:
+    virtual QBitmap contentsMask() const;
+    virtual QPixmap grab() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_picker.cpp b/qwt/qwt_plot_picker.cpp
new file mode 100644
index 0000000..1c0733d
--- /dev/null
+++ b/qwt/qwt_plot_picker.cpp
@@ -0,0 +1,378 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_picker.h"
+#include "qwt_plot.h"
+#include "qwt_scale_div.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include "qwt_picker_machine.h"
+
+/*!
+  \brief Create a plot picker
+
+  The picker is set to those x- and y-axis of the plot
+  that are enabled. If both or no x-axis are enabled, the picker
+  is set to QwtPlot::xBottom. If both or no y-axis are
+  enabled, it is set to QwtPlot::yLeft.
+
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+
+QwtPlotPicker::QwtPlotPicker( QWidget *canvas ):
+    QwtPicker( canvas ),
+    d_xAxis( -1 ),
+    d_yAxis( -1 )
+{
+    if ( !canvas )
+        return;
+
+    // attach axes
+
+    int xAxis = QwtPlot::xBottom;
+
+    const QwtPlot *plot = QwtPlotPicker::plot();
+    if ( !plot->axisEnabled( QwtPlot::xBottom ) &&
+        plot->axisEnabled( QwtPlot::xTop ) )
+    {
+        xAxis = QwtPlot::xTop;
+    }
+
+    int yAxis = QwtPlot::yLeft;
+    if ( !plot->axisEnabled( QwtPlot::yLeft ) &&
+        plot->axisEnabled( QwtPlot::yRight ) )
+    {
+        yAxis = QwtPlot::yRight;
+    }
+
+    setAxis( xAxis, yAxis );
+}
+
+/*!
+  Create a plot picker
+
+  \param xAxis Set the x axis of the picker
+  \param yAxis Set the y axis of the picker
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis, QWidget *canvas ):
+    QwtPicker( canvas ),
+    d_xAxis( xAxis ),
+    d_yAxis( yAxis )
+{
+}
+
+/*!
+  Create a plot picker
+
+  \param xAxis X axis of the picker
+  \param yAxis Y axis of the picker
+  \param rubberBand Rubber band style
+  \param trackerMode Tracker mode
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPicker, QwtPicker::setSelectionFlags(), QwtPicker::setRubberBand(),
+      QwtPicker::setTrackerMode
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis,
+        RubberBand rubberBand, DisplayMode trackerMode,
+        QWidget *canvas ):
+    QwtPicker( rubberBand, trackerMode, canvas ),
+    d_xAxis( xAxis ),
+    d_yAxis( yAxis )
+{
+}
+
+//! Destructor
+QwtPlotPicker::~QwtPlotPicker()
+{
+}
+
+//! \return Observed plot canvas
+QWidget *QwtPlotPicker::canvas()
+{
+    return parentWidget();
+}
+
+//! \return Observed plot canvas
+const QWidget *QwtPlotPicker::canvas() const
+{
+    return parentWidget();
+}
+
+//! \return Plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotPicker::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! \return Plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotPicker::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+  \return Normalized bounding rectangle of the axes
+  \sa QwtPlot::autoReplot(), QwtPlot::replot().
+*/
+QRectF QwtPlotPicker::scaleRect() const
+{
+    QRectF rect;
+
+    if ( plot() )
+    {
+        const QwtScaleDiv &xs = plot()->axisScaleDiv( xAxis() );
+        const QwtScaleDiv &ys = plot()->axisScaleDiv( yAxis() );
+
+        rect = QRectF( xs.lowerBound(), ys.lowerBound(),
+            xs.range(), ys.range() );
+        rect = rect.normalized();
+    }
+
+    return rect;
+}
+
+/*!
+  Set the x and y axes of the picker
+
+  \param xAxis X axis
+  \param yAxis Y axis
+*/
+void QwtPlotPicker::setAxis( int xAxis, int yAxis )
+{
+    const QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    if ( xAxis != d_xAxis || yAxis != d_yAxis )
+    {
+        d_xAxis = xAxis;
+        d_yAxis = yAxis;
+    }
+}
+
+//! Return x axis
+int QwtPlotPicker::xAxis() const
+{
+    return d_xAxis;
+}
+
+//! Return y axis
+int QwtPlotPicker::yAxis() const
+{
+    return d_yAxis;
+}
+
+/*!
+  Translate a pixel position into a position string
+
+  \param pos Position in pixel coordinates
+  \return Position string
+*/
+QwtText QwtPlotPicker::trackerText( const QPoint &pos ) const
+{
+    return trackerTextF( invTransform( pos ) );
+}
+
+/*!
+  \brief Translate a position into a position string
+
+  In case of HLineRubberBand the label is the value of the
+  y position, in case of VLineRubberBand the value of the x position.
+  Otherwise the label contains x and y position separated by a ',' .
+
+  The format for the double to string conversion is "%.4f".
+
+  \param pos Position
+  \return Position string
+*/
+QwtText QwtPlotPicker::trackerTextF( const QPointF &pos ) const
+{
+    QString text;
+
+    switch ( rubberBand() )
+    {
+        case HLineRubberBand:
+            text.sprintf( "%.4f", pos.y() );
+            break;
+        case VLineRubberBand:
+            text.sprintf( "%.4f", pos.x() );
+            break;
+        default:
+            text.sprintf( "%.4f, %.4f", pos.x(), pos.y() );
+    }
+    return QwtText( text );
+}
+
+/*!
+  Append a point to the selection and update rubber band and tracker.
+
+  \param pos Additional point
+  \sa isActive, begin(), end(), move(), appended()
+
+  \note The appended(const QPoint &), appended(const QDoublePoint &)
+        signals are emitted.
+*/
+void QwtPlotPicker::append( const QPoint &pos )
+{
+    QwtPicker::append( pos );
+    Q_EMIT appended( invTransform( pos ) );
+}
+
+/*!
+  Move the last point of the selection
+
+  \param pos New position
+  \sa isActive, begin(), end(), append()
+
+  \note The moved(const QPoint &), moved(const QDoublePoint &)
+        signals are emitted.
+*/
+void QwtPlotPicker::move( const QPoint &pos )
+{
+    QwtPicker::move( pos );
+    Q_EMIT moved( invTransform( pos ) );
+}
+
+/*!
+  Close a selection setting the state to inactive.
+
+  \param ok If true, complete the selection and emit selected signals
+            otherwise discard the selection.
+  \return True if the selection has been accepted, false otherwise
+*/
+
+bool QwtPlotPicker::end( bool ok )
+{
+    ok = QwtPicker::end( ok );
+    if ( !ok )
+        return false;
+
+    QwtPlot *plot = QwtPlotPicker::plot();
+    if ( !plot )
+        return false;
+
+    const QPolygon points = selection();
+    if ( points.count() == 0 )
+        return false;
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( stateMachine() )
+        selectionType = stateMachine()->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::PointSelection:
+        {
+            const QPointF pos = invTransform( points.first() );
+            Q_EMIT selected( pos );
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( points.count() >= 2 )
+            {
+                const QPoint p1 = points.first();
+                const QPoint p2 = points.last();
+
+                const QRect rect = QRect( p1, p2 ).normalized();
+                Q_EMIT selected( invTransform( rect ) );
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            QVector<QPointF> dpa( points.count() );
+            for ( int i = 0; i < points.count(); i++ )
+                dpa[i] = invTransform( points[i] );
+
+            Q_EMIT selected( dpa );
+        }
+        default:
+            break;
+    }
+
+    return true;
+}
+
+/*!
+    Translate a rectangle from pixel into plot coordinates
+
+    \return Rectangle in plot coordinates
+    \sa transform()
+*/
+QRectF QwtPlotPicker::invTransform( const QRect &rect ) const
+{
+    const QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    const QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QwtScaleMap::invTransform( xMap, yMap, rect );
+}
+
+/*!
+    Translate a rectangle from plot into pixel coordinates
+    \return Rectangle in pixel coordinates
+    \sa invTransform()
+*/
+QRect QwtPlotPicker::transform( const QRectF &rect ) const
+{
+    const QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    const QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QwtScaleMap::transform( xMap, yMap, rect ).toRect();
+}
+
+/*!
+    Translate a point from pixel into plot coordinates
+    \return Point in plot coordinates
+    \sa transform()
+*/
+QPointF QwtPlotPicker::invTransform( const QPoint &pos ) const
+{
+    QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QPointF(
+        xMap.invTransform( pos.x() ),
+        yMap.invTransform( pos.y() )
+    );
+}
+
+/*!
+    Translate a point from plot into pixel coordinates
+    \return Point in pixel coordinates
+    \sa invTransform()
+*/
+QPoint QwtPlotPicker::transform( const QPointF &pos ) const
+{
+    QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    const QPointF p( xMap.transform( pos.x() ),
+        yMap.transform( pos.y() ) );
+
+    return p.toPoint();
+}
diff --git a/qwt/qwt_plot_picker.h b/qwt/qwt_plot_picker.h
new file mode 100644
index 0000000..a6fe366
--- /dev/null
+++ b/qwt/qwt_plot_picker.h
@@ -0,0 +1,111 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_PICKER_H
+#define QWT_PLOT_PICKER_H
+
+#include "qwt_global.h"
+#include "qwt_picker.h"
+#include <qvector.h>
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotPicker provides selections on a plot canvas
+
+  QwtPlotPicker is a QwtPicker tailored for selections on
+  a plot canvas. It is set to a x-Axis and y-Axis and
+  translates all pixel coordinates into this coordinate system.
+*/
+
+class QWT_EXPORT QwtPlotPicker: public QwtPicker
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotPicker( QWidget *canvas );
+    virtual ~QwtPlotPicker();
+
+    explicit QwtPlotPicker( int xAxis, int yAxis, QWidget * );
+
+    explicit QwtPlotPicker( int xAxis, int yAxis,
+        RubberBand rubberBand, DisplayMode trackerMode, QWidget * );
+
+    virtual void setAxis( int xAxis, int yAxis );
+
+    int xAxis() const;
+    int yAxis() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+Q_SIGNALS:
+
+    /*!
+      A signal emitted in case of QwtPickerMachine::PointSelection.
+      \param pos Selected point
+    */
+    void selected( const QPointF &pos );
+
+    /*!
+      A signal emitted in case of QwtPickerMachine::RectSelection.
+      \param rect Selected rectangle
+    */
+    void selected( const QRectF &rect );
+
+    /*!
+      A signal emitting the selected points,
+      at the end of a selection.
+
+      \param pa Selected points
+    */
+    void selected( const QVector<QPointF> &pa );
+
+    /*!
+      A signal emitted when a point has been appended to the selection
+
+      \param pos Position of the appended point.
+      \sa append(). moved()
+    */
+    void appended( const QPointF &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been moved.
+
+      \param pos Position of the moved last point of the selection.
+      \sa move(), appended()
+    */
+    void moved( const QPointF &pos );
+
+protected:
+    QRectF scaleRect() const;
+
+    QRectF invTransform( const QRect & ) const;
+    QRect transform( const QRectF & ) const;
+
+    QPointF invTransform( const QPoint & ) const;
+    QPoint transform( const QPointF & ) const;
+
+    virtual QwtText trackerText( const QPoint & ) const;
+    virtual QwtText trackerTextF( const QPointF & ) const;
+
+    virtual void move( const QPoint & );
+    virtual void append( const QPoint & );
+    virtual bool end( bool ok = true );
+
+private:
+    int d_xAxis;
+    int d_yAxis;
+};
+
+#endif
diff --git a/qwt/qwt_plot_rasteritem.cpp b/qwt/qwt_plot_rasteritem.cpp
new file mode 100644
index 0000000..e12d7c7
--- /dev/null
+++ b/qwt/qwt_plot_rasteritem.cpp
@@ -0,0 +1,946 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_rasteritem.h"
+#include "qwt_scale_map.h"
+#include "qwt_painter.h"
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+#if QT_VERSION >= 0x040400
+#include <qthread.h>
+#include <qfuture.h>
+#include <qtconcurrentrun.h>
+#endif
+#include <float.h>
+
+class QwtPlotRasterItem::PrivateData
+{
+public:
+    PrivateData():
+        alpha( -1 ),
+        paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution )
+    {
+        cache.policy = QwtPlotRasterItem::NoCache;
+    }
+
+    int alpha;
+
+    QwtPlotRasterItem::PaintAttributes paintAttributes;
+
+    struct ImageCache
+    {
+        QwtPlotRasterItem::CachePolicy policy;
+        QRectF area;
+        QSizeF size;
+        QImage image;
+    } cache;
+};
+
+
+static QRectF qwtAlignRect(const QRectF &rect)
+{
+    QRectF r;
+    r.setLeft( qRound( rect.left() ) );
+    r.setRight( qRound( rect.right() ) );
+    r.setTop( qRound( rect.top() ) );
+    r.setBottom( qRound( rect.bottom() ) );
+
+    return r;
+}
+
+static QRectF qwtStripRect(const QRectF &rect, const QRectF &area,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtInterval &xInterval, const QwtInterval &yInterval)
+{
+    QRectF r = rect;
+    if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+    {
+        if ( area.left() <= xInterval.minValue() )
+        {
+            if ( xMap.isInverting() )
+                r.adjust(0, 0, -1, 0);
+            else
+                r.adjust(1, 0, 0, 0);
+        }
+    }
+
+    if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+    {
+        if ( area.right() >= xInterval.maxValue() )
+        {
+            if ( xMap.isInverting() )
+                r.adjust(1, 0, 0, 0);
+            else
+                r.adjust(0, 0, -1, 0);
+        }
+    }
+
+    if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+    {
+        if ( area.top() <= yInterval.minValue() )
+        {
+            if ( yMap.isInverting() )
+                r.adjust(0, 0, 0, -1);
+            else
+                r.adjust(0, 1, 0, 0);
+        }
+    }
+
+    if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+    {
+        if ( area.bottom() >= yInterval.maxValue() )
+        {
+            if ( yMap.isInverting() )
+                r.adjust(0, 1, 0, 0);
+            else
+                r.adjust(0, 0, 0, -1);
+        }
+    }
+
+    return r;
+}
+
+static QImage qwtExpandImage(const QImage &image,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &area, const QRectF &area2, const QRectF &paintRect,
+    const QwtInterval &xInterval, const QwtInterval &yInterval )
+{
+    const QRectF strippedRect = qwtStripRect(paintRect, area2,
+        xMap, yMap, xInterval, yInterval);
+    const QSize sz = strippedRect.toRect().size();
+
+    const int w = image.width();
+    const int h = image.height();
+
+    const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized();
+    const double pw = ( r.width() - 1) / w;
+    const double ph = ( r.height() - 1) / h;
+
+    double px0, py0;
+    if ( !xMap.isInverting() )
+    {
+        px0 = xMap.transform( area2.left() );
+        px0 = qRound( px0 );
+        px0 = px0 - xMap.transform( area.left() );
+    }
+    else
+    {
+        px0 = xMap.transform( area2.right() );
+        px0 = qRound( px0 );
+        px0 -= xMap.transform( area.right() );
+
+        px0 -= 1.0;
+    }
+    px0 += strippedRect.left() - paintRect.left();
+
+    if ( !yMap.isInverting() )
+    {
+        py0 = yMap.transform( area2.top() );
+        py0 = qRound( py0 );
+        py0 -= yMap.transform( area.top() );
+    }
+    else
+    {
+        py0 = yMap.transform( area2.bottom() );
+        py0 = qRound( py0 );
+        py0 -= yMap.transform( area.bottom() );
+
+        py0 -= 1.0;
+    }
+    py0 += strippedRect.top() - paintRect.top();
+
+    QImage expanded(sz, image.format());
+
+    switch( image.depth() )
+    {
+        case 32:
+        {
+            for ( int y1 = 0; y1 < h; y1++ )
+            {
+                int yy1;
+                if ( y1 == 0 )
+                {
+                    yy1 = 0;
+                }
+                else
+                {
+                    yy1 = qRound( y1 * ph - py0 );
+                    if ( yy1 < 0 )
+                        yy1 = 0;
+                }
+
+                int yy2;
+                if ( y1 == h - 1 )
+                {
+                    yy2 = sz.height();
+                }
+                else
+                {
+                    yy2 = qRound( ( y1 + 1 ) * ph - py0 );
+                    if ( yy2 > sz.height() )
+                        yy2 = sz.height();
+                }
+
+                const quint32 *line1 = 
+                    reinterpret_cast<const quint32 *>( image.scanLine( y1 ) );
+
+                for ( int x1 = 0; x1 < w; x1++ )
+                {
+                    int xx1;
+                    if ( x1 == 0 )
+                    {
+                        xx1 = 0;
+                    }
+                    else
+                    {
+                        xx1 = qRound( x1 * pw - px0 );
+                        if ( xx1 < 0 )
+                            xx1 = 0;
+                    }
+
+                    int xx2;
+                    if ( x1 == w - 1 )
+                    {
+                        xx2 = sz.width();
+                    }
+                    else
+                    {
+                        xx2 = qRound( ( x1 + 1 ) * pw - px0 );
+                        if ( xx2 > sz.width() )
+                            xx2 = sz.width();
+                    }
+
+                    const quint32 rgb( line1[x1] );
+                    for ( int y2 = yy1; y2 < yy2; y2++ )
+                    {
+                        quint32 *line2 = reinterpret_cast<quint32 *>( 
+                            expanded.scanLine( y2 ) );
+
+                        for ( int x2 = xx1; x2 < xx2; x2++ ) 
+                            line2[x2] = rgb;
+                    }       
+                }   
+            }   
+            break;
+        }
+        case 8:
+        {
+            for ( int y1 = 0; y1 < h; y1++ )
+            {
+                int yy1;
+                if ( y1 == 0 )
+                {
+                    yy1 = 0;
+                }   
+                else
+                {
+                    yy1 = qRound( y1 * ph - py0 );
+                    if ( yy1 < 0 )
+                        yy1 = 0; 
+                }       
+                
+                int yy2;
+                if ( y1 == h - 1 )
+                {
+                    yy2 = sz.height();
+                }   
+                else
+                {
+                    yy2 = qRound( ( y1 + 1 ) * ph - py0 );
+                    if ( yy2 > sz.height() )
+                        yy2 = sz.height();
+                }
+    
+                const uchar *line1 = image.scanLine( y1 );
+
+                for ( int x1 = 0; x1 < w; x1++ )
+                {
+                    int xx1;
+                    if ( x1 == 0 )
+                    {
+                        xx1 = 0;
+                    }
+                    else
+                    {
+                        xx1 = qRound( x1 * pw - px0 );
+                        if ( xx1 < 0 )
+                            xx1 = 0;
+                    }
+
+                    int xx2;
+                    if ( x1 == w - 1 )
+                    {
+                        xx2 = sz.width();
+                    }
+                    else
+                    {
+                        xx2 = qRound( ( x1 + 1 ) * pw - px0 );
+                        if ( xx2 > sz.width() )
+                            xx2 = sz.width();
+                    }
+
+                    for ( int y2 = yy1; y2 < yy2; y2++ )
+                    {
+                        uchar *line2 = expanded.scanLine( y2 );
+                        memset( line2 + xx1, line1[x1], xx2 - xx1 );
+                    }       
+                }   
+            }
+            break;
+        }
+        default:
+            expanded = image;
+    }
+    
+    return expanded;
+}   
+
+static QRectF qwtExpandToPixels(const QRectF &rect, const QRectF &pixelRect)
+{
+    const double pw = pixelRect.width();
+    const double ph = pixelRect.height();
+
+    const double dx1 = pixelRect.left() - rect.left();
+    const double dx2 = pixelRect.right() - rect.right();
+    const double dy1 = pixelRect.top() - rect.top();
+    const double dy2 = pixelRect.bottom() - rect.bottom();
+
+    QRectF r;
+    r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw );
+    r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph );
+    r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw );
+    r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph );
+
+    return r;
+}
+
+static void qwtTransformMaps( const QTransform &tr,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    QwtScaleMap &xxMap, QwtScaleMap &yyMap )
+{
+    const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) );
+    const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) );
+
+    xxMap = xMap;
+    xxMap.setPaintInterval( p1.x(), p2.x() );
+
+    yyMap = yMap;
+    yyMap.setPaintInterval( p1.y(), p2.y() );
+}
+
+static void qwtAdjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
+    const QRectF &area, const QRectF &paintRect)
+{
+    double sx1 = area.left();
+    double sx2 = area.right();
+    if ( xMap.isInverting() )
+        qSwap(sx1, sx2);
+
+    double sy1 = area.top();
+    double sy2 = area.bottom();
+
+    if ( yMap.isInverting() )
+        qSwap(sy1, sy2);
+
+    xMap.setPaintInterval(paintRect.left(), paintRect.right());
+    xMap.setScaleInterval(sx1, sx2);
+
+    yMap.setPaintInterval(paintRect.top(), paintRect.bottom());
+    yMap.setScaleInterval(sy1, sy2);
+}
+
+static bool qwtUseCache( QwtPlotRasterItem::CachePolicy policy,
+    const QPainter *painter )
+{
+    bool doCache = false;
+
+    if ( policy == QwtPlotRasterItem::PaintCache )
+    {
+        // Caching doesn't make sense, when the item is
+        // not painted to screen
+
+        switch ( painter->paintEngine()->type() )
+        {
+            case QPaintEngine::SVG:
+            case QPaintEngine::Pdf:
+            case QPaintEngine::PostScript:
+            case QPaintEngine::MacPrinter:
+            case QPaintEngine::Picture:
+                break;
+            default:;
+                doCache = true;
+        }
+    }
+
+    return doCache;
+}
+
+static void qwtToRgba( const QImage* from, QImage* to,  
+    const QRect& tile, int alpha )
+{
+    const QRgb mask1 = qRgba( 0, 0, 0, alpha );
+    const QRgb mask2 = qRgba( 255, 255, 255, 0 );
+    const QRgb mask3 = qRgba( 0, 0, 0, 255 );
+
+    const int y0 = tile.top();
+    const int y1 = tile.bottom();
+    const int x0 = tile.left();
+    const int x1 = tile.right();
+
+    if ( from->depth() == 8 )
+    {
+        for ( int y = y0; y <= y1; y++ )
+        {
+            QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
+            const unsigned char *line = from->scanLine( y );
+
+            for ( int x = x0; x <= x1; x++ )
+                *alphaLine++ = ( from->color( *line++ ) & mask2 ) | mask1;
+        }
+    }
+    else if ( from->depth() == 32 )
+    {
+        for ( int y = y0; y <= y1; y++ )
+        {
+            QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
+            const QRgb *line = reinterpret_cast<const QRgb *>( from->scanLine( y ) );
+
+            for ( int x = x0; x <= x1; x++ )
+            {
+                const QRgb rgb = *line++;
+                if ( rgb & mask3 ) // alpha != 0
+                    *alphaLine++ = ( rgb & mask2 ) | mask1;
+                else
+                    *alphaLine++ = rgb;
+            }
+        }
+    }
+}
+
+//! Constructor
+QwtPlotRasterItem::QwtPlotRasterItem( const QString& title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Constructor
+QwtPlotRasterItem::QwtPlotRasterItem( const QwtText& title ):
+    QwtPlotItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotRasterItem::~QwtPlotRasterItem()
+{
+    delete d_data;
+}
+
+void QwtPlotRasterItem::init()
+{
+    d_data = new PrivateData();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+/*!
+  Specify an attribute how to draw the raster item
+
+  \param attribute Paint attribute
+  \param on On/Off
+  /sa PaintAttribute, testPaintAttribute()
+*/
+void QwtPlotRasterItem::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotRasterItem::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+   \brief Set an alpha value for the raster data
+
+   Often a plot has several types of raster data organized in layers.
+   ( f.e a geographical map, with weather statistics ).
+   Using setAlpha() raster items can be stacked easily.
+
+   The alpha value is a value [0, 255] to
+   control the transparency of the image. 0 represents a fully
+   transparent color, while 255 represents a fully opaque color.
+
+   \param alpha Alpha value
+
+   - alpha >= 0\n
+     All alpha values of the pixels returned by renderImage() will be set to
+     alpha, beside those with an alpha value of 0 (invalid pixels).
+   - alpha < 0
+     The alpha values returned by renderImage() are not changed.
+
+   The default alpha value is -1.
+
+   \sa alpha()
+*/
+void QwtPlotRasterItem::setAlpha( int alpha )
+{
+    if ( alpha < 0 )
+        alpha = -1;
+
+    if ( alpha > 255 )
+        alpha = 255;
+
+    if ( alpha != d_data->alpha )
+    {
+        d_data->alpha = alpha;
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Alpha value of the raster item
+  \sa setAlpha()
+*/
+int QwtPlotRasterItem::alpha() const
+{
+    return d_data->alpha;
+}
+
+/*!
+  Change the cache policy
+
+  The default policy is NoCache
+
+  \param policy Cache policy
+  \sa CachePolicy, cachePolicy()
+*/
+void QwtPlotRasterItem::setCachePolicy(
+    QwtPlotRasterItem::CachePolicy policy )
+{
+    if ( d_data->cache.policy != policy )
+    {
+        d_data->cache.policy = policy;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Cache policy
+  \sa CachePolicy, setCachePolicy()
+*/
+QwtPlotRasterItem::CachePolicy QwtPlotRasterItem::cachePolicy() const
+{
+    return d_data->cache.policy;
+}
+
+/*!
+   Invalidate the paint cache
+   \sa setCachePolicy()
+*/
+void QwtPlotRasterItem::invalidateCache()
+{
+    d_data->cache.image = QImage();
+    d_data->cache.area = QRect();
+    d_data->cache.size = QSize();
+}
+
+/*!
+   \brief Pixel hint
+
+   The geometry of a pixel is used to calculated the resolution and
+   alignment of the rendered image. 
+
+   Width and height of the hint need to be the horizontal  
+   and vertical distances between 2 neighbored points. 
+   The center of the hint has to be the position of any point 
+   ( it doesn't matter which one ).
+
+   Limiting the resolution of the image might significantly improve
+   the performance and heavily reduce the amount of memory when rendering
+   a QImage from the raster data. 
+
+   The default implementation returns an empty rectangle (QRectF()),
+   meaning, that the image will be rendered in target device ( f.e screen )
+   resolution.
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel
+
+   \sa render(), renderImage()
+*/
+QRectF QwtPlotRasterItem::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area );
+    return QRectF();
+}
+
+/*!
+  \brief Draw the raster data
+  \param painter Painter
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param canvasRect Contents rectangle of the plot canvas
+*/
+void QwtPlotRasterItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( canvasRect.isEmpty() || d_data->alpha == 0 )
+        return;
+
+    const bool doCache = qwtUseCache( d_data->cache.policy, painter );
+
+    const QwtInterval xInterval = interval( Qt::XAxis );
+    const QwtInterval yInterval = interval( Qt::YAxis );
+
+    /*
+        Scaling an image always results in a loss of
+        precision/quality. So we always render the image in
+        paint device resolution.
+    */
+
+    QwtScaleMap xxMap, yyMap;
+    qwtTransformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
+
+    QRectF paintRect = painter->transform().mapRect( canvasRect );
+    QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect );
+
+    const QRectF br = boundingRect();
+    if ( br.isValid() && !br.contains( area ) )
+    {
+        area &= br;
+        if ( !area.isValid() )
+            return;
+
+        paintRect = QwtScaleMap::transform( xxMap, yyMap, area );
+    }
+
+    QRectF imageRect;
+    QImage image;
+
+    QRectF pixelRect = pixelHint(area);
+    if ( !pixelRect.isEmpty() )
+    {
+        // pixel in target device resolution 
+        const double dx = qAbs( xxMap.invTransform( 1 ) - xxMap.invTransform( 0 ) );
+        const double dy = qAbs( yyMap.invTransform( 1 ) - yyMap.invTransform( 0 ) );
+
+        if ( dx > pixelRect.width() && dy > pixelRect.height() )
+        {
+            /*
+              When the resolution of the data pixels is higher than
+              the resolution of the target device we render in
+              target device resolution.
+             */
+            pixelRect = QRectF();
+        }
+    }
+
+    if ( pixelRect.isEmpty() )
+    {
+        if ( QwtPainter::roundingAlignment( painter ) )
+        {
+            // we want to have maps, where the boundaries of
+            // the aligned paint rectangle exactly match the area
+
+            paintRect = qwtAlignRect(paintRect);
+            qwtAdjustMaps(xxMap, yyMap, area, paintRect);
+        }
+
+        // When we have no information about position and size of
+        // data pixels we render in resolution of the paint device.
+
+        image = compose(xxMap, yyMap, 
+            area, paintRect, paintRect.size().toSize(), doCache);
+        if ( image.isNull() )
+            return;
+
+        // Remove pixels at the boundaries, when explicitly
+        // excluded in the intervals
+
+        imageRect = qwtStripRect(paintRect, area, 
+            xxMap, yyMap, xInterval, yInterval);
+
+        if ( imageRect != paintRect )
+        {
+            const QRect r( 
+                qRound( imageRect.x() - paintRect.x()),
+                qRound( imageRect.y() - paintRect.y() ),
+                qRound( imageRect.width() ),
+                qRound( imageRect.height() ) );
+                
+            image = image.copy(r);
+        }   
+    }
+    else
+    {
+        if ( QwtPainter::roundingAlignment( painter ) )
+            paintRect = qwtAlignRect(paintRect);
+
+        // align the area to the data pixels
+        QRectF imageArea = qwtExpandToPixels(area, pixelRect);
+
+        if ( imageArea.right() == xInterval.maxValue() &&
+            !( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
+        {
+            imageArea.adjust(0, 0, pixelRect.width(), 0);
+        }
+        if ( imageArea.bottom() == yInterval.maxValue() &&
+            !( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
+        {
+            imageArea.adjust(0, 0, 0, pixelRect.height() );
+        }
+
+        QSize imageSize;
+        imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) );
+        imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) );
+        image = compose(xxMap, yyMap, 
+            imageArea, paintRect, imageSize, doCache );
+        if ( image.isNull() )
+            return;
+
+        imageRect = qwtStripRect(paintRect, area, 
+            xxMap, yyMap, xInterval, yInterval);
+
+        if ( ( image.width() > 1 || image.height() > 1 ) &&
+            testPaintAttribute( PaintInDeviceResolution ) )
+        {
+            // Because of rounding errors the pixels 
+            // need to be expanded manually to rectangles of 
+            // different sizes
+
+            image = qwtExpandImage(image, xxMap, yyMap, 
+                imageArea, area, paintRect, xInterval, yInterval );
+        }
+    }
+
+    painter->save();
+    painter->setWorldTransform( QTransform() );
+    
+    QwtPainter::drawImage( painter, imageRect, image );
+
+    painter->restore();
+}
+
+/*!
+   \return Bounding interval for an axis
+
+   This method is intended to be reimplemented by derived classes.
+   The default implementation returns an invalid interval.
+   
+   \param axis X, Y, or Z axis
+*/
+QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const
+{
+    Q_UNUSED( axis );
+    return QwtInterval();
+}
+
+/*!
+   \return Bounding rectangle of the data
+   \sa QwtPlotRasterItem::interval()
+*/
+QRectF QwtPlotRasterItem::boundingRect() const
+{
+    const QwtInterval intervalX = interval( Qt::XAxis );
+    const QwtInterval intervalY = interval( Qt::YAxis );
+
+    if ( !intervalX.isValid() && !intervalY.isValid() )
+        return QRectF(); // no bounding rect
+
+    QRectF r;
+
+    if ( intervalX.isValid() )
+    {
+        r.setLeft( intervalX.minValue() );
+        r.setRight( intervalX.maxValue() );
+    }
+    else
+    {
+        r.setLeft(-0.5 * FLT_MAX);
+        r.setWidth(FLT_MAX);
+    }
+
+    if ( intervalY.isValid() )
+    {
+        r.setTop( intervalY.minValue() );
+        r.setBottom( intervalY.maxValue() );
+    }
+    else
+    {
+        r.setTop(-0.5 * FLT_MAX);
+        r.setHeight(FLT_MAX);
+    }
+
+    return r.normalized();
+}
+
+QImage QwtPlotRasterItem::compose( 
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &imageArea, const QRectF &paintRect, 
+    const QSize &imageSize, bool doCache) const
+{
+    QImage image;
+    if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() )
+        return image;
+
+    if ( doCache )
+    {
+        if ( !d_data->cache.image.isNull()
+            && d_data->cache.area == imageArea
+            && d_data->cache.size == paintRect.size() )
+        {
+            image = d_data->cache.image;
+        }
+    }
+
+    if ( image.isNull() )
+    {
+        double dx = 0.0;
+        if ( paintRect.toRect().width() > imageSize.width() )
+            dx = imageArea.width() / imageSize.width();
+
+        const QwtScaleMap xxMap = 
+            imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
+        
+        double dy = 0.0;
+        if ( paintRect.toRect().height() > imageSize.height() )
+            dy = imageArea.height() / imageSize.height();
+
+        const QwtScaleMap yyMap = 
+            imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy);
+
+        image = renderImage( xxMap, yyMap, imageArea, imageSize );
+
+        if ( doCache )
+        {
+            d_data->cache.area = imageArea;
+            d_data->cache.size = paintRect.size();
+            d_data->cache.image = image;
+        }
+    }
+
+    if ( d_data->alpha >= 0 && d_data->alpha < 255 )
+    {
+        QImage alphaImage( image.size(), QImage::Format_ARGB32 );
+
+#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
+        uint numThreads = renderThreadCount();
+
+        if ( numThreads <= 0 )
+            numThreads = QThread::idealThreadCount();
+
+        if ( numThreads <= 0 )
+            numThreads = 1;
+
+        const int numRows = image.height() / numThreads;
+
+        QList< QFuture<void> > futures;
+        for ( uint i = 0; i < numThreads; i++ )
+        {
+            QRect tile( 0, i * numRows, image.width(), numRows );
+            if ( i == numThreads - 1 )
+            {
+                tile.setHeight( image.height() - i * numRows );
+                qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
+            }
+            else
+            {
+                futures += QtConcurrent::run(
+                    &qwtToRgba, &image, &alphaImage, tile, d_data->alpha );
+            }
+        }
+        for ( int i = 0; i < futures.size(); i++ )
+            futures[i].waitForFinished();
+#else
+        const QRect tile( 0, 0, image.width(), image.height() );
+        qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
+#endif
+        image = alphaImage;
+    }
+
+    return image;
+}
+
+/*!
+   \brief Calculate a scale map for painting to an image
+
+   \param orientation Orientation, Qt::Horizontal means a X axis
+   \param map Scale map for rendering the plot item
+   \param area Area to be painted on the image
+   \param imageSize Image size
+   \param pixelSize Width/Height of a data pixel
+
+   \return Calculated scale map
+*/
+QwtScaleMap QwtPlotRasterItem::imageMap(
+    Qt::Orientation orientation,
+    const QwtScaleMap &map, const QRectF &area,
+    const QSize &imageSize, double pixelSize) const
+{
+    double p1, p2, s1, s2;
+
+    if ( orientation == Qt::Horizontal )
+    {
+        p1 = 0.0;
+        p2 = imageSize.width();
+        s1 = area.left();
+        s2 = area.right();
+    }
+    else
+    {
+        p1 = 0.0;
+        p2 = imageSize.height();
+        s1 = area.top();
+        s2 = area.bottom();
+    }
+
+    if ( pixelSize > 0.0 )
+    {
+        double off = 0.5 * pixelSize;
+        if ( map.isInverting() )
+            off = -off;
+
+        s1 += off;
+        s2 += off;
+    }
+    else
+    {
+        p2--;
+    }
+
+    if ( map.isInverting() && ( s1 < s2 ) )
+        qSwap( s1, s2 );
+
+    QwtScaleMap newMap = map;
+    newMap.setPaintInterval( p1, p2 );
+    newMap.setScaleInterval( s1, s2 );
+
+    return newMap;
+}
diff --git a/qwt/qwt_plot_rasteritem.h b/qwt/qwt_plot_rasteritem.h
new file mode 100644
index 0000000..f411816
--- /dev/null
+++ b/qwt/qwt_plot_rasteritem.h
@@ -0,0 +1,152 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RASTERITEM_H
+#define QWT_PLOT_RASTERITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_interval.h"
+#include <qglobal.h>
+#include <qstring.h>
+#include <qimage.h>
+
+/*!
+  \brief A class, which displays raster data
+
+  Raster data is a grid of pixel values, that can be represented
+  as a QImage. It is used for many types of information like
+  spectrograms, cartograms, geographical maps ...
+
+  Often a plot has several types of raster data organized in layers.
+  ( f.e a geographical map, with weather statistics ).
+  Using setAlpha() raster items can be stacked easily.
+
+  QwtPlotRasterItem is only implemented for images of the following formats:
+  QImage::Format_Indexed8, QImage::Format_ARGB32.
+
+  \sa QwtPlotSpectrogram
+*/
+
+class QWT_EXPORT QwtPlotRasterItem: public QwtPlotItem
+{
+public:
+    /*!
+      \brief Cache policy
+      The default policy is NoCache
+     */
+    enum CachePolicy
+    {
+        /*!
+          renderImage() is called each time the item has to be repainted
+         */
+        NoCache,
+
+        /*!
+          renderImage() is called, whenever the image cache is not valid,
+          or the scales, or the size of the canvas has changed. 
+
+          This type of cache is useful for improving the performance 
+          of hide/show operations or manipulations of the alpha value. 
+          All other situations are handled by the canvas backing store.
+         */
+        PaintCache
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          When the image is rendered according to the data pixels
+          ( QwtRasterData::pixelHint() ) it can be expanded to paint
+          device resolution before it is passed to QPainter. 
+          The expansion algorithm rounds the pixel borders in the same 
+          way as the axis ticks, what is usually better than the
+          scaling algorithm implemented in Qt.
+          Disabling this flag might make sense, to reduce the size of a 
+          document/file. If this is possible for a document format
+          depends on the implementation of the specific QPaintEngine.
+         */
+
+        PaintInDeviceResolution = 1
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotRasterItem( const QString& title = QString::null );
+    explicit QwtPlotRasterItem( const QwtText& title );
+    virtual ~QwtPlotRasterItem();
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setAlpha( int alpha );
+    int alpha() const;
+
+    void setCachePolicy( CachePolicy );
+    CachePolicy cachePolicy() const;
+
+    void invalidateCache();
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual QwtInterval interval(Qt::Axis) const;
+    virtual QRectF boundingRect() const;
+
+protected:
+    /*!
+      \brief Render an image 
+
+      An implementation of render() might iterate over all
+      pixels of imageRect. Each pixel has to be translated into 
+      the corresponding position in scale coordinates using the maps.
+      This position can be used to look up a value in a implementation
+      specific way and to map it into a color.
+
+      \param xMap X-Scale Map
+      \param yMap Y-Scale Map
+      \param area Requested area for the image in scale coordinates
+      \param imageSize Requested size of the image
+   
+      \return Rendered image
+     */
+    virtual QImage renderImage( const QwtScaleMap &xMap,
+        const QwtScaleMap &yMap, const QRectF &area,
+        const QSize &imageSize ) const = 0;
+
+    virtual QwtScaleMap imageMap( Qt::Orientation,
+        const QwtScaleMap &map, const QRectF &area,
+        const QSize &imageSize, double pixelSize) const;
+
+private:
+    QwtPlotRasterItem( const QwtPlotRasterItem & );
+    QwtPlotRasterItem &operator=( const QwtPlotRasterItem & );
+
+    void init();
+
+    QImage compose( const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &imageArea, const QRectF &paintRect,
+        const QSize &imageSize, bool doCache) const;
+
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRasterItem::PaintAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_renderer.cpp b/qwt/qwt_plot_renderer.cpp
new file mode 100644
index 0000000..ff84fe4
--- /dev/null
+++ b/qwt/qwt_plot_renderer.cpp
@@ -0,0 +1,938 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_renderer.h"
+#include "qwt_plot.h"
+#include "qwt_painter.h"
+#include "qwt_plot_layout.h"
+#include "qwt_abstract_legend.h"
+#include "qwt_scale_widget.h"
+#include "qwt_scale_engine.h"
+#include "qwt_text.h"
+#include "qwt_text_label.h"
+#include "qwt_math.h"
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qtransform.h>
+#include <qfiledialog.h>
+#include <qfileinfo.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qimagewriter.h>
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#include <qsvggenerator.h>
+#endif
+#endif
+
+static QPainterPath qwtCanvasClip( 
+    const QWidget* canvas, const QRectF &canvasRect )
+{
+    // The clip region is calculated in integers
+    // To avoid too much rounding errors better
+    // calculate it in target device resolution
+
+    int x1 = qCeil( canvasRect.left() );
+    int x2 = qFloor( canvasRect.right() );
+    int y1 = qCeil( canvasRect.top() );
+    int y2 = qFloor( canvasRect.bottom() );
+
+    const QRect r( x1, y1, x2 - x1 - 1, y2 - y1 - 1 );
+
+    QPainterPath clipPath;
+
+    ( void ) QMetaObject::invokeMethod(
+        const_cast< QWidget *>( canvas ), "borderPath",
+        Qt::DirectConnection,
+        Q_RETURN_ARG( QPainterPath, clipPath ), Q_ARG( QRect, r ) );
+
+    return clipPath;
+}
+
+class QwtPlotRenderer::PrivateData
+{
+public:
+    PrivateData():
+        discardFlags( QwtPlotRenderer::DiscardNone ),
+        layoutFlags( QwtPlotRenderer::DefaultLayout )
+    {
+    }
+
+    QwtPlotRenderer::DiscardFlags discardFlags;
+    QwtPlotRenderer::LayoutFlags layoutFlags;
+};
+
+/*! 
+   Constructor
+   \param parent Parent object
+*/
+QwtPlotRenderer::QwtPlotRenderer( QObject *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlotRenderer::~QwtPlotRenderer()
+{
+    delete d_data;
+}
+
+/*!
+  Change a flag, indicating what to discard from rendering
+
+  \param flag Flag to change
+  \param on On/Off
+
+  \sa DiscardFlag, testDiscardFlag(), setDiscardFlags(), discardFlags()
+*/
+void QwtPlotRenderer::setDiscardFlag( DiscardFlag flag, bool on )
+{
+    if ( on )
+        d_data->discardFlags |= flag;
+    else
+        d_data->discardFlags &= ~flag;
+}
+
+/*!
+  \return True, if flag is enabled.
+  \param flag Flag to be tested
+  \sa DiscardFlag, setDiscardFlag(), setDiscardFlags(), discardFlags()
+*/
+bool QwtPlotRenderer::testDiscardFlag( DiscardFlag flag ) const
+{
+    return d_data->discardFlags & flag;
+}
+
+/*!
+  Set the flags, indicating what to discard from rendering
+
+  \param flags Flags
+  \sa DiscardFlag, setDiscardFlag(), testDiscardFlag(), discardFlags()
+*/
+void QwtPlotRenderer::setDiscardFlags( DiscardFlags flags )
+{
+    d_data->discardFlags = flags;
+}
+
+/*!
+  \return Flags, indicating what to discard from rendering
+  \sa DiscardFlag, setDiscardFlags(), setDiscardFlag(), testDiscardFlag()
+*/
+QwtPlotRenderer::DiscardFlags QwtPlotRenderer::discardFlags() const
+{
+    return d_data->discardFlags;
+}
+
+/*!
+  Change a layout flag
+
+  \param flag Flag to change
+  \param on On/Off
+
+  \sa LayoutFlag, testLayoutFlag(), setLayoutFlags(), layoutFlags()
+*/
+void QwtPlotRenderer::setLayoutFlag( LayoutFlag flag, bool on )
+{
+    if ( on )
+        d_data->layoutFlags |= flag;
+    else
+        d_data->layoutFlags &= ~flag;
+}
+
+/*!
+  \return True, if flag is enabled.
+  \param flag Flag to be tested
+  \sa LayoutFlag, setLayoutFlag(), setLayoutFlags(), layoutFlags()
+*/
+bool QwtPlotRenderer::testLayoutFlag( LayoutFlag flag ) const
+{
+    return d_data->layoutFlags & flag;
+}
+
+/*!
+  Set the layout flags
+
+  \param flags Flags
+  \sa LayoutFlag, setLayoutFlag(), testLayoutFlag(), layoutFlags()
+*/
+void QwtPlotRenderer::setLayoutFlags( LayoutFlags flags )
+{
+    d_data->layoutFlags = flags;
+}
+
+/*!
+  \return Layout flags
+  \sa LayoutFlag, setLayoutFlags(), setLayoutFlag(), testLayoutFlag()
+*/
+QwtPlotRenderer::LayoutFlags QwtPlotRenderer::layoutFlags() const
+{
+    return d_data->layoutFlags;
+}
+
+/*!
+  Render a plot to a file
+
+  The format of the document will be auto-detected from the
+  suffix of the file name.
+
+  \param plot Plot widget
+  \param fileName Path of the file, where the document will be stored
+  \param sizeMM Size for the document in millimeters.
+  \param resolution Resolution in dots per Inch (dpi)
+*/
+void QwtPlotRenderer::renderDocument( QwtPlot *plot,
+    const QString &fileName, const QSizeF &sizeMM, int resolution )
+{
+    renderDocument( plot, fileName,
+        QFileInfo( fileName ).suffix(), sizeMM, resolution );
+}
+
+/*!
+  Render a plot to a file
+
+  Supported formats are:
+
+  - pdf\n
+    Portable Document Format PDF
+  - ps\n
+    Postcript
+  - svg\n
+    Scalable Vector Graphics SVG
+  - all image formats supported by Qt\n
+    see QImageWriter::supportedImageFormats()
+
+  Scalable vector graphic formats like PDF or SVG are superior to
+  raster graphics formats.
+
+  \param plot Plot widget
+  \param fileName Path of the file, where the document will be stored
+  \param format Format for the document
+  \param sizeMM Size for the document in millimeters.
+  \param resolution Resolution in dots per Inch (dpi)
+
+  \sa renderTo(), render(), QwtPainter::setRoundingAlignment()
+*/
+void QwtPlotRenderer::renderDocument( QwtPlot *plot,
+    const QString &fileName, const QString &format,
+    const QSizeF &sizeMM, int resolution )
+{
+    if ( plot == NULL || sizeMM.isEmpty() || resolution <= 0 )
+        return;
+
+    QString title = plot->title().text();
+    if ( title.isEmpty() )
+        title = "Plot Document";
+
+    const double mmToInch = 1.0 / 25.4;
+    const QSizeF size = sizeMM * mmToInch * resolution;
+
+    const QRectF documentRect( 0.0, 0.0, size.width(), size.height() );
+
+    const QString fmt = format.toLower();
+    if ( fmt == "pdf" )
+    {
+
+    }
+    else if ( fmt == "ps" )
+    {
+
+    }
+    else if ( fmt == "svg" )
+    {
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#if QT_VERSION >= 0x040500
+        QSvgGenerator generator;
+        generator.setTitle( title );
+        generator.setFileName( fileName );
+        generator.setResolution( resolution );
+        generator.setViewBox( documentRect );
+
+        QPainter painter( &generator );
+        render( plot, &painter, documentRect );
+#endif
+#endif
+#endif
+    }
+    else
+    {
+        if ( QImageWriter::supportedImageFormats().indexOf(
+            format.toLatin1() ) >= 0 )
+        {
+            const QRect imageRect = documentRect.toRect();
+            const int dotsPerMeter = qRound( resolution * mmToInch * 1000.0 );
+
+            QImage image( imageRect.size(), QImage::Format_ARGB32 );
+            image.setDotsPerMeterX( dotsPerMeter );
+            image.setDotsPerMeterY( dotsPerMeter );
+            image.fill( QColor( Qt::white ).rgb() );
+
+            QPainter painter( &image );
+            render( plot, &painter, imageRect );
+            painter.end();
+
+            image.save( fileName, format.toLatin1() );
+        }
+    }
+}
+
+/*!
+  \brief Render the plot to a \c QPaintDevice
+
+  This function renders the contents of a QwtPlot instance to
+  \c QPaintDevice object. The target rectangle is derived from
+  its device metrics.
+
+  \param plot Plot to be rendered
+  \param paintDevice device to paint on, f.e a QImage
+
+  \sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
+*/
+
+void QwtPlotRenderer::renderTo(
+    QwtPlot *plot, QPaintDevice &paintDevice ) const
+{
+    int w = paintDevice.width();
+    int h = paintDevice.height();
+
+    QPainter p( &paintDevice );
+    render( plot, &p, QRectF( 0, 0, w, h ) );
+}
+
+/*!
+  \brief Render the plot to a QPrinter
+
+  This function renders the contents of a QwtPlot instance to
+  \c QPaintDevice object. The size is derived from the printer
+  metrics.
+
+  \param plot Plot to be rendered
+  \param printer Printer to paint on
+
+  \sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
+*/
+
+
+
+
+/*!
+  Paint the contents of a QwtPlot instance into a given rectangle.
+
+  \param plot Plot to be rendered
+  \param painter Painter
+  \param plotRect Bounding rectangle
+
+  \sa renderDocument(), renderTo(), QwtPainter::setRoundingAlignment()
+*/
+void QwtPlotRenderer::render( QwtPlot *plot,
+    QPainter *painter, const QRectF &plotRect ) const
+{
+    if ( painter == 0 || !painter->isActive() ||
+            !plotRect.isValid() || plot->size().isNull() )
+    {
+        return;
+    }
+
+    if ( !( d_data->discardFlags & DiscardBackground ) )
+        QwtPainter::drawBackgound( painter, plotRect, plot );
+
+    /*
+      The layout engine uses the same methods as they are used
+      by the Qt layout system. Therefore we need to calculate the
+      layout in screen coordinates and paint with a scaled painter.
+     */
+    QTransform transform;
+    transform.scale(
+        double( painter->device()->logicalDpiX() ) / plot->logicalDpiX(),
+        double( painter->device()->logicalDpiY() ) / plot->logicalDpiY() );
+
+    QRectF layoutRect = transform.inverted().mapRect( plotRect );
+
+    if ( !( d_data->discardFlags & DiscardBackground ) )
+    {
+        // subtract the contents margins
+
+        int left, top, right, bottom;
+        plot->getContentsMargins( &left, &top, &right, &bottom );
+        layoutRect.adjust( left, top, -right, -bottom );
+    }
+
+    QwtPlotLayout *layout = plot->plotLayout();
+
+    int baseLineDists[QwtPlot::axisCnt];
+    int canvasMargins[QwtPlot::axisCnt];
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        canvasMargins[ axisId ] = layout->canvasMargin( axisId );
+
+        if ( d_data->layoutFlags & FrameWithScales )
+        {
+            QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+            if ( scaleWidget )
+            {
+                baseLineDists[axisId] = scaleWidget->margin();
+                scaleWidget->setMargin( 0 );
+            }
+
+            if ( !plot->axisEnabled( axisId ) )
+            {
+                int left = 0;
+                int right = 0;
+                int top = 0;
+                int bottom = 0;
+
+                // When we have a scale the frame is painted on
+                // the position of the backbone - otherwise we
+                // need to introduce a margin around the canvas
+
+                switch( axisId )
+                {
+                    case QwtPlot::yLeft:
+                        layoutRect.adjust( 1, 0, 0, 0 );
+                        break;
+                    case QwtPlot::yRight:
+                        layoutRect.adjust( 0, 0, -1, 0 );
+                        break;
+                    case QwtPlot::xTop:
+                        layoutRect.adjust( 0, 1, 0, 0 );
+                        break;
+                    case QwtPlot::xBottom:
+                        layoutRect.adjust( 0, 0, 0, -1 );
+                        break;
+                    default:
+                        break;
+                }
+                layoutRect.adjust( left, top, right, bottom );
+            }
+        }
+    }
+
+    // Calculate the layout for the document.
+
+    QwtPlotLayout::Options layoutOptions = QwtPlotLayout::IgnoreScrollbars;
+
+    if ( ( d_data->layoutFlags & FrameWithScales ) ||
+        ( d_data->discardFlags & DiscardCanvasFrame ) )
+    {
+        layoutOptions |= QwtPlotLayout::IgnoreFrames;
+    } 
+
+
+    if ( d_data->discardFlags & DiscardLegend )
+        layoutOptions |= QwtPlotLayout::IgnoreLegend;
+
+    if ( d_data->discardFlags & DiscardTitle )
+        layoutOptions |= QwtPlotLayout::IgnoreTitle;
+
+    if ( d_data->discardFlags & DiscardFooter )
+        layoutOptions |= QwtPlotLayout::IgnoreFooter;
+
+    layout->activate( plot, layoutRect, layoutOptions );
+
+    // canvas
+
+    QwtScaleMap maps[QwtPlot::axisCnt];
+    buildCanvasMaps( plot, layout->canvasRect(), maps );
+    if ( updateCanvasMargins( plot, layout->canvasRect(), maps ) )
+    {
+        // recalculate maps and layout, when the margins
+        // have been changed
+
+        layout->activate( plot, layoutRect, layoutOptions );
+        buildCanvasMaps( plot, layout->canvasRect(), maps );
+    }
+
+    // now start painting
+
+    painter->save();
+    painter->setWorldTransform( transform, true );
+
+    renderCanvas( plot, painter, layout->canvasRect(), maps );
+
+    if ( !( d_data->discardFlags & DiscardTitle )
+        && ( !plot->titleLabel()->text().isEmpty() ) )
+    {
+        renderTitle( plot, painter, layout->titleRect() );
+    }
+
+    if ( !( d_data->discardFlags & DiscardFooter )
+        && ( !plot->footerLabel()->text().isEmpty() ) )
+    {
+        renderFooter( plot, painter, layout->footerRect() );
+    }
+
+    if ( !( d_data->discardFlags & DiscardLegend )
+        && plot->legend() && !plot->legend()->isEmpty() )
+    {
+        renderLegend( plot, painter, layout->legendRect() );
+    }
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+        if ( scaleWidget )
+        {
+            int baseDist = scaleWidget->margin();
+
+            int startDist, endDist;
+            scaleWidget->getBorderDistHint( startDist, endDist );
+
+            renderScale( plot, painter, axisId, startDist, endDist,
+                baseDist, layout->scaleRect( axisId ) );
+        }
+    }
+
+    painter->restore();
+
+    // restore all setting to their original attributes.
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        if ( d_data->layoutFlags & FrameWithScales )
+        {
+            QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+            if ( scaleWidget  )
+                scaleWidget->setMargin( baseLineDists[axisId] );
+        }
+
+        layout->setCanvasMargin( canvasMargins[axisId] );
+    }
+
+    layout->invalidate();
+
+}
+
+/*!
+  Render the title into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderTitle( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->setFont( plot->titleLabel()->font() );
+
+    const QColor color = plot->titleLabel()->palette().color(
+            QPalette::Active, QPalette::Text );
+
+    painter->setPen( color );
+    plot->titleLabel()->text().draw( painter, rect );
+}
+
+/*!
+  Render the footer into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderFooter( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->setFont( plot->footerLabel()->font() );
+
+    const QColor color = plot->footerLabel()->palette().color(
+            QPalette::Active, QPalette::Text );
+
+    painter->setPen( color );
+    plot->footerLabel()->text().draw( painter, rect );
+}
+
+
+/*!
+  Render the legend into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderLegend( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    if ( plot->legend() )
+    {
+        bool fillBackground = !( d_data->discardFlags & DiscardBackground );
+        plot->legend()->renderLegend( painter, rect, fillBackground );
+    }
+}
+
+/*!
+  \brief Paint a scale into a given rectangle.
+  Paint the scale into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param axisId Axis
+  \param startDist Start border distance
+  \param endDist End border distance
+  \param baseDist Base distance
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderScale( const QwtPlot *plot,
+    QPainter *painter,
+    int axisId, int startDist, int endDist, int baseDist,
+    const QRectF &rect ) const
+{
+    if ( !plot->axisEnabled( axisId ) )
+        return;
+
+    const QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+    if ( scaleWidget->isColorBarEnabled()
+        && scaleWidget->colorBarWidth() > 0 )
+    {
+        scaleWidget->drawColorBar( painter, scaleWidget->colorBarRect( rect ) );
+        baseDist += scaleWidget->colorBarWidth() + scaleWidget->spacing();
+    }
+
+    painter->save();
+
+    QwtScaleDraw::Alignment align;
+    double x, y, w;
+
+    switch ( axisId )
+    {
+        case QwtPlot::yLeft:
+        {
+            x = rect.right() - 1.0 - baseDist;
+            y = rect.y() + startDist;
+            w = rect.height() - startDist - endDist;
+            align = QwtScaleDraw::LeftScale;
+            break;
+        }
+        case QwtPlot::yRight:
+        {
+            x = rect.left() + baseDist;
+            y = rect.y() + startDist;
+            w = rect.height() - startDist - endDist;
+            align = QwtScaleDraw::RightScale;
+            break;
+        }
+        case QwtPlot::xTop:
+        {
+            x = rect.left() + startDist;
+            y = rect.bottom() - 1.0 - baseDist;
+            w = rect.width() - startDist - endDist;
+            align = QwtScaleDraw::TopScale;
+            break;
+        }
+        case QwtPlot::xBottom:
+        {
+            x = rect.left() + startDist;
+            y = rect.top() + baseDist;
+            w = rect.width() - startDist - endDist;
+            align = QwtScaleDraw::BottomScale;
+            break;
+        }
+        default:
+            return;
+    }
+
+    scaleWidget->drawTitle( painter, align, rect );
+
+    painter->setFont( scaleWidget->font() );
+
+    QwtScaleDraw *sd = const_cast<QwtScaleDraw *>( scaleWidget->scaleDraw() );
+    const QPointF sdPos = sd->pos();
+    const double sdLength = sd->length();
+
+    sd->move( x, y );
+    sd->setLength( w );
+
+    QPalette palette = scaleWidget->palette();
+    palette.setCurrentColorGroup( QPalette::Active );
+    sd->draw( painter, palette );
+
+    // reset previous values
+    sd->move( sdPos );
+    sd->setLength( sdLength );
+
+    painter->restore();
+}
+
+/*!
+  Render the canvas into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param map Maps mapping between plot and paint device coordinates
+  \param canvasRect Canvas rectangle
+*/
+void QwtPlotRenderer::renderCanvas( const QwtPlot *plot,
+    QPainter *painter, const QRectF &canvasRect, 
+    const QwtScaleMap *map ) const
+{
+    const QWidget *canvas = plot->canvas();
+
+    QRectF r = canvasRect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+    if ( d_data->layoutFlags & FrameWithScales )
+    {
+        painter->save();
+
+        r.adjust( -1.0, -1.0, 1.0, 1.0 );
+        painter->setPen( QPen( Qt::black ) );
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            const QBrush bgBrush =
+                canvas->palette().brush( plot->backgroundRole() );
+            painter->setBrush( bgBrush );
+        }
+
+        QwtPainter::drawRect( painter, r );
+
+        painter->restore();
+        painter->save();
+
+        painter->setClipRect( canvasRect );
+        plot->drawItems( painter, canvasRect, map );
+
+        painter->restore();
+    }
+    else if ( canvas->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QPainterPath clipPath;
+
+        painter->save();
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, r, canvas );
+            clipPath = qwtCanvasClip( canvas, canvasRect );
+        }
+
+        painter->restore();
+        painter->save();
+
+        if ( clipPath.isEmpty() )
+            painter->setClipRect( canvasRect );
+        else
+            painter->setClipPath( clipPath );
+
+        plot->drawItems( painter, canvasRect, map );
+
+        painter->restore();
+    }
+    else
+    {
+        QPainterPath clipPath;
+
+        int frameWidth = 0;
+
+        if ( !( d_data->discardFlags & DiscardCanvasFrame ) )
+        {
+            const QVariant fw = canvas->property( "frameWidth" );
+            if ( fw.type() == QVariant::Int )
+                frameWidth = fw.toInt();
+
+            clipPath = qwtCanvasClip( canvas, canvasRect );
+        }
+
+        QRectF innerRect = canvasRect.adjusted( 
+            frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+        painter->save();
+
+        if ( clipPath.isEmpty() )
+        {
+            painter->setClipRect( innerRect );
+        }
+        else
+        {
+            painter->setClipPath( clipPath );
+        }
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, innerRect, canvas );
+        }
+
+        plot->drawItems( painter, innerRect, map );
+
+        painter->restore();
+
+        if ( frameWidth > 0 )
+        {
+            painter->save();
+
+            const int frameStyle =
+                canvas->property( "frameShadow" ).toInt() |
+                canvas->property( "frameShape" ).toInt();
+
+            const int frameWidth = canvas->property( "frameWidth" ).toInt();
+
+
+            const QVariant borderRadius = canvas->property( "borderRadius" );
+            if ( borderRadius.type() == QVariant::Double 
+                && borderRadius.toDouble() > 0.0 )
+            {
+                const double r = borderRadius.toDouble();
+
+                QwtPainter::drawRoundedFrame( painter, canvasRect,
+                    r, r, canvas->palette(), frameWidth, frameStyle );
+            }
+            else
+            {
+                const int midLineWidth = canvas->property( "midLineWidth" ).toInt();
+
+                QwtPainter::drawFrame( painter, canvasRect,
+                    canvas->palette(), canvas->foregroundRole(),
+                    frameWidth, midLineWidth, frameStyle );
+            }
+            painter->restore();
+        }
+    }
+}
+
+/*!
+   Calculated the scale maps for rendering the canvas
+
+   \param plot Plot widget
+   \param canvasRect Target rectangle
+   \param maps Scale maps to be calculated
+*/
+void QwtPlotRenderer::buildCanvasMaps( const QwtPlot *plot,
+    const QRectF &canvasRect, QwtScaleMap maps[] ) const
+{
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        maps[axisId].setTransformation(
+            plot->axisScaleEngine( axisId )->transformation() );
+
+        const QwtScaleDiv &scaleDiv = plot->axisScaleDiv( axisId );
+        maps[axisId].setScaleInterval(
+            scaleDiv.lowerBound(), scaleDiv.upperBound() );
+
+        double from, to;
+        if ( plot->axisEnabled( axisId ) )
+        {
+            const int sDist = plot->axisWidget( axisId )->startBorderDist();
+            const int eDist = plot->axisWidget( axisId )->endBorderDist();
+            const QRectF scaleRect = plot->plotLayout()->scaleRect( axisId );
+
+            if ( axisId == QwtPlot::xTop || axisId == QwtPlot::xBottom )
+            {
+                from = scaleRect.left() + sDist;
+                to = scaleRect.right() - eDist;
+            }
+            else
+            {
+                from = scaleRect.bottom() - eDist;
+                to = scaleRect.top() + sDist;
+            }
+        }
+        else
+        {
+            int margin = 0;
+            if ( !plot->plotLayout()->alignCanvasToScale( axisId ) )
+                margin = plot->plotLayout()->canvasMargin( axisId );
+
+            if ( axisId == QwtPlot::yLeft || axisId == QwtPlot::yRight )
+            {
+                from = canvasRect.bottom() - margin;
+                to = canvasRect.top() + margin;
+            }
+            else
+            {
+                from = canvasRect.left() + margin;
+                to = canvasRect.right() - margin;
+            }
+        }
+        maps[axisId].setPaintInterval( from, to );
+    }
+}
+
+bool QwtPlotRenderer::updateCanvasMargins( QwtPlot *plot,
+    const QRectF &canvasRect, const QwtScaleMap maps[] ) const
+{
+    double margins[QwtPlot::axisCnt];
+    plot->getCanvasMarginsHint( maps, canvasRect,
+        margins[QwtPlot::yLeft], margins[QwtPlot::xTop], 
+        margins[QwtPlot::yRight], margins[QwtPlot::xBottom] );
+
+    bool marginsChanged = false;
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        if ( margins[axisId] >= 0.0 )
+        {
+            const int m = qCeil( margins[axisId] );
+            plot->plotLayout()->setCanvasMargin( m, axisId);
+            marginsChanged = true;
+        }
+    }
+
+    return marginsChanged;
+}
+
+/*!
+   \brief Execute a file dialog and render the plot to the selected file
+
+   \param plot Plot widget
+   \param documentName Default document name
+   \param sizeMM Size for the document in millimeters.
+   \param resolution Resolution in dots per Inch (dpi)
+
+   \return True, when exporting was successful
+   \sa renderDocument()
+*/
+bool QwtPlotRenderer::exportTo( QwtPlot *plot, const QString &documentName,
+     const QSizeF &sizeMM, int resolution )
+{       
+    if ( plot == NULL )
+        return false;
+    
+    QString fileName = documentName;
+
+    // What about translation 
+
+#ifndef QT_NO_FILEDIALOG
+    const QList<QByteArray> imageFormats =
+        QImageWriter::supportedImageFormats();
+        
+    QStringList filter;
+#ifndef QT_NO_PRINTER
+    filter += QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)";
+#endif
+#ifndef QWT_NO_SVG 
+    filter += QString( "SVG " ) + tr( "Documents" ) + " (*.svg)";
+#endif
+#ifndef QT_NO_PRINTER
+    filter += QString( "Postscript " ) + tr( "Documents" ) + " (*.ps)";
+#endif
+    
+    if ( imageFormats.size() > 0 )
+    {
+        QString imageFilter( tr( "Images" ) );
+        imageFilter += " (";
+        for ( int i = 0; i < imageFormats.size(); i++ )
+        {
+            if ( i > 0 )
+                imageFilter += " ";
+            imageFilter += "*."; 
+            imageFilter += imageFormats[i];
+        }   
+        imageFilter += ")";
+        
+        filter += imageFilter;
+    }   
+    
+    fileName = QFileDialog::getSaveFileName(
+        NULL, tr( "Export File Name" ), fileName,
+        filter.join( ";;" ), NULL, QFileDialog::DontConfirmOverwrite );
+#endif  
+    if ( fileName.isEmpty() )
+        return false;
+
+    renderDocument( plot, fileName, sizeMM, resolution );
+
+    return true;
+}   
diff --git a/qwt/qwt_plot_renderer.h b/qwt/qwt_plot_renderer.h
new file mode 100644
index 0000000..68ffcd1
--- /dev/null
+++ b/qwt/qwt_plot_renderer.h
@@ -0,0 +1,170 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RENDERER_H
+#define QWT_PLOT_RENDERER_H
+
+#include "qwt_global.h"
+#include <qobject.h>
+#include <qsize.h>
+
+class QwtPlot;
+class QwtScaleMap;
+class QRectF;
+class QPainter;
+class QPaintDevice;
+
+#ifndef QT_NO_PRINTER
+class QPrinter;
+#endif
+
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+class QSvgGenerator;
+#endif
+#endif
+
+/*!
+    \brief Renderer for exporting a plot to a document, a printer
+           or anything else, that is supported by QPainter/QPaintDevice
+*/
+class QWT_EXPORT QwtPlotRenderer: public QObject
+{
+    Q_OBJECT
+
+public:
+    //! Disard flags
+    enum DiscardFlag
+    {
+        //! Render all components of the plot
+        DiscardNone             = 0x00,
+
+        //! Don't render the background of the plot
+        DiscardBackground       = 0x01,
+
+        //! Don't render the title of the plot
+        DiscardTitle            = 0x02,
+
+        //! Don't render the legend of the plot
+        DiscardLegend           = 0x04,
+
+        //! Don't render the background of the canvas
+        DiscardCanvasBackground = 0x08,
+
+        //! Don't render the footer of the plot
+        DiscardFooter           = 0x10,
+
+        /*! 
+            Don't render the frame of the canvas
+
+            \note This flag has no effect when using
+                  style sheets, where the frame is part
+                  of the background
+         */
+        DiscardCanvasFrame           = 0x20
+
+    };
+
+    //! Disard flags
+    typedef QFlags<DiscardFlag> DiscardFlags;
+
+    /*!
+       \brief Layout flags
+       \sa setLayoutFlag(), testLayoutFlag()
+     */
+    enum LayoutFlag
+    {
+        //! Use the default layout as on screen
+        DefaultLayout   = 0x00,
+
+        /*!
+          Instead of the scales a box is painted around the plot canvas,
+          where the scale ticks are aligned to.
+         */
+        FrameWithScales = 0x01
+    };
+
+    //! Layout flags
+    typedef QFlags<LayoutFlag> LayoutFlags;
+
+    explicit QwtPlotRenderer( QObject * = NULL );
+    virtual ~QwtPlotRenderer();
+
+    void setDiscardFlag( DiscardFlag flag, bool on = true );
+    bool testDiscardFlag( DiscardFlag flag ) const;
+
+    void setDiscardFlags( DiscardFlags flags );
+    DiscardFlags discardFlags() const;
+
+    void setLayoutFlag( LayoutFlag flag, bool on = true );
+    bool testLayoutFlag( LayoutFlag flag ) const;
+
+    void setLayoutFlags( LayoutFlags flags );
+    LayoutFlags layoutFlags() const;
+
+    void renderDocument( QwtPlot *, const QString &fileName,
+        const QSizeF &sizeMM, int resolution = 85 );
+
+    void renderDocument( QwtPlot *,
+        const QString &fileName, const QString &format,
+        const QSizeF &sizeMM, int resolution = 85 );
+
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#if QT_VERSION >= 0x040500
+    void renderTo( QwtPlot *, QSvgGenerator & ) const;
+#endif
+#endif
+#endif
+
+#ifndef QT_NO_PRINTER
+    void renderTo( QwtPlot *, QPrinter & ) const;
+#endif
+
+    void renderTo( QwtPlot *, QPaintDevice &p ) const;
+
+    virtual void render( QwtPlot *,
+        QPainter *, const QRectF &rect ) const;
+
+    virtual void renderTitle( const QwtPlot *,
+        QPainter *, const QRectF & ) const;
+
+    virtual void renderFooter( const QwtPlot *,
+        QPainter *, const QRectF & ) const;
+
+    virtual void renderScale( const QwtPlot *, QPainter *,
+        int axisId, int startDist, int endDist,
+        int baseDist, const QRectF & ) const;
+
+    virtual void renderCanvas( const QwtPlot *,
+        QPainter *, const QRectF &canvasRect,
+        const QwtScaleMap* maps ) const;
+
+    virtual void renderLegend( 
+        const QwtPlot *, QPainter *, const QRectF & ) const;
+
+    bool exportTo( QwtPlot *, const QString &documentName,
+        const QSizeF &sizeMM = QSizeF( 300, 200 ), int resolution = 85 );
+
+private:
+    void buildCanvasMaps( const QwtPlot *,
+        const QRectF &, QwtScaleMap maps[] ) const;
+
+    bool updateCanvasMargins( QwtPlot *,
+        const QRectF &, const QwtScaleMap maps[] ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::DiscardFlags )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::LayoutFlags )
+
+#endif
diff --git a/qwt/qwt_plot_rescaler.cpp b/qwt/qwt_plot_rescaler.cpp
new file mode 100644
index 0000000..54843fa
--- /dev/null
+++ b/qwt/qwt_plot_rescaler.cpp
@@ -0,0 +1,631 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_rescaler.h"
+#include "qwt_plot.h"
+#include "qwt_scale_div.h"
+#include "qwt_interval.h"
+#include "qwt_plot_canvas.h"
+#include <qevent.h>
+#include <qalgorithms.h>
+
+class QwtPlotRescaler::AxisData
+{
+public:
+    AxisData():
+        aspectRatio( 1.0 ),
+        expandingDirection( QwtPlotRescaler::ExpandUp )
+    {
+    }
+
+    double aspectRatio;
+    QwtInterval intervalHint;
+    QwtPlotRescaler::ExpandingDirection expandingDirection;
+    mutable QwtScaleDiv scaleDiv;
+};
+
+class QwtPlotRescaler::PrivateData
+{
+public:
+    PrivateData():
+        referenceAxis( QwtPlot::xBottom ),
+        rescalePolicy( QwtPlotRescaler::Expanding ),
+        isEnabled( false ),
+        inReplot( 0 )
+    {
+    }
+
+    int referenceAxis;
+    RescalePolicy rescalePolicy;
+    QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt];
+    bool isEnabled;
+
+    mutable int inReplot;
+};
+
+/*!
+   Constructor
+
+   \param canvas Canvas
+   \param referenceAxis Reference axis, see RescalePolicy
+   \param policy Rescale policy
+
+   \sa setRescalePolicy(), setReferenceAxis()
+*/
+QwtPlotRescaler::QwtPlotRescaler( QWidget *canvas,
+        int referenceAxis, RescalePolicy policy ):
+    QObject( canvas )
+{
+    d_data = new PrivateData;
+    d_data->referenceAxis = referenceAxis;
+    d_data->rescalePolicy = policy;
+
+    setEnabled( true );
+}
+
+//! Destructor
+QwtPlotRescaler::~QwtPlotRescaler()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/disable the rescaler
+
+  When enabled is true an event filter is installed for
+  the canvas, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPlotRescaler::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QWidget *w = canvas();
+        if ( w )
+        {
+            if ( d_data->isEnabled )
+                w->installEventFilter( this );
+            else
+                w->removeEventFilter( this );
+        }
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled, eventFilter()
+*/
+bool QwtPlotRescaler::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+  Change the rescale policy
+
+  \param policy Rescale policy
+  \sa rescalePolicy()
+*/
+void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
+{
+    d_data->rescalePolicy = policy;
+}
+
+/*!
+  \return Rescale policy
+  \sa setRescalePolicy()
+*/
+QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
+{
+    return d_data->rescalePolicy;
+}
+
+/*!
+  Set the reference axis ( see RescalePolicy )
+
+  \param axis Axis index ( QwtPlot::Axis )
+  \sa referenceAxis()
+*/
+void QwtPlotRescaler::setReferenceAxis( int axis )
+{
+    d_data->referenceAxis = axis;
+}
+
+/*!
+  \return Reference axis ( see RescalePolicy )
+  \sa setReferenceAxis()
+*/
+int QwtPlotRescaler::referenceAxis() const
+{
+    return d_data->referenceAxis;
+}
+
+/*!
+  Set the direction in which all axis should be expanded
+
+  \param direction Direction
+  \sa expandingDirection()
+*/
+void QwtPlotRescaler::setExpandingDirection(
+    ExpandingDirection direction )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        setExpandingDirection( axis, direction );
+}
+
+/*!
+  Set the direction in which an axis should be expanded
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param direction Direction
+  \sa expandingDirection()
+*/
+void QwtPlotRescaler::setExpandingDirection(
+    int axis, ExpandingDirection direction )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].expandingDirection = direction;
+}
+
+/*!
+  \return Direction in which an axis should be expanded
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \sa setExpandingDirection()
+*/
+QwtPlotRescaler::ExpandingDirection
+QwtPlotRescaler::expandingDirection( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].expandingDirection;
+
+    return ExpandBoth;
+}
+
+/*!
+  Set the aspect ratio between the scale of the reference axis
+  and the other scales. The default ratio is 1.0
+
+  \param ratio Aspect ratio
+  \sa aspectRatio()
+*/
+void QwtPlotRescaler::setAspectRatio( double ratio )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        setAspectRatio( axis, ratio );
+}
+
+/*!
+  Set the aspect ratio between the scale of the reference axis
+  and another scale. The default ratio is 1.0
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param ratio Aspect ratio
+  \sa aspectRatio()
+*/
+void QwtPlotRescaler::setAspectRatio( int axis, double ratio )
+{
+    if ( ratio < 0.0 )
+        ratio = 0.0;
+
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].aspectRatio = ratio;
+}
+
+/*!
+  \return Aspect ratio between an axis and the reference axis.
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \sa setAspectRatio()
+*/
+double QwtPlotRescaler::aspectRatio( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].aspectRatio;
+
+    return 0.0;
+}
+
+/*!
+  Set an interval hint for an axis
+
+  In Fitting mode, the hint is used as minimal interval
+  that always needs to be displayed.
+
+  \param axis Axis, see QwtPlot::Axis
+  \param interval Axis
+  \sa intervalHint(), RescalePolicy
+*/
+void QwtPlotRescaler::setIntervalHint( int axis,
+    const QwtInterval &interval )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].intervalHint = interval;
+}
+
+/*!
+  \param axis Axis, see QwtPlot::Axis
+  \return Interval hint
+  \sa setIntervalHint(), RescalePolicy
+*/
+QwtInterval QwtPlotRescaler::intervalHint( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].intervalHint;
+
+    return QwtInterval();
+}
+
+//! \return plot canvas
+QWidget *QwtPlotRescaler::canvas()
+{
+    return qobject_cast<QWidget *>( parent() );
+}
+
+//! \return plot canvas
+const QWidget *QwtPlotRescaler::canvas() const
+{
+    return qobject_cast<const QWidget *>( parent() );
+}
+
+//! \return plot widget
+QwtPlot *QwtPlotRescaler::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! \return plot widget
+const QwtPlot *QwtPlotRescaler::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+//!  Event filter for the plot canvas
+bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == canvas() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::Resize:
+            {
+                canvasResizeEvent( static_cast<QResizeEvent *>( event ) );
+                break;
+            }
+            case QEvent::PolishRequest:
+            {
+                rescale();
+                break;
+            }
+            default:;
+        }
+    }
+
+    return false;
+}
+
+/*!
+  Event handler for resize events of the plot canvas
+
+  \param event Resize event
+  \sa rescale()
+*/
+void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
+{
+    int left, top, right, bottom;
+    canvas()->getContentsMargins( &left, &top, &right, &bottom );
+
+    const QSize marginSize( left + right, top + bottom );
+
+    const QSize newSize = event->size() - marginSize;
+    const QSize oldSize = event->oldSize() - marginSize;
+
+    rescale( oldSize, newSize );
+}
+
+//! Adjust the plot axes scales
+void QwtPlotRescaler::rescale() const
+{
+    const QSize size = canvas()->contentsRect().size();
+    rescale( size, size );
+}
+
+/*!
+   Adjust the plot axes scales
+
+   \param oldSize Previous size of the canvas
+   \param newSize New size of the canvas
+*/
+void QwtPlotRescaler::rescale(
+    const QSize &oldSize, const QSize &newSize ) const
+{
+    if ( newSize.isEmpty() )
+        return;
+
+    QwtInterval intervals[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        intervals[axis] = interval( axis );
+
+    const int refAxis = referenceAxis();
+    intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
+            intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
+    }
+
+    updateScales( intervals );
+}
+
+/*!
+  Calculate the new scale interval of a plot axis
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param oldSize Previous size of the canvas
+  \param newSize New size of the canvas
+
+  \return Calculated new interval for the axis
+*/
+QwtInterval QwtPlotRescaler::expandScale( int axis,
+        const QSize &oldSize, const QSize &newSize ) const
+{
+    const QwtInterval oldInterval = interval( axis );
+
+    QwtInterval expanded = oldInterval;
+    switch ( rescalePolicy() )
+    {
+        case Fixed:
+        {
+            break; // do nothing
+        }
+        case Expanding:
+        {
+            if ( !oldSize.isEmpty() )
+            {
+                double width = oldInterval.width();
+                if ( orientation( axis ) == Qt::Horizontal )
+                    width *= double( newSize.width() ) / oldSize.width();
+                else
+                    width *= double( newSize.height() ) / oldSize.height();
+
+                expanded = expandInterval( oldInterval,
+                    width, expandingDirection( axis ) );
+            }
+            break;
+        }
+        case Fitting:
+        {
+            double dist = 0.0;
+            for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
+            {
+                const double d = pixelDist( ax, newSize );
+                if ( d > dist )
+                    dist = d;
+            }
+            if ( dist > 0.0 )
+            {
+                double width;
+                if ( orientation( axis ) == Qt::Horizontal )
+                    width = newSize.width() * dist;
+                else
+                    width = newSize.height() * dist;
+
+                expanded = expandInterval( intervalHint( axis ),
+                    width, expandingDirection( axis ) );
+            }
+            break;
+        }
+    }
+
+    return expanded;
+}
+
+/*!
+  Synchronize an axis scale according to the scale of the reference axis
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param reference Interval of the reference axis
+  \param size Size of the canvas
+
+  \return New interval for axis
+*/
+QwtInterval QwtPlotRescaler::syncScale( int axis,
+    const QwtInterval& reference, const QSize &size ) const
+{
+    double dist;
+    if ( orientation( referenceAxis() ) == Qt::Horizontal )
+        dist = reference.width() / size.width();
+    else
+        dist = reference.width() / size.height();
+
+    if ( orientation( axis ) == Qt::Horizontal )
+        dist *= size.width();
+    else
+        dist *= size.height();
+
+    dist /= aspectRatio( axis );
+
+    QwtInterval intv;
+    if ( rescalePolicy() == Fitting )
+        intv = intervalHint( axis );
+    else
+        intv = interval( axis );
+
+    intv = expandInterval( intv, dist, expandingDirection( axis ) );
+
+    return intv;
+}
+
+/*!
+  \return Orientation of an axis
+  \param axis Axis index ( see QwtPlot::AxisId )
+*/
+Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
+{
+    if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
+        return Qt::Vertical;
+
+    return Qt::Horizontal;
+}
+
+/*!
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \return Normalized interval of an axis
+*/
+QwtInterval QwtPlotRescaler::interval( int axis ) const
+{
+    if ( axis < 0 || axis >= QwtPlot::axisCnt )
+        return QwtInterval();
+
+    return plot()->axisScaleDiv( axis ).interval().normalized();
+}
+
+/*!
+  Expand the interval
+
+  \param interval Interval to be expanded
+  \param width Distance to be added to the interval
+  \param direction Direction of the expand operation
+
+  \return Expanded interval
+*/
+QwtInterval QwtPlotRescaler::expandInterval(
+    const QwtInterval &interval, double width,
+    ExpandingDirection direction ) const
+{
+    QwtInterval expanded = interval;
+
+    switch ( direction )
+    {
+        case ExpandUp:
+            expanded.setMinValue( interval.minValue() );
+            expanded.setMaxValue( interval.minValue() + width );
+            break;
+
+        case ExpandDown:
+            expanded.setMaxValue( interval.maxValue() );
+            expanded.setMinValue( interval.maxValue() - width );
+            break;
+
+        case ExpandBoth:
+        default:
+            expanded.setMinValue( interval.minValue() +
+                interval.width() / 2.0 - width / 2.0 );
+            expanded.setMaxValue( expanded.minValue() + width );
+    }
+    return expanded;
+}
+
+double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
+{
+    const QwtInterval intv = intervalHint( axis );
+
+    double dist = 0.0;
+    if ( !intv.isNull() )
+    {
+        if ( axis == referenceAxis() )
+            dist = intv.width();
+        else
+        {
+            const double r = aspectRatio( axis );
+            if ( r > 0.0 )
+                dist = intv.width() * r;
+        }
+    }
+
+    if ( dist > 0.0 )
+    {
+        if ( orientation( axis ) == Qt::Horizontal )
+            dist /= size.width();
+        else
+            dist /= size.height();
+    }
+
+    return dist;
+}
+
+/*!
+   Update the axes scales
+
+   \param intervals Scale intervals
+*/
+void QwtPlotRescaler::updateScales(
+    QwtInterval intervals[QwtPlot::axisCnt] ) const
+{
+    if ( d_data->inReplot >= 5 )
+    {
+        return;
+    }
+
+    QwtPlot *plt = const_cast<QwtPlot *>( plot() );
+
+    const bool doReplot = plt->autoReplot();
+    plt->setAutoReplot( false );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
+        {
+            double v1 = intervals[axis].minValue();
+            double v2 = intervals[axis].maxValue();
+
+            if ( !plt->axisScaleDiv( axis ).isIncreasing() )
+                qSwap( v1, v2 );
+
+            if ( d_data->inReplot >= 1 )
+                d_data->axisData[axis].scaleDiv = plt->axisScaleDiv( axis );
+
+            if ( d_data->inReplot >= 2 )
+            {
+                QList<double> ticks[QwtScaleDiv::NTickTypes];
+                for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+                    ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i );
+
+                plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) );
+            }
+            else
+            {
+                plt->setAxisScale( axis, v1, v2 );
+            }
+        }
+    }
+
+    QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>( plt->canvas() );
+
+    bool immediatePaint = false;
+    if ( canvas )
+    {
+        immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
+        canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
+    }
+
+    plt->setAutoReplot( doReplot );
+
+    d_data->inReplot++;
+    plt->replot();
+    d_data->inReplot--;
+
+    if ( canvas && immediatePaint )
+    {
+        canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
+    }
+}
diff --git a/qwt/qwt_plot_rescaler.h b/qwt/qwt_plot_rescaler.h
new file mode 100644
index 0000000..0a4890f
--- /dev/null
+++ b/qwt/qwt_plot_rescaler.h
@@ -0,0 +1,142 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RESCALER_H
+#define QWT_PLOT_RESCALER_H 1
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include "qwt_plot.h"
+#include <qobject.h>
+
+class QwtPlot;
+class QResizeEvent;
+
+/*!
+    \brief QwtPlotRescaler takes care of fixed aspect ratios for plot scales
+
+    QwtPlotRescaler auto adjusts the axes of a QwtPlot according
+    to fixed aspect ratios.
+*/
+
+class QWT_EXPORT QwtPlotRescaler: public QObject
+{
+public:
+    /*!
+      The rescale policy defines how to rescale the reference axis and
+      their depending axes.
+
+      \sa ExpandingDirection, setIntervalHint()
+    */
+    enum RescalePolicy
+    {
+        /*!
+          The interval of the reference axis remains unchanged, when the
+          geometry of the canvas changes. All other axes
+          will be adjusted according to their aspect ratio.
+         */
+        Fixed,
+
+        /*!
+          The interval of the reference axis will be shrunk/expanded,
+          when the geometry of the canvas changes. All other axes
+          will be adjusted according to their aspect ratio.
+
+          The interval, that is represented by one pixel is fixed.
+
+         */
+        Expanding,
+
+        /*!
+          The intervals of the axes are calculated, so that all axes include
+          their interval hint.
+         */
+        Fitting
+    };
+
+    /*!
+       When rescalePolicy() is set to Expanding its direction depends
+       on ExpandingDirection
+     */
+    enum ExpandingDirection
+    {
+        //! The upper limit of the scale is adjusted
+        ExpandUp,
+
+        //! The lower limit of the scale is adjusted
+        ExpandDown,
+
+        //! Both limits of the scale are adjusted
+        ExpandBoth
+    };
+
+    explicit QwtPlotRescaler( QWidget *canvas,
+        int referenceAxis = QwtPlot::xBottom,
+        RescalePolicy = Expanding );
+
+    virtual ~QwtPlotRescaler();
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    void setRescalePolicy( RescalePolicy );
+    RescalePolicy rescalePolicy() const;
+
+    void setExpandingDirection( ExpandingDirection );
+    void setExpandingDirection( int axis, ExpandingDirection );
+    ExpandingDirection expandingDirection( int axis ) const;
+
+    void setReferenceAxis( int axis );
+    int referenceAxis() const;
+
+    void setAspectRatio( double ratio );
+    void setAspectRatio( int axis, double ratio );
+    double aspectRatio( int axis ) const;
+
+    void setIntervalHint( int axis, const QwtInterval& );
+    QwtInterval intervalHint( int axis ) const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    void rescale() const;
+
+protected:
+    virtual void canvasResizeEvent( QResizeEvent * );
+
+    virtual void rescale( const QSize &oldSize, const QSize &newSize ) const;
+    virtual QwtInterval expandScale( 
+        int axis, const QSize &oldSize, const QSize &newSize ) const;
+
+    virtual QwtInterval syncScale(
+        int axis, const QwtInterval& reference,
+        const QSize &size ) const;
+
+    virtual void updateScales(
+        QwtInterval intervals[QwtPlot::axisCnt] ) const;
+
+    Qt::Orientation orientation( int axis ) const;
+    QwtInterval interval( int axis ) const;
+    QwtInterval expandInterval( const QwtInterval &,
+        double width, ExpandingDirection ) const;
+
+private:
+    double pixelDist( int axis, const QSize & ) const;
+
+    class AxisData;
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_scaleitem.cpp b/qwt/qwt_plot_scaleitem.cpp
new file mode 100644
index 0000000..aeed6be
--- /dev/null
+++ b/qwt/qwt_plot_scaleitem.cpp
@@ -0,0 +1,458 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_scaleitem.h"
+#include "qwt_plot.h"
+#include "qwt_scale_map.h"
+#include "qwt_interval.h"
+#include <qpalette.h>
+#include <qpainter.h>
+
+class QwtPlotScaleItem::PrivateData
+{
+public:
+    PrivateData():
+        position( 0.0 ),
+        borderDistance( -1 ),
+        scaleDivFromAxis( true ),
+        scaleDraw( new QwtScaleDraw() )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete scaleDraw;
+    }
+
+    void updateBorders( const QRectF &,
+        const QwtScaleMap &, const QwtScaleMap & );
+
+    QPalette palette;
+    QFont font;
+    double position;
+    int borderDistance;
+    bool scaleDivFromAxis;
+    QwtScaleDraw *scaleDraw;
+    QRectF canvasRectCache;
+};
+
+void QwtPlotScaleItem::PrivateData::updateBorders( const QRectF &canvasRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap )
+{
+    QwtInterval interval;
+    if ( scaleDraw->orientation() == Qt::Horizontal )
+    {
+        interval.setMinValue( xMap.invTransform( canvasRect.left() ) );
+        interval.setMaxValue( xMap.invTransform( canvasRect.right() - 1 ) );
+    }
+    else
+    {
+        interval.setMinValue( yMap.invTransform( canvasRect.bottom() - 1 ) );
+        interval.setMaxValue( yMap.invTransform( canvasRect.top() ) );
+    }
+
+    QwtScaleDiv scaleDiv = scaleDraw->scaleDiv();
+    scaleDiv.setInterval( interval );
+    scaleDraw->setScaleDiv( scaleDiv );
+}
+
+/*!
+   \brief Constructor for scale item at the position pos.
+
+   \param alignment In case of QwtScaleDraw::BottomScale or QwtScaleDraw::TopScale
+                    the scale item is corresponding to the xAxis(),
+                    otherwise it corresponds to the yAxis().
+
+   \param pos x or y position, depending on the corresponding axis.
+
+   \sa setPosition(), setAlignment()
+*/
+QwtPlotScaleItem::QwtPlotScaleItem(
+        QwtScaleDraw::Alignment alignment, const double pos ):
+    QwtPlotItem( QwtText( "Scale" ) )
+{
+    d_data = new PrivateData;
+    d_data->position = pos;
+    d_data->scaleDraw->setAlignment( alignment );
+
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+    setZ( 11.0 );
+}
+
+//! Destructor
+QwtPlotScaleItem::~QwtPlotScaleItem()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotScale
+int QwtPlotScaleItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotScale;
+}
+
+/*!
+   \brief Assign a scale division
+
+   When assigning a scaleDiv the scale division won't be synchronized
+   with the corresponding axis anymore.
+
+   \param scaleDiv Scale division
+   \sa scaleDiv(), setScaleDivFromAxis(), isScaleDivFromAxis()
+*/
+void QwtPlotScaleItem::setScaleDiv( const QwtScaleDiv& scaleDiv )
+{
+    d_data->scaleDivFromAxis = false;
+    d_data->scaleDraw->setScaleDiv( scaleDiv );
+}
+
+//! \return Scale division
+const QwtScaleDiv& QwtPlotScaleItem::scaleDiv() const
+{
+    return d_data->scaleDraw->scaleDiv();
+}
+
+/*!
+   Enable/Disable the synchronization of the scale division with
+   the corresponding axis.
+
+   \param on true/false
+   \sa isScaleDivFromAxis()
+*/
+void QwtPlotScaleItem::setScaleDivFromAxis( bool on )
+{
+    if ( on != d_data->scaleDivFromAxis )
+    {
+        d_data->scaleDivFromAxis = on;
+        if ( on )
+        {
+            const QwtPlot *plt = plot();
+            if ( plt )
+            {
+                updateScaleDiv( plt->axisScaleDiv( xAxis() ),
+                    plt->axisScaleDiv( yAxis() ) );
+                itemChanged();
+            }
+        }
+    }
+}
+
+/*!
+   \return True, if the synchronization of the scale division with
+           the corresponding axis is enabled.
+   \sa setScaleDiv(), setScaleDivFromAxis()
+*/
+bool QwtPlotScaleItem::isScaleDivFromAxis() const
+{
+    return d_data->scaleDivFromAxis;
+}
+
+/*!
+   Set the palette
+   \sa QwtAbstractScaleDraw::draw(), palette()
+*/
+void QwtPlotScaleItem::setPalette( const QPalette &palette )
+{
+    if ( palette != d_data->palette )
+    {
+        d_data->palette = palette;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+   \return palette
+   \sa setPalette()
+*/
+QPalette QwtPlotScaleItem::palette() const
+{
+    return d_data->palette;
+}
+
+/*!
+   Change the tick label font
+   \sa font()
+*/
+void QwtPlotScaleItem::setFont( const QFont &font )
+{
+    if ( font != d_data->font )
+    {
+        d_data->font = font;
+        itemChanged();
+    }
+}
+
+/*!
+   \return tick label font
+   \sa setFont()
+*/
+QFont QwtPlotScaleItem::font() const
+{
+    return d_data->font;
+}
+
+/*!
+  \brief Set a scale draw
+
+  \param scaleDraw object responsible for drawing scales.
+
+  The main use case for replacing the default QwtScaleDraw is
+  to overload QwtAbstractScaleDraw::label, to replace or swallow
+  tick labels.
+
+  \sa scaleDraw()
+*/
+void QwtPlotScaleItem::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    if ( scaleDraw == NULL )
+        return;
+
+    if ( scaleDraw != d_data->scaleDraw )
+        delete d_data->scaleDraw;
+
+    d_data->scaleDraw = scaleDraw;
+
+    const QwtPlot *plt = plot();
+    if ( plt )
+    {
+        updateScaleDiv( plt->axisScaleDiv( xAxis() ),
+            plt->axisScaleDiv( yAxis() ) );
+    }
+
+    itemChanged();
+}
+
+/*!
+   \return Scale draw
+   \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtPlotScaleItem::scaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+   \return Scale draw
+   \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtPlotScaleItem::scaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+   Change the position of the scale
+
+   The position is interpreted as y value for horizontal axes
+   and as x value for vertical axes.
+
+   The border distance is set to -1.
+
+   \param pos New position
+   \sa position(), setAlignment()
+*/
+void QwtPlotScaleItem::setPosition( double pos )
+{
+    if ( d_data->position != pos )
+    {
+        d_data->position = pos;
+        d_data->borderDistance = -1;
+        itemChanged();
+    }
+}
+
+/*!
+   \return Position of the scale
+   \sa setPosition(), setAlignment()
+*/
+double QwtPlotScaleItem::position() const
+{
+    return d_data->position;
+}
+
+/*!
+   \brief Align the scale to the canvas
+
+   If distance is >= 0 the scale will be aligned to a
+   border of the contents rectangle of the canvas. If
+   alignment() is QwtScaleDraw::LeftScale, the scale will
+   be aligned to the right border, if it is QwtScaleDraw::TopScale
+   it will be aligned to the bottom (and vice versa),
+
+   If distance is < 0 the scale will be at the position().
+
+   \param distance Number of pixels between the canvas border and the
+                   backbone of the scale.
+
+   \sa setPosition(), borderDistance()
+*/
+void QwtPlotScaleItem::setBorderDistance( int distance )
+{
+    if ( distance < 0 )
+        distance = -1;
+
+    if ( distance != d_data->borderDistance )
+    {
+        d_data->borderDistance = distance;
+        itemChanged();
+    }
+}
+
+/*!
+   \return Distance from a canvas border
+   \sa setBorderDistance(), setPosition()
+*/
+int QwtPlotScaleItem::borderDistance() const
+{
+    return d_data->borderDistance;
+}
+
+/*!
+   Change the alignment of the scale
+
+   The alignment sets the orientation of the scale and the position of
+   the ticks:
+
+   - QwtScaleDraw::BottomScale: horizontal, ticks below
+   - QwtScaleDraw::TopScale: horizontal, ticks above
+   - QwtScaleDraw::LeftScale: vertical, ticks left
+   - QwtScaleDraw::RightScale: vertical, ticks right
+
+   For horizontal scales the position corresponds to QwtPlotItem::yAxis(),
+   otherwise to QwtPlotItem::xAxis().
+
+   \sa scaleDraw(), QwtScaleDraw::alignment(), setPosition()
+*/
+void QwtPlotScaleItem::setAlignment( QwtScaleDraw::Alignment alignment )
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( sd->alignment() != alignment )
+    {
+        sd->setAlignment( alignment );
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Draw the scale
+*/
+void QwtPlotScaleItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( d_data->scaleDivFromAxis )
+    {
+        if ( canvasRect != d_data->canvasRectCache )
+        {
+            d_data->updateBorders( canvasRect, xMap, yMap );
+            d_data->canvasRectCache = canvasRect;
+        }
+    }
+
+    QPen pen = painter->pen();
+    pen.setStyle( Qt::SolidLine );
+    painter->setPen( pen );
+
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( sd->orientation() == Qt::Horizontal )
+    {
+        double y;
+        if ( d_data->borderDistance >= 0 )
+        {
+            if ( sd->alignment() == QwtScaleDraw::BottomScale )
+                y = canvasRect.top() + d_data->borderDistance;
+            else
+            {
+                y = canvasRect.bottom() - d_data->borderDistance;
+            }
+
+        }
+        else
+        {
+            y = yMap.transform( d_data->position );
+        }
+
+        if ( y < canvasRect.top() || y > canvasRect.bottom() )
+            return;
+
+        sd->move( canvasRect.left(), y );
+        sd->setLength( canvasRect.width() - 1 );
+
+        QwtTransform *transform = NULL;
+        if ( xMap.transformation() )
+            transform = xMap.transformation()->copy();
+
+        sd->setTransformation( transform );
+    }
+    else // == Qt::Vertical
+    {
+        double x;
+        if ( d_data->borderDistance >= 0 )
+        {
+            if ( sd->alignment() == QwtScaleDraw::RightScale )
+                x = canvasRect.left() + d_data->borderDistance;
+            else
+            {
+                x = canvasRect.right() - d_data->borderDistance;
+            }
+        }
+        else
+        {
+            x = xMap.transform( d_data->position );
+        }
+        if ( x < canvasRect.left() || x > canvasRect.right() )
+            return;
+
+        sd->move( x, canvasRect.top() );
+        sd->setLength( canvasRect.height() - 1 );
+
+        QwtTransform *transform = NULL;
+        if ( yMap.transformation() )
+            transform = yMap.transformation()->copy();
+
+        sd->setTransformation( transform );
+    }
+
+    painter->setFont( d_data->font );
+
+    sd->draw( painter, d_data->palette );
+}
+
+/*!
+   \brief Update the item to changes of the axes scale division
+
+   In case of isScaleDivFromAxis(), the scale draw is synchronized
+   to the correspond axis.
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes()
+*/
+
+void QwtPlotScaleItem::updateScaleDiv( const QwtScaleDiv& xScaleDiv,
+    const QwtScaleDiv& yScaleDiv )
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( d_data->scaleDivFromAxis && sd )
+    {
+        sd->setScaleDiv(
+            sd->orientation() == Qt::Horizontal ? xScaleDiv : yScaleDiv );
+
+        const QwtPlot *plt = plot();
+        if ( plt != NULL )
+        {
+            d_data->updateBorders( plt->canvas()->contentsRect(),
+                plt->canvasMap( xAxis() ), plt->canvasMap( yAxis() ) );
+            d_data->canvasRectCache = QRect();
+        }
+    }
+}
diff --git a/qwt/qwt_plot_scaleitem.h b/qwt/qwt_plot_scaleitem.h
new file mode 100644
index 0000000..ead67a3
--- /dev/null
+++ b/qwt/qwt_plot_scaleitem.h
@@ -0,0 +1,94 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SCALE_ITEM_H
+#define QWT_PLOT_SCALE_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_scale_draw.h"
+
+class QPalette;
+
+/*!
+  \brief A class which draws a scale inside the plot canvas
+
+  QwtPlotScaleItem can be used to draw an axis inside the plot canvas.
+  It might by synchronized to one of the axis of the plot, but can
+  also display its own ticks and labels.
+
+  It is allowed to synchronize the scale item with a disabled axis.
+  In plots with vertical and horizontal scale items, it might be
+  necessary to remove ticks at the intersections, by overloading
+  updateScaleDiv().
+
+  The scale might be at a specific position (f.e 0.0) or it might be
+  aligned to a canvas border.
+
+  \par Example
+  The following example shows how to replace the left axis, by a scale item
+  at the x position 0.0.
+  \verbatim
+QwtPlotScaleItem *scaleItem =
+    new QwtPlotScaleItem(QwtScaleDraw::RightScale, 0.0);
+scaleItem->setFont(plot->axisWidget(QwtPlot::yLeft)->font());
+scaleItem->attach(plot);
+
+plot->enableAxis(QwtPlot::yLeft, false);
+\endverbatim
+*/
+
+class QWT_EXPORT QwtPlotScaleItem: public QwtPlotItem
+{
+public:
+    explicit QwtPlotScaleItem(
+        QwtScaleDraw::Alignment = QwtScaleDraw::BottomScale,
+        const double pos = 0.0 );
+
+    virtual ~QwtPlotScaleItem();
+
+    virtual int rtti() const;
+
+    void setScaleDiv( const QwtScaleDiv& );
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setScaleDivFromAxis( bool on );
+    bool isScaleDivFromAxis() const;
+
+    void setPalette( const QPalette & );
+    QPalette palette() const;
+
+    void setFont( const QFont& );
+    QFont font() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+
+    const QwtScaleDraw *scaleDraw() const;
+    QwtScaleDraw *scaleDraw();
+
+    void setPosition( double pos );
+    double position() const;
+
+    void setBorderDistance( int numPixels );
+    int borderDistance() const;
+
+    void setAlignment( QwtScaleDraw::Alignment );
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual void updateScaleDiv( const QwtScaleDiv &, const QwtScaleDiv & );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_seriesitem.cpp b/qwt/qwt_plot_seriesitem.cpp
new file mode 100644
index 0000000..e5007ef
--- /dev/null
+++ b/qwt/qwt_plot_seriesitem.cpp
@@ -0,0 +1,112 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_seriesitem.h"
+
+class QwtPlotSeriesItem::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Vertical )
+    {
+    }
+
+    Qt::Orientation orientation;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSeriesItem::QwtPlotSeriesItem( const QwtText &title ):
+    QwtPlotItem( title )
+{
+    d_data = new PrivateData();
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSeriesItem::QwtPlotSeriesItem( const QString &title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPlotSeriesItem::~QwtPlotSeriesItem()
+{
+    delete d_data;
+}
+
+/*!
+  Set the orientation of the item.
+
+  The orientation() might be used in specific way by a plot item.
+  F.e. a QwtPlotCurve uses it to identify how to display the curve
+  int QwtPlotCurve::Steps or QwtPlotCurve::Sticks style.
+
+  \sa orientation()
+*/
+void QwtPlotSeriesItem::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation != orientation )
+    {
+        d_data->orientation = orientation;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Orientation of the plot item
+  \sa setOrientation()
+*/
+Qt::Orientation QwtPlotSeriesItem::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Draw the complete series
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+*/
+void QwtPlotSeriesItem::draw( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const
+{
+    drawSeries( painter, xMap, yMap, canvasRect, 0, -1 );
+}
+
+QRectF QwtPlotSeriesItem::boundingRect() const
+{
+    return dataRect();
+}
+
+void QwtPlotSeriesItem::updateScaleDiv(
+    const QwtScaleDiv &xScaleDiv, const QwtScaleDiv &yScaleDiv )
+{   
+    const QRectF rect = QRectF(
+        xScaleDiv.lowerBound(), yScaleDiv.lowerBound(),
+        xScaleDiv.range(), yScaleDiv.range() );
+        
+    setRectOfInterest( rect );
+}   
+
+void QwtPlotSeriesItem::dataChanged()
+{
+    itemChanged();
+}
diff --git a/qwt/qwt_plot_seriesitem.h b/qwt/qwt_plot_seriesitem.h
new file mode 100644
index 0000000..f58334a
--- /dev/null
+++ b/qwt/qwt_plot_seriesitem.h
@@ -0,0 +1,66 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SERIES_ITEM_H
+#define QWT_PLOT_SERIES_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_scale_div.h"
+#include "qwt_series_data.h"
+#include "qwt_series_store.h"
+
+/*!
+  \brief Base class for plot items representing a series of samples
+*/
+class QWT_EXPORT QwtPlotSeriesItem: public QwtPlotItem,
+    public virtual QwtAbstractSeriesStore
+{
+public:
+    explicit QwtPlotSeriesItem( const QString &title = QString::null );
+    explicit QwtPlotSeriesItem( const QwtText &title );
+
+    virtual ~QwtPlotSeriesItem();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF & ) const;
+
+    /*!
+      Draw a subset of the samples
+
+      \param painter Painter
+      \param xMap Maps x-values into pixel coordinates.
+      \param yMap Maps y-values into pixel coordinates.
+      \param canvasRect Contents rectangle of the canvas
+      \param from Index of the first point to be painted
+      \param to Index of the last point to be painted. If to < 0 the
+             curve will be painted to its last point.
+    */
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const = 0;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv &, const QwtScaleDiv & );
+
+protected:
+    virtual void dataChanged();
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_shapeitem.cpp b/qwt/qwt_plot_shapeitem.cpp
new file mode 100644
index 0000000..d2ece19
--- /dev/null
+++ b/qwt/qwt_plot_shapeitem.cpp
@@ -0,0 +1,491 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_shapeitem.h"
+#include "qwt_scale_map.h"
+#include "qwt_painter.h"
+#include "qwt_curve_fitter.h"
+#include "qwt_clipper.h"
+
+static QPainterPath qwtTransformPath( const QwtScaleMap &xMap,
+        const QwtScaleMap &yMap, const QPainterPath &path, bool doAlign )
+{
+    QPainterPath shape;
+    shape.setFillRule( path.fillRule() );
+
+    for ( int i = 0; i < path.elementCount(); i++ )
+    {
+        const QPainterPath::Element &element = path.elementAt( i );
+
+        double x = xMap.transform( element.x );
+        double y = yMap.transform( element.y );
+
+        switch( element.type )
+        {
+            case QPainterPath::MoveToElement:
+            {
+                if ( doAlign )
+                {
+                    x = qRound( x );
+                    y = qRound( y );
+                }
+
+                shape.moveTo( x, y );
+                break;
+            }
+            case QPainterPath::LineToElement:
+            {
+                if ( doAlign )
+                {
+                    x = qRound( x );
+                    y = qRound( y );
+                }
+
+                shape.lineTo( x, y );
+                break;
+            }
+            case QPainterPath::CurveToElement:
+            {
+                const QPainterPath::Element& element1 = path.elementAt( ++i );
+                const double x1 = xMap.transform( element1.x );
+                const double y1 = yMap.transform( element1.y );
+
+                const QPainterPath::Element& element2 = path.elementAt( ++i );
+                const double x2 = xMap.transform( element2.x );
+                const double y2 = yMap.transform( element2.y );
+
+                shape.cubicTo( x, y, x1, y1, x2, y2 );
+                break;
+            }
+            case QPainterPath::CurveToDataElement:
+            {
+                break;
+            }
+        }
+    }
+
+    return shape;
+}
+
+
+class QwtPlotShapeItem::PrivateData
+{
+public:
+    PrivateData():
+        legendMode( QwtPlotShapeItem::LegendColor ),
+        renderTolerance( 0.0 )
+    {
+    }
+
+    QwtPlotShapeItem::PaintAttributes paintAttributes;
+    QwtPlotShapeItem::LegendMode legendMode;
+
+    double renderTolerance;
+    QRectF boundingRect;
+
+    QPen pen;
+    QBrush brush;
+    QPainterPath shape;
+};
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotShapeItem::QwtPlotShapeItem( const QString& title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    init();
+}
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotShapeItem::QwtPlotShapeItem( const QwtText& title ):
+    QwtPlotItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotShapeItem::~QwtPlotShapeItem()
+{
+    delete d_data;
+}
+
+void QwtPlotShapeItem::init()
+{
+    d_data = new PrivateData();
+    d_data->boundingRect = QwtPlotItem::boundingRect();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotShape
+int QwtPlotShapeItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotShape;
+}
+
+/*!
+  Specify an attribute how to draw the shape
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotShapeItem::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+  \return True, when attribute is enabled
+  \sa setPaintAttribute()
+*/
+bool QwtPlotShapeItem::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Set the mode how to represent the item on the legend
+
+  \param mode Mode
+  \sa legendMode()
+ */
+void QwtPlotShapeItem::setLegendMode( LegendMode mode )
+{
+    if ( mode != d_data->legendMode )
+    {
+        d_data->legendMode = mode;
+        legendChanged();
+    }
+}
+
+/*!
+  \return Mode how to represent the item on the legend
+  \sa legendMode()
+ */
+QwtPlotShapeItem::LegendMode QwtPlotShapeItem::legendMode() const
+{
+    return d_data->legendMode;
+}
+
+//! Bounding rectangle of the shape
+QRectF QwtPlotShapeItem::boundingRect() const
+{
+    return d_data->boundingRect;
+}
+
+/*!
+  \brief Set a path built from a rectangle
+
+  \param rect Rectangle
+  \sa setShape(), setPolygon(), shape()
+ */
+void QwtPlotShapeItem::setRect( const QRectF &rect ) 
+{
+    QPainterPath path;
+    path.addRect( rect );
+
+    setShape( path );
+}
+
+/*!
+  \brief Set a path built from a polygon
+
+  \param polygon Polygon
+  \sa setShape(), setRect(), shape()
+ */
+void QwtPlotShapeItem::setPolygon( const QPolygonF &polygon )
+{
+    QPainterPath shape;
+    shape.addPolygon( polygon );
+
+    setShape( shape );
+}
+
+/*!
+  \brief Set the shape to be displayed
+
+  \param shape Shape
+  \sa setShape(), shape()
+ */
+void QwtPlotShapeItem::setShape( const QPainterPath &shape )
+{
+    if ( shape != d_data->shape )
+    {
+        d_data->shape = shape;
+        if ( shape.isEmpty() )
+        {
+            d_data->boundingRect = QwtPlotItem::boundingRect();
+        }
+        else
+        {
+            d_data->boundingRect = shape.boundingRect();
+        }
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Shape to be displayed
+  \sa setShape()
+ */
+QPainterPath QwtPlotShapeItem::shape() const
+{
+    return d_data->shape;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotShapeItem::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Assign a pen
+
+  The pen is used to draw the outline of the shape
+
+  \param pen Pen
+  \sa pen(), brush()
+*/
+void QwtPlotShapeItem::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+    \return Pen used to draw the outline of the shape
+    \sa setPen(), brush()
+*/
+QPen QwtPlotShapeItem::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush.
+
+  The brush is used to fill the path
+
+  \param brush Brush
+  \sa brush(), pen()
+*/
+void QwtPlotShapeItem::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the shape
+  \sa setBrush(), pen()
+*/
+QBrush QwtPlotShapeItem::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Set the tolerance for the weeding optimization
+
+  After translating the shape into target device coordinate 
+  ( usually widget geometries ) the painter path can be simplified
+  by a point weeding algorithm ( Douglas-Peucker ).
+
+  For shapes built from curves and ellipses weeding might
+  have the opposite effect because they have to be expanded
+  to polygons.
+
+  \param tolerance Accepted error when reducing the number of points
+                   A value <= 0.0 disables weeding.
+
+  \sa renderTolerance(), QwtWeedingCurveFitter
+ */
+void QwtPlotShapeItem::setRenderTolerance( double tolerance )
+{
+    tolerance = qMax( tolerance, 0.0 );
+
+    if ( tolerance != d_data->renderTolerance )
+    {
+        d_data->renderTolerance = tolerance;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Tolerance for the weeding optimization
+  \sa setRenderTolerance()
+ */
+double QwtPlotShapeItem::renderTolerance() const
+{
+    return d_data->renderTolerance;
+}
+
+/*!
+  Draw the shape item
+
+  \param painter Painter
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param canvasRect Contents rect of the plot canvas
+*/
+void QwtPlotShapeItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( d_data->shape.isEmpty() )
+        return;
+
+    if ( d_data->pen.style() == Qt::NoPen 
+        && d_data->brush.style() == Qt::NoBrush )
+    {
+        return;
+    }
+
+    const QRectF cRect = QwtScaleMap::invTransform(
+        xMap, yMap, canvasRect.toRect() );
+
+    if ( d_data->boundingRect.intersects( cRect ) )
+    {
+        const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+        QPainterPath path = qwtTransformPath( xMap, yMap, 
+            d_data->shape, doAlign );
+
+        if ( testPaintAttribute( QwtPlotShapeItem::ClipPolygons ) )
+        {
+            qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
+            QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );
+
+            QPainterPath clippedPath;
+            clippedPath.setFillRule( path.fillRule() );
+
+            const QList<QPolygonF> polygons = path.toSubpathPolygons();
+            for ( int i = 0; i < polygons.size(); i++ )
+            {
+                const QPolygonF p = QwtClipper::clipPolygonF(
+                    clipRect, polygons[i], true );
+
+                clippedPath.addPolygon( p );
+
+            }
+
+            path = clippedPath;
+        }
+
+        if ( d_data->renderTolerance > 0.0 )
+        {
+            QwtWeedingCurveFitter fitter( d_data->renderTolerance );
+
+            QPainterPath fittedPath;
+            fittedPath.setFillRule( path.fillRule() );
+
+            const QList<QPolygonF> polygons = path.toSubpathPolygons();
+            for ( int i = 0; i < polygons.size(); i++ )
+                fittedPath.addPolygon( fitter.fitCurve( polygons[ i ] ) );
+
+            path = fittedPath;
+        }
+
+        painter->setPen( d_data->pen );
+        painter->setBrush( d_data->brush );
+
+        painter->drawPath( path );
+    }
+}
+
+/*!
+  \return A rectangle filled with the color of the brush ( or the pen )
+
+  \param index Index of the legend entry 
+                ( usually there is only one )
+  \param size Icon size
+
+  \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotShapeItem::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+
+    if ( size.isEmpty() )
+        return icon;
+
+    if ( d_data->legendMode == QwtPlotShapeItem::LegendShape )
+    {
+        const QRectF &br = d_data->boundingRect;
+
+        QPainter painter( &icon );
+        painter.setRenderHint( QPainter::Antialiasing,
+            testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+        painter.translate( -br.topLeft() );
+
+        painter.setPen( d_data->pen );
+        painter.setBrush( d_data->brush );
+        painter.drawPath( d_data->shape );
+    }
+    else
+    {
+        QColor iconColor;
+        if ( d_data->brush.style() != Qt::NoBrush )
+            iconColor = d_data->brush.color();
+        else
+            iconColor = d_data->pen.color();
+
+        icon = defaultIcon( iconColor, size );
+    }
+
+    return icon;
+}
+
diff --git a/qwt/qwt_plot_shapeitem.h b/qwt/qwt_plot_shapeitem.h
new file mode 100644
index 0000000..76f78a1
--- /dev/null
+++ b/qwt/qwt_plot_shapeitem.h
@@ -0,0 +1,111 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SHAPE_ITEM_H
+#define QWT_PLOT_SHAPE_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include <qpainterpath.h>
+
+/*!
+  \brief A plot item, which displays any graphical shape, 
+         that can be defined by a QPainterPath
+
+  A QPainterPath is a shape composed from intersecting and uniting
+  regions, rectangles, ellipses or irregular areas defined by lines, and curves.
+  QwtPlotShapeItem displays a shape with a pen and brush.
+
+  QwtPlotShapeItem offers a couple of optimizations like clipping or weeding.
+  These algorithms need to convert the painter path into polygons that might be
+  less performant for paths built from curves and ellipses.
+
+  \sa QwtPlotZone
+*/
+class QWT_EXPORT QwtPlotShapeItem: public QwtPlotItem
+{
+public:
+    /*!
+        Attributes to modify the drawing algorithm.
+        The default disables all attributes
+
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance
+
+          But polygon clipping will convert the painter path into
+          polygons what might introduce a negative impact on the
+          performance of paths composed from curves or ellipses.
+         */
+        ClipPolygons = 0x01,
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    //! Mode how to display the item on the legend
+    enum LegendMode
+    {
+        //! Display a scaled down version of the shape
+        LegendShape,
+
+        //! Display a filled rectangle 
+        LegendColor
+    };
+
+    explicit QwtPlotShapeItem( const QString &title = QString::null );
+    explicit QwtPlotShapeItem( const QwtText &title );
+
+    virtual ~QwtPlotShapeItem();
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLegendMode( LegendMode );
+    LegendMode legendMode() const;
+
+    void setRect( const QRectF & );
+    void setPolygon( const QPolygonF & );
+
+    void setShape( const QPainterPath & );
+    QPainterPath shape() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    QPen pen() const;
+
+    void setBrush( const QBrush & );
+    QBrush brush() const;
+
+    void setRenderTolerance( double );
+    double renderTolerance() const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+    virtual int rtti() const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_spectrocurve.cpp b/qwt/qwt_plot_spectrocurve.cpp
new file mode 100644
index 0000000..8af8037
--- /dev/null
+++ b/qwt/qwt_plot_spectrocurve.cpp
@@ -0,0 +1,321 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_spectrocurve.h"
+#include "qwt_color_map.h"
+#include "qwt_scale_map.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+
+class QwtPlotSpectroCurve::PrivateData
+{
+public:
+    PrivateData():
+        colorRange( 0.0, 1000.0 ),
+        penWidth(0.0),
+        paintAttributes( QwtPlotSpectroCurve::ClipPoints )
+    {
+        colorMap = new QwtLinearColorMap();
+    }
+
+    ~PrivateData()
+    {
+        delete colorMap;
+    }
+
+    QwtColorMap *colorMap;
+    QwtInterval colorRange;
+    QVector<QRgb> colorTable;
+    double penWidth;
+    QwtPlotSpectroCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotSpectroCurve::~QwtPlotSpectroCurve()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Initialize data members
+*/
+void QwtPlotSpectroCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend );
+    setItemAttribute( QwtPlotItem::AutoScale );
+
+    d_data = new PrivateData;
+    setData( new QwtPoint3DSeriesData() );
+
+    setZ( 20.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotSpectroCurve
+int QwtPlotSpectroCurve::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotSpectroCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  /sa PaintAttribute, testPaintAttribute()
+*/
+void QwtPlotSpectroCurve::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotSpectroCurve::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotSpectroCurve::setSamples( const QVector<QwtPoint3D> &samples )
+{
+    setData( new QwtPoint3DSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore. 
+*/
+void QwtPlotSpectroCurve::setSamples(
+    QwtSeriesData<QwtPoint3D> *data )
+{
+    setData( data );
+}  
+
+/*!
+  Change the color map
+
+  Often it is useful to display the mapping between intensities and
+  colors as an additional plot axis, showing a color bar.
+
+  \param colorMap Color Map
+
+  \sa colorMap(), setColorRange(), QwtColorMap::color(),
+      QwtScaleWidget::setColorBarEnabled(), QwtScaleWidget::setColorMap()
+*/
+void QwtPlotSpectroCurve::setColorMap( QwtColorMap *colorMap )
+{
+    if ( colorMap != d_data->colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Color Map used for mapping the intensity values to colors
+   \sa setColorMap(), setColorRange(), QwtColorMap::color()
+*/
+const QwtColorMap *QwtPlotSpectroCurve::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*!
+   Set the value interval, that corresponds to the color map
+
+   \param interval interval.minValue() corresponds to 0.0,
+                   interval.maxValue() to 1.0 on the color map.
+
+   \sa colorRange(), setColorMap(), QwtColorMap::color()
+*/
+void QwtPlotSpectroCurve::setColorRange( const QwtInterval &interval )
+{
+    if ( interval != d_data->colorRange )
+    {
+        d_data->colorRange = interval;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value interval, that corresponds to the color map
+  \sa setColorRange(), setColorMap(), QwtColorMap::color()
+*/
+QwtInterval &QwtPlotSpectroCurve::colorRange() const
+{
+    return d_data->colorRange;
+}
+
+/*!
+  Assign a pen width
+
+  \param penWidth New pen width
+  \sa penWidth()
+*/
+void QwtPlotSpectroCurve::setPenWidth(double penWidth)
+{
+    if ( penWidth < 0.0 )
+        penWidth = 0.0;
+
+    if ( d_data->penWidth != penWidth )
+    {
+        d_data->penWidth = penWidth;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen width used to draw a dot
+  \sa setPenWidth()
+*/
+double QwtPlotSpectroCurve::penWidth() const
+{
+    return d_data->penWidth;
+}
+
+/*!
+  Draw a subset of the points
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawDots()
+*/
+void QwtPlotSpectroCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( !painter || dataSize() <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    drawDots( painter, xMap, yMap, canvasRect, from, to );
+}
+
+/*!
+  Draw a subset of the points
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawSeries()
+*/
+void QwtPlotSpectroCurve::drawDots( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( !d_data->colorRange.isValid() )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    const QwtColorMap::Format format = d_data->colorMap->format();
+    if ( format == QwtColorMap::Indexed )
+        d_data->colorTable = d_data->colorMap->colorTable( d_data->colorRange );
+
+    const QwtSeriesData<QwtPoint3D> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtPoint3D sample = series->sample( i );
+
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( d_data->paintAttributes & QwtPlotSpectroCurve::ClipPoints )
+        {
+            if ( !canvasRect.contains( xi, yi ) )
+                continue;
+        }
+
+        if ( format == QwtColorMap::RGB )
+        {
+            const QRgb rgb = d_data->colorMap->rgb(
+                d_data->colorRange, sample.z() );
+
+            painter->setPen( QPen( QColor( rgb ), d_data->penWidth ) );
+        }
+        else
+        {
+            const unsigned char index = d_data->colorMap->colorIndex(
+                d_data->colorRange, sample.z() );
+
+            painter->setPen( QPen( QColor( d_data->colorTable[index] ), 
+                d_data->penWidth ) );
+        }
+
+        QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
+    }
+
+    d_data->colorTable.clear();
+}
diff --git a/qwt/qwt_plot_spectrocurve.h b/qwt/qwt_plot_spectrocurve.h
new file mode 100644
index 0000000..1972c3e
--- /dev/null
+++ b/qwt/qwt_plot_spectrocurve.h
@@ -0,0 +1,79 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CURVE_3D_H
+#define QWT_PLOT_CURVE_3D_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_series_data.h"
+
+class QwtSymbol;
+class QwtColorMap;
+
+/*!
+    \brief Curve that displays 3D points as dots, where the z coordinate is
+           mapped to a color.
+*/
+class QWT_EXPORT QwtPlotSpectroCurve: 
+    public QwtPlotSeriesItem, QwtSeriesStore<QwtPoint3D>
+{
+public:
+    //! Paint attributes
+    enum PaintAttribute
+    {
+        //! Clip points outside the canvas rectangle
+        ClipPoints = 1
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotSpectroCurve( const QString &title = QString::null );
+    explicit QwtPlotSpectroCurve( const QwtText &title );
+
+    virtual ~QwtPlotSpectroCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtPoint3D> & );
+    void setSamples( QwtSeriesData<QwtPoint3D> * );
+
+
+    void setColorMap( QwtColorMap * );
+    const QwtColorMap *colorMap() const;
+
+    void setColorRange( const QwtInterval & );
+    QwtInterval & colorRange() const;
+
+    virtual void drawSeries( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    void setPenWidth(double width);
+    double penWidth() const;
+
+protected:
+    virtual void drawDots( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectroCurve::PaintAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_spectrogram.cpp b/qwt/qwt_plot_spectrogram.cpp
new file mode 100644
index 0000000..0c59013
--- /dev/null
+++ b/qwt/qwt_plot_spectrogram.cpp
@@ -0,0 +1,660 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_spectrogram.h"
+#include "qwt_painter.h"
+#include "qwt_interval.h"
+#include "qwt_scale_map.h"
+#include "qwt_color_map.h"
+#include <qimage.h>
+#include <qpen.h>
+#include <qpainter.h>
+#include <qmath.h>
+#include <qalgorithms.h>
+#if QT_VERSION >= 0x040400
+#include <qthread.h>
+#include <qfuture.h>
+#include <qtconcurrentrun.h>
+#endif
+
+class QwtPlotSpectrogram::PrivateData
+{
+public:
+    PrivateData():
+        data( NULL )
+    {
+        colorMap = new QwtLinearColorMap();
+        displayMode = ImageMode;
+
+        conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel;
+#if 0
+        conrecFlags |= QwtRasterData::IgnoreOutOfRange;
+#endif
+    }
+    ~PrivateData()
+    {
+        delete data;
+        delete colorMap;
+    }
+
+    QwtRasterData *data;
+    QwtColorMap *colorMap;
+    DisplayModes displayMode;
+
+    QList<double> contourLevels;
+    QPen defaultContourPen;
+    QwtRasterData::ConrecFlags conrecFlags;
+};
+
+/*!
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 8.0.
+
+   \param title Title
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+QwtPlotSpectrogram::QwtPlotSpectrogram( const QString &title ):
+    QwtPlotRasterItem( title )
+{
+    d_data = new PrivateData();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+//! Destructor
+QwtPlotSpectrogram::~QwtPlotSpectrogram()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotSpectrogram
+int QwtPlotSpectrogram::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotSpectrogram;
+}
+
+/*!
+   The display mode controls how the raster data will be represented.
+
+   \param mode Display mode
+   \param on On/Off
+
+   The default setting enables ImageMode.
+
+   \sa DisplayMode, displayMode()
+*/
+void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on )
+{
+    if ( on != bool( mode & d_data->displayMode ) )
+    {
+        if ( on )
+            d_data->displayMode |= mode;
+        else
+            d_data->displayMode &= ~mode;
+    }
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   The display mode controls how the raster data will be represented.
+
+   \param mode Display mode
+   \return true if mode is enabled
+*/
+bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const
+{
+    return ( d_data->displayMode & mode );
+}
+
+/*!
+  Change the color map
+
+  Often it is useful to display the mapping between intensities and
+  colors as an additional plot axis, showing a color bar.
+
+  \param colorMap Color Map
+
+  \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
+      QwtScaleWidget::setColorMap()
+*/
+void QwtPlotSpectrogram::setColorMap( QwtColorMap *colorMap )
+{
+    if ( d_data->colorMap != colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+
+    invalidateCache();
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Color Map used for mapping the intensity values to colors
+   \sa setColorMap()
+*/
+const QwtColorMap *QwtPlotSpectrogram::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*! 
+  Build and assign the default pen for the contour lines
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotSpectrogram::setDefaultContourPen( 
+    const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setDefaultContourPen( QPen( color, width, style ) );
+}
+
+/*!
+   \brief Set the default pen for the contour lines
+
+   If the spectrogram has a valid default contour pen
+   a contour line is painted using the default contour pen.
+   Otherwise (pen.style() == Qt::NoPen) the pen is calculated
+   for each contour level using contourPen().
+
+   \sa defaultContourPen(), contourPen()
+*/
+void QwtPlotSpectrogram::setDefaultContourPen( const QPen &pen )
+{
+    if ( pen != d_data->defaultContourPen )
+    {
+        d_data->defaultContourPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+   \return Default contour pen
+   \sa setDefaultContourPen()
+*/
+QPen QwtPlotSpectrogram::defaultContourPen() const
+{
+    return d_data->defaultContourPen;
+}
+
+/*!
+   \brief Calculate the pen for a contour line
+
+   The color of the pen is the color for level calculated by the color map
+
+   \param level Contour level
+   \return Pen for the contour line
+   \note contourPen is only used if defaultContourPen().style() == Qt::NoPen
+
+   \sa setDefaultContourPen(), setColorMap(), setContourLevels()
+*/
+QPen QwtPlotSpectrogram::contourPen( double level ) const
+{
+    if ( d_data->data == NULL || d_data->colorMap == NULL )
+        return QPen();
+
+    const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis);
+    const QColor c( d_data->colorMap->rgb( intensityRange, level ) );
+
+    return QPen( c );
+}
+
+/*!
+   Modify an attribute of the CONREC algorithm, used to calculate
+   the contour lines.
+
+   \param flag CONREC flag
+   \param on On/Off
+
+   \sa testConrecFlag(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+void QwtPlotSpectrogram::setConrecFlag(
+    QwtRasterData::ConrecFlag flag, bool on )
+{
+    if ( bool( d_data->conrecFlags & flag ) == on )
+        return;
+
+    if ( on )
+        d_data->conrecFlags |= flag;
+    else
+        d_data->conrecFlags &= ~flag;
+
+    itemChanged();
+}
+
+/*!
+   Test an attribute of the CONREC algorithm, used to calculate
+   the contour lines.
+
+   \param flag CONREC flag
+   \return true, is enabled
+
+   The default setting enables QwtRasterData::IgnoreAllVerticesOnLevel
+
+   \sa setConrecClag(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+bool QwtPlotSpectrogram::testConrecFlag(
+    QwtRasterData::ConrecFlag flag ) const
+{
+    return d_data->conrecFlags & flag;
+}
+
+/*!
+   Set the levels of the contour lines
+
+   \param levels Values of the contour levels
+   \sa contourLevels(), renderContourLines(),
+       QwtRasterData::contourLines()
+
+   \note contourLevels returns the same levels but sorted.
+*/
+void QwtPlotSpectrogram::setContourLevels( const QList<double> &levels )
+{
+    d_data->contourLevels = levels;
+    qSort( d_data->contourLevels );
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Levels of the contour lines.
+
+   The levels are sorted in increasing order.
+
+   \sa contourLevels(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+QList<double> QwtPlotSpectrogram::contourLevels() const
+{
+    return d_data->contourLevels;
+}
+
+/*!
+  Set the data to be displayed
+
+  \param data Spectrogram Data
+  \sa data()
+*/
+void QwtPlotSpectrogram::setData( QwtRasterData *data )
+{
+    if ( data != d_data->data )
+    {
+        delete d_data->data;
+        d_data->data = data;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spectrogram data
+  \sa setData()
+*/
+const QwtRasterData *QwtPlotSpectrogram::data() const
+{
+    return d_data->data;
+}
+
+/*!
+  \return Spectrogram data
+  \sa setData()
+*/
+QwtRasterData *QwtPlotSpectrogram::data()
+{
+    return d_data->data;
+}
+
+/*!
+   \return Bounding interval for an axis
+
+   The default implementation returns the interval of the
+   associated raster data object.
+
+   \param axis X, Y, or Z axis
+   \sa QwtRasterData::interval()
+*/
+QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const
+{
+    if ( d_data->data == NULL )
+        return QwtInterval();
+
+    return d_data->data->interval( axis );
+}
+
+/*!
+   \brief Pixel hint
+
+   The geometry of a pixel is used to calculated the resolution and
+   alignment of the rendered image. 
+
+   The default implementation returns data()->pixelHint( rect );
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel
+
+   \sa QwtPlotRasterItem::pixelHint(), QwtRasterData::pixelHint(), 
+       render(), renderImage()
+*/
+QRectF QwtPlotSpectrogram::pixelHint( const QRectF &area ) const
+{
+    if ( d_data->data == NULL )
+        return QRectF();
+
+    return d_data->data->pixelHint( area );
+}
+
+/*!
+   \brief Render an image from data and color map.
+
+   For each pixel of area the value is mapped into a color.
+
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param area Requested area for the image in scale coordinates
+  \param imageSize Size of the requested image
+
+   \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
+           on the color map.
+
+   \sa QwtRasterData::value(), QwtColorMap::rgb(),
+       QwtColorMap::colorIndex()
+*/
+QImage QwtPlotSpectrogram::renderImage(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &area, const QSize &imageSize ) const
+{
+    if ( imageSize.isEmpty() || d_data->data == NULL 
+        || d_data->colorMap == NULL )
+    {
+        return QImage();
+    }
+
+    const QwtInterval intensityRange = d_data->data->interval( Qt::ZAxis );
+    if ( !intensityRange.isValid() )
+        return QImage();
+
+    QImage::Format format = ( d_data->colorMap->format() == QwtColorMap::RGB )
+        ? QImage::Format_ARGB32 : QImage::Format_Indexed8;
+
+    QImage image( imageSize, format );
+
+    if ( d_data->colorMap->format() == QwtColorMap::Indexed )
+        image.setColorTable( d_data->colorMap->colorTable( intensityRange ) );
+
+    d_data->data->initRaster( area, image.size() );
+
+#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
+    uint numThreads = renderThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = QThread::idealThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = 1;
+
+    const int numRows = imageSize.height() / numThreads;
+
+    QList< QFuture<void> > futures;
+    for ( uint i = 0; i < numThreads; i++ )
+    {
+        QRect tile( 0, i * numRows, image.width(), numRows );
+        if ( i == numThreads - 1 )
+        {
+            tile.setHeight( image.height() - i * numRows );
+            renderTile( xMap, yMap, tile, &image );
+        }
+        else
+        {
+            futures += QtConcurrent::run(
+                this, &QwtPlotSpectrogram::renderTile,
+                xMap, yMap, tile, &image );
+        }
+    }
+    for ( int i = 0; i < futures.size(); i++ )
+        futures[i].waitForFinished();
+
+#else // QT_VERSION < 0x040400
+    const QRect tile( 0, 0, image.width(), image.height() );
+    renderTile( xMap, yMap, tile, &image );
+#endif
+
+    d_data->data->discardRaster();
+
+    return image;
+}
+
+/*!
+    \brief Render a tile of an image.
+
+    Rendering in tiles can be used to composite an image in parallel
+    threads.
+
+    \param xMap X-Scale Map
+    \param yMap Y-Scale Map
+    \param tile Geometry of the tile in image coordinates
+    \param image Image to be rendered
+*/
+void QwtPlotSpectrogram::renderTile(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRect &tile, QImage *image ) const
+{
+    const QwtInterval range = d_data->data->interval( Qt::ZAxis );
+    if ( !range.isValid() )
+        return;
+
+    if ( d_data->colorMap->format() == QwtColorMap::RGB )
+    {
+        for ( int y = tile.top(); y <= tile.bottom(); y++ )
+        {
+            const double ty = yMap.invTransform( y );
+
+            QRgb *line = reinterpret_cast<QRgb *>( image->scanLine( y ) );
+            line += tile.left();
+
+            for ( int x = tile.left(); x <= tile.right(); x++ )
+            {
+                const double tx = xMap.invTransform( x );
+
+                *line++ = d_data->colorMap->rgb( range,
+                    d_data->data->value( tx, ty ) );
+            }
+        }
+    }
+    else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
+    {
+        for ( int y = tile.top(); y <= tile.bottom(); y++ )
+        {
+            const double ty = yMap.invTransform( y );
+
+            unsigned char *line = image->scanLine( y );
+            line += tile.left();
+
+            for ( int x = tile.left(); x <= tile.right(); x++ )
+            {
+                const double tx = xMap.invTransform( x );
+
+                *line++ = d_data->colorMap->colorIndex( range,
+                    d_data->data->value( tx, ty ) );
+            }
+        }
+    }
+}
+
+/*!
+   \brief Return the raster to be used by the CONREC contour algorithm.
+
+   A larger size will improve the precision of the CONREC algorithm,
+   but will slow down the time that is needed to calculate the lines.
+
+   The default implementation returns rect.size() / 2 bounded to
+   the resolution depending on pixelSize().
+
+   \param area Rectangle, where to calculate the contour lines
+   \param rect Rectangle in pixel coordinates, where to paint the contour lines
+   \return Raster to be used by the CONREC contour algorithm.
+
+   \note The size will be bounded to rect.size().
+
+   \sa drawContourLines(), QwtRasterData::contourLines()
+*/
+QSize QwtPlotSpectrogram::contourRasterSize( 
+    const QRectF &area, const QRect &rect ) const
+{
+    QSize raster = rect.size() / 2;
+
+    const QRectF pixelRect = pixelHint( area );
+    if ( !pixelRect.isEmpty() )
+    {
+        const QSize res( qCeil( rect.width() / pixelRect.width() ),
+            qCeil( rect.height() / pixelRect.height() ) );
+        raster = raster.boundedTo( res );
+    }
+
+    return raster;
+}
+
+/*!
+   Calculate contour lines
+
+   \param rect Rectangle, where to calculate the contour lines
+   \param raster Raster, used by the CONREC algorithm
+   \return Calculated contour lines
+
+   \sa contourLevels(), setConrecFlag(),
+       QwtRasterData::contourLines()
+*/
+QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
+    const QRectF &rect, const QSize &raster ) const
+{
+    if ( d_data->data == NULL )
+        return QwtRasterData::ContourLines();
+
+    return d_data->data->contourLines( rect, raster,
+        d_data->contourLevels, d_data->conrecFlags );
+}
+
+/*!
+   Paint the contour lines
+
+   \param painter Painter
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param contourLines Contour lines
+
+   \sa renderContourLines(), defaultContourPen(), contourPen()
+*/
+void QwtPlotSpectrogram::drawContourLines( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtRasterData::ContourLines &contourLines ) const
+{
+    if ( d_data->data == NULL )
+        return;
+
+    const int numLevels = d_data->contourLevels.size();
+    for ( int l = 0; l < numLevels; l++ )
+    {
+        const double level = d_data->contourLevels[l];
+
+        QPen pen = defaultContourPen();
+        if ( pen.style() == Qt::NoPen )
+            pen = contourPen( level );
+
+        if ( pen.style() == Qt::NoPen )
+            continue;
+
+        painter->setPen( pen );
+
+        const QPolygonF &lines = contourLines[level];
+        for ( int i = 0; i < lines.size(); i += 2 )
+        {
+            const QPointF p1( xMap.transform( lines[i].x() ),
+                yMap.transform( lines[i].y() ) );
+            const QPointF p2( xMap.transform( lines[i+1].x() ),
+                yMap.transform( lines[i+1].y() ) );
+
+            QwtPainter::drawLine( painter, p1, p2 );
+        }
+    }
+}
+
+/*!
+  \brief Draw the spectrogram
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+
+  \sa setDisplayMode(), renderImage(),
+      QwtPlotRasterItem::draw(), drawContourLines()
+*/
+void QwtPlotSpectrogram::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( d_data->displayMode & ImageMode )
+        QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect );
+
+    if ( d_data->displayMode & ContourMode )
+    {
+        // Add some pixels at the borders
+        const int margin = 2;
+        QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin,
+            canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin );
+
+        QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect );
+
+        const QRectF br = boundingRect();
+        if ( br.isValid() )
+        {
+            area &= br;
+            if ( area.isEmpty() )
+                return;
+
+            rasterRect = QwtScaleMap::transform( xMap, yMap, area );
+        }
+
+        QSize raster = contourRasterSize( area, rasterRect.toRect() );
+        raster = raster.boundedTo( rasterRect.toRect().size() );
+        if ( raster.isValid() )
+        {
+            const QwtRasterData::ContourLines lines =
+                renderContourLines( area, raster );
+
+            drawContourLines( painter, xMap, yMap, lines );
+        }
+    }
+}
diff --git a/qwt/qwt_plot_spectrogram.h b/qwt/qwt_plot_spectrogram.h
new file mode 100644
index 0000000..1aa6989
--- /dev/null
+++ b/qwt/qwt_plot_spectrogram.h
@@ -0,0 +1,118 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SPECTROGRAM_H
+#define QWT_PLOT_SPECTROGRAM_H
+
+#include "qwt_global.h"
+#include "qwt_raster_data.h"
+#include "qwt_plot_rasteritem.h"
+#include <qlist.h>
+
+class QwtColorMap;
+
+/*!
+  \brief A plot item, which displays a spectrogram
+
+  A spectrogram displays 3-dimensional data, where the 3rd dimension
+  ( the intensity ) is displayed using colors. The colors are calculated
+  from the values using a color map.
+
+  On multi-core systems the performance of the image composition
+  can often be improved by dividing the area into tiles - each of them
+  rendered in a different thread ( see QwtPlotItem::setRenderThreadCount() ).
+
+  In ContourMode contour lines are painted for the contour levels.
+
+  \image html spectrogram3.png
+
+  \sa QwtRasterData, QwtColorMap, QwtPlotItem::setRenderThreadCount()
+*/
+
+class QWT_EXPORT QwtPlotSpectrogram: public QwtPlotRasterItem
+{
+public:
+    /*!
+      The display mode controls how the raster data will be represented.
+      \sa setDisplayMode(), testDisplayMode()
+    */
+
+    enum DisplayMode
+    {
+        //! The values are mapped to colors using a color map.
+        ImageMode = 0x01,
+
+        //! The data is displayed using contour lines
+        ContourMode = 0x02
+    };
+
+    //! Display modes
+    typedef QFlags<DisplayMode> DisplayModes;
+
+    explicit QwtPlotSpectrogram( const QString &title = QString::null );
+    virtual ~QwtPlotSpectrogram();
+
+    void setDisplayMode( DisplayMode, bool on = true );
+    bool testDisplayMode( DisplayMode ) const;
+
+    void setData( QwtRasterData *data );
+    const QwtRasterData *data() const;
+    QwtRasterData *data();
+
+    void setColorMap( QwtColorMap * );
+    const QwtColorMap *colorMap() const;
+
+    virtual QwtInterval interval(Qt::Axis) const;
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    void setDefaultContourPen( const QColor &, 
+        qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setDefaultContourPen( const QPen & );
+    QPen defaultContourPen() const;
+
+    virtual QPen contourPen( double level ) const;
+
+    void setConrecFlag( QwtRasterData::ConrecFlag, bool on );
+    bool testConrecFlag( QwtRasterData::ConrecFlag ) const;
+
+    void setContourLevels( const QList<double> & );
+    QList<double> contourLevels() const;
+
+    virtual int rtti() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+protected:
+    virtual QImage renderImage(
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &area, const QSize &imageSize ) const;
+
+    virtual QSize contourRasterSize(
+        const QRectF &, const QRect & ) const;
+
+    virtual QwtRasterData::ContourLines renderContourLines(
+        const QRectF &rect, const QSize &raster ) const;
+
+    virtual void drawContourLines( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtRasterData::ContourLines& lines ) const;
+
+    void renderTile( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRect &imageRect, QImage *image ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectrogram::DisplayModes )
+
+#endif
diff --git a/qwt/qwt_plot_svgitem.h b/qwt/qwt_plot_svgitem.h
new file mode 100644
index 0000000..1d98dee
--- /dev/null
+++ b/qwt/qwt_plot_svgitem.h
@@ -0,0 +1,61 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SVGITEM_H
+#define QWT_PLOT_SVGITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include <qstring.h>
+
+class QSvgRenderer;
+class QByteArray;
+
+/*!
+  \brief A plot item, which displays
+         data in Scalable Vector Graphics (SVG) format.
+
+  SVG images are often used to display maps
+*/
+
+class QWT_EXPORT QwtPlotSvgItem: public QwtPlotItem
+{
+public:
+    explicit QwtPlotSvgItem( const QString& title = QString::null );
+    explicit QwtPlotSvgItem( const QwtText& title );
+    virtual ~QwtPlotSvgItem();
+
+    bool loadFile( const QRectF&, const QString &fileName );
+    bool loadData( const QRectF&, const QByteArray & );
+
+    virtual QRectF boundingRect() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual int rtti() const;
+
+protected:
+    const QSvgRenderer &renderer() const;
+    QSvgRenderer &renderer();
+
+    void render( QPainter *painter,
+        const QRectF &viewBox, const QRectF &rect ) const;
+
+    QRectF viewBox( const QRectF &area ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_textlabel.cpp b/qwt/qwt_plot_textlabel.cpp
new file mode 100644
index 0000000..5727c26
--- /dev/null
+++ b/qwt/qwt_plot_textlabel.cpp
@@ -0,0 +1,246 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_textlabel.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qmath.h>
+
+static QRect qwtItemRect( int renderFlags,
+    const QRectF &rect, const QSizeF &itemSize ) 
+{
+    int x;
+    if ( renderFlags & Qt::AlignLeft )
+    {
+        x = rect.left();
+    }
+    else if ( renderFlags & Qt::AlignRight )
+    {
+        x = rect.right() - itemSize.width();
+    }
+    else
+    {
+        x = rect.center().x() - 0.5 * itemSize.width();
+    }
+
+    int y;
+    if ( renderFlags & Qt::AlignTop ) 
+    {
+        y = rect.top();
+    }
+    else if ( renderFlags & Qt::AlignBottom )
+    {
+        y = rect.bottom() - itemSize.height();
+    }
+    else
+    {
+        y = rect.center().y() - 0.5 * itemSize.height();
+    }
+
+    return QRect( x, y, itemSize.width(), itemSize.height() );
+}
+
+class QwtPlotTextLabel::PrivateData
+{   
+public:
+    PrivateData():
+        margin( 5 )
+    {
+    }
+
+    QwtText text;
+    int margin;
+
+    QPixmap pixmap;
+};  
+
+/*!
+   \brief Constructor
+
+   Initializes an text label with an empty text
+
+   Sets the following item attributes:
+
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 150
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+
+QwtPlotTextLabel::QwtPlotTextLabel():
+    QwtPlotItem( QwtText( "Label" ) )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::AutoScale, false );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 150 );
+}
+
+//! Destructor
+QwtPlotTextLabel::~QwtPlotTextLabel()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotTextLabel
+int QwtPlotTextLabel::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotTextLabel;
+}
+
+/*!
+  Set the text 
+
+  The label will be aligned to the plot canvas according to
+  the alignment flags of text.
+
+  \param text Text to be displayed
+
+  \sa text(), QwtText::renderFlags()
+*/
+void QwtPlotTextLabel::setText( const QwtText &text )
+{
+    if ( d_data->text != text )
+    {
+        d_data->text = text;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Text to be displayed
+  \sa setText()
+*/
+QwtText QwtPlotTextLabel::text() const
+{
+    return d_data->text;
+}
+
+/*!
+  Set the margin
+
+  The margin is the distance between the contentsRect()
+  of the plot canvas and the rectangle where the label can
+  be displayed.
+
+  \param margin Margin
+
+  \sa margin(), textRect()
+ */
+void QwtPlotTextLabel::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( d_data->margin != margin )
+    {
+        d_data->margin = margin;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin added to the contentsMargins() of the canvas
+  \sa setMargin()
+*/
+int QwtPlotTextLabel::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+  Draw the text label
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+
+  \sa textRect()
+*/
+
+void QwtPlotTextLabel::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+
+    const int m = d_data->margin;
+
+    const QRectF rect = textRect( canvasRect.adjusted( m, m, -m, -m ),
+        d_data->text.textSize( painter->font() ) );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+    if ( doAlign )
+    {
+        // when the paint device is aligning it is not one
+        // where scalability matters ( PDF, SVG ).
+        // As rendering a text label is an expensive operation
+        // we use a cache.
+
+        int pw = 0;
+        if ( d_data->text.borderPen().style() != Qt::NoPen )
+            pw = qMax( d_data->text.borderPen().width(), 1 );
+
+        QRect pixmapRect; 
+        pixmapRect.setLeft( qFloor( rect.left() ) - pw );
+        pixmapRect.setTop( qFloor( rect.top() ) - pw );
+        pixmapRect.setRight( qCeil( rect.right() ) + pw );
+        pixmapRect.setBottom( qCeil( rect.bottom() ) + pw );
+        
+        if ( d_data->pixmap.isNull() || 
+            ( pixmapRect.size() != d_data->pixmap.size() )  )
+        {
+            d_data->pixmap = QPixmap( pixmapRect.size() );
+            d_data->pixmap.fill( Qt::transparent );
+
+            const QRect r( pw, pw, 
+                pixmapRect.width() - 2 * pw, pixmapRect.height() - 2 * pw );
+
+            QPainter pmPainter( &d_data->pixmap );
+            d_data->text.draw( &pmPainter, r );
+        }
+
+        painter->drawPixmap( pixmapRect, d_data->pixmap );
+    }
+    else
+    {
+        d_data->text.draw( painter, rect );
+    }
+}
+
+/*!
+   \brief Align the text label
+
+   \param rect Canvas rectangle with margins subtracted
+   \param textSize Size required to draw the text
+
+   \return A rectangle aligned according the the alignment flags of
+           the text.
+
+   \sa setMargin(), QwtText::renderFlags(), QwtText::textSize()
+ */
+QRectF QwtPlotTextLabel::textRect( 
+    const QRectF &rect, const QSizeF &textSize ) const
+{
+    return qwtItemRect( d_data->text.renderFlags(), rect, textSize );
+}
+
+//!  Invalidate all internal cache
+void QwtPlotTextLabel::invalidateCache()
+{
+    d_data->pixmap = QPixmap();
+}
diff --git a/qwt/qwt_plot_textlabel.h b/qwt/qwt_plot_textlabel.h
new file mode 100644
index 0000000..c7b8496
--- /dev/null
+++ b/qwt/qwt_plot_textlabel.h
@@ -0,0 +1,75 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_TEXT_LABEL_H
+#define QWT_PLOT_TEXT_LABEL_H 1
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_text.h"
+
+/*!
+  \brief A plot item, which displays a text label
+
+  QwtPlotTextLabel displays a text label aligned to the plot canvas.
+
+  In opposite to QwtPlotMarker the position of the label is unrelated to
+  plot coordinates.
+    
+  As drawing a text is an expensive operation the label is cached
+  in a pixmap to speed up replots.
+
+  \par Example
+  The following code shows how to add a title.
+
+\verbatim
+    QwtText title( "Plot Title" );
+    title.setRenderFlags( Qt::AlignHCenter | Qt::AlignTop );
+
+    QFont font;
+    font.setBold( true );
+    title.setFont( font );
+
+    QwtPlotTextLabel *titleItem = new QwtPlotTextLabel();
+    titleItem->setText( title );
+    titleItem->attach( this );
+\endverbatim
+
+  \sa QwtPlotMarker
+*/  
+
+class QWT_EXPORT QwtPlotTextLabel: public QwtPlotItem
+{
+public:
+    QwtPlotTextLabel();
+    virtual ~QwtPlotTextLabel();
+
+    virtual int rtti() const;
+
+    void setText( const QwtText & );
+    QwtText text() const;
+
+    void setMargin( int margin );
+    int margin() const;
+
+    virtual QRectF textRect( const QRectF &, const QSizeF & ) const;
+
+protected:
+    virtual void draw( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &) const;
+
+    void invalidateCache();
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_tradingcurve.cpp b/qwt/qwt_plot_tradingcurve.cpp
new file mode 100644
index 0000000..04d877b
--- /dev/null
+++ b/qwt/qwt_plot_tradingcurve.cpp
@@ -0,0 +1,682 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_tradingcurve.h"
+#include "qwt_scale_map.h"
+#include "qwt_clipper.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+
+static inline bool qwtIsSampleInside( const QwtOHLCSample &sample,
+    double tMin, double tMax, double vMin, double vMax )
+{
+    const double t = sample.time;
+    const QwtInterval interval = sample.boundingInterval();
+
+    const bool isOffScreen = ( t < tMin ) || ( t > tMax )
+        || ( interval.maxValue() < vMin ) || ( interval.minValue() > vMax );
+
+    return !isOffScreen;
+}
+
+class QwtPlotTradingCurve::PrivateData
+{
+public:
+    PrivateData():
+        symbolStyle( QwtPlotTradingCurve::CandleStick ),
+        symbolExtent( 0.6 ),
+        minSymbolWidth( 2.0 ),
+        maxSymbolWidth( -1.0 ),
+        paintAttributes( QwtPlotTradingCurve::ClipSymbols )
+    {
+        symbolBrush[0] = QBrush( Qt::white );
+        symbolBrush[1] = QBrush( Qt::black );
+    }
+
+    QwtPlotTradingCurve::SymbolStyle symbolStyle;
+    double symbolExtent;
+    double minSymbolWidth;
+    double maxSymbolWidth;
+
+    QPen symbolPen;
+    QBrush symbolBrush[2]; // Increasing/Decreasing
+
+    QwtPlotTradingCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotTradingCurve::QwtPlotTradingCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotTradingCurve::QwtPlotTradingCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotTradingCurve::~QwtPlotTradingCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotTradingCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+
+    d_data = new PrivateData;
+    setData( new QwtTradingChartData() );
+
+    setZ( 19.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotTradingCurve
+int QwtPlotTradingCurve::rtti() const
+{
+    return QwtPlotTradingCurve::Rtti_PlotTradingCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotTradingCurve::setPaintAttribute(
+    PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotTradingCurve::testPaintAttribute(
+    PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of samples
+
+  \sa QwtPlotSeriesItem::setData()
+*/
+void QwtPlotTradingCurve::setSamples(
+    const QVector<QwtOHLCSample> &samples )
+{
+    setData( new QwtTradingChartData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore. 
+*/
+void QwtPlotTradingCurve::setSamples(
+    QwtSeriesData<QwtOHLCSample> *data )
+{
+    setData( data );
+}   
+
+/*!
+  Set the symbol style
+
+  \param style Symbol style
+
+  \sa symbolStyle(), setSymbolExtent(),
+      setSymbolPen(), setSymbolBrush()
+*/
+void QwtPlotTradingCurve::setSymbolStyle( SymbolStyle style )
+{
+    if ( style != d_data->symbolStyle )
+    {
+        d_data->symbolStyle = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Symbol style
+  \sa setSymbolStyle(), symbolExtent(), symbolPen(), symbolBrush()
+*/
+QwtPlotTradingCurve::SymbolStyle QwtPlotTradingCurve::symbolStyle() const
+{
+    return d_data->symbolStyle;
+}
+
+/*! 
+  Build and assign the symbol pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotTradingCurve::setSymbolPen( 
+    const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setSymbolPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Set the symbol pen
+
+  The symbol pen is used for rendering the lines of the
+  bar or candlestick symbols
+
+  \sa symbolPen(), setSymbolBrush()
+*/
+void QwtPlotTradingCurve::setSymbolPen( const QPen &pen )
+{
+    if ( pen != d_data->symbolPen )
+    {
+        d_data->symbolPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Symbol pen
+  \sa setSymbolPen(), symbolBrush()
+*/
+QPen QwtPlotTradingCurve::symbolPen() const
+{
+    return d_data->symbolPen;
+}
+
+/*!
+  Set the symbol brush
+
+  \param direction Direction type
+  \param brush Brush used to fill the body of all candlestick
+               symbols with the direction
+
+  \sa symbolBrush(), setSymbolPen()
+*/
+void QwtPlotTradingCurve::setSymbolBrush(
+    Direction direction, const QBrush &brush )
+{
+    if ( direction < 0 || direction >= 2 )
+        return;
+
+    if ( brush != d_data->symbolBrush[ direction ] )
+    {
+        d_data->symbolBrush[ direction ] = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \param direction
+  \return Brush used to fill the body of all candlestick
+          symbols with the direction
+
+  \sa setSymbolPen(), symbolBrush()
+*/
+QBrush QwtPlotTradingCurve::symbolBrush( Direction direction ) const
+{
+    if ( direction < 0 || direction >= 2 )
+        return QBrush();
+
+    return d_data->symbolBrush[ direction ];
+}
+
+/*!
+  \brief Set the extent of the symbol
+
+  The width of the symbol is given in scale coordinates. When painting
+  a symbol the width is scaled into paint device coordinates
+  by scaledSymbolWidth(). The scaled width is bounded by
+  minSymbolWidth(), maxSymbolWidth()
+
+  \param extent Symbol width in scale coordinates
+
+  \sa symbolExtent(), scaledSymbolWidth(), 
+      setMinSymbolWidth(), setMaxSymbolWidth()
+*/
+void QwtPlotTradingCurve::setSymbolExtent( double extent )
+{
+    extent = qMax( 0.0, extent );
+    if ( extent != d_data->symbolExtent )
+    {
+        d_data->symbolExtent = extent;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Extent of a symbol in scale coordinates
+  \sa setSymbolExtent(), scaledSymbolWidth(),
+      minSymbolWidth(), maxSymbolWidth()
+*/
+double QwtPlotTradingCurve::symbolExtent() const
+{
+    return d_data->symbolExtent;
+}
+
+/*!
+  Set a minimum for the symbol width
+
+  \param width Width in paint device coordinates
+  \sa minSymbolWidth(), setMaxSymbolWidth(), setSymbolExtent()
+ */
+void QwtPlotTradingCurve::setMinSymbolWidth( double width )
+{
+    width = qMax( width, 0.0 );
+    if ( width != d_data->minSymbolWidth )
+    {
+        d_data->minSymbolWidth = width;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Minmum for the symbol width
+  \sa setMinSymbolWidth(), maxSymbolWidth(), symbolExtent()
+ */
+double QwtPlotTradingCurve::minSymbolWidth() const
+{
+    return d_data->minSymbolWidth;
+}
+
+/*!
+  Set a maximum for the symbol width
+
+  A value <= 0.0 means an unlimited width
+
+  \param width Width in paint device coordinates
+  \sa maxSymbolWidth(), setMinSymbolWidth(), setSymbolExtent()
+ */
+void QwtPlotTradingCurve::setMaxSymbolWidth( double width )
+{
+    if ( width != d_data->maxSymbolWidth )
+    {
+        d_data->maxSymbolWidth = width;
+    
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Maximum for the symbol width
+  \sa setMaxSymbolWidth(), minSymbolWidth(), symbolExtent()
+ */
+double QwtPlotTradingCurve::maxSymbolWidth() const
+{
+    return d_data->maxSymbolWidth;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotTradingCurve::boundingRect() const
+{
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.isValid() && orientation() == Qt::Vertical )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the curve
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotTradingCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    painter->save();
+
+    if ( d_data->symbolStyle != QwtPlotTradingCurve::NoSymbol )
+        drawSymbols( painter, xMap, yMap, canvasRect, from, to );
+
+    painter->restore();
+}
+
+/*!
+  Draw symbols
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \sa drawSeries()
+*/
+void QwtPlotTradingCurve::drawSymbols( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect );
+
+    const QwtScaleMap *timeMap, *valueMap;
+    double tMin, tMax, vMin, vMax;
+
+    const Qt::Orientation orient = orientation();
+    if ( orient == Qt::Vertical )
+    {
+        timeMap = &xMap;
+        valueMap = &yMap;
+
+        tMin = tr.left();
+        tMax = tr.right();
+        vMin = tr.top();
+        vMax = tr.bottom();
+    }
+    else
+    {
+        timeMap = &yMap;
+        valueMap = &xMap;
+
+        vMin = tr.left();
+        vMax = tr.right();
+        tMin = tr.top();
+        tMax = tr.bottom();
+    }
+
+    const bool inverted = timeMap->isInverting();
+    const bool doClip = d_data->paintAttributes & ClipSymbols;
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double symbolWidth = scaledSymbolWidth( xMap, yMap, canvasRect );
+    if ( doAlign )
+        symbolWidth = qFloor( 0.5 * symbolWidth ) * 2.0;
+
+    QPen pen = d_data->symbolPen;
+    pen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( pen );
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtOHLCSample s = sample( i );
+
+        if ( !doClip || qwtIsSampleInside( s, tMin, tMax, vMin, vMax ) )
+        {
+            QwtOHLCSample translatedSample;
+
+            translatedSample.time = timeMap->transform( s.time );
+            translatedSample.open = valueMap->transform( s.open );
+            translatedSample.high = valueMap->transform( s.high );
+            translatedSample.low = valueMap->transform( s.low );
+            translatedSample.close = valueMap->transform( s.close );
+
+            const int brushIndex = ( s.open < s.close )
+                ? QwtPlotTradingCurve::Increasing
+                : QwtPlotTradingCurve::Decreasing;
+
+            if ( doAlign )
+            {
+                translatedSample.time = qRound( translatedSample.time );
+                translatedSample.open = qRound( translatedSample.open );
+                translatedSample.high = qRound( translatedSample.high );
+                translatedSample.low = qRound( translatedSample.low );
+                translatedSample.close = qRound( translatedSample.close );
+            }
+
+            switch( d_data->symbolStyle )
+            {
+                case Bar:
+                {
+                    drawBar( painter, translatedSample, 
+                        orient, inverted, symbolWidth );
+                    break;
+                }
+                case CandleStick:
+                {
+                    painter->setBrush( d_data->symbolBrush[ brushIndex ] );
+                    drawCandleStick( painter, translatedSample, 
+                        orient, symbolWidth );
+                    break;
+                }
+                default:
+                {
+                    if ( d_data->symbolStyle >= UserSymbol )
+                    {
+                        painter->setBrush( d_data->symbolBrush[ brushIndex ] );
+                        drawUserSymbol( painter, d_data->symbolStyle,
+                            translatedSample, orient, inverted, symbolWidth );
+                    }
+                }
+            }
+        }
+    }
+}
+
+/*!
+  \brief Draw a symbol for a symbol style >= UserSymbol
+
+  The implementation does nothing and is intended to be overloaded
+
+  \param painter Qt painter, initialized with pen/brush
+  \param symbolStyle Symbol style
+  \param sample Samples already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param inverted True, when the opposite scale 
+                  ( Qt::Vertical: x, Qt::Horizontal: y ) is increasing
+                  in the opposite direction as QPainter coordinates.
+  \param symbolWidth Width of the symbol in paint device coordinates
+*/
+void QwtPlotTradingCurve::drawUserSymbol( QPainter *painter,
+    SymbolStyle symbolStyle, const QwtOHLCSample &sample,
+    Qt::Orientation orientation, bool inverted, double symbolWidth ) const
+{
+    Q_UNUSED( painter )
+    Q_UNUSED( symbolStyle )
+    Q_UNUSED( orientation )
+    Q_UNUSED( inverted )
+    Q_UNUSED( symbolWidth )
+    Q_UNUSED( sample )
+}
+
+/*!
+  \brief Draw a bar
+
+  \param painter Qt painter, initialized with pen/brush
+  \param sample Sample, already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param inverted When inverted is false the open tick is painted
+                  to the left/top, otherwise it is painted right/bottom.
+                  The close tick is painted in the opposite direction
+                  of the open tick.
+                  painted in the opposite d
+                  opposite direction.
+  \param width Width or height of the candle, depending on the orientation
+
+  \sa Bar
+*/
+void QwtPlotTradingCurve::drawBar( QPainter *painter,
+    const QwtOHLCSample &sample, Qt::Orientation orientation, 
+    bool inverted, double width ) const
+{
+    double w2 = 0.5 * width;
+    if ( inverted )
+        w2 *= -1;
+
+    if ( orientation == Qt::Vertical )
+    {
+        QwtPainter::drawLine( painter,
+            sample.time, sample.low, sample.time, sample.high );
+
+        QwtPainter::drawLine( painter,
+            sample.time - w2, sample.open, sample.time, sample.open );
+        QwtPainter::drawLine( painter,
+            sample.time + w2, sample.close, sample.time, sample.close );
+    }
+    else
+    {
+        QwtPainter::drawLine( painter, sample.low, sample.time,
+            sample.high, sample.time );
+        QwtPainter::drawLine( painter,
+            sample.open, sample.time - w2, sample.open, sample.time );
+        QwtPainter::drawLine( painter,
+            sample.close, sample.time + w2, sample.close, sample.time );
+    }
+}
+
+/*!
+  \brief Draw a candle stick
+
+  \param painter Qt painter, initialized with pen/brush
+  \param sample Samples already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param width Width or height of the candle, depending on the orientation
+
+  \sa CandleStick
+*/
+void QwtPlotTradingCurve::drawCandleStick( QPainter *painter,
+    const QwtOHLCSample &sample, Qt::Orientation orientation, 
+    double width ) const
+{
+    const double t = sample.time;
+    const double v1 = qMin( sample.low, sample.high );
+    const double v2 = qMin( sample.open, sample.close );
+    const double v3 = qMax( sample.low, sample.high );
+    const double v4 = qMax( sample.open, sample.close );
+
+    if ( orientation == Qt::Vertical )
+    {
+        QwtPainter::drawLine( painter, t, v1, t, v2 );
+        QwtPainter::drawLine( painter, t, v3, t, v4 );
+
+        QRectF rect( t - 0.5 * width, sample.open,
+            width, sample.close - sample.open );
+
+        QwtPainter::drawRect( painter, rect );
+    }
+    else
+    {
+        QwtPainter::drawLine( painter, v1, t, v2, t );
+        QwtPainter::drawLine( painter, v3, t, v4, t );
+
+        const QRectF rect( sample.open, t - 0.5 * width,
+            sample.close - sample.open, width );
+
+        QwtPainter::drawRect( painter, rect );
+    }
+}
+
+/*!
+  \return A rectangle filled with the color of the symbol pen
+
+  \param index Index of the legend entry 
+                ( usually there is only one )
+  \param size Icon size
+
+  \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotTradingCurve::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+    return defaultIcon( d_data->symbolPen.color(), size );
+}
+
+/*!
+  Calculate the symbol width in paint coordinates
+
+  The width is calculated by scaling the symbol extent into
+  paint device coordinates bounded by the minimum/maximum
+  symbol width.
+
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+
+  \return Symbol width in paint coordinates
+
+  \sa symbolExtent(), minSymbolWidth(), maxSymbolWidth()
+*/
+double QwtPlotTradingCurve::scaledSymbolWidth(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( canvasRect );
+
+    if ( d_data->maxSymbolWidth > 0.0 &&
+        d_data->minSymbolWidth >= d_data->maxSymbolWidth )
+    {
+        return d_data->minSymbolWidth;
+    }
+
+    const QwtScaleMap *map =
+        ( orientation() == Qt::Vertical ) ? &xMap : &yMap;
+
+    const double pos = map->transform( map->s1() + d_data->symbolExtent ); 
+
+    double width = qAbs( pos - map->p1() );
+
+    width = qMax( width,  d_data->minSymbolWidth );
+    if ( d_data->maxSymbolWidth > 0.0 )
+        width = qMin( width, d_data->maxSymbolWidth );
+
+    return width;
+}
diff --git a/qwt/qwt_plot_tradingcurve.h b/qwt/qwt_plot_tradingcurve.h
new file mode 100644
index 0000000..8a4121f
--- /dev/null
+++ b/qwt/qwt_plot_tradingcurve.h
@@ -0,0 +1,174 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_TRADING_CURVE_H
+#define QWT_PLOT_TRADING_CURVE_H
+
+#include "qwt_global.h"
+#include "qwt_plot_seriesitem.h"
+#include "qwt_series_data.h"
+
+/*!
+  \brief QwtPlotTradingCurve illustrates movements in the price of a
+         financial instrument over time.
+
+  QwtPlotTradingCurve supports candlestick or bar ( OHLC ) charts
+  that are used in the domain of technical analysis.
+
+  While the length ( height or width depending on orientation() ) 
+  of each symbol depends on the corresponding OHLC sample the size
+  of the other dimension can be controlled using:
+
+  - setSymbolExtent()
+  - setSymbolMinWidth()
+  - setSymbolMaxWidth()
+
+  The extent is a size in scale coordinates, so that the symbol width
+  is increasing when the plot is zoomed in. Minimum/Maximum width
+  is in widget coordinates independent from the zoom level. 
+  When setting the minimum and maximum to the same value, the width of 
+  the symbol is fixed. 
+*/
+class QWT_EXPORT QwtPlotTradingCurve: 
+    public QwtPlotSeriesItem, QwtSeriesStore<QwtOHLCSample>
+{
+public:
+    /*!
+        \brief Symbol styles.
+
+        The default setting is QwtPlotSeriesItem::CandleStick.
+        \sa setSymbolStyle(), symbolStyle()
+    */
+    enum SymbolStyle
+    {
+        //! Nothing is displayed
+        NoSymbol = -1,
+
+        /*!
+          A line on the chart shows the price range (the highest and lowest
+          prices) over one unit of time, e.g. one day or one hour.
+          Tick marks project from each side of the line indicating the
+          opening and closing price.
+         */
+        Bar,
+
+        /*!
+          The range between opening/closing price are displayed as
+          a filled box. The fill brush depends on the direction of the
+          price movement. The box is connected to the highest/lowest
+          values by lines.
+        */
+        CandleStick,
+
+        /*!
+          SymbolTypes >= UserSymbol are displayed by drawUserSymbol(),
+          that needs to be overloaded and implemented in derived
+          curve classes.
+
+          \sa drawUserSymbol()
+        */
+        UserSymbol = 100
+    };
+
+    /*!
+        \brief Direction of a price movement
+     */
+    enum Direction
+    {
+        //! The closing price is higher than the opening price
+        Increasing,
+
+        //! The closing price is lower than the opening price
+        Decreasing
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        //! Check if a symbol is on the plot canvas before painting it.
+        ClipSymbols   = 0x01
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotTradingCurve( const QString &title = QString::null );
+    explicit QwtPlotTradingCurve( const QwtText &title );
+
+    virtual ~QwtPlotTradingCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtOHLCSample> & );
+    void setSamples( QwtSeriesData<QwtOHLCSample> * );
+
+    void setSymbolStyle( SymbolStyle style );
+    SymbolStyle symbolStyle() const;
+
+    void setSymbolPen( const QColor &, 
+        qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setSymbolPen( const QPen & );
+    QPen symbolPen() const;
+
+    void setSymbolBrush( Direction, const QBrush & );
+    QBrush symbolBrush( Direction ) const;
+
+    void setSymbolExtent( double width );
+    double symbolExtent() const;
+
+    void setMinSymbolWidth( double );
+    double minSymbolWidth() const;
+
+    void setMaxSymbolWidth( double );
+    double maxSymbolWidth() const;
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawSymbols( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawUserSymbol( QPainter *, 
+        SymbolStyle, const QwtOHLCSample &,
+        Qt::Orientation, bool inverted, double width ) const;
+
+    void drawBar( QPainter *painter, const QwtOHLCSample &, 
+        Qt::Orientation, bool inverted, double width ) const;
+
+    void drawCandleStick( QPainter *, const QwtOHLCSample &, 
+        Qt::Orientation, double width ) const;
+
+    virtual double scaledSymbolWidth(
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotTradingCurve::PaintAttributes )
+
+#endif
diff --git a/qwt/qwt_plot_xml.cpp b/qwt/qwt_plot_xml.cpp
new file mode 100644
index 0000000..5332a78
--- /dev/null
+++ b/qwt/qwt_plot_xml.cpp
@@ -0,0 +1,42 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot.h"
+
+/*!
+  This method is intended for manipulating the plot widget
+  from a specific editor in the Qwt designer plugin.
+
+  \warning The plot editor has never been implemented.
+*/
+void QwtPlot::applyProperties( const QString & /* xmlDocument */ )
+{
+#if 0
+    // Temporary dummy code, for designer tests
+    setTitle( xmlDocument );
+    replot();
+#endif
+}
+
+/*!
+  This method is intended for manipulating the plot widget
+  from a specific editor in the Qwt designer plugin.
+
+  \return QString::null
+  \warning The plot editor has never been implemented.
+*/
+QString QwtPlot::grabProperties() const
+{
+#if 0
+    // Temporary dummy code, for designer tests
+    return title().text();
+#else
+    return QString::null;
+#endif
+}
diff --git a/qwt/qwt_plot_zoneitem.cpp b/qwt/qwt_plot_zoneitem.cpp
new file mode 100644
index 0000000..f4ef696
--- /dev/null
+++ b/qwt/qwt_plot_zoneitem.cpp
@@ -0,0 +1,315 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_zoneitem.h"
+#include "qwt_painter.h"
+#include "qwt_scale_map.h"
+#include <qpainter.h>
+
+class QwtPlotZoneItem::PrivateData
+{   
+public:
+    PrivateData():
+        orientation( Qt::Vertical ),
+        pen( Qt::NoPen )
+    {
+        QColor c( Qt::darkGray );
+        c.setAlpha( 100 );
+        brush = QBrush( c );
+    }
+        
+    Qt::Orientation orientation;
+    QPen pen;
+    QBrush brush;
+    QwtInterval interval;
+};  
+
+/*!
+   \brief Constructor
+
+   Initializes the zone with no pen and a semi transparent gray brush
+
+   Sets the following item attributes:
+
+   - QwtPlotItem::AutoScale: false
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 5
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+QwtPlotZoneItem::QwtPlotZoneItem():
+    QwtPlotItem( QwtText( "Zone" ) )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::AutoScale, false );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 5 );
+}
+
+//! Destructor
+QwtPlotZoneItem::~QwtPlotZoneItem()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotZone
+int QwtPlotZoneItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotZone;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotZoneItem::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Assign a pen 
+
+  The pen is used to draw the border lines of the zone
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtPlotZoneItem::setPen( const QPen &pen )
+{
+    if ( d_data->pen != pen )
+    {
+        d_data->pen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used to draw the border lines
+  \sa setPen(), brush()
+*/
+const QPen &QwtPlotZoneItem::pen() const
+{
+    return d_data->pen;
+}
+
+/*! 
+  \brief Assign a brush 
+    
+  The brush is used to fill the zone
+
+  \param brush Brush
+  \sa pen(), setBrush()
+*/
+void QwtPlotZoneItem::setBrush( const QBrush &brush )
+{
+    if ( d_data->brush != brush )
+    {
+        d_data->brush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the zone
+  \sa setPen(), brush()
+*/
+const QBrush &QwtPlotZoneItem::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Set the orientation of the zone
+
+  A horizontal zone highlights an interval of the y axis, 
+  a vertical zone of the x axis. It is unbounded in the 
+  opposite direction.
+
+  \sa orientation(), QwtPlotItem::setAxes()
+*/
+void QwtPlotZoneItem::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation != orientation )
+    {
+        d_data->orientation = orientation;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Orientation of the zone
+  \sa setOrientation()
+ */
+Qt::Orientation QwtPlotZoneItem::orientation()
+{
+    return d_data->orientation;
+}
+
+/*!
+  Set the interval of the zone
+
+  For a horizontal zone the interval is related to the y axis,
+  for a vertical zone it is related to the x axis.
+
+  \param min Minimum of the interval
+  \param max Maximum of the interval
+
+  \sa interval(), setOrientation()
+ */
+void QwtPlotZoneItem::setInterval( double min, double max )
+{
+    setInterval( QwtInterval( min, max ) );
+}
+
+/*!
+  Set the interval of the zone
+
+  For a horizontal zone the interval is related to the y axis,
+  for a vertical zone it is related to the x axis.
+
+  \param interval Zone interval
+
+  \sa interval(), setOrientation()
+ */
+void QwtPlotZoneItem::setInterval( const QwtInterval &interval )
+{
+    if ( d_data->interval != interval )
+    {
+        d_data->interval = interval;
+        itemChanged(); 
+    }   
+}   
+
+/*!
+  \return Zone interval
+  \sa setInterval(), orientation()
+ */
+QwtInterval QwtPlotZoneItem::interval() const
+{
+    return d_data->interval;
+}   
+
+/*!
+  Draw the zone
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+
+void QwtPlotZoneItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( !d_data->interval.isValid() )
+        return;
+
+    QPen pen = d_data->pen;
+    pen.setCapStyle( Qt::FlatCap );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        double y1 = yMap.transform( d_data->interval.minValue() );
+        double y2 = yMap.transform( d_data->interval.maxValue() );
+
+        if ( doAlign )
+        {
+            y1 = qRound( y1 );
+            y2 = qRound( y2 );
+        }
+
+        QRectF r( canvasRect.left(), y1, canvasRect.width(), y2 - y1 );
+        r = r.normalized();
+
+        if ( ( d_data->brush.style() != Qt::NoBrush ) && ( y1 != y2 ) )
+        {
+            QwtPainter::fillRect( painter, r, d_data->brush );
+        }
+
+        if ( d_data->pen.style() != Qt::NoPen )
+        {
+            painter->setPen( d_data->pen );
+
+            QwtPainter::drawLine( painter, r.left(), r.top(), r.right(), r.top() );
+            QwtPainter::drawLine( painter, r.left(), r.bottom(), r.right(), r.bottom() );
+        }
+    }
+    else
+    {
+        double x1 = xMap.transform( d_data->interval.minValue() );
+        double x2 = xMap.transform( d_data->interval.maxValue() );
+
+        if ( doAlign )
+        {
+            x1 = qRound( x1 );
+            x2 = qRound( x2 );
+        }
+
+        QRectF r( x1, canvasRect.top(), x2 - x1, canvasRect.height() );
+        r = r.normalized();
+
+        if ( ( d_data->brush.style() != Qt::NoBrush ) && ( x1 != x2 ) )
+        {
+            QwtPainter::fillRect( painter, r, d_data->brush );
+        }
+
+        if ( d_data->pen.style() != Qt::NoPen )
+        {
+            painter->setPen( d_data->pen );
+
+            QwtPainter::drawLine( painter, r.left(), r.top(), r.left(), r.bottom() );
+            QwtPainter::drawLine( painter, r.right(), r.top(), r.right(), r.bottom() );
+        }
+    }
+}
+
+/*! 
+  The bounding rectangle is build from the interval in one direction
+  and something invalid for the opposite direction.
+
+  \return An invalid rectangle with valid boundaries in one direction
+*/
+QRectF QwtPlotZoneItem::boundingRect() const
+{
+    QRectF br = QwtPlotItem::boundingRect();
+
+    const QwtInterval &intv = d_data->interval;
+
+    if ( intv.isValid() )
+    {
+        if ( d_data->orientation == Qt::Horizontal )
+        {
+            br.setTop( intv.minValue() );
+            br.setBottom( intv.maxValue() );
+        }
+        else
+        {
+            br.setLeft( intv.minValue() );
+            br.setRight( intv.maxValue() );
+        }
+    }
+
+    return br;
+}
diff --git a/qwt/qwt_plot_zoneitem.h b/qwt/qwt_plot_zoneitem.h
new file mode 100644
index 0000000..f78226a
--- /dev/null
+++ b/qwt/qwt_plot_zoneitem.h
@@ -0,0 +1,65 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ZONE_ITEM_H
+#define QWT_PLOT_ZONE_ITEM_H
+
+#include "qwt_global.h"
+#include "qwt_plot_item.h"
+#include "qwt_interval.h"
+
+class QPen;
+class QBrush;
+
+/*!
+  \brief A plot item, which displays a zone
+
+  A horizontal zone highlights an interval of the y axis - a vertical 
+  zone an interval of the x axis - and is unbounded in the opposite direction.
+  It is filled with a brush and its border lines are optionally displayed with a pen. 
+
+  \note For displaying an area that is bounded for x and y coordinates 
+        use QwtPlotShapeItem
+*/
+
+class QWT_EXPORT QwtPlotZoneItem: 
+    public QwtPlotItem
+{
+public:
+    explicit QwtPlotZoneItem();
+    virtual ~QwtPlotZoneItem();
+
+    virtual int rtti() const;
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation();
+
+    void setInterval( double min, double max );
+    void setInterval( const QwtInterval & );
+    QwtInterval interval() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    virtual void draw( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &) const;
+
+    virtual QRectF boundingRect() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_plot_zoomer.cpp b/qwt/qwt_plot_zoomer.cpp
new file mode 100644
index 0000000..beee39f
--- /dev/null
+++ b/qwt/qwt_plot_zoomer.cpp
@@ -0,0 +1,604 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_plot_zoomer.h"
+#include "qwt_plot.h"
+#include "qwt_scale_div.h"
+#include "qwt_picker_machine.h"
+#include <qalgorithms.h>
+
+class QwtPlotZoomer::PrivateData
+{
+public:
+    uint zoomRectIndex;
+    QStack<QRectF> zoomStack;
+
+    int maxStackDepth;
+};
+
+/*!
+  \brief Create a zoomer for a plot canvas.
+
+  The zoomer is set to those x- and y-axis of the parent plot of the
+  canvas that are enabled. If both or no x-axis are enabled, the picker
+  is set to QwtPlot::xBottom. If both or no y-axis are
+  enabled, it is set to QwtPlot::yLeft.
+
+  The zoomer is initialized with a QwtPickerDragRectMachine,
+  the tracker mode is set to QwtPicker::ActiveOnly and the rubber band
+  is set to QwtPicker::RectRubberBand
+
+  \param canvas Plot canvas to observe, also the parent object
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
+*/
+QwtPlotZoomer::QwtPlotZoomer( QWidget *canvas, bool doReplot ):
+    QwtPlotPicker( canvas )
+{
+    if ( canvas )
+        init( doReplot );
+}
+
+/*!
+  \brief Create a zoomer for a plot canvas.
+
+  The zoomer is initialized with a QwtPickerDragRectMachine,
+  the tracker mode is set to QwtPicker::ActiveOnly and the rubber band
+  is set to QwtPicker;;RectRubberBand
+
+  \param xAxis X axis of the zoomer
+  \param yAxis Y axis of the zoomer
+  \param canvas Plot canvas to observe, also the parent object
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
+*/
+
+QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis,
+        QWidget *canvas, bool doReplot ):
+    QwtPlotPicker( xAxis, yAxis, canvas )
+{
+    if ( canvas )
+        init( doReplot );
+}
+
+//! Init the zoomer, used by the constructors
+void QwtPlotZoomer::init( bool doReplot )
+{
+    d_data = new PrivateData;
+
+    d_data->maxStackDepth = -1;
+
+    setTrackerMode( ActiveOnly );
+    setRubberBand( RectRubberBand );
+    setStateMachine( new QwtPickerDragRectMachine() );
+
+    if ( doReplot && plot() )
+        plot()->replot();
+
+    setZoomBase( scaleRect() );
+}
+
+QwtPlotZoomer::~QwtPlotZoomer()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Limit the number of recursive zoom operations to depth.
+
+  A value of -1 set the depth to unlimited, 0 disables zooming.
+  If the current zoom rectangle is below depth, the plot is unzoomed.
+
+  \param depth Maximum for the stack depth
+  \sa maxStackDepth()
+  \note depth doesn't include the zoom base, so zoomStack().count() might be
+              maxStackDepth() + 1.
+*/
+void QwtPlotZoomer::setMaxStackDepth( int depth )
+{
+    d_data->maxStackDepth = depth;
+
+    if ( depth >= 0 )
+    {
+        // unzoom if the current depth is below d_data->maxStackDepth
+
+        const int zoomOut =
+            int( d_data->zoomStack.count() ) - 1 - depth; // -1 for the zoom base
+
+        if ( zoomOut > 0 )
+        {
+            zoom( -zoomOut );
+            for ( int i = int( d_data->zoomStack.count() ) - 1;
+                i > int( d_data->zoomRectIndex ); i-- )
+            {
+                ( void )d_data->zoomStack.pop(); // remove trailing rects
+            }
+        }
+    }
+}
+
+/*!
+  \return Maximal depth of the zoom stack.
+  \sa setMaxStackDepth()
+*/
+int QwtPlotZoomer::maxStackDepth() const
+{
+    return d_data->maxStackDepth;
+}
+
+/*!
+  \return The zoom stack. zoomStack()[0] is the zoom base,
+          zoomStack()[1] the first zoomed rectangle.
+
+  \sa setZoomStack(), zoomRectIndex()
+*/
+const QStack<QRectF> &QwtPlotZoomer::zoomStack() const
+{
+    return d_data->zoomStack;
+}
+
+/*!
+  \return Initial rectangle of the zoomer
+  \sa setZoomBase(), zoomRect()
+*/
+QRectF QwtPlotZoomer::zoomBase() const
+{
+    return d_data->zoomStack[0];
+}
+
+/*!
+  Reinitialized the zoom stack with scaleRect() as base.
+
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa zoomBase(), scaleRect() QwtPlot::autoReplot(), QwtPlot::replot().
+*/
+void QwtPlotZoomer::setZoomBase( bool doReplot )
+{
+    QwtPlot *plt = plot();
+    if ( plt == NULL )
+        return;
+
+    if ( doReplot )
+        plt->replot();
+
+    d_data->zoomStack.clear();
+    d_data->zoomStack.push( scaleRect() );
+    d_data->zoomRectIndex = 0;
+
+    rescale();
+}
+
+/*!
+  \brief Set the initial size of the zoomer.
+
+  base is united with the current scaleRect() and the zoom stack is
+  reinitialized with it as zoom base. plot is zoomed to scaleRect().
+
+  \param base Zoom base
+
+  \sa zoomBase(), scaleRect()
+*/
+void QwtPlotZoomer::setZoomBase( const QRectF &base )
+{
+    const QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    const QRectF sRect = scaleRect();
+    const QRectF bRect = base | sRect;
+
+    d_data->zoomStack.clear();
+    d_data->zoomStack.push( bRect );
+    d_data->zoomRectIndex = 0;
+
+    if ( base != sRect )
+    {
+        d_data->zoomStack.push( sRect );
+        d_data->zoomRectIndex++;
+    }
+
+    rescale();
+}
+
+/*!
+  \return Rectangle at the current position on the zoom stack.
+  \sa zoomRectIndex(), scaleRect().
+*/
+QRectF QwtPlotZoomer::zoomRect() const
+{
+    return d_data->zoomStack[d_data->zoomRectIndex];
+}
+
+/*!
+  \return Index of current position of zoom stack.
+*/
+uint QwtPlotZoomer::zoomRectIndex() const
+{
+    return d_data->zoomRectIndex;
+}
+
+/*!
+  \brief Zoom in
+
+  Clears all rectangles above the current position of the
+  zoom stack and pushes the normalized rectangle on it.
+
+  \note If the maximal stack depth is reached, zoom is ignored.
+  \note The zoomed signal is emitted.
+*/
+
+void QwtPlotZoomer::zoom( const QRectF &rect )
+{
+    if ( d_data->maxStackDepth >= 0 &&
+            int( d_data->zoomRectIndex ) >= d_data->maxStackDepth )
+    {
+        return;
+    }
+
+    const QRectF zoomRect = rect.normalized();
+    if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
+    {
+        for ( uint i = int( d_data->zoomStack.count() ) - 1;
+           i > d_data->zoomRectIndex; i-- )
+        {
+            ( void )d_data->zoomStack.pop();
+        }
+
+        d_data->zoomStack.push( zoomRect );
+        d_data->zoomRectIndex++;
+
+        rescale();
+
+        Q_EMIT zoomed( zoomRect );
+    }
+}
+
+/*!
+  \brief Zoom in or out
+
+  Activate a rectangle on the zoom stack with an offset relative
+  to the current position. Negative values of offset will zoom out,
+  positive zoom in. A value of 0 zooms out to the zoom base.
+
+  \param offset Offset relative to the current position of the zoom stack.
+  \note The zoomed signal is emitted.
+  \sa zoomRectIndex()
+*/
+void QwtPlotZoomer::zoom( int offset )
+{
+    if ( offset == 0 )
+        d_data->zoomRectIndex = 0;
+    else
+    {
+        int newIndex = d_data->zoomRectIndex + offset;
+        newIndex = qMax( 0, newIndex );
+        newIndex = qMin( int( d_data->zoomStack.count() ) - 1, newIndex );
+
+        d_data->zoomRectIndex = uint( newIndex );
+    }
+
+    rescale();
+
+    Q_EMIT zoomed( zoomRect() );
+}
+
+/*!
+  \brief Assign a zoom stack
+
+  In combination with other types of navigation it might be useful to
+  modify to manipulate the complete zoom stack.
+
+  \param zoomStack New zoom stack
+  \param zoomRectIndex Index of the current position of zoom stack.
+                       In case of -1 the current position is at the top
+                       of the stack.
+
+  \note The zoomed signal might be emitted.
+  \sa zoomStack(), zoomRectIndex()
+*/
+void QwtPlotZoomer::setZoomStack(
+    const QStack<QRectF> &zoomStack, int zoomRectIndex )
+{
+    if ( zoomStack.isEmpty() )
+        return;
+
+    if ( d_data->maxStackDepth >= 0 &&
+        int( zoomStack.count() ) > d_data->maxStackDepth )
+    {
+        return;
+    }
+
+    if ( zoomRectIndex < 0 || zoomRectIndex > int( zoomStack.count() ) )
+        zoomRectIndex = zoomStack.count() - 1;
+
+    const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
+
+    d_data->zoomStack = zoomStack;
+    d_data->zoomRectIndex = uint( zoomRectIndex );
+
+    if ( doRescale )
+    {
+        rescale();
+        Q_EMIT zoomed( zoomRect() );
+    }
+}
+
+/*!
+  Adjust the observed plot to zoomRect()
+
+  \note Initiates QwtPlot::replot()
+*/
+
+void QwtPlotZoomer::rescale()
+{
+    QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
+    if ( rect != scaleRect() )
+    {
+        const bool doReplot = plt->autoReplot();
+        plt->setAutoReplot( false );
+
+        double x1 = rect.left();
+        double x2 = rect.right();
+        if ( !plt->axisScaleDiv( xAxis() ).isIncreasing() )
+            qSwap( x1, x2 );
+
+        plt->setAxisScale( xAxis(), x1, x2 );
+
+        double y1 = rect.top();
+        double y2 = rect.bottom();
+        if ( !plt->axisScaleDiv( yAxis() ).isIncreasing() )
+            qSwap( y1, y2 );
+
+        plt->setAxisScale( yAxis(), y1, y2 );
+
+        plt->setAutoReplot( doReplot );
+
+        plt->replot();
+    }
+}
+
+/*!
+  Reinitialize the axes, and set the zoom base to their scales.
+
+  \param xAxis X axis
+  \param yAxis Y axis
+*/
+
+void QwtPlotZoomer::setAxis( int xAxis, int yAxis )
+{
+    if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
+    {
+        QwtPlotPicker::setAxis( xAxis, yAxis );
+        setZoomBase( scaleRect() );
+    }
+}
+
+/*!
+   Qt::MidButton zooms out one position on the zoom stack,
+   Qt::RightButton to the zoom base.
+
+   Changes the current position on the stack, but doesn't pop
+   any rectangle.
+
+   \note The mouse events can be changed, using
+         QwtEventPattern::setMousePattern: 2, 1
+*/
+void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me )
+{
+    if ( mouseMatch( MouseSelect2, me ) )
+        zoom( 0 );
+    else if ( mouseMatch( MouseSelect3, me ) )
+        zoom( -1 );
+    else if ( mouseMatch( MouseSelect6, me ) )
+        zoom( +1 );
+    else
+        QwtPlotPicker::widgetMouseReleaseEvent( me );
+}
+
+/*!
+   Qt::Key_Plus zooms in, Qt::Key_Minus zooms out one position on the
+   zoom stack, Qt::Key_Escape zooms out to the zoom base.
+
+   Changes the current position on the stack, but doesn't pop
+   any rectangle.
+
+   \note The keys codes can be changed, using
+         QwtEventPattern::setKeyPattern: 3, 4, 5
+*/
+
+void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke )
+{
+    if ( !isActive() )
+    {
+        if ( keyMatch( KeyUndo, ke ) )
+            zoom( -1 );
+        else if ( keyMatch( KeyRedo, ke ) )
+            zoom( +1 );
+        else if ( keyMatch( KeyHome, ke ) )
+            zoom( 0 );
+    }
+
+    QwtPlotPicker::widgetKeyPressEvent( ke );
+}
+
+/*!
+  Move the current zoom rectangle.
+
+  \param dx X offset
+  \param dy Y offset
+
+  \note The changed rectangle is limited by the zoom base
+*/
+void QwtPlotZoomer::moveBy( double dx, double dy )
+{
+    const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
+    moveTo( QPointF( rect.left() + dx, rect.top() + dy ) );
+}
+
+/*!
+  Move the the current zoom rectangle.
+
+  \param pos New position
+
+  \sa QRectF::moveTo()
+  \note The changed rectangle is limited by the zoom base
+*/
+void QwtPlotZoomer::moveTo( const QPointF &pos )
+{
+    double x = pos.x();
+    double y = pos.y();
+
+    if ( x < zoomBase().left() )
+        x = zoomBase().left();
+    if ( x > zoomBase().right() - zoomRect().width() )
+        x = zoomBase().right() - zoomRect().width();
+
+    if ( y < zoomBase().top() )
+        y = zoomBase().top();
+    if ( y > zoomBase().bottom() - zoomRect().height() )
+        y = zoomBase().bottom() - zoomRect().height();
+
+    if ( x != zoomRect().left() || y != zoomRect().top() )
+    {
+        d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y );
+        rescale();
+    }
+}
+
+/*!
+  \brief Check and correct a selected rectangle
+
+  Reject rectangles with a height or width < 2, otherwise
+  expand the selected rectangle to a minimum size of 11x11
+  and accept it.
+
+  \return true If the rectangle is accepted, or has been changed
+          to an accepted one.
+*/
+
+bool QwtPlotZoomer::accept( QPolygon &pa ) const
+{
+    if ( pa.count() < 2 )
+        return false;
+
+    QRect rect = QRect( pa[0], pa[int( pa.count() ) - 1] );
+    rect = rect.normalized();
+
+    const int minSize = 2;
+    if ( rect.width() < minSize && rect.height() < minSize )
+        return false;
+
+    const int minZoomSize = 11;
+
+    const QPoint center = rect.center();
+    rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) );
+    rect.moveCenter( center );
+
+    pa.resize( 2 );
+    pa[0] = rect.topLeft();
+    pa[1] = rect.bottomRight();
+
+    return true;
+}
+
+/*!
+  \brief Limit zooming by a minimum rectangle
+
+  \return zoomBase().width() / 10e4, zoomBase().height() / 10e4
+*/
+QSizeF QwtPlotZoomer::minZoomSize() const
+{
+    return QSizeF( d_data->zoomStack[0].width() / 10e4,
+        d_data->zoomStack[0].height() / 10e4 );
+}
+
+/*!
+  Rejects selections, when the stack depth is too deep, or
+  the zoomed rectangle is minZoomSize().
+
+  \sa minZoomSize(), maxStackDepth()
+*/
+void QwtPlotZoomer::begin()
+{
+    if ( d_data->maxStackDepth >= 0 )
+    {
+        if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) )
+            return;
+    }
+
+    const QSizeF minSize = minZoomSize();
+    if ( minSize.isValid() )
+    {
+        const QSizeF sz =
+            d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
+
+        if ( minSize.width() >= sz.width() &&
+            minSize.height() >= sz.height() )
+        {
+            return;
+        }
+    }
+
+    QwtPlotPicker::begin();
+}
+
+/*!
+  Expand the selected rectangle to minZoomSize() and zoom in
+  if accepted.
+
+  \param ok If true, complete the selection and emit selected signals
+            otherwise discard the selection.
+
+  \sa accept(), minZoomSize()
+  \return True if the selection has been accepted, false otherwise
+*/
+bool QwtPlotZoomer::end( bool ok )
+{
+    ok = QwtPlotPicker::end( ok );
+    if ( !ok )
+        return false;
+
+    QwtPlot *plot = QwtPlotZoomer::plot();
+    if ( !plot )
+        return false;
+
+    const QPolygon &pa = selection();
+    if ( pa.count() < 2 )
+        return false;
+
+    QRect rect = QRect( pa[0], pa[int( pa.count() - 1 )] );
+    rect = rect.normalized();
+
+    QRectF zoomRect = invTransform( rect ).normalized();
+
+    const QSizeF minSize = minZoomSize();
+    if ( minSize.isValid() )
+    {
+        const QPointF center = zoomRect.center();
+        zoomRect.setSize( zoomRect.size().expandedTo( minZoomSize() ) );
+        zoomRect.moveCenter( center );
+    }
+
+    zoom( zoomRect );
+
+    return true;
+}
diff --git a/qwt/qwt_plot_zoomer.h b/qwt/qwt_plot_zoomer.h
new file mode 100644
index 0000000..1bc05b4
--- /dev/null
+++ b/qwt/qwt_plot_zoomer.h
@@ -0,0 +1,140 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ZOOMER_H
+#define QWT_PLOT_ZOOMER_H
+
+#include "qwt_global.h"
+#include "qwt_plot_picker.h"
+#include <qstack.h>
+
+/*!
+  \brief QwtPlotZoomer provides stacked zooming for a plot widget
+
+  QwtPlotZoomer selects rectangles from user inputs ( mouse or keyboard )
+  translates them into plot coordinates and adjusts the axes to them.
+  The selection is supported by a rubber band and optionally by displaying
+  the coordinates of the current mouse position.
+
+  Zooming can be repeated as often as possible, limited only by 
+  maxStackDepth() or minZoomSize().  Each rectangle is pushed on a stack.
+
+  The default setting how to select rectangles is 
+  a QwtPickerDragRectMachine with the following bindings:
+
+  - QwtEventPattern::MouseSelect1\n
+    The first point of the zoom rectangle is selected by a mouse press, 
+    the second point from the position, where the mouse is released.
+
+  - QwtEventPattern::KeySelect1\n
+    The first key press selects the first, the second key press
+    selects the second point.
+
+  - QwtEventPattern::KeyAbort\n
+    Discard the selection in the state, where the first point
+    is selected.
+
+  To traverse the zoom stack the following bindings are used:
+
+  - QwtEventPattern::MouseSelect3, QwtEventPattern::KeyUndo\n
+    Zoom out one position on the zoom stack
+    
+  - QwtEventPattern::MouseSelect6, QwtEventPattern::KeyRedo\n
+    Zoom in one position on the zoom stack
+
+  - QwtEventPattern::MouseSelect2, QwtEventPattern::KeyHome\n
+    Zoom to the zoom base
+
+  The setKeyPattern() and setMousePattern() functions can be used
+  to configure the zoomer actions. The following example 
+  shows, how to configure the 'I' and 'O' keys for zooming in and out 
+  one position on the zoom stack. The "Home" key is used to 
+  "unzoom" the plot.
+
+  \code
+   zoomer = new QwtPlotZoomer( plot );
+   zoomer->setKeyPattern( QwtEventPattern::KeyRedo, Qt::Key_I, Qt::ShiftModifier );
+   zoomer->setKeyPattern( QwtEventPattern::KeyUndo, Qt::Key_O, Qt::ShiftModifier );
+   zoomer->setKeyPattern( QwtEventPattern::KeyHome, Qt::Key_Home );
+  \endcode
+
+  QwtPlotZoomer is tailored for plots with one x and y axis, but it is
+  allowed to attach a second QwtPlotZoomer ( without rubber band and tracker )
+  for the other axes.
+
+  \note The realtime example includes an derived zoomer class that adds
+        scrollbars to the plot canvas.
+
+  \sa QwtPlotPanner, QwtPlotMagnifier
+*/
+
+class QWT_EXPORT QwtPlotZoomer: public QwtPlotPicker
+{
+    Q_OBJECT
+public:
+    explicit QwtPlotZoomer( QWidget *, bool doReplot = true );
+    explicit QwtPlotZoomer( int xAxis, int yAxis,
+                            QWidget *, bool doReplot = true );
+
+    virtual ~QwtPlotZoomer();
+
+    virtual void setZoomBase( bool doReplot = true );
+    virtual void setZoomBase( const QRectF & );
+
+    QRectF zoomBase() const;
+    QRectF zoomRect() const;
+
+    virtual void setAxis( int xAxis, int yAxis );
+
+    void setMaxStackDepth( int );
+    int maxStackDepth() const;
+
+    const QStack<QRectF> &zoomStack() const;
+    void setZoomStack( const QStack<QRectF> &,
+        int zoomRectIndex = -1 );
+
+    uint zoomRectIndex() const;
+
+public Q_SLOTS:
+    void moveBy( double x, double y );
+    virtual void moveTo( const QPointF & );
+
+    virtual void zoom( const QRectF & );
+    virtual void zoom( int up );
+
+Q_SIGNALS:
+    /*!
+      A signal emitting the zoomRect(), when the plot has been
+      zoomed in or out.
+
+      \param rect Current zoom rectangle.
+    */
+
+    void zoomed( const QRectF &rect );
+
+protected:
+    virtual void rescale();
+
+    virtual QSizeF minZoomSize() const;
+
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+
+    virtual void begin();
+    virtual bool end( bool ok = true );
+    virtual bool accept( QPolygon & ) const;
+
+private:
+    void init( bool doReplot );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_point_3d.cpp b/qwt/qwt_point_3d.cpp
new file mode 100644
index 0000000..27e4c1e
--- /dev/null
+++ b/qwt/qwt_point_3d.cpp
@@ -0,0 +1,22 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_point_3d.h"
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtPoint3D &point )
+{
+    debug.nospace() << "QwtPoint3D(" << point.x() 
+        << "," << point.y() << "," << point.z() << ")";
+    return debug.space();
+}
+
+#endif
+
diff --git a/qwt/qwt_point_3d.h b/qwt/qwt_point_3d.h
new file mode 100644
index 0000000..5b0b40e
--- /dev/null
+++ b/qwt/qwt_point_3d.h
@@ -0,0 +1,189 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file */
+#ifndef QWT_POINT_3D_H
+#define QWT_POINT_3D_H 1
+
+#include "qwt_global.h"
+#include <qpoint.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief QwtPoint3D class defines a 3D point in double coordinates
+*/
+
+class QWT_EXPORT QwtPoint3D
+{
+public:
+    QwtPoint3D();
+    QwtPoint3D( double x, double y, double z );
+    QwtPoint3D( const QwtPoint3D & );
+    QwtPoint3D( const QPointF & );
+
+    bool isNull()    const;
+
+    double x() const;
+    double y() const;
+    double z() const;
+
+    double &rx();
+    double &ry();
+    double &rz();
+
+    void setX( double x );
+    void setY( double y );
+    void setZ( double y );
+
+    QPointF toPoint() const;
+
+    bool operator==( const QwtPoint3D & ) const;
+    bool operator!=( const QwtPoint3D & ) const;
+
+private:
+    double d_x;
+    double d_y;
+    double d_z;
+};
+
+Q_DECLARE_TYPEINFO(QwtPoint3D, Q_MOVABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtPoint3D & );
+#endif
+
+/*!
+    Constructs a null point.
+    \sa isNull()
+*/
+inline QwtPoint3D::QwtPoint3D():
+    d_x( 0.0 ),
+    d_y( 0.0 ),
+    d_z( 0.0 )
+{
+}
+
+//! Constructs a point with coordinates specified by x, y and z.
+inline QwtPoint3D::QwtPoint3D( double x, double y, double z = 0.0 ):
+    d_x( x ),
+    d_y( y ),
+    d_z( z )
+{
+}
+
+/*!
+    Copy constructor.
+    Constructs a point using the values of the point specified.
+*/
+inline QwtPoint3D::QwtPoint3D( const QwtPoint3D &other ):
+    d_x( other.d_x ),
+    d_y( other.d_y ),
+    d_z( other.d_z )
+{
+}
+
+/*!
+    Constructs a point with x and y coordinates from a 2D point,
+    and a z coordinate of 0.
+*/
+inline QwtPoint3D::QwtPoint3D( const QPointF &other ):
+    d_x( other.x() ),
+    d_y( other.y() ),
+    d_z( 0.0 )
+{
+}
+
+/*!
+    \return True if the point is null; otherwise returns false.
+
+    A point is considered to be null if x, y and z-coordinates
+    are equal to zero.
+*/
+inline bool QwtPoint3D::isNull() const
+{
+    return d_x == 0.0 && d_y == 0.0 && d_z == 0.0;
+}
+
+//! \return The x-coordinate of the point.
+inline double QwtPoint3D::x() const
+{
+    return d_x;
+}
+
+//! \return The y-coordinate of the point.
+inline double QwtPoint3D::y() const
+{
+    return d_y;
+}
+
+//! \return The z-coordinate of the point.
+inline double QwtPoint3D::z() const
+{
+    return d_z;
+}
+
+//! \return A reference to the x-coordinate of the point.
+inline double &QwtPoint3D::rx()
+{
+    return d_x;
+}
+
+//! \return A reference to the y-coordinate of the point.
+inline double &QwtPoint3D::ry()
+{
+    return d_y;
+}
+
+//! \return A reference to the z-coordinate of the point.
+inline double &QwtPoint3D::rz()
+{
+    return d_z;
+}
+
+//! Sets the x-coordinate of the point to the value specified by x.
+inline void QwtPoint3D::setX( double x )
+{
+    d_x = x;
+}
+
+//! Sets the y-coordinate of the point to the value specified by y.
+inline void QwtPoint3D::setY( double y )
+{
+    d_y = y;
+}
+
+//! Sets the z-coordinate of the point to the value specified by z.
+inline void QwtPoint3D::setZ( double z )
+{
+    d_z = z;
+}
+
+/*!
+   \return 2D point, where the z coordinate is dropped.
+*/
+inline QPointF QwtPoint3D::toPoint() const
+{
+    return QPointF( d_x, d_y );
+}
+
+//! \return True, if this point and other are equal; otherwise returns false.
+inline bool QwtPoint3D::operator==( const QwtPoint3D &other ) const
+{
+    return ( d_x == other.d_x ) && ( d_y == other.d_y ) && ( d_z == other.d_z );
+}
+
+//! \return True if this rect and other are different; otherwise returns false.
+inline bool QwtPoint3D::operator!=( const QwtPoint3D &other ) const
+{
+    return !operator==( other );
+}
+
+#endif
diff --git a/qwt/qwt_point_data.cpp b/qwt/qwt_point_data.cpp
new file mode 100644
index 0000000..d4eeaaa
--- /dev/null
+++ b/qwt/qwt_point_data.cpp
@@ -0,0 +1,304 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_point_data.h"
+#include "qwt_math.h"
+#include <string.h>
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples()
+*/
+QwtPointArrayData::QwtPointArrayData(
+        const QVector<double> &x, const QVector<double> &y ):
+    d_x( x ),
+    d_y( y )
+{
+}
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+  \param size Size of the x and y arrays
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples()
+*/
+QwtPointArrayData::QwtPointArrayData( const double *x,
+        const double *y, size_t size )
+{
+    d_x.resize( size );
+    ::memcpy( d_x.data(), x, size * sizeof( double ) );
+
+    d_y.resize( size );
+    ::memcpy( d_y.data(), y, size * sizeof( double ) );
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPointArrayData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+//! \return Size of the data set
+size_t QwtPointArrayData::size() const
+{
+    return qMin( d_x.size(), d_y.size() );
+}
+
+/*!
+  Return the sample at position i
+
+  \param index Index
+  \return Sample at position i
+*/
+QPointF QwtPointArrayData::sample( size_t index ) const
+{
+    return QPointF( d_x[int( index )], d_y[int( index )] );
+}
+
+//! \return Array of the x-values
+const QVector<double> &QwtPointArrayData::xData() const
+{
+    return d_x;
+}
+
+//! \return Array of the y-values
+const QVector<double> &QwtPointArrayData::yData() const
+{
+    return d_y;
+}
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+  \param size Size of the x and y arrays
+
+  \warning The programmer must assure that the memory blocks referenced
+           by the pointers remain valid during the lifetime of the
+           QwtPlotCPointer object.
+
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setRawSamples()
+*/
+QwtCPointerData::QwtCPointerData(
+        const double *x, const double *y, size_t size ):
+    d_x( x ),
+    d_y( y ),
+    d_size( size )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtCPointerData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+//! \return Size of the data set
+size_t QwtCPointerData::size() const
+{
+    return d_size;
+}
+
+/*!
+  Return the sample at position i
+
+  \param index Index
+  \return Sample at position i
+*/
+QPointF QwtCPointerData::sample( size_t index ) const
+{
+    return QPointF( d_x[int( index )], d_y[int( index )] );
+}
+
+//! \return Array of the x-values
+const double *QwtCPointerData::xData() const
+{
+    return d_x;
+}
+
+//! \return Array of the y-values
+const double *QwtCPointerData::yData() const
+{
+    return d_y;
+}
+
+/*!
+   Constructor
+
+   \param size Number of points
+   \param interval Bounding interval for the points
+
+   \sa setInterval(), setSize()
+*/
+QwtSyntheticPointData::QwtSyntheticPointData(
+        size_t size, const QwtInterval &interval ):
+    d_size( size ),
+    d_interval( interval )
+{
+}
+
+/*!
+  Change the number of points
+
+  \param size Number of points
+  \sa size(), setInterval()
+*/
+void QwtSyntheticPointData::setSize( size_t size )
+{
+    d_size = size;
+}
+
+/*!
+  \return Number of points
+  \sa setSize(), interval()
+*/
+size_t QwtSyntheticPointData::size() const
+{
+    return d_size;
+}
+
+/*!
+   Set the bounding interval
+
+   \param interval Interval
+   \sa interval(), setSize()
+*/
+void QwtSyntheticPointData::setInterval( const QwtInterval &interval )
+{
+    d_interval = interval.normalized();
+}
+
+/*!
+   \return Bounding interval
+   \sa setInterval(), size()
+*/
+QwtInterval QwtSyntheticPointData::interval() const
+{
+    return d_interval;
+}
+
+/*!
+   Set a the "rectangle of interest"
+
+   QwtPlotSeriesItem defines the current area of the plot canvas
+   as "rect of interest" ( QwtPlotSeriesItem::updateScaleDiv() ).
+
+   If interval().isValid() == false the x values are calculated
+   in the interval rect.left() -> rect.right().
+
+   \sa rectOfInterest()
+*/
+void QwtSyntheticPointData::setRectOfInterest( const QRectF &rect )
+{
+    d_rectOfInterest = rect;
+    d_intervalOfInterest = QwtInterval(
+        rect.left(), rect.right() ).normalized();
+}
+
+/*!
+   \return "rectangle of interest"
+   \sa setRectOfInterest()
+*/
+QRectF QwtSyntheticPointData::rectOfInterest() const
+{
+    return d_rectOfInterest;
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  This implementation iterates over all points, what could often
+  be implemented much faster using the characteristics of the series.
+  When there are many points it is recommended to overload and
+  reimplement this method using the characteristics of the series
+  ( if possible ).
+
+  \return Bounding rectangle
+*/
+QRectF QwtSyntheticPointData::boundingRect() const
+{
+    if ( d_size == 0 ||
+        !( d_interval.isValid() || d_intervalOfInterest.isValid() ) )
+    {
+        return QRectF( 1.0, 1.0, -2.0, -2.0 ); // something invalid
+    }
+
+    return qwtBoundingRect( *this );
+}
+
+/*!
+   Calculate the point from an index
+
+   \param index Index
+   \return QPointF(x(index), y(x(index)));
+
+   \warning For invalid indices ( index < 0 || index >= size() )
+            (0, 0) is returned.
+*/
+QPointF QwtSyntheticPointData::sample( size_t index ) const
+{
+    if ( index >= d_size )
+        return QPointF( 0, 0 );
+
+    const double xValue = x( index );
+    const double yValue = y( xValue );
+
+    return QPointF( xValue, yValue );
+}
+
+/*!
+   Calculate a x-value from an index
+
+   x values are calculated by dividing an interval into
+   equidistant steps. If !interval().isValid() the
+   interval is calculated from the "rectangle of interest".
+
+   \param index Index of the requested point 
+   \return Calculated x coordinate
+
+   \sa interval(), rectOfInterest(), y()
+*/
+double QwtSyntheticPointData::x( uint index ) const
+{
+    const QwtInterval &interval = d_interval.isValid() ?
+        d_interval : d_intervalOfInterest;
+
+    if ( !interval.isValid() || d_size == 0 || index >= d_size )
+        return 0.0;
+
+    const double dx = interval.width() / d_size;
+    return interval.minValue() + index * dx;
+}
diff --git a/qwt/qwt_point_data.h b/qwt/qwt_point_data.h
new file mode 100644
index 0000000..c4c9c0c
--- /dev/null
+++ b/qwt/qwt_point_data.h
@@ -0,0 +1,146 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_POINT_DATA_H
+#define QWT_POINT_DATA_H 1
+
+#include "qwt_global.h"
+#include "qwt_series_data.h"
+
+/*!
+  \brief Interface for iterating over two QVector<double> objects.
+*/
+class QWT_EXPORT QwtPointArrayData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtPointArrayData( const QVector<double> &x, const QVector<double> &y );
+    QwtPointArrayData( const double *x, const double *y, size_t size );
+
+    virtual QRectF boundingRect() const;
+
+    virtual size_t size() const;
+    virtual QPointF sample( size_t i ) const;
+
+    const QVector<double> &xData() const;
+    const QVector<double> &yData() const;
+
+private:
+    QVector<double> d_x;
+    QVector<double> d_y;
+};
+
+/*!
+  \brief Data class containing two pointers to memory blocks of doubles.
+ */
+class QWT_EXPORT QwtCPointerData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtCPointerData( const double *x, const double *y, size_t size );
+
+    virtual QRectF boundingRect() const;
+    virtual size_t size() const;
+    virtual QPointF sample( size_t i ) const;
+
+    const double *xData() const;
+    const double *yData() const;
+
+private:
+    const double *d_x;
+    const double *d_y;
+    size_t d_size;
+};
+
+/*!
+  \brief Synthetic point data
+
+  QwtSyntheticPointData provides a fixed number of points for an interval.
+  The points are calculated in equidistant steps in x-direction.
+
+  If the interval is invalid, the points are calculated for
+  the "rectangle of interest", what normally is the displayed area on the
+  plot canvas. In this mode you get different levels of detail, when
+  zooming in/out.
+
+  \par Example
+
+  The following example shows how to implement a sinus curve.
+
+  \code
+#include <cmath>
+#include <qwt_series_data.h>
+#include <qwt_plot_curve.h>
+#include <qwt_plot.h>
+#include <qapplication.h>
+
+class SinusData: public QwtSyntheticPointData
+{
+public:
+    SinusData():
+        QwtSyntheticPointData( 100 )
+    {
+    }
+
+    virtual double y( double x ) const
+    {
+        return qSin( x );
+    }
+};
+
+int main(int argc, char **argv)
+{
+    QApplication a( argc, argv );
+
+    QwtPlot plot;
+    plot.setAxisScale( QwtPlot::xBottom, 0.0, 10.0 );
+    plot.setAxisScale( QwtPlot::yLeft, -1.0, 1.0 );
+
+    QwtPlotCurve *curve = new QwtPlotCurve( "y = sin(x)" );
+    curve->setData( new SinusData() );
+    curve->attach( &plot );
+
+    plot.show();
+    return a.exec();
+}
+   \endcode
+*/
+class QWT_EXPORT QwtSyntheticPointData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtSyntheticPointData( size_t size,
+        const QwtInterval & = QwtInterval() );
+
+    void setSize( size_t size );
+    virtual size_t size() const;
+
+    void setInterval( const QwtInterval& );
+    QwtInterval interval() const;
+
+    virtual QRectF boundingRect() const;
+    virtual QPointF sample( size_t i ) const;
+
+    /*!
+       Calculate a y value for a x value
+
+       \param x x value
+       \return Corresponding y value
+     */
+    virtual double y( double x ) const = 0;
+    virtual double x( uint index ) const;
+
+    virtual void setRectOfInterest( const QRectF & );
+    QRectF rectOfInterest() const;
+
+private:
+    size_t d_size;
+    QwtInterval d_interval;
+    QRectF d_rectOfInterest;
+    QwtInterval d_intervalOfInterest;
+};
+
+#endif
diff --git a/qwt/qwt_point_mapper.cpp b/qwt/qwt_point_mapper.cpp
new file mode 100644
index 0000000..eee0e29
--- /dev/null
+++ b/qwt/qwt_point_mapper.cpp
@@ -0,0 +1,721 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_point_mapper.h"
+#include "qwt_scale_map.h"
+#include "qwt_pixel_matrix.h"
+#include <qpolygon.h>
+#include <qimage.h>
+#include <qpen.h>
+#include <qpainter.h>
+
+#if QT_VERSION >= 0x040400
+
+#include <qthread.h>
+#include <qfuture.h>
+#include <qtconcurrentrun.h>
+
+#if !defined(QT_NO_QFUTURE)
+#define QWT_USE_THREADS 0
+#endif
+
+#endif
+
+static QRectF qwtInvalidRect( 0.0, 0.0, -1.0, -1.0 );
+
+// Helper class to work around the 5 parameters
+// limitation of QtConcurrent::run()
+class QwtDotsCommand
+{
+public:
+    const QwtSeriesData<QPointF> *series;
+    int from;
+    int to;
+    QRgb rgb;
+};
+
+static void qwtRenderDots(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtDotsCommand command, const QPoint &pos, QImage *image ) 
+{
+    const QRgb rgb = command.rgb;
+    QRgb *bits = reinterpret_cast<QRgb *>( image->bits() );
+
+    const int w = image->width();
+    const int h = image->height();
+
+    const int x0 = pos.x();
+    const int y0 = pos.y();
+
+    for ( int i = command.from; i <= command.to; i++ )
+    {
+        const QPointF sample = command.series->sample( i );
+
+        const int x = static_cast<int>( xMap.transform( sample.x() ) + 0.5 ) - x0;
+        const int y = static_cast<int>( yMap.transform( sample.y() ) + 0.5 ) - y0;
+
+        if ( x >= 0 && x < w && y >= 0 && y < h )
+            bits[ y * w + x ] = rgb;
+    }
+}
+
+static inline int qwtRoundValue( double value )
+{
+#if 1
+    return qRound( value );
+#else
+    // A little bit faster, but differs from qRound()
+    // for negative values. Should be no problem as we are
+    // rounding widgets coordinates, where negative values 
+    // are clipped off anyway ( at least when there is no 
+    // painter transformation )
+
+    return static_cast<int>( value + 0.5 );
+#endif
+}
+
+// some functors, so that the compile can inline
+struct QwtRoundI
+{
+    inline int operator()( double value )
+    {
+        return qwtRoundValue( value );
+    }
+};
+
+struct QwtRoundF
+{
+    inline double operator()( double value )
+    {
+        return static_cast<double>( qwtRoundValue( value ) );
+    }
+};
+
+struct QwtNoRoundF
+{   
+    inline double operator()( double value )
+    {
+        return value;
+    }
+};
+
+// mapping points without any filtering - beside checking
+// the bounding rectangle
+
+template<class Polygon, class Point, class Round>
+static inline Polygon qwtToPoints( 
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, 
+    int from, int to, Round round )
+{
+    Polygon polyline( to - from + 1 );
+    Point *points = polyline.data();
+
+    int numPoints = 0;
+
+    if ( boundingRect.isValid() )
+    {
+        // iterating over all values
+        // filtering out all points outside of
+        // the bounding rectangle
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            const double x = xMap.transform( sample.x() );
+            const double y = yMap.transform( sample.y() );
+
+            if ( boundingRect.contains( x, y ) )
+            {
+                points[ numPoints ].rx() = round( x );
+                points[ numPoints ].ry() = round( y );
+
+                numPoints++;
+            }
+        }
+
+        polyline.resize( numPoints );
+    }
+    else
+    {
+        // simply iterating over all values
+        // without any filtering
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            const double x = xMap.transform( sample.x() );
+            const double y = yMap.transform( sample.y() );
+
+            points[ numPoints ].rx() = round( x );
+            points[ numPoints ].ry() = round( y );
+
+            numPoints++;
+        }
+    }
+
+    return polyline;
+}
+
+static inline QPolygon qwtToPointsI(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to )
+{
+    return qwtToPoints<QPolygon, QPoint>( 
+        boundingRect, xMap, yMap, series, from, to, QwtRoundI() );
+}
+
+template<class Round>
+static inline QPolygonF qwtToPointsF(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to, Round round )
+{
+    return qwtToPoints<QPolygonF, QPointF>( 
+        boundingRect, xMap, yMap, series, from, to, round );
+}
+
+// Mapping points with filtering out consecutive
+// points mapped to the same position
+
+template<class Polygon, class Point, class Round>
+static inline Polygon qwtToPolylineFiltered( 
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, 
+    int from, int to, Round round )
+{
+    // in curves with many points consecutive points
+    // are often mapped to the same position. As this might
+    // result in empty lines ( or symbols hidden by others )
+    // we try to filter them out
+
+    Polygon polyline( to - from + 1 );
+    Point *points = polyline.data();
+
+    const QPointF sample0 = series->sample( from );
+
+    points[0].rx() = round( xMap.transform( sample0.x() ) );
+    points[0].ry() = round( yMap.transform( sample0.y() ) );
+
+    int pos = 0;
+    for ( int i = from + 1; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const Point p( round( xMap.transform( sample.x() ) ),
+            round( yMap.transform( sample.y() ) ) );
+
+        if ( points[pos] != p )
+            points[++pos] = p;
+    }
+
+    polyline.resize( pos + 1 );
+    return polyline;
+}
+
+static inline QPolygon qwtToPolylineFilteredI(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to )
+{
+    return qwtToPolylineFiltered<QPolygon, QPoint>(
+        xMap, yMap, series, from, to, QwtRoundI() );
+}
+
+template<class Round>
+static inline QPolygonF qwtToPolylineFilteredF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to, Round round )
+{
+    return qwtToPolylineFiltered<QPolygonF, QPointF>(
+        xMap, yMap, series, from, to, round );
+} 
+
+template<class Polygon, class Point>
+static inline Polygon qwtToPointsFiltered(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    // F.e. in scatter plots ( no connecting lines ) we
+    // can sort out all duplicates ( not only consecutive points )
+
+    Polygon polygon( to - from + 1 );
+    Point *points = polygon.data();
+
+    QwtPixelMatrix pixelMatrix( boundingRect.toAlignedRect() );
+
+    int numPoints = 0;
+    for ( int i = from; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const int x = qwtRoundValue( xMap.transform( sample.x() ) );
+        const int y = qwtRoundValue( yMap.transform( sample.y() ) );
+
+        if ( pixelMatrix.testAndSetPixel( x, y, true ) == false )
+        {
+            points[ numPoints ].rx() = x;
+            points[ numPoints ].ry() = y;
+
+            numPoints++;
+        }
+    }
+
+    polygon.resize( numPoints );
+    return polygon;
+}
+
+static inline QPolygon qwtToPointsFilteredI(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    return qwtToPointsFiltered<QPolygon, QPoint>(
+        boundingRect, xMap, yMap, series, from, to );
+} 
+
+static inline QPolygonF qwtToPointsFilteredF(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    return qwtToPointsFiltered<QPolygonF, QPointF>(
+        boundingRect, xMap, yMap, series, from, to );
+}
+
+class QwtPointMapper::PrivateData
+{
+public:
+    PrivateData():
+        boundingRect( qwtInvalidRect )
+    {
+    }
+
+    QRectF boundingRect;
+    QwtPointMapper::TransformationFlags flags;
+};
+
+//! Constructor
+QwtPointMapper::QwtPointMapper()
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPointMapper::~QwtPointMapper()
+{
+    delete d_data;
+}
+
+/*!
+  Set the flags affecting the transformation process
+
+  \param flags Flags
+  \sa flags(), setFlag()
+ */
+void QwtPointMapper::setFlags( TransformationFlags flags )
+{
+    d_data->flags = flags;
+}
+
+/*!
+  \return Flags affecting the transformation process
+  \sa setFlags(), setFlag()
+ */
+QwtPointMapper::TransformationFlags QwtPointMapper::flags() const
+{
+    return d_data->flags;
+}
+
+/*!
+  Modify a flag affecting the transformation process
+
+  \param flag Flag type
+  \param on Value
+
+  \sa flag(), setFlags()
+ */
+void QwtPointMapper::setFlag( TransformationFlag flag, bool on )
+{
+    if ( on )
+        d_data->flags |= flag;
+    else
+        d_data->flags &= ~flag;
+}
+
+/*!
+  \return True, when the flag is set
+  \param flag Flag type
+  \sa setFlag(), setFlags()
+ */
+bool QwtPointMapper::testFlag( TransformationFlag flag ) const
+{
+    return d_data->flags & flag;
+}
+
+/*!
+  Set a bounding rectangle for the point mapping algorithm
+
+  A valid bounding rectangle can be used for optimizations
+
+  \param rect Bounding rectangle
+  \sa boundingRect()
+ */
+void QwtPointMapper::setBoundingRect( const QRectF &rect )
+{
+    d_data->boundingRect = rect;
+}
+
+/*!
+  \return Bounding rectangle
+  \sa setBoundingRect()
+ */
+QRectF QwtPointMapper::boundingRect() const
+{
+    return d_data->boundingRect;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygonF
+
+  When the WeedOutPoints flag is enabled consecutive points,
+  that are mapped to the same position will be one point. 
+
+  When RoundPoints is set all points are rounded to integers
+  but returned as PolygonF - what only makes sense
+  when the further processing of the values need a QPolygonF.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygonF QwtPointMapper::toPolygonF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygonF polyline;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            polyline = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            polyline = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+    else
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            polyline = qwtToPointsF( qwtInvalidRect, 
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            polyline = qwtToPointsF( qwtInvalidRect, 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+
+    return polyline;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygon
+
+  When the WeedOutPoints flag is enabled consecutive points,
+  that are mapped to the same position will be one point. 
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygon QwtPointMapper::toPolygon(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygon polyline;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        polyline = qwtToPolylineFilteredI( 
+            xMap, yMap, series, from, to );
+    }
+    else
+    {
+        polyline = qwtToPointsI( 
+            qwtInvalidRect, xMap, yMap, series, from, to );
+    }
+
+    return polyline;
+}
+
+/*!
+  \brief Translate a series into a QPolygonF
+
+  - WeedOutPoints & RoundPoints & boundingRect().isValid()
+    All points that are mapped to the same position 
+    will be one point. Points outside of the bounding
+    rectangle are ignored.
+ 
+  - WeedOutPoints & RoundPoints & !boundingRect().isValid()
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - WeedOutPoints & !RoundPoints 
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - !WeedOutPoints & boundingRect().isValid()
+    Points outside of the bounding rectangle are ignored.
+
+  When RoundPoints is set all points are rounded to integers
+  but returned as PolygonF - what only makes sense
+  when the further processing of the values need a QPolygonF.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygonF QwtPointMapper::toPointsF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygonF points;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            if ( d_data->boundingRect.isValid() )
+            {   
+                points = qwtToPointsFilteredF( d_data->boundingRect,
+                    xMap, yMap, series, from, to );
+            }
+            else
+            {   
+                // without a bounding rectangle all we can
+                // do is to filter out duplicates of
+                // consecutive points
+
+                points = qwtToPolylineFilteredF( 
+                    xMap, yMap, series, from, to, QwtRoundF() );
+            }
+        }
+        else
+        {
+            // when rounding is not allowed we can't use
+            // qwtToPointsFilteredF
+
+            points = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+    else
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            points = qwtToPointsF( d_data->boundingRect,
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            points = qwtToPointsF( d_data->boundingRect,
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+
+    return points;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygon
+
+  - WeedOutPoints & boundingRect().isValid()
+    All points that are mapped to the same position 
+    will be one point. Points outside of the bounding
+    rectangle are ignored.
+ 
+  - WeedOutPoints & !boundingRect().isValid()
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - !WeedOutPoints & boundingRect().isValid()
+    Points outside of the bounding rectangle are ignored.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygon QwtPointMapper::toPoints(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygon points;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->boundingRect.isValid() )
+        {
+            points = qwtToPointsFilteredI( d_data->boundingRect,
+                xMap, yMap, series, from, to );
+        }
+        else
+        {
+            // when we don't have the bounding rectangle all
+            // we can do is to filter out consecutive duplicates
+
+            points = qwtToPolylineFilteredI( 
+                xMap, yMap, series, from, to );
+        }
+    }
+    else
+    {
+        points = qwtToPointsI( 
+            d_data->boundingRect, xMap, yMap, series, from, to );
+    }
+
+    return points;
+}
+
+
+/*!
+  \brief Translate a series into a QImage
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+  \param pen Pen used for drawing a point
+             of the image, where a point is mapped to
+  \param antialiased True, when the dots should be displayed
+                     antialiased
+  \param numThreads Number of threads to be used for rendering.
+                   If numThreads is set to 0, the system specific
+                   ideal thread count is used.
+
+  \return Image displaying the series
+*/
+QImage QwtPointMapper::toImage(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to, 
+    const QPen &pen, bool antialiased, uint numThreads ) const
+{
+    Q_UNUSED( antialiased )
+
+#if QWT_USE_THREADS
+    if ( numThreads == 0 )
+        numThreads = QThread::idealThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = 1;
+#else
+    Q_UNUSED( numThreads )
+#endif
+
+    // a very special optimization for scatter plots
+    // where every sample is mapped to one pixel only.
+
+    const QRect rect = d_data->boundingRect.toAlignedRect();
+
+    QImage image( rect.size(), QImage::Format_ARGB32 );
+    image.fill( Qt::transparent );
+
+    if ( pen.width() <= 1 && pen.color().alpha() == 255 )
+    {
+        QwtDotsCommand command;
+        command.series = series;
+        command.rgb = pen.color().rgba();
+
+#if QWT_USE_THREADS
+        const int numPoints = ( to - from + 1 ) / numThreads;
+
+        QList< QFuture<void> > futures;
+        for ( uint i = 0; i < numThreads; i++ )
+        {
+            const QPoint pos = rect.topLeft();
+
+            const int index0 = from + i * numPoints;
+            if ( i == numThreads - 1 )
+            {
+                command.from = index0;
+                command.to = to;
+
+                qwtRenderDots( xMap, yMap, command, pos, &image );
+            }
+            else
+            {
+                command.from = index0;
+                command.to = index0 + numPoints - 1;
+
+                futures += QtConcurrent::run( &qwtRenderDots, 
+                    xMap, yMap, command, pos, &image );
+            }
+        }
+        for ( int i = 0; i < futures.size(); i++ )
+            futures[i].waitForFinished();
+#else
+        command.from = from;
+        command.to = to;
+
+        qwtRenderDots( xMap, yMap, command, rect.topLeft(), &image );
+#endif
+    }
+    else
+    {
+        // fallback implementation: to be replaced later by
+        // setting the pixels of the image like above, TODO ...
+
+        QPainter painter( &image );
+        painter.setPen( pen );
+        painter.setRenderHint( QPainter::Antialiasing, antialiased );
+
+        const int chunkSize = 1000;
+        for ( int i = from; i <= to; i += chunkSize )
+        {
+            const int indexTo = qMin( i + chunkSize - 1, to );
+            const QPolygon points = toPoints(
+                xMap, yMap, series, i, indexTo );
+
+            painter.drawPoints( points );
+        }
+    }
+
+    return image;
+}
diff --git a/qwt/qwt_point_mapper.h b/qwt/qwt_point_mapper.h
new file mode 100644
index 0000000..918fc8a
--- /dev/null
+++ b/qwt/qwt_point_mapper.h
@@ -0,0 +1,89 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_POINT_MAPPER_H
+#define QWT_POINT_MAPPER_H
+
+#include "qwt_global.h"
+#include "qwt_series_data.h"
+#include <qimage.h>
+
+class QwtScaleMap;
+class QPolygonF;
+class QPolygon;
+
+/*!
+  \brief A helper class for translating a series of points
+
+  QwtPointMapper is a collection of methods and optimizations
+  for translating a series of points into paint device coordinates. 
+  It is used by QwtPlotCurve but might also be useful for 
+  similar plot items displaying a QwtSeriesData<QPointF>.
+ */
+class QWT_EXPORT QwtPointMapper
+{
+public:
+    /*!  
+      \brief Flags affecting the transformation process
+      \sa setFlag(), setFlags()
+     */
+    enum TransformationFlag
+    {
+        //! Round points to integer values
+        RoundPoints = 0x01,
+
+        /*! 
+          Try to remove points, that are translated to the
+          same position.
+         */
+        WeedOutPoints = 0x02
+    };
+
+    /*!  
+      \brief Flags affecting the transformation process
+      \sa setFlag(), setFlags()
+     */
+    typedef QFlags<TransformationFlag> TransformationFlags;
+
+    QwtPointMapper();
+    ~QwtPointMapper();
+
+    void setFlags( TransformationFlags );
+    TransformationFlags flags() const;
+
+    void setFlag( TransformationFlag, bool on = true );
+    bool testFlag( TransformationFlag ) const;
+
+    void setBoundingRect( const QRectF & );
+    QRectF boundingRect() const;
+
+    QPolygonF toPolygonF( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygon toPolygon( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygon toPoints( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygonF toPointsF( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QImage toImage( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to, 
+        const QPen &, bool antialiased, uint numThreads ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPointMapper::TransformationFlags )
+
+#endif
diff --git a/qwt/qwt_point_polar.cpp b/qwt/qwt_point_polar.cpp
new file mode 100644
index 0000000..644bcf1
--- /dev/null
+++ b/qwt/qwt_point_polar.cpp
@@ -0,0 +1,121 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * QwtPolar Widget Library
+ * Copyright (C) 2008   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_point_polar.h"
+#include "qwt_math.h"
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#endif
+
+/*!
+   Convert and assign values from a point in Cartesian coordinates
+
+   \param p Point in Cartesian coordinates
+   \sa setPoint(), toPoint()
+*/
+QwtPointPolar::QwtPointPolar( const QPointF &p )
+{
+    d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) );
+    d_azimuth = qAtan2( p.y(), p.x() );
+}
+
+/*!
+   Convert and assign values from a point in Cartesian coordinates
+   \param p Point in Cartesian coordinates
+*/
+void QwtPointPolar::setPoint( const QPointF &p )
+{
+    d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) );
+    d_azimuth = qAtan2( p.y(), p.x() );
+}
+
+/*!
+   Convert and return values in Cartesian coordinates
+
+   \return Converted point in Cartesian coordinates
+
+   \note Invalid or null points will be returned as QPointF(0.0, 0.0)
+   \sa isValid(), isNull()
+*/
+QPointF QwtPointPolar::toPoint() const
+{
+    if ( d_radius <= 0.0 )
+        return QPointF( 0.0, 0.0 );
+
+    const double x = d_radius * qCos( d_azimuth );
+    const double y = d_radius * qSin( d_azimuth );
+
+    return QPointF( x, y );
+}
+
+/*!
+    \brief Compare 2 points
+
+    Two points are equal to each other if radius and
+    azimuth-coordinates are the same. Points are not equal, when
+    the azimuth differs, but other.azimuth() == azimuth() % (2 * PI).
+
+    \return True if the point is equal to other; otherwise return false.
+
+    \sa normalized()
+*/
+bool QwtPointPolar::operator==( const QwtPointPolar &other ) const
+{
+    return d_radius == other.d_radius && d_azimuth == other.d_azimuth;
+}
+
+/*!
+    Compare 2 points
+
+    Two points are equal to each other if radius and
+    azimuth-coordinates are the same. Points are not equal, when
+    the azimuth differs, but other.azimuth() == azimuth() % (2 * PI).
+
+    \return True if the point is not equal to other; otherwise return false.
+    \sa normalized()
+*/
+bool QwtPointPolar::operator!=( const QwtPointPolar &other ) const
+{
+    return d_radius != other.d_radius || d_azimuth != other.d_azimuth;
+}
+
+/*!
+   Normalize radius and azimuth
+
+   When the radius is < 0.0 it is set to 0.0. The azimuth is
+   a value >= 0.0 and < 2 * M_PI.
+
+   \return Normalized point
+*/
+QwtPointPolar QwtPointPolar::normalized() const
+{
+    const double radius = qMax( d_radius, 0.0 );
+
+    double azimuth = d_azimuth;
+    if ( azimuth < -2.0 * M_PI || azimuth >= 2 * M_PI )
+        azimuth = ::fmod( d_azimuth, 2 * M_PI );
+
+    if ( azimuth < 0.0 )
+        azimuth += 2 * M_PI;
+
+    return QwtPointPolar( azimuth, radius );
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtPointPolar &point )
+{
+    debug.nospace() << "QwtPointPolar(" 
+        << point.azimuth() << "," << point.radius() << ")";
+
+    return debug.space();
+}
+
+#endif
+
diff --git a/qwt/qwt_point_polar.h b/qwt/qwt_point_polar.h
new file mode 100644
index 0000000..2b0a162
--- /dev/null
+++ b/qwt/qwt_point_polar.h
@@ -0,0 +1,201 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file */
+#ifndef _QWT_POINT_POLAR_H_
+#define _QWT_POINT_POLAR_H_ 1
+
+#include "qwt_global.h"
+#include "qwt_math.h"
+#include <qpoint.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A point in polar coordinates
+
+  In polar coordinates a point is determined by an angle and a distance.
+  See http://en.wikipedia.org/wiki/Polar_coordinate_system
+*/
+
+class QWT_EXPORT QwtPointPolar
+{
+public:
+    QwtPointPolar();
+    QwtPointPolar( double azimuth, double radius );
+    QwtPointPolar( const QwtPointPolar & );
+    QwtPointPolar( const QPointF & );
+
+    void setPoint( const QPointF & );
+    QPointF toPoint() const;
+
+    bool isValid() const;
+    bool isNull() const;
+
+    double radius() const;
+    double azimuth() const;
+
+    double &rRadius();
+    double &rAzimuth();
+
+    void setRadius( double );
+    void setAzimuth( double );
+
+    bool operator==( const QwtPointPolar & ) const;
+    bool operator!=( const QwtPointPolar & ) const;
+
+    QwtPointPolar normalized() const;
+
+private:
+    double d_azimuth;
+    double d_radius;
+};
+
+/*!
+    Constructs a null point, with a radius and azimuth set to 0.0.
+    \sa QPointF::isNull()
+*/
+inline QwtPointPolar::QwtPointPolar():
+    d_azimuth( 0.0 ),
+    d_radius( 0.0 )
+{
+}
+
+/*!
+   Constructs a point with coordinates specified by radius and azimuth.
+
+   \param azimuth Azimuth
+   \param radius Radius
+*/
+inline QwtPointPolar::QwtPointPolar( double azimuth, double radius ):
+    d_azimuth( azimuth ),
+    d_radius( radius )
+{
+}
+
+/*!
+    Constructs a point using the values of the point specified.
+    \param other Other point
+*/
+inline QwtPointPolar::QwtPointPolar( const QwtPointPolar &other ):
+    d_azimuth( other.d_azimuth ),
+    d_radius( other.d_radius )
+{
+}
+
+//! Returns true if radius() >= 0.0
+inline bool QwtPointPolar::isValid() const
+{
+    return d_radius >= 0.0;
+}
+
+//! Returns true if radius() >= 0.0
+inline bool QwtPointPolar::isNull() const
+{
+    return d_radius == 0.0;
+}
+
+//! Returns the radius.
+inline double QwtPointPolar::radius() const
+{
+    return d_radius;
+}
+
+//! Returns the azimuth.
+inline double QwtPointPolar::azimuth() const
+{
+    return d_azimuth;
+}
+
+//! Returns the radius.
+inline double &QwtPointPolar::rRadius()
+{
+    return d_radius;
+}
+
+//! Returns the azimuth.
+inline double &QwtPointPolar::rAzimuth()
+{
+    return d_azimuth;
+}
+
+//! Sets the radius to radius.
+inline void QwtPointPolar::setRadius( double radius )
+{
+    d_radius = radius;
+}
+
+//! Sets the atimuth to atimuth.
+inline void QwtPointPolar::setAzimuth( double azimuth )
+{
+    d_azimuth = azimuth;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtPointPolar & );
+#endif
+
+inline QPoint qwtPolar2Pos( const QPoint &pole,
+    double radius, double angle )
+{
+    const double x = pole.x() + radius * qCos( angle );
+    const double y = pole.y() - radius * qSin( angle );
+
+    return QPoint( qRound( x ), qRound( y ) );
+}
+
+inline QPoint qwtDegree2Pos( const QPoint &pole,
+    double radius, double angle )
+{
+    return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+}
+
+inline QPointF qwtPolar2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+    const double x = pole.x() + radius * qCos( angle );
+    const double y = pole.y() - radius * qSin( angle );
+
+    return QPointF( x, y);
+}
+
+inline QPointF qwtDegree2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+    return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+}
+
+inline QPointF qwtFastPolar2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+#if QT_VERSION < 0x040601
+    const double x = pole.x() + radius * ::cos( angle );
+    const double y = pole.y() - radius * ::sin( angle );
+#else
+    const double x = pole.x() + radius * qFastCos( angle );
+    const double y = pole.y() - radius * qFastSin( angle );
+#endif
+
+    return QPointF( x, y);
+}
+
+inline QPointF qwtFastDegree2Pos( const QPointF &pole,
+    double radius, double angle )
+{   
+    return qwtFastPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+} 
+
+inline QwtPointPolar qwtFastPos2Polar( const QPointF &pos )
+{
+    return QwtPointPolar( qwtFastAtan2( pos.y(), pos.x() ),
+        qSqrt( qwtSqr( pos.x() ) + qwtSqr( pos.y() ) ) );
+}
+
+#endif 
diff --git a/qwt/qwt_raster_data.cpp b/qwt/qwt_raster_data.cpp
new file mode 100644
index 0000000..a3b0d3d
--- /dev/null
+++ b/qwt/qwt_raster_data.cpp
@@ -0,0 +1,397 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_raster_data.h"
+#include "qwt_point_3d.h"
+
+class QwtRasterData::ContourPlane
+{
+public:
+    inline ContourPlane( double z ):
+        d_z( z )
+    {
+    }
+
+    inline bool intersect( const QwtPoint3D vertex[3],
+        QPointF line[2], bool ignoreOnPlane ) const;
+
+    inline double z() const { return d_z; }
+
+private:
+    inline int compare( double z ) const;
+    inline QPointF intersection(
+        const QwtPoint3D& p1, const QwtPoint3D &p2 ) const;
+
+    double d_z;
+};
+
+inline bool QwtRasterData::ContourPlane::intersect(
+    const QwtPoint3D vertex[3], QPointF line[2],
+    bool ignoreOnPlane ) const
+{
+    bool found = true;
+
+    // Are the vertices below (-1), on (0) or above (1) the plan ?
+    const int eq1 = compare( vertex[0].z() );
+    const int eq2 = compare( vertex[1].z() );
+    const int eq3 = compare( vertex[2].z() );
+
+    /*
+        (a) All the vertices lie below the contour level.
+        (b) Two vertices lie below and one on the contour level.
+        (c) Two vertices lie below and one above the contour level.
+        (d) One vertex lies below and two on the contour level.
+        (e) One vertex lies below, one on and one above the contour level.
+        (f) One vertex lies below and two above the contour level.
+        (g) Three vertices lie on the contour level.
+        (h) Two vertices lie on and one above the contour level.
+        (i) One vertex lies on and two above the contour level.
+        (j) All the vertices lie above the contour level.
+     */
+
+    static const int tab[3][3][3] =
+    {
+        // jump table to avoid nested case statements
+        { { 0, 0, 8 }, { 0, 2, 5 }, { 7, 6, 9 } },
+        { { 0, 3, 4 }, { 1, 10, 1 }, { 4, 3, 0 } },
+        { { 9, 6, 7 }, { 5, 2, 0 }, { 8, 0, 0 } }
+    };
+
+    const int edgeType = tab[eq1+1][eq2+1][eq3+1];
+    switch ( edgeType )
+    {
+        case 1:
+            // d(0,0,-1), h(0,0,1)
+            line[0] = vertex[0].toPoint();
+            line[1] = vertex[1].toPoint();
+            break;
+        case 2:
+            // d(-1,0,0), h(1,0,0)
+            line[0] = vertex[1].toPoint();
+            line[1] = vertex[2].toPoint();
+            break;
+        case 3:
+            // d(0,-1,0), h(0,1,0)
+            line[0] = vertex[2].toPoint();
+            line[1] = vertex[0].toPoint();
+            break;
+        case 4:
+            // e(0,-1,1), e(0,1,-1)
+            line[0] = vertex[0].toPoint();
+            line[1] = intersection( vertex[1], vertex[2] );
+            break;
+        case 5:
+            // e(-1,0,1), e(1,0,-1)
+            line[0] = vertex[1].toPoint();
+            line[1] = intersection( vertex[2], vertex[0] );
+            break;
+        case 6:
+            // e(-1,1,0), e(1,0,-1)
+            line[0] = vertex[2].toPoint();
+            line[1] = intersection( vertex[0], vertex[1] );
+            break;
+        case 7:
+            // c(-1,1,-1), f(1,1,-1)
+            line[0] = intersection( vertex[0], vertex[1] );
+            line[1] = intersection( vertex[1], vertex[2] );
+            break;
+        case 8:
+            // c(-1,-1,1), f(1,1,-1)
+            line[0] = intersection( vertex[1], vertex[2] );
+            line[1] = intersection( vertex[2], vertex[0] );
+            break;
+        case 9:
+            // f(-1,1,1), c(1,-1,-1)
+            line[0] = intersection( vertex[2], vertex[0] );
+            line[1] = intersection( vertex[0], vertex[1] );
+            break;
+        case 10:
+            // g(0,0,0)
+            // The CONREC algorithm has no satisfying solution for
+            // what to do, when all vertices are on the plane.
+
+            if ( ignoreOnPlane )
+                found = false;
+            else
+            {
+                line[0] = vertex[2].toPoint();
+                line[1] = vertex[0].toPoint();
+            }
+            break;
+        default:
+            found = false;
+    }
+
+    return found;
+}
+
+inline int QwtRasterData::ContourPlane::compare( double z ) const
+{
+    if ( z > d_z )
+        return 1;
+
+    if ( z < d_z )
+        return -1;
+
+    return 0;
+}
+
+inline QPointF QwtRasterData::ContourPlane::intersection(
+    const QwtPoint3D& p1, const QwtPoint3D &p2 ) const
+{
+    const double h1 = p1.z() - d_z;
+    const double h2 = p2.z() - d_z;
+
+    const double x = ( h2 * p1.x() - h1 * p2.x() ) / ( h2 - h1 );
+    const double y = ( h2 * p1.y() - h1 * p2.y() ) / ( h2 - h1 );
+
+    return QPointF( x, y );
+}
+
+//! Constructor
+QwtRasterData::QwtRasterData()
+{
+}
+
+//! Destructor
+QwtRasterData::~QwtRasterData()
+{
+}
+
+/*!
+   Set the bounding interval for the x, y or z coordinates.
+
+   \param axis Axis
+   \param interval Bounding interval
+
+   \sa interval()
+*/
+void QwtRasterData::setInterval( Qt::Axis axis, const QwtInterval &interval )
+{
+    d_intervals[axis] = interval;
+}
+
+/*!
+  \brief Initialize a raster
+
+  Before the composition of an image QwtPlotSpectrogram calls initRaster(),
+  announcing the area and its resolution that will be requested.
+
+  The default implementation does nothing, but for data sets that
+  are stored in files, it might be good idea to reimplement initRaster(),
+  where the data is resampled and loaded into memory.
+
+  \param area Area of the raster
+  \param raster Number of horizontal and vertical pixels
+
+  \sa initRaster(), value()
+*/
+void QwtRasterData::initRaster( const QRectF &area, const QSize &raster )
+{
+    Q_UNUSED( area );
+    Q_UNUSED( raster );
+}
+
+/*!
+  \brief Discard a raster
+
+  After the composition of an image QwtPlotSpectrogram calls discardRaster().
+
+  The default implementation does nothing, but if data has been loaded
+  in initRaster(), it could deleted now.
+
+  \sa initRaster(), value()
+*/
+void QwtRasterData::discardRaster()
+{
+}
+
+/*!
+   \brief Pixel hint
+
+   pixelHint() returns the geometry of a pixel, that can be used 
+   to calculate the resolution and alignment of the plot item, that is
+   representing the data. 
+   
+   Width and height of the hint need to be the horizontal  
+   and vertical distances between 2 neighbored points. 
+   The center of the hint has to be the position of any point 
+   ( it doesn't matter which one ).
+
+   An empty hint indicates, that there are values for any detail level.
+
+   Limiting the resolution of the image might significantly improve
+   the performance and heavily reduce the amount of memory when rendering
+   a QImage from the raster data. 
+
+   The default implementation returns an empty rectangle recommending
+   to render in target device ( f.e. screen ) resolution.
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel 
+*/
+QRectF QwtRasterData::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area );
+    return QRectF(); 
+}
+
+/*!
+   Calculate contour lines
+
+   \param rect Bounding rectangle for the contour lines
+   \param raster Number of data pixels of the raster data
+   \param levels List of limits, where to insert contour lines
+   \param flags Flags to customize the contouring algorithm
+
+   \return Calculated contour lines
+
+   An adaption of CONREC, a simple contouring algorithm.
+   http://local.wasp.uwa.edu.au/~pbourke/papers/conrec/
+*/
+QwtRasterData::ContourLines QwtRasterData::contourLines(
+    const QRectF &rect, const QSize &raster,
+    const QList<double> &levels, ConrecFlags flags ) const
+{
+    ContourLines contourLines;
+
+    if ( levels.size() == 0 || !rect.isValid() || !raster.isValid() )
+        return contourLines;
+
+    const double dx = rect.width() / raster.width();
+    const double dy = rect.height() / raster.height();
+
+    const bool ignoreOnPlane =
+        flags & QwtRasterData::IgnoreAllVerticesOnLevel;
+
+    const QwtInterval range = interval( Qt::ZAxis );
+    bool ignoreOutOfRange = false;
+    if ( range.isValid() )
+        ignoreOutOfRange = flags & IgnoreOutOfRange;
+
+    QwtRasterData *that = const_cast<QwtRasterData *>( this );
+    that->initRaster( rect, raster );
+
+    for ( int y = 0; y < raster.height() - 1; y++ )
+    {
+        enum Position
+        {
+            Center,
+
+            TopLeft,
+            TopRight,
+            BottomRight,
+            BottomLeft,
+
+            NumPositions
+        };
+
+        QwtPoint3D xy[NumPositions];
+
+        for ( int x = 0; x < raster.width() - 1; x++ )
+        {
+            const QPointF pos( rect.x() + x * dx, rect.y() + y * dy );
+
+            if ( x == 0 )
+            {
+                xy[TopRight].setX( pos.x() );
+                xy[TopRight].setY( pos.y() );
+                xy[TopRight].setZ(
+                    value( xy[TopRight].x(), xy[TopRight].y() )
+                );
+
+                xy[BottomRight].setX( pos.x() );
+                xy[BottomRight].setY( pos.y() + dy );
+                xy[BottomRight].setZ(
+                    value( xy[BottomRight].x(), xy[BottomRight].y() )
+                );
+            }
+
+            xy[TopLeft] = xy[TopRight];
+            xy[BottomLeft] = xy[BottomRight];
+
+            xy[TopRight].setX( pos.x() + dx );
+            xy[TopRight].setY( pos.y() );
+            xy[BottomRight].setX( pos.x() + dx );
+            xy[BottomRight].setY( pos.y() + dy );
+
+            xy[TopRight].setZ(
+                value( xy[TopRight].x(), xy[TopRight].y() )
+            );
+            xy[BottomRight].setZ(
+                value( xy[BottomRight].x(), xy[BottomRight].y() )
+            );
+
+            double zMin = xy[TopLeft].z();
+            double zMax = zMin;
+            double zSum = zMin;
+
+            for ( int i = TopRight; i <= BottomLeft; i++ )
+            {
+                const double z = xy[i].z();
+
+                zSum += z;
+                if ( z < zMin )
+                    zMin = z;
+                if ( z > zMax )
+                    zMax = z;
+            }
+
+            if ( ignoreOutOfRange )
+            {
+                if ( !range.contains( zMin ) || !range.contains( zMax ) )
+                    continue;
+            }
+
+            if ( zMax < levels[0] ||
+                zMin > levels[levels.size() - 1] )
+            {
+                continue;
+            }
+
+            xy[Center].setX( pos.x() + 0.5 * dx );
+            xy[Center].setY( pos.y() + 0.5 * dy );
+            xy[Center].setZ( 0.25 * zSum );
+
+            const int numLevels = levels.size();
+            for ( int l = 0; l < numLevels; l++ )
+            {
+                const double level = levels[l];
+                if ( level < zMin || level > zMax )
+                    continue;
+                QPolygonF &lines = contourLines[level];
+                const ContourPlane plane( level );
+
+                QPointF line[2];
+                QwtPoint3D vertex[3];
+
+                for ( int m = TopLeft; m < NumPositions; m++ )
+                {
+                    vertex[0] = xy[m];
+                    vertex[1] = xy[0];
+                    vertex[2] = xy[m != BottomLeft ? m + 1 : TopLeft];
+
+                    const bool intersects =
+                        plane.intersect( vertex, line, ignoreOnPlane );
+                    if ( intersects )
+                    {
+                        lines += line[0];
+                        lines += line[1];
+                    }
+                }
+            }
+        }
+    }
+
+    that->discardRaster();
+
+    return contourLines;
+}
diff --git a/qwt/qwt_raster_data.h b/qwt/qwt_raster_data.h
new file mode 100644
index 0000000..f69fa46
--- /dev/null
+++ b/qwt/qwt_raster_data.h
@@ -0,0 +1,95 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_RASTER_DATA_H
+#define QWT_RASTER_DATA_H 1
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qmap.h>
+#include <qlist.h>
+#include <qpolygon.h>
+
+class QwtScaleMap;
+
+/*!
+  \brief QwtRasterData defines an interface to any type of raster data.
+
+  QwtRasterData is an abstract interface, that is used by
+  QwtPlotRasterItem to find the values at the pixels of its raster.
+
+  Often a raster item is used to display values from a matrix. Then the
+  derived raster data class needs to implement some sort of resampling,
+  that maps the raster of the matrix into the requested raster of
+  the raster item ( depending on resolution and scales of the canvas ).
+*/
+class QWT_EXPORT QwtRasterData
+{
+public:
+    //! Contour lines
+    typedef QMap<double, QPolygonF> ContourLines;
+
+    //! Flags to modify the contour algorithm
+    enum ConrecFlag
+    {
+        //! Ignore all vertices on the same level
+        IgnoreAllVerticesOnLevel = 0x01,
+
+        //! Ignore all values, that are out of range
+        IgnoreOutOfRange = 0x02
+    };
+
+    //! Flags to modify the contour algorithm
+    typedef QFlags<ConrecFlag> ConrecFlags;
+
+    QwtRasterData();
+    virtual ~QwtRasterData();
+
+    virtual void setInterval( Qt::Axis, const QwtInterval & );
+    const QwtInterval &interval(Qt::Axis) const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual void initRaster( const QRectF &, const QSize& raster );
+    virtual void discardRaster();
+
+    /*!
+       \return the value at a raster position
+       \param x X value in plot coordinates
+       \param y Y value in plot coordinates
+    */
+    virtual double value( double x, double y ) const = 0;
+
+    virtual ContourLines contourLines( const QRectF &rect,
+        const QSize &raster, const QList<double> &levels,
+        ConrecFlags ) const;
+
+    class Contour3DPoint;
+    class ContourPlane;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtRasterData( const QwtRasterData & );
+    QwtRasterData &operator=( const QwtRasterData & );
+
+    QwtInterval d_intervals[3];
+};
+
+/*!
+   \return Bounding interval for a axis
+   \sa setInterval
+*/
+inline const QwtInterval &QwtRasterData::interval( Qt::Axis axis) const
+{
+    return d_intervals[axis];
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtRasterData::ConrecFlags )
+
+#endif
diff --git a/qwt/qwt_round_scale_draw.cpp b/qwt/qwt_round_scale_draw.cpp
new file mode 100644
index 0000000..17a116f
--- /dev/null
+++ b/qwt/qwt_round_scale_draw.cpp
@@ -0,0 +1,314 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_round_scale_draw.h"
+#include "qwt_painter.h"
+#include "qwt_scale_div.h"
+#include "qwt_scale_map.h"
+#include "qwt_math.h"
+#include <qpen.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qmath.h>
+
+class QwtRoundScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        center( 50.0, 50.0 ),
+        radius( 50.0 ),
+        startAngle( -135.0 ),
+        endAngle( 135.0 )
+    {
+    }
+
+    QPointF center;
+    double radius;
+
+    double startAngle;
+    double endAngle;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The center is set to (50, 50) with a radius of 50.
+  The angle range is set to [-135, 135].
+*/
+QwtRoundScaleDraw::QwtRoundScaleDraw()
+{
+    d_data = new QwtRoundScaleDraw::PrivateData;
+
+    setRadius( 50 );
+    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
+}
+
+//! Destructor
+QwtRoundScaleDraw::~QwtRoundScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  Change of radius the scale
+
+  Radius is the radius of the backbone without ticks and labels.
+
+  \param radius New Radius
+  \sa moveCenter()
+*/
+void QwtRoundScaleDraw::setRadius( double radius )
+{
+    d_data->radius = radius;
+}
+
+/*!
+  Get the radius
+
+  Radius is the radius of the backbone without ticks and labels.
+
+  \return Radius of the scale
+  \sa setRadius(), extent()
+*/
+double QwtRoundScaleDraw::radius() const
+{
+    return d_data->radius;
+}
+
+/*!
+   Move the center of the scale draw, leaving the radius unchanged
+
+   \param center New center
+   \sa setRadius()
+*/
+void QwtRoundScaleDraw::moveCenter( const QPointF &center )
+{
+    d_data->center = center;
+}
+
+//! Get the center of the scale
+QPointF QwtRoundScaleDraw::center() const
+{
+    return d_data->center;
+}
+
+/*!
+  \brief Adjust the baseline circle segment for round scales.
+
+  The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
+  The default setting is [ -135, 135 ].
+  An angle of 0 degrees corresponds to the 12 o'clock position,
+  and positive angles count in a clockwise direction.
+  \param angle1
+  \param angle2 boundaries of the angle interval in degrees.
+  \warning <ul>
+  <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
+      this range will be clipped.
+  <li>For angles more or equal than 360 degrees above or below min(angle1, angle2),
+      scale marks will not be drawn.
+  <li>If you need a counterclockwise scale, use QwtScaleDiv::setInterval()
+  </ul>
+*/
+void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 )
+{
+#if 0
+    angle1 = qBound( -360.0, angle1, 360.0 );
+    angle2 = qBound( -360.0, angle2, 360.0 );
+#endif
+
+    d_data->startAngle = angle1;
+    d_data->endAngle = angle2;
+
+    if ( d_data->startAngle == d_data->endAngle )
+    {
+        d_data->startAngle -= 1;
+        d_data->endAngle += 1;
+    }
+
+    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
+}
+
+/*!
+   Draws the label for a major scale tick
+
+   \param painter Painter
+   \param value Value
+
+   \sa drawTick(), drawBackbone()
+*/
+void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const
+{
+    const QwtText label = tickLabel( painter->font(), value );
+    if ( label.isEmpty() )
+        return;
+
+    const double tval = scaleMap().transform( value );
+    if ( ( tval >= d_data->startAngle + 360.0 )
+        || ( tval <= d_data->startAngle - 360.0 ) )
+    {
+        return;
+    }
+
+    double radius = d_data->radius;
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
+        hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        radius += spacing();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+        radius += tickLength( QwtScaleDiv::MajorTick );
+
+    const QSizeF sz = label.textSize( painter->font() );
+    const double arc = qwtRadians( tval );
+
+    const double x = d_data->center.x() +
+        ( radius + sz.width() / 2.0 ) * qSin( arc );
+    const double y = d_data->center.y() -
+        ( radius + sz.height() / 2.0 ) * qCos( arc );
+
+    const QRectF r( x - sz.width() / 2, y - sz.height() / 2,
+        sz.width(), sz.height() );
+    label.draw( painter, r );
+}
+
+/*!
+   Draw a tick
+
+   \param painter Painter
+   \param value Value of the tick
+   \param len Lenght of the tick
+
+   \sa drawBackbone(), drawLabel()
+*/
+void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const
+{
+    if ( len <= 0 )
+        return;
+
+    const double tval = scaleMap().transform( value );
+
+    const double cx = d_data->center.x();
+    const double cy = d_data->center.y();
+    const double radius = d_data->radius;
+
+    if ( ( tval < d_data->startAngle + 360.0 )
+        || ( tval > d_data->startAngle - 360.0 ) )
+    {
+        const double arc = qwtRadians( tval );
+
+        const double sinArc = qSin( arc );
+        const double cosArc = qCos( arc );
+
+        const double x1 = cx + radius * sinArc;
+        const double x2 = cx + ( radius + len ) * sinArc;
+        const double y1 = cy - radius * cosArc;
+        const double y2 = cy - ( radius + len ) * cosArc;
+
+        QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+    }
+}
+
+/*!
+   Draws the baseline of the scale
+   \param painter Painter
+
+   \sa drawTick(), drawLabel()
+*/
+void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const
+{
+    const double deg1 = scaleMap().p1();
+    const double deg2 = scaleMap().p2();
+
+    const int a1 = qRound( qMin( deg1, deg2 ) - 90 );
+    const int a2 = qRound( qMax( deg1, deg2 ) - 90 );
+
+    const double radius = d_data->radius;
+    const double x = d_data->center.x() - radius;
+    const double y = d_data->center.y() - radius;
+
+    painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ),
+        -a2 * 16, ( a2 - a1 + 1 ) * 16 );          // counterclockwise
+}
+
+/*!
+   Calculate the extent of the scale
+
+   The extent is the distance between the baseline to the outermost
+   pixel of the scale draw. radius() + extent() is an upper limit
+   for the radius of the bounding circle.
+
+   \param font Font used for painting the labels
+   \return Calculated extent
+
+   \sa setMinimumExtent(), minimumExtent()
+   \warning The implemented algorithm is not too smart and
+            calculates only an upper limit, that might be a
+            few pixels too large
+*/
+double QwtRoundScaleDraw::extent( const QFont &font ) const
+{
+    double d = 0.0;
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        const QwtScaleDiv &sd = scaleDiv();
+        const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick );
+        for ( int i = 0; i < ticks.count(); i++ )
+        {
+            const double value = ticks[i];
+            if ( !sd.contains( value ) )
+                continue;
+
+            const QwtText label = tickLabel( font, value );
+            if ( label.isEmpty() )
+                continue;
+
+            const double tval = scaleMap().transform( value );
+            if ( ( tval < d_data->startAngle + 360 )
+                && ( tval > d_data->startAngle - 360 ) )
+            {
+                const double arc = qwtRadians( tval );
+
+                const QSizeF sz = label.textSize( font );
+                const double off = qMax( sz.width(), sz.height() );
+
+                double x = off * qSin( arc );
+                double y = off * qCos( arc );
+
+                const double dist = qSqrt( x * x + y * y );
+                if ( dist > d )
+                    d = dist;
+            }
+        }
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        d += maxTickLength();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // pen width can be zero
+        d += pw;
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) &&
+        ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
+            hasComponent( QwtAbstractScaleDraw::Backbone ) ) )
+    {
+        d += spacing();
+    }
+
+    d = qMax( d, minimumExtent() );
+
+    return d;
+}
diff --git a/qwt/qwt_round_scale_draw.h b/qwt/qwt_round_scale_draw.h
new file mode 100644
index 0000000..54971bb
--- /dev/null
+++ b/qwt/qwt_round_scale_draw.h
@@ -0,0 +1,66 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ROUND_SCALE_DRAW_H
+#define QWT_ROUND_SCALE_DRAW_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_scale_draw.h"
+#include <qpoint.h>
+
+/*!
+  \brief A class for drawing round scales
+
+  QwtRoundScaleDraw can be used to draw round scales.
+  The circle segment can be adjusted by setAngleRange().
+  The geometry of the scale can be specified with
+  moveCenter() and setRadius().
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s),
+  the scale can be drawn with the QwtAbstractScaleDraw::draw() member.
+*/
+
+class QWT_EXPORT QwtRoundScaleDraw: public QwtAbstractScaleDraw
+{
+public:
+    QwtRoundScaleDraw();
+    virtual ~QwtRoundScaleDraw();
+
+    void setRadius( double radius );
+    double radius() const;
+
+    void moveCenter( double x, double y );
+    void moveCenter( const QPointF & );
+    QPointF center() const;
+
+    void setAngleRange( double angle1, double angle2 );
+
+    virtual double extent( const QFont & ) const;
+
+protected:
+    virtual void drawTick( QPainter *, double val, double len ) const;
+    virtual void drawBackbone( QPainter * ) const;
+    virtual void drawLabel( QPainter *, double val ) const;
+
+private:
+    QwtRoundScaleDraw( const QwtRoundScaleDraw & );
+    QwtRoundScaleDraw &operator=( const QwtRoundScaleDraw &other );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+//! Move the center of the scale draw, leaving the radius unchanged
+inline void QwtRoundScaleDraw::moveCenter( double x, double y )
+{
+    moveCenter( QPointF( x, y ) );
+}
+
+#endif
diff --git a/qwt/qwt_samples.h b/qwt/qwt_samples.h
new file mode 100644
index 0000000..6f9be3d
--- /dev/null
+++ b/qwt/qwt_samples.h
@@ -0,0 +1,239 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SAMPLES_H
+#define QWT_SAMPLES_H 1
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qvector.h>
+#include <qrect.h>
+
+//! \brief A sample of the types (x1-x2, y) or (x, y1-y2)
+class QWT_EXPORT QwtIntervalSample
+{
+public:
+    QwtIntervalSample();
+    QwtIntervalSample( double, const QwtInterval & );
+    QwtIntervalSample( double value, double min, double max );
+
+    bool operator==( const QwtIntervalSample & ) const;
+    bool operator!=( const QwtIntervalSample & ) const;
+
+    //! Value
+    double value;
+
+    //! Interval
+    QwtInterval interval;
+};
+
+/*!
+  Constructor
+  The value is set to 0.0, the interval is invalid
+*/
+inline QwtIntervalSample::QwtIntervalSample():
+    value( 0.0 )
+{
+}
+
+//! Constructor
+inline QwtIntervalSample::QwtIntervalSample(
+        double v, const QwtInterval &intv ):
+    value( v ),
+    interval( intv )
+{
+}
+
+//! Constructor
+inline QwtIntervalSample::QwtIntervalSample(
+        double v, double min, double max ):
+    value( v ),
+    interval( min, max )
+{
+}
+
+//! Compare operator
+inline bool QwtIntervalSample::operator==(
+    const QwtIntervalSample &other ) const
+{
+    return value == other.value && interval == other.interval;
+}
+
+//! Compare operator
+inline bool QwtIntervalSample::operator!=(
+    const QwtIntervalSample &other ) const
+{
+    return !( *this == other );
+}
+
+//! \brief A sample of the types (x1...xn, y) or (x, y1..yn)
+class QWT_EXPORT QwtSetSample
+{
+public:
+    QwtSetSample();
+    QwtSetSample( double, const QVector<double> & = QVector<double>() );
+
+    bool operator==( const QwtSetSample &other ) const;
+    bool operator!=( const QwtSetSample &other ) const;
+
+    double added() const;
+
+    //! value
+    double value;
+
+    //! Vector of values associated to value
+    QVector<double> set;
+};
+
+/*!
+  Constructor
+  The value is set to 0.0
+*/
+inline QwtSetSample::QwtSetSample():
+    value( 0.0 )
+{
+}
+
+/*!
+  Constructor
+
+  \param v Value
+  \param s Set of values
+*/
+inline QwtSetSample::QwtSetSample( double v, const QVector< double > &s ):
+    value( v ),
+    set( s )
+{
+}
+
+//! Compare operator
+inline bool QwtSetSample::operator==( const QwtSetSample &other ) const
+{
+    return value == other.value && set == other.set;
+}
+
+//! Compare operator
+inline bool QwtSetSample::operator!=( const QwtSetSample &other ) const
+{
+    return !( *this == other );
+}
+
+//! \return All values of the set added
+inline double QwtSetSample::added() const
+{
+    double y = 0.0;
+    for ( int i = 0; i < set.size(); i++ )
+        y += set[i];
+
+    return y;
+}
+
+/*!
+   \brief Open-High-Low-Close sample used in financial charts
+
+   In financial charts the movement of a price in a time interval is often
+   represented by the opening/closing prices and the lowest/highest prices
+   in this interval.
+
+   \sa QwtTradingChartData
+*/
+class QWT_EXPORT QwtOHLCSample
+{
+public:
+    QwtOHLCSample( double time = 0.0,
+        double open = 0.0, double high = 0.0,
+        double low = 0.0, double close = 0.0 );
+
+    QwtInterval boundingInterval() const;
+
+    bool isValid() const;
+
+    /*!
+      Time of the sample, usually a number representing
+      a specific interval - like a day.
+    */
+    double time;
+
+    //! Opening price
+    double open;
+
+    //! Highest price
+    double high;
+
+    //! Lowest price
+    double low;
+
+    //! Closing price
+    double close;
+};
+
+
+/*!
+  Constructor
+
+  \param t Time value
+  \param o Open value
+  \param h High value
+  \param l Low value
+  \param c Close value
+*/
+inline QwtOHLCSample::QwtOHLCSample( double t,
+        double o, double h, double l, double c ):
+    time( t ),
+    open( o ),
+    high( h ),
+    low( l ),
+    close( c )
+{
+}
+
+/*!
+  \brief Check if a sample is valid
+
+  A sample is valid, when all of the following checks are true:
+
+  - low <= high
+  - low <= open <= high
+  - low <= close <= high
+
+  \return True, when the sample is valid
+ */
+inline bool QwtOHLCSample::isValid() const
+{
+    return ( low <= high )
+        && ( open >= low )
+        && ( open <= high )
+        && ( close >= low )
+        && ( close <= high );
+}
+
+/*!
+   \brief Calculate the bounding interval of the OHLC values
+
+   For valid samples the limits of this interval are always low/high.
+
+   \return Bounding interval
+   \sa isValid()
+ */
+inline QwtInterval QwtOHLCSample::boundingInterval() const
+{
+    double minY = open;
+    minY = qMin( minY, high );
+    minY = qMin( minY, low );
+    minY = qMin( minY, close );
+
+    double maxY = open;
+    maxY = qMax( maxY, high );
+    maxY = qMax( maxY, low );
+    maxY = qMax( maxY, close );
+
+    return QwtInterval( minY, maxY );
+}
+
+#endif
diff --git a/qwt/qwt_sampling_thread.cpp b/qwt/qwt_sampling_thread.cpp
new file mode 100644
index 0000000..4cffb3d
--- /dev/null
+++ b/qwt/qwt_sampling_thread.cpp
@@ -0,0 +1,106 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_sampling_thread.h"
+#include "qwt_system_clock.h"
+
+class QwtSamplingThread::PrivateData
+{
+public:
+    QwtSystemClock clock;
+
+    double interval;
+    bool isStopped;
+};
+
+
+//! Constructor
+QwtSamplingThread::QwtSamplingThread( QObject *parent ):
+    QThread( parent )
+{
+    d_data = new PrivateData;
+    d_data->interval = 1000; // 1 second
+    d_data->isStopped = true;
+}
+
+//! Destructor
+QwtSamplingThread::~QwtSamplingThread()
+{
+    delete d_data;
+}
+
+/*!
+   Change the interval (in ms), when sample() is called.
+   The default interval is 1000.0 ( = 1s )
+
+   \param interval Interval
+   \sa interval()
+*/
+void QwtSamplingThread::setInterval( double interval )
+{
+    if ( interval < 0.0 )
+        interval = 0.0;
+
+    d_data->interval = interval;
+}
+
+/*!
+   \return Interval (in ms), between 2 calls of sample()
+   \sa setInterval()
+*/
+double QwtSamplingThread::interval() const
+{
+    return d_data->interval;
+}
+
+/*!
+   \return Time (in ms) since the thread was started
+   \sa QThread::start(), run()
+*/
+double QwtSamplingThread::elapsed() const
+{
+    if ( d_data->isStopped )
+        return 0.0;
+
+    return d_data->clock.elapsed();
+}
+
+/*!
+   Terminate the collecting thread
+   \sa QThread::start(), run()
+*/
+void QwtSamplingThread::stop()
+{
+    d_data->isStopped = true;
+}
+
+/*!
+   Loop collecting samples started from QThread::start()
+   \sa stop()
+*/
+void QwtSamplingThread::run()
+{
+    d_data->clock.start();
+    d_data->isStopped = false;
+
+    while ( !d_data->isStopped )
+    {
+        const double elapsed = d_data->clock.elapsed();
+        sample( elapsed / 1000.0 );
+
+        if ( d_data->interval > 0.0 )
+        {
+            const double msecs =
+                d_data->interval - ( d_data->clock.elapsed() - elapsed );
+
+            if ( msecs > 0.0 )
+                usleep( qRound( 1000.0 * msecs ) );
+        }
+    }
+}
diff --git a/qwt/qwt_sampling_thread.h b/qwt/qwt_sampling_thread.h
new file mode 100644
index 0000000..9b1a908
--- /dev/null
+++ b/qwt/qwt_sampling_thread.h
@@ -0,0 +1,50 @@
+#ifndef _QWT_SAMPLING_THREAD_H_
+#define _QWT_SAMPLING_THREAD_H_
+
+#include "qwt_global.h"
+#include <qthread.h>
+
+/*!
+  \brief A thread collecting samples at regular intervals.
+
+  Continuous signals are converted into a discrete signal by
+  collecting samples at regular intervals. A discrete signal
+  can be displayed by a QwtPlotSeriesItem on a QwtPlot widget.
+
+  QwtSamplingThread starts a thread calling periodically sample(),
+  to collect and store ( or emit ) a single sample.
+
+  \sa QwtPlotCurve, QwtPlotSeriesItem
+*/
+class QWT_EXPORT QwtSamplingThread: public QThread
+{
+    Q_OBJECT
+
+public:
+    virtual ~QwtSamplingThread();
+
+    double interval() const;
+    double elapsed() const;
+
+public Q_SLOTS:
+    void setInterval( double interval );
+    void stop();
+
+protected:
+    explicit QwtSamplingThread( QObject *parent = NULL );
+
+    virtual void run();
+
+    /*!
+       Collect a sample
+
+       \param elapsed Time since the thread was started in milliseconds
+     */
+    virtual void sample( double elapsed ) = 0;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_scale_div.cpp b/qwt/qwt_scale_div.cpp
new file mode 100644
index 0000000..2fd3d3f
--- /dev/null
+++ b/qwt/qwt_scale_div.cpp
@@ -0,0 +1,331 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_scale_div.h"
+#include "qwt_math.h"
+#include <qalgorithms.h>
+
+/*!
+  Construct a division without ticks
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+
+  \note lowerBound might be greater than upperBound for inverted scales
+ */
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+}
+
+/*!
+  Construct a scale division
+
+  \param interval Interval
+  \param ticks List of major, medium and minor ticks
+*/
+QwtScaleDiv::QwtScaleDiv( const QwtInterval &interval,
+        QList<double> ticks[NTickTypes] ):
+    d_lowerBound( interval.minValue() ),
+    d_upperBound( interval.maxValue() )
+{
+    for ( int i = 0; i < NTickTypes; i++ )
+        d_ticks[i] = ticks[i];
+}
+
+/*!
+  Construct a scale division
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+  \param ticks List of major, medium and minor ticks
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound,
+        QList<double> ticks[NTickTypes] ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+    for ( int i = 0; i < NTickTypes; i++ )
+        d_ticks[i] = ticks[i];
+}
+
+/*!
+  Construct a scale division
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+  \param minorTicks List of minor ticks
+  \param mediumTicks List medium ticks
+  \param majorTicks List of major ticks
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound,
+        const QList<double> &minorTicks, 
+        const QList<double> &mediumTicks,
+        const QList<double> &majorTicks ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+    d_ticks[ MinorTick ] = minorTicks;
+    d_ticks[ MediumTick ] = mediumTicks;
+    d_ticks[ MajorTick ] = majorTicks;
+}
+
+/*!
+  Change the interval
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+void QwtScaleDiv::setInterval( double lowerBound, double upperBound )
+{
+    d_lowerBound = lowerBound;
+    d_upperBound = upperBound;
+}
+
+/*!
+   Change the interval
+
+   \param interval Interval
+*/
+void QwtScaleDiv::setInterval( const QwtInterval &interval )
+{
+    d_lowerBound = interval.minValue();
+    d_upperBound = interval.maxValue();
+}
+
+/*!
+  \return lowerBound -> upperBound
+*/
+QwtInterval QwtScaleDiv::interval() const
+{
+    return QwtInterval( d_lowerBound, d_upperBound );
+}
+
+/*!
+  Set the first boundary
+
+  \param lowerBound First boundary
+  \sa lowerBiound(), setUpperBound()
+ */
+void QwtScaleDiv::setLowerBound( double lowerBound  )
+{
+    d_lowerBound = lowerBound;
+}
+
+/*!
+  \return First boundary
+  \sa upperBound()
+*/
+double QwtScaleDiv::lowerBound() const
+{
+    return d_lowerBound;
+}   
+
+/*!
+  Set the second boundary
+
+  \param upperBound Second boundary
+  \sa upperBound(), setLowerBound()
+ */
+void QwtScaleDiv::setUpperBound( double upperBound  )
+{
+    d_upperBound = upperBound;
+} 
+
+/*!
+  \return upper bound
+  \sa lowerBound()
+*/
+double QwtScaleDiv::upperBound() const
+{
+    return d_upperBound;
+}
+
+/*!
+  \return upperBound() - lowerBound()
+*/
+double QwtScaleDiv::range() const
+{
+    return d_upperBound - d_lowerBound;
+}
+
+/*!
+  \brief Equality operator
+  \return true if this instance is equal to other
+*/
+bool QwtScaleDiv::operator==( const QwtScaleDiv &other ) const
+{
+    if ( d_lowerBound != other.d_lowerBound ||
+        d_upperBound != other.d_upperBound )
+    {
+        return false;
+    }
+
+    for ( int i = 0; i < NTickTypes; i++ )
+    {
+        if ( d_ticks[i] != other.d_ticks[i] )
+            return false;
+    }
+
+    return true;
+}
+
+/*!
+  \brief Inequality
+  \return true if this instance is not equal to other
+*/
+bool QwtScaleDiv::operator!=( const QwtScaleDiv &other ) const
+{
+    return ( !( *this == other ) );
+}
+
+//! Check if the scale division is empty( lowerBound() == upperBound() )
+bool QwtScaleDiv::isEmpty() const
+{
+    return ( d_lowerBound == d_upperBound );
+}
+
+//! Check if the scale division is increasing( lowerBound() <= upperBound() )
+bool QwtScaleDiv::isIncreasing() const
+{
+    return d_lowerBound <= d_upperBound;
+}
+
+/*!
+  Return if a value is between lowerBound() and upperBound()
+
+  \param value Value
+  \return true/false
+*/
+bool QwtScaleDiv::contains( double value ) const
+{
+    const double min = qMin( d_lowerBound, d_upperBound );
+    const double max = qMax( d_lowerBound, d_upperBound );
+
+    return value >= min && value <= max;
+}
+
+/*! 
+   Invert the scale division
+   \sa inverted()
+ */
+void QwtScaleDiv::invert()
+{
+    qSwap( d_lowerBound, d_upperBound );
+
+    for ( int i = 0; i < NTickTypes; i++ )
+    {
+        QList<double>& ticks = d_ticks[i];
+
+        const int size = ticks.count();
+        const int size2 = size / 2;
+
+        for ( int j = 0; j < size2; j++ )
+            qSwap( ticks[j], ticks[size - 1 - j] );
+    }
+}
+
+/*! 
+  \return A scale division with inverted boundaries and ticks
+  \sa invert()
+ */
+QwtScaleDiv QwtScaleDiv::inverted() const
+{
+    QwtScaleDiv other = *this;
+    other.invert();
+
+    return other;
+}
+
+/*! 
+   Return a scale division with an interval [lowerBound, upperBound]
+   where all ticks outside this interval are removed
+
+   \param lowerBound Lower bound
+   \param upperBound Upper bound
+
+   \return Scale division with all ticks inside of the given interval
+
+   \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv QwtScaleDiv::bounded( 
+    double lowerBound, double upperBound ) const
+{
+    const double min = qMin( lowerBound, upperBound );
+    const double max = qMax( lowerBound, upperBound );
+
+    QwtScaleDiv sd;
+    sd.setInterval( lowerBound, upperBound );
+
+    for ( int tickType = 0; tickType < QwtScaleDiv::NTickTypes; tickType++ )
+    {
+        const QList<double> &ticks = d_ticks[ tickType ];
+
+        QList<double> boundedTicks;
+        for ( int i = 0; i < ticks.size(); i++ )
+        {
+            const double tick = ticks[i];
+            if ( tick >= min && tick <= max )
+                boundedTicks += tick;
+        }
+
+        sd.setTicks( tickType, boundedTicks );
+    }
+
+    return sd;
+
+}
+
+/*!
+    Assign ticks
+
+   \param type MinorTick, MediumTick or MajorTick
+   \param ticks Values of the tick positions
+*/
+void QwtScaleDiv::setTicks( int type, const QList<double> &ticks )
+{
+    if ( type >= 0 && type < NTickTypes )
+        d_ticks[type] = ticks;
+}
+
+/*!
+   Return a list of ticks
+
+   \param type MinorTick, MediumTick or MajorTick
+   \return Tick list
+*/
+QList<double> QwtScaleDiv::ticks( int type ) const
+{
+    if ( type >= 0 && type < NTickTypes )
+        return d_ticks[type];
+
+    return QList<double>();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtScaleDiv &scaleDiv )
+{
+    debug << scaleDiv.lowerBound() << "<->" << scaleDiv.upperBound();
+    debug << "Major: " << scaleDiv.ticks( QwtScaleDiv::MajorTick );
+    debug << "Medium: " << scaleDiv.ticks( QwtScaleDiv::MediumTick );
+    debug << "Minor: " << scaleDiv.ticks( QwtScaleDiv::MinorTick );
+
+    return debug;
+}
+
+#endif
+
diff --git a/qwt/qwt_scale_div.h b/qwt/qwt_scale_div.h
new file mode 100644
index 0000000..7259e22
--- /dev/null
+++ b/qwt/qwt_scale_div.h
@@ -0,0 +1,110 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_DIV_H
+#define QWT_SCALE_DIV_H
+
+#include "qwt_global.h"
+#include "qwt_interval.h"
+#include <qlist.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A class representing a scale division
+
+  A Qwt scale is defined by its boundaries and 3 list
+  for the positions of the major, medium and minor ticks.
+
+  The upperLimit() might be smaller than the lowerLimit()
+  to indicate inverted scales.
+
+  Scale divisions can be calculated from a QwtScaleEngine.
+
+  \sa QwtScaleEngine::divideScale(), QwtPlot::setAxisScaleDiv(),
+      QwtAbstractSlider::setScaleDiv()
+*/
+
+class QWT_EXPORT QwtScaleDiv
+{
+public:
+    //! Scale tick types
+    enum TickType
+    {
+        //! No ticks
+        NoTick = -1,
+
+        //! Minor ticks
+        MinorTick,
+
+        //! Medium ticks
+        MediumTick,
+
+        //! Major ticks
+        MajorTick,
+
+        //! Number of valid tick types
+        NTickTypes
+    };
+
+    explicit QwtScaleDiv( double lowerBound = 0.0, 
+        double upperBound = 0.0 );
+
+    explicit QwtScaleDiv( const QwtInterval &, QList<double>[NTickTypes] );
+
+    explicit QwtScaleDiv( double lowerBound, double upperBound,
+        QList<double>[NTickTypes] );
+
+    explicit QwtScaleDiv( double lowerBound, double upperBound, 
+        const QList<double> &minorTicks, const QList<double> &mediumTicks,
+        const QList<double> &majorTicks );
+
+    bool operator==( const QwtScaleDiv & ) const;
+    bool operator!=( const QwtScaleDiv & ) const;
+
+    void setInterval( double lowerBound, double upperBound );
+    void setInterval( const QwtInterval & );
+    QwtInterval interval() const;
+
+    void setLowerBound( double );
+    double lowerBound() const;
+
+    void setUpperBound( double );
+    double upperBound() const;
+
+    double range() const;
+
+    bool contains( double value ) const;
+
+    void setTicks( int tickType, const QList<double> & );
+    QList<double> ticks( int tickType ) const;
+
+    bool isEmpty() const;
+    bool isIncreasing() const;
+
+    void invert();
+    QwtScaleDiv inverted() const;
+
+    QwtScaleDiv bounded( double lowerBound, double upperBound ) const;
+
+private:
+    double d_lowerBound;
+    double d_upperBound;
+    QList<double> d_ticks[NTickTypes];
+};
+
+Q_DECLARE_TYPEINFO( QwtScaleDiv, Q_MOVABLE_TYPE );
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtScaleDiv & );
+#endif
+
+#endif
diff --git a/qwt/qwt_scale_draw.cpp b/qwt/qwt_scale_draw.cpp
new file mode 100644
index 0000000..6778ebe
--- /dev/null
+++ b/qwt/qwt_scale_draw.cpp
@@ -0,0 +1,926 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_scale_draw.h"
+#include "qwt_scale_div.h"
+#include "qwt_scale_map.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include <qpen.h>
+#include <qpainter.h>
+#include <qmath.h>
+
+#if QT_VERSION < 0x040601
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+class QwtScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        len( 0 ),
+        alignment( QwtScaleDraw::BottomScale ),
+        labelAlignment( 0 ),
+        labelRotation( 0.0 )
+    {
+    }
+
+    QPointF pos;
+    double len;
+
+    Alignment alignment;
+
+    Qt::Alignment labelAlignment;
+    double labelRotation;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The position is at (0, 0) with a length of 100.
+  The orientation is QwtAbstractScaleDraw::Bottom.
+*/
+QwtScaleDraw::QwtScaleDraw()
+{
+    d_data = new QwtScaleDraw::PrivateData;
+    setLength( 100 );
+}
+
+//! Destructor
+QwtScaleDraw::~QwtScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+   Return alignment of the scale
+   \sa setAlignment()
+   \return Alignment of the scale
+*/
+QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+   Set the alignment of the scale
+
+   \param align Alignment of the scale
+
+   The default alignment is QwtScaleDraw::BottomScale
+   \sa alignment()
+*/
+void QwtScaleDraw::setAlignment( Alignment align )
+{
+    d_data->alignment = align;
+}
+
+/*!
+  Return the orientation
+
+  TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
+  LeftScale, RightScale are vertical (Qt::Vertical) scales.
+
+  \return Orientation of the scale
+
+  \sa alignment()
+*/
+Qt::Orientation QwtScaleDraw::orientation() const
+{
+    switch ( d_data->alignment )
+    {
+        case TopScale:
+        case BottomScale:
+            return Qt::Horizontal;
+        case LeftScale:
+        case RightScale:
+        default:
+            return Qt::Vertical;
+    }
+}
+
+/*!
+  \brief Determine the minimum border distance
+
+  This member function returns the minimum space
+  needed to draw the mark labels at the scale's endpoints.
+
+  \param font Font
+  \param start Start border distance
+  \param end End border distance
+*/
+void QwtScaleDraw::getBorderDistHint( 
+    const QFont &font, int &start, int &end ) const
+{
+    start = 0;
+    end = 0;
+
+    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
+        return;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    if ( ticks.count() == 0 )
+        return;
+
+    // Find the ticks, that are mapped to the borders.
+    // minTick is the tick, that is mapped to the top/left-most position
+    // in widget coordinates.
+
+    double minTick = ticks[0];
+    double minPos = scaleMap().transform( minTick );
+    double maxTick = minTick;
+    double maxPos = minPos;
+
+    for ( int i = 1; i < ticks.count(); i++ )
+    {
+        const double tickPos = scaleMap().transform( ticks[i] );
+        if ( tickPos < minPos )
+        {
+            minTick = ticks[i];
+            minPos = tickPos;
+        }
+        if ( tickPos > scaleMap().transform( maxTick ) )
+        {
+            maxTick = ticks[i];
+            maxPos = tickPos;
+        }
+    }
+
+    double e = 0.0;
+    double s = 0.0;
+    if ( orientation() == Qt::Vertical )
+    {
+        s = -labelRect( font, minTick ).top();
+        s -= qAbs( minPos - qRound( scaleMap().p2() ) );
+
+        e = labelRect( font, maxTick ).bottom();
+        e -= qAbs( maxPos - scaleMap().p1() );
+    }
+    else
+    {
+        s = -labelRect( font, minTick ).left();
+        s -= qAbs( minPos - scaleMap().p1() );
+
+        e = labelRect( font, maxTick ).right();
+        e -= qAbs( maxPos - scaleMap().p2() );
+    }
+
+    if ( s < 0.0 )
+        s = 0.0;
+    if ( e < 0.0 )
+        e = 0.0;
+
+    start = qCeil( s );
+    end = qCeil( e );
+}
+
+/*!
+  Determine the minimum distance between two labels, that is necessary
+  that the texts don't overlap.
+
+  \param font Font
+  \return The maximum width of a label
+
+  \sa getBorderDistHint()
+*/
+
+int QwtScaleDraw::minLabelDist( const QFont &font ) const
+{
+    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
+        return 0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    if ( ticks.isEmpty() )
+        return 0;
+
+    const QFontMetrics fm( font );
+
+    const bool vertical = ( orientation() == Qt::Vertical );
+
+    QRectF bRect1;
+    QRectF bRect2 = labelRect( font, ticks[0] );
+    if ( vertical )
+    {
+        bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
+    }
+
+    double maxDist = 0.0;
+
+    for ( int i = 1; i < ticks.count(); i++ )
+    {
+        bRect1 = bRect2;
+        bRect2 = labelRect( font, ticks[i] );
+        if ( vertical )
+        {
+            bRect2.setRect( -bRect2.bottom(), 0.0,
+                bRect2.height(), bRect2.width() );
+        }
+
+        double dist = fm.leading(); // space between the labels
+        if ( bRect1.right() > 0 )
+            dist += bRect1.right();
+        if ( bRect2.left() < 0 )
+            dist += -bRect2.left();
+
+        if ( dist > maxDist )
+            maxDist = dist;
+    }
+
+    double angle = qwtRadians( labelRotation() ); 
+    if ( vertical )
+        angle += M_PI / 2;
+
+    const double sinA = qFastSin( angle ); // qreal -> double
+    if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
+        return qCeil( maxDist );
+
+    const int fmHeight = fm.ascent() - 2;
+
+    // The distance we need until there is
+    // the height of the label font. This height is needed
+    // for the neighbored label.
+
+    double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
+    if ( labelDist < 0 )
+        labelDist = -labelDist;
+
+    // For text orientations close to the scale orientation
+
+    if ( labelDist > maxDist )
+        labelDist = maxDist;
+
+    // For text orientations close to the opposite of the
+    // scale orientation
+
+    if ( labelDist < fmHeight )
+        labelDist = fmHeight;
+
+    return qCeil( labelDist );
+}
+
+/*!
+   Calculate the width/height that is needed for a
+   vertical/horizontal scale.
+
+   The extent is calculated from the pen width of the backbone,
+   the major tick length, the spacing and the maximum width/height
+   of the labels.
+
+   \param font Font used for painting the labels
+   \return Extent
+
+   \sa minLength()
+*/
+double QwtScaleDraw::extent( const QFont &font ) const
+{
+    double d = 0;
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        if ( orientation() == Qt::Vertical )
+            d = maxLabelWidth( font );
+        else
+            d = maxLabelHeight( font );
+
+        if ( d > 0 )
+            d += spacing();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        d += maxTickLength();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // pen width can be zero
+        d += pw;
+    }
+
+    d = qMax( d, minimumExtent() );
+    return d;
+}
+
+/*!
+   Calculate the minimum length that is needed to draw the scale
+
+   \param font Font used for painting the labels
+   \return Minimum length that is needed to draw the scale
+
+   \sa extent()
+*/
+int QwtScaleDraw::minLength( const QFont &font ) const
+{
+    int startDist, endDist;
+    getBorderDistHint( font, startDist, endDist );
+
+    const QwtScaleDiv &sd = scaleDiv();
+
+    const uint minorCount =
+        sd.ticks( QwtScaleDiv::MinorTick ).count() +
+        sd.ticks( QwtScaleDiv::MediumTick ).count();
+    const uint majorCount =
+        sd.ticks( QwtScaleDiv::MajorTick ).count();
+
+    int lengthForLabels = 0;
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+        lengthForLabels = minLabelDist( font ) * majorCount;
+
+    int lengthForTicks = 0;
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // penwidth can be zero
+        lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
+    }
+
+    return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
+}
+
+/*!
+   Find the position, where to paint a label
+
+   The position has a distance that depends on the length of the ticks 
+   in direction of the alignment().
+
+   \param value Value
+   \return Position, where to paint a label
+*/
+QPointF QwtScaleDraw::labelPosition( double value ) const
+{
+    const double tval = scaleMap().transform( value );
+    double dist = spacing();
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+        dist += qMax( 1, penWidth() );
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+        dist += tickLength( QwtScaleDiv::MajorTick );
+
+    double px = 0;
+    double py = 0;
+
+    switch ( alignment() )
+    {
+        case RightScale:
+        {
+            px = d_data->pos.x() + dist;
+            py = tval;
+            break;
+        }
+        case LeftScale:
+        {
+            px = d_data->pos.x() - dist;
+            py = tval;
+            break;
+        }
+        case BottomScale:
+        {
+            px = tval;
+            py = d_data->pos.y() + dist;
+            break;
+        }
+        case TopScale:
+        {
+            px = tval;
+            py = d_data->pos.y() - dist;
+            break;
+        }
+    }
+
+    return QPointF( px, py );
+}
+
+/*!
+   Draw a tick
+
+   \param painter Painter
+   \param value Value of the tick
+   \param len Length of the tick
+
+   \sa drawBackbone(), drawLabel()
+*/
+void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
+{
+    if ( len <= 0 )
+        return;
+
+    const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
+
+    QPointF pos = d_data->pos;
+
+    double tval = scaleMap().transform( value );
+    if ( roundingAlignment )
+        tval = qRound( tval );
+
+    const int pw = penWidth();
+    int a = 0;
+    if ( pw > 1 && roundingAlignment )
+        a = 1;
+
+    switch ( alignment() )
+    {
+        case LeftScale:
+        {
+            double x1 = pos.x() + a;
+            double x2 = pos.x() + a - pw - len;
+            if ( roundingAlignment )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            QwtPainter::drawLine( painter, x1, tval, x2, tval );
+            break;
+        }
+
+        case RightScale:
+        {
+            double x1 = pos.x();
+            double x2 = pos.x() + pw + len;
+            if ( roundingAlignment )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            QwtPainter::drawLine( painter, x1, tval, x2, tval );
+            break;
+        }
+
+        case BottomScale:
+        {
+            double y1 = pos.y();
+            double y2 = pos.y() + pw + len;
+            if ( roundingAlignment )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
+            break;
+        }
+
+        case TopScale:
+        {
+            double y1 = pos.y() + a;
+            double y2 = pos.y() - pw - len + a;
+            if ( roundingAlignment )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
+            break;
+        }
+    }
+}
+
+/*!
+   Draws the baseline of the scale
+   \param painter Painter
+
+   \sa drawTick(), drawLabel()
+*/
+void QwtScaleDraw::drawBackbone( QPainter *painter ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    const QPointF &pos = d_data->pos;
+    const double len = d_data->len;
+    const int pw = qMax( penWidth(), 1 );
+
+    // pos indicates a border not the center of the backbone line
+    // so we need to shift its position depending on the pen width
+    // and the alignment of the scale
+
+    double off;
+    if ( doAlign )
+    {
+        if ( alignment() == LeftScale || alignment() == TopScale )
+            off = ( pw - 1 ) / 2;
+        else
+            off = pw / 2;
+    }
+    else
+    {
+        off = 0.5 * penWidth();
+    }
+
+    switch ( alignment() )
+    {
+        case LeftScale:
+        {
+            double x = pos.x() - off;
+            if ( doAlign )
+                x = qRound( x );
+
+            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
+            break;
+        }
+        case RightScale:
+        {
+            double x = pos.x() + off;
+            if ( doAlign )
+                x = qRound( x );
+
+            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
+            break;
+        }
+        case TopScale:
+        {
+            double y = pos.y() - off;
+            if ( doAlign )
+                y = qRound( y );
+
+            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
+            break;
+        }
+        case BottomScale:
+        {
+            double y = pos.y() + off;
+            if ( doAlign )
+                y = qRound( y );
+
+            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
+            break;
+        }
+    }
+}
+
+/*!
+  \brief Move the position of the scale
+
+  The meaning of the parameter pos depends on the alignment:
+  <dl>
+  <dt>QwtScaleDraw::LeftScale
+  <dd>The origin is the topmost point of the
+      backbone. The backbone is a vertical line.
+      Scale marks and labels are drawn
+      at the left of the backbone.
+  <dt>QwtScaleDraw::RightScale
+  <dd>The origin is the topmost point of the
+      backbone. The backbone is a vertical line.
+      Scale marks and labels are drawn
+      at the right of the backbone.
+  <dt>QwtScaleDraw::TopScale
+  <dd>The origin is the leftmost point of the
+      backbone. The backbone is a horizontal line.
+      Scale marks and labels are drawn
+      above the backbone.
+  <dt>QwtScaleDraw::BottomScale
+  <dd>The origin is the leftmost point of the
+      backbone. The backbone is a horizontal line
+      Scale marks and labels are drawn
+      below the backbone.
+  </dl>
+
+  \param pos Origin of the scale
+
+  \sa pos(), setLength()
+*/
+void QwtScaleDraw::move( const QPointF &pos )
+{
+    d_data->pos = pos;
+    updateMap();
+}
+
+/*!
+   \return Origin of the scale
+   \sa move(), length()
+*/
+QPointF QwtScaleDraw::pos() const
+{
+    return d_data->pos;
+}
+
+/*!
+  Set the length of the backbone.
+
+  The length doesn't include the space needed for
+  overlapping labels.
+
+  \param length Length of the backbone
+
+  \sa move(), minLabelDist()
+*/
+void QwtScaleDraw::setLength( double length )
+{
+#if 1
+    if ( length >= 0 && length < 10 )
+        length = 10;
+
+    // why should we accept negative lengths ???
+    if ( length < 0 && length > -10 )
+        length = -10;
+#else
+    length = qMax( length, 10 );
+#endif
+
+    d_data->len = length;
+    updateMap();
+}
+
+/*!
+   \return the length of the backbone
+   \sa setLength(), pos()
+*/
+double QwtScaleDraw::length() const
+{
+    return d_data->len;
+}
+
+/*!
+   Draws the label for a major scale tick
+
+   \param painter Painter
+   \param value Value
+
+   \sa drawTick(), drawBackbone(), boundingLabelRect()
+*/
+void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
+{
+    QwtText lbl = tickLabel( painter->font(), value );
+    if ( lbl.isEmpty() )
+        return;
+
+    QPointF pos = labelPosition( value );
+
+    QSizeF labelSize = lbl.textSize( painter->font() );
+
+    const QTransform transform = labelTransformation( pos, labelSize );
+
+    painter->save();
+    painter->setWorldTransform( transform, true );
+
+    lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
+
+    painter->restore();
+}
+
+/*!
+  \brief Find the bounding rectangle for the label.
+
+  The coordinates of the rectangle are absolute ( calculated from pos() ).
+  in direction of the tick.
+
+  \param font Font used for painting
+  \param value Value
+
+  \return Bounding rectangle
+  \sa labelRect()
+*/
+QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
+{
+    QwtText lbl = tickLabel( font, value );
+    if ( lbl.isEmpty() )
+        return QRect();
+
+    const QPointF pos = labelPosition( value );
+    QSizeF labelSize = lbl.textSize( font );
+
+    const QTransform transform = labelTransformation( pos, labelSize );
+    return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
+}
+
+/*!
+   Calculate the transformation that is needed to paint a label
+   depending on its alignment and rotation.
+
+   \param pos Position where to paint the label
+   \param size Size of the label
+
+   \return Transformation matrix
+   \sa setLabelAlignment(), setLabelRotation()
+*/
+QTransform QwtScaleDraw::labelTransformation(
+    const QPointF &pos, const QSizeF &size ) const
+{
+    QTransform transform;
+    transform.translate( pos.x(), pos.y() );
+    transform.rotate( labelRotation() );
+
+    int flags = labelAlignment();
+    if ( flags == 0 )
+    {
+        switch ( alignment() )
+        {
+            case RightScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignRight | Qt::AlignVCenter;
+                break;
+            }
+            case LeftScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignLeft | Qt::AlignVCenter;
+                break;
+            }
+            case BottomScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignHCenter | Qt::AlignBottom;
+                break;
+            }
+            case TopScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignHCenter | Qt::AlignTop;
+                break;
+            }
+        }
+    }
+
+    double x, y;
+
+    if ( flags & Qt::AlignLeft )
+        x = -size.width();
+    else if ( flags & Qt::AlignRight )
+        x = 0.0;
+    else // Qt::AlignHCenter
+        x = -( 0.5 * size.width() );
+
+    if ( flags & Qt::AlignTop )
+        y = -size.height();
+    else if ( flags & Qt::AlignBottom )
+        y = 0;
+    else // Qt::AlignVCenter
+        y = -( 0.5 * size.height() );
+
+    transform.translate( x, y );
+
+    return transform;
+}
+
+/*!
+  Find the bounding rectangle for the label. The coordinates of
+  the rectangle are relative to spacing + tick length from the backbone
+  in direction of the tick.
+
+  \param font Font used for painting
+  \param value Value
+
+   \return Bounding rectangle that is needed to draw a label
+*/
+QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
+{
+    QwtText lbl = tickLabel( font, value );
+    if ( lbl.isEmpty() )
+        return QRectF( 0.0, 0.0, 0.0, 0.0 );
+
+    const QPointF pos = labelPosition( value );
+
+    const QSizeF labelSize = lbl.textSize( font );
+    const QTransform transform = labelTransformation( pos, labelSize );
+
+    QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
+    br.translate( -pos.x(), -pos.y() );
+
+    return br;
+}
+
+/*!
+   Calculate the size that is needed to draw a label
+
+   \param font Label font
+   \param value Value
+
+   \return Size that is needed to draw a label
+*/
+QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
+{
+    return labelRect( font, value ).size();
+}
+
+/*!
+  Rotate all labels.
+
+  When changing the rotation, it might be necessary to
+  adjust the label flags too. Finding a useful combination is
+  often the result of try and error.
+
+  \param rotation Angle in degrees. When changing the label rotation,
+                  the label flags often needs to be adjusted too.
+
+  \sa setLabelAlignment(), labelRotation(), labelAlignment().
+
+*/
+void QwtScaleDraw::setLabelRotation( double rotation )
+{
+    d_data->labelRotation = rotation;
+}
+
+/*!
+  \return the label rotation
+  \sa setLabelRotation(), labelAlignment()
+*/
+double QwtScaleDraw::labelRotation() const
+{
+    return d_data->labelRotation;
+}
+
+/*!
+  \brief Change the label flags
+
+  Labels are aligned to the point tick length + spacing away from the backbone.
+
+  The alignment is relative to the orientation of the label text.
+  In case of an flags of 0 the label will be aligned
+  depending on the orientation of the scale:
+
+      QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n
+      QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n
+      QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n
+      QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n
+
+  Changing the alignment is often necessary for rotated labels.
+
+  \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
+
+  \sa setLabelRotation(), labelRotation(), labelAlignment()
+  \warning The various alignments might be confusing.
+           The alignment of the label is not the alignment
+           of the scale and is not the alignment of the flags
+           ( QwtText::flags() ) returned from QwtAbstractScaleDraw::label().
+*/
+
+void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
+{
+    d_data->labelAlignment = alignment;
+}
+
+/*!
+  \return the label flags
+  \sa setLabelAlignment(), labelRotation()
+*/
+Qt::Alignment QwtScaleDraw::labelAlignment() const
+{
+    return d_data->labelAlignment;
+}
+
+/*!
+  \param font Font
+  \return the maximum width of a label
+*/
+int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
+{
+    double maxWidth = 0.0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        const double v = ticks[i];
+        if ( scaleDiv().contains( v ) )
+        {
+            const double w = labelSize( font, ticks[i] ).width();
+            if ( w > maxWidth )
+                maxWidth = w;
+        }
+    }
+
+    return qCeil( maxWidth );
+}
+
+/*!
+  \param font Font
+  \return the maximum height of a label
+*/
+int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
+{
+    double maxHeight = 0.0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        const double v = ticks[i];
+        if ( scaleDiv().contains( v ) )
+        {
+            const double h = labelSize( font, ticks[i] ).height();
+            if ( h > maxHeight )
+                maxHeight = h;
+        }
+    }
+
+    return qCeil( maxHeight );
+}
+
+void QwtScaleDraw::updateMap()
+{
+    const QPointF pos = d_data->pos;
+    double len = d_data->len;
+
+    QwtScaleMap &sm = scaleMap();
+    if ( orientation() == Qt::Vertical )
+        sm.setPaintInterval( pos.y() + len, pos.y() );
+    else
+        sm.setPaintInterval( pos.x(), pos.x() + len );
+}
diff --git a/qwt/qwt_scale_draw.h b/qwt/qwt_scale_draw.h
new file mode 100644
index 0000000..005e834
--- /dev/null
+++ b/qwt/qwt_scale_draw.h
@@ -0,0 +1,120 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_DRAW_H
+#define QWT_SCALE_DRAW_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_scale_draw.h"
+#include <qpoint.h>
+#include <qrect.h>
+#include <qtransform.h>
+
+/*!
+  \brief A class for drawing scales
+
+  QwtScaleDraw can be used to draw linear or logarithmic scales.
+  A scale has a position, an alignment and a length, which can be specified .
+  The labels can be rotated and aligned
+  to the ticks using setLabelRotation() and setLabelAlignment().
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s),
+  the scale can be drawn with the QwtAbstractScaleDraw::draw() member.
+*/
+class QWT_EXPORT QwtScaleDraw: public QwtAbstractScaleDraw
+{
+public:
+    /*!
+        Alignment of the scale draw
+        \sa setAlignment(), alignment()
+     */
+    enum Alignment 
+    { 
+        //! The scale is below
+        BottomScale, 
+
+        //! The scale is above
+        TopScale, 
+
+        //! The scale is left
+        LeftScale, 
+
+        //! The scale is right
+        RightScale 
+    };
+
+    QwtScaleDraw();
+    virtual ~QwtScaleDraw();
+
+    void getBorderDistHint( const QFont &, int &start, int &end ) const;
+    int minLabelDist( const QFont & ) const;
+
+    int minLength( const QFont & ) const;
+    virtual double extent( const QFont & ) const;
+
+    void move( double x, double y );
+    void move( const QPointF & );
+    void setLength( double length );
+
+    Alignment alignment() const;
+    void setAlignment( Alignment );
+
+    Qt::Orientation orientation() const;
+
+    QPointF pos() const;
+    double length() const;
+
+    void setLabelAlignment( Qt::Alignment );
+    Qt::Alignment labelAlignment() const;
+
+    void setLabelRotation( double rotation );
+    double labelRotation() const;
+
+    int maxLabelHeight( const QFont & ) const;
+    int maxLabelWidth( const QFont & ) const;
+
+    QPointF labelPosition( double val ) const;
+
+    QRectF labelRect( const QFont &, double val ) const;
+    QSizeF labelSize( const QFont &, double val ) const;
+
+    QRect boundingLabelRect( const QFont &, double val ) const;
+
+protected:
+    QTransform labelTransformation( const QPointF &, const QSizeF & ) const;
+
+    virtual void drawTick( QPainter *, double val, double len ) const;
+    virtual void drawBackbone( QPainter * ) const;
+    virtual void drawLabel( QPainter *, double val ) const;
+
+private:
+    QwtScaleDraw( const QwtScaleDraw & );
+    QwtScaleDraw &operator=( const QwtScaleDraw &other );
+
+    void updateMap();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+   Move the position of the scale
+
+   \param x X coordinate
+   \param y Y coordinate
+
+   \sa move(const QPointF &)
+*/
+inline void QwtScaleDraw::move( double x, double y )
+{
+    move( QPointF( x, y ) );
+}
+
+#endif
diff --git a/qwt/qwt_scale_engine.cpp b/qwt/qwt_scale_engine.cpp
new file mode 100644
index 0000000..686c9d6
--- /dev/null
+++ b/qwt/qwt_scale_engine.cpp
@@ -0,0 +1,1118 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_scale_engine.h"
+#include "qwt_math.h"
+#include "qwt_scale_map.h"
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <float.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#define qExp(x) ::exp(x)
+#endif
+
+static inline double qwtLog( double base, double value )
+{
+    return log( value ) / log( base );
+}
+
+static inline QwtInterval qwtLogInterval( double base, const QwtInterval &interval )
+{
+    return QwtInterval( qwtLog( base, interval.minValue() ),
+            qwtLog( base, interval.maxValue() ) );
+}
+
+static inline QwtInterval qwtPowInterval( double base, const QwtInterval &interval ) 
+{
+    return QwtInterval( qPow( base, interval.minValue() ),
+            qPow( base, interval.maxValue() ) );
+}
+
+
+#if 1
+
+// this version often doesn't find the best ticks: f.e for 15: 5, 10
+static double qwtStepSize( double intervalSize, int maxSteps, uint base )
+{
+    const double minStep = 
+        QwtScaleArithmetic::divideInterval( intervalSize, maxSteps, base );
+
+    if ( minStep != 0.0 )
+    {
+        // # ticks per interval
+        const int numTicks = qCeil( qAbs( intervalSize / minStep ) ) - 1;
+
+        // Do the minor steps fit into the interval?
+        if ( qwtFuzzyCompare( ( numTicks +  1 ) * qAbs( minStep ),
+            qAbs( intervalSize ), intervalSize ) > 0 )
+        {
+            // The minor steps doesn't fit into the interval
+            return 0.5 * intervalSize;
+        }
+    }
+
+    return minStep;
+}
+
+#else
+
+static double qwtStepSize( double intervalSize, int maxSteps, uint base )
+{
+    if ( maxSteps <= 0 )
+        return 0.0;
+
+    if ( maxSteps > 2 )
+    {
+        for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
+        {
+            const double stepSize = intervalSize / numSteps;
+
+            const double p = ::floor( ::log( stepSize ) / ::log( base ) );
+            const double fraction = qPow( base, p );
+
+            for ( uint n = base; n > 1; n /= 2 )
+            {
+                if ( qFuzzyCompare( stepSize, n * fraction ) )
+                    return stepSize;
+
+                if ( n == 3 && ( base % 2 ) == 0 )
+                {
+                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
+                        return stepSize;
+                }
+            }
+        }
+    }
+
+    return intervalSize * 0.5;
+}
+
+#endif
+
+static const double _eps = 1.0e-6;
+
+/*!
+  Ceil a value, relative to an interval
+
+  \param value Value to be ceiled
+  \param intervalSize Interval size
+
+  \return Rounded value
+
+  \sa floorEps()
+*/
+double QwtScaleArithmetic::ceilEps( double value,
+    double intervalSize )
+{
+    const double eps = _eps * intervalSize;
+
+    value = ( value - eps ) / intervalSize;
+    return ::ceil( value ) * intervalSize;
+}
+
+/*!
+  Floor a value, relative to an interval
+
+  \param value Value to be floored
+  \param intervalSize Interval size
+
+  \return Rounded value
+  \sa floorEps()
+*/
+double QwtScaleArithmetic::floorEps( double value, double intervalSize )
+{
+    const double eps = _eps * intervalSize;
+
+    value = ( value + eps ) / intervalSize;
+    return ::floor( value ) * intervalSize;
+}
+
+/*!
+  \brief Divide an interval into steps
+
+  \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+  \return Step size
+*/
+double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
+{
+    if ( numSteps == 0.0 || intervalSize == 0.0 )
+        return 0.0;
+
+    return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
+}
+
+/*!
+  Calculate a step size for a given interval
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+  \param base Base for the division ( usually 10 )
+
+  \return Calculated step size
+ */
+double QwtScaleArithmetic::divideInterval( 
+    double intervalSize, int numSteps, uint base ) 
+{
+    if ( numSteps <= 0 )
+        return 0.0;
+
+    const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
+    if ( v == 0.0 )
+        return 0.0;
+
+    const double lx = qwtLog( base, qFabs( v ) );
+    const double p = ::floor( lx );
+
+    const double fraction = qPow( base, lx - p );
+
+    uint n = base;
+    while ( ( n > 1 ) && ( fraction <= n / 2 ) )
+        n /= 2;
+
+    double stepSize = n * qPow( base, p );
+    if ( v < 0 )
+        stepSize = -stepSize;
+
+    return stepSize;
+}
+
+class QwtScaleEngine::PrivateData
+{
+public:
+    PrivateData():
+        attributes( QwtScaleEngine::NoAttribute ),
+        lowerMargin( 0.0 ),
+        upperMargin( 0.0 ),
+        referenceValue( 0.0 ),
+        base( 10 ),
+        transform( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete transform;
+    }
+
+    QwtScaleEngine::Attributes attributes;
+
+    double lowerMargin;
+    double upperMargin;
+
+    double referenceValue;
+
+    uint base;
+
+    QwtTransform* transform;
+};
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtScaleEngine::QwtScaleEngine( uint base )
+{
+    d_data = new PrivateData;
+    setBase( base );
+}
+
+
+//! Destructor
+QwtScaleEngine::~QwtScaleEngine ()
+{
+    delete d_data;
+}
+
+/*!
+   Assign a transformation
+
+   \param transform Transformation
+
+   The transformation object is used as factory for clones
+   that are returned by transformation()
+
+   The scale engine takes ownership of the transformation.
+
+   \sa QwtTransform::copy(), transformation()
+
+ */
+void QwtScaleEngine::setTransformation( QwtTransform *transform )
+{
+    if ( transform != d_data->transform )
+    {
+        delete d_data->transform;
+        d_data->transform = transform;
+    }
+}
+
+/*!
+   Create and return a clone of the transformation 
+   of the engine. When the engine has no special transformation
+   NULL is returned, indicating no transformation.
+
+   \return A clone of the transfomation
+   \sa setTransformation()
+ */
+QwtTransform *QwtScaleEngine::transformation() const
+{
+    QwtTransform *transform = NULL;
+    if ( d_data->transform )
+        transform = d_data->transform->copy();
+
+    return transform;
+}
+
+/*!
+    \return the margin at the lower end of the scale
+    The default margin is 0.
+
+    \sa setMargins()
+*/
+double QwtScaleEngine::lowerMargin() const
+{
+    return d_data->lowerMargin;
+}
+
+/*!
+    \return the margin at the upper end of the scale
+    The default margin is 0.
+
+    \sa setMargins()
+*/
+double QwtScaleEngine::upperMargin() const
+{
+    return d_data->upperMargin;
+}
+
+/*!
+  \brief Specify margins at the scale's endpoints
+  \param lower minimum distance between the scale's lower boundary and the
+             smallest enclosed value
+  \param upper minimum distance between the scale's upper boundary and the
+             greatest enclosed value
+
+  Margins can be used to leave a minimum amount of space between
+  the enclosed intervals and the boundaries of the scale.
+
+  \warning
+  \li QwtLogScaleEngine measures the margins in decades.
+
+  \sa upperMargin(), lowerMargin()
+*/
+
+void QwtScaleEngine::setMargins( double lower, double upper )
+{
+    d_data->lowerMargin = qMax( lower, 0.0 );
+    d_data->upperMargin = qMax( upper, 0.0 );
+}
+
+/*!
+  Calculate a step size for an interval size
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+
+  \return Step size
+*/
+double QwtScaleEngine::divideInterval(
+    double intervalSize, int numSteps ) const
+{
+    return QwtScaleArithmetic::divideInterval( 
+        intervalSize, numSteps, d_data->base );
+}
+
+/*!
+  Check if an interval "contains" a value
+
+  \param interval Interval
+  \param value Value
+
+  \return True, when the value is inside the interval
+*/
+bool QwtScaleEngine::contains(
+    const QwtInterval &interval, double value ) const
+{
+    if ( !interval.isValid() )
+        return false;
+
+    if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
+        return false;
+
+    if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
+        return false;
+
+    return true;
+}
+
+/*!
+  Remove ticks from a list, that are not inside an interval
+
+  \param ticks Tick list
+  \param interval Interval
+
+  \return Stripped tick list
+*/
+QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
+    const QwtInterval &interval ) const
+{
+    if ( !interval.isValid() || ticks.count() == 0 )
+        return QList<double>();
+
+    if ( contains( interval, ticks.first() )
+        && contains( interval, ticks.last() ) )
+    {
+        return ticks;
+    }
+
+    QList<double> strippedTicks;
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        if ( contains( interval, ticks[i] ) )
+            strippedTicks += ticks[i];
+    }
+    return strippedTicks;
+}
+
+/*!
+  \brief Build an interval around a value
+
+  In case of v == 0.0 the interval is [-0.5, 0.5],
+  otherwide it is [0.5 * v, 1.5 * v]
+
+  \param value Initial value
+  \return Calculated interval
+*/
+
+QwtInterval QwtScaleEngine::buildInterval( double value ) const
+{
+    const double delta = ( value == 0.0 ) ? 0.5 : qAbs( 0.5 * value );
+
+    if ( DBL_MAX - delta < value )
+        return QwtInterval( DBL_MAX - delta, DBL_MAX );
+
+    if ( -DBL_MAX + delta > value )
+        return QwtInterval( -DBL_MAX, -DBL_MAX + delta );
+
+    return QwtInterval( value - delta, value + delta );
+}
+
+/*!
+  Change a scale attribute
+
+  \param attribute Attribute to change
+  \param on On/Off
+
+  \sa Attribute, testAttribute()
+*/
+void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
+{
+    if ( on )
+        d_data->attributes |= attribute;
+    else
+        d_data->attributes &= ~attribute;
+}
+
+/*!
+  \return True, if attribute is enabled.
+
+  \param attribute Attribute to be tested
+  \sa Attribute, setAttribute()
+*/
+bool QwtScaleEngine::testAttribute( Attribute attribute ) const
+{
+    return ( d_data->attributes & attribute );
+}
+
+/*!
+  Change the scale attribute
+
+  \param attributes Set scale attributes
+  \sa Attribute, attributes()
+*/
+void QwtScaleEngine::setAttributes( Attributes attributes )
+{
+    d_data->attributes = attributes;
+}
+
+/*!
+  \return Scale attributes
+  \sa Attribute, setAttributes(), testAttribute()
+*/
+QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
+{
+    return d_data->attributes;
+}
+
+/*!
+  \brief Specify a reference point
+  \param r new reference value
+
+  The reference point is needed if options IncludeReference or
+  Symmetric are active. Its default value is 0.0.
+
+  \sa Attribute
+*/
+void QwtScaleEngine::setReference( double r )
+{
+    d_data->referenceValue = r;
+}
+
+/*!
+  \return the reference value
+  \sa setReference(), setAttribute()
+*/
+double QwtScaleEngine::reference() const
+{
+    return d_data->referenceValue;
+}
+
+/*!
+  Set the base of the scale engine
+
+  While a base of 10 is what 99.9% of all applications need
+  certain scales might need a different base: f.e 2
+
+  The default setting is 10
+
+  \param base Base of the engine
+
+  \sa base()
+ */
+void QwtScaleEngine::setBase( uint base )
+{ 
+    d_data->base = qMax( base, 2U );
+}
+
+/*!
+  \return base Base of the scale engine
+  \sa setBase()
+ */
+uint QwtScaleEngine::base() const
+{
+    return d_data->base;
+}
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
+    QwtScaleEngine( base )
+{
+}
+
+//! Destructor
+QwtLinearScaleEngine::~QwtLinearScaleEngine()
+{
+}
+
+/*!
+  Align and divide an interval
+
+  \param maxNumSteps Max. number of steps
+  \param x1 First limit of the interval (In/Out)
+  \param x2 Second limit of the interval (In/Out)
+  \param stepSize Step size (Out)
+
+  \sa setAttribute()
+*/
+void QwtLinearScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    QwtInterval interval( x1, x2 );
+    interval = interval.normalized();
+
+    interval.setMinValue( interval.minValue() - lowerMargin() );
+    interval.setMaxValue( interval.maxValue() + upperMargin() );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+        interval = interval.symmetrize( reference() );
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( reference() );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    stepSize = QwtScaleArithmetic::divideInterval( 
+        interval.width(), qMax( maxNumSteps, 1 ), base() );
+
+    if ( !testAttribute( QwtScaleEngine::Floating ) )
+        interval = align( interval, stepSize );
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for an interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the engine
+                   calculates one.
+
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
+    if ( interval.width() <= 0 )
+        return QwtScaleDiv();
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize == 0.0 )
+    {
+        if ( maxMajorSteps < 1 )
+            maxMajorSteps = 1;
+
+        stepSize = QwtScaleArithmetic::divideInterval( 
+            interval.width(), maxMajorSteps, base() );
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    if ( stepSize != 0.0 )
+    {
+        QList<double> ticks[QwtScaleDiv::NTickTypes];
+        buildTicks( interval, stepSize, maxMinorSteps, ticks );
+
+        scaleDiv = QwtScaleDiv( interval, ticks );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+/*!
+   \brief Calculate ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+   \param maxMinorSteps Maximum number of minor steps
+   \param ticks Arrays to be filled with the calculated ticks
+
+   \sa buildMajorTicks(), buildMinorTicks
+*/
+void QwtLinearScaleEngine::buildTicks(
+    const QwtInterval& interval, double stepSize, int maxMinorSteps,
+    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
+{
+    const QwtInterval boundingInterval = align( interval, stepSize );
+
+    ticks[QwtScaleDiv::MajorTick] =
+        buildMajorTicks( boundingInterval, stepSize );
+
+    if ( maxMinorSteps > 0 )
+    {
+        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
+            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
+    }
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+    {
+        ticks[i] = strip( ticks[i], interval );
+
+        // ticks very close to 0.0 are
+        // explicitely set to 0.0
+
+        for ( int j = 0; j < ticks[i].count(); j++ )
+        {
+            if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
+                ticks[i][j] = 0.0;
+        }
+    }
+}
+
+/*!
+   \brief Calculate major ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+
+   \return Calculated ticks
+*/
+QList<double> QwtLinearScaleEngine::buildMajorTicks(
+    const QwtInterval &interval, double stepSize ) const
+{
+    int numTicks = qRound( interval.width() / stepSize ) + 1;
+    if ( numTicks > 10000 )
+        numTicks = 10000;
+
+    QList<double> ticks;
+
+    ticks += interval.minValue();
+    for ( int i = 1; i < numTicks - 1; i++ )
+        ticks += interval.minValue() + i * stepSize;
+    ticks += interval.maxValue();
+
+    return ticks;
+}
+
+/*!
+   \brief Calculate minor/medium ticks for major ticks
+
+   \param majorTicks Major ticks
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param minorTicks Array to be filled with the calculated minor ticks
+   \param mediumTicks Array to be filled with the calculated medium ticks
+
+*/
+void QwtLinearScaleEngine::buildMinorTicks(
+    const QList<double>& majorTicks,
+    int maxMinorSteps, double stepSize,
+    QList<double> &minorTicks,
+    QList<double> &mediumTicks ) const
+{
+    double minStep = qwtStepSize( stepSize, maxMinorSteps, base() );
+    if ( minStep == 0.0 )
+        return;
+
+    // # ticks per interval
+    const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
+
+    int medIndex = -1;
+    if ( numTicks % 2 )
+        medIndex = numTicks / 2;
+
+    // calculate minor ticks
+
+    for ( int i = 0; i < majorTicks.count(); i++ )
+    {
+        double val = majorTicks[i];
+        for ( int k = 0; k < numTicks; k++ )
+        {
+            val += minStep;
+
+            double alignedValue = val;
+            if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
+                alignedValue = 0.0;
+
+            if ( k == medIndex )
+                mediumTicks += alignedValue;
+            else
+                minorTicks += alignedValue;
+        }
+    }
+}
+
+/*!
+  \brief Align an interval to a step size
+
+  The limits of an interval are aligned that both are integer
+  multiples of the step size.
+
+  \param interval Interval
+  \param stepSize Step size
+
+  \return Aligned interval
+*/
+QwtInterval QwtLinearScaleEngine::align(
+    const QwtInterval &interval, double stepSize ) const
+{
+    double x1 = interval.minValue();
+    double x2 = interval.maxValue();
+
+    if ( -DBL_MAX + stepSize <= x1 )
+    {
+        const double x = QwtScaleArithmetic::floorEps( x1, stepSize );
+        if ( qwtFuzzyCompare( x1, x, stepSize ) != 0 )
+            x1 = x;
+    }
+
+    if ( DBL_MAX - stepSize >= x2 )
+    {
+        const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
+        if ( qwtFuzzyCompare( x2, x, stepSize ) != 0 )
+            x2 = x;
+    }
+
+    return QwtInterval( x1, x2 );
+}
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
+    QwtScaleEngine( base )
+{
+    setTransformation( new QwtLogTransform() );
+}
+
+//! Destructor
+QwtLogScaleEngine::~QwtLogScaleEngine()
+{
+}
+
+/*!
+    Align and divide an interval
+
+   \param maxNumSteps Max. number of steps
+   \param x1 First limit of the interval (In/Out)
+   \param x2 Second limit of the interval (In/Out)
+   \param stepSize Step size (Out)
+
+   \sa QwtScaleEngine::setAttribute()
+*/
+void QwtLogScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    if ( x1 > x2 )
+        qSwap( x1, x2 );
+
+    const double logBase = base();
+
+    QwtInterval interval( x1 / qPow( logBase, lowerMargin() ),
+        x2 * qPow( logBase, upperMargin() ) );
+
+    if ( interval.maxValue() / interval.minValue() < logBase )
+    {
+        // scale width is less than one step -> try to build a linear scale
+
+        QwtLinearScaleEngine linearScaler;
+        linearScaler.setAttributes( attributes() );
+        linearScaler.setReference( reference() );
+        linearScaler.setMargins( lowerMargin(), upperMargin() );
+
+        linearScaler.autoScale( maxNumSteps, x1, x2, stepSize );
+
+        QwtInterval linearInterval = QwtInterval( x1, x2 ).normalized();
+        linearInterval = linearInterval.limited( LOG_MIN, LOG_MAX );
+
+        if ( linearInterval.maxValue() / linearInterval.minValue() < logBase )
+        {
+            // the aligned scale is still less than one step
+            if ( stepSize < 0.0 )
+                stepSize = -qwtLog( logBase, qAbs( stepSize ) );
+            else
+                stepSize = qwtLog( logBase, stepSize );
+
+            return;
+        }
+    }
+
+    double logRef = 1.0;
+    if ( reference() > LOG_MIN / 2 )
+        logRef = qMin( reference(), LOG_MAX / 2 );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+    {
+        const double delta = qMax( interval.maxValue() / logRef,
+            logRef / interval.minValue() );
+        interval.setInterval( logRef / delta, logRef * delta );
+    }
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( logRef );
+
+    interval = interval.limited( LOG_MIN, LOG_MAX );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(), 
+        qMax( maxNumSteps, 1 ) );
+    if ( stepSize < 1.0 )
+        stepSize = 1.0;
+
+    if ( !testAttribute( QwtScaleEngine::Floating ) )
+        interval = align( interval, stepSize );
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for an interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the engine
+                   calculates one.
+
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
+    interval = interval.limited( LOG_MIN, LOG_MAX );
+
+    if ( interval.width() <= 0 )
+        return QwtScaleDiv();
+
+    const double logBase = base();
+
+    if ( interval.maxValue() / interval.minValue() < logBase )
+    {
+        // scale width is less than one decade -> build linear scale
+
+        QwtLinearScaleEngine linearScaler;
+        linearScaler.setAttributes( attributes() );
+        linearScaler.setReference( reference() );
+        linearScaler.setMargins( lowerMargin(), upperMargin() );
+
+        if ( stepSize != 0.0 )
+        {
+            if ( stepSize < 0.0 )
+                stepSize = -qPow( logBase, -stepSize );
+            else
+                stepSize = qPow( logBase, stepSize );
+        }
+
+        return linearScaler.divideScale( x1, x2,
+            maxMajorSteps, maxMinorSteps, stepSize );
+    }
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize == 0.0 )
+    {
+        if ( maxMajorSteps < 1 )
+            maxMajorSteps = 1;
+
+        stepSize = divideInterval( 
+            qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
+        if ( stepSize < 1.0 )
+            stepSize = 1.0; // major step must be >= 1 decade
+    }
+
+    QwtScaleDiv scaleDiv;
+    if ( stepSize != 0.0 )
+    {
+        QList<double> ticks[QwtScaleDiv::NTickTypes];
+        buildTicks( interval, stepSize, maxMinorSteps, ticks );
+
+        scaleDiv = QwtScaleDiv( interval, ticks );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+/*!
+   \brief Calculate ticks for an interval
+
+   \param interval Interval
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param ticks Arrays to be filled with the calculated ticks
+
+   \sa buildMajorTicks(), buildMinorTicks
+*/
+void QwtLogScaleEngine::buildTicks(
+    const QwtInterval& interval, double stepSize, int maxMinorSteps,
+    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
+{
+    const QwtInterval boundingInterval = align( interval, stepSize );
+
+    ticks[QwtScaleDiv::MajorTick] =
+        buildMajorTicks( boundingInterval, stepSize );
+
+    if ( maxMinorSteps > 0 )
+    {
+        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
+            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
+    }
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+        ticks[i] = strip( ticks[i], interval );
+}
+
+/*!
+   \brief Calculate major ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+
+   \return Calculated ticks
+*/
+QList<double> QwtLogScaleEngine::buildMajorTicks(
+    const QwtInterval &interval, double stepSize ) const
+{
+    double width = qwtLogInterval( base(), interval ).width();
+
+    int numTicks = qRound( width / stepSize ) + 1;
+    if ( numTicks > 10000 )
+        numTicks = 10000;
+
+    const double lxmin = ::log( interval.minValue() );
+    const double lxmax = ::log( interval.maxValue() );
+    const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
+
+    QList<double> ticks;
+
+    ticks += interval.minValue();
+
+    for ( int i = 1; i < numTicks - 1; i++ )
+        ticks += qExp( lxmin + double( i ) * lstep );
+
+    ticks += interval.maxValue();
+
+    return ticks;
+}
+
+/*!
+   \brief Calculate minor/medium ticks for major ticks
+
+   \param majorTicks Major ticks
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param minorTicks Array to be filled with the calculated minor ticks
+   \param mediumTicks Array to be filled with the calculated medium ticks
+*/
+void QwtLogScaleEngine::buildMinorTicks(
+    const QList<double> &majorTicks,
+    int maxMinorSteps, double stepSize,
+    QList<double> &minorTicks,
+    QList<double> &mediumTicks ) const
+{
+    const double logBase = base();
+
+    if ( stepSize < 1.1 )          // major step width is one base
+    {
+        double minStep = divideInterval( stepSize, maxMinorSteps + 1 );
+        if ( minStep == 0.0 )
+            return;
+        
+        const int numSteps = qRound( stepSize / minStep ); 
+
+        int mediumTickIndex = -1;
+        if ( ( numSteps > 2 ) && ( numSteps % 2 == 0 ) )
+            mediumTickIndex = numSteps / 2;
+
+        for ( int i = 0; i < majorTicks.count() - 1; i++ )
+        {
+            const double v = majorTicks[i];
+            const double s = logBase / numSteps;
+
+            if ( s >= 1.0 )
+            {
+                for ( int j = 2; j < numSteps; j++ )
+                {
+                    minorTicks += v * j * s;
+                }
+            }
+            else
+            {
+                for ( int j = 1; j < numSteps; j++ )
+                {
+                    const double tick = v + j * v * ( logBase - 1 ) / numSteps;
+                    if ( j == mediumTickIndex )
+                        mediumTicks += tick;
+                    else
+                        minorTicks += tick;
+                }
+            }
+        }
+    }
+    else
+    {
+        double minStep = divideInterval( stepSize, maxMinorSteps );
+        if ( minStep == 0.0 )
+            return;
+
+        if ( minStep < 1.0 )
+            minStep = 1.0;
+
+        // # subticks per interval
+        int numTicks = qRound( stepSize / minStep ) - 1;
+
+        // Do the minor steps fit into the interval?
+        if ( qwtFuzzyCompare( ( numTicks +  1 ) * minStep,
+            stepSize, stepSize ) > 0 )
+        {
+            numTicks = 0;
+        }
+
+        if ( numTicks < 1 )
+            return; 
+
+        int mediumTickIndex = -1;
+        if ( ( numTicks > 2 ) && ( numTicks % 2 ) )
+            mediumTickIndex = numTicks / 2;
+
+        // substep factor = base^substeps
+        const qreal minFactor = qMax( qPow( logBase, minStep ), qreal( logBase ) );
+
+        for ( int i = 0; i < majorTicks.count(); i++ )
+        {
+            double tick = majorTicks[i];
+            for ( int j = 0; j < numTicks; j++ )
+            {
+                tick *= minFactor;
+
+                if ( j == mediumTickIndex )
+                    mediumTicks += tick;
+                else
+                    minorTicks += tick;
+            }
+        }
+    }
+}
+
+/*!
+  \brief Align an interval to a step size
+
+  The limits of an interval are aligned that both are integer
+  multiples of the step size.
+
+  \param interval Interval
+  \param stepSize Step size
+
+  \return Aligned interval
+*/
+QwtInterval QwtLogScaleEngine::align(
+    const QwtInterval &interval, double stepSize ) const
+{
+    const QwtInterval intv = qwtLogInterval( base(), interval );
+
+    double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize );
+    if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
+        x1 = interval.minValue();
+
+    double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize );
+    if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
+        x2 = interval.maxValue();
+
+    return qwtPowInterval( base(), QwtInterval( x1, x2 ) );
+}
diff --git a/qwt/qwt_scale_engine.h b/qwt/qwt_scale_engine.h
new file mode 100644
index 0000000..30e75fd
--- /dev/null
+++ b/qwt/qwt_scale_engine.h
@@ -0,0 +1,220 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_ENGINE_H
+#define QWT_SCALE_ENGINE_H
+
+#include "qwt_global.h"
+#include "qwt_scale_div.h"
+#include "qwt_interval.h"
+
+class QwtTransform;
+
+/*!
+  \brief Arithmetic including a tolerance
+*/
+class QWT_EXPORT QwtScaleArithmetic
+{
+public:
+    static double ceilEps( double value, double intervalSize );
+    static double floorEps( double value, double intervalSize );
+
+    static double divideEps( double interval, double steps );
+
+    static double divideInterval( double interval, 
+        int numSteps, uint base );
+};
+
+/*!
+  \brief Base class for scale engines.
+
+  A scale engine tries to find "reasonable" ranges and step sizes
+  for scales.
+
+  The layout of the scale can be varied with setAttribute().
+
+  Qwt offers implementations for logarithmic and linear scales. 
+*/
+
+class QWT_EXPORT QwtScaleEngine
+{
+public:
+    /*! 
+       Layout attributes
+       \sa setAttribute(), testAttribute(), reference(),
+           lowerMargin(), upperMargin()
+     */
+
+    enum Attribute
+    {
+        //! No attributes
+        NoAttribute = 0x00,
+
+        //! Build a scale which includes the reference() value.
+        IncludeReference = 0x01,
+
+        //! Build a scale which is symmetric to the reference() value.
+        Symmetric = 0x02,
+
+        /*!
+           The endpoints of the scale are supposed to be equal the
+           outmost included values plus the specified margins 
+           (see setMargins()).
+           If this attribute is *not* set, the endpoints of the scale will
+           be integer multiples of the step size.
+         */
+        Floating = 0x04,
+
+        //! Turn the scale upside down.
+        Inverted = 0x08
+    };
+
+    //! Layout attributes
+    typedef QFlags<Attribute> Attributes;
+
+    explicit QwtScaleEngine( uint base = 10 );
+    virtual ~QwtScaleEngine();
+
+    void setBase( uint base );
+    uint base() const;
+
+    void setAttribute( Attribute, bool on = true );
+    bool testAttribute( Attribute ) const;
+
+    void setAttributes( Attributes );
+    Attributes attributes() const;
+
+    void setReference( double reference );
+    double reference() const;
+
+    void setMargins( double lower, double upper );
+    double lowerMargin() const;
+    double upperMargin() const;
+
+    /*!
+      Align and divide an interval
+
+      \param maxNumSteps Max. number of steps
+      \param x1 First limit of the interval (In/Out)
+      \param x2 Second limit of the interval (In/Out)
+      \param stepSize Step size (Return value)
+    */
+    virtual void autoScale( int maxNumSteps,
+        double &x1, double &x2, double &stepSize ) const = 0;
+
+    /*!
+      \brief Calculate a scale division
+
+      \param x1 First interval limit
+      \param x2 Second interval limit
+      \param maxMajorSteps Maximum for the number of major steps
+      \param maxMinorSteps Maximum number of minor steps
+      \param stepSize Step size. If stepSize == 0.0, the scaleEngine
+                   calculates one.
+
+      \return Calculated scale division
+    */
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int maxMajorSteps, int maxMinorSteps,
+        double stepSize = 0.0 ) const = 0;
+
+    void setTransformation( QwtTransform * );
+    QwtTransform *transformation() const;
+
+protected:
+    bool contains( const QwtInterval &, double val ) const;
+    QList<double> strip( const QList<double>&, const QwtInterval & ) const;
+
+    double divideInterval( double interval, int numSteps ) const;
+
+    QwtInterval buildInterval( double v ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief A scale engine for linear scales
+
+  The step size will fit into the pattern
+  \f$\left\{ 1,2,5\right\} \cdot 10^{n}\f$, where n is an integer.
+*/
+
+class QWT_EXPORT QwtLinearScaleEngine: public QwtScaleEngine
+{
+public:
+    QwtLinearScaleEngine( uint base = 10 );
+    virtual ~QwtLinearScaleEngine();
+
+    virtual void autoScale( int maxSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int numMajorSteps, int numMinorSteps,
+                                     double stepSize = 0.0 ) const;
+
+
+protected:
+    QwtInterval align( const QwtInterval&, double stepSize ) const;
+
+    void buildTicks(
+        const QwtInterval &, double stepSize, int maxMinSteps,
+        QList<double> ticks[QwtScaleDiv::NTickTypes] ) const;
+
+    QList<double> buildMajorTicks(
+        const QwtInterval &interval, double stepSize ) const;
+
+    void buildMinorTicks( const QList<double>& majorTicks,
+        int maxMinorSteps, double stepSize,
+        QList<double> &minorTicks, QList<double> &mediumTicks ) const;
+};
+
+/*!
+  \brief A scale engine for logarithmic scales
+
+  The step size is measured in *decades*
+  and the major step size will be adjusted to fit the pattern
+  \f$\left\{ 1,2,3,5\right\} \cdot 10^{n}\f$, where n is a natural number
+  including zero.
+
+  \warning the step size as well as the margins are measured in *decades*.
+*/
+
+class QWT_EXPORT QwtLogScaleEngine: public QwtScaleEngine
+{
+public:
+    QwtLogScaleEngine( uint base = 10 );
+    virtual ~QwtLogScaleEngine();
+
+    virtual void autoScale( int maxSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int numMajorSteps, int numMinorSteps,
+        double stepSize = 0.0 ) const;
+
+protected:
+    QwtInterval align( const QwtInterval&, double stepSize ) const;
+
+    void buildTicks(
+        const QwtInterval &, double stepSize, int maxMinSteps,
+        QList<double> ticks[QwtScaleDiv::NTickTypes] ) const;
+
+    QList<double> buildMajorTicks(
+        const QwtInterval &interval, double stepSize ) const;
+
+    void buildMinorTicks( const QList<double>& majorTicks,
+        int maxMinorSteps, double stepSize,
+        QList<double> &minorTicks, QList<double> &mediumTicks ) const;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleEngine::Attributes )
+
+#endif
diff --git a/qwt/qwt_scale_map.cpp b/qwt/qwt_scale_map.cpp
new file mode 100644
index 0000000..c3fbad6
--- /dev/null
+++ b/qwt/qwt_scale_map.cpp
@@ -0,0 +1,248 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_scale_map.h"
+#include "qwt_math.h"
+#include <qrect.h>
+#include <qdebug.h>
+
+/*!
+  \brief Constructor
+
+  The scale and paint device intervals are both set to [0,1].
+*/
+QwtScaleMap::QwtScaleMap():
+    d_s1( 0.0 ),
+    d_s2( 1.0 ),
+    d_p1( 0.0 ),
+    d_p2( 1.0 ),
+    d_cnv( 1.0 ),
+    d_ts1( 0.0 ),
+    d_transform( NULL )
+{
+}
+
+//! Copy constructor
+QwtScaleMap::QwtScaleMap( const QwtScaleMap& other ):
+    d_s1( other.d_s1 ),
+    d_s2( other.d_s2 ),
+    d_p1( other.d_p1 ),
+    d_p2( other.d_p2 ),
+    d_cnv( other.d_cnv ),
+    d_ts1( other.d_ts1 ),
+    d_transform( NULL )
+{
+    if ( other.d_transform )
+        d_transform = other.d_transform->copy();
+}
+
+/*!
+  Destructor
+*/
+QwtScaleMap::~QwtScaleMap()
+{
+    delete d_transform;
+}
+
+//! Assignment operator
+QwtScaleMap &QwtScaleMap::operator=( const QwtScaleMap & other )
+{
+    d_s1 = other.d_s1;
+    d_s2 = other.d_s2;
+    d_p1 = other.d_p1;
+    d_p2 = other.d_p2;
+    d_cnv = other.d_cnv;
+    d_ts1 = other.d_ts1;
+
+    delete d_transform;
+    d_transform = NULL;
+
+    if ( other.d_transform )
+        d_transform = other.d_transform->copy();
+
+    return *this;
+}
+
+/*!
+   Initialize the map with a transformation
+*/
+void QwtScaleMap::setTransformation( QwtTransform *transform )
+{
+    if ( transform != d_transform )
+    {
+        delete d_transform;
+        d_transform = transform;
+    }
+
+    setScaleInterval( d_s1, d_s2 );
+}
+
+//! Get the transformation
+const QwtTransform *QwtScaleMap::transformation() const
+{
+    return d_transform;
+}
+
+/*!
+  \brief Specify the borders of the scale interval
+  \param s1 first border
+  \param s2 second border
+  \warning scales might be aligned to 
+           transformation depending boundaries
+*/
+void QwtScaleMap::setScaleInterval( double s1, double s2 )
+{
+    d_s1 = s1;
+    d_s2 = s2;
+
+    if ( d_transform )
+    {
+        d_s1 = d_transform->bounded( d_s1 );
+        d_s2 = d_transform->bounded( d_s2 );
+    }
+
+    updateFactor();
+}
+
+/*!
+  \brief Specify the borders of the paint device interval
+  \param p1 first border
+  \param p2 second border
+*/
+void QwtScaleMap::setPaintInterval( double p1, double p2 )
+{
+    d_p1 = p1;
+    d_p2 = p2;
+
+    updateFactor();
+}
+
+void QwtScaleMap::updateFactor()
+{
+    d_ts1 = d_s1;
+    double ts2 = d_s2;
+
+    if ( d_transform )
+    {
+        d_ts1 = d_transform->transform( d_ts1 );
+        ts2 = d_transform->transform( ts2 );
+    }
+
+    d_cnv = 1.0;
+    if ( d_ts1 != ts2 )
+        d_cnv = ( d_p2 - d_p1 ) / ( ts2 - d_ts1 );
+}
+
+/*!
+   Transform a rectangle from scale to paint coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param rect Rectangle in scale coordinates
+   \return Rectangle in paint coordinates
+
+   \sa invTransform()
+*/
+QRectF QwtScaleMap::transform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QRectF &rect )
+{
+    double x1 = xMap.transform( rect.left() );
+    double x2 = xMap.transform( rect.right() );
+    double y1 = yMap.transform( rect.top() );
+    double y2 = yMap.transform( rect.bottom() );
+
+    if ( x2 < x1 )
+        qSwap( x1, x2 );
+    if ( y2 < y1 )
+        qSwap( y1, y2 );
+
+    if ( qwtFuzzyCompare( x1, 0.0, x2 - x1 ) == 0 )
+        x1 = 0.0;
+    if ( qwtFuzzyCompare( x2, 0.0, x2 - x1 ) == 0 )
+        x2 = 0.0;
+    if ( qwtFuzzyCompare( y1, 0.0, y2 - y1 ) == 0 )
+        y1 = 0.0;
+    if ( qwtFuzzyCompare( y2, 0.0, y2 - y1 ) == 0 )
+        y2 = 0.0;
+
+    return QRectF( x1, y1, x2 - x1 + 1, y2 - y1 + 1 );
+}
+
+/*!
+   Transform a rectangle from paint to scale coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param pos Position in paint coordinates
+   \return Position in scale coordinates
+   \sa transform()
+*/
+QPointF QwtScaleMap::invTransform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QPointF &pos )
+{
+    return QPointF( 
+        xMap.invTransform( pos.x() ), 
+        yMap.invTransform( pos.y() ) 
+    );
+}
+
+/*!
+   Transform a point from scale to paint coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param pos Position in scale coordinates
+   \return Position in paint coordinates
+
+   \sa invTransform()
+*/
+QPointF QwtScaleMap::transform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QPointF &pos )
+{
+    return QPointF( 
+        xMap.transform( pos.x() ), 
+        yMap.transform( pos.y() )
+    );
+}
+
+/*!
+   Transform a rectangle from paint to scale coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param rect Rectangle in paint coordinates
+   \return Rectangle in scale coordinates
+   \sa transform()
+*/
+QRectF QwtScaleMap::invTransform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QRectF &rect )
+{
+    const double x1 = xMap.invTransform( rect.left() );
+    const double x2 = xMap.invTransform( rect.right() - 1 );
+    const double y1 = yMap.invTransform( rect.top() );
+    const double y2 = yMap.invTransform( rect.bottom() - 1 );
+
+    const QRectF r( x1, y1, x2 - x1, y2 - y1 );
+    return r.normalized();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtScaleMap &map )
+{
+    debug.nospace() << "QwtScaleMap("
+        << map.transformation()
+        << ", s:" << map.s1() << "->" << map.s2()
+        << ", p:" << map.p1() << "->" << map.p2()
+        << ")";
+
+    return debug.space();
+}
+
+#endif
diff --git a/qwt/qwt_scale_map.h b/qwt/qwt_scale_map.h
new file mode 100644
index 0000000..c07f1ce
--- /dev/null
+++ b/qwt/qwt_scale_map.h
@@ -0,0 +1,175 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_MAP_H
+#define QWT_SCALE_MAP_H
+
+#include "qwt_global.h"
+#include "qwt_transform.h"
+#include <qrect.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+class QRectF;
+
+/*!
+   \brief A scale map
+
+   QwtScaleMap offers transformations from the coordinate system
+   of a scale into the linear coordinate system of a paint device 
+   and vice versa.
+*/
+class QWT_EXPORT QwtScaleMap
+{
+public:
+    QwtScaleMap();
+    QwtScaleMap( const QwtScaleMap& );
+
+    ~QwtScaleMap();
+
+    QwtScaleMap &operator=( const QwtScaleMap & );
+
+    void setTransformation( QwtTransform * );
+    const QwtTransform *transformation() const;
+
+    void setPaintInterval( double p1, double p2 );
+    void setScaleInterval( double s1, double s2 );
+
+    double transform( double s ) const;
+    double invTransform( double p ) const;
+
+    double p1() const;
+    double p2() const;
+
+    double s1() const;
+    double s2() const;
+
+    double pDist() const;
+    double sDist() const;
+
+    static QRectF transform( const QwtScaleMap &,
+        const QwtScaleMap &, const QRectF & );
+    static QRectF invTransform( const QwtScaleMap &,
+        const QwtScaleMap &, const QRectF & );
+
+    static QPointF transform( const QwtScaleMap &,
+        const QwtScaleMap &, const QPointF & );
+    static QPointF invTransform( const QwtScaleMap &,
+        const QwtScaleMap &, const QPointF & );
+
+    bool isInverting() const;
+
+private:
+    void updateFactor();
+
+    double d_s1, d_s2;     // scale interval boundaries
+    double d_p1, d_p2;     // paint device interval boundaries
+
+    double d_cnv;       // conversion factor
+    double d_ts1;
+
+    QwtTransform *d_transform;
+};
+
+/*!
+    \return First border of the scale interval
+*/
+inline double QwtScaleMap::s1() const
+{
+    return d_s1;
+}
+
+/*!
+    \return Second border of the scale interval
+*/
+inline double QwtScaleMap::s2() const
+{
+    return d_s2;
+}
+
+/*!
+    \return First border of the paint interval
+*/
+inline double QwtScaleMap::p1() const
+{
+    return d_p1;
+}
+
+/*!
+    \return Second border of the paint interval
+*/
+inline double QwtScaleMap::p2() const
+{
+    return d_p2;
+}
+
+/*!
+    \return qwtAbs(p2() - p1())
+*/
+inline double QwtScaleMap::pDist() const
+{
+    return qAbs( d_p2 - d_p1 );
+}
+
+/*!
+    \return qwtAbs(s2() - s1())
+*/
+inline double QwtScaleMap::sDist() const
+{
+    return qAbs( d_s2 - d_s1 );
+}
+
+/*!
+  Transform a point related to the scale interval into an point
+  related to the interval of the paint device
+
+  \param s Value relative to the coordinates of the scale
+  \return Transformed value
+
+  \sa invTransform()
+*/
+inline double QwtScaleMap::transform( double s ) const
+{
+    if ( d_transform )
+        s = d_transform->transform( s );
+
+    return d_p1 + ( s - d_ts1 ) * d_cnv;
+}
+
+/*!
+  Transform an paint device value into a value in the
+  interval of the scale.
+
+  \param p Value relative to the coordinates of the paint device
+  \return Transformed value
+
+  \sa transform()
+*/
+inline double QwtScaleMap::invTransform( double p ) const
+{
+    double s = d_ts1 + ( p - d_p1 ) / d_cnv;
+    if ( d_transform )
+        s = d_transform->invTransform( s );
+
+    return s;
+}
+
+//! \return True, when ( p1() < p2() ) != ( s1() < s2() )
+inline bool QwtScaleMap::isInverting() const
+{
+    return ( ( d_p1 < d_p2 ) != ( d_s1 < d_s2 ) );
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtScaleMap & );
+#endif
+
+#endif
diff --git a/qwt/qwt_scale_widget.cpp b/qwt/qwt_scale_widget.cpp
new file mode 100644
index 0000000..d34ada5
--- /dev/null
+++ b/qwt/qwt_scale_widget.cpp
@@ -0,0 +1,942 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_scale_widget.h"
+#include "qwt_painter.h"
+#include "qwt_color_map.h"
+#include "qwt_scale_map.h"
+#include "qwt_math.h"
+#include "qwt_scale_div.h"
+#include "qwt_text.h"
+#include "qwt_scale_engine.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+class QwtScaleWidget::PrivateData
+{
+public:
+    PrivateData():
+        scaleDraw( NULL )
+    {
+        colorBar.colorMap = NULL;
+    }
+
+    ~PrivateData()
+    {
+        delete scaleDraw;
+        delete colorBar.colorMap;
+    }
+
+    QwtScaleDraw *scaleDraw;
+
+    int borderDist[2];
+    int minBorderDist[2];
+    int scaleLength;
+    int margin;
+
+    int titleOffset;
+    int spacing;
+    QwtText title;
+
+    QwtScaleWidget::LayoutFlags layoutFlags;
+
+    struct t_colorBar
+    {
+        bool isEnabled;
+        int width;
+        QwtInterval interval;
+        QwtColorMap *colorMap;
+    } colorBar;
+};
+
+/*!
+  \brief Create a scale with the position QwtScaleWidget::Left
+  \param parent Parent widget
+*/
+QwtScaleWidget::QwtScaleWidget( QWidget *parent ):
+    QWidget( parent )
+{
+    initScale( QwtScaleDraw::LeftScale );
+}
+
+/*!
+  \brief Constructor
+  \param align Alignment.
+  \param parent Parent widget
+*/
+QwtScaleWidget::QwtScaleWidget(
+        QwtScaleDraw::Alignment align, QWidget *parent ):
+    QWidget( parent )
+{
+    initScale( align );
+}
+
+//! Destructor
+QwtScaleWidget::~QwtScaleWidget()
+{
+    delete d_data;
+}
+
+//! Initialize the scale
+void QwtScaleWidget::initScale( QwtScaleDraw::Alignment align )
+{
+    d_data = new PrivateData;
+
+    d_data->layoutFlags = 0;
+    if ( align == QwtScaleDraw::RightScale )
+        d_data->layoutFlags |= TitleInverted;
+
+    d_data->borderDist[0] = 0;
+    d_data->borderDist[1] = 0;
+    d_data->minBorderDist[0] = 0;
+    d_data->minBorderDist[1] = 0;
+    d_data->margin = 4;
+    d_data->titleOffset = 0;
+    d_data->spacing = 2;
+
+    d_data->scaleDraw = new QwtScaleDraw;
+    d_data->scaleDraw->setAlignment( align );
+    d_data->scaleDraw->setLength( 10 );
+
+    d_data->scaleDraw->setScaleDiv(
+        QwtLinearScaleEngine().divideScale( 0.0, 100.0, 10, 5 ) );
+
+    d_data->colorBar.colorMap = new QwtLinearColorMap();
+    d_data->colorBar.isEnabled = false;
+    d_data->colorBar.width = 10;
+
+    const int flags = Qt::AlignHCenter
+        | Qt::TextExpandTabs | Qt::TextWordWrap;
+    d_data->title.setRenderFlags( flags );
+    d_data->title.setFont( font() );
+
+    QSizePolicy policy( QSizePolicy::MinimumExpanding,
+        QSizePolicy::Fixed );
+    if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+        policy.transpose();
+
+    setSizePolicy( policy );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+}
+
+/*!
+   Toggle an layout flag
+
+   \param flag Layout flag
+   \param on true/false
+
+   \sa testLayoutFlag(), LayoutFlag
+*/
+void QwtScaleWidget::setLayoutFlag( LayoutFlag flag, bool on )
+{
+    if ( ( ( d_data->layoutFlags & flag ) != 0 ) != on )
+    {
+        if ( on )
+            d_data->layoutFlags |= flag;
+        else
+            d_data->layoutFlags &= ~flag;
+    }
+}
+
+/*!
+   Test a layout flag
+
+   \param flag Layout flag
+   \return true/false
+   \sa setLayoutFlag(), LayoutFlag
+*/
+bool QwtScaleWidget::testLayoutFlag( LayoutFlag flag ) const
+{
+    return ( d_data->layoutFlags & flag );
+}
+
+/*!
+  Give title new text contents
+
+  \param title New title
+  \sa title(), setTitle(const QwtText &);
+*/
+void QwtScaleWidget::setTitle( const QString &title )
+{
+    if ( d_data->title.text() != title )
+    {
+        d_data->title.setText( title );
+        layoutScale();
+    }
+}
+
+/*!
+  Give title new text contents
+
+  \param title New title
+  \sa title()
+  \warning The title flags are interpreted in
+               direction of the label, AlignTop, AlignBottom can't be set
+               as the title will always be aligned to the scale.
+*/
+void QwtScaleWidget::setTitle( const QwtText &title )
+{
+    QwtText t = title;
+    const int flags = title.renderFlags() & ~( Qt::AlignTop | Qt::AlignBottom );
+    t.setRenderFlags( flags );
+
+    if ( t != d_data->title )
+    {
+        d_data->title = t;
+        layoutScale();
+    }
+}
+
+/*!
+  Change the alignment
+
+  \param alignment New alignment
+  \sa alignment()
+*/
+void QwtScaleWidget::setAlignment( QwtScaleDraw::Alignment alignment )
+{
+    if ( d_data->scaleDraw )
+        d_data->scaleDraw->setAlignment( alignment );
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy policy( QSizePolicy::MinimumExpanding,
+            QSizePolicy::Fixed );
+        if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+            policy.transpose();
+
+        setSizePolicy( policy );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    layoutScale();
+}
+
+
+/*!
+    \return position
+    \sa setPosition()
+*/
+QwtScaleDraw::Alignment QwtScaleWidget::alignment() const
+{
+    if ( !scaleDraw() )
+        return QwtScaleDraw::LeftScale;
+
+    return scaleDraw()->alignment();
+}
+
+/*!
+  Specify distances of the scale's endpoints from the
+  widget's borders. The actual borders will never be less
+  than minimum border distance.
+  \param dist1 Left or top Distance
+  \param dist2 Right or bottom distance
+  \sa borderDist()
+*/
+void QwtScaleWidget::setBorderDist( int dist1, int dist2 )
+{
+    if ( dist1 != d_data->borderDist[0] || dist2 != d_data->borderDist[1] )
+    {
+        d_data->borderDist[0] = dist1;
+        d_data->borderDist[1] = dist2;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Specify the margin to the colorBar/base line.
+  \param margin Margin
+  \sa margin()
+*/
+void QwtScaleWidget::setMargin( int margin )
+{
+    margin = qMax( 0, margin );
+    if ( margin != d_data->margin )
+    {
+        d_data->margin = margin;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Specify the distance between color bar, scale and title
+  \param spacing Spacing
+  \sa spacing()
+*/
+void QwtScaleWidget::setSpacing( int spacing )
+{
+    spacing = qMax( 0, spacing );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Change the alignment for the labels.
+
+  \sa QwtScaleDraw::setLabelAlignment(), setLabelRotation()
+*/
+void QwtScaleWidget::setLabelAlignment( Qt::Alignment alignment )
+{
+    d_data->scaleDraw->setLabelAlignment( alignment );
+    layoutScale();
+}
+
+/*!
+  \brief Change the rotation for the labels.
+  See QwtScaleDraw::setLabelRotation().
+
+  \param rotation Rotation
+  \sa QwtScaleDraw::setLabelRotation(), setLabelFlags()
+*/
+void QwtScaleWidget::setLabelRotation( double rotation )
+{
+    d_data->scaleDraw->setLabelRotation( rotation );
+    layoutScale();
+}
+
+/*!
+  Set a scale draw
+
+  scaleDraw has to be created with new and will be deleted in
+  ~QwtScaleWidget() or the next call of setScaleDraw().
+  scaleDraw will be initialized with the attributes of
+  the previous scaleDraw object.
+
+  \param scaleDraw ScaleDraw object
+  \sa scaleDraw()
+*/
+void QwtScaleWidget::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    if ( ( scaleDraw == NULL ) || ( scaleDraw == d_data->scaleDraw ) )
+        return;
+
+    const QwtScaleDraw* sd = d_data->scaleDraw;
+    if ( sd )
+    {
+        scaleDraw->setAlignment( sd->alignment() );
+        scaleDraw->setScaleDiv( sd->scaleDiv() );
+
+        QwtTransform *transform = NULL;
+        if ( sd->scaleMap().transformation() )
+            transform = sd->scaleMap().transformation()->copy();
+
+        scaleDraw->setTransformation( transform );
+    }
+
+    delete d_data->scaleDraw;
+    d_data->scaleDraw = scaleDraw;
+
+    layoutScale();
+}
+
+/*!
+    \return scaleDraw of this scale
+    \sa setScaleDraw(), QwtScaleDraw::setScaleDraw()
+*/
+const QwtScaleDraw *QwtScaleWidget::scaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return scaleDraw of this scale
+    \sa QwtScaleDraw::setScaleDraw()
+*/
+QwtScaleDraw *QwtScaleWidget::scaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return title
+    \sa setTitle()
+*/
+QwtText QwtScaleWidget::title() const
+{
+    return d_data->title;
+}
+
+/*!
+    \return start border distance
+    \sa setBorderDist()
+*/
+int QwtScaleWidget::startBorderDist() const
+{
+    return d_data->borderDist[0];
+}
+
+/*!
+    \return end border distance
+    \sa setBorderDist()
+*/
+int QwtScaleWidget::endBorderDist() const
+{
+    return d_data->borderDist[1];
+}
+
+/*!
+    \return margin
+    \sa setMargin()
+*/
+int QwtScaleWidget::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+    \return distance between scale and title
+    \sa setMargin()
+*/
+int QwtScaleWidget::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief paintEvent
+*/
+void QwtScaleWidget::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    draw( &painter );
+}
+
+/*!
+  \brief draw the scale
+*/
+void QwtScaleWidget::draw( QPainter *painter ) const
+{
+    d_data->scaleDraw->draw( painter, palette() );
+
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.width > 0 &&
+        d_data->colorBar.interval.isValid() )
+    {
+        drawColorBar( painter, colorBarRect( contentsRect() ) );
+    }
+
+    QRect r = contentsRect();
+    if ( d_data->scaleDraw->orientation() == Qt::Horizontal )
+    {
+        r.setLeft( r.left() + d_data->borderDist[0] );
+        r.setWidth( r.width() - d_data->borderDist[1] );
+    }
+    else
+    {
+        r.setTop( r.top() + d_data->borderDist[0] );
+        r.setHeight( r.height() - d_data->borderDist[1] );
+    }
+
+    if ( !d_data->title.isEmpty() )
+        drawTitle( painter, d_data->scaleDraw->alignment(), r );
+}
+
+/*!
+  Calculate the the rectangle for the color bar
+
+  \param rect Bounding rectangle for all components of the scale
+  \return Rectangle for the color bar
+*/
+QRectF QwtScaleWidget::colorBarRect( const QRectF& rect ) const
+{
+    QRectF cr = rect;
+
+    if ( d_data->scaleDraw->orientation() == Qt::Horizontal )
+    {
+        cr.setLeft( cr.left() + d_data->borderDist[0] );
+        cr.setWidth( cr.width() - d_data->borderDist[1] + 1 );
+    }
+    else
+    {
+        cr.setTop( cr.top() + d_data->borderDist[0] );
+        cr.setHeight( cr.height() - d_data->borderDist[1] + 1 );
+    }
+
+    switch ( d_data->scaleDraw->alignment() )
+    {
+        case QwtScaleDraw::LeftScale:
+        {
+            cr.setLeft( cr.right() - d_data->margin
+                - d_data->colorBar.width );
+            cr.setWidth( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::RightScale:
+        {
+            cr.setLeft( cr.left() + d_data->margin );
+            cr.setWidth( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::BottomScale:
+        {
+            cr.setTop( cr.top() + d_data->margin );
+            cr.setHeight( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::TopScale:
+        {
+            cr.setTop( cr.bottom() - d_data->margin
+                - d_data->colorBar.width );
+            cr.setHeight( d_data->colorBar.width );
+            break;
+        }
+    }
+
+    return cr;
+}
+
+/*!
+  Event handler for resize events
+  \param event Resize event
+*/
+void QwtScaleWidget::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+    layoutScale( false );
+}
+
+/*!
+  Recalculate the scale's geometry and layout based on
+  the current geometry and fonts.
+
+  \param update_geometry Notify the layout system and call update
+                         to redraw the scale
+*/
+
+void QwtScaleWidget::layoutScale( bool update_geometry )
+{
+    int bd0, bd1;
+    getBorderDistHint( bd0, bd1 );
+    if ( d_data->borderDist[0] > bd0 )
+        bd0 = d_data->borderDist[0];
+    if ( d_data->borderDist[1] > bd1 )
+        bd1 = d_data->borderDist[1];
+
+    int colorBarWidth = 0;
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() )
+        colorBarWidth = d_data->colorBar.width + d_data->spacing;
+
+    const QRectF r = contentsRect();
+    double x, y, length;
+
+    if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+    {
+        y = r.top() + bd0;
+        length = r.height() - ( bd0 + bd1 );
+
+        if ( d_data->scaleDraw->alignment() == QwtScaleDraw::LeftScale )
+            x = r.right() - 1.0 - d_data->margin - colorBarWidth;
+        else
+            x = r.left() + d_data->margin + colorBarWidth;
+    }
+    else
+    {
+        x = r.left() + bd0;
+        length = r.width() - ( bd0 + bd1 );
+
+        if ( d_data->scaleDraw->alignment() == QwtScaleDraw::BottomScale )
+            y = r.top() + d_data->margin + colorBarWidth;
+        else
+            y = r.bottom() - 1.0 - d_data->margin - colorBarWidth;
+    }
+
+    d_data->scaleDraw->move( x, y );
+    d_data->scaleDraw->setLength( length );
+
+    const int extent = qCeil( d_data->scaleDraw->extent( font() ) );
+
+    d_data->titleOffset =
+        d_data->margin + d_data->spacing + colorBarWidth + extent;
+
+    if ( update_geometry )
+    {
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  Draw the color bar of the scale widget
+
+  \param painter Painter
+  \param rect Bounding rectangle for the color bar
+
+  \sa setColorBarEnabled()
+*/
+void QwtScaleWidget::drawColorBar( QPainter *painter, const QRectF& rect ) const
+{
+    if ( !d_data->colorBar.interval.isValid() )
+        return;
+
+    const QwtScaleDraw* sd = d_data->scaleDraw;
+
+    QwtPainter::drawColorBar( painter, *d_data->colorBar.colorMap,
+        d_data->colorBar.interval.normalized(), sd->scaleMap(),
+        sd->orientation(), rect );
+}
+
+/*!
+  Rotate and paint a title according to its position into a given rectangle.
+
+  \param painter Painter
+  \param align Alignment
+  \param rect Bounding rectangle
+*/
+
+void QwtScaleWidget::drawTitle( QPainter *painter,
+    QwtScaleDraw::Alignment align, const QRectF &rect ) const
+{
+    QRectF r = rect;
+    double angle;
+    int flags = d_data->title.renderFlags() &
+        ~( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter );
+
+    switch ( align )
+    {
+        case QwtScaleDraw::LeftScale:
+            angle = -90.0;
+            flags |= Qt::AlignTop;
+            r.setRect( r.left(), r.bottom(),
+                r.height(), r.width() - d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::RightScale:
+            angle = -90.0;
+            flags |= Qt::AlignTop;
+            r.setRect( r.left() + d_data->titleOffset, r.bottom(),
+                r.height(), r.width() - d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::BottomScale:
+            angle = 0.0;
+            flags |= Qt::AlignBottom;
+            r.setTop( r.top() + d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::TopScale:
+        default:
+            angle = 0.0;
+            flags |= Qt::AlignTop;
+            r.setBottom( r.bottom() - d_data->titleOffset );
+            break;
+    }
+
+    if ( d_data->layoutFlags & TitleInverted )
+    {
+        if ( align == QwtScaleDraw::LeftScale
+            || align == QwtScaleDraw::RightScale )
+        {
+            angle = -angle;
+            r.setRect( r.x() + r.height(), r.y() - r.width(),
+                r.width(), r.height() );
+        }
+    }
+
+    painter->save();
+    painter->setFont( font() );
+    painter->setPen( palette().color( QPalette::Text ) );
+
+    painter->translate( r.x(), r.y() );
+    if ( angle != 0.0 )
+        painter->rotate( angle );
+
+    QwtText title = d_data->title;
+    title.setRenderFlags( flags );
+    title.draw( painter, QRectF( 0.0, 0.0, r.width(), r.height() ) );
+
+    painter->restore();
+}
+
+/*!
+  \brief Notify a change of the scale
+
+  This virtual function can be overloaded by derived
+  classes. The default implementation updates the geometry
+  and repaints the widget.
+*/
+
+void QwtScaleWidget::scaleChange()
+{
+    layoutScale();
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtScaleWidget::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+/*!
+  \return a minimum size hint
+*/
+QSize QwtScaleWidget::minimumSizeHint() const
+{
+    const Qt::Orientation o = d_data->scaleDraw->orientation();
+
+    // Border Distance cannot be less than the scale borderDistHint
+    // Note, the borderDistHint is already included in minHeight/minWidth
+    int length = 0;
+    int mbd1, mbd2;
+    getBorderDistHint( mbd1, mbd2 );
+    length += qMax( 0, d_data->borderDist[0] - mbd1 );
+    length += qMax( 0, d_data->borderDist[1] - mbd2 );
+    length += d_data->scaleDraw->minLength( font() );
+
+    int dim = dimForLength( length, font() );
+    if ( length < dim )
+    {
+        // compensate for long titles
+        length = dim;
+        dim = dimForLength( length, font() );
+    }
+
+    QSize size( length + 2, dim );
+    if ( o == Qt::Vertical )
+        size.transpose();
+
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+    return size + QSize( left + right, top + bottom );
+}
+
+/*!
+  \brief Find the height of the title for a given width.
+  \param width Width
+  \return height Height
+ */
+
+int QwtScaleWidget::titleHeightForWidth( int width ) const
+{
+    return qCeil( d_data->title.heightForWidth( width, font() ) );
+}
+
+/*!
+  \brief Find the minimum dimension for a given length.
+         dim is the height, length the width seen in
+         direction of the title.
+  \param length width for horizontal, height for vertical scales
+  \param scaleFont Font of the scale
+  \return height for horizontal, width for vertical scales
+*/
+
+int QwtScaleWidget::dimForLength( int length, const QFont &scaleFont ) const
+{
+    const int extent = qCeil( d_data->scaleDraw->extent( scaleFont ) );
+
+    int dim = d_data->margin + extent + 1;
+
+    if ( !d_data->title.isEmpty() )
+        dim += titleHeightForWidth( length ) + d_data->spacing;
+
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() )
+        dim += d_data->colorBar.width + d_data->spacing;
+
+    return dim;
+}
+
+/*!
+  \brief Calculate a hint for the border distances.
+
+  This member function calculates the distance
+  of the scale's endpoints from the widget borders which
+  is required for the mark labels to fit into the widget.
+  The maximum of this distance an the minimum border distance
+  is returned.
+
+  \param start Return parameter for the border width at 
+               the beginning of the scale
+  \param end Return parameter for the border width at the 
+             end of the scale
+
+  \warning
+  <ul> <li>The minimum border distance depends on the font.</ul>
+  \sa setMinBorderDist(), getMinBorderDist(), setBorderDist()
+*/
+void QwtScaleWidget::getBorderDistHint( int &start, int &end ) const
+{
+    d_data->scaleDraw->getBorderDistHint( font(), start, end );
+
+    if ( start < d_data->minBorderDist[0] )
+        start = d_data->minBorderDist[0];
+
+    if ( end < d_data->minBorderDist[1] )
+        end = d_data->minBorderDist[1];
+}
+
+/*!
+  Set a minimum value for the distances of the scale's endpoints from
+  the widget borders. This is useful to avoid that the scales
+  are "jumping", when the tick labels or their positions change
+  often.
+
+  \param start Minimum for the start border
+  \param end Minimum for the end border
+  \sa getMinBorderDist(), getBorderDistHint()
+*/
+void QwtScaleWidget::setMinBorderDist( int start, int end )
+{
+    d_data->minBorderDist[0] = start;
+    d_data->minBorderDist[1] = end;
+}
+
+/*!
+  Get the minimum value for the distances of the scale's endpoints from
+  the widget borders.
+
+  \param start Return parameter for the border width at 
+               the beginning of the scale
+  \param end Return parameter for the border width at the 
+             end of the scale
+
+  \sa setMinBorderDist(), getBorderDistHint()
+*/
+void QwtScaleWidget::getMinBorderDist( int &start, int &end ) const
+{
+    start = d_data->minBorderDist[0];
+    end = d_data->minBorderDist[1];
+}
+
+/*!
+  \brief Assign a scale division
+
+  The scale division determines where to set the tick marks.
+
+  \param scaleDiv Scale Division
+  \sa For more information about scale divisions, see QwtScaleDiv.
+*/
+void QwtScaleWidget::setScaleDiv( const QwtScaleDiv &scaleDiv )
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( sd->scaleDiv() != scaleDiv )
+    {
+        sd->setScaleDiv( scaleDiv );
+        layoutScale();
+
+        Q_EMIT scaleDivChanged();
+    }
+}
+
+/*!
+  Set the transformation
+
+  \param transformation Transformation
+  \sa QwtAbstractScaleDraw::scaleDraw(), QwtScaleMap
+ */
+void QwtScaleWidget::setTransformation( QwtTransform *transformation )
+{
+    d_data->scaleDraw->setTransformation( transformation );
+    layoutScale();
+}
+
+/*!
+  En/disable a color bar associated to the scale
+  \sa isColorBarEnabled(), setColorBarWidth()
+*/
+void QwtScaleWidget::setColorBarEnabled( bool on )
+{
+    if ( on != d_data->colorBar.isEnabled )
+    {
+        d_data->colorBar.isEnabled = on;
+        layoutScale();
+    }
+}
+
+/*!
+  \return true, when the color bar is enabled
+  \sa setColorBarEnabled(), setColorBarWidth()
+*/
+bool QwtScaleWidget::isColorBarEnabled() const
+{
+    return d_data->colorBar.isEnabled;
+}
+
+/*!
+  Set the width of the color bar
+
+  \param width Width
+  \sa colorBarWidth(), setColorBarEnabled()
+*/
+void QwtScaleWidget::setColorBarWidth( int width )
+{
+    if ( width != d_data->colorBar.width )
+    {
+        d_data->colorBar.width = width;
+        if ( isColorBarEnabled() )
+            layoutScale();
+    }
+}
+
+/*!
+  \return Width of the color bar
+  \sa setColorBarEnabled(), setColorBarEnabled()
+*/
+int QwtScaleWidget::colorBarWidth() const
+{
+    return d_data->colorBar.width;
+}
+
+/*!
+  \return Value interval for the color bar
+  \sa setColorMap(), colorMap()
+*/
+QwtInterval QwtScaleWidget::colorBarInterval() const
+{
+    return d_data->colorBar.interval;
+}
+
+/*!
+  Set the color map and value interval, that are used for displaying
+  the color bar.
+
+  \param interval Value interval
+  \param colorMap Color map
+
+  \sa colorMap(), colorBarInterval()
+*/
+void QwtScaleWidget::setColorMap(
+    const QwtInterval &interval, QwtColorMap *colorMap )
+{
+    d_data->colorBar.interval = interval;
+
+    if ( colorMap != d_data->colorBar.colorMap )
+    {
+        delete d_data->colorBar.colorMap;
+        d_data->colorBar.colorMap = colorMap;
+    }
+
+    if ( isColorBarEnabled() )
+        layoutScale();
+}
+
+/*!
+  \return Color map
+  \sa setColorMap(), colorBarInterval()
+*/
+const QwtColorMap *QwtScaleWidget::colorMap() const
+{
+    return d_data->colorBar.colorMap;
+}
diff --git a/qwt/qwt_scale_widget.h b/qwt/qwt_scale_widget.h
new file mode 100644
index 0000000..067723d
--- /dev/null
+++ b/qwt/qwt_scale_widget.h
@@ -0,0 +1,136 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_WIDGET_H
+#define QWT_SCALE_WIDGET_H
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include "qwt_scale_draw.h"
+#include <qwidget.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qstring.h>
+
+class QPainter;
+class QwtTransform;
+class QwtScaleDiv;
+class QwtColorMap;
+
+/*!
+  \brief A Widget which contains a scale
+
+  This Widget can be used to decorate composite widgets with
+  a scale.
+*/
+
+class QWT_EXPORT QwtScaleWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    //! Layout flags of the title
+    enum LayoutFlag
+    {
+        /*!
+          The title of vertical scales is painted from top to bottom. 
+          Otherwise it is painted from bottom to top.
+         */
+        TitleInverted = 1
+    };
+
+    //! Layout flags of the title
+    typedef QFlags<LayoutFlag> LayoutFlags;
+
+    explicit QwtScaleWidget( QWidget *parent = NULL );
+    explicit QwtScaleWidget( QwtScaleDraw::Alignment, QWidget *parent = NULL );
+    virtual ~QwtScaleWidget();
+
+Q_SIGNALS:
+    //! Signal emitted, whenever the scale division changes
+    void scaleDivChanged();
+
+public:
+    void setTitle( const QString &title );
+    void setTitle( const QwtText &title );
+    QwtText title() const;
+
+    void setLayoutFlag( LayoutFlag, bool on );
+    bool testLayoutFlag( LayoutFlag ) const;
+
+    void setBorderDist( int start, int end );
+    int startBorderDist() const;
+    int endBorderDist() const;
+
+    void getBorderDistHint( int &start, int &end ) const;
+
+    void getMinBorderDist( int &start, int &end ) const;
+    void setMinBorderDist( int start, int end );
+
+    void setMargin( int );
+    int margin() const;
+
+    void setSpacing( int td );
+    int spacing() const;
+
+    void setScaleDiv( const QwtScaleDiv &sd );
+    void setTransformation( QwtTransform * );
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+    QwtScaleDraw *scaleDraw();
+
+    void setLabelAlignment( Qt::Alignment );
+    void setLabelRotation( double rotation );
+
+    void setColorBarEnabled( bool );
+    bool isColorBarEnabled() const;
+
+    void setColorBarWidth( int );
+    int colorBarWidth() const;
+
+    void setColorMap( const QwtInterval &, QwtColorMap * );
+
+    QwtInterval colorBarInterval() const;
+    const QwtColorMap *colorMap() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    int titleHeightForWidth( int width ) const;
+    int dimForLength( int length, const QFont &scaleFont ) const;
+
+    void drawColorBar( QPainter *painter, const QRectF & ) const;
+    void drawTitle( QPainter *painter, QwtScaleDraw::Alignment,
+        const QRectF &rect ) const;
+
+    void setAlignment( QwtScaleDraw::Alignment );
+    QwtScaleDraw::Alignment alignment() const;
+
+    QRectF colorBarRect( const QRectF& ) const;
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+
+    void draw( QPainter *p ) const;
+
+    void scaleChange();
+    void layoutScale( bool update = true );
+
+private:
+    void initScale( QwtScaleDraw::Alignment );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleWidget::LayoutFlags )
+
+#endif
diff --git a/qwt/qwt_series_data.cpp b/qwt/qwt_series_data.cpp
new file mode 100644
index 0000000..319af41
--- /dev/null
+++ b/qwt/qwt_series_data.cpp
@@ -0,0 +1,346 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_series_data.h"
+#include "qwt_math.h"
+
+static inline QRectF qwtBoundingRect( const QPointF &sample )
+{
+    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtPoint3D &sample )
+{
+    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtPointPolar &sample )
+{
+    return QRectF( sample.azimuth(), sample.radius(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtIntervalSample &sample )
+{
+    return QRectF( sample.interval.minValue(), sample.value,
+        sample.interval.maxValue() - sample.interval.minValue(), 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtSetSample &sample )
+{
+    double minY = sample.set[0];
+    double maxY = sample.set[0];
+
+    for ( int i = 1; i < sample.set.size(); i++ )
+    {
+        if ( sample.set[i] < minY )
+            minY = sample.set[i];
+        if ( sample.set[i] > maxY )
+            maxY = sample.set[i];
+    }
+
+    double minX = sample.value;
+    double maxX = sample.value;
+
+    return QRectF( minX, minY, maxX - minX, maxY - minY );
+}
+
+static inline QRectF qwtBoundingRect( const QwtOHLCSample &sample )
+{
+    const QwtInterval interval = sample.boundingInterval();
+    return QRectF( interval.minValue(), sample.time, interval.width(), 0.0 );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+
+template <class T>
+QRectF qwtBoundingRectT(
+    const QwtSeriesData<T>& series, int from, int to )
+{
+    QRectF boundingRect( 1.0, 1.0, -2.0, -2.0 ); // invalid;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( to < 0 )
+        to = series.size() - 1;
+
+    if ( to < from )
+        return boundingRect;
+
+    int i;
+    for ( i = from; i <= to; i++ )
+    {
+        const QRectF rect = qwtBoundingRect( series.sample( i ) );
+        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
+        {
+            boundingRect = rect;
+            i++;
+            break;
+        }
+    }
+
+    for ( ; i <= to; i++ )
+    {
+        const QRectF rect = qwtBoundingRect( series.sample( i ) );
+        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
+        {
+            boundingRect.setLeft( qMin( boundingRect.left(), rect.left() ) );
+            boundingRect.setRight( qMax( boundingRect.right(), rect.right() ) );
+            boundingRect.setTop( qMin( boundingRect.top(), rect.top() ) );
+            boundingRect.setBottom( qMax( boundingRect.bottom(), rect.bottom() ) );
+        }
+    }
+
+    return boundingRect;
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QPointF> &series, int from, int to )
+{
+    return qwtBoundingRectT<QPointF>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPoint3D> &series, int from, int to )
+{
+    return qwtBoundingRectT<QwtPoint3D>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  The horizontal coordinates represent the azimuth, the
+  vertical coordinates the radius.
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPointPolar> &series, int from, int to )
+{
+    return qwtBoundingRectT<QwtPointPolar>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtIntervalSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtIntervalSample>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtOHLCSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtOHLCSample>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtSetSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtSetSample>( series, from, to );
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtPointSeriesData::QwtPointSeriesData(
+        const QVector<QPointF> &samples ):
+    QwtArraySeriesData<QPointF>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPointSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtPoint3DSeriesData::QwtPoint3DSeriesData(
+        const QVector<QwtPoint3D> &samples ):
+    QwtArraySeriesData<QwtPoint3D>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPoint3DSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtIntervalSeriesData::QwtIntervalSeriesData(
+        const QVector<QwtIntervalSample> &samples ):
+    QwtArraySeriesData<QwtIntervalSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtIntervalSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtSetSeriesData::QwtSetSeriesData(
+        const QVector<QwtSetSample> &samples ):
+    QwtArraySeriesData<QwtSetSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtSetSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtTradingChartData::QwtTradingChartData(
+        const QVector<QwtOHLCSample> &samples ):
+    QwtArraySeriesData<QwtOHLCSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtTradingChartData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
diff --git a/qwt/qwt_series_data.h b/qwt/qwt_series_data.h
new file mode 100644
index 0000000..afd8b1a
--- /dev/null
+++ b/qwt/qwt_series_data.h
@@ -0,0 +1,355 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SERIES_DATA_H
+#define QWT_SERIES_DATA_H 1
+
+#include "qwt_global.h"
+#include "qwt_samples.h"
+#include "qwt_point_3d.h"
+#include "qwt_point_polar.h"
+#include <qvector.h>
+#include <qrect.h>
+
+/*!
+   \brief Abstract interface for iterating over samples
+
+   Qwt offers several implementations of the QwtSeriesData API,
+   but in situations, where data of an application specific format
+   needs to be displayed, without having to copy it, it is recommended
+   to implement an individual data access.
+
+   A subclass of QwtSeriesData<QPointF> must implement: 
+
+   - size()\n 
+     Should return number of data points.
+
+   - sample()\n
+     Should return values x and y values of the sample at specific position
+     as QPointF object.
+
+   - boundingRect()\n 
+     Should return the bounding rectangle of the data series.
+     It is used for autoscaling and might help certain algorithms for displaying
+     the data. You can use qwtBoundingRect() for an implementation
+     but often it is possible to implement a more efficient algorithm 
+     depending on the characteristics of the series.
+     The member d_boundingRect is intended for caching the calculated rectangle.
+    
+*/
+template <typename T>
+class QwtSeriesData
+{
+public:
+    //! Constructor
+    QwtSeriesData();
+
+    //! Destructor
+    virtual ~QwtSeriesData();
+
+    //! \return Number of samples
+    virtual size_t size() const = 0;
+
+    /*!
+      Return a sample
+      \param i Index
+      \return Sample at position i
+     */
+    virtual T sample( size_t i ) const = 0;
+
+    /*!
+       Calculate the bounding rect of all samples
+
+       The bounding rect is necessary for autoscaling and can be used
+       for a couple of painting optimizations.
+
+       qwtBoundingRect(...) offers slow implementations iterating
+       over the samples. For large sets it is recommended to implement
+       something faster f.e. by caching the bounding rectangle.
+
+       \return Bounding rectangle
+     */
+    virtual QRectF boundingRect() const = 0;
+
+    /*!
+       Set a the "rect of interest"
+
+       QwtPlotSeriesItem defines the current area of the plot canvas
+       as "rectangle of interest" ( QwtPlotSeriesItem::updateScaleDiv() ).
+       It can be used to implement different levels of details.
+
+       The default implementation does nothing.
+   
+       \param rect Rectangle of interest
+    */
+    virtual void setRectOfInterest( const QRectF &rect );
+
+protected:
+    //! Can be used to cache a calculated bounding rectangle
+    mutable QRectF d_boundingRect;
+
+private:
+    QwtSeriesData<T> &operator=( const QwtSeriesData<T> & );
+};
+
+template <typename T>
+QwtSeriesData<T>::QwtSeriesData():
+    d_boundingRect( 0.0, 0.0, -1.0, -1.0 )
+{
+}
+
+template <typename T>
+QwtSeriesData<T>::~QwtSeriesData()
+{
+}
+
+template <typename T>
+void QwtSeriesData<T>::setRectOfInterest( const QRectF & )
+{
+}
+
+/*!
+  \brief Template class for data, that is organized as QVector
+
+  QVector uses implicit data sharing and can be
+  passed around as argument efficiently.
+*/
+template <typename T>
+class QwtArraySeriesData: public QwtSeriesData<T>
+{
+public:
+    //! Constructor
+    QwtArraySeriesData();
+
+    /*!
+       Constructor
+       \param samples Array of samples
+    */
+    QwtArraySeriesData( const QVector<T> &samples );
+
+    /*!
+      Assign an array of samples
+      \param samples Array of samples
+    */
+    void setSamples( const QVector<T> &samples );
+
+    //! \return Array of samples
+    const QVector<T> samples() const;
+
+    //! \return Number of samples
+    virtual size_t size() const;
+
+    /*!
+      \return Sample at a specific position
+
+      \param index Index
+      \return Sample at position index
+    */
+    virtual T sample( size_t index ) const;
+
+protected:
+    //! Vector of samples
+    QVector<T> d_samples;
+};
+
+template <typename T>
+QwtArraySeriesData<T>::QwtArraySeriesData()
+{
+}
+
+template <typename T>
+QwtArraySeriesData<T>::QwtArraySeriesData( const QVector<T> &samples ):
+    d_samples( samples )
+{
+}
+
+template <typename T>
+void QwtArraySeriesData<T>::setSamples( const QVector<T> &samples )
+{
+    QwtSeriesData<T>::d_boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_samples = samples;
+}
+
+template <typename T>
+const QVector<T> QwtArraySeriesData<T>::samples() const
+{
+    return d_samples;
+}
+
+template <typename T>
+size_t QwtArraySeriesData<T>::size() const
+{
+    return d_samples.size();
+}
+
+template <typename T>
+T QwtArraySeriesData<T>::sample( size_t i ) const
+{
+    return d_samples[ static_cast<int>( i ) ];
+}
+
+//! Interface for iterating over an array of points
+class QWT_EXPORT QwtPointSeriesData: public QwtArraySeriesData<QPointF>
+{
+public:
+    QwtPointSeriesData(
+        const QVector<QPointF> & = QVector<QPointF>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of 3D points
+class QWT_EXPORT QwtPoint3DSeriesData: public QwtArraySeriesData<QwtPoint3D>
+{
+public:
+    QwtPoint3DSeriesData(
+        const QVector<QwtPoint3D> & = QVector<QwtPoint3D>() );
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of intervals
+class QWT_EXPORT QwtIntervalSeriesData: public QwtArraySeriesData<QwtIntervalSample>
+{
+public:
+    QwtIntervalSeriesData(
+        const QVector<QwtIntervalSample> & = QVector<QwtIntervalSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of samples
+class QWT_EXPORT QwtSetSeriesData: public QwtArraySeriesData<QwtSetSample>
+{
+public:
+    QwtSetSeriesData(
+        const QVector<QwtSetSample> & = QVector<QwtSetSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+/*!
+    Interface for iterating over an array of OHLC samples
+*/
+class QWT_EXPORT QwtTradingChartData: public QwtArraySeriesData<QwtOHLCSample>
+{
+public:
+    QwtTradingChartData(
+        const QVector<QwtOHLCSample> & = QVector<QwtOHLCSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QPointF> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPoint3D> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPointPolar> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtIntervalSample> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtSetSample> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtOHLCSample> &, int from = 0, int to = -1 );
+
+/*!
+    Binary search for a sorted series of samples
+
+    qwtUpperSampleIndex returns the index of sample that is the upper bound
+    of value. Is the the value smaller than the smallest value the return
+    value will be 0. Is the value greater or equal than the largest
+    value the return value will be -1.
+
+  \par Example
+    The following example shows finds a point of curve from an x
+    coordinate
+
+  \verbatim
+#include <qwt_series_data.h>
+#include <qwt_plot_curve.h>
+
+struct compareX
+{
+    inline bool operator()( const double x, const QPointF &pos ) const
+    {
+        return ( x < pos.x() );
+    }
+};
+
+QLineF curveLineAt( const QwtPlotCurve *curve, double x )
+{
+    int index = qwtUpperSampleIndex<QPointF>( 
+        *curve->data(), x, compareX() );
+            
+    if ( index == -1 && 
+        x == curve->sample( curve->dataSize() - 1 ).x() )
+    {   
+        // the last sample is excluded from qwtUpperSampleIndex
+        index = curve->dataSize() - 1;
+    }
+
+    QLineF line; // invalid
+    if ( index > 0 )
+    {
+        line.setP1( curve->sample( index - 1 ) );
+        line.setP2( curve->sample( index ) );
+    }
+
+    return line;
+}
+
+\endverbatim
+
+
+    \param series Series of samples
+    \param value Value
+    \param lessThan Compare operation
+
+    \note The samples must be sorted according to the order specified 
+          by the lessThan object
+
+of the range [begin, end) and returns the position of the one-past-the-last occurrence of value. If no such item is found, returns the position where the item should be inserted.
+ */
+template <typename T, typename LessThan>
+inline int qwtUpperSampleIndex( const QwtSeriesData<T> &series,
+    double value, LessThan lessThan  ) 
+{
+    const int indexMax = series.size() - 1;
+
+    if ( indexMax < 0 || !lessThan( value, series.sample( indexMax ) )  )
+        return -1;
+
+    int indexMin = 0;
+    int n = indexMax;
+
+    while ( n > 0 )
+    {
+        const int half = n >> 1;
+        const int indexMid = indexMin + half;
+
+        if ( lessThan( value, series.sample( indexMid ) ) )
+        {
+            n = half;
+        }
+        else
+        {
+            indexMin = indexMid + 1;
+            n -= half + 1;
+        }
+    }
+
+    return indexMin;
+}
+
+#endif
diff --git a/qwt/qwt_series_store.h b/qwt/qwt_series_store.h
new file mode 100644
index 0000000..cb841a4
--- /dev/null
+++ b/qwt/qwt_series_store.h
@@ -0,0 +1,199 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SERIES_STORE_H
+#define QWT_SERIES_STORE_H
+
+#include "qwt_global.h"
+#include "qwt_series_data.h"
+
+/*!
+  \brief Bridge between QwtSeriesStore and QwtPlotSeriesItem
+
+  QwtAbstractSeriesStore is an abstract interface only
+  to make it possible to isolate the template based methods ( QwtSeriesStore )
+  from the regular methods ( QwtPlotSeriesItem ) to make it possible
+  to derive from QwtPlotSeriesItem without any hassle with templates.
+*/
+class QwtAbstractSeriesStore
+{
+protected:
+    //! Destructor
+    virtual ~QwtAbstractSeriesStore() {}
+
+    //! dataChanged() indicates, that the series has been changed.
+    virtual void dataChanged() = 0;
+
+    /*!
+      Set a the "rectangle of interest" for the stored series
+      \sa QwtSeriesData<T>::setRectOfInterest()
+     */
+    virtual void setRectOfInterest( const QRectF & ) = 0;
+
+    //! \return Bounding rectangle of the stored series
+    virtual QRectF dataRect() const = 0;
+
+    //! \return Number of samples
+    virtual size_t dataSize() const = 0;
+};
+
+/*!
+  \brief Class storing a QwtSeriesData object
+
+  QwtSeriesStore and QwtPlotSeriesItem are intended as base classes for all
+  plot items iterating over a series of samples. Both classes share
+  a virtual base class ( QwtAbstractSeriesStore ) to bridge between them.
+
+  QwtSeriesStore offers the template based part for the plot item API, so
+  that QwtPlotSeriesItem can be derived without any hassle with templates.
+ */
+template <typename T>
+class QwtSeriesStore: public virtual QwtAbstractSeriesStore
+{
+public:
+    /*!
+      \brief Constructor
+      The store contains no series
+    */
+    explicit QwtSeriesStore<T>();
+
+    //! Destructor
+    ~QwtSeriesStore<T>();
+
+    /*!
+      Assign a series of samples
+
+      \param series Data
+      \warning The item takes ownership of the data object, deleting
+               it when its not used anymore.
+    */
+    void setData( QwtSeriesData<T> *series );
+
+    //! \return the the series data
+    QwtSeriesData<T> *data();
+
+    //! \return the the series data
+    const QwtSeriesData<T> *data() const;
+
+    /*!
+        \param index Index
+        \return Sample at position index
+    */
+    T sample( int index ) const;
+
+    /*!
+      \return Number of samples of the series
+      \sa setData(), QwtSeriesData<T>::size()
+    */
+    virtual size_t dataSize() const;
+
+    /*!
+      \return Bounding rectangle of the series
+              or an invalid rectangle, when no series is stored
+
+      \sa QwtSeriesData<T>::boundingRect()
+    */
+    virtual QRectF dataRect() const;
+
+    /*!
+      Set a the "rect of interest" for the series
+
+      \param rect Rectangle of interest
+      \sa QwtSeriesData<T>::setRectOfInterest()
+    */
+    virtual void setRectOfInterest( const QRectF &rect );
+
+    /*!
+      Replace a series without deleting the previous one
+
+      \param series New series
+      \return Previously assigned series
+     */
+    QwtSeriesData<T> *swapData( QwtSeriesData<T> *series );
+
+private:
+    QwtSeriesData<T> *d_series;
+};
+
+template <typename T>
+QwtSeriesStore<T>::QwtSeriesStore():
+    d_series( NULL )
+{
+}
+
+template <typename T>
+QwtSeriesStore<T>::~QwtSeriesStore()
+{
+    delete d_series;
+}
+
+template <typename T>
+inline QwtSeriesData<T> *QwtSeriesStore<T>::data()
+{
+    return d_series;
+}
+
+template <typename T>
+inline const QwtSeriesData<T> *QwtSeriesStore<T>::data() const
+{
+    return d_series;
+}
+
+template <typename T>
+inline T QwtSeriesStore<T>::sample( int index ) const
+{
+    return d_series ? d_series->sample( index ) : T();
+}
+
+template <typename T>
+void QwtSeriesStore<T>::setData( QwtSeriesData<T> *series )
+{
+    if ( d_series != series )
+    {
+        delete d_series;
+        d_series = series;
+        dataChanged();
+    }
+}
+
+template <typename T>
+size_t QwtSeriesStore<T>::dataSize() const
+{
+    if ( d_series == NULL )
+        return 0;
+
+    return d_series->size();
+}
+
+template <typename T>
+QRectF QwtSeriesStore<T>::dataRect() const
+{
+    if ( d_series == NULL )
+        return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid
+
+    return d_series->boundingRect();
+}
+
+template <typename T>
+void QwtSeriesStore<T>::setRectOfInterest( const QRectF &rect )
+{
+    if ( d_series )
+        d_series->setRectOfInterest( rect );
+}
+
+template <typename T>
+QwtSeriesData<T>* QwtSeriesStore<T>::swapData( QwtSeriesData<T> *series )
+{
+    QwtSeriesData<T> * swappedSeries = d_series;
+    d_series = series;
+
+    return swappedSeries;
+}
+
+#endif
diff --git a/qwt/qwt_slider.cpp b/qwt/qwt_slider.cpp
new file mode 100644
index 0000000..e4e3942
--- /dev/null
+++ b/qwt/qwt_slider.cpp
@@ -0,0 +1,991 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_slider.h"
+#include "qwt_painter.h"
+#include "qwt_scale_draw.h"
+#include "qwt_scale_map.h"
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static QSize qwtHandleSize( const QSize &size, 
+    Qt::Orientation orientation, bool hasTrough )
+{
+    QSize handleSize = size;
+
+    if ( handleSize.isEmpty() )
+    {
+        const int handleThickness = 16;
+        handleSize.setWidth( 2 * handleThickness );
+        handleSize.setHeight( handleThickness );
+
+        if ( !hasTrough )
+            handleSize.transpose();
+
+        if ( orientation == Qt::Vertical )
+            handleSize.transpose();
+    }
+
+    return handleSize;
+}
+
+static QwtScaleDraw::Alignment qwtScaleDrawAlignment( 
+    Qt::Orientation orientation, QwtSlider::ScalePosition scalePos )
+{
+    QwtScaleDraw::Alignment align;
+
+    if ( orientation == Qt::Vertical )
+    {
+        // NoScale lays out like Left
+        if ( scalePos == QwtSlider::LeadingScale )
+            align = QwtScaleDraw::RightScale;
+        else
+            align = QwtScaleDraw::LeftScale;
+    }
+    else
+    {
+        // NoScale lays out like Bottom
+        if ( scalePos == QwtSlider::TrailingScale )
+            align = QwtScaleDraw::TopScale;
+        else
+            align = QwtScaleDraw::BottomScale;
+    }
+
+    return align;
+}
+
+class QwtSlider::PrivateData
+{
+public:
+    PrivateData():
+        repeatTimerId( 0 ),
+        updateInterval( 150 ),
+        stepsIncrement( 0 ),
+        pendingValueChange( false ),
+        borderWidth( 2 ),
+        spacing( 4 ),
+        scalePosition( QwtSlider::TrailingScale ),
+        hasTrough( true ),
+        hasGroove( false ),
+        mouseOffset( 0 )
+    {
+    }
+
+    int repeatTimerId;
+    bool timerTick;
+    int updateInterval;
+    int stepsIncrement;
+    bool pendingValueChange;
+
+    QRect sliderRect;
+
+    QSize handleSize;
+    int borderWidth;
+    int spacing;
+
+    Qt::Orientation orientation;
+    QwtSlider::ScalePosition scalePosition;
+
+    bool hasTrough;
+    bool hasGroove;
+
+    int mouseOffset;
+
+    mutable QSize sizeHintCache;
+};
+/*!
+  Construct vertical slider in QwtSlider::Trough style
+  with a scale to the left. 
+
+  The scale is initialized to [0.0, 100.0] and the value set to 0.0.
+
+  \param parent Parent widget
+
+  \sa setOrientation(), setScalePosition(), setBackgroundStyle()
+*/
+QwtSlider::QwtSlider( QWidget *parent ):
+    QwtAbstractSlider( parent )
+{
+    initSlider( Qt::Vertical );
+}
+
+/*!
+  Construct a slider in QwtSlider::Trough style
+
+  When orientation is Qt::Vertical the scale will be aligned to
+  the left - otherwise at the the top of the slider.
+
+  The scale is initialized to [0.0, 100.0] and the value set to 0.0.
+
+  \param parent Parent widget
+  \param orientation Orientation of the slider. 
+*/
+QwtSlider::QwtSlider( Qt::Orientation orientation, QWidget *parent ):
+    QwtAbstractSlider( parent )
+{
+    initSlider( orientation );
+}
+
+//! Destructor
+QwtSlider::~QwtSlider()
+{
+    delete d_data;
+}
+
+void QwtSlider::initSlider( Qt::Orientation orientation )
+{
+    if ( orientation == Qt::Vertical )
+        setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
+    else
+        setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+
+    d_data = new QwtSlider::PrivateData;
+
+    d_data->orientation = orientation;
+
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
+    scaleDraw()->setLength( 100 );
+
+    setScale( 0.0, 100.0 );
+    setValue( 0.0 );
+}
+
+/*!
+  \brief Set the orientation.
+  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
+
+  \sa orientation(), scalePosition()
+*/
+void QwtSlider::setOrientation( Qt::Orientation orientation )
+{
+    if ( orientation == d_data->orientation )
+        return;
+
+    d_data->orientation = orientation;
+
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtSlider::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Change the position of the scale
+  \param scalePosition Position of the scale.
+
+  \sa ScalePosition, scalePosition()
+*/
+void QwtSlider::setScalePosition( ScalePosition scalePosition )
+{
+    if ( d_data->scalePosition == scalePosition )
+        return;
+
+    d_data->scalePosition = scalePosition;
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( d_data->orientation, scalePosition ) );
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*! 
+  \return Position of the scale
+  \sa setScalePosition()
+ */
+QwtSlider::ScalePosition QwtSlider::scalePosition() const
+{
+    return d_data->scalePosition;
+}
+
+/*!
+  \brief Change the slider's border width
+
+  The border width is used for drawing the slider handle and the
+  trough.
+
+  \param width Border width
+  \sa borderWidth()
+*/
+void QwtSlider::setBorderWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    if ( width != d_data->borderWidth )
+    {
+        d_data->borderWidth = width;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return the border width.
+  \sa setBorderWidth()
+*/
+int QwtSlider::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Change the spacing between trough and scale
+
+  A spacing of 0 means, that the backbone of the scale is covered
+  by the trough.
+
+  The default setting is 4 pixels.
+
+  \param spacing Number of pixels
+  \sa spacing();
+*/
+void QwtSlider::setSpacing( int spacing )
+{
+    if ( spacing <= 0 )
+        spacing = 0;
+
+    if ( spacing != d_data->spacing  )
+    {
+        d_data->spacing = spacing;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return Number of pixels between slider and scale
+  \sa setSpacing()
+*/
+int QwtSlider::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Set the slider's handle size
+
+  When the size is empty the slider handle will be painted with a
+  default size depending on its orientation() and backgroundStyle().
+
+  \param size New size
+
+  \sa handleSize()
+*/
+void QwtSlider::setHandleSize( const QSize &size )
+{
+    if ( size != d_data->handleSize )
+    {
+        d_data->handleSize = size;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return Size of the handle.
+  \sa setHandleSize()
+*/
+QSize QwtSlider::handleSize() const
+{
+    return d_data->handleSize;
+}
+
+/*!
+  \brief Set a scale draw
+
+  For changing the labels of the scales, it
+  is necessary to derive from QwtScaleDraw and
+  overload QwtScaleDraw::label().
+
+  \param scaleDraw ScaleDraw object, that has to be created with
+                   new and will be deleted in ~QwtSlider() or the next
+                   call of setScaleDraw().
+
+  \sa scaleDraw()
+*/
+void QwtSlider::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    const QwtScaleDraw *previousScaleDraw = this->scaleDraw();
+    if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
+        return;
+
+    if ( previousScaleDraw )
+        scaleDraw->setAlignment( previousScaleDraw->alignment() );
+
+    setAbstractScaleDraw( scaleDraw );
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \return the scale draw of the slider
+  \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtSlider::scaleDraw() const
+{
+    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  \return the scale draw of the slider
+  \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtSlider::scaleDraw()
+{
+    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+//! Notify changed scale
+void QwtSlider::scaleChange()
+{
+    QwtAbstractSlider::scaleChange();
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \brief Specify the update interval for automatic scrolling
+
+  The minimal accepted value is 50 ms.
+
+  \param interval Update interval in milliseconds
+
+  \sa setUpdateInterval()
+*/
+void QwtSlider::setUpdateInterval( int interval )
+{
+    d_data->updateInterval = qMax( interval, 50 );
+}
+
+/*!
+  \return Update interval in milliseconds for automatic scrolling
+  \sa setUpdateInterval()
+ */
+int QwtSlider::updateInterval() const
+{
+    return d_data->updateInterval;
+}
+
+/*!
+   Draw the slider into the specified rectangle.
+
+   \param painter Painter
+   \param sliderRect Bounding rectangle of the slider
+*/
+void QwtSlider::drawSlider( 
+    QPainter *painter, const QRect &sliderRect ) const
+{
+    QRect innerRect( sliderRect );
+
+    if ( d_data->hasTrough )
+    {
+        const int bw = d_data->borderWidth;
+        innerRect = sliderRect.adjusted( bw, bw, -bw, -bw );
+
+        painter->fillRect( innerRect, palette().brush( QPalette::Mid ) );
+        qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL );
+    }
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    if ( d_data->hasGroove )
+    {
+        const int slotExtent = 4;
+        const int slotMargin = 4;
+
+        QRect slotRect; 
+        if ( orientation() == Qt::Horizontal )
+        {
+            int slotOffset = qMax( 1, handleSize.width() / 2 - slotMargin );
+            int slotHeight = slotExtent + ( innerRect.height() % 2 );
+
+            slotRect.setWidth( innerRect.width() - 2 * slotOffset );
+            slotRect.setHeight( slotHeight );
+        }
+        else
+        {
+            int slotOffset = qMax( 1, handleSize.height() / 2 - slotMargin );
+            int slotWidth = slotExtent + ( innerRect.width() % 2 );
+
+            slotRect.setWidth( slotWidth );
+            slotRect.setHeight( innerRect.height() - 2 * slotOffset );
+
+        }
+
+        slotRect.moveCenter( innerRect.center() );
+
+        QBrush brush = palette().brush( QPalette::Dark );
+        qDrawShadePanel( painter, slotRect, palette(), true, 1 , &brush );
+    }
+
+    if ( isValid() )
+        drawHandle( painter, handleRect(), transform( value() ) );
+}
+
+/*!
+  Draw the thumb at a position
+
+  \param painter Painter
+  \param handleRect Bounding rectangle of the handle
+  \param pos Position of the handle marker in widget coordinates
+*/
+void QwtSlider::drawHandle( QPainter *painter, 
+    const QRect &handleRect, int pos ) const
+{
+    const int bw = d_data->borderWidth;
+
+    qDrawShadePanel( painter, 
+        handleRect, palette(), false, bw,
+        &palette().brush( QPalette::Button ) );
+
+    pos++; // shade line points one pixel below
+    if ( orientation() == Qt::Horizontal )
+    {
+        qDrawShadeLine( painter, pos, handleRect.top() + bw,
+            pos, handleRect.bottom() - bw, palette(), true, 1 );
+    }
+    else // Vertical
+    {
+        qDrawShadeLine( painter, handleRect.left() + bw, pos,
+            handleRect.right() - bw, pos, palette(), true, 1 );
+    }
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when handleRect() contains pos 
+  \sa scrolledTo()
+*/
+bool QwtSlider::isScrollPosition( const QPoint &pos ) const
+{
+    if ( handleRect().contains( pos ) )
+    {
+        const double v = ( orientation() == Qt::Horizontal ) 
+            ? pos.x() : pos.y();
+
+        d_data->mouseOffset = v - transform( value() );
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the
+         slider handle.
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtSlider::scrolledTo( const QPoint &pos ) const
+{
+    int p = ( orientation() == Qt::Horizontal ) 
+        ? pos.x() : pos.y();
+
+    p -= d_data->mouseOffset;
+
+    int min = transform( lowerBound() );
+    int max = transform( upperBound() );
+    if ( min > max )
+        qSwap( min, max );
+
+    p = qBound( min, p, max );
+
+    return invTransform( p );
+}
+
+/*!
+   Mouse press event handler
+   \param event Mouse event
+*/
+void QwtSlider::mousePressEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    const QPoint pos = event->pos();
+
+    if ( isValid() && d_data->sliderRect.contains( pos ) )
+    {
+        if ( !handleRect().contains( pos ) )
+        {
+            const int markerPos = transform( value() );
+
+            d_data->stepsIncrement = pageSteps();
+
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( pos.x() < markerPos )
+                    d_data->stepsIncrement = -d_data->stepsIncrement;
+            }
+            else
+            {
+                if ( pos.y() < markerPos )
+                    d_data->stepsIncrement = -d_data->stepsIncrement;
+            }
+
+            if ( isInverted() )
+                d_data->stepsIncrement = -d_data->stepsIncrement;
+
+            d_data->timerTick = false;
+            d_data->repeatTimerId = startTimer( qMax( 250, 2 * updateInterval() ) );
+
+            return;
+        }
+    }
+
+    QwtAbstractSlider::mousePressEvent( event );
+}
+
+/*!
+   Mouse release event handler
+   \param event Mouse event
+*/
+void QwtSlider::mouseReleaseEvent( QMouseEvent *event )
+{
+    if ( d_data->repeatTimerId > 0 )
+    {
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = 0;
+        d_data->timerTick = false;
+        d_data->stepsIncrement = 0;
+    }
+
+    if ( d_data->pendingValueChange )
+    {
+        d_data->pendingValueChange = false;
+        Q_EMIT valueChanged( value() );
+    }
+
+    QwtAbstractSlider::mouseReleaseEvent( event );
+}
+
+/*!
+   Timer event handler
+
+   Handles the timer, when the mouse stays pressed
+   inside the sliderRect().
+
+   \param event Mouse event
+*/  
+void QwtSlider::timerEvent( QTimerEvent *event )
+{
+    if ( event->timerId() != d_data->repeatTimerId )
+    {
+        QwtAbstractSlider::timerEvent( event );
+        return;
+    }
+
+    if ( !isValid() )
+    {
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = 0;
+        return;
+    }
+
+    const double v = value();
+    incrementValue( d_data->stepsIncrement );
+
+    if ( v != value() )
+    {
+        if ( isTracking() )
+            Q_EMIT valueChanged( value() );
+        else
+            d_data->pendingValueChange = true;
+
+        Q_EMIT sliderMoved( value() );
+    }
+
+    if ( !d_data->timerTick )
+    {
+        // restart the timer with a shorter interval
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = startTimer( updateInterval() );
+        
+        d_data->timerTick = true;
+    }   
+}
+
+/*!
+   Qt paint event handler
+   \param event Paint event
+*/
+void QwtSlider::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        if ( !d_data->sliderRect.contains( event->rect() ) )
+            scaleDraw()->draw( &painter, palette() );
+    }
+
+    drawSlider( &painter, d_data->sliderRect );
+
+    if ( hasFocus() )
+        QwtPainter::drawFocusRect( &painter, this, d_data->sliderRect );
+}
+
+/*!
+   Qt resize event handler
+   \param event Resize event
+*/
+void QwtSlider::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+
+    layoutSlider( false );
+}
+
+/*!
+   Handles QEvent::StyleChange and QEvent::FontChange events
+   \param event Change event
+*/
+void QwtSlider::changeEvent( QEvent *event )
+{
+    if ( event->type() == QEvent::StyleChange || 
+        event->type() == QEvent::FontChange )
+    {
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+
+    QwtAbstractSlider::changeEvent( event );
+}
+
+/*!
+  Recalculate the slider's geometry and layout based on
+  the current geometry and fonts.
+
+  \param update_geometry  notify the layout system and call update
+         to redraw the scale
+*/
+void QwtSlider::layoutSlider( bool update_geometry )
+{
+    int bw = 0;
+    if ( d_data->hasTrough )
+        bw = d_data->borderWidth;
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    QRect sliderRect = contentsRect();
+
+    /*
+       The marker line of the handle needs to be aligned to
+       the scale. But the marker is in the center 
+       and we need space enough to display the rest of the handle.
+
+       But the scale itself usually needs margins for displaying
+       the tick labels, that also might needs space beyond the
+       backbone.
+
+       Now it depends on what needs more margins. If it is the
+       slider the scale gets shrunk, otherwise the slider.
+     */
+
+    int scaleMargin = 0;
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+
+        scaleMargin = qMax( d1, d2 ) - bw;
+    }
+
+    int scaleX, scaleY, scaleLength;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const int handleMargin = handleSize.width() / 2 - 1;
+        if ( scaleMargin > handleMargin )
+        {
+            int off = scaleMargin - handleMargin;
+            sliderRect.adjust( off, 0, -off, 0 );
+        }
+
+        scaleX = sliderRect.left() + bw + handleSize.width() / 2 - 1;
+        scaleLength = sliderRect.width() - handleSize.width();
+    }
+    else
+    {
+        int handleMargin = handleSize.height() / 2 - 1;
+        if ( scaleMargin > handleMargin )
+        {
+            int off = scaleMargin - handleMargin;
+            sliderRect.adjust( 0, off, 0, -off );
+        }
+
+        scaleY = sliderRect.top() + bw + handleSize.height() / 2 - 1;
+        scaleLength = sliderRect.height() - handleSize.height();
+    }
+
+    scaleLength -= 2 * bw;
+
+    // now align slider and scale according to the ScalePosition
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const int h = handleSize.height() + 2 * bw;
+
+        if ( d_data->scalePosition == QwtSlider::TrailingScale )
+        {
+            sliderRect.setTop( sliderRect.bottom() + 1 - h );
+            scaleY = sliderRect.top() - d_data->spacing;
+        }
+        else
+        {
+            sliderRect.setHeight( h );
+            scaleY = sliderRect.bottom() + 1 + d_data->spacing;
+        }
+    }
+    else // Qt::Vertical
+    {
+        const int w = handleSize.width() + 2 * bw;
+
+        if ( d_data->scalePosition == QwtSlider::LeadingScale )
+        {
+            sliderRect.setWidth( w );
+            scaleX = sliderRect.right() + 1 + d_data->spacing;
+        }
+        else
+        {
+            sliderRect.setLeft( sliderRect.right() + 1 - w );
+            scaleX = sliderRect.left() - d_data->spacing;
+        }
+    }
+
+    d_data->sliderRect = sliderRect;
+
+    scaleDraw()->move( scaleX, scaleY );
+    scaleDraw()->setLength( scaleLength );
+
+    if ( update_geometry )
+    {
+        d_data->sizeHintCache = QSize(); // invalidate
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  En/Disable the trough
+
+  The slider can be cutomized by showing a trough for the
+  handle.
+
+  \param on When true, the groove is visible
+  \sa hasTrough(), setGroove()
+ */
+void QwtSlider::setTrough( bool on )
+{
+    if ( d_data->hasTrough != on )
+    {
+        d_data->hasTrough = on;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return True, when the trough is visisble
+  \sa setTrough(), hasGroove()
+ */
+bool QwtSlider::hasTrough() const
+{
+    return d_data->hasTrough;
+}
+
+/*!
+  En/Disable the groove
+
+  The slider can be cutomized by showing a groove for the
+  handle.
+
+  \param on When true, the groove is visible
+  \sa hasGroove(), setThrough()
+ */
+void QwtSlider::setGroove( bool on )
+{
+    if ( d_data->hasGroove != on )
+    {
+        d_data->hasGroove = on;
+        
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return True, when the groove is visisble
+  \sa setGroove(), hasTrough()
+ */
+bool QwtSlider::hasGroove() const
+{
+    return d_data->hasGroove;
+} 
+
+/*!
+  \return minimumSizeHint()
+*/
+QSize QwtSlider::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtSlider::minimumSizeHint() const
+{
+    if ( !d_data->sizeHintCache.isEmpty() )
+        return d_data->sizeHintCache;
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    int bw = 0;
+    if ( d_data->hasTrough )
+        bw = d_data->borderWidth;
+
+    int sliderLength = 0; 
+    int scaleExtent = 0;
+
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+
+        const int scaleBorderDist = 2 * ( qMax( d1, d2 ) - bw );
+
+        int handleBorderDist;
+        if ( d_data->orientation == Qt::Horizontal )
+            handleBorderDist = handleSize.width();
+        else
+            handleBorderDist = handleSize.height();
+
+        sliderLength = scaleDraw()->minLength( font() );
+        if ( handleBorderDist > scaleBorderDist )
+        {
+            // We need additional space for the overlapping handle
+            sliderLength += handleBorderDist - scaleBorderDist;
+        }
+
+        scaleExtent += d_data->spacing;
+        scaleExtent += qCeil( scaleDraw()->extent( font() ) );
+    }
+
+    sliderLength = qMax( sliderLength, 84 ); // from QSlider
+
+    int w = 0;
+    int h = 0;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        w = sliderLength;
+        h = handleSize.height() + 2 * bw + scaleExtent;
+    }
+    else
+    {
+        w = handleSize.width() + 2 * bw + scaleExtent;
+        h = sliderLength;
+    }
+
+    // finally add margins
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+
+    w += left + right;
+    h += top + bottom;
+
+    d_data->sizeHintCache = QSize( w, h );
+    return d_data->sizeHintCache;
+}
+
+/*!
+   \return Bounding rectangle of the slider handle
+ */
+QRect QwtSlider::handleRect() const
+{
+    if ( !isValid() )
+        return QRect();
+
+    const int markerPos = transform( value() );
+
+    QPoint center = d_data->sliderRect.center();
+    if ( d_data->orientation == Qt::Horizontal )
+        center.setX( markerPos );
+    else
+        center.setY( markerPos );
+
+    QRect rect;
+    rect.setSize( qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough ) );
+    rect.moveCenter( center );
+
+    return rect;
+}
+
+/*!
+ \return Bounding rectangle of the slider - without the scale
+ */
+QRect QwtSlider::sliderRect() const
+{
+    return d_data->sliderRect;
+}
diff --git a/qwt/qwt_slider.h b/qwt/qwt_slider.h
new file mode 100644
index 0000000..d1b36ba
--- /dev/null
+++ b/qwt/qwt_slider.h
@@ -0,0 +1,130 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SLIDER_H
+#define QWT_SLIDER_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_slider.h"
+
+class QwtScaleDraw;
+
+/*!
+  \brief The Slider Widget
+
+  QwtSlider is a slider widget which operates on an interval
+  of type double. Its position is related to a scale showing
+  the current value.
+
+  The slider can be customized by having a through, a groove - or both.
+
+  \image html sliders.png
+*/
+
+class QWT_EXPORT QwtSlider: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS( ScalePosition BackgroundStyle )
+
+    Q_PROPERTY( Qt::Orientation orientation
+                READ orientation WRITE setOrientation )
+    Q_PROPERTY( ScalePosition scalePosition READ scalePosition
+        WRITE setScalePosition )
+
+    Q_PROPERTY( bool trough READ hasTrough WRITE setTrough )
+    Q_PROPERTY( bool groove READ hasGroove WRITE setGroove )
+
+    Q_PROPERTY( QSize handleSize READ handleSize WRITE setHandleSize )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int spacing READ spacing WRITE setSpacing )
+
+public:
+
+    /*!
+      Position of the scale
+      \sa QwtSlider(), setScalePosition(), setOrientation()
+     */
+    enum ScalePosition
+    {
+        //! The slider has no scale
+        NoScale,
+
+        //! The scale is right of a vertical or below a horizontal slider
+        LeadingScale,
+
+        //! The scale is left of a vertical or above a horizontal slider
+        TrailingScale
+    };
+
+    explicit QwtSlider( QWidget *parent = NULL );
+    explicit QwtSlider( Qt::Orientation, QWidget *parent = NULL );
+
+    virtual ~QwtSlider();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    void setScalePosition( ScalePosition );
+    ScalePosition scalePosition() const;
+
+    void setTrough( bool );
+    bool hasTrough() const;
+
+    void setGroove( bool );
+    bool hasGroove() const;
+
+    void setHandleSize( const QSize & );
+    QSize handleSize() const;
+
+    void setBorderWidth( int bw );
+    int borderWidth() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+
+    void setUpdateInterval( int );
+    int updateInterval() const;
+
+protected:
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+    virtual void drawSlider ( QPainter *, const QRect & ) const;
+    virtual void drawHandle( QPainter *, const QRect &, int pos ) const;
+
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+    virtual void paintEvent ( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+    virtual void timerEvent( QTimerEvent * );
+
+    virtual void scaleChange();
+
+    QRect sliderRect() const;
+    QRect handleRect() const;
+
+private:
+    QwtScaleDraw *scaleDraw();
+
+    void layoutSlider( bool );
+    void initSlider( Qt::Orientation );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_spline.cpp b/qwt/qwt_spline.cpp
new file mode 100644
index 0000000..5952b18
--- /dev/null
+++ b/qwt/qwt_spline.cpp
@@ -0,0 +1,384 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_spline.h"
+#include "qwt_math.h"
+
+class QwtSpline::PrivateData
+{
+public:
+    PrivateData():
+        splineType( QwtSpline::Natural )
+    {
+    }
+
+    QwtSpline::SplineType splineType;
+
+    // coefficient vectors
+    QVector<double> a;
+    QVector<double> b;
+    QVector<double> c;
+
+    // control points
+    QPolygonF points;
+};
+
+static int lookup( double x, const QPolygonF &values )
+{
+#if 0
+//qLowerBound/qHigherBound ???
+#endif
+    int i1;
+    const int size = values.size();
+
+    if ( x <= values[0].x() )
+        i1 = 0;
+    else if ( x >= values[size - 2].x() )
+        i1 = size - 2;
+    else
+    {
+        i1 = 0;
+        int i2 = size - 2;
+        int i3 = 0;
+
+        while ( i2 - i1 > 1 )
+        {
+            i3 = i1 + ( ( i2 - i1 ) >> 1 );
+
+            if ( values[i3].x() > x )
+                i2 = i3;
+            else
+                i1 = i3;
+        }
+    }
+    return i1;
+}
+
+//! Constructor
+QwtSpline::QwtSpline()
+{
+    d_data = new PrivateData;
+}
+
+/*!
+   Copy constructor
+   \param other Spline used for initialization
+*/
+QwtSpline::QwtSpline( const QwtSpline& other )
+{
+    d_data = new PrivateData( *other.d_data );
+}
+
+/*!
+   Assignment operator
+   \param other Spline used for initialization
+   \return *this
+*/
+QwtSpline &QwtSpline::operator=( const QwtSpline & other )
+{
+    *d_data = *other.d_data;
+    return *this;
+}
+
+//! Destructor
+QwtSpline::~QwtSpline()
+{
+    delete d_data;
+}
+
+/*!
+   Select the algorithm used for calculating the spline
+
+   \param splineType Spline type
+   \sa splineType()
+*/
+void QwtSpline::setSplineType( SplineType splineType )
+{
+    d_data->splineType = splineType;
+}
+
+/*!
+   \return the spline type
+   \sa setSplineType()
+*/
+QwtSpline::SplineType QwtSpline::splineType() const
+{
+    return d_data->splineType;
+}
+
+/*!
+  \brief Calculate the spline coefficients
+
+  Depending on the value of \a periodic, this function
+  will determine the coefficients for a natural or a periodic
+  spline and store them internally.
+
+  \param points Points
+  \return true if successful
+  \warning The sequence of x (but not y) values has to be strictly monotone
+           increasing, which means <code>points[i].x() < points[i+1].x()</code>.
+       If this is not the case, the function will return false
+*/
+bool QwtSpline::setPoints( const QPolygonF& points )
+{
+    const int size = points.size();
+    if ( size <= 2 )
+    {
+        reset();
+        return false;
+    }
+
+    d_data->points = points;
+
+    d_data->a.resize( size - 1 );
+    d_data->b.resize( size - 1 );
+    d_data->c.resize( size - 1 );
+
+    bool ok;
+    if ( d_data->splineType == Periodic )
+        ok = buildPeriodicSpline( points );
+    else
+        ok = buildNaturalSpline( points );
+
+    if ( !ok )
+        reset();
+
+    return ok;
+}
+
+/*!
+   \return Points, that have been by setPoints()
+*/
+QPolygonF QwtSpline::points() const
+{
+    return d_data->points;
+}
+
+//! \return A coefficients
+const QVector<double> &QwtSpline::coefficientsA() const
+{
+    return d_data->a;
+}
+
+//! \return B coefficients
+const QVector<double> &QwtSpline::coefficientsB() const
+{
+    return d_data->b;
+}
+
+//! \return C coefficients
+const QVector<double> &QwtSpline::coefficientsC() const
+{
+    return d_data->c;
+}
+
+
+//! Free allocated memory and set size to 0
+void QwtSpline::reset()
+{
+    d_data->a.resize( 0 );
+    d_data->b.resize( 0 );
+    d_data->c.resize( 0 );
+    d_data->points.resize( 0 );
+}
+
+//! True if valid
+bool QwtSpline::isValid() const
+{
+    return d_data->a.size() > 0;
+}
+
+/*!
+  Calculate the interpolated function value corresponding
+  to a given argument x.
+
+  \param x Coordinate
+  \return Interpolated coordinate
+*/
+double QwtSpline::value( double x ) const
+{
+    if ( d_data->a.size() == 0 )
+        return 0.0;
+
+    const int i = lookup( x, d_data->points );
+
+    const double delta = x - d_data->points[i].x();
+    return( ( ( ( d_data->a[i] * delta ) + d_data->b[i] )
+        * delta + d_data->c[i] ) * delta + d_data->points[i].y() );
+}
+
+/*!
+  \brief Determines the coefficients for a natural spline
+  \return true if successful
+*/
+bool QwtSpline::buildNaturalSpline( const QPolygonF &points )
+{
+    int i;
+
+    const QPointF *p = points.data();
+    const int size = points.size();
+
+    double *a = d_data->a.data();
+    double *b = d_data->b.data();
+    double *c = d_data->c.data();
+
+    //  set up tridiagonal equation system; use coefficient
+    //  vectors as temporary buffers
+    QVector<double> h( size - 1 );
+    for ( i = 0; i < size - 1; i++ )
+    {
+        h[i] = p[i+1].x() - p[i].x();
+        if ( h[i] <= 0 )
+            return false;
+    }
+
+    QVector<double> d( size - 1 );
+    double dy1 = ( p[1].y() - p[0].y() ) / h[0];
+    for ( i = 1; i < size - 1; i++ )
+    {
+        b[i] = c[i] = h[i];
+        a[i] = 2.0 * ( h[i-1] + h[i] );
+
+        const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i];
+        d[i] = 6.0 * ( dy1 - dy2 );
+        dy1 = dy2;
+    }
+
+    //
+    // solve it
+    //
+
+    // L-U Factorization
+    for ( i = 1; i < size - 2; i++ )
+    {
+        c[i] /= a[i];
+        a[i+1] -= b[i] * c[i];
+    }
+
+    // forward elimination
+    QVector<double> s( size );
+    s[1] = d[1];
+    for ( i = 2; i < size - 1; i++ )
+        s[i] = d[i] - c[i-1] * s[i-1];
+
+    // backward elimination
+    s[size - 2] = - s[size - 2] / a[size - 2];
+    for ( i = size - 3; i > 0; i-- )
+        s[i] = - ( s[i] + b[i] * s[i+1] ) / a[i];
+    s[size - 1] = s[0] = 0.0;
+
+    //
+    // Finally, determine the spline coefficients
+    //
+    for ( i = 0; i < size - 1; i++ )
+    {
+        a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] );
+        b[i] = 0.5 * s[i];
+        c[i] = ( p[i+1].y() - p[i].y() ) / h[i]
+            - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0;
+    }
+
+    return true;
+}
+
+/*!
+  \brief Determines the coefficients for a periodic spline
+  \return true if successful
+*/
+bool QwtSpline::buildPeriodicSpline( const QPolygonF &points )
+{
+    int i;
+
+    const QPointF *p = points.data();
+    const int size = points.size();
+
+    double *a = d_data->a.data();
+    double *b = d_data->b.data();
+    double *c = d_data->c.data();
+
+    QVector<double> d( size - 1 );
+    QVector<double> h( size - 1 );
+    QVector<double> s( size );
+
+    //
+    //  setup equation system; use coefficient
+    //  vectors as temporary buffers
+    //
+    for ( i = 0; i < size - 1; i++ )
+    {
+        h[i] = p[i+1].x() - p[i].x();
+        if ( h[i] <= 0.0 )
+            return false;
+    }
+
+    const int imax = size - 2;
+    double htmp = h[imax];
+    double dy1 = ( p[0].y() - p[imax].y() ) / htmp;
+    for ( i = 0; i <= imax; i++ )
+    {
+        b[i] = c[i] = h[i];
+        a[i] = 2.0 * ( htmp + h[i] );
+        const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i];
+        d[i] = 6.0 * ( dy1 - dy2 );
+        dy1 = dy2;
+        htmp = h[i];
+    }
+
+    //
+    // solve it
+    //
+
+    // L-U Factorization
+    a[0] = qSqrt( a[0] );
+    c[0] = h[imax] / a[0];
+    double sum = 0;
+
+    for ( i = 0; i < imax - 1; i++ )
+    {
+        b[i] /= a[i];
+        if ( i > 0 )
+            c[i] = - c[i-1] * b[i-1] / a[i];
+        a[i+1] = qSqrt( a[i+1] - qwtSqr( b[i] ) );
+        sum += qwtSqr( c[i] );
+    }
+    b[imax-1] = ( b[imax-1] - c[imax-2] * b[imax-2] ) / a[imax-1];
+    a[imax] = qSqrt( a[imax] - qwtSqr( b[imax-1] ) - sum );
+
+
+    // forward elimination
+    s[0] = d[0] / a[0];
+    sum = 0;
+    for ( i = 1; i < imax; i++ )
+    {
+        s[i] = ( d[i] - b[i-1] * s[i-1] ) / a[i];
+        sum += c[i-1] * s[i-1];
+    }
+    s[imax] = ( d[imax] - b[imax-1] * s[imax-1] - sum ) / a[imax];
+
+
+    // backward elimination
+    s[imax] = - s[imax] / a[imax];
+    s[imax-1] = -( s[imax-1] + b[imax-1] * s[imax] ) / a[imax-1];
+    for ( i = imax - 2; i >= 0; i-- )
+        s[i] = - ( s[i] + b[i] * s[i+1] + c[i] * s[imax] ) / a[i];
+
+    //
+    // Finally, determine the spline coefficients
+    //
+    s[size-1] = s[0];
+    for ( i = 0; i < size - 1; i++ )
+    {
+        a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] );
+        b[i] = 0.5 * s[i];
+        c[i] = ( p[i+1].y() - p[i].y() )
+            / h[i] - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0;
+    }
+
+    return true;
+}
diff --git a/qwt/qwt_spline.h b/qwt/qwt_spline.h
new file mode 100644
index 0000000..802e1da
--- /dev/null
+++ b/qwt/qwt_spline.h
@@ -0,0 +1,101 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SPLINE_H
+#define QWT_SPLINE_H
+
+#include "qwt_global.h"
+#include <qpolygon.h>
+#include <qvector.h>
+
+/*!
+  \brief A class for spline interpolation
+
+  The QwtSpline class is used for cubical spline interpolation.
+  Two types of splines, natural and periodic, are supported.
+
+  \par Usage:
+  <ol>
+  <li>First call setPoints() to determine the spline coefficients
+      for a tabulated function y(x).
+  <li>After the coefficients have been set up, the interpolated
+      function value for an argument x can be determined by calling
+      QwtSpline::value().
+  </ol>
+
+  \par Example:
+  \code
+#include <qwt_spline.h>
+
+QPolygonF interpolate(const QPolygonF& points, int numValues)
+{
+    QwtSpline spline;
+    if ( !spline.setPoints(points) )
+        return points;
+
+    QPolygonF interpolatedPoints(numValues);
+
+    const double delta =
+        (points[numPoints - 1].x() - points[0].x()) / (points.size() - 1);
+    for(i = 0; i < points.size(); i++)  / interpolate
+    {
+        const double x = points[0].x() + i * delta;
+        interpolatedPoints[i].setX(x);
+        interpolatedPoints[i].setY(spline.value(x));
+    }
+    return interpolatedPoints;
+}
+  \endcode
+*/
+
+class QWT_EXPORT QwtSpline
+{
+public:
+    //! Spline type
+    enum SplineType
+    {
+        //! A natural spline
+        Natural,
+
+        //! A periodic spline
+        Periodic
+    };
+
+    QwtSpline();
+    QwtSpline( const QwtSpline & );
+
+    ~QwtSpline();
+
+    QwtSpline &operator=( const QwtSpline & );
+
+    void setSplineType( SplineType );
+    SplineType splineType() const;
+
+    bool setPoints( const QPolygonF& points );
+    QPolygonF points() const;
+
+    void reset();
+
+    bool isValid() const;
+    double value( double x ) const;
+
+    const QVector<double> &coefficientsA() const;
+    const QVector<double> &coefficientsB() const;
+    const QVector<double> &coefficientsC() const;
+
+protected:
+    bool buildNaturalSpline( const QPolygonF & );
+    bool buildPeriodicSpline( const QPolygonF & );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_symbol.cpp b/qwt/qwt_symbol.cpp
new file mode 100644
index 0000000..1727656
--- /dev/null
+++ b/qwt/qwt_symbol.cpp
@@ -0,0 +1,1651 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+#include "qwt.h"
+#include "qwt_symbol.h"
+#include "qwt_painter.h"
+#include "qwt_graphic.h"
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qpainterpath.h>
+#include <qpixmap.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+
+namespace QwtTriangle
+{
+    enum Type
+    {
+        Left,
+        Right,
+        Up,
+        Down
+    };
+}
+
+static QwtGraphic qwtPathGraphic( const QPainterPath &path, 
+    const QPen &pen, const QBrush& brush )
+{
+    QwtGraphic graphic;
+    graphic.setRenderHint( QwtGraphic::RenderPensUnscaled );
+
+    QPainter painter( &graphic );
+    painter.setPen( pen );
+    painter.setBrush( brush );
+    painter.drawPath( path );
+    painter.end();
+
+    return graphic;
+}
+
+static inline QRectF qwtScaledBoundingRect( 
+    const QwtGraphic &graphic, const QSizeF size )
+{
+    QSizeF scaledSize = size;
+    if ( scaledSize.isEmpty() )
+        scaledSize = graphic.defaultSize();
+        
+    const QSizeF sz = graphic.controlPointRect().size();
+
+    double sx = 1.0;
+    if ( sz.width() > 0.0 )
+        sx = scaledSize.width() / sz.width();
+    
+    double sy = 1.0;
+    if ( sz.height() > 0.0 )
+        sy = scaledSize.height() / sz.height();
+
+    return graphic.scaledBoundingRect( sx, sy );
+}
+
+static inline void qwtDrawPixmapSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    QSize size = symbol.size();
+    if ( size.isEmpty() )
+        size = symbol.pixmap().size();
+
+    const QTransform transform = painter->transform();
+    if ( transform.isScaling() )
+    {
+        const QRect r( 0, 0, size.width(), size.height() );
+        size = transform.mapRect( r ).size();
+    }
+
+    QPixmap pm = symbol.pixmap();
+    if ( pm.size() != size )
+        pm = pm.scaled( size );
+    
+    QPointF pinPoint( 0.5 * size.width(), 0.5 * size.height() );
+    if ( symbol.isPinPointEnabled() )
+        pinPoint = symbol.pinPoint();
+
+    painter->resetTransform();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        const QPointF pos = transform.map( points[i] ) - pinPoint;
+
+        QwtPainter::drawPixmap( painter, 
+            QRect( pos.toPoint(), pm.size() ), pm );
+    }
+}
+
+
+
+static inline void qwtDrawGraphicSymbols( QPainter *painter, 
+    const QPointF *points, int numPoints, const QwtGraphic &graphic,
+    const QwtSymbol &symbol )
+{
+    const QRectF pointRect = graphic.controlPointRect();
+    if ( pointRect.isEmpty() )
+        return;
+
+    double sx = 1.0;
+    double sy = 1.0;
+
+    const QSize sz = symbol.size();
+    if ( sz.isValid() )
+    {
+        sx = sz.width() / pointRect.width();
+        sy = sz.height() / pointRect.height();
+    }
+
+    QPointF pinPoint = pointRect.center();
+    if ( symbol.isPinPointEnabled() )
+        pinPoint = symbol.pinPoint();
+
+    const QTransform transform = painter->transform();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        QTransform tr = transform;
+        tr.translate( points[i].x(), points[i].y() );
+        tr.scale( sx, sy );
+        tr.translate( -pinPoint.x(), -pinPoint.y() );
+
+        painter->setTransform( tr );
+
+        graphic.render( painter );
+    }
+
+    painter->setTransform( transform );
+}
+
+static inline void qwtDrawEllipseSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    painter->setBrush( symbol.brush() );
+    painter->setPen( symbol.pen() );
+
+    const QSize size = symbol.size();
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawEllipse( painter, r );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const double x = points[i].x();
+            const double y = points[i].y();
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawEllipse( painter, r );
+        }
+    }
+}
+
+static inline void qwtDrawRectSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const QRect r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawRect( painter, r );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const double x = points[i].x();
+            const double y = points[i].y();
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawRect( painter, r );
+        }
+    }
+}
+
+static inline void qwtDrawDiamondSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const int x1 = x - size.width() / 2;
+            const int y1 = y - size.height() / 2;
+            const int x2 = x1 + size.width();
+            const int y2 = y1 + size.height();
+
+            QPolygonF polygon;
+            polygon += QPointF( x, y1 );
+            polygon += QPointF( x1, y );
+            polygon += QPointF( x, y2 );
+            polygon += QPointF( x2, y );
+
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+    else
+    {
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const double x1 = pos.x() - 0.5 * size.width();
+            const double y1 = pos.y() - 0.5 * size.height();
+            const double x2 = x1 + size.width();
+            const double y2 = y1 + size.height();
+
+            QPolygonF polygon;
+            polygon += QPointF( pos.x(), y1 );
+            polygon += QPointF( x2, pos.y() );
+            polygon += QPointF( pos.x(), y2 );
+            polygon += QPointF( x1, pos.y() );
+
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+}
+
+static inline void qwtDrawTriangleSymbols(
+    QPainter *painter, QwtTriangle::Type type,
+    const QPointF *points, int numPoints,
+    const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+
+    painter->setBrush( symbol.brush() );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double sw2 = 0.5 * size.width();
+    double sh2 = 0.5 * size.height();
+
+    if ( doAlign )
+    {
+        sw2 = qFloor( sw2 );
+        sh2 = qFloor( sh2 );
+    }
+
+    QPolygonF triangle( 3 );
+    QPointF *trianglePoints = triangle.data();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        const QPointF &pos = points[i];
+
+        double x = pos.x();
+        double y = pos.y();
+
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        const double x1 = x - sw2;
+        const double x2 = x1 + size.width();
+        const double y1 = y - sh2;
+        const double y2 = y1 + size.height();
+
+        switch ( type )
+        {
+            case QwtTriangle::Left:
+            {
+                trianglePoints[0].rx() = x2;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x1;
+                trianglePoints[1].ry() = y;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Right:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x2;
+                trianglePoints[1].ry() = y;
+
+                trianglePoints[2].rx() = x1;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Up:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y2;
+
+                trianglePoints[1].rx() = x;
+                trianglePoints[1].ry() = y1;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Down:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x;
+                trianglePoints[1].ry() = y2;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y1;
+
+                break;
+            }
+        }
+        QwtPainter::drawPolygon( painter, triangle );
+    }
+}
+
+static inline void qwtDrawLineSymbols(
+    QPainter *painter, int orientations,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    int off = 0;
+
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+    {
+        pen.setCapStyle( Qt::FlatCap );
+        off = 1;
+    }
+
+    painter->setPen( pen );
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = qFloor( size.width() );
+        const int sh = qFloor( size.height() );
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            if ( orientations & Qt::Horizontal )
+            {
+                const int x = qRound( points[i].x() ) - sw2;
+                const int y = qRound( points[i].y() );
+
+                QwtPainter::drawLine( painter, x, y, x + sw + off, y );
+            }
+            if ( orientations & Qt::Vertical )
+            {
+                const int x = qRound( points[i].x() );
+                const int y = qRound( points[i].y() ) - sh2;
+
+                QwtPainter::drawLine( painter, x, y, x, y + sh + off );
+            }
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            if ( orientations & Qt::Horizontal )
+            {
+                const double x = points[i].x() - sw2;
+                const double y = points[i].y();
+
+                QwtPainter::drawLine( painter, x, y, x + sw, y );
+            }
+            if ( orientations & Qt::Vertical )
+            {
+                const double y = points[i].y() - sh2;
+                const double x = points[i].x();
+
+                QwtPainter::drawLine( painter, x, y, x, y + sh );
+            }
+        }
+    }
+}
+
+static inline void qwtDrawXCrossSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+    int off = 0;
+
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+    {
+        pen.setCapStyle( Qt::FlatCap );
+        off = 1;
+    }
+    painter->setPen( pen );
+
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const int x = qRound( pos.x() );
+            const int y = qRound( pos.y() );
+
+            const int x1 = x - sw2;
+            const int x2 = x1 + sw + off;
+            const int y1 = y - sh2;
+            const int y2 = y1 + sh + off;
+
+            QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+            QwtPainter::drawLine( painter, x2, y1, x1, y2 );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const double x1 = pos.x() - sw2;
+            const double x2 = x1 + sw;
+            const double y1 = pos.y() - sh2;
+            const double y2 = y1 + sh;
+
+            QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+            QwtPainter::drawLine( painter, x1, y2, x2, y1 );
+        }
+    }
+}
+
+static inline void qwtDrawStar1Symbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+    painter->setPen( symbol.pen() );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        QRect r( 0, 0, size.width(), size.height() );
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            r.moveCenter( points[i].toPoint() );
+
+            const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+            const double d1 = r.width() / 2.0 * ( 1.0 - sqrt1_2 );
+
+            QwtPainter::drawLine( painter,
+                qRound( r.left() + d1 ), qRound( r.top() + d1 ),
+                qRound( r.right() - d1 ), qRound( r.bottom() - d1 ) );
+            QwtPainter::drawLine( painter,
+                qRound( r.left() + d1 ), qRound( r.bottom() - d1 ),
+                qRound( r .right() - d1), qRound( r.top() + d1 ) );
+
+            const QPoint c = r.center();
+
+            QwtPainter::drawLine( painter,
+                c.x(), r.top(), c.x(), r.bottom() );
+            QwtPainter::drawLine( painter,
+                r.left(), c.y(), r.right(), c.y() );
+        }
+    }
+    else
+    {
+        QRectF r( 0, 0, size.width(), size.height() );
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            r.moveCenter( points[i] );
+
+            const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+            const QPointF c = r.center();
+            const double d1  = r.width() / 2.0 * ( 1.0 - sqrt1_2 );
+
+            QwtPainter::drawLine( painter,
+                r.left() + d1, r.top() + d1,
+                r.right() - d1, r.bottom() - d1 );
+            QwtPainter::drawLine( painter,
+                r.left() + d1, r.bottom() - d1,
+                r.right() - d1, r.top() + d1 );
+            QwtPainter::drawLine( painter,
+                c.x(), r.top(),
+                c.x(), r.bottom() );
+            QwtPainter::drawLine( painter,
+                r.left(), c.y(),
+                r.right(), c.y() );
+        }
+    }
+}
+
+static inline void qwtDrawStar2Symbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+        pen.setCapStyle( Qt::FlatCap );
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+
+    painter->setBrush( symbol.brush() );
+
+    const double cos30 = 0.866025; // cos(30°)
+
+    const double dy = 0.25 * symbol.size().height();
+    const double dx = 0.5 * symbol.size().width() * cos30 / 3.0;
+
+    QPolygonF star( 12 );
+    QPointF *starPoints = star.data();
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        double x = points[i].x();
+        double y = points[i].y();
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        double x1 = x - 3 * dx;
+        double y1 = y - 2 * dy;
+        if ( doAlign )
+        {
+            x1 = qRound( x - 3 * dx );
+            y1 = qRound( y - 2 * dy );
+        }
+
+        const double x2 = x1 + 1 * dx;
+        const double x3 = x1 + 2 * dx;
+        const double x4 = x1 + 3 * dx;
+        const double x5 = x1 + 4 * dx;
+        const double x6 = x1 + 5 * dx;
+        const double x7 = x1 + 6 * dx;
+
+        const double y2 = y1 + 1 * dy;
+        const double y3 = y1 + 2 * dy;
+        const double y4 = y1 + 3 * dy;
+        const double y5 = y1 + 4 * dy;
+
+        starPoints[0].rx() = x4;
+        starPoints[0].ry() = y1;
+
+        starPoints[1].rx() = x5;
+        starPoints[1].ry() = y2;
+
+        starPoints[2].rx() = x7;
+        starPoints[2].ry() = y2;
+
+        starPoints[3].rx() = x6;
+        starPoints[3].ry() = y3;
+
+        starPoints[4].rx() = x7;
+        starPoints[4].ry() = y4;
+
+        starPoints[5].rx() = x5;
+        starPoints[5].ry() = y4;
+
+        starPoints[6].rx() = x4;
+        starPoints[6].ry() = y5;
+
+        starPoints[7].rx() = x3;
+        starPoints[7].ry() = y4;
+
+        starPoints[8].rx() = x1;
+        starPoints[8].ry() = y4;
+
+        starPoints[9].rx() = x2;
+        starPoints[9].ry() = y3;
+
+        starPoints[10].rx() = x1;
+        starPoints[10].ry() = y2;
+
+        starPoints[11].rx() = x3;
+        starPoints[11].ry() = y2;
+
+        QwtPainter::drawPolygon( painter, star );
+    }
+}
+
+static inline void qwtDrawHexagonSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    painter->setBrush( symbol.brush() );
+    painter->setPen( symbol.pen() );
+
+    const double cos30 = 0.866025; // cos(30°)
+    const double dx = 0.5 * ( symbol.size().width() - cos30 );
+
+    const double dy = 0.25 * symbol.size().height();
+
+    QPolygonF hexaPolygon( 6 );
+    QPointF *hexaPoints = hexaPolygon.data();
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        double x = points[i].x();
+        double y = points[i].y();
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        double x1 = x - dx;
+        double y1 = y - 2 * dy;
+        if ( doAlign )
+        {
+            x1 = qCeil( x1 );
+            y1 = qCeil( y1 );
+        }
+
+        const double x2 = x1 + 1 * dx;
+        const double x3 = x1 + 2 * dx;
+
+        const double y2 = y1 + 1 * dy;
+        const double y3 = y1 + 3 * dy;
+        const double y4 = y1 + 4 * dy;
+
+        hexaPoints[0].rx() = x2;
+        hexaPoints[0].ry() = y1;
+
+        hexaPoints[1].rx() = x3;
+        hexaPoints[1].ry() = y2;
+
+        hexaPoints[2].rx() = x3;
+        hexaPoints[2].ry() = y3;
+
+        hexaPoints[3].rx() = x2;
+        hexaPoints[3].ry() = y4;
+
+        hexaPoints[4].rx() = x1;
+        hexaPoints[4].ry() = y3;
+
+        hexaPoints[5].rx() = x1;
+        hexaPoints[5].ry() = y2;
+
+        QwtPainter::drawPolygon( painter, hexaPolygon );
+    }
+}
+
+class QwtSymbol::PrivateData
+{
+public:
+    PrivateData( QwtSymbol::Style st, const QBrush &br,
+            const QPen &pn, const QSize &sz ):
+        style( st ),
+        size( sz ),
+        brush( br ),
+        pen( pn ),
+        isPinPointEnabled( false )
+    {
+        cache.policy = QwtSymbol::AutoCache;
+    }
+
+    ~PrivateData()
+    {
+    }
+
+    Style style;
+    QSize size;
+    QBrush brush;
+    QPen pen;
+
+    bool isPinPointEnabled;
+    QPointF pinPoint;
+
+    struct Path
+    {
+        QPainterPath path;
+        QwtGraphic graphic;
+
+    } path;
+
+    struct Pixmap
+    {
+        QPixmap pixmap;
+
+    } pixmap;
+
+    struct Graphic
+    {
+        QwtGraphic graphic;
+
+    } graphic;
+
+
+    struct PaintCache
+    {
+        QwtSymbol::CachePolicy policy;
+        QPixmap pixmap;
+
+    } cache;
+};
+
+/*!
+  Default Constructor
+  \param style Symbol Style
+
+  The symbol is constructed with gray interior,
+  black outline with zero width, no size and style 'NoSymbol'.
+*/
+QwtSymbol::QwtSymbol( Style style )
+{
+    d_data = new PrivateData( style, QBrush( Qt::gray ),
+        QPen( Qt::black, 0 ), QSize() );
+}
+
+/*!
+  \brief Constructor
+  \param style Symbol Style
+  \param brush brush to fill the interior
+  \param pen outline pen
+  \param size size
+
+  \sa setStyle(), setBrush(), setPen(), setSize()
+*/
+QwtSymbol::QwtSymbol( QwtSymbol::Style style, const QBrush &brush,
+    const QPen &pen, const QSize &size )
+{
+    d_data = new PrivateData( style, brush, pen, size );
+}
+
+/*!
+  \brief Constructor
+
+  The symbol gets initialized by a painter path. The style is
+  set to QwtSymbol::Path, the size is set to empty ( the path
+  is displayed unscaled ).
+
+  \param path painter path
+  \param brush brush to fill the interior
+  \param pen outline pen
+
+  \sa setPath(), setBrush(), setPen(), setSize()
+*/
+
+QwtSymbol::QwtSymbol( const QPainterPath &path, 
+    const QBrush &brush, const QPen &pen )
+{
+    d_data = new PrivateData( QwtSymbol::Path, brush, pen, QSize() );
+    setPath( path );
+}
+
+//! Destructor
+QwtSymbol::~QwtSymbol()
+{
+    delete d_data;
+}
+
+/*!
+  Change the cache policy
+
+  The default policy is AutoCache
+
+  \param policy Cache policy
+  \sa CachePolicy, cachePolicy()
+*/
+void QwtSymbol::setCachePolicy(
+    QwtSymbol::CachePolicy policy )
+{
+    if ( d_data->cache.policy != policy )
+    {
+        d_data->cache.policy = policy;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return Cache policy
+  \sa CachePolicy, setCachePolicy()
+*/
+QwtSymbol::CachePolicy QwtSymbol::cachePolicy() const
+{
+    return d_data->cache.policy;
+}
+
+/*!
+  \brief Set a painter path as symbol
+
+  The symbol is represented by a painter path, where the 
+  origin ( 0, 0 ) of the path coordinate system is mapped to
+  the position of the symbol.
+
+  When the symbol has valid size the painter path gets scaled
+  to fit into the size. Otherwise the symbol size depends on
+  the bounding rectangle of the path.
+
+  The following code defines a symbol drawing an arrow:
+
+  \verbatim
+#include <qwt_symbol.h>
+
+QwtSymbol *symbol = new QwtSymbol();
+
+QPen pen( Qt::black, 2 );
+pen.setJoinStyle( Qt::MiterJoin );
+
+symbol->setPen( pen );
+symbol->setBrush( Qt::red );
+
+QPainterPath path;
+path.moveTo( 0, 8 );
+path.lineTo( 0, 5 );
+path.lineTo( -3, 5 );
+path.lineTo( 0, 0 );
+path.lineTo( 3, 5 );
+path.lineTo( 0, 5 );
+
+QTransform transform;
+transform.rotate( -30.0 );
+path = transform.map( path );
+
+symbol->setPath( path );
+symbol->setPinPoint( QPointF( 0.0, 0.0 ) );
+
+setSize( 10, 14 );
+\endverbatim
+
+  \param path Painter path
+
+  \note The style is implicitely set to QwtSymbol::Path.
+  \sa path(), setSize()
+ */
+void QwtSymbol::setPath( const QPainterPath &path )
+{
+    d_data->style = QwtSymbol::Path;
+    d_data->path.path = path;
+    d_data->path.graphic.reset();
+}
+
+/*!
+   \return Painter path for displaying the symbol
+   \sa setPath()
+*/
+const QPainterPath &QwtSymbol::path() const
+{
+    return d_data->path.path;
+}
+
+/*!
+  Set a pixmap as symbol
+
+  \param pixmap Pixmap
+
+  \sa pixmap(), setGraphic()
+
+  \note the style() is set to QwtSymbol::Pixmap
+  \note brush() and pen() have no effect
+ */
+void QwtSymbol::setPixmap( const QPixmap &pixmap )
+{
+    d_data->style = QwtSymbol::Pixmap;
+    d_data->pixmap.pixmap = pixmap;
+}
+
+/*!
+  \return Assigned pixmap
+  \sa setPixmap()
+ */
+const QPixmap &QwtSymbol::pixmap() const
+{
+    return d_data->pixmap.pixmap;
+}
+
+/*!
+  Set a graphic as symbol
+
+  \param graphic Graphic
+
+  \sa graphic(), setPixmap()
+
+  \note the style() is set to QwtSymbol::Graphic
+  \note brush() and pen() have no effect
+ */
+void QwtSymbol::setGraphic( const QwtGraphic &graphic )
+{
+    d_data->style = QwtSymbol::Graphic;
+    d_data->graphic.graphic = graphic;
+}
+
+/*!
+  \return Assigned graphic
+  \sa setGraphic()
+ */
+const QwtGraphic &QwtSymbol::graphic() const
+{
+    return d_data->graphic.graphic;
+}
+
+
+
+/*!
+  \brief Specify the symbol's size
+
+  If the 'h' parameter is left out or less than 0,
+  and the 'w' parameter is greater than or equal to 0,
+  the symbol size will be set to (w,w).
+
+  \param width Width
+  \param height Height (defaults to -1)
+
+  \sa size()
+*/
+void QwtSymbol::setSize( int width, int height )
+{
+    if ( ( width >= 0 ) && ( height < 0 ) )
+        height = width;
+
+    setSize( QSize( width, height ) );
+}
+
+/*!
+   Set the symbol's size
+   \param size Size
+
+   \sa size()
+*/
+void QwtSymbol::setSize( const QSize &size )
+{
+    if ( size.isValid() && size != d_data->size )
+    {
+        d_data->size = size;
+        invalidateCache();
+    }
+}
+
+/*!
+   \return Size
+   \sa setSize()
+*/
+const QSize& QwtSymbol::size() const
+{
+    return d_data->size;
+}
+
+/*!
+  \brief Assign a brush
+
+  The brush is used to draw the interior of the symbol.
+  \param brush Brush
+
+  \sa brush()
+*/
+void QwtSymbol::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+        invalidateCache();
+
+        if ( d_data->style == QwtSymbol::Path )
+            d_data->path.graphic.reset();
+    }
+}
+
+/*!
+  \return Brush
+  \sa setBrush()
+*/
+const QBrush& QwtSymbol::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  Build and assign a pen
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 )
+  what makes it non cosmetic ( see QPen::isCosmetic() ).
+  This method has been introduced to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtSymbol::setPen( const QColor &color,
+    qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  The pen is used to draw the symbol's outline.
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtSymbol::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+        invalidateCache();
+
+        if ( d_data->style == QwtSymbol::Path )
+            d_data->path.graphic.reset();
+    }
+}
+
+/*!
+  \return Pen
+  \sa setPen(), brush()
+*/
+const QPen& QwtSymbol::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  \brief Set the color of the symbol
+
+  Change the color of the brush for symbol types with a filled area.
+  For all other symbol types the color will be assigned to the pen.
+
+  \param color Color
+
+  \sa setBrush(), setPen(), brush(), pen()
+*/
+void QwtSymbol::setColor( const QColor &color )
+{
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        case QwtSymbol::Rect:
+        case QwtSymbol::Diamond:
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        case QwtSymbol::DTriangle:
+        case QwtSymbol::RTriangle:
+        case QwtSymbol::LTriangle:
+        case QwtSymbol::Star2:
+        case QwtSymbol::Hexagon:
+        {
+            if ( d_data->brush.color() != color )
+            {
+                d_data->brush.setColor( color );
+                invalidateCache();
+            }
+            break;
+        }
+        case QwtSymbol::Cross:
+        case QwtSymbol::XCross:
+        case QwtSymbol::HLine:
+        case QwtSymbol::VLine:
+        case QwtSymbol::Star1:
+        {
+            if ( d_data->pen.color() != color )
+            {
+                d_data->pen.setColor( color );
+                invalidateCache();
+            }
+            break;
+        }
+        default:
+        {
+            if ( d_data->brush.color() != color ||
+                d_data->pen.color() != color )
+            {
+                invalidateCache();
+            }
+
+            d_data->brush.setColor( color );
+            d_data->pen.setColor( color );
+        }
+    }
+}
+
+/*!
+  \brief Set and enable a pin point
+
+  The position of a complex symbol is not always aligned to its center
+  ( f.e an arrow, where the peak points to a position ). The pin point
+  defines the position inside of a Pixmap, Graphic, SvgDocument 
+  or PainterPath symbol where the represented point has to
+  be aligned to.
+  
+  \param pos Position
+  \param enable En/Disable the pin point alignment
+
+  \sa pinPoint(), setPinPointEnabled()
+ */
+void QwtSymbol::setPinPoint( const QPointF &pos, bool enable )
+{
+    if ( d_data->pinPoint != pos )
+    {
+        d_data->pinPoint = pos;
+        if ( d_data->isPinPointEnabled )
+        {
+            invalidateCache();
+        }
+    }
+
+    setPinPointEnabled( enable );
+}
+
+/*!
+  \return Pin point
+  \sa setPinPoint(), setPinPointEnabled()
+ */
+QPointF QwtSymbol::pinPoint() const
+{
+    return d_data->pinPoint;
+}
+
+/*!
+  En/Disable the pin point alignment
+
+  \param on Enabled, when on is true
+  \sa setPinPoint(), isPinPointEnabled()
+ */
+void QwtSymbol::setPinPointEnabled( bool on )
+{
+    if ( d_data->isPinPointEnabled != on )
+    {
+        d_data->isPinPointEnabled = on;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return True, when the pin point translation is enabled
+  \sa setPinPoint(), setPinPointEnabled()
+ */
+bool QwtSymbol::isPinPointEnabled() const
+{
+    return d_data->isPinPointEnabled;
+}
+
+/*!
+  Render an array of symbols
+
+  Painting several symbols is more effective than drawing symbols
+  one by one, as a couple of layout calculations and setting of pen/brush
+  can be done once for the complete array.
+
+  \param painter Painter
+  \param points Array of points
+  \param numPoints Number of points
+*/
+void QwtSymbol::drawSymbols( QPainter *painter,
+    const QPointF *points, int numPoints ) const
+{
+    if ( numPoints <= 0 )
+        return;
+
+    bool useCache = false;
+
+    // Don't use the pixmap, when the paint device
+    // could generate scalable vectors
+
+    if ( QwtPainter::roundingAlignment( painter ) &&
+        !painter->transform().isScaling() )
+    {
+        if ( d_data->cache.policy == QwtSymbol::Cache )
+        {
+            useCache = true;
+        }
+        else if ( d_data->cache.policy == QwtSymbol::AutoCache )
+        {
+            if ( painter->paintEngine()->type() == QPaintEngine::Raster )
+            {
+                useCache = true;
+            }
+            else
+            {
+                switch( d_data->style )
+                {
+                    case QwtSymbol::XCross:
+                    case QwtSymbol::HLine:
+                    case QwtSymbol::VLine:
+                    case QwtSymbol::Cross:
+                        break;
+
+                    case QwtSymbol::Pixmap:
+                    {
+                        if ( !d_data->size.isEmpty() &&
+                            d_data->size != d_data->pixmap.pixmap.size() ) 
+                        {
+                            useCache = true;
+                        }
+                        break;
+                    }                       
+                    default:
+                        useCache = true;
+                }
+            }
+        }
+    }
+
+    if ( useCache )
+    {
+        const QRect br = boundingRect();
+
+        const QRect rect( 0, 0, br.width(), br.height() );
+        
+        if ( d_data->cache.pixmap.isNull() )
+        {
+            d_data->cache.pixmap = QwtPainter::backingStore( NULL, br.size() );
+            d_data->cache.pixmap.fill( Qt::transparent );
+
+            QPainter p( &d_data->cache.pixmap );
+            p.setRenderHints( painter->renderHints() );
+            p.translate( -br.topLeft() );
+
+            const QPointF pos;
+            renderSymbols( &p, &pos, 1 );
+        }
+
+        const int dx = br.left();
+        const int dy = br.top();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int left = qRound( points[i].x() ) + dx;
+            const int top = qRound( points[i].y() ) + dy;
+
+            painter->drawPixmap( left, top, d_data->cache.pixmap );
+        }
+    }
+    else
+    {
+        painter->save();
+        renderSymbols( painter, points, numPoints );
+        painter->restore();
+    }
+}
+
+/*!
+  \brief Draw the symbol into a rectangle
+
+  The symbol is painted centered and scaled into the target rectangle.
+  It is always painted uncached and the pin point is ignored.
+
+  This method is primarily intended for drawing a symbol to
+  the legend.
+
+  \param painter Painter
+  \param rect Target rectangle for the symbol 
+*/
+void QwtSymbol::drawSymbol( QPainter *painter, const QRectF &rect ) const
+{
+    if ( d_data->style == QwtSymbol::NoSymbol )
+        return;
+
+    if ( d_data->style == QwtSymbol::Graphic )
+    {
+        d_data->graphic.graphic.render( 
+            painter, rect, Qt::KeepAspectRatio );
+    }
+    else if ( d_data->style == QwtSymbol::Path )
+    {
+        if ( d_data->path.graphic.isNull() )
+        {
+            d_data->path.graphic = qwtPathGraphic( 
+                d_data->path.path, d_data->pen, d_data->brush );
+        }
+
+        d_data->path.graphic.render( 
+            painter, rect, Qt::KeepAspectRatio );
+        return;
+    }
+    else if ( d_data->style == QwtSymbol::SvgDocument )
+    {
+
+    }
+    else
+    {
+        const QRect br = boundingRect();
+
+        // scale the symbol size to fit into rect.
+
+        const double ratio = qMin( rect.width() / br.width(), 
+            rect.height() / br.height() );
+
+        painter->save();
+
+        painter->translate( rect.center() );
+        painter->scale( ratio, ratio );
+
+        const bool isPinPointEnabled = d_data->isPinPointEnabled;
+        d_data->isPinPointEnabled = false;
+
+        const QPointF pos;
+        renderSymbols( painter, &pos, 1 );
+    
+        d_data->isPinPointEnabled = isPinPointEnabled;
+
+        painter->restore();
+    }
+}
+
+/*!
+  Render the symbol to series of points
+
+  \param painter Qt painter
+  \param points Positions of the symbols
+  \param numPoints Number of points
+ */
+void QwtSymbol::renderSymbols( QPainter *painter,
+    const QPointF *points, int numPoints ) const
+{
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        {
+            qwtDrawEllipseSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Rect:
+        {
+            qwtDrawRectSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Diamond:
+        {
+            qwtDrawDiamondSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Cross:
+        {
+            qwtDrawLineSymbols( painter, Qt::Horizontal | Qt::Vertical,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::XCross:
+        {
+            qwtDrawXCrossSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Up,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::DTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Down,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::RTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Right,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::LTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Left,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::HLine:
+        {
+            qwtDrawLineSymbols( painter, Qt::Horizontal,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::VLine:
+        {
+            qwtDrawLineSymbols( painter, Qt::Vertical,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Star1:
+        {
+            qwtDrawStar1Symbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Star2:
+        {
+            qwtDrawStar2Symbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Hexagon:
+        {
+            qwtDrawHexagonSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Path:
+        {
+            if ( d_data->path.graphic.isNull() )
+            {
+                d_data->path.graphic = qwtPathGraphic( d_data->path.path, 
+                    d_data->pen, d_data->brush );
+            }
+
+            qwtDrawGraphicSymbols( painter, points, numPoints, 
+                d_data->path.graphic, *this );
+            break;
+        }
+        case QwtSymbol::Pixmap:
+        {
+            qwtDrawPixmapSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Graphic:
+        {
+            qwtDrawGraphicSymbols( painter, points, numPoints, 
+                d_data->graphic.graphic, *this );
+            break;
+        }
+        case QwtSymbol::SvgDocument:
+        {
+
+            break;
+        }
+        default:;
+    }
+}
+
+/*!
+  Calculate the bounding rectangle for a symbol
+  at position (0,0).
+
+  \return Bounding rectangle
+ */
+QRect QwtSymbol::boundingRect() const
+{
+    QRectF rect;
+
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        case QwtSymbol::Rect:
+        case QwtSymbol::Hexagon:
+        {
+            qreal pw = 0.0;
+            if ( d_data->pen.style() != Qt::NoPen )
+                pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) );
+
+            rect.setSize( d_data->size + QSizeF( pw, pw ) );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+
+            break;
+        }
+        case QwtSymbol::XCross:
+        case QwtSymbol::Diamond:
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        case QwtSymbol::DTriangle:
+        case QwtSymbol::RTriangle:
+        case QwtSymbol::LTriangle:
+        case QwtSymbol::Star1:
+        case QwtSymbol::Star2:
+        {
+            qreal pw = 0.0;
+            if ( d_data->pen.style() != Qt::NoPen )
+                pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) );
+
+            rect.setSize( d_data->size + QSizeF( 2 * pw, 2 * pw ) );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+            break;
+        }
+        case QwtSymbol::Path:
+        {
+            if ( d_data->path.graphic.isNull() )
+            {
+                d_data->path.graphic = qwtPathGraphic(
+                    d_data->path.path, d_data->pen, d_data->brush );
+            }
+
+            rect = qwtScaledBoundingRect( 
+                d_data->path.graphic, d_data->size );
+
+            break;
+        }
+        case QwtSymbol::Pixmap:
+        {
+            if ( d_data->size.isEmpty() )
+                rect.setSize( d_data->pixmap.pixmap.size() );
+            else
+                rect.setSize( d_data->size );
+            
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+
+            // pinpoint ???
+            break;
+        }
+        case QwtSymbol::Graphic:
+        {
+            rect = qwtScaledBoundingRect( 
+                d_data->graphic.graphic, d_data->size );
+
+            break;
+        }
+
+        default:
+        {
+            rect.setSize( d_data->size );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+        }
+    }
+
+    if ( d_data->style == QwtSymbol::Graphic || 
+        d_data->style == QwtSymbol::SvgDocument || d_data->style == QwtSymbol::Path )
+    {
+        QPointF pinPoint( 0.0, 0.0 );
+        if ( d_data->isPinPointEnabled )
+            pinPoint = rect.center() - d_data->pinPoint;
+
+        rect.moveCenter( pinPoint );
+    }
+
+    QRect r;
+    r.setLeft( qFloor( rect.left() ) );
+    r.setTop( qFloor( rect.top() ) );
+    r.setRight( qCeil( rect.right() ) );
+    r.setBottom( qCeil( rect.bottom() ) );
+
+    if ( d_data->style != QwtSymbol::Pixmap )
+        r.adjust( -1, -1, 1, 1 ); // for antialiasing
+
+    return r;
+}
+
+/*!
+  Invalidate the cached symbol pixmap
+
+  The symbol invalidates its cache, whenever an attribute is changed
+  that has an effect ob how to display a symbol. In case of derived
+  classes with individual styles ( >= QwtSymbol::UserStyle ) it
+  might be necessary to call invalidateCache() for attributes
+  that are relevant for this style.
+
+  \sa CachePolicy, setCachePolicy(), drawSymbols()
+ */
+void QwtSymbol::invalidateCache()
+{
+    if ( !d_data->cache.pixmap.isNull() )
+        d_data->cache.pixmap = QPixmap();
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style()
+*/
+void QwtSymbol::setStyle( QwtSymbol::Style style )
+{
+    if ( d_data->style != style )
+    {
+        d_data->style = style;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtSymbol::Style QwtSymbol::style() const
+{
+    return d_data->style;
+}
diff --git a/qwt/qwt_symbol.h b/qwt/qwt_symbol.h
new file mode 100644
index 0000000..ea16d88
--- /dev/null
+++ b/qwt/qwt_symbol.h
@@ -0,0 +1,258 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SYMBOL_H
+#define QWT_SYMBOL_H
+
+#include "qwt_global.h"
+#include <qpolygon.h>
+
+class QPainter;
+class QRect;
+class QSize;
+class QBrush;
+class QPen;
+class QColor;
+class QPointF;
+class QPolygonF;
+class QPainterPath;
+class QPixmap;
+class QByteArray;
+class QwtGraphic;
+
+//! A class for drawing symbols
+class QWT_EXPORT QwtSymbol
+{
+public:
+    /*!
+      Symbol Style
+      \sa setStyle(), style()
+     */
+    enum Style
+    {
+        //! No Style. The symbol cannot be drawn.
+        NoSymbol = -1,
+
+        //! Ellipse or circle
+        Ellipse,
+
+        //! Rectangle
+        Rect,
+
+        //!  Diamond
+        Diamond,
+
+        //! Triangle pointing upwards
+        Triangle,
+
+        //! Triangle pointing downwards
+        DTriangle,
+
+        //! Triangle pointing upwards
+        UTriangle,
+
+        //! Triangle pointing left
+        LTriangle,
+
+        //! Triangle pointing right
+        RTriangle,
+
+        //! Cross (+)
+        Cross,
+
+        //! Diagonal cross (X)
+        XCross,
+
+        //! Horizontal line
+        HLine,
+
+        //! Vertical line
+        VLine,
+
+        //! X combined with +
+        Star1,
+
+        //! Six-pointed star
+        Star2,
+
+        //! Hexagon
+        Hexagon,
+
+        /*!
+          The symbol is represented by a painter path, where the 
+          origin ( 0, 0 ) of the path coordinate system is mapped to
+          the position of the symbol.
+
+          \sa setPath(), path()
+         */
+        Path,
+
+        /*!
+          The symbol is represented by a pixmap. The pixmap is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        Pixmap,
+
+        /*!
+          The symbol is represented by a graphic. The graphic is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        Graphic,
+
+        /*!
+          The symbol is represented by a SVG graphic. The graphic is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        SvgDocument,
+
+        /*!
+         Styles >= QwtSymbol::UserSymbol are reserved for derived
+         classes of QwtSymbol that overload drawSymbols() with
+         additional application specific symbol types.
+         */
+        UserStyle = 1000
+    };
+
+    /*!
+      Depending on the render engine and the complexity of the
+      symbol shape it might be faster to render the symbol
+      to a pixmap and to paint this pixmap.
+
+      F.e. the raster paint engine is a pure software renderer
+      where in cache mode a draw operation usually ends in 
+      raster operation with the the backing store, that are usually
+      faster, than the algorithms for rendering polygons.
+      But the opposite can be expected for graphic pipelines
+      that can make use of hardware acceleration.
+
+      The default setting is AutoCache
+
+      \sa setCachePolicy(), cachePolicy()
+
+      \note The policy has no effect, when the symbol is painted 
+            to a vector graphics format ( PDF, SVG ).
+      \warning Since Qt 4.8 raster is the default backend on X11
+     */
+
+    enum CachePolicy
+    {
+        //! Don't use a pixmap cache
+        NoCache,
+
+        //! Always use a pixmap cache
+        Cache,
+
+        /*! 
+           Use a cache when one of the following conditions is true:
+
+           - The symbol is rendered with the software 
+             renderer ( QPaintEngine::Raster )
+         */
+        AutoCache
+    };
+
+public:
+    QwtSymbol( Style = NoSymbol );
+    QwtSymbol( Style, const QBrush &, const QPen &, const QSize & );
+    QwtSymbol( const QPainterPath &, const QBrush &, const QPen & );
+
+    virtual ~QwtSymbol();
+
+    void setCachePolicy( CachePolicy );
+    CachePolicy cachePolicy() const;
+
+    void setSize( const QSize & );
+    void setSize( int width, int height = -1 );
+    const QSize& size() const;
+
+    void setPinPoint( const QPointF &pos, bool enable = true );
+    QPointF pinPoint() const;
+
+    void setPinPointEnabled( bool );
+    bool isPinPointEnabled() const;
+
+    virtual void setColor( const QColor & );
+
+    void setBrush( const QBrush& b );
+    const QBrush& brush() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen& pen() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    void setPath( const QPainterPath & );
+    const QPainterPath &path() const;
+
+    void setPixmap( const QPixmap & );
+    const QPixmap &pixmap() const;
+
+    void setGraphic( const QwtGraphic & );
+    const QwtGraphic &graphic() const;
+
+#ifndef QWT_NO_SVG
+    void setSvgDocument( const QByteArray & );
+#endif
+
+    void drawSymbol( QPainter *, const QRectF & ) const;
+    void drawSymbol( QPainter *, const QPointF & ) const;
+    void drawSymbols( QPainter *, const QPolygonF & ) const;
+    void drawSymbols( QPainter *,
+        const QPointF *, int numPoints ) const;
+
+    virtual QRect boundingRect() const;
+    void invalidateCache();
+
+protected:
+    virtual void renderSymbols( QPainter *,
+        const QPointF *, int numPoints ) const;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtSymbol( const QwtSymbol & );
+    QwtSymbol &operator=( const QwtSymbol & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief Draw the symbol at a specified position
+
+  \param painter Painter
+  \param pos Position of the symbol in screen coordinates
+*/
+inline void QwtSymbol::drawSymbol(
+    QPainter *painter, const QPointF &pos ) const
+{
+    drawSymbols( painter, &pos, 1 );
+}
+
+/*!
+  \brief Draw symbols at the specified points
+
+  \param painter Painter
+  \param points Positions of the symbols in screen coordinates
+*/
+
+inline void QwtSymbol::drawSymbols(
+    QPainter *painter, const QPolygonF &points ) const
+{
+    drawSymbols( painter, points.data(), points.size() );
+}
+
+#endif
diff --git a/qwt/qwt_system_clock.cpp b/qwt/qwt_system_clock.cpp
new file mode 100644
index 0000000..fb0c5be
--- /dev/null
+++ b/qwt/qwt_system_clock.cpp
@@ -0,0 +1,396 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_system_clock.h"
+
+#if QT_VERSION >= 0x040800
+#define USE_ELAPSED_TIMER 1
+#endif
+
+#if USE_ELAPSED_TIMER
+
+#include <qelapsedtimer.h>
+
+class QwtSystemClock::PrivateData
+{
+public:
+    QElapsedTimer timer;
+};
+
+QwtSystemClock::QwtSystemClock()
+{
+    d_data = new PrivateData();
+}
+
+QwtSystemClock::~QwtSystemClock()
+{
+    delete d_data;
+}
+    
+bool QwtSystemClock::isNull() const
+{
+    return d_data->timer.isValid();
+}
+        
+void QwtSystemClock::start()
+{
+    d_data->timer.start();
+}
+
+double QwtSystemClock::restart()
+{
+    const qint64 nsecs = d_data->timer.restart();
+    return nsecs / 1e6;
+}
+
+double QwtSystemClock::elapsed() const
+{
+    const qint64 nsecs = d_data->timer.nsecsElapsed();
+    return nsecs / 1e6;
+}
+    
+#else // !USE_ELAPSED_TIMER
+
+#include <qdatetime.h>
+
+#if !defined(Q_OS_WIN)
+#include <unistd.h>
+#endif
+
+#if defined(Q_OS_MAC)
+#include <stdint.h>
+#include <mach/mach_time.h>
+#define QWT_HIGH_RESOLUTION_CLOCK
+#elif defined(_POSIX_TIMERS)
+#include <time.h>
+#define QWT_HIGH_RESOLUTION_CLOCK
+#elif defined(Q_OS_WIN)
+#define QWT_HIGH_RESOLUTION_CLOCK
+#include <qt_windows.h>
+#endif
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+
+class QwtHighResolutionClock
+{
+public:
+    QwtHighResolutionClock();
+
+    void start();
+    double restart();
+    double elapsed() const;
+
+    bool isNull() const;
+
+    static double precision();
+
+private:
+
+#if defined(Q_OS_MAC)
+    static double msecsTo( uint64_t, uint64_t );
+
+    uint64_t d_timeStamp;
+#elif defined(_POSIX_TIMERS)
+
+    static double msecsTo( const struct timespec &,
+        const struct timespec & );
+
+    static bool isMonotonic();
+
+    struct timespec d_timeStamp;
+    clockid_t d_clockId;
+
+#elif defined(Q_OS_WIN)
+
+    LARGE_INTEGER d_startTicks;
+    LARGE_INTEGER d_ticksPerSecond;
+#endif
+};
+
+#if defined(Q_OS_MAC)
+QwtHighResolutionClock::QwtHighResolutionClock():
+    d_timeStamp( 0 )
+{
+}
+
+double QwtHighResolutionClock::precision()
+{
+    return 1e-6;
+}
+
+void QwtHighResolutionClock::start()
+{
+    d_timeStamp = mach_absolute_time();
+}
+
+double QwtHighResolutionClock::restart()
+{
+    const uint64_t timeStamp = mach_absolute_time();
+    const double elapsed = msecsTo( d_timeStamp, timeStamp );
+    d_timeStamp = timeStamp;
+
+    return elapsed;
+}
+
+double QwtHighResolutionClock::elapsed() const
+{
+    return msecsTo( d_timeStamp, mach_absolute_time() );
+}
+
+bool QwtHighResolutionClock::isNull() const
+{
+    return d_timeStamp == 0;
+}
+
+double QwtHighResolutionClock::msecsTo(
+    uint64_t from, uint64_t to )
+{
+    const uint64_t difference = to - from;
+
+    static double conversion = 0.0;
+    if ( conversion == 0.0 )
+    {
+        mach_timebase_info_data_t info;
+        kern_return_t err = mach_timebase_info( &info );
+
+        // convert the timebase into ms
+        if ( err == 0  )
+            conversion = 1e-6 * ( double ) info.numer / ( double ) info.denom;
+    }
+
+    return conversion * ( double ) difference;
+}
+
+#elif defined(_POSIX_TIMERS)
+
+QwtHighResolutionClock::QwtHighResolutionClock()
+{
+    d_clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    d_timeStamp.tv_sec = d_timeStamp.tv_nsec = 0;
+}
+
+double QwtHighResolutionClock::precision()
+{
+    struct timespec resolution;
+
+    int clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    ::clock_getres( clockId, &resolution );
+
+    return resolution.tv_nsec / 1e3;
+}
+
+inline bool QwtHighResolutionClock::isNull() const
+{
+    return d_timeStamp.tv_sec <= 0 && d_timeStamp.tv_nsec <= 0;
+}
+
+inline void QwtHighResolutionClock::start()
+{
+    ::clock_gettime( d_clockId, &d_timeStamp );
+}
+
+double QwtHighResolutionClock::restart()
+{
+    struct timespec timeStamp;
+    ::clock_gettime( d_clockId, &timeStamp );
+
+    const double elapsed = msecsTo( d_timeStamp, timeStamp );
+
+    d_timeStamp = timeStamp;
+    return elapsed;
+}
+
+inline double QwtHighResolutionClock::elapsed() const
+{
+    struct timespec timeStamp;
+    ::clock_gettime( d_clockId, &timeStamp );
+
+    return msecsTo( d_timeStamp, timeStamp );
+}
+
+inline double QwtHighResolutionClock::msecsTo(
+    const struct timespec &t1, const struct timespec &t2 )
+{
+    return ( t2.tv_sec - t1.tv_sec ) * 1e3
+        + ( t2.tv_nsec - t1.tv_nsec ) * 1e-6;
+}
+
+bool QwtHighResolutionClock::isMonotonic()
+{
+    // code copied from qcore_unix.cpp
+
+#if (_POSIX_MONOTONIC_CLOCK-0 > 0)
+    return true;
+#else
+    static int returnValue = 0;
+
+    if ( returnValue == 0 )
+    {
+#if (_POSIX_MONOTONIC_CLOCK-0 < 0) || !defined(_SC_MONOTONIC_CLOCK)
+        returnValue = -1;
+#elif (_POSIX_MONOTONIC_CLOCK == 0)
+        // detect if the system support monotonic timers
+        const long x = sysconf( _SC_MONOTONIC_CLOCK );
+        returnValue = ( x >= 200112L ) ? 1 : -1;
+#endif
+    }
+
+    return returnValue != -1;
+#endif
+}
+
+#elif defined(Q_OS_WIN)
+
+QwtHighResolutionClock::QwtHighResolutionClock()
+{
+    d_startTicks.QuadPart = 0;
+    QueryPerformanceFrequency( &d_ticksPerSecond );
+}
+
+double QwtHighResolutionClock::precision()
+{
+    LARGE_INTEGER ticks;
+    if ( QueryPerformanceFrequency( &ticks ) && ticks.QuadPart > 0 )
+        return 1e3 / ticks.QuadPart;
+
+    return 0.0;
+}
+
+inline bool QwtHighResolutionClock::isNull() const
+{
+    return d_startTicks.QuadPart <= 0;
+}
+
+inline void QwtHighResolutionClock::start()
+{
+    QueryPerformanceCounter( &d_startTicks );
+}
+
+inline double QwtHighResolutionClock::restart()
+{
+    LARGE_INTEGER ticks;
+    QueryPerformanceCounter( &ticks );
+
+    const double dt = ticks.QuadPart - d_startTicks.QuadPart;
+    d_startTicks = ticks;
+
+    return dt / d_ticksPerSecond.QuadPart * 1e3;
+}
+
+inline double QwtHighResolutionClock::elapsed() const
+{
+    LARGE_INTEGER ticks;
+    QueryPerformanceCounter( &ticks );
+
+    const double dt = ticks.QuadPart - d_startTicks.QuadPart;
+    return dt / d_ticksPerSecond.QuadPart * 1e3;
+}
+
+#endif
+
+#endif // QWT_HIGH_RESOLUTION_CLOCK
+
+class QwtSystemClock::PrivateData
+{
+public:
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    QwtHighResolutionClock *clock;
+#endif
+    QTime time;
+};
+
+//!  Constructs a null clock object.
+QwtSystemClock::QwtSystemClock()
+{
+    d_data = new PrivateData;
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    d_data->clock = NULL;
+    if ( QwtHighResolutionClock::precision() > 0.0 )
+        d_data->clock = new QwtHighResolutionClock;
+#endif
+}
+
+//! Destructor
+QwtSystemClock::~QwtSystemClock()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    delete d_data->clock;
+#endif
+    delete d_data;
+}
+
+/*!
+  \return true if the clock has never been started.
+*/
+bool QwtSystemClock::isNull() const
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+        return d_data->clock->isNull();
+#endif
+
+    return d_data->time.isNull();
+}
+
+/*!
+  Sets the start time to the current time.
+*/
+void QwtSystemClock::start()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+    {
+        d_data->clock->start();
+        return;
+    }
+#endif
+
+    d_data->time.start();
+}
+
+/*!
+  Set the start time to the current time 
+  \return Time, that is elapsed since the previous start time.
+*/
+double QwtSystemClock::restart()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+        return d_data->clock->restart();
+#endif
+
+    return d_data->time.restart();
+}
+
+/*!
+  \return Number of milliseconds that have elapsed since the last time
+          start() or restart() was called or 0.0 for null clocks.
+*/
+double QwtSystemClock::elapsed() const
+{
+    double elapsed = 0.0;
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+    {
+        if ( !d_data->clock->isNull() )
+            elapsed = d_data->clock->elapsed();
+
+        return elapsed;
+    }
+#endif
+
+    if ( !d_data->time.isNull() )
+        elapsed = d_data->time.elapsed();
+
+    return elapsed;
+}
+
+#endif
diff --git a/qwt/qwt_system_clock.h b/qwt/qwt_system_clock.h
new file mode 100644
index 0000000..b134b11
--- /dev/null
+++ b/qwt/qwt_system_clock.h
@@ -0,0 +1,47 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SYSTEM_CLOCK_H
+#define QWT_SYSTEM_CLOCK_H
+
+#include "qwt_global.h"
+
+/*!
+  \brief QwtSystemClock provides high resolution clock time functions.
+
+  Sometimes the resolution offered by QTime ( millisecond ) is not accurate
+  enough for implementing time measurements ( f.e. sampling ).
+  QwtSystemClock offers a subset of the QTime functionality using higher
+  resolution timers ( if possible ).
+
+  Precision and time intervals are multiples of milliseconds (ms).
+
+  \note The implementation uses high-resolution performance counter on Windows,
+        mach_absolute_time() on the Mac or POSIX timers on other systems. 
+        If none is available it falls back on QTimer.
+*/
+
+class QWT_EXPORT QwtSystemClock
+{
+public:
+    QwtSystemClock();
+    virtual ~QwtSystemClock();
+
+    bool isNull() const;
+
+    void start();
+    double restart();
+    double elapsed() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_text.cpp b/qwt/qwt_text.cpp
new file mode 100644
index 0000000..b93798a
--- /dev/null
+++ b/qwt/qwt_text.cpp
@@ -0,0 +1,684 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_text.h"
+#include "qwt_painter.h"
+#include "qwt_text_engine.h"
+#include <qmap.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qbrush.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qmath.h>
+
+class QwtTextEngineDict
+{
+public:
+    static QwtTextEngineDict &dict();
+
+    void setTextEngine( QwtText::TextFormat, QwtTextEngine * );
+
+    const QwtTextEngine *textEngine( QwtText::TextFormat ) const;
+    const QwtTextEngine *textEngine( const QString &,
+        QwtText::TextFormat ) const;
+
+private:
+    QwtTextEngineDict();
+    ~QwtTextEngineDict();
+
+    typedef QMap<int, QwtTextEngine *> EngineMap;
+
+    inline const QwtTextEngine *engine( EngineMap::const_iterator &it ) const
+    {
+        return it.value();
+    }
+
+    EngineMap d_map;
+};
+
+QwtTextEngineDict &QwtTextEngineDict::dict()
+{
+    static QwtTextEngineDict engineDict;
+    return engineDict;
+}
+
+QwtTextEngineDict::QwtTextEngineDict()
+{
+    d_map.insert( QwtText::PlainText, new QwtPlainTextEngine() );
+#ifndef QT_NO_RICHTEXT
+    d_map.insert( QwtText::RichText, new QwtRichTextEngine() );
+#endif
+}
+
+QwtTextEngineDict::~QwtTextEngineDict()
+{
+    for ( EngineMap::const_iterator it = d_map.begin();
+        it != d_map.end(); ++it )
+    {
+        const QwtTextEngine *textEngine = engine( it );
+        delete textEngine;
+    }
+}
+
+const QwtTextEngine *QwtTextEngineDict::textEngine( const QString& text,
+    QwtText::TextFormat format ) const
+{
+    if ( format == QwtText::AutoText )
+    {
+        for ( EngineMap::const_iterator it = d_map.begin();
+            it != d_map.end(); ++it )
+        {
+            if ( it.key() != QwtText::PlainText )
+            {
+                const QwtTextEngine *e = engine( it );
+                if ( e && e->mightRender( text ) )
+                    return e;
+            }
+        }
+    }
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+    {
+        const QwtTextEngine *e = engine( it );
+        if ( e )
+            return e;
+    }
+
+    it = d_map.find( QwtText::PlainText );
+    return engine( it );
+}
+
+void QwtTextEngineDict::setTextEngine( QwtText::TextFormat format,
+    QwtTextEngine *engine )
+{
+    if ( format == QwtText::AutoText )
+        return;
+
+    if ( format == QwtText::PlainText && engine == NULL )
+        return;
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+    {
+        const QwtTextEngine *e = this->engine( it );
+        if ( e )
+            delete e;
+
+        d_map.remove( format );
+    }
+
+    if ( engine != NULL )
+        d_map.insert( format, engine );
+}
+
+const QwtTextEngine *QwtTextEngineDict::textEngine(
+    QwtText::TextFormat format ) const
+{
+    const QwtTextEngine *e = NULL;
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+        e = engine( it );
+
+    return e;
+}
+
+class QwtText::PrivateData
+{
+public:
+    PrivateData():
+        renderFlags( Qt::AlignCenter ),
+        borderRadius( 0 ),
+        borderPen( Qt::NoPen ),
+        backgroundBrush( Qt::NoBrush ),
+        paintAttributes( 0 ),
+        layoutAttributes( 0 ),
+        textEngine( NULL )
+    {
+    }
+
+    int renderFlags;
+    QString text;
+    QFont font;
+    QColor color;
+    double borderRadius;
+    QPen borderPen;
+    QBrush backgroundBrush;
+
+    QwtText::PaintAttributes paintAttributes;
+    QwtText::LayoutAttributes layoutAttributes;
+
+    const QwtTextEngine *textEngine;
+};
+
+class QwtText::LayoutCache
+{
+public:
+    void invalidate()
+    {
+        textSize = QSizeF();
+    }
+
+    QFont font;
+    QSizeF textSize;
+};
+
+/*!
+   Constructor
+
+   \param text Text content
+   \param textFormat Text format
+*/
+QwtText::QwtText( const QString &text, QwtText::TextFormat textFormat )
+{
+    d_data = new PrivateData;
+    d_data->text = text;
+    d_data->textEngine = textEngine( text, textFormat );
+
+    d_layoutCache = new LayoutCache;
+}
+
+//! Copy constructor
+QwtText::QwtText( const QwtText &other )
+{
+    d_data = new PrivateData;
+    *d_data = *other.d_data;
+
+    d_layoutCache = new LayoutCache;
+    *d_layoutCache = *other.d_layoutCache;
+}
+
+//! Destructor
+QwtText::~QwtText()
+{
+    delete d_data;
+    delete d_layoutCache;
+}
+
+//! Assignment operator
+QwtText &QwtText::operator=( const QwtText & other )
+{
+    *d_data = *other.d_data;
+    *d_layoutCache = *other.d_layoutCache;
+    return *this;
+}
+
+//! Relational operator
+bool QwtText::operator==( const QwtText &other ) const
+{
+    return d_data->renderFlags == other.d_data->renderFlags &&
+        d_data->text == other.d_data->text &&
+        d_data->font == other.d_data->font &&
+        d_data->color == other.d_data->color &&
+        d_data->borderRadius == other.d_data->borderRadius &&
+        d_data->borderPen == other.d_data->borderPen &&
+        d_data->backgroundBrush == other.d_data->backgroundBrush &&
+        d_data->paintAttributes == other.d_data->paintAttributes &&
+        d_data->textEngine == other.d_data->textEngine;
+}
+
+//! Relational operator
+bool QwtText::operator!=( const QwtText &other ) const // invalidate
+{
+    return !( other == *this );
+}
+
+/*!
+   Assign a new text content
+
+   \param text Text content
+   \param textFormat Text format
+
+   \sa text()
+*/
+void QwtText::setText( const QString &text,
+    QwtText::TextFormat textFormat )
+{
+    d_data->text = text;
+    d_data->textEngine = textEngine( text, textFormat );
+    d_layoutCache->invalidate();
+}
+
+/*!
+   \return Text as QString.
+   \sa setText()
+*/
+QString QwtText::text() const
+{
+    return d_data->text;
+}
+
+/*!
+   \brief Change the render flags
+
+   The default setting is Qt::AlignCenter
+
+   \param renderFlags Bitwise OR of the flags used like in QPainter::drawText()
+
+   \sa renderFlags(), QwtTextEngine::draw()
+   \note Some renderFlags might have no effect, depending on the text format.
+*/
+void QwtText::setRenderFlags( int renderFlags )
+{
+    if ( renderFlags != d_data->renderFlags )
+    {
+        d_data->renderFlags = renderFlags;
+        d_layoutCache->invalidate();
+    }
+}
+
+/*!
+   \return Render flags
+   \sa setRenderFlags()
+*/
+int QwtText::renderFlags() const
+{
+    return d_data->renderFlags;
+}
+
+/*!
+   Set the font.
+
+   \param font Font
+   \note Setting the font might have no effect, when
+         the text contains control sequences for setting fonts.
+*/
+void QwtText::setFont( const QFont &font )
+{
+    d_data->font = font;
+    setPaintAttribute( PaintUsingTextFont );
+}
+
+//! Return the font.
+QFont QwtText::font() const
+{
+    return d_data->font;
+}
+
+/*!
+   Return the font of the text, if it has one.
+   Otherwise return defaultFont.
+
+   \param defaultFont Default font
+   \return Font used for drawing the text
+
+   \sa setFont(), font(), PaintAttributes
+*/
+QFont QwtText::usedFont( const QFont &defaultFont ) const
+{
+    if ( d_data->paintAttributes & PaintUsingTextFont )
+        return d_data->font;
+
+    return defaultFont;
+}
+
+/*!
+   Set the pen color used for drawing the text.
+
+   \param color Color
+   \note Setting the color might have no effect, when
+         the text contains control sequences for setting colors.
+*/
+void QwtText::setColor( const QColor &color )
+{
+    d_data->color = color;
+    setPaintAttribute( PaintUsingTextColor );
+}
+
+//! Return the pen color, used for painting the text
+QColor QwtText::color() const
+{
+    return d_data->color;
+}
+
+/*!
+  Return the color of the text, if it has one.
+  Otherwise return defaultColor.
+
+  \param defaultColor Default color
+  \return Color used for drawing the text
+
+  \sa setColor(), color(), PaintAttributes
+*/
+QColor QwtText::usedColor( const QColor &defaultColor ) const
+{
+    if ( d_data->paintAttributes & PaintUsingTextColor )
+        return d_data->color;
+
+    return defaultColor;
+}
+
+/*!
+  Set the radius for the corners of the border frame
+
+  \param radius Radius of a rounded corner
+  \sa borderRadius(), setBorderPen(), setBackgroundBrush()
+*/
+void QwtText::setBorderRadius( double radius )
+{
+    d_data->borderRadius = qMax( 0.0, radius );
+}
+
+/*!
+  \return Radius for the corners of the border frame
+  \sa setBorderRadius(), borderPen(), backgroundBrush()
+*/
+double QwtText::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+   Set the background pen
+
+   \param pen Background pen
+   \sa borderPen(), setBackgroundBrush()
+*/
+void QwtText::setBorderPen( const QPen &pen )
+{
+    d_data->borderPen = pen;
+    setPaintAttribute( PaintBackground );
+}
+
+/*!
+   \return Background pen
+   \sa setBorderPen(), backgroundBrush()
+*/
+QPen QwtText::borderPen() const
+{
+    return d_data->borderPen;
+}
+
+/*!
+   Set the background brush
+
+   \param brush Background brush
+   \sa backgroundBrush(), setBorderPen()
+*/
+void QwtText::setBackgroundBrush( const QBrush &brush )
+{
+    d_data->backgroundBrush = brush;
+    setPaintAttribute( PaintBackground );
+}
+
+/*!
+   \return Background brush
+   \sa setBackgroundBrush(), borderPen()
+*/
+QBrush QwtText::backgroundBrush() const
+{
+    return d_data->backgroundBrush;
+}
+
+/*!
+   Change a paint attribute
+
+   \param attribute Paint attribute
+   \param on On/Off
+
+   \note Used by setFont(), setColor(),
+         setBorderPen() and setBackgroundBrush()
+   \sa testPaintAttribute()
+*/
+void QwtText::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+   Test a paint attribute
+
+   \param attribute Paint attribute
+   \return true, if attribute is enabled
+
+   \sa setPaintAttribute()
+*/
+bool QwtText::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return d_data->paintAttributes & attribute;
+}
+
+/*!
+   Change a layout attribute
+
+   \param attribute Layout attribute
+   \param on On/Off
+   \sa testLayoutAttribute()
+*/
+void QwtText::setLayoutAttribute( LayoutAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->layoutAttributes |= attribute;
+    else
+        d_data->layoutAttributes &= ~attribute;
+}
+
+/*!
+   Test a layout attribute
+
+   \param attribute Layout attribute
+   \return true, if attribute is enabled
+
+   \sa setLayoutAttribute()
+*/
+bool QwtText::testLayoutAttribute( LayoutAttribute attribute ) const
+{
+    return d_data->layoutAttributes | attribute;
+}
+
+/*!
+   Find the height for a given width
+
+   \param defaultFont Font, used for the calculation if the text has no font
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtText::heightForWidth( double width, const QFont &defaultFont ) const
+{
+    // We want to calculate in screen metrics. So
+    // we need a font that uses screen metrics
+
+    const QFont font( usedFont( defaultFont ), QApplication::desktop() );
+
+    double h = 0;
+
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins( font, d_data->text,
+            left, right, top, bottom );
+
+        h = d_data->textEngine->heightForWidth(
+            font, d_data->renderFlags, d_data->text,
+            width + left + right );
+
+        h -= top + bottom;
+    }
+    else
+    {
+        h = d_data->textEngine->heightForWidth(
+            font, d_data->renderFlags, d_data->text, width );
+    }
+
+    return h;
+}
+
+/*!
+   Find the height for a given width
+
+   \param defaultFont Font, used for the calculation if the text has no font
+
+   \return Calculated height
+*/
+
+/*!
+   Returns the size, that is needed to render text
+
+   \param defaultFont Font of the text
+   \return Caluclated size
+*/
+QSizeF QwtText::textSize( const QFont &defaultFont ) const
+{
+    // We want to calculate in screen metrics. So
+    // we need a font that uses screen metrics
+
+    const QFont font( usedFont( defaultFont ), QApplication::desktop() );
+
+    if ( !d_layoutCache->textSize.isValid()
+        || d_layoutCache->font != font )
+    {
+        d_layoutCache->textSize = d_data->textEngine->textSize(
+            font, d_data->renderFlags, d_data->text );
+        d_layoutCache->font = font;
+    }
+
+    QSizeF sz = d_layoutCache->textSize;
+
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins( font, d_data->text,
+            left, right, top, bottom );
+        sz -= QSizeF( left + right, top + bottom );
+    }
+
+    return sz;
+}
+
+/*!
+   Draw a text into a rectangle
+
+   \param painter Painter
+   \param rect Rectangle
+*/
+void QwtText::draw( QPainter *painter, const QRectF &rect ) const
+{
+    if ( d_data->paintAttributes & PaintBackground )
+    {
+        if ( d_data->borderPen != Qt::NoPen ||
+            d_data->backgroundBrush != Qt::NoBrush )
+        {
+            painter->save();
+
+            painter->setPen( d_data->borderPen );
+            painter->setBrush( d_data->backgroundBrush );
+
+            if ( d_data->borderRadius == 0 )
+            {
+                QwtPainter::drawRect( painter, rect );
+            }
+            else
+            {
+                painter->setRenderHint( QPainter::Antialiasing, true );
+                painter->drawRoundedRect( rect,
+                    d_data->borderRadius, d_data->borderRadius );
+            }
+
+            painter->restore();
+        }
+    }
+
+    painter->save();
+
+    if ( d_data->paintAttributes & PaintUsingTextFont )
+    {
+        painter->setFont( d_data->font );
+    }
+
+    if ( d_data->paintAttributes & PaintUsingTextColor )
+    {
+        if ( d_data->color.isValid() )
+            painter->setPen( d_data->color );
+    }
+
+    QRectF expandedRect = rect;
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        // We want to calculate in screen metrics. So
+        // we need a font that uses screen metrics
+
+        const QFont font( painter->font(), QApplication::desktop() );
+
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins(
+            font, d_data->text, left, right, top, bottom );
+
+        expandedRect.setTop( rect.top() - top );
+        expandedRect.setBottom( rect.bottom() + bottom );
+        expandedRect.setLeft( rect.left() - left );
+        expandedRect.setRight( rect.right() + right );
+    }
+
+    d_data->textEngine->draw( painter, expandedRect,
+        d_data->renderFlags, d_data->text );
+
+    painter->restore();
+}
+
+/*!
+   Find the text engine for a text format
+
+   In case of QwtText::AutoText the first text engine
+   (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender
+   returns true. If there is none QwtPlainTextEngine is returned.
+
+   If no text engine is registered for the format QwtPlainTextEngine
+   is returnd.
+
+   \param text Text, needed in case of AutoText
+   \param format Text format
+
+   \return Corresponding text engine
+*/
+const QwtTextEngine *QwtText::textEngine( const QString &text,
+    QwtText::TextFormat format )
+{
+    return QwtTextEngineDict::dict().textEngine( text, format );
+}
+
+/*!
+   Assign/Replace a text engine for a text format
+
+   With setTextEngine it is possible to extend Qwt with
+   other types of text formats.
+
+   For QwtText::PlainText it is not allowed to assign a engine == NULL.
+
+   \param format Text format
+   \param engine Text engine
+
+   \sa QwtMathMLTextEngine
+   \warning Using QwtText::AutoText does nothing.
+*/
+void QwtText::setTextEngine( QwtText::TextFormat format,
+    QwtTextEngine *engine )
+{
+    QwtTextEngineDict::dict().setTextEngine( format, engine );
+}
+
+/*!
+   \brief Find the text engine for a text format
+
+   textEngine can be used to find out if a text format is supported.
+
+   \param format Text format
+   \return The text engine, or NULL if no engine is available.
+*/
+const QwtTextEngine *QwtText::textEngine( QwtText::TextFormat format )
+{
+    return  QwtTextEngineDict::dict().textEngine( format );
+}
diff --git a/qwt/qwt_text.h b/qwt/qwt_text.h
new file mode 100644
index 0000000..e789690
--- /dev/null
+++ b/qwt/qwt_text.h
@@ -0,0 +1,223 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_H
+#define QWT_TEXT_H
+
+#include "qwt_global.h"
+#include <qstring.h>
+#include <qsize.h>
+#include <qfont.h>
+#include <qmetatype.h>
+
+class QColor;
+class QPen;
+class QBrush;
+class QRectF;
+class QPainter;
+class QwtTextEngine;
+
+/*!
+  \brief A class representing a text
+
+  A QwtText is a text including a set of attributes how to render it.
+
+  - Format\n
+    A text might include control sequences (f.e tags) describing
+    how to render it. Each format (f.e MathML, TeX, Qt Rich Text)
+    has its own set of control sequences, that can be handles by
+    a special QwtTextEngine for this format.
+  - Background\n
+    A text might have a background, defined by a QPen and QBrush
+    to improve its visibility. The corners of the background might
+    be rounded.
+  - Font\n
+    A text might have an individual font.
+  - Color\n
+    A text might have an individual color.
+  - Render Flags\n
+    Flags from Qt::AlignmentFlag and Qt::TextFlag used like in
+    QPainter::drawText().
+
+  \sa QwtTextEngine, QwtTextLabel
+*/
+
+class QWT_EXPORT QwtText
+{
+public:
+
+    /*!
+      \brief Text format
+
+      The text format defines the QwtTextEngine, that is used to render
+      the text.
+
+      \sa QwtTextEngine, setTextEngine()
+    */
+
+    enum TextFormat
+    {
+        /*!
+          The text format is determined using QwtTextEngine::mightRender() for
+          all available text engines in increasing order > PlainText.
+          If none of the text engines can render the text is rendered
+          like QwtText::PlainText.
+         */
+        AutoText = 0,
+
+        //! Draw the text as it is, using a QwtPlainTextEngine.
+        PlainText,
+
+        //! Use the Scribe framework (Qt Rich Text) to render the text.
+        RichText,
+
+        /*!
+          Use a MathML (http://en.wikipedia.org/wiki/MathML) render engine
+          to display the text. The Qwt MathML extension offers such an engine
+          based on the MathML renderer of the Qt solutions package. 
+          To enable MathML support the following code needs to be added to the
+          application:
+\verbatim QwtText::setTextEngine(QwtText::MathMLText, new QwtMathMLTextEngine()); \endverbatim
+         */
+        MathMLText,
+
+        /*!
+          Use a TeX (http://en.wikipedia.org/wiki/TeX) render engine
+          to display the text ( not implemented yet ).
+         */
+        TeXText,
+
+        /*!
+          The number of text formats can be extended using setTextEngine.
+          Formats >= QwtText::OtherFormat are not used by Qwt.
+         */
+        OtherFormat = 100
+    };
+
+    /*!
+      \brief Paint Attributes
+
+      Font and color and background are optional attributes of a QwtText.
+      The paint attributes hold the information, if they are set.
+    */
+    enum PaintAttribute
+    {
+        //! The text has an individual font.
+        PaintUsingTextFont = 0x01,
+
+        //! The text has an individual color.
+        PaintUsingTextColor = 0x02,
+
+        //! The text has an individual background.
+        PaintBackground = 0x04
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    /*!
+      \brief Layout Attributes
+      The layout attributes affects some aspects of the layout of the text.
+    */
+    enum LayoutAttribute
+    {
+        /*!
+          Layout the text without its margins. This mode is useful if a
+          text needs to be aligned accurately, like the tick labels of a scale.
+          If QwtTextEngine::textMargins is not implemented for the format
+          of the text, MinimumLayout has no effect.
+         */
+        MinimumLayout = 0x01
+    };
+
+    //! Layout attributes
+    typedef QFlags<LayoutAttribute> LayoutAttributes;
+
+    QwtText( const QString & = QString::null,
+             TextFormat textFormat = AutoText );
+    QwtText( const QwtText & );
+    ~QwtText();
+
+    QwtText &operator=( const QwtText & );
+
+    bool operator==( const QwtText & ) const;
+    bool operator!=( const QwtText & ) const;
+
+    void setText( const QString &,
+        QwtText::TextFormat textFormat = AutoText );
+    QString text() const;
+
+    bool isNull() const;
+    bool isEmpty() const;
+
+    void setFont( const QFont & );
+    QFont font() const;
+
+    QFont usedFont( const QFont & ) const;
+
+    void setRenderFlags( int flags );
+    int renderFlags() const;
+
+    void setColor( const QColor & );
+    QColor color() const;
+
+    QColor usedColor( const QColor & ) const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setBorderPen( const QPen & );
+    QPen borderPen() const;
+
+    void setBackgroundBrush( const QBrush & );
+    QBrush backgroundBrush() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLayoutAttribute( LayoutAttribute, bool on = true );
+    bool testLayoutAttribute( LayoutAttribute ) const;
+
+    double heightForWidth( double width, const QFont & = QFont() ) const;
+    QSizeF textSize( const QFont & = QFont() ) const;
+
+    void draw( QPainter *painter, const QRectF &rect ) const;
+
+    static const QwtTextEngine *textEngine( 
+        const QString &text, QwtText::TextFormat = AutoText );
+
+    static const QwtTextEngine *textEngine( QwtText::TextFormat );
+    static void setTextEngine( QwtText::TextFormat, QwtTextEngine * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+
+    class LayoutCache;
+    LayoutCache *d_layoutCache;
+};
+
+//! \return text().isNull()
+inline bool QwtText::isNull() const
+{
+    return text().isNull();
+}
+
+//! \return text().isEmpty()
+inline bool QwtText::isEmpty() const
+{
+    return text().isEmpty();
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::PaintAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::LayoutAttributes )
+
+Q_DECLARE_METATYPE( QwtText )
+
+#endif
diff --git a/qwt/qwt_text_engine.cpp b/qwt/qwt_text_engine.cpp
new file mode 100644
index 0000000..5991588
--- /dev/null
+++ b/qwt/qwt_text_engine.cpp
@@ -0,0 +1,345 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_text_engine.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qwidget.h>
+#include <qtextobject.h>
+#include <qtextdocument.h>
+#include <qabstracttextdocumentlayout.h>
+
+static QString taggedRichText( const QString &text, int flags )
+{
+    QString richText = text;
+
+    // By default QSimpleRichText is Qt::AlignLeft
+    if ( flags & Qt::AlignJustify )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"justify\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+    else if ( flags & Qt::AlignRight )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"right\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+    else if ( flags & Qt::AlignHCenter )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"center\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+
+    return richText;
+}
+
+class QwtRichTextDocument: public QTextDocument
+{
+public:
+    QwtRichTextDocument( const QString &text, int flags, const QFont &font )
+    {
+        setUndoRedoEnabled( false );
+        setDefaultFont( font );
+        setHtml( text );
+
+        // make sure we have a document layout
+        ( void )documentLayout();
+
+        QTextOption option = defaultTextOption();
+        if ( flags & Qt::TextWordWrap )
+            option.setWrapMode( QTextOption::WordWrap );
+        else
+            option.setWrapMode( QTextOption::NoWrap );
+
+        option.setAlignment( static_cast<Qt::Alignment>( flags ) );
+        setDefaultTextOption( option );
+
+        QTextFrame *root = rootFrame();
+        QTextFrameFormat fm = root->frameFormat();
+        fm.setBorder( 0 );
+        fm.setMargin( 0 );
+        fm.setPadding( 0 );
+        fm.setBottomMargin( 0 );
+        fm.setLeftMargin( 0 );
+        root->setFrameFormat( fm );
+
+        adjustSize();
+    }
+};
+
+class QwtPlainTextEngine::PrivateData
+{
+public:
+    int effectiveAscent( const QFont &font ) const
+    {
+        const QString fontKey = font.key();
+
+        QMap<QString, int>::const_iterator it =
+            d_ascentCache.find( fontKey );
+        if ( it == d_ascentCache.end() )
+        {
+            int ascent = findAscent( font );
+            it = d_ascentCache.insert( fontKey, ascent );
+        }
+
+        return ( *it );
+    }
+
+private:
+    int findAscent( const QFont &font ) const
+    {
+        static const QString dummy( "E" );
+        static const QColor white( Qt::white );
+
+        const QFontMetrics fm( font );
+        QPixmap pm( fm.width( dummy ), fm.height() );
+        pm.fill( white );
+
+        QPainter p( &pm );
+        p.setFont( font );
+        p.drawText( 0, 0,  pm.width(), pm.height(), 0, dummy );
+        p.end();
+
+        const QImage img = pm.toImage();
+
+        int row = 0;
+        for ( row = 0; row < img.height(); row++ )
+        {
+            const QRgb *line = reinterpret_cast<const QRgb *>( 
+                img.scanLine( row ) );
+
+            const int w = pm.width();
+            for ( int col = 0; col < w; col++ )
+            {
+                if ( line[col] != white.rgb() )
+                    return fm.ascent() - row + 1;
+            }
+        }
+
+        return fm.ascent();
+    }
+
+    mutable QMap<QString, int> d_ascentCache;
+};
+
+//! Constructor
+QwtTextEngine::QwtTextEngine()
+{
+}
+
+//! Destructor
+QwtTextEngine::~QwtTextEngine()
+{
+}
+
+//! Constructor
+QwtPlainTextEngine::QwtPlainTextEngine()
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlainTextEngine::~QwtPlainTextEngine()
+{
+    delete d_data;
+}
+
+/*!
+   Find the height for a given width
+
+   \param font Font of the text
+   \param flags Bitwise OR of the flags used like in QPainter::drawText
+   \param text Text to be rendered
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtPlainTextEngine::heightForWidth( const QFont& font, int flags,
+        const QString& text, double width ) const
+{
+    const QFontMetricsF fm( font );
+    const QRectF rect = fm.boundingRect(
+        QRectF( 0, 0, width, QWIDGETSIZE_MAX ), flags, text );
+
+    return rect.height();
+}
+
+/*!
+  Returns the size, that is needed to render text
+
+  \param font Font of the text
+  \param flags Bitwise OR of the flags used like in QPainter::drawText
+  \param text Text to be rendered
+
+  \return Caluclated size
+*/
+QSizeF QwtPlainTextEngine::textSize( const QFont &font,
+    int flags, const QString& text ) const
+{
+    const QFontMetricsF fm( font );
+    const QRectF rect = fm.boundingRect(
+        QRectF( 0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ), flags, text );
+
+    return rect.size();
+}
+
+/*!
+  Return margins around the texts
+
+  \param font Font of the text
+  \param left Return 0
+  \param right Return 0
+  \param top Return value for the top margin
+  \param bottom Return value for the bottom margin
+*/
+void QwtPlainTextEngine::textMargins( const QFont &font, const QString &,
+    double &left, double &right, double &top, double &bottom ) const
+{
+    left = right = top = 0;
+
+    const QFontMetricsF fm( font );
+    top = fm.ascent() - d_data->effectiveAscent( font );
+    bottom = fm.descent();
+}
+
+/*!
+  \brief Draw the text in a clipping rectangle
+
+  A wrapper for QPainter::drawText.
+
+  \param painter Painter
+  \param rect Clipping rectangle
+  \param flags Bitwise OR of the flags used like in QPainter::drawText
+  \param text Text to be rendered
+*/
+void QwtPlainTextEngine::draw( QPainter *painter, const QRectF &rect,
+    int flags, const QString& text ) const
+{
+    QwtPainter::drawText( painter, rect, flags, text );
+}
+
+/*!
+  Test if a string can be rendered by this text engine.
+  \return Always true. All texts can be rendered by QwtPlainTextEngine
+*/
+bool QwtPlainTextEngine::mightRender( const QString & ) const
+{
+    return true;
+}
+
+#ifndef QT_NO_RICHTEXT
+
+//! Constructor
+QwtRichTextEngine::QwtRichTextEngine()
+{
+}
+
+/*!
+   Find the height for a given width
+
+   \param font Font of the text
+   \param flags Bitwise OR of the flags used like in QPainter::drawText()
+   \param text Text to be rendered
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtRichTextEngine::heightForWidth( const QFont& font, int flags,
+        const QString& text, double width ) const
+{
+    QwtRichTextDocument doc( text, flags, font );
+
+    doc.setPageSize( QSizeF( width, QWIDGETSIZE_MAX ) );
+    return doc.documentLayout()->documentSize().height();
+}
+
+/*!
+  Returns the size, that is needed to render text
+
+  \param font Font of the text
+  \param flags Bitwise OR of the flags used like in QPainter::drawText()
+  \param text Text to be rendered
+
+  \return Caluclated size
+*/
+
+QSizeF QwtRichTextEngine::textSize( const QFont &font,
+    int flags, const QString& text ) const
+{
+    QwtRichTextDocument doc( text, flags, font );
+
+    QTextOption option = doc.defaultTextOption();
+    if ( option.wrapMode() != QTextOption::NoWrap )
+    {
+        option.setWrapMode( QTextOption::NoWrap );
+        doc.setDefaultTextOption( option );
+        doc.adjustSize();
+    }
+
+    return doc.size();
+}
+
+/*!
+  Draw the text in a clipping rectangle
+
+  \param painter Painter
+  \param rect Clipping rectangle
+  \param flags Bitwise OR of the flags like in for QPainter::drawText()
+  \param text Text to be rendered
+*/
+void QwtRichTextEngine::draw( QPainter *painter, const QRectF &rect,
+    int flags, const QString& text ) const
+{
+    QwtRichTextDocument doc( text, flags, painter->font() );
+    QwtPainter::drawSimpleRichText( painter, rect, flags, doc );
+}
+
+/*!
+   Wrap text into <div align=...> </div> tags according flags
+
+   \param text Text
+   \param flags Bitwise OR of the flags like in for QPainter::drawText()
+
+   \return Tagged text
+*/
+QString QwtRichTextEngine::taggedText( const QString &text, int flags ) const
+{
+    return taggedRichText( text, flags );
+}
+
+/*!
+  Test if a string can be rendered by this text engine
+
+  \param text Text to be tested
+  \return Qt::mightBeRichText(text);
+*/
+bool QwtRichTextEngine::mightRender( const QString &text ) const
+{
+    return Qt::mightBeRichText( text );
+}
+
+/*!
+  Return margins around the texts
+
+  \param left Return 0
+  \param right Return 0
+  \param top Return 0
+  \param bottom Return 0
+*/
+void QwtRichTextEngine::textMargins( const QFont &, const QString &,
+    double &left, double &right, double &top, double &bottom ) const
+{
+    left = right = top = bottom = 0;
+}
+
+#endif // !QT_NO_RICHTEXT
diff --git a/qwt/qwt_text_engine.h b/qwt/qwt_text_engine.h
new file mode 100644
index 0000000..927ec4e
--- /dev/null
+++ b/qwt/qwt_text_engine.h
@@ -0,0 +1,172 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2003   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_ENGINE_H
+#define QWT_TEXT_ENGINE_H 1
+
+#include "qwt_global.h"
+#include <qsize.h>
+
+class QFont;
+class QRectF;
+class QString;
+class QPainter;
+
+/*!
+  \brief Abstract base class for rendering text strings
+
+  A text engine is responsible for rendering texts for a
+  specific text format. They are used by QwtText to render a text.
+
+  QwtPlainTextEngine and QwtRichTextEngine are part of the Qwt library.
+  The implementation of QwtMathMLTextEngine uses code from the 
+  Qt solution package. Because of license implications it is built into
+  a separate library.
+ 
+  \sa QwtText::setTextEngine()
+*/
+
+class QWT_EXPORT QwtTextEngine
+{
+public:
+    virtual ~QwtTextEngine();
+
+    /*!
+      Find the height for a given width
+
+      \param font Font of the text
+      \param flags Bitwise OR of the flags used like in QPainter::drawText
+      \param text Text to be rendered
+      \param width Width
+
+      \return Calculated height
+     */
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const = 0;
+
+    /*!
+      Returns the size, that is needed to render text
+
+      \param font Font of the text
+      \param flags Bitwise OR of the flags like in for QPainter::drawText
+      \param text Text to be rendered
+
+      \return Calculated size
+     */
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const = 0;
+
+    /*!
+      Test if a string can be rendered by this text engine
+
+      \param text Text to be tested
+      \return true, if it can be rendered
+     */
+    virtual bool mightRender( const QString &text ) const = 0;
+
+    /*!
+      Return margins around the texts
+
+      The textSize might include margins around the
+      text, like QFontMetrics::descent(). In situations
+      where texts need to be aligned in detail, knowing
+      these margins might improve the layout calculations.
+
+      \param font Font of the text
+      \param text Text to be rendered
+      \param left Return value for the left margin
+      \param right Return value for the right margin
+      \param top Return value for the top margin
+      \param bottom Return value for the bottom margin
+     */
+    virtual void textMargins( const QFont &font, const QString &text,
+        double &left, double &right, double &top, double &bottom ) const = 0;
+
+    /*!
+      Draw the text in a clipping rectangle
+
+      \param painter Painter
+      \param rect Clipping rectangle
+      \param flags Bitwise OR of the flags like in for QPainter::drawText()
+      \param text Text to be rendered
+     */
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const = 0;
+
+protected:
+    QwtTextEngine();
+};
+
+
+/*!
+  \brief A text engine for plain texts
+
+  QwtPlainTextEngine renders texts using the basic Qt classes
+  QPainter and QFontMetrics.
+*/
+class QWT_EXPORT QwtPlainTextEngine: public QwtTextEngine
+{
+public:
+    QwtPlainTextEngine();
+    virtual ~QwtPlainTextEngine();
+
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const;
+
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const;
+
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const;
+
+    virtual bool mightRender( const QString & ) const;
+
+    virtual void textMargins( const QFont &, const QString &,
+        double &left, double &right, double &top, double &bottom ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+
+#ifndef QT_NO_RICHTEXT
+
+/*!
+  \brief A text engine for Qt rich texts
+
+  QwtRichTextEngine renders Qt rich texts using the classes
+  of the Scribe framework of Qt.
+*/
+class QWT_EXPORT QwtRichTextEngine: public QwtTextEngine
+{
+public:
+    QwtRichTextEngine();
+
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const;
+
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const;
+
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const;
+
+    virtual bool mightRender( const QString & ) const;
+
+    virtual void textMargins( const QFont &, const QString &,
+        double &left, double &right, double &top, double &bottom ) const;
+
+private:
+    QString taggedText( const QString &, int flags ) const;
+};
+
+#endif // !QT_NO_RICHTEXT
+
+#endif
diff --git a/qwt/qwt_text_label.cpp b/qwt/qwt_text_label.cpp
new file mode 100644
index 0000000..0b58baa
--- /dev/null
+++ b/qwt/qwt_text_label.cpp
@@ -0,0 +1,324 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_text_label.h"
+#include "qwt_text.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qmath.h>
+
+class QwtTextLabel::PrivateData
+{
+public:
+    PrivateData():
+        indent( 4 ),
+        margin( 0 )
+    {
+    }
+
+    int indent;
+    int margin;
+    QwtText text;
+};
+
+/*!
+  Constructs an empty label.
+  \param parent Parent widget
+*/
+QwtTextLabel::QwtTextLabel( QWidget *parent ):
+    QFrame( parent )
+{
+    init();
+}
+
+/*!
+  Constructs a label that displays the text, text
+  \param parent Parent widget
+  \param text Text
+*/
+QwtTextLabel::QwtTextLabel( const QwtText &text, QWidget *parent ):
+    QFrame( parent )
+{
+    init();
+    d_data->text = text;
+}
+
+//! Destructor
+QwtTextLabel::~QwtTextLabel()
+{
+    delete d_data;
+}
+
+void QwtTextLabel::init()
+{
+    d_data = new PrivateData();
+    setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
+}
+
+/*!
+   Interface for the designer plugin - does the same as setText()
+   \sa plainText()
+ */
+void QwtTextLabel::setPlainText( const QString &text )
+{
+    setText( QwtText( text ) );
+}
+
+/*!
+   Interface for the designer plugin
+
+   \return Text as plain text
+   \sa setPlainText(), text()
+ */
+QString QwtTextLabel::plainText() const
+{
+    return d_data->text.text();
+}
+
+/*!
+   Change the label's text, keeping all other QwtText attributes
+   \param text New text
+   \param textFormat Format of text
+
+  \sa QwtText
+*/
+void QwtTextLabel::setText( const QString &text, 
+    QwtText::TextFormat textFormat )
+{
+    d_data->text.setText( text, textFormat );
+
+    update();
+    updateGeometry();
+}
+
+/*!
+   Change the label's text
+   \param text New text
+*/
+void QwtTextLabel::setText( const QwtText &text )
+{
+    d_data->text = text;
+
+    update();
+    updateGeometry();
+}
+
+//! Return the text
+const QwtText &QwtTextLabel::text() const
+{
+    return d_data->text;
+}
+
+//! Clear the text and all QwtText attributes
+void QwtTextLabel::clear()
+{
+    d_data->text = QwtText();
+
+    update();
+    updateGeometry();
+}
+
+//! Return label's text indent in pixels
+int QwtTextLabel::indent() const
+{
+    return d_data->indent;
+}
+
+/*!
+  Set label's text indent in pixels
+  \param indent Indentation in pixels
+*/
+void QwtTextLabel::setIndent( int indent )
+{
+    if ( indent < 0 )
+        indent = 0;
+
+    d_data->indent = indent;
+
+    update();
+    updateGeometry();
+}
+
+//! Return label's text indent in pixels
+int QwtTextLabel::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+  Set label's margin in pixels
+  \param margin Margin in pixels
+*/
+void QwtTextLabel::setMargin( int margin )
+{
+    d_data->margin = margin;
+
+    update();
+    updateGeometry();
+}
+
+//! Return label's margin in pixels
+QSize QwtTextLabel::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+//! Return a minimum size hint
+QSize QwtTextLabel::minimumSizeHint() const
+{
+    QSizeF sz = d_data->text.textSize( font() );
+
+    int mw = 2 * ( frameWidth() + d_data->margin );
+    int mh = mw;
+
+    int indent = d_data->indent;
+    if ( indent <= 0 )
+        indent = defaultIndent();
+
+    if ( indent > 0 )
+    {
+        const int align = d_data->text.renderFlags();
+        if ( align & Qt::AlignLeft || align & Qt::AlignRight )
+            mw += d_data->indent;
+        else if ( align & Qt::AlignTop || align & Qt::AlignBottom )
+            mh += d_data->indent;
+    }
+
+    sz += QSizeF( mw, mh );
+
+    return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
+}
+
+/*!
+   \param width Width
+   \return Preferred height for this widget, given the width.
+*/
+int QwtTextLabel::heightForWidth( int width ) const
+{
+    const int renderFlags = d_data->text.renderFlags();
+
+    int indent = d_data->indent;
+    if ( indent <= 0 )
+        indent = defaultIndent();
+
+    width -= 2 * frameWidth();
+    if ( renderFlags & Qt::AlignLeft || renderFlags & Qt::AlignRight )
+        width -= indent;
+
+    int height = qCeil( d_data->text.heightForWidth( width, font() ) );
+    if ( ( renderFlags & Qt::AlignTop ) || ( renderFlags & Qt::AlignBottom ) )
+        height += indent;
+
+    height += 2 * frameWidth();
+
+    return height;
+}
+
+/*!
+   Qt paint event
+   \param event Paint event
+*/
+void QwtTextLabel::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+
+    if ( !contentsRect().contains( event->rect() ) )
+    {
+        painter.save();
+        painter.setClipRegion( event->region() & frameRect() );
+        drawFrame( &painter );
+        painter.restore();
+    }
+
+    painter.setClipRegion( event->region() & contentsRect() );
+
+    drawContents( &painter );
+}
+
+//! Redraw the text and focus indicator
+void QwtTextLabel::drawContents( QPainter *painter )
+{
+    const QRect r = textRect();
+    if ( r.isEmpty() )
+        return;
+
+    painter->setFont( font() );
+    painter->setPen( palette().color( QPalette::Active, QPalette::Text ) );
+
+    drawText( painter, QRectF( r ) );
+
+    if ( hasFocus() )
+    {
+        const int m = 2;
+
+        QRect focusRect = contentsRect().adjusted( m, m, -m + 1, -m + 1);
+
+        QwtPainter::drawFocusRect( painter, this, focusRect );
+    }
+}
+
+//! Redraw the text
+void QwtTextLabel::drawText( QPainter *painter, const QRectF &textRect )
+{
+    d_data->text.draw( painter, textRect );
+}
+
+/*!
+  Calculate geometry for the text in widget coordinates
+  \return Geometry for the text
+*/
+QRect QwtTextLabel::textRect() const
+{
+    QRect r = contentsRect();
+
+    if ( !r.isEmpty() && d_data->margin > 0 )
+    {
+        r.setRect( r.x() + d_data->margin, r.y() + d_data->margin,
+            r.width() - 2 * d_data->margin, r.height() - 2 * d_data->margin );
+    }
+
+    if ( !r.isEmpty() )
+    {
+        int indent = d_data->indent;
+        if ( indent <= 0 )
+            indent = defaultIndent();
+
+        if ( indent > 0 )
+        {
+            const int renderFlags = d_data->text.renderFlags();
+
+            if ( renderFlags & Qt::AlignLeft )
+                r.setX( r.x() + indent );
+            else if ( renderFlags & Qt::AlignRight )
+                r.setWidth( r.width() - indent );
+            else if ( renderFlags & Qt::AlignTop )
+                r.setY( r.y() + indent );
+            else if ( renderFlags & Qt::AlignBottom )
+                r.setHeight( r.height() - indent );
+        }
+    }
+
+    return r;
+}
+
+int QwtTextLabel::defaultIndent() const
+{
+    if ( frameWidth() <= 0 )
+        return 0;
+
+    QFont fnt;
+    if ( d_data->text.testPaintAttribute( QwtText::PaintUsingTextFont ) )
+        fnt = d_data->text.font();
+    else
+        fnt = font();
+
+    return QFontMetrics( fnt ).width( 'x' ) / 2;
+}
+
diff --git a/qwt/qwt_text_label.h b/qwt/qwt_text_label.h
new file mode 100644
index 0000000..c481a24
--- /dev/null
+++ b/qwt/qwt_text_label.h
@@ -0,0 +1,77 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_LABEL_H
+#define QWT_TEXT_LABEL_H
+
+#include "qwt_global.h"
+#include "qwt_text.h"
+#include <qframe.h>
+
+class QString;
+class QPaintEvent;
+class QPainter;
+
+/*!
+   \brief A Widget which displays a QwtText
+*/
+
+class QWT_EXPORT QwtTextLabel : public QFrame
+{
+    Q_OBJECT
+
+    Q_PROPERTY( int indent READ indent WRITE setIndent )
+    Q_PROPERTY( int margin READ margin WRITE setMargin )
+    Q_PROPERTY( QString plainText READ plainText WRITE setPlainText )
+
+public:
+    explicit QwtTextLabel( QWidget *parent = NULL );
+    explicit QwtTextLabel( const QwtText &, QWidget *parent = NULL );
+    virtual ~QwtTextLabel();
+
+    void setPlainText( const QString & );
+    QString plainText() const;
+
+public Q_SLOTS:
+    void setText( const QString &,
+        QwtText::TextFormat textFormat = QwtText::AutoText );
+    virtual void setText( const QwtText & );
+
+    void clear();
+
+public:
+    const QwtText &text() const;
+
+    int indent() const;
+    void setIndent( int );
+
+    int margin() const;
+    void setMargin( int );
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+    virtual int heightForWidth( int ) const;
+
+    QRect textRect() const;
+
+    virtual void drawText( QPainter *, const QRectF & );
+
+protected:
+    virtual void paintEvent( QPaintEvent *e );
+    virtual void drawContents( QPainter * );
+
+private:
+    void init();
+    int defaultIndent() const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_thermo.cpp b/qwt/qwt_thermo.cpp
new file mode 100644
index 0000000..ea641b2
--- /dev/null
+++ b/qwt/qwt_thermo.cpp
@@ -0,0 +1,1004 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_thermo.h"
+#include "qwt_scale_engine.h"
+#include "qwt_scale_draw.h"
+#include "qwt_scale_map.h"
+#include "qwt_color_map.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qmath.h>
+
+static inline void qwtDrawLine( QPainter *painter, int pos, 
+    const QColor &color, const QRect &pipeRect, const QRect &liquidRect,
+    Qt::Orientation orientation )
+{
+    painter->setPen( color );
+    if ( orientation == Qt::Horizontal )
+    {
+        if ( pos >= liquidRect.left() && pos < liquidRect.right() )
+            painter->drawLine( pos, pipeRect.top(), pos, pipeRect.bottom() );
+    }
+    else
+    {
+        if ( pos >= liquidRect.top() && pos < liquidRect.bottom() )
+            painter->drawLine( pipeRect.left(), pos, pipeRect.right(), pos );
+    }
+}
+
+QVector<double> qwtTickList( const QwtScaleDiv &scaleDiv )
+{
+    QVector<double> values;
+
+    double lowerLimit = scaleDiv.interval().minValue();
+    double upperLimit = scaleDiv.interval().maxValue();
+
+    if ( upperLimit < lowerLimit )
+        qSwap( lowerLimit, upperLimit );
+
+    values += lowerLimit;
+
+    for ( int tickType = QwtScaleDiv::MinorTick;
+        tickType < QwtScaleDiv::NTickTypes; tickType++ )
+    {
+        const QList<double> ticks = scaleDiv.ticks( tickType );
+
+        for ( int i = 0; i < ticks.count(); i++ )
+        {
+            const double v = ticks[i];
+            if ( v > lowerLimit && v < upperLimit )
+                values += v;
+        }       
+    }   
+
+    values += upperLimit;
+    
+    return values;
+}
+
+class QwtThermo::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Vertical ),
+        scalePosition( QwtThermo::TrailingScale ),
+        spacing( 3 ),
+        borderWidth( 2 ),
+        pipeWidth( 10 ),
+        alarmLevel( 0.0 ),
+        alarmEnabled( false ),
+        autoFillPipe( true ),
+        originMode( QwtThermo::OriginMinimum ),
+        origin( 0.0 ),
+        colorMap( NULL ),
+        value( 0.0 )
+    {
+        rangeFlags = QwtInterval::IncludeBorders;
+    }
+
+    ~PrivateData()
+    {
+        delete colorMap;
+    }
+
+    Qt::Orientation orientation;
+    QwtThermo::ScalePosition scalePosition;
+
+    int spacing;
+    int borderWidth;
+    int pipeWidth;
+
+    QwtInterval::BorderFlags rangeFlags;
+    double alarmLevel;
+    bool alarmEnabled;
+    bool autoFillPipe;
+    QwtThermo::OriginMode originMode;
+    double origin;
+
+    QwtColorMap *colorMap;
+
+    double value;
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtThermo::QwtThermo( QWidget *parent ):
+    QwtAbstractScale( parent )
+{
+    d_data = new PrivateData;
+
+    QSizePolicy policy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
+    if ( d_data->orientation == Qt::Vertical )
+        policy.transpose();
+
+    setSizePolicy( policy );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    layoutThermo( true );
+}
+
+//! Destructor
+QwtThermo::~QwtThermo()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Exclude/Include min/max values
+
+  According to the flags minValue() and maxValue()
+  are included/excluded from the pipe. In case of an
+  excluded value the corresponding tick is painted
+  1 pixel off of the pipeRect().
+
+  F.e. when a minimum
+  of 0.0 has to be displayed as an empty pipe the minValue()
+  needs to be excluded.
+
+  \param flags Range flags
+  \sa rangeFlags()
+*/
+void QwtThermo::setRangeFlags( QwtInterval::BorderFlags flags )
+{
+    if ( d_data->rangeFlags != flags )
+    {
+        d_data->rangeFlags = flags;
+        update();
+    }
+}
+
+/*!
+  \return Range flags
+  \sa setRangeFlags()
+*/
+QwtInterval::BorderFlags QwtThermo::rangeFlags() const
+{
+    return d_data->rangeFlags;
+}
+
+/*!
+  Set the current value.
+
+  \param value New Value
+  \sa value()
+*/
+void QwtThermo::setValue( double value )
+{
+    if ( d_data->value != value )
+    {
+        d_data->value = value;
+        update();
+    }
+}
+
+//! Return the value.
+double QwtThermo::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief Set a scale draw
+
+  For changing the labels of the scales, it
+  is necessary to derive from QwtScaleDraw and
+  overload QwtScaleDraw::label().
+
+  \param scaleDraw ScaleDraw object, that has to be created with
+                   new and will be deleted in ~QwtThermo() or the next
+                   call of setScaleDraw().
+*/
+void QwtThermo::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+}
+
+/*!
+   \return the scale draw of the thermo
+   \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtThermo::scaleDraw() const
+{
+    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+   \return the scale draw of the thermo
+   \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtThermo::scaleDraw()
+{
+    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Paint event handler
+  \param event Paint event
+*/
+void QwtThermo::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    const QRect tRect = pipeRect();
+
+    if ( !tRect.contains( event->rect() ) )
+    {
+        if ( d_data->scalePosition != QwtThermo::NoScale )
+            scaleDraw()->draw( &painter, palette() );
+    }
+
+    const int bw = d_data->borderWidth;
+
+    const QBrush brush = palette().brush( QPalette::Base );
+    qDrawShadePanel( &painter, 
+        tRect.adjusted( -bw, -bw, bw, bw ),
+        palette(), true, bw, 
+        d_data->autoFillPipe ? &brush : NULL );
+
+    drawLiquid( &painter, tRect );
+}
+
+/*! 
+  Resize event handler
+  \param event Resize event
+*/
+void QwtThermo::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+    layoutThermo( false );
+}
+
+/*! 
+  Qt change event handler
+  \param event Event
+*/
+void QwtThermo::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::StyleChange:
+        case QEvent::FontChange:
+        {
+            layoutThermo( true );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Recalculate the QwtThermo geometry and layout based on
+  pipeRect() and the fonts.
+
+  \param update_geometry notify the layout system and call update
+         to redraw the scale
+*/
+void QwtThermo::layoutThermo( bool update_geometry )
+{
+    const QRect tRect = pipeRect();
+    const int bw = d_data->borderWidth + d_data->spacing;
+    const bool inverted = ( upperBound() < lowerBound() );
+
+    int from, to;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        from = tRect.left();
+        to = tRect.right();
+
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
+        {
+            if ( inverted )
+                to++;
+            else
+                from--;
+        }
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
+        {
+            if ( inverted )
+                from--;
+            else
+                to++;
+        }
+
+        if ( d_data->scalePosition == QwtThermo::TrailingScale )
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::TopScale );
+            scaleDraw()->move( from, tRect.top() - bw );
+        }
+        else
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::BottomScale );
+            scaleDraw()->move( from, tRect.bottom() + bw );
+        }
+
+        scaleDraw()->setLength( to - from );
+    }
+    else // Qt::Vertical
+    {
+        from = tRect.top();
+        to = tRect.bottom();
+
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
+        {
+            if ( inverted )
+                from--;
+            else
+                to++;
+        }
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
+        {
+            if ( inverted )
+                to++;
+            else
+                from--;
+        }
+
+        if ( d_data->scalePosition == QwtThermo::LeadingScale )
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::RightScale );
+            scaleDraw()->move( tRect.right() + bw, from );
+        }
+        else
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::LeftScale );
+            scaleDraw()->move( tRect.left() - bw, from );
+        }
+
+        scaleDraw()->setLength( to - from );
+    }
+
+    if ( update_geometry )
+    {
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  \return Bounding rectangle of the pipe ( without borders )
+          in widget coordinates
+*/
+QRect QwtThermo::pipeRect() const
+{
+    int mbd = 0;
+    if ( d_data->scalePosition != QwtThermo::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+        mbd = qMax( d1, d2 );
+    }
+    const int bw = d_data->borderWidth;
+    const int scaleOff = bw + mbd;
+
+    const QRect cr = contentsRect();
+
+    QRect pipeRect = cr;
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        pipeRect.adjust( scaleOff, 0, -scaleOff, 0 );
+
+        if ( d_data->scalePosition == QwtThermo::TrailingScale )
+            pipeRect.setTop( cr.top() + cr.height() - bw - d_data->pipeWidth );
+        else
+            pipeRect.setTop( bw );
+
+        pipeRect.setHeight( d_data->pipeWidth );
+    }
+    else // Qt::Vertical
+    {
+        pipeRect.adjust( 0, scaleOff, 0, -scaleOff );
+
+        if ( d_data->scalePosition == QwtThermo::LeadingScale )
+            pipeRect.setLeft( bw );
+        else 
+            pipeRect.setLeft( cr.left() + cr.width() - bw - d_data->pipeWidth );
+
+        pipeRect.setWidth( d_data->pipeWidth );
+    }
+
+    return pipeRect;
+}
+
+/*!
+  \brief Set the orientation.
+  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
+
+  \sa orientation(), scalePosition()
+*/
+void QwtThermo::setOrientation( Qt::Orientation orientation )
+{
+    if ( orientation == d_data->orientation )
+        return;
+
+    d_data->orientation = orientation;
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    layoutThermo( true );
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtThermo::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Change how the origin is determined.
+  \sa originMode(), serOrigin(), origin()
+ */
+void QwtThermo::setOriginMode( OriginMode m )
+{
+    if ( m == d_data->originMode )
+        return;
+
+    d_data->originMode = m;
+    update();
+}
+
+/*!
+  \return Mode, how the origin is determined.
+  \sa setOriginMode(), serOrigin(), origin()
+ */
+QwtThermo::OriginMode QwtThermo::originMode() const
+{
+    return d_data->originMode;
+}
+
+/*!
+  \brief Specifies the custom origin.
+
+  If originMode is set to OriginCustom this property controls where the
+  liquid starts.
+
+  \param origin New origin level
+  \sa setOriginMode(), originMode(), origin()
+ */
+void QwtThermo::setOrigin( double origin )
+{
+    if ( origin == d_data->origin )
+        return;
+
+    d_data->origin = origin;
+    update();
+}
+
+/*!
+  \return Origin of the thermo, when OriginCustom is enabled
+  \sa setOrigin(), setOriginMode(), originMode()
+ */
+double QwtThermo::origin() const
+{
+    return d_data->origin;
+}
+
+/*!
+  \brief Change the position of the scale
+  \param scalePosition Position of the scale.
+
+  \sa ScalePosition, scalePosition()
+*/
+void QwtThermo::setScalePosition( ScalePosition scalePosition )
+{
+    if ( d_data->scalePosition == scalePosition )
+        return;
+
+    d_data->scalePosition = scalePosition;
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutThermo( true );
+}
+
+/*!
+   \return Scale position.
+   \sa setScalePosition()
+*/
+QwtThermo::ScalePosition QwtThermo::scalePosition() const
+{
+    return d_data->scalePosition;
+}
+
+//! Notify a scale change.
+void QwtThermo::scaleChange()
+{
+    layoutThermo( true );
+}
+
+/*!
+   Redraw the liquid in thermometer pipe.
+   \param painter Painter
+   \param pipeRect Bounding rectangle of the pipe without borders
+*/
+void QwtThermo::drawLiquid( 
+    QPainter *painter, const QRect &pipeRect ) const
+{
+    painter->save();
+    painter->setClipRect( pipeRect, Qt::IntersectClip );
+    painter->setPen( Qt::NoPen );
+
+    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
+
+    QRect liquidRect = fillRect( pipeRect );
+
+    if ( d_data->colorMap != NULL )
+    {
+        const QwtInterval interval = scaleDiv().interval().normalized();
+
+        // Because the positions of the ticks are rounded
+        // we calculate the colors for the rounded tick values
+
+        QVector<double> values = qwtTickList( scaleDraw()->scaleDiv() );
+
+        if ( scaleMap.isInverting() )
+            qSort( values.begin(), values.end(), qGreater<double>() );
+        else
+            qSort( values.begin(), values.end(), qLess<double>() );
+
+        int from;
+        if ( !values.isEmpty() )
+        {
+            from = qRound( scaleMap.transform( values[0] ) );
+            qwtDrawLine( painter, from,
+                d_data->colorMap->color( interval, values[0] ),
+                pipeRect, liquidRect, d_data->orientation );
+        }
+
+        for ( int i = 1; i < values.size(); i++ )
+        {
+            const int to = qRound( scaleMap.transform( values[i] ) );
+
+            for ( int pos = from + 1; pos < to; pos++ )
+            {
+                const double v = scaleMap.invTransform( pos );
+
+                qwtDrawLine( painter, pos, 
+                    d_data->colorMap->color( interval, v ),
+                    pipeRect, liquidRect, d_data->orientation );
+            }
+
+            qwtDrawLine( painter, to,
+                d_data->colorMap->color( interval, values[i] ),
+                pipeRect, liquidRect, d_data->orientation );
+
+            from = to;
+        }
+    }
+    else
+    {
+        if ( !liquidRect.isEmpty() && d_data->alarmEnabled )
+        {
+            const QRect r = alarmRect( liquidRect );
+            if ( !r.isEmpty() )
+            {
+                painter->fillRect( r, palette().brush( QPalette::Highlight ) );
+                liquidRect = QRegion( liquidRect ).subtracted( r ).boundingRect();
+            }
+        }
+
+        painter->fillRect( liquidRect, palette().brush( QPalette::ButtonText ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Change the spacing between pipe and scale
+
+  A spacing of 0 means, that the backbone of the scale is below
+  the pipe.
+
+  The default setting is 3 pixels.
+
+  \param spacing Number of pixels
+  \sa spacing();
+*/
+void QwtThermo::setSpacing( int spacing )
+{
+    if ( spacing <= 0 )
+        spacing = 0;
+
+    if ( spacing != d_data->spacing  )
+    {
+        d_data->spacing = spacing;
+        layoutThermo( true );
+    }
+}
+
+/*!
+  \return Number of pixels between pipe and scale
+  \sa setSpacing()
+*/
+int QwtThermo::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+   Set the border width of the pipe.
+   \param width Border width
+   \sa borderWidth()
+*/
+void QwtThermo::setBorderWidth( int width )
+{
+    if ( width <= 0 )
+        width = 0;
+
+    if ( width != d_data->borderWidth  )
+    {
+        d_data->borderWidth = width;
+        layoutThermo( true );
+    }
+}
+
+/*!
+   \return Border width of the thermometer pipe.
+   \sa setBorderWidth()
+*/
+int QwtThermo::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Assign a color map for the fill color
+
+  \param colorMap Color map
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setColorMap( QwtColorMap *colorMap )
+{
+    if ( colorMap != d_data->colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+}
+
+/*!
+  \return Color map for the fill color
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+QwtColorMap *QwtThermo::colorMap()
+{
+    return d_data->colorMap;
+}
+
+/*!
+  \return Color map for the fill color
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+const QwtColorMap *QwtThermo::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*!
+  \brief Change the brush of the liquid.
+ 
+  Changes the QPalette::ButtonText brush of the palette.
+
+  \param brush New brush. 
+  \sa fillBrush(), QWidget::setPalette()
+*/
+void QwtThermo::setFillBrush( const QBrush& brush )
+{
+    QPalette pal = palette();
+    pal.setBrush( QPalette::ButtonText, brush );
+    setPalette( pal );
+}
+
+/*!
+  \return Liquid ( QPalette::ButtonText ) brush. 
+  \sa setFillBrush(), QWidget::palette()
+*/
+QBrush QwtThermo::fillBrush() const
+{
+    return palette().brush( QPalette::ButtonText );
+}
+
+/*!
+  \brief Specify the liquid brush above the alarm threshold
+
+  Changes the QPalette::Highlight brush of the palette.
+
+  \param brush New brush. 
+  \sa alarmBrush(), QWidget::setPalette()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmBrush( const QBrush& brush )
+{
+    QPalette pal = palette();
+    pal.setBrush( QPalette::Highlight, brush );
+    setPalette( pal );
+}
+
+/*!
+  \return Liquid brush ( QPalette::Highlight ) above the alarm threshold.
+  \sa setAlarmBrush(), QWidget::palette()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+QBrush QwtThermo::alarmBrush() const
+{
+    return palette().brush( QPalette::Highlight );
+}
+
+/*!
+  Specify the alarm threshold.
+
+  \param level Alarm threshold
+  \sa alarmLevel()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmLevel( double level )
+{
+    d_data->alarmLevel = level;
+    d_data->alarmEnabled = 1;
+    update();
+}
+
+/*!
+  \return Alarm threshold.
+  \sa setAlarmLevel()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+double QwtThermo::alarmLevel() const
+{
+    return d_data->alarmLevel;
+}
+
+/*!
+  Change the width of the pipe.
+
+  \param width Width of the pipe
+  \sa pipeWidth()
+*/
+void QwtThermo::setPipeWidth( int width )
+{
+    if ( width > 0 )
+    {
+        d_data->pipeWidth = width;
+        layoutThermo( true );
+    }
+}
+
+/*!
+  \return Width of the pipe.
+  \sa setPipeWidth()
+*/
+int QwtThermo::pipeWidth() const
+{
+    return d_data->pipeWidth;
+}
+
+/*!
+  \brief Enable or disable the alarm threshold
+  \param on true (disabled) or false (enabled)
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmEnabled( bool on )
+{
+    d_data->alarmEnabled = on;
+    update();
+}
+
+/*! 
+  \return True, when the alarm threshold is enabled.
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+bool QwtThermo::alarmEnabled() const
+{
+    return d_data->alarmEnabled;
+}
+
+/*!
+  \return the minimum size hint
+  \sa minimumSizeHint()
+*/
+QSize QwtThermo::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+/*!
+  \return Minimum size hint
+  \warning The return value depends on the font and the scale.
+  \sa sizeHint()
+*/
+QSize QwtThermo::minimumSizeHint() const
+{
+    int w = 0, h = 0;
+
+    if ( d_data->scalePosition != NoScale )
+    {
+        const int sdExtent = qCeil( scaleDraw()->extent( font() ) );
+        const int sdLength = scaleDraw()->minLength( font() );
+
+        w = sdLength;
+        h = d_data->pipeWidth + sdExtent + d_data->spacing;
+
+    }
+    else // no scale
+    {
+        w = 200;
+        h = d_data->pipeWidth;
+    }
+
+    if ( d_data->orientation == Qt::Vertical )
+        qSwap( w, h );
+
+    w += 2 * d_data->borderWidth;
+    h += 2 * d_data->borderWidth;
+
+    // finally add the margins
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+    w += left + right;
+    h += top + bottom;
+
+    return QSize( w, h );
+}
+
+/*!
+  \brief Calculate the filled rectangle of the pipe
+
+  \param pipeRect Rectangle of the pipe
+  \return Rectangle to be filled ( fill and alarm brush )
+
+  \sa pipeRect(), alarmRect()
+ */
+QRect QwtThermo::fillRect( const QRect &pipeRect ) const
+{
+    double origin;        
+    if ( d_data->originMode == OriginMinimum )
+    {
+        origin = qMin( lowerBound(), upperBound() );
+    }
+    else if ( d_data->originMode == OriginMaximum )
+    {
+        origin = qMax( lowerBound(), upperBound() );
+    }
+    else // OriginCustom
+    {
+        origin = d_data->origin;
+    }
+
+    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
+
+    int from = qRound( scaleMap.transform( d_data->value ) );
+    int to = qRound( scaleMap.transform( origin ) );
+
+    if ( to < from )
+        qSwap( from, to );
+    
+    QRect fillRect = pipeRect;
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        fillRect.setLeft( from );
+        fillRect.setRight( to );
+    }
+    else // Qt::Vertical
+    {
+        fillRect.setTop( from );
+        fillRect.setBottom( to );
+    }
+
+    return fillRect.normalized();
+}
+
+/*!
+  \brief Calculate the alarm rectangle of the pipe
+
+  \param fillRect Filled rectangle in the pipe
+  \return Rectangle to be filled with the alarm brush
+
+  \sa pipeRect(), fillRect(), alarmLevel(), alarmBrush()
+ */
+QRect QwtThermo::alarmRect( const QRect &fillRect ) const
+{
+    QRect alarmRect( 0, 0, -1, -1); // something invalid
+
+    if ( !d_data->alarmEnabled )
+        return alarmRect;
+
+    const bool inverted = ( upperBound() < lowerBound() );
+    
+    bool increasing;
+    if ( d_data->originMode == OriginCustom )
+    {
+        increasing = d_data->value > d_data->origin;
+    }
+    else
+    {
+        increasing = d_data->originMode == OriginMinimum;
+    }
+
+    const QwtScaleMap map = scaleDraw()->scaleMap();
+    const int alarmPos = qRound( map.transform( d_data->alarmLevel ) );
+    const int valuePos = qRound( map.transform( d_data->value ) );
+    
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        int v1, v2;
+        if ( inverted )
+        {
+            v1 = fillRect.left();
+
+            v2 = alarmPos - 1;
+            v2 = qMin( v2, increasing ? fillRect.right() : valuePos );
+        }
+        else
+        {
+            v1 = alarmPos + 1;
+            v1 = qMax( v1, increasing ? fillRect.left() : valuePos );
+
+            v2 = fillRect.right();
+
+        }
+        alarmRect.setRect( v1, fillRect.top(), v2 - v1 + 1, fillRect.height() );
+    }
+    else
+    {
+        int v1, v2;
+        if ( inverted )
+        {
+            v1 = alarmPos + 1;
+            v1 = qMax( v1, increasing ? fillRect.top() : valuePos );
+
+            v2 = fillRect.bottom();
+        }
+        else
+        {
+            v1 = fillRect.top();
+
+            v2 = alarmPos - 1;
+            v2 = qMin( v2, increasing ? fillRect.bottom() : valuePos );
+        }
+        alarmRect.setRect( fillRect.left(), v1, fillRect.width(), v2 - v1 + 1 );
+    }
+
+    return alarmRect;
+} 
diff --git a/qwt/qwt_thermo.h b/qwt/qwt_thermo.h
new file mode 100644
index 0000000..b9aa67d
--- /dev/null
+++ b/qwt/qwt_thermo.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_THERMO_H
+#define QWT_THERMO_H
+
+#include "qwt_global.h"
+#include "qwt_abstract_scale.h"
+#include "qwt_interval.h"
+
+class QwtScaleDraw;
+class QwtColorMap;
+
+/*!
+  \brief The Thermometer Widget
+
+  QwtThermo is a widget which displays a value in an interval. It supports:
+  - a horizontal or vertical layout;
+  - a range;
+  - a scale;
+  - an alarm level.
+
+  \image html sysinfo.png
+
+  The fill colors might be calculated from an optional color map
+  If no color map has been assigned QwtThermo uses the 
+  following colors/brushes from the widget palette:
+
+  - QPalette::Base
+    Background of the pipe
+  - QPalette::ButtonText
+    Fill brush below the alarm level
+  - QPalette::Highlight
+    Fill brush for the values above the alarm level
+  - QPalette::WindowText
+    For the axis of the scale
+  - QPalette::Text
+    For the labels of the scale
+*/
+class QWT_EXPORT QwtThermo: public QwtAbstractScale
+{
+    Q_OBJECT
+
+    Q_ENUMS( ScalePosition )
+    Q_ENUMS( OriginMode )
+
+    Q_PROPERTY( Qt::Orientation orientation
+        READ orientation WRITE setOrientation )
+    Q_PROPERTY( ScalePosition scalePosition 
+        READ scalePosition WRITE setScalePosition )
+    Q_PROPERTY( OriginMode originMode READ originMode WRITE setOriginMode )
+
+    Q_PROPERTY( bool alarmEnabled READ alarmEnabled WRITE setAlarmEnabled )
+    Q_PROPERTY( double alarmLevel READ alarmLevel WRITE setAlarmLevel )
+    Q_PROPERTY( double origin READ origin WRITE setOrigin )
+    Q_PROPERTY( int spacing READ spacing WRITE setSpacing )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int pipeWidth READ pipeWidth WRITE setPipeWidth )
+    Q_PROPERTY( double value READ value WRITE setValue )
+
+public:
+
+    /*!
+      Position of the scale
+      \sa setScalePosition(), setOrientation()
+     */
+    enum ScalePosition
+    {
+        //! The slider has no scale
+        NoScale,
+
+        //! The scale is right of a vertical or below of a horizontal slider
+        LeadingScale,
+
+        //! The scale is left of a vertical or above of a horizontal slider
+        TrailingScale
+    };
+
+    /*!
+      Origin mode. This property specifies where the beginning of the liquid
+      is placed.
+
+      \sa setOriginMode(), setOrigin()
+    */
+    enum OriginMode
+    {
+        //! The origin is the minimum of the scale
+        OriginMinimum,
+
+        //! The origin is the maximum of the scale
+        OriginMaximum,
+
+        //! The origin is specified using the origin() property
+        OriginCustom
+    };
+
+    explicit QwtThermo( QWidget *parent = NULL );
+    virtual ~QwtThermo();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    void setScalePosition( ScalePosition );
+    ScalePosition scalePosition() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setBorderWidth( int w );
+    int borderWidth() const;
+
+    void setOriginMode( OriginMode );
+    OriginMode originMode() const;
+
+    void setOrigin( double );
+    double origin() const;
+
+    void setFillBrush( const QBrush &b );
+    QBrush fillBrush() const;
+
+    void setAlarmBrush( const QBrush &b );
+    QBrush alarmBrush() const;
+
+    void setAlarmLevel( double v );
+    double alarmLevel() const;
+
+    void setAlarmEnabled( bool tf );
+    bool alarmEnabled() const;
+
+    void setColorMap( QwtColorMap * );
+    QwtColorMap *colorMap();
+    const QwtColorMap *colorMap() const;
+
+    void setPipeWidth( int w );
+    int pipeWidth() const;
+
+    void setRangeFlags( QwtInterval::BorderFlags );
+    QwtInterval::BorderFlags rangeFlags() const;
+
+    double value() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+
+public Q_SLOTS:
+    virtual void setValue( double val );
+
+protected:
+    virtual void drawLiquid( QPainter *, const QRect & ) const;
+    virtual void scaleChange();
+
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+    virtual void changeEvent( QEvent * );
+
+    QwtScaleDraw *scaleDraw();
+
+    QRect pipeRect() const;
+    QRect fillRect( const QRect & ) const;
+    QRect alarmRect( const QRect & ) const;
+
+private:
+    void layoutThermo( bool );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_transform.cpp b/qwt/qwt_transform.cpp
new file mode 100644
index 0000000..359fc9f
--- /dev/null
+++ b/qwt/qwt_transform.cpp
@@ -0,0 +1,165 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_transform.h"
+#include "qwt_math.h"
+
+#if QT_VERSION < 0x040601
+#define qExp(x) ::exp(x)
+#endif
+
+//! Smallest allowed value for logarithmic scales: 1.0e-150
+QT_STATIC_CONST_IMPL double QwtLogTransform::LogMin = 1.0e-150;
+
+//! Largest allowed value for logarithmic scales: 1.0e150
+QT_STATIC_CONST_IMPL double QwtLogTransform::LogMax = 1.0e150;
+
+//! Constructor
+QwtTransform::QwtTransform()
+{
+}
+
+//! Destructor
+QwtTransform::~QwtTransform()
+{
+}
+
+/*! 
+  \param value Value to be bounded
+  \return value unmodified
+ */
+double QwtTransform::bounded( double value ) const
+{
+    return value;
+}
+
+//! Constructor
+QwtNullTransform::QwtNullTransform():
+    QwtTransform()
+{
+}
+
+//! Destructor
+QwtNullTransform::~QwtNullTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return value unmodified
+ */
+double QwtNullTransform::transform( double value ) const
+{
+    return value;
+}
+
+/*! 
+  \param value Value to be transformed
+  \return value unmodified
+ */
+double QwtNullTransform::invTransform( double value ) const
+{
+    return value;
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtNullTransform::copy() const
+{
+    return new QwtNullTransform();
+}
+
+//! Constructor
+QwtLogTransform::QwtLogTransform():
+    QwtTransform()
+{
+}
+
+//! Destructor
+QwtLogTransform::~QwtLogTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return log( value )
+ */
+double QwtLogTransform::transform( double value ) const
+{
+    return ::log( value );
+}
+
+/*! 
+  \param value Value to be transformed
+  \return exp( value )
+ */
+double QwtLogTransform::invTransform( double value ) const
+{
+    return qExp( value );
+}
+
+/*! 
+  \param value Value to be bounded
+  \return qBound( LogMin, value, LogMax )
+ */
+double QwtLogTransform::bounded( double value ) const
+{
+    return qBound( LogMin, value, LogMax );
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtLogTransform::copy() const
+{
+    return new QwtLogTransform();
+}
+
+/*!
+  Constructor
+  \param exponent Exponent
+*/
+QwtPowerTransform::QwtPowerTransform( double exponent ):
+    QwtTransform(),
+    d_exponent( exponent )
+{
+}
+
+//! Destructor
+QwtPowerTransform::~QwtPowerTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return Exponentiation preserving the sign
+ */
+double QwtPowerTransform::transform( double value ) const
+{
+    if ( value < 0.0 )
+        return -qPow( -value, 1.0 / d_exponent );
+    else
+        return qPow( value, 1.0 / d_exponent );
+    
+}
+
+/*! 
+  \param value Value to be transformed
+  \return Inverse exponentiation preserving the sign
+ */
+double QwtPowerTransform::invTransform( double value ) const
+{
+    if ( value < 0.0 )
+        return -qPow( -value, d_exponent );
+    else
+        return qPow( value, d_exponent );
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtPowerTransform::copy() const
+{
+    return new QwtPowerTransform( d_exponent );
+}
diff --git a/qwt/qwt_transform.h b/qwt/qwt_transform.h
new file mode 100644
index 0000000..ce13fa6
--- /dev/null
+++ b/qwt/qwt_transform.h
@@ -0,0 +1,137 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TRANSFORM_H
+#define QWT_TRANSFORM_H
+
+#include "qwt_global.h"
+
+/*!
+   \brief A transformation between coordinate systems
+
+   QwtTransform manipulates values, when being mapped between
+   the scale and the paint device coordinate system.
+
+   A transformation consists of 2 methods:
+
+   - transform
+   - invTransform
+
+   where one is is the inverse function of the other.
+
+   When p1, p2 are the boundaries of the paint device coordinates
+   and s1, s2 the boundaries of the scale, QwtScaleMap uses the
+   following calculations:
+
+   - p = p1 + ( p2 - p1 ) * ( T( s ) - T( s1 ) / ( T( s2 ) - T( s1 ) );
+   - s = invT ( T( s1 ) + ( T( s2 ) - T( s1 ) ) * ( p - p1 ) / ( p2 - p1 ) );
+*/
+class QWT_EXPORT QwtTransform
+{
+public:
+    QwtTransform();
+    virtual ~QwtTransform();
+
+    /*!
+       Modify value to be a valid value for the transformation.
+       The default implementation does nothing.
+     */
+    virtual double bounded( double value ) const;
+
+    /*!
+        Transformation function
+
+        \param value Value
+        \return Modified value
+
+        \sa invTransform()
+     */
+    virtual double transform( double value ) const = 0;
+
+    /*!
+        Inverse transformation function
+
+        \param value Value
+        \return Modified value
+
+        \sa transform()
+     */
+    virtual double invTransform( double value ) const = 0;
+
+    //! Virtualized copy operation
+    virtual QwtTransform *copy() const = 0;
+};
+
+/*!
+   \brief Null transformation
+
+   QwtNullTransform returns the values unmodified.
+   
+ */
+class QWT_EXPORT QwtNullTransform: public QwtTransform
+{
+public:
+    QwtNullTransform();
+    virtual ~QwtNullTransform();
+
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual QwtTransform *copy() const;
+};
+/*!
+   \brief Logarithmic transformation
+
+   QwtLogTransform modifies the values using log() and exp().
+
+   \note In the calculations of QwtScaleMap the base of the log function
+         has no effect on the mapping. So QwtLogTransform can be used 
+         for log2(), log10() or any other logarithmic scale.
+ */
+class QWT_EXPORT QwtLogTransform: public QwtTransform
+{   
+public:
+    QwtLogTransform();
+    virtual ~QwtLogTransform();
+    
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual double bounded( double value ) const;
+
+    virtual QwtTransform *copy() const;
+
+    QT_STATIC_CONST double LogMin;
+    QT_STATIC_CONST double LogMax;
+};
+
+/*!
+   \brief A transformation using pow()
+
+   QwtPowerTransform preserves the sign of a value. 
+   F.e. a transformation with a factor of 2
+   transforms a value of -3 to -9 and v.v. Thus QwtPowerTransform
+   can be used for scales including negative values.
+ */
+class QWT_EXPORT QwtPowerTransform: public QwtTransform
+{
+public:
+    QwtPowerTransform( double exponent );
+    virtual ~QwtPowerTransform();
+
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual QwtTransform *copy() const;
+
+private:
+    const double d_exponent;
+};
+
+#endif
diff --git a/qwt/qwt_wheel.cpp b/qwt/qwt_wheel.cpp
new file mode 100644
index 0000000..72276f0
--- /dev/null
+++ b/qwt/qwt_wheel.cpp
@@ -0,0 +1,1293 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_wheel.h"
+#include "qwt_math.h"
+#include "qwt_painter.h"
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+#include <qdatetime.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#define qFastSin(x) ::sin(x)
+#define qExp(x) ::exp(x)
+#endif
+
+class QwtWheel::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Horizontal ),
+        viewAngle( 175.0 ),
+        totalAngle( 360.0 ),
+        tickCount( 10 ),
+        wheelBorderWidth( 2 ),
+        borderWidth( 2 ),
+        wheelWidth( 20 ),
+        isScrolling( false ),
+        mouseOffset( 0.0 ),
+        tracking( true ),
+        pendingValueChanged( false ),
+        updateInterval( 50 ),
+        mass( 0.0 ),
+        timerId( 0 ),
+        speed( 0.0 ),
+        mouseValue( 0.0 ),
+        flyingValue( 0.0 ),
+        minimum( 0.0 ),
+        maximum( 100.0 ),
+        singleStep( 1.0 ),
+        pageStepCount( 1 ),
+        stepAlignment( true ),
+        value( 0.0 ),
+        inverted( false ),
+        wrapping( false )
+    {
+    };
+
+    Qt::Orientation orientation;
+    double viewAngle;
+    double totalAngle;
+    int tickCount;
+    int wheelBorderWidth;
+    int borderWidth;
+    int wheelWidth;
+
+    bool isScrolling;
+    double mouseOffset;
+
+    bool tracking;
+    bool pendingValueChanged; // when not tracking
+
+    int updateInterval;
+    double mass;
+
+    // for the flying wheel effect
+    int timerId;
+    QTime time;
+    double speed;
+    double mouseValue;
+    double flyingValue;
+
+    double minimum;
+    double maximum;
+
+    double singleStep;
+    int pageStepCount;
+    bool stepAlignment;
+
+    double value;
+
+    bool inverted;
+    bool wrapping;
+};
+
+//! Constructor
+QwtWheel::QwtWheel( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData;
+
+    setFocusPolicy( Qt::StrongFocus );
+    setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+}
+
+//! Destructor
+QwtWheel::~QwtWheel()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/Disable tracking
+
+  If tracking is enabled (the default), the wheel emits the valueChanged() 
+  signal while the wheel is moving. If tracking is disabled, the wheel 
+  emits the valueChanged() signal only when the wheel movement is terminated.
+
+  The wheelMoved() signal is emitted regardless id tracking is enabled or not.
+
+  \param enable On/Off
+  \sa isTracking()
+ */
+void QwtWheel::setTracking( bool enable )
+{
+    d_data->tracking = enable;
+}
+
+/*!
+  \return True, when tracking is enabled
+  \sa setTracking(), valueChanged(), wheelMoved()
+*/
+bool QwtWheel::isTracking() const
+{
+    return d_data->tracking;
+}
+
+/*!
+  \brief Specify the update interval when the wheel is flying
+
+  Default and minimum value is 50 ms.
+
+  \param interval Interval in milliseconds
+  \sa updateInterval(), setMass(), setTracking()
+*/
+void QwtWheel::setUpdateInterval( int interval )
+{
+    d_data->updateInterval = qMax( interval, 50 );
+}
+
+/*!
+  \return Update interval when the wheel is flying
+  \sa setUpdateInterval(), mass(), isTracking()
+ */
+int QwtWheel::updateInterval() const
+{
+    return d_data->updateInterval;
+}
+
+/*!
+   \brief Mouse press event handler
+
+   Start movement of the wheel. 
+
+   \param event Mouse event
+*/
+void QwtWheel::mousePressEvent( QMouseEvent *event )
+{
+    stopFlying();
+
+    d_data->isScrolling = wheelRect().contains( event->pos() );
+
+    if ( d_data->isScrolling )
+    {
+        d_data->time.start();
+        d_data->speed = 0.0;
+        d_data->mouseValue = valueAt( event->pos() );
+        d_data->mouseOffset = d_data->mouseValue - d_data->value;
+        d_data->pendingValueChanged = false;
+
+        Q_EMIT wheelPressed();
+    }
+}
+
+/*!
+   \brief Mouse Move Event handler
+
+   Turn the wheel according to the mouse position
+
+   \param event Mouse event
+*/
+void QwtWheel::mouseMoveEvent( QMouseEvent *event )
+{
+    if ( !d_data->isScrolling )
+        return;
+
+    double mouseValue = valueAt( event->pos() );
+
+    if ( d_data->mass > 0.0 )
+    {
+        double ms = d_data->time.restart();
+
+        // the interval when mouse move events are posted are somehow
+        // random. To avoid unrealistic speed values we limit ms
+
+        ms = qMax( ms, 5.0 );
+
+        d_data->speed = ( mouseValue - d_data->mouseValue ) / ms;
+    }
+    
+    d_data->mouseValue = mouseValue; 
+
+    double value = boundedValue( mouseValue - d_data->mouseOffset );
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+        
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+
+        update();
+
+        Q_EMIT wheelMoved( d_data->value );
+
+        if ( d_data->tracking )
+            Q_EMIT valueChanged( d_data->value );
+        else
+            d_data->pendingValueChanged = true;
+    }
+}
+
+/*!
+   \brief Mouse Release Event handler
+
+   When the wheel has no mass the movement of the wheel stops, otherwise
+   it starts flying.
+
+   \param event Mouse event
+*/  
+
+void QwtWheel::mouseReleaseEvent( QMouseEvent *event )
+{
+    Q_UNUSED( event );
+
+    if ( !d_data->isScrolling )
+        return;
+
+    d_data->isScrolling = false;
+
+    bool startFlying = false;
+
+    if ( d_data->mass > 0.0 )
+    {
+        const int ms = d_data->time.elapsed();
+        if ( ( qFabs( d_data->speed ) > 0.0 ) && ( ms < 50 ) )
+            startFlying = true;
+    }
+
+    if ( startFlying )
+    {
+        d_data->flyingValue = 
+            boundedValue( d_data->mouseValue - d_data->mouseOffset );
+
+        d_data->timerId = startTimer( d_data->updateInterval );
+    }
+    else
+    {
+        if ( d_data->pendingValueChanged )
+            Q_EMIT valueChanged( d_data->value );
+    }
+
+    d_data->pendingValueChanged = false;
+    d_data->mouseOffset = 0.0;
+
+    Q_EMIT wheelReleased();
+}
+
+/*!
+  \brief Qt timer event
+
+  The flying wheel effect is implemented using a timer
+   
+  \param event Timer event
+
+  \sa updateInterval()
+ */
+void QwtWheel::timerEvent( QTimerEvent *event )
+{
+    if ( event->timerId() != d_data->timerId )
+    {
+        QWidget::timerEvent( event );
+        return;
+    }
+
+    d_data->speed *= qExp( -d_data->updateInterval * 0.001 / d_data->mass );
+
+    d_data->flyingValue += d_data->speed * d_data->updateInterval;
+    d_data->flyingValue = boundedValue( d_data->flyingValue );
+
+    double value = d_data->flyingValue;
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    if ( qFabs( d_data->speed ) < 0.001 * d_data->singleStep )
+    {
+        // stop if d_data->speed < one step per second
+        stopFlying();
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        if ( d_data->tracking || d_data->timerId == 0 )
+            Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+
+/*!
+  \brief Handle wheel events
+
+  In/Decrement the value 
+
+  \param event Wheel event
+*/
+void QwtWheel::wheelEvent( QWheelEvent *event )
+{
+    if ( !wheelRect().contains( event->pos() ) )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isScrolling )
+        return;
+
+    stopFlying();
+
+    double increment = 0.0;
+
+    if ( ( event->modifiers() & Qt::ControlModifier) || 
+        ( event->modifiers() & Qt::ShiftModifier ) )
+    {
+        // one page regardless of delta
+        increment = d_data->singleStep * d_data->pageStepCount;
+        if ( event->delta() < 0 )
+            increment = -increment;
+    }
+    else
+    {
+        const int numSteps = event->delta() / 120;
+        increment = d_data->singleStep * numSteps;
+    }
+
+    if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+        increment = -increment;
+
+    double value = boundedValue( d_data->value + increment );
+
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        Q_EMIT valueChanged( d_data->value );
+        Q_EMIT wheelMoved( d_data->value );
+    }
+}
+
+/*!
+  Handle key events
+
+  - Qt::Key_Home\n
+    Step to minimum()
+
+  - Qt::Key_End\n
+    Step to maximum()
+
+  - Qt::Key_Up\n
+    In case of a horizontal or not inverted vertical wheel the value 
+    will be incremented by the step size. For an inverted vertical wheel
+    the value will be decremented by the step size.
+
+  - Qt::Key_Down\n
+    In case of a horizontal or not inverted vertical wheel the value 
+    will be decremented by the step size. For an inverted vertical wheel
+    the value will be incremented by the step size.
+
+  - Qt::Key_PageUp\n
+    The value will be incremented by pageStepSize() * singleStepSize().
+
+  - Qt::Key_PageDown\n
+    The value will be decremented by pageStepSize() * singleStepSize().
+
+  \param event Key event
+*/
+void QwtWheel::keyPressEvent( QKeyEvent *event )
+{
+    if ( d_data->isScrolling )
+    {
+        // don't interfere mouse scrolling
+        return;
+    }
+
+    double value = d_data->value;
+    double increment = 0.0;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Down:
+        {
+            if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+                increment = d_data->singleStep;
+            else
+                increment = -d_data->singleStep;
+
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+                increment = -d_data->singleStep;
+            else
+                increment = d_data->singleStep;
+
+            break;
+        }
+        case Qt::Key_Left:
+        {
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( d_data->inverted )
+                    increment = d_data->singleStep;
+                else
+                    increment = -d_data->singleStep;
+            }
+            break;
+        }
+        case Qt::Key_Right:
+        {
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( d_data->inverted )
+                    increment = -d_data->singleStep;
+                else
+                    increment = d_data->singleStep;
+            }
+            break;
+        }
+        case Qt::Key_PageUp:
+        {
+            increment = d_data->pageStepCount * d_data->singleStep;
+            break;
+        }
+        case Qt::Key_PageDown:
+        {
+            increment = -d_data->pageStepCount * d_data->singleStep;
+            break;
+        }
+        case Qt::Key_Home:
+        {
+            value = d_data->minimum;
+            break;
+        }
+        case Qt::Key_End:
+        {
+            value = d_data->maximum;
+            break;
+        }
+        default:;
+        {
+            event->ignore();
+        }
+    }
+
+    if ( event->isAccepted() )
+        stopFlying();
+    
+    if ( increment != 0.0 )
+    {
+        value = boundedValue( d_data->value + increment );
+
+        if ( d_data->stepAlignment )
+            value = alignedValue( value );
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        Q_EMIT valueChanged( d_data->value );
+        Q_EMIT wheelMoved( d_data->value );
+    }
+}
+
+/*!
+  \brief Adjust the number of grooves in the wheel's surface.
+
+  The number of grooves is limited to 6 <= count <= 50.
+  Values outside this range will be clipped.
+  The default value is 10.
+
+  \param count Number of grooves per 360 degrees
+  \sa tickCount()
+*/
+void QwtWheel::setTickCount( int count )
+{
+    count = qBound( 6, count, 50 );
+
+    if ( count != d_data->tickCount )
+    {
+        d_data->tickCount = qBound( 6, count, 50 );
+        update();
+    }
+}
+
+/*!
+  \return Number of grooves in the wheel's surface.
+  \sa setTickCnt()
+*/
+int QwtWheel::tickCount() const
+{
+    return d_data->tickCount;
+}
+
+/*!
+  \brief Set the wheel border width of the wheel.
+
+  The wheel border must not be smaller than 1
+  and is limited in dependence on the wheel's size.
+  Values outside the allowed range will be clipped.
+
+  The wheel border defaults to 2.
+
+  \param borderWidth Border width
+  \sa internalBorder()
+*/
+void QwtWheel::setWheelBorderWidth( int borderWidth )
+{
+    const int d = qMin( width(), height() ) / 3;
+    borderWidth = qMin( borderWidth, d );
+    d_data->wheelBorderWidth = qMax( borderWidth, 1 );
+    update();
+}
+
+/*!
+   \return Wheel border width 
+   \sa setWheelBorderWidth()
+*/
+int QwtWheel::wheelBorderWidth() const
+{
+    return d_data->wheelBorderWidth;
+}
+
+/*!
+  \brief Set the border width 
+
+  The border defaults to 2.
+
+  \param width Border width
+  \sa borderWidth()
+*/
+void QwtWheel::setBorderWidth( int width )
+{
+    d_data->borderWidth = qMax( width, 0 );
+    update();
+}
+
+/*!
+   \return Border width 
+   \sa setBorderWidth()
+*/
+int QwtWheel::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+   \return Rectangle of the wheel without the outer border
+*/
+QRect QwtWheel::wheelRect() const
+{
+    const int bw = d_data->borderWidth;
+    return contentsRect().adjusted( bw, bw, -bw, -bw );
+}
+
+/*!
+  \brief Set the total angle which the wheel can be turned.
+
+  One full turn of the wheel corresponds to an angle of
+  360 degrees. A total angle of n*360 degrees means
+  that the wheel has to be turned n times around its axis
+  to get from the minimum value to the maximum value.
+
+  The default setting of the total angle is 360 degrees.
+
+  \param angle total angle in degrees
+  \sa totalAngle()
+*/
+void QwtWheel::setTotalAngle( double angle )
+{
+    if ( angle < 0.0 )
+        angle = 0.0;
+
+    d_data->totalAngle = angle;
+    update();
+}
+
+/*!
+  \return Total angle which the wheel can be turned.
+  \sa setTotalAngle()
+*/
+double QwtWheel::totalAngle() const
+{
+    return d_data->totalAngle;
+}
+
+/*!
+  \brief Set the wheel's orientation.
+
+  The default orientation is Qt::Horizontal.
+
+  \param orientation Qt::Horizontal or Qt::Vertical.
+  \sa orientation()
+*/
+void QwtWheel::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation == orientation )
+        return;
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    d_data->orientation = orientation;
+    update();
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtWheel::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Specify the visible portion of the wheel.
+
+  You may use this function for fine-tuning the appearance of
+  the wheel. The default value is 175 degrees. The value is
+  limited from 10 to 175 degrees.
+
+  \param angle Visible angle in degrees
+  \sa viewAngle(), setTotalAngle()
+*/
+void QwtWheel::setViewAngle( double angle )
+{
+    d_data->viewAngle = qBound( 10.0, angle, 175.0 );
+    update();
+}
+
+/*!
+  \return Visible portion of the wheel
+  \sa setViewAngle(), totalAngle()
+*/
+double QwtWheel::viewAngle() const
+{
+    return d_data->viewAngle;
+}
+
+/*! 
+  Determine the value corresponding to a specified point
+
+  \param pos Position
+  \return Value corresponding to pos
+*/
+double QwtWheel::valueAt( const QPoint &pos ) const
+{
+    const QRectF rect = wheelRect();
+
+    double w, dx;
+    if ( d_data->orientation == Qt::Vertical )
+    {
+        w = rect.height();
+        dx = rect.top() - pos.y();
+    }
+    else
+    {
+        w = rect.width();
+        dx = pos.x() - rect.left();
+    }
+
+    if ( w == 0.0 )
+        return 0.0;
+
+    if ( d_data->inverted )
+    {
+        dx = w - dx;
+    }
+
+    // w pixels is an arc of viewAngle degrees,
+    // so we convert change in pixels to change in angle
+    const double ang = dx * d_data->viewAngle / w;
+
+    // value range maps to totalAngle degrees,
+    // so convert the change in angle to a change in value
+    const double val = ang * ( maximum() - minimum() ) / d_data->totalAngle;
+
+    return val;
+}
+
+/*! 
+   \brief Qt Paint Event
+   \param event Paint event
+*/
+void QwtWheel::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    qDrawShadePanel( &painter, 
+        contentsRect(), palette(), true, d_data->borderWidth );
+
+    drawWheelBackground( &painter, wheelRect() );
+    drawTicks( &painter, wheelRect() );
+
+    if ( hasFocus() )
+        QwtPainter::drawFocusRect( &painter, this );
+}
+
+/*!
+   Draw the Wheel's background gradient
+
+   \param painter Painter
+   \param rect Geometry for the wheel
+*/
+void QwtWheel::drawWheelBackground( 
+    QPainter *painter, const QRectF &rect )
+{
+    painter->save();
+
+    QPalette pal = palette();
+
+    //  draw shaded background
+    QLinearGradient gradient( rect.topLeft(), 
+        ( d_data->orientation == Qt::Horizontal ) ? rect.topRight() : rect.bottomLeft() );
+    gradient.setColorAt( 0.0, pal.color( QPalette::Button ) );
+    gradient.setColorAt( 0.2, pal.color( QPalette::Midlight ) );
+    gradient.setColorAt( 0.7, pal.color( QPalette::Mid ) );
+    gradient.setColorAt( 1.0, pal.color( QPalette::Dark ) );
+
+    painter->fillRect( rect, gradient );
+
+    // draw internal border
+
+    const QPen lightPen( palette().color( QPalette::Light ), 
+        d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap );
+    const QPen darkPen( pal.color( QPalette::Dark ), 
+        d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap );
+
+    const double bw2 = 0.5 * d_data->wheelBorderWidth;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        painter->setPen( lightPen );
+        painter->drawLine( QPointF( rect.left(), rect.top() + bw2 ), 
+            QPointF( rect.right(), rect.top() + bw2 ) );
+
+        painter->setPen( darkPen );
+        painter->drawLine( QPointF( rect.left(), rect.bottom() - bw2 ), 
+            QPointF( rect.right(), rect.bottom() - bw2 ) );
+    }
+    else // Qt::Vertical
+    {
+        painter->setPen( lightPen );
+        painter->drawLine( QPointF( rect.left() + bw2, rect.top() ), 
+            QPointF( rect.left() + bw2, rect.bottom() ) );
+
+        painter->setPen( darkPen );
+        painter->drawLine( QPointF( rect.right() - bw2, rect.top() ), 
+            QPointF( rect.right() - bw2, rect.bottom() ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+   Draw the Wheel's ticks
+
+   \param painter Painter
+   \param rect Geometry for the wheel
+*/
+void QwtWheel::drawTicks( QPainter *painter, const QRectF &rect )
+{
+    const double range = d_data->maximum - d_data->minimum;
+
+    if ( range == 0.0 || d_data->totalAngle == 0.0 )
+    {
+        return;
+    }
+
+    const QPen lightPen( palette().color( QPalette::Light ), 
+        0, Qt::SolidLine, Qt::FlatCap );
+    const QPen darkPen( palette().color( QPalette::Dark ), 
+        0, Qt::SolidLine, Qt::FlatCap );
+
+    const double cnvFactor = qAbs( d_data->totalAngle / range );
+    const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor;
+    const double loValue = value() - halfIntv;
+    const double hiValue = value() + halfIntv;
+    const double tickWidth = 360.0 / double( d_data->tickCount ) / cnvFactor;
+    const double sinArc = qFastSin( d_data->viewAngle * M_PI / 360.0 );
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const double radius = rect.width() * 0.5;
+
+        double l1 = rect.top() + d_data->wheelBorderWidth;
+        double l2 = rect.bottom() - d_data->wheelBorderWidth - 1;
+
+        // draw one point over the border if border > 1
+        if ( d_data->wheelBorderWidth > 1 )
+        {
+            l1--;
+            l2++;
+        }
+
+        const double maxpos = rect.right() - 2;
+        const double minpos = rect.left() + 2;
+
+        // draw tick marks
+        for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth;
+            tickValue < hiValue; tickValue += tickWidth )
+        {
+            const double angle = qwtRadians( tickValue - value() );
+            const double s = qFastSin( angle * cnvFactor );
+
+            const double off = radius * ( sinArc + s ) / sinArc;
+
+            double tickPos;
+            if ( d_data->inverted ) 
+                tickPos = rect.left() + off;
+            else
+                tickPos = rect.right() - off;
+
+            if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
+            {
+                painter->setPen( darkPen );
+                painter->drawLine( QPointF( tickPos - 1 , l1 ), 
+                    QPointF( tickPos - 1,  l2 ) );
+                painter->setPen( lightPen );
+                painter->drawLine( QPointF( tickPos, l1 ), 
+                    QPointF( tickPos, l2 ) );
+            }
+        }
+    }
+    else // Qt::Vertical
+    {
+        const double radius = rect.height() * 0.5;
+
+        double l1 = rect.left() + d_data->wheelBorderWidth;
+        double l2 = rect.right() - d_data->wheelBorderWidth - 1;
+
+        if ( d_data->wheelBorderWidth > 1 )
+        {
+            l1--;
+            l2++;
+        }
+
+        const double maxpos = rect.bottom() - 2;
+        const double minpos = rect.top() + 2;
+
+        for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth;
+            tickValue < hiValue; tickValue += tickWidth )
+        {
+            const double angle = qwtRadians( tickValue - value() );
+            const double s = qFastSin( angle * cnvFactor );
+
+            const double off = radius * ( sinArc + s ) / sinArc;
+
+            double tickPos;
+
+            if ( d_data->inverted )
+                tickPos = rect.bottom() - off;
+            else
+                tickPos = rect.top() + off;
+
+            if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
+            {
+                painter->setPen( darkPen );
+                painter->drawLine( QPointF( l1, tickPos - 1 ), 
+                    QPointF( l2, tickPos - 1 ) );
+                painter->setPen( lightPen );
+                painter->drawLine( QPointF( l1, tickPos ), 
+                    QPointF( l2, tickPos ) );
+            }
+        }
+    }
+}
+
+/*!
+  \brief Set the width of the wheel
+
+  Corresponds to the wheel height for horizontal orientation,
+  and the wheel width for vertical orientation.
+
+  \param width the wheel's width
+  \sa wheelWidth()
+*/
+void QwtWheel::setWheelWidth( int width )
+{
+    d_data->wheelWidth = width;
+    update();
+}
+
+/*!
+  \return Width of the wheel
+  \sa setWheelWidth()
+*/
+int QwtWheel::wheelWidth() const
+{
+    return d_data->wheelWidth;
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtWheel::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \warning The return value is based on the wheel width.
+*/
+QSize QwtWheel::minimumSizeHint() const
+{
+    QSize sz( 3 * d_data->wheelWidth + 2 * d_data->borderWidth,
+        d_data->wheelWidth + 2 * d_data->borderWidth );
+    if ( d_data->orientation != Qt::Horizontal )
+        sz.transpose();
+
+    return sz;
+}
+
+/*!
+  \brief Set the step size of the counter
+
+  A value <= 0.0 disables stepping
+
+  \param stepSize Single step size
+  \sa singleStep(), setPageStepCount()
+*/
+void QwtWheel::setSingleStep( double stepSize )
+{
+    d_data->singleStep = qMax( stepSize, 0.0 );
+}
+
+/*!
+  \return Single step size
+  \sa setSingleStep()
+ */
+double QwtWheel::singleStep() const
+{
+    return d_data->singleStep;
+}
+
+/*!
+  \brief En/Disable step alignment
+
+  When step alignment is enabled value changes initiated by
+  user input ( mouse, keyboard, wheel ) are aligned to
+  the multiples of the single step.
+
+  \param on On/Off
+  \sa stepAlignment(), setSingleStep()
+ */
+void QwtWheel::setStepAlignment( bool on )
+{
+    if ( on != d_data->stepAlignment )
+    {
+        d_data->stepAlignment = on;
+    }
+}
+
+/*!
+  \return True, when the step alignment is enabled
+  \sa setStepAlignment(), singleStep()
+ */
+bool QwtWheel::stepAlignment() const
+{
+    return d_data->stepAlignment;
+}
+
+/*!
+  \brief Set the page step count  
+    
+  pageStepCount is a multiplicator for the single step size
+  that typically corresponds to the user pressing PageUp or PageDown.
+    
+  A value of 0 disables page stepping. 
+
+  The default value is 1.
+
+  \param count Multiplicator for the single step size
+  \sa pageStepCount(), setSingleStep()
+ */
+void QwtWheel::setPageStepCount( int count )
+{
+    d_data->pageStepCount = qMax( 0, count );
+}
+
+/*! 
+  \return Page step count
+  \sa setPageStepCount(), singleStep()
+ */
+int QwtWheel::pageStepCount() const
+{
+    return d_data->pageStepCount;
+}
+
+/*!
+  \brief Set the minimum and maximum values
+
+  The maximum is adjusted if necessary to ensure that the range remains valid.
+  The value might be modified to be inside of the range.
+
+  \param min Minimum value
+  \param max Maximum value
+
+  \sa minimum(), maximum()
+ */
+void QwtWheel::setRange( double min, double max )
+{
+    max = qMax( min, max );
+
+    if ( d_data->minimum == min && d_data->maximum == max )
+        return;
+
+    d_data->minimum = min;
+    d_data->maximum = max;
+
+    if ( d_data->value < min || d_data->value > max )
+    {
+        d_data->value = qBound( min, d_data->value, max );
+
+        update();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+/*!
+  Set the minimum value of the range
+
+  \param value Minimum value
+  \sa setRange(), setMaximum(), minimum()
+
+  \note The maximum is adjusted if necessary to ensure that the range remains valid.
+*/
+void QwtWheel::setMinimum( double value )
+{
+    setRange( value, maximum() );
+}
+
+/*!
+  \return The minimum of the range
+  \sa setRange(), setMinimum(), maximum()
+*/
+double QwtWheel::minimum() const
+{
+    return d_data->minimum;
+}
+
+/*!
+  Set the maximum value of the range
+
+  \param value Maximum value
+  \sa setRange(), setMinimum(), maximum()
+*/
+void QwtWheel::setMaximum( double value )
+{
+    setRange( minimum(), value );
+}
+
+/*!
+  \return The maximum of the range
+  \sa setRange(), setMaximum(), minimum()
+*/
+double QwtWheel::maximum() const
+{
+    return d_data->maximum;
+}
+
+/*!
+  \brief Set a new value without adjusting to the step raster
+
+  \param value New value
+
+  \sa value(), valueChanged()
+  \warning The value is clipped when it lies outside the range.
+*/
+void QwtWheel::setValue( double value )
+{
+    stopFlying();
+    d_data->isScrolling = false;
+
+    value = qBound( d_data->minimum, value, d_data->maximum );
+
+    if ( d_data->value != value )
+    {
+        d_data->value = value;
+
+        update();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  \return Current value of the wheel
+  \sa setValue(), valueChanged()
+ */
+double QwtWheel::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief En/Disable inverted appearance
+
+  An inverted wheel increases its values in the opposite direction.
+  The direction of an inverted horizontal wheel will be from right to left
+  an inverted vertical wheel will increase from bottom to top.
+  
+  \param on En/Disable inverted appearance
+  \sa isInverted()
+ 
+ */
+void QwtWheel::setInverted( bool on )
+{
+    if ( d_data->inverted != on )
+    {
+        d_data->inverted = on;
+        update();
+    }
+}
+
+/*!
+  \return True, when the wheel is inverted
+  \sa setInverted()
+ */
+bool QwtWheel::isInverted() const
+{
+    return d_data->inverted;
+}
+
+/*!
+  \brief En/Disable wrapping
+
+  If wrapping is true stepping up from maximum() value will take 
+  you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+ */
+void QwtWheel::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */
+bool QwtWheel::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  \brief Set the slider's mass for flywheel effect.
+
+  If the slider's mass is greater then 0, it will continue
+  to move after the mouse button has been released. Its speed
+  decreases with time at a rate depending on the slider's mass.
+  A large mass means that it will continue to move for a
+  long time.
+
+  Derived widgets may overload this function to make it public.
+
+  \param mass New mass in kg
+
+  \bug If the mass is smaller than 1g, it is set to zero.
+       The maximal mass is limited to 100kg.
+  \sa mass()
+*/
+void QwtWheel::setMass( double mass )
+{
+    if ( mass < 0.001 )
+    {
+        d_data->mass = 0.0;
+    }
+    else
+    {
+        d_data->mass = qMin( 100.0, mass );
+    }
+
+    if ( d_data->mass <= 0.0 )
+        stopFlying();
+}
+
+/*!
+  \return mass
+  \sa setMass()
+*/
+double QwtWheel::mass() const
+{
+    return d_data->mass;
+}
+
+//!  Stop the flying movement of the wheel
+void QwtWheel::stopFlying()
+{
+    if ( d_data->timerId != 0 )
+    {
+        killTimer( d_data->timerId );
+        d_data->timerId = 0;
+        d_data->speed = 0.0;
+    }
+}
+
+double QwtWheel::boundedValue( double value ) const
+{
+    const double range = d_data->maximum - d_data->minimum;
+    
+    if ( d_data->wrapping && range >= 0.0 )
+    {
+        if ( value < d_data->minimum )
+        {
+            value += ::ceil( ( d_data->minimum - value ) / range ) * range;
+        }       
+        else if ( value > d_data->maximum )
+        {
+            value -= ::ceil( ( value - d_data->maximum ) / range ) * range;
+        }
+    }
+    else
+    {
+        value = qBound( d_data->minimum, value, d_data->maximum );
+    }
+
+    return value;
+}
+
+double QwtWheel::alignedValue( double value ) const
+{
+    const double stepSize = d_data->singleStep;
+
+    if ( stepSize > 0.0 )
+    {
+        value = d_data->minimum +
+            qRound( ( value - d_data->minimum ) / stepSize ) * stepSize;
+
+        // correct rounding error at the border
+        if ( qFuzzyCompare( value, d_data->maximum ) )
+            value = d_data->maximum;
+            
+        // correct rounding error if value = 0
+        if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+            value = 0.0;
+    }       
+
+    return value;
+}
+
diff --git a/qwt/qwt_wheel.h b/qwt/qwt_wheel.h
new file mode 100644
index 0000000..846543b
--- /dev/null
+++ b/qwt/qwt_wheel.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_WHEEL_H
+#define QWT_WHEEL_H
+
+#include "qwt_global.h"
+#include <qwidget.h>
+
+/*!
+  \brief The Wheel Widget
+
+  The wheel widget can be used to change values over a very large range
+  in very small steps. Using the setMass() member, it can be configured
+  as a flying wheel.
+
+  The default range of the wheel is [0.0, 100.0]
+
+  \sa The radio example.
+*/
+class QWT_EXPORT QwtWheel: public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( Qt::Orientation orientation
+                READ orientation WRITE setOrientation )
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+    Q_PROPERTY( double minimum READ minimum WRITE setMinimum )
+    Q_PROPERTY( double maximum READ maximum WRITE setMaximum )
+
+    Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep )
+    Q_PROPERTY( int pageStepCount READ pageStepCount WRITE setPageStepCount )
+    Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment )
+
+    Q_PROPERTY( bool tracking READ isTracking WRITE setTracking )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+    Q_PROPERTY( bool inverted READ isInverted WRITE setInverted )
+
+    Q_PROPERTY( double mass READ mass WRITE setMass )
+    Q_PROPERTY( int updateInterval READ updateInterval WRITE setUpdateInterval )
+
+    Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle )
+    Q_PROPERTY( double viewAngle READ viewAngle WRITE setViewAngle )
+    Q_PROPERTY( int tickCount READ tickCount WRITE setTickCount )
+    Q_PROPERTY( int wheelWidth READ wheelWidth WRITE setWheelWidth )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int wheelBorderWidth READ wheelBorderWidth WRITE setWheelBorderWidth )
+
+public:
+    explicit QwtWheel( QWidget *parent = NULL );
+    virtual ~QwtWheel();
+
+    double value() const;
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    double totalAngle() const;
+    double viewAngle() const;
+
+    void setTickCount( int );
+    int tickCount() const;
+
+    void setWheelWidth( int );
+    int wheelWidth() const;
+
+    void setWheelBorderWidth( int );
+    int wheelBorderWidth() const;
+
+    void setBorderWidth( int );
+    int borderWidth() const;
+
+    void setInverted( bool tf );
+    bool isInverted() const;
+
+    void setWrapping( bool tf );
+    bool wrapping() const;
+
+    void setSingleStep( double );
+    double singleStep() const;
+
+    void setPageStepCount( int );
+    int pageStepCount() const;
+
+    void setStepAlignment( bool on );
+    bool stepAlignment() const;
+
+    void setRange( double vmin, double vmax );
+
+    void setMinimum( double min );
+    double minimum() const;
+
+    void setMaximum( double max );
+    double maximum() const;
+
+    void setUpdateInterval( int );
+    int updateInterval() const;
+
+    void setTracking( bool enable );
+    bool isTracking() const;
+
+    double mass() const;
+
+public Q_SLOTS:
+    void setValue( double );
+    void setTotalAngle ( double );
+    void setViewAngle( double );
+    void setMass( double );
+
+Q_SIGNALS:
+
+    /*!
+      \brief Notify a change of value.
+
+      When tracking is enabled this signal will be emitted every
+      time the value changes. 
+
+      \param value new value
+      \sa setTracking()
+    */
+    void valueChanged( double value );
+
+    /*!
+      This signal is emitted when the user presses the
+      the wheel with the mouse
+    */
+    void wheelPressed();
+
+    /*!
+      This signal is emitted when the user releases the mouse
+    */
+    void wheelReleased();
+
+    /*!
+      This signal is emitted when the user moves the
+      wheel with the mouse.
+
+      \param value new value
+    */
+    void wheelMoved( double value );
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void mouseMoveEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void timerEvent( QTimerEvent * );
+
+    void stopFlying();
+
+    QRect wheelRect() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    virtual void drawTicks( QPainter *, const QRectF & );
+    virtual void drawWheelBackground( QPainter *, const QRectF & );
+
+    virtual double valueAt( const QPoint & ) const;
+
+private:
+    double alignedValue( double ) const;
+    double boundedValue( double ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/qwt/qwt_widget_overlay.cpp b/qwt/qwt_widget_overlay.cpp
new file mode 100644
index 0000000..07c6272
--- /dev/null
+++ b/qwt/qwt_widget_overlay.cpp
@@ -0,0 +1,373 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt_widget_overlay.h"
+#include "qwt_painter.h"
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qimage.h>
+#include <qevent.h>
+
+static QImage::Format qwtMaskImageFormat()
+{
+    if ( QwtPainter::isX11GraphicsSystem() )
+        return QImage::Format_ARGB32;
+
+    return QImage::Format_ARGB32_Premultiplied;
+}
+
+static QRegion qwtAlphaMask( 
+    const QImage& image, const QVector<QRect> rects )
+{
+    const int w = image.width();
+    const int h = image.height();
+
+    QRegion region;
+    QRect rect;
+
+    for ( int i = 0; i < rects.size(); i++ )
+    {
+        int x1, x2, y1, y2;
+        rects[i].getCoords( &x1, &y1, &x2, &y2 );
+
+        x1 = qMax( x1, 0 );
+        x2 = qMin( x2, w - 1 );
+        y1 = qMax( y1, 0 );
+        y2 = qMin( y2, h - 1 );
+
+        for ( int y = y1; y <= y2; ++y ) 
+        {
+            bool inRect = false;
+            int rx0 = -1;
+
+            const uint *line = 
+                reinterpret_cast<const uint *> ( image.scanLine( y ) ) + x1;
+            for ( int x = x1; x <= x2; x++ ) 
+            {
+                const bool on = ( ( *line++ >> 24 ) != 0 );
+                if ( on != inRect ) 
+                {
+                    if ( inRect  ) 
+                    {
+                        rect.setCoords( rx0, y, x - 1, y );
+                        region += rect;
+                    } 
+                    else 
+                    {
+                        rx0 = x;
+                    }
+
+                    inRect = on;
+                } 
+            }
+
+            if ( inRect ) 
+            {
+                rect.setCoords( rx0, y, x2, y );
+                region = region.united( rect );
+            }
+        }
+    }
+
+    return region;
+}
+
+class QwtWidgetOverlay::PrivateData
+{
+public:
+    PrivateData():
+        maskMode( QwtWidgetOverlay::MaskHint ),
+        renderMode( QwtWidgetOverlay::AutoRenderMode ),
+        rgbaBuffer( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        resetRgbaBuffer();
+    }
+
+    void resetRgbaBuffer()
+    {
+        if ( rgbaBuffer )
+        {
+            ::free( rgbaBuffer );
+            rgbaBuffer = NULL;
+        }
+    }
+
+    MaskMode maskMode;
+    RenderMode renderMode;
+    uchar *rgbaBuffer;
+};
+
+/*!
+   \brief Constructor
+   \param widget Parent widget, where the overlay is aligned to
+*/
+QwtWidgetOverlay::QwtWidgetOverlay( QWidget* widget ):
+    QWidget( widget )
+{
+    d_data = new PrivateData;
+
+    setAttribute( Qt::WA_TransparentForMouseEvents );
+    setAttribute( Qt::WA_NoSystemBackground );
+    setFocusPolicy( Qt::NoFocus );
+
+    if ( widget )
+    {
+        resize( widget->size() );
+        widget->installEventFilter( this );
+    }
+}
+
+//! Destructor
+QwtWidgetOverlay::~QwtWidgetOverlay()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Specify how to find the mask for the overlay
+
+   \param mode New mode
+   \sa maskMode()
+ */
+void QwtWidgetOverlay::setMaskMode( MaskMode mode )
+{
+    if ( mode != d_data->maskMode )
+    {
+        d_data->maskMode = mode;
+        d_data->resetRgbaBuffer();
+    }
+}
+
+/*!
+   \return Mode how to find the mask for the overlay
+   \sa setMaskMode()
+ */
+QwtWidgetOverlay::MaskMode QwtWidgetOverlay::maskMode() const
+{
+    return d_data->maskMode;
+}
+
+/*!
+   Set the render mode
+   \param mode Render mode
+
+   \sa RenderMode, renderMode()
+*/
+void QwtWidgetOverlay::setRenderMode( RenderMode mode )
+{
+    d_data->renderMode = mode;
+}
+
+/*!
+   \return Render mode
+   \sa RenderMode, setRenderMode()
+ */
+QwtWidgetOverlay::RenderMode QwtWidgetOverlay::renderMode() const
+{
+    return d_data->renderMode;
+}
+
+/*!
+   Recalculate the mask and repaint the overlay
+ */
+void QwtWidgetOverlay::updateOverlay()
+{
+    updateMask();
+    update();
+}
+
+void QwtWidgetOverlay::updateMask()
+{
+    d_data->resetRgbaBuffer();
+
+    QRegion mask;
+
+    if ( d_data->maskMode == QwtWidgetOverlay::MaskHint )
+    {
+        mask = maskHint();
+    }
+    else if ( d_data->maskMode == QwtWidgetOverlay::AlphaMask )
+    {
+        // TODO: the image doesn't need to be larger than
+        //       the bounding rectangle of the hint !!
+
+        QRegion hint = maskHint();
+        if ( hint.isEmpty() )
+            hint += QRect( 0, 0, width(), height() );
+
+        // A fresh buffer from calloc() is usually faster
+        // than reinitializing an existing one with
+        // QImage::fill( 0 ) or memset()
+
+        d_data->rgbaBuffer = ( uchar* )::calloc( width() * height(), 4 );
+
+        QImage image( d_data->rgbaBuffer, 
+            width(), height(), qwtMaskImageFormat() );
+
+        QPainter painter( &image );
+        draw( &painter );
+        painter.end();
+
+        mask = qwtAlphaMask( image, hint.rects() );
+
+        if ( d_data->renderMode == QwtWidgetOverlay::DrawOverlay )
+        {
+            // we don't need the buffer later
+            d_data->resetRgbaBuffer();
+        }
+    }
+
+    // A bug in Qt initiates a full repaint of the widget
+    // when we change the mask, while we are visible !
+
+    setVisible( false );
+
+    if ( mask.isEmpty() )
+        clearMask();
+    else
+        setMask( mask );
+
+    setVisible( true );
+}
+
+/*!
+  Paint event
+  \param event Paint event
+
+  \sa drawOverlay()
+*/
+void QwtWidgetOverlay::paintEvent( QPaintEvent* event )
+{
+    const QRegion clipRegion = event->region();
+
+    QPainter painter( this );
+
+    bool useRgbaBuffer = false;
+    if ( d_data->renderMode == QwtWidgetOverlay::CopyAlphaMask )
+    {
+        useRgbaBuffer = true;
+    }
+    else if ( d_data->renderMode == QwtWidgetOverlay::AutoRenderMode )
+    {
+        if ( painter.paintEngine()->type() == QPaintEngine::Raster )
+            useRgbaBuffer = true;
+    }
+
+    if ( d_data->rgbaBuffer && useRgbaBuffer )
+    {
+        const QImage image( d_data->rgbaBuffer, 
+            width(), height(), qwtMaskImageFormat() );
+
+        QVector<QRect> rects;
+        if ( clipRegion.rects().size() > 2000 )
+        {
+            // the region is to complex
+            painter.setClipRegion( clipRegion );
+            rects += clipRegion.boundingRect();
+        }
+        else
+        {
+            rects = clipRegion.rects();
+        }
+
+        for ( int i = 0; i < rects.size(); i++ )
+        {
+            const QRect r = rects[i];
+            painter.drawImage( r.topLeft(), image, r );
+        }
+    }
+    else
+    {
+        painter.setClipRegion( clipRegion );
+        draw( &painter );
+    }
+}
+
+/*!
+  Resize event
+  \param event Resize event
+*/
+void QwtWidgetOverlay::resizeEvent( QResizeEvent* event )
+{
+    Q_UNUSED( event );
+
+    d_data->resetRgbaBuffer();
+}
+
+void QwtWidgetOverlay::draw( QPainter *painter ) const
+{
+    QWidget *widget = const_cast< QWidget *>( parentWidget() );
+    if ( widget )
+    {
+        painter->setClipRect( parentWidget()->contentsRect() );
+
+        // something special for the plot canvas
+        QPainterPath clipPath;
+
+        ( void )QMetaObject::invokeMethod(
+            widget, "borderPath", Qt::DirectConnection,
+            Q_RETURN_ARG( QPainterPath, clipPath ), Q_ARG( QRect, rect() ) );
+
+        if (!clipPath.isEmpty())
+        {
+            painter->setClipPath( clipPath, Qt::IntersectClip );
+        }
+    }
+
+    drawOverlay( painter );
+}
+
+/*!
+   \brief Calculate an approximation for the mask
+
+   - MaskHint
+     The hint is used as mask.
+
+   - AlphaMask
+     The hint is used to speed up the algorithm
+     for calculating a mask from non transparent pixels
+
+   - NoMask
+     The hint is unused.
+
+   The default implementation returns an invalid region
+   indicating no hint.
+
+   \return Hint for the mask
+ */
+QRegion QwtWidgetOverlay::maskHint() const
+{
+    return QRegion();
+}
+
+/*!
+  \brief Event filter
+
+  Resize the overlay according to the size of the parent widget.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return See QObject::eventFilter()
+*/
+
+bool QwtWidgetOverlay::eventFilter( QObject* object, QEvent* event )
+{
+    if ( object == parent() && event->type() == QEvent::Resize )
+    {
+        QResizeEvent *resizeEvent = static_cast<QResizeEvent *>( event );
+        resize( resizeEvent->size() );
+    }
+
+    return QObject::eventFilter( object, event );
+}
diff --git a/qwt/qwt_widget_overlay.h b/qwt/qwt_widget_overlay.h
new file mode 100644
index 0000000..40d4551
--- /dev/null
+++ b/qwt/qwt_widget_overlay.h
@@ -0,0 +1,148 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_WIDGET_OVERLAY_H
+#define QWT_WIDGET_OVERLAY_H
+
+#include "qwt_global.h"
+#include <qwidget.h>
+#include <qregion.h>
+
+class QPainter;
+
+/*!
+   \brief An overlay for a widget
+
+   The main use case of an widget overlay is to avoid
+   heavy repaint operation of the widget below.
+
+   F.e. in combination with the plot canvas an overlay 
+   avoid replots as the content of the canvas can be restored from 
+   its backing store.
+
+   QwtWidgetOverlay is an abstract base class. Deriving classes are
+   supposed to reimplement the following methods:
+
+   - drawOverlay()
+   - maskHint()
+
+   Internally QwtPlotPicker uses overlays for displaying 
+   the rubber band and the tracker text.
+
+   \sa QwtPlotCanvas::BackingStore
+ */
+class QWT_EXPORT QwtWidgetOverlay: public QWidget
+{
+public:
+    /*!
+       \brief Mask mode
+
+       When using masks the widget below gets paint events for
+       the masked regions of the overlay only. Otherwise
+       Qt triggers full repaints. On less powerful hardware
+       ( f.e embedded systems ) - or when using the raster paint 
+       engine on a remote desktop - bit blitting is a noticeable
+       operation, that needs to be avoided.
+       
+       If and how to mask depends on how expensive the calculation 
+       of the mask is and how many pixels can be excluded by the mask.
+
+       The default setting is MaskHint.
+
+       \sa setMaskMode(), maskMode()
+     */
+    enum MaskMode
+    {
+        //! Don't use a mask.
+        NoMask,
+
+        /*!
+           \brief Use maskHint() as mask
+
+           For many situations a fast approximation is good enough 
+           and it is not necessary to build a more detailed mask
+           ( f.e the bounding rectangle of a text ).
+         */
+        MaskHint,
+
+        /*!
+           \brief Calculate a mask by checking the alpha values
+
+           Sometimes it is not possible to give a fast approximation
+           and the mask needs to be calculated by drawing the overlay
+           and testing the result.
+          
+           When a valid maskHint() is available
+           only pixels inside this approximation are checked.
+         */
+        AlphaMask
+    };
+
+    /*!
+       \brief Render mode
+
+       For calculating the alpha mask the overlay has already
+       been painted to a temporary QImage. Instead of rendering
+       the overlay twice this buffer can be copied for drawing
+       the overlay.
+
+       On graphic systems using the raster paint engine ( QWS, Windows )
+       it means usually copying some memory only. On X11 it results in an
+       expensive operation building a pixmap and for simple overlays
+       it might not be recommended.
+
+       \note The render mode has no effect, when maskMode() != AlphaMask.
+     */
+    enum RenderMode
+    {
+        //! Copy the buffer, when using the raster paint engine.
+        AutoRenderMode,
+
+        //! Always copy the buffer
+        CopyAlphaMask,
+
+        //! Never copy the buffer
+        DrawOverlay
+    };
+
+    QwtWidgetOverlay( QWidget* );
+    virtual ~QwtWidgetOverlay();
+
+    void setMaskMode( MaskMode );
+    MaskMode maskMode() const;
+
+    void setRenderMode( RenderMode );
+    RenderMode renderMode() const;
+
+    void updateOverlay();
+
+    virtual bool eventFilter( QObject *, QEvent *);
+
+protected:
+    virtual void paintEvent( QPaintEvent* event );
+    virtual void resizeEvent( QResizeEvent* event );
+
+    virtual QRegion maskHint() const;
+
+    /*!
+       Draw the widget overlay
+       \param painter Painter
+     */
+    virtual void drawOverlay( QPainter *painter ) const = 0;
+
+private:
+    void updateMask();
+    void draw( QPainter * ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/qsstv.git



More information about the pkg-hamradio-commits mailing list