diff --git a/src/pappsomspp/processing/tandem/tandemwrapper.cpp b/src/pappsomspp/processing/tandem/tandemwrapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a7e71da611232102f787ab81fed8021bcfd3353
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapper.cpp
@@ -0,0 +1,200 @@
+/**
+ * \file tandemwrapper.cpp
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief run tandem directly on Bruker's data
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "tandemwrapper.h"
+#include <QCommandLineParser>
+#include <QDateTime>
+#include <QTimer>
+#include <QFile>
+#include <pappsomspp/pappsoexception.h>
+#include "tandemwrapperlib/tandemwrapperrun.h"
+
+
+TandemWrapper::TandemWrapper(QObject *parent) : QObject(parent)
+{
+  // get the instance of the main application
+  app = QCoreApplication::instance();
+  // setup everything here
+  // create any global objects
+  // setup debug and warning mode
+}
+
+// 10ms after the application starts this method will run
+// all QT messaging is running at this point so threads, signals and slots
+// will all work as expected.
+void
+TandemWrapper::run()
+{
+  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));
+  /* ./src/pt-mzxmlconverter -i
+   /gorgone/pappso/fichiers_fabricants/Bruker/tims_doc/tdf-sdk/example_data/200ngHeLaPASEF_2min_compressed.d/analysis.tdf
+   -o
+   /gorgone/pappso/versions_logiciels_pappso/xtpcpp/bruker/200ngHeLaPASEF_2min.mzXML
+  */
+
+  //./src/pt-mzxmlconverter -i
+  /// gorgone/pappso/fichiers_fabricants/Bruker/tims_doc/tdf-sdk/example_data/200ngHeLaPASEF_2min_compressed.d/analysis.tdf
+  //-o /tmp/test.xml
+
+
+  QTextStream errorStream(stderr, QIODevice::WriteOnly);
+  QTextStream outputStream(stdout, QIODevice::WriteOnly);
+
+  try
+    {
+      qDebug();
+      QCommandLineParser parser;
+
+      // throw pappso::PappsoException("test");
+      parser.setApplicationDescription(
+        QString("%1 ")
+          .arg(QCoreApplication::applicationName())
+          .append(PAPPSOMSPP_NAME)
+          .append(" ")
+          .append(PAPPSOMSPP_VERSION)
+          .append(" tandem run on Bruker's data"));
+      parser.addHelpOption();
+      parser.addVersionOption();
+      QCommandLineOption tandemOption(
+        QStringList() << "tandem",
+        QCoreApplication::translate("tandem",
+                                    "tandem executable binary file <tandem>."),
+        QCoreApplication::translate("tandem", "tandem"));
+
+
+      QCommandLineOption tmpDirOption(
+        QStringList() << "t"
+                      << "tmp",
+        QCoreApplication::translate("tmp", "temporary directory"));
+
+      QCommandLineOption centroidOption(
+        QStringList() << "centroid",
+        QCoreApplication::translate("centroid",
+                                    "centroid function parameters <centroid>."),
+        QCoreApplication::translate("centroid", "centroid"));
+
+
+      parser.addOption(tandemOption);
+      parser.addOption(tmpDirOption);
+      parser.addOption(centroidOption);
+
+      qDebug();
+
+      // Process the actual command line arguments given by the user
+      parser.process(*app);
+
+      // QCoreApplication * app(this);
+      // Add your main code here
+      qDebug();
+      const QDateTime dt_begin = QDateTime::currentDateTime();
+      const QStringList args   = parser.positionalArguments();
+
+
+      TandemWrapperRun tandem_run(parser.value(tandemOption),
+                                  parser.value(tmpDirOption),
+                                  parser.value(centroidOption));
+
+      if(args.size() > 0)
+        {
+          tandem_run.run(args[0], outputStream, errorStream);
+        }
+
+      qDebug();
+    }
+  catch(pappso::PappsoException &error)
+    {
+      errorStream << "Oops! an error occurred in PAPPSO MS tools. Dont Panic :"
+                  << endl;
+      errorStream << error.qwhat() << endl;
+      exit(1);
+      app->exit(1);
+    }
+
+  catch(std::exception &error)
+    {
+      errorStream << "Oops! an error occurred in PAPPSO MS tools. Dont Panic :"
+                  << endl;
+      errorStream << error.what() << endl;
+      exit(1);
+      app->exit(1);
+    }
+
+
+  // you must call quit when complete or the program will stay in the
+  // messaging loop
+  quit();
+}
+
+// call this routine to quit the application
+void
+TandemWrapper::quit()
+{
+  // you can do some cleanup here
+  // then do emit finished to signal CoreApplication to quit
+  emit finished();
+}
+
+// shortly after quit is called the CoreApplication will signal this routine
+// this is a good place to delete any objects that were created in the
+// constructor and/or to stop any threads
+void
+TandemWrapper::aboutToQuitApp()
+{
+  // stop threads
+  // sleep(1);   // wait for threads to stop.
+  // delete any objects
+}
+
+
+int
+main(int argc, char **argv)
+{
+  // QTextStream consoleErr(stderr);
+  // QTextStream consoleOut(stdout, QIODevice::WriteOnly);
+  // ConsoleOut::setCout(new QTextStream(stdout, QIODevice::WriteOnly));
+  // ConsoleOut::setCerr(new QTextStream(stderr, QIODevice::WriteOnly));
+  qDebug();
+  QCoreApplication app(argc, argv);
+  qDebug();
+  QCoreApplication::setApplicationName("tandemwrapper");
+  QCoreApplication::setApplicationVersion(PAPPSOMSPP_VERSION);
+  QLocale::setDefault(QLocale::system());
+
+  // create the main class
+  TandemWrapper myMain;
+  // connect up the signals
+  QObject::connect(&myMain, SIGNAL(finished()), &app, SLOT(quit()));
+  QObject::connect(
+    &app, SIGNAL(aboutToQuit()), &myMain, SLOT(aboutToQuitApp()));
+  qDebug();
+
+
+  // This code will start the messaging engine in QT and in
+  // 10ms it will start the execution in the MainClass.run routine;
+  QTimer::singleShot(10, &myMain, SLOT(run()));
+  return app.exec();
+}
diff --git a/src/pappsomspp/processing/tandem/tandemwrapper.h b/src/pappsomspp/processing/tandem/tandemwrapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..36b6c472fd83fbff4c13a20353ec0de1c877ed7e
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapper.h
@@ -0,0 +1,67 @@
+/**
+ * \file tandemwrapper.h
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief run tandem directly on Bruker's data
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <QDebug>
+
+#include <QObject>
+#include <QCoreApplication>
+#include "../../config.h"
+
+class TandemWrapper : public QObject
+{
+  Q_OBJECT
+
+  private:
+  QCoreApplication *app;
+
+  public:
+  explicit TandemWrapper(QObject *parent = 0);
+  /////////////////////////////////////////////////////////////
+  /// Call this to quit application
+  /////////////////////////////////////////////////////////////
+  void quit();
+
+  signals:
+  /////////////////////////////////////////////////////////////
+  /// Signal to finish, this is connected to Application Quit
+  /////////////////////////////////////////////////////////////
+  void finished();
+
+  public slots:
+  /////////////////////////////////////////////////////////////
+  /// This is the slot that gets called from main to start everything
+  /// but, everthing is set up in the Constructor
+  /////////////////////////////////////////////////////////////
+  void run();
+
+  /////////////////////////////////////////////////////////////
+  /// slot that get signal when that application is about to quit
+  /////////////////////////////////////////////////////////////
+  void aboutToQuitApp();
+};
diff --git a/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.cpp b/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e88649fcd0a0e749fe851fc6f7a558358e081889
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.cpp
@@ -0,0 +1,366 @@
+/**
+ * \file lib/tandemwrapperrun.cpp
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief actually does really run tandem directly on Bruker's data
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "tandemwrapperrun.h"
+#include <QDebug>
+#include <QFileInfo>
+#include <QSettings>
+#include <pappsomspp/pappsoexception.h>
+#include <pappsomspp/msfile/msfileaccessor.h>
+#include <pappsomspp/msrun/private/timsmsrunreaderms2.h>
+#include <pappsomspp/processing/filters/filterpseudocentroid.h>
+#include <pappsomspp/msrun/mzxmloutput.h>
+#include "xtandeminputsaxhandler.h"
+
+TandemWrapperRun::TandemWrapperRun(const QString &tandem_binary,
+                                   const QString &tmp_dir,
+                                   const QString &centroid_options)
+{
+
+  setTandemBinaryPath(tandem_binary);
+  m_centroidOptions = centroid_options;
+}
+
+TandemWrapperRun::~TandemWrapperRun()
+{
+}
+
+void
+TandemWrapperRun::setTandemBinaryPath(const QString &tandem_binary_path)
+{
+
+  m_tandemBinary = tandem_binary_path;
+  QSettings settings;
+  if(m_tandemBinary.isEmpty())
+    {
+      m_tandemBinary =
+        settings.value("path/tandem_binary", "/usr/bin/tandem").toString();
+    }
+  // check for tandem executable
+  m_tandemVersion = checkXtandemVersion(m_tandemBinary);
+
+  qDebug() << m_tandemVersion;
+  settings.setValue("path/tandem_binary", m_tandemBinary);
+}
+
+
+const QString
+TandemWrapperRun::checkXtandemVersion(const QString &tandem_bin_path)
+{
+  qDebug();
+  // check tandem path
+  QFileInfo tandem_exe(tandem_bin_path);
+  if(!tandem_exe.exists())
+    {
+      // dir.path() returns the unique directory path
+      throw pappso::PappsoException(
+        QObject::tr(
+          "X!Tandem software not found at %1.\nPlease check the X!Tandem "
+          "installation on your computer and set tandem.exe path.")
+          .arg(tandem_exe.absoluteFilePath()));
+    }
+  if(!tandem_exe.isReadable())
+    {
+      // dir.path() returns the unique directory path
+      throw pappso::PappsoException(
+        QObject::tr("Please check permissions on X!Tandem software found at %1 "
+                    "(file not readable).")
+          .arg(tandem_exe.absoluteFilePath()));
+    }
+  if(!tandem_exe.isExecutable())
+    {
+      // dir.path() returns the unique directory path
+      throw pappso::PappsoException(
+        QObject::tr("Please check permissions on X!Tandem software found at %1 "
+                    "(file not executable).")
+          .arg(tandem_exe.absoluteFilePath()));
+    }
+
+
+  QString version_return;
+  QStringList arguments;
+
+  arguments << "-v";
+
+  QProcess *xt_process = new QProcess();
+  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
+
+  xt_process->start(tandem_bin_path, arguments);
+
+  if(!xt_process->waitForStarted())
+    {
+      throw pappso::PappsoException(
+        QObject::tr("X!Tandem %1 process failed to start")
+          .arg(m_tandemVersion));
+    }
+
+  while(xt_process->waitForReadyRead(1000))
+    {
+    }
+  /*
+  if (!xt_process->waitForFinished(_max_xt_time_ms)) {
+      throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
+  to finish : timeout at %1").arg(_max_xt_time_ms));
+  }
+  */
+  QByteArray result = xt_process->readAll();
+
+
+  qDebug() << result.constData();
+
+  // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)
+
+  QRegExp parse_version("(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
+  qDebug() << parse_version;
+  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\((.*)\\)",
+  //			Pattern.CASE_INSENSITIVE);
+
+  if(parse_version.exactMatch(result.constData()))
+    {
+      version_return = QString("X!Tandem %1 %2")
+                         .arg(parse_version.capturedTexts()[2])
+                         .arg(parse_version.capturedTexts()[3]); //.join(" ");
+    }
+  else
+    {
+      throw pappso::PappsoException(
+        QObject::tr("This executable %1 may not be a valid X!Tandem software. "
+                    "Please check your X!Tandem installation.")
+          .arg(tandem_bin_path));
+    }
+
+  QProcess::ExitStatus Status = xt_process->exitStatus();
+  delete xt_process;
+  if(Status != 0)
+    {
+      // != QProcess::NormalExit
+      throw pappso::PappsoException(
+        QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
+          .arg(tandem_bin_path)
+          .arg(arguments.join(" ").arg(result.data())));
+    }
+  qDebug();
+  return version_return;
+}
+
+
+bool
+TandemWrapperRun::shouldIstop()
+{
+  return false;
+}
+
+
+void
+TandemWrapperRun::readyReadStandardOutput()
+{
+  *mp_outputStream << m_xtProcess->readAllStandardOutput();
+}
+
+void
+TandemWrapperRun::readyReadStandardError()
+{
+  *mp_errorStream << m_xtProcess->readAllStandardError();
+}
+
+void
+TandemWrapperRun::writeFinalTandemOutput(const QString &tmp_tandem_output,
+                                         const QString &final_tandem_output)
+{
+}
+
+
+void
+TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
+{
+  // read original tandem input file
+  // store original ms data file name
+  // create new mzXML data file in temporary directory
+  // create new tandem input file based on new mzXML file
+  QString mzxml_data_file_name  = "/tmp/msdata.mzxml";
+  QString wrapped_tandem_input  = "/tmp/input_tandem.xml";
+  QString wrapped_tandem_output = "/tmp/output_tandem.xml";
+
+  XtandemInputSaxHandler wrap_input(
+    mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
+
+  QFile qfile(tandem_input_file);
+  QXmlInputSource xmlInputSource(&qfile);
+  QXmlSimpleReader simplereader;
+  simplereader.setContentHandler(&wrap_input);
+  simplereader.setErrorHandler(&wrap_input);
+
+  if(simplereader.parse(xmlInputSource))
+    {
+    }
+  else
+    {
+      throw pappso::PappsoException(
+        QObject::tr("Error reading %1 X!Tandem input file :\n %2")
+          .arg(tandem_input_file)
+          .arg(wrap_input.errorString()));
+    }
+
+  // convert to mzXML
+  QString original_msdata_file_name = wrap_input.getOriginalMsDataFileName();
+  convertOrginalMsData2mzXmlData(original_msdata_file_name,
+                                 mzxml_data_file_name);
+
+  // launch tandem
+  runTandem(wrapped_tandem_input);
+
+  // rewrite tandem result file
+  writeFinalTandemOutput(wrapped_tandem_output,
+                         wrap_input.getOriginalTandemOutputFileName());
+}
+
+void
+TandemWrapperRun::convertOrginalMsData2mzXmlData(const QString &origin,
+                                                 const QString &target) const
+{
+  qDebug();
+  pappso::MsFileAccessor origin_access(origin, "runa1");
+  origin_access.setPreferedFileReaderType(pappso::MzFormat::brukerTims,
+                                          pappso::FileReaderType::tims_ms2);
+  origin_access.getMsRunIds();
+
+  pappso::MsRunReaderSPtr p_reader;
+  p_reader = origin_access.msRunReaderSp(origin_access.getMsRunIds().front());
+  if(!m_centroidOptions.isEmpty())
+    {
+      pappso::TimsMsRunReaderMs2 *tims2_reader =
+        dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
+      if(tims2_reader != nullptr)
+        {
+          qDebug();
+          QStringList option_list = m_centroidOptions.split(" ");
+          std::shared_ptr<pappso::FilterPseudoCentroid> ms2filter =
+            std::make_shared<pappso::FilterPseudoCentroid>(
+              option_list.at(0).toDouble(),
+              option_list.at(1).toDouble(),
+              option_list.at(2).toDouble(),
+              option_list.at(3).toDouble());
+
+          tims2_reader->setMs2FilterCstSPtr(ms2filter);
+          qDebug();
+        }
+    }
+
+
+  pappso::MzxmlOutput *p_mzxml_output;
+  QFile output_file(target);
+  // qDebug() << " TsvDirectoryWriter::writeSheet " <<
+  // QFileInfo(*_p_ofile).absoluteFilePath();
+  if(output_file.open(QIODevice::WriteOnly))
+    {
+      p_mzxml_output =
+        new pappso::MzxmlOutput(QTextStream(&output_file).device());
+
+      p_mzxml_output->maskMs1(true);
+
+      p_mzxml_output->setReadAhead(true);
+
+      p_mzxml_output->write(p_reader.get());
+
+      p_mzxml_output->close();
+    }
+  else
+    {
+      throw pappso::PappsoException(
+        tr("unable to write into %1 mzXML output file").arg(target));
+    }
+
+  qDebug();
+}
+
+void
+TandemWrapperRun::run(const QString &tandem_input_file,
+                      QTextStream &output_stream,
+                      QTextStream &error_stream)
+{
+  mp_outputStream = &output_stream;
+  mp_errorStream  = &error_stream;
+
+  wrapTandemInputFile(tandem_input_file);
+  mp_outputStream = nullptr;
+  mp_errorStream  = nullptr;
+}
+
+void
+TandemWrapperRun::runTandem(const QString &tandem_input_file)
+{
+
+  m_xtProcess = new QProcess();
+  QStringList arguments;
+  arguments << tandem_input_file;
+  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
+  m_xtProcess->start(m_tandemBinary, arguments);
+
+  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
+
+  connect(m_xtProcess,
+          &QProcess::readyReadStandardOutput,
+          this,
+          &TandemWrapperRun::readyReadStandardOutput);
+  connect(m_xtProcess,
+          &QProcess::readyReadStandardError,
+          this,
+          &TandemWrapperRun::readyReadStandardError);
+
+
+  if(!m_xtProcess->waitForStarted())
+    {
+      throw pappso::PappsoException(
+        QObject::tr("X!Tandem process failed to start"));
+    }
+
+  while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs))
+    {
+      //_p_monitor->appendText(xt_process->readAll().data());
+      // data.append(xt_process->readAll());
+      if(shouldIstop())
+        {
+          m_xtProcess->kill();
+          delete m_xtProcess;
+          throw pappso::PappsoException(
+            QObject::tr("X!Tandem stopped by the user processing on file %1")
+              .arg(tandem_input_file));
+        }
+    }
+
+  QProcess::ExitStatus Status = m_xtProcess->exitStatus();
+
+  delete m_xtProcess;
+  if(Status != 0)
+    {
+      // != QProcess::NormalExit
+      throw pappso::PappsoException(
+        QObject::tr("error executing X!Tandem Status != 0 : %1")
+          .arg(m_tandemBinary));
+    }
+  m_xtProcess = nullptr;
+}
diff --git a/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.h b/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.h
new file mode 100644
index 0000000000000000000000000000000000000000..416bf87da97ec5fa06a39f70ee77622529c6da74
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapperlib/tandemwrapperrun.h
@@ -0,0 +1,107 @@
+/**
+ * \file lib/tandemwrapperrun.h
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief actually does really run tandem directly on Bruker's data
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <QDebug>
+#include <QObject>
+#include <QProcess>
+
+/**
+ * @todo write docs
+ */
+class TandemWrapperRun : public QObject
+{
+  Q_OBJECT
+  public:
+  /** @brief prepare a tandem run
+   * @param tandem_binary file path to tandem.exe if not set, a default value is
+   * given in QSettings
+   * @param tmp_dir temporary directory, where to write mzXML file conversion if
+   * not set, a default value is given in QSettings
+   * @param centroid_options controls the way MS2 spectrum are centroided from
+   * raw bruker's data if not set, a default value is given in QSettings
+   */
+  TandemWrapperRun(const QString &tandem_binary,
+                   const QString &tmp_dir,
+                   const QString &centroid_options);
+
+  /** @brief run a tandem job
+   * @param tandem_input_file tandem xml input file
+   * @param output_stream standard output where to write tandem stdout
+   * @param error_stream standard error where to write tandem stderr
+   */
+  void run(const QString &tandem_input_file,
+           QTextStream &output_stream,
+           QTextStream &error_stream);
+
+  /**
+   * Destructor
+   */
+  ~TandemWrapperRun();
+
+  private:
+  void setTandemBinaryPath(const QString &tandem_binary_path);
+  bool shouldIstop();
+  const QString checkXtandemVersion(const QString &tandem_bin_path);
+  void wrapTandemInputFile(const QString &tandem_input_file);
+
+  void convertOrginalMsData2mzXmlData(const QString &origin,
+                                      const QString &target) const;
+
+
+  /** @brief run a tandem job
+   * @param tandem_input_file tandem xml input file
+   */
+  void runTandem(const QString &tandem_input_file);
+
+  /** @brief tandem output modification
+   * tandem output is modified to contain the Bruker's file as input and
+   * centroidization parameters
+   * @param tmp_tandem_output raw tandem output filename
+   * @param final_tandem_output final destination file for modified tandem
+   * output
+   */
+  void writeFinalTandemOutput(const QString &tmp_tandem_output,
+                              const QString &final_tandem_output);
+
+
+  private slots:
+  void readyReadStandardOutput();
+  void readyReadStandardError();
+
+  private:
+  QString m_tandemBinary;
+  QString m_tandemVersion;
+  QString m_tmpDir;
+  QString m_centroidOptions;
+  int m_maxTandemRunTimeMs = (60000 * 60 * 24); // 1 day
+  QProcess *m_xtProcess    = nullptr;
+
+  QTextStream *mp_outputStream = nullptr;
+  QTextStream *mp_errorStream  = nullptr;
+};
diff --git a/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.cpp b/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d46f082250aa813da1506250818f2a2f910cfcb8
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.cpp
@@ -0,0 +1,306 @@
+/**
+ * \file lib/xtandeminputsaxhandler.cpp
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief rewrites tandem xml input file with temporary files
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+
+#include "xtandeminputsaxhandler.h"
+#include <pappsomspp/pappsoexception.h>
+#include <QFileInfo>
+
+XtandemInputSaxHandler::XtandemInputSaxHandler(
+  const QString &destinationMzXmlFile,
+  const QString &destinationTandemInputFile,
+  const QString &destinationTandemOutputFile)
+  : m_destinationTandemInputFile(destinationTandemInputFile)
+{
+  m_destinationMzXmlFileName        = destinationMzXmlFile;
+  m_destinationTandemOutputFileName = destinationTandemOutputFile;
+  m_destinationTandemInputFileName =
+    QFileInfo(destinationTandemInputFile).absoluteFilePath();
+
+  m_destinationTandemInputFile.open(QIODevice::WriteOnly);
+  p_writeXmlTandemInput = new QXmlStreamWriter();
+  p_writeXmlTandemInput->setDevice(&m_destinationTandemInputFile);
+}
+
+XtandemInputSaxHandler::~XtandemInputSaxHandler()
+{
+  m_destinationTandemInputFile.close();
+  delete p_writeXmlTandemInput;
+}
+
+void
+XtandemInputSaxHandler::writeOpenTag(const QString &qName,
+                                     const QXmlAttributes &attributes)
+{
+  p_writeXmlTandemInput->writeStartElement(qName);
+  for(std::size_t i = 0; i < attributes.length(); i++)
+    {
+      p_writeXmlTandemInput->writeAttribute(attributes.qName(i),
+                                            attributes.value(i));
+    }
+}
+
+bool
+XtandemInputSaxHandler::startElement(const QString &namespaceURI,
+                                     const QString &localName,
+                                     const QString &qName,
+                                     const QXmlAttributes &attributes)
+{
+  /*
+<?xml version="1.0" encoding="UTF-8"?>
+<bioml label="20191222_18_EF1_test_condor_22janv_third_step.xml">
+<note type="heading">Paths</note>
+<note type="input" label="list path, default
+parameters">/gorgone/pappso/jouy/presets/metapappso/Lumos_04112019_PROTEOCARDIS_THIRD_STEP_AB.xml</note>
+<note type="input" label="list path, taxonomy
+information">/gorgone/pappso/jouy/users/Celine/2019_Lumos/20191222_107_Juste_APD/metapappso_condor/params/xtandem_database_third_step_test_condor_22janv.database</note>
+<note type="input" label="spectrum,
+path">/gorgone/pappso/jouy/raw/2019_Lumos/20191222_107_Juste/20191222_18_EF1.mzXML</note>
+<note type="heading">Protein general</note>
+<note type="input" label="protein, taxon">usedefined</note>
+<note type="heading">Output</note>
+<note type="input" label="output,
+path">/gorgone/pappso/jouy/users/Celine/2019_Lumos/20191222_107_Juste_APD/metapappso_condor/test_run/20191222_18_EF1_third_step_test_condor_22janv.xml</note>
+</bioml>
+*/
+  m_tagStack.push_back(qName);
+  bool is_ok = true;
+
+  try
+    {
+      m_currentText.clear();
+      //<bioml label="example api document">
+      if(m_tagStack.size() == 1)
+        {
+          if(qName != "bioml")
+            {
+              m_errorString = QObject::tr(
+                                "ERROR in XtandemInputSaxHandler::startElement "
+                                "root tag %1 is not <bioml>")
+                                .arg(qName);
+              m_isTandemParameter = false;
+              return false;
+            }
+          else
+            {
+
+              m_isTandemParameter = true;
+              // label="20191222_18_EF1_test_condor_22janv_third_step.xml"
+              m_labelName = attributes.value("label");
+
+              writeOpenTag(qName, attributes);
+            }
+        }
+      // startElement_group
+
+      if(qName == "note")
+        {
+          is_ok = startElement_note(attributes);
+        }
+    }
+  catch(pappso::PappsoException exception_pappso)
+    {
+      m_errorString = QObject::tr(
+                        "ERROR in XtandemInputSaxHandler::startElement "
+                        "tag %1, PAPPSO exception:\n%2")
+                        .arg(qName)
+                        .arg(exception_pappso.qwhat());
+      return false;
+    }
+  catch(std::exception exception_std)
+    {
+      m_errorString = QObject::tr(
+                        "ERROR in XtandemInputSaxHandler::startElement "
+                        "tag %1, std exception:\n%2")
+                        .arg(qName)
+                        .arg(exception_std.what());
+      return false;
+    }
+  return is_ok;
+}
+
+bool
+XtandemInputSaxHandler::endElement(const QString &namespaceURI,
+                                   const QString &localName,
+                                   const QString &qName)
+{
+
+  bool is_ok = true;
+  // endElement_peptide_list
+  try
+    {
+
+      if(qName == "note")
+        {
+          is_ok = endElement_note();
+        }
+      else
+        {
+          p_writeXmlTandemInput->writeCharacters(m_currentText);
+          p_writeXmlTandemInput->writeEndElement();
+        }
+    }
+  catch(pappso::PappsoException exception_pappso)
+    {
+      m_errorString = QObject::tr(
+                        "ERROR in XtandemInputSaxHandler::endElement tag "
+                        "%1, PAPPSO exception:\n%2")
+                        .arg(qName)
+                        .arg(exception_pappso.qwhat());
+      return false;
+    }
+  catch(std::exception exception_std)
+    {
+      m_errorString = QObject::tr(
+                        "ERROR in XtandemInputSaxHandler::endElement tag "
+                        "%1, std exception:\n%2")
+                        .arg(qName)
+                        .arg(exception_std.what());
+      return false;
+    }
+
+  m_currentText.clear();
+  m_tagStack.pop_back();
+
+  return is_ok;
+}
+
+bool
+XtandemInputSaxHandler::startDocument()
+{
+
+  p_writeXmlTandemInput->setAutoFormatting(true);
+  p_writeXmlTandemInput->writeStartDocument("1.0");
+  return true;
+}
+
+bool
+XtandemInputSaxHandler::endDocument()
+{
+  p_writeXmlTandemInput->writeEndDocument();
+  return true;
+}
+
+bool
+XtandemInputSaxHandler::characters(const QString &str)
+{
+  m_currentText += str;
+  return true;
+}
+
+
+bool
+XtandemInputSaxHandler::error(const QXmlParseException &exception)
+{
+  m_errorString = QObject::tr(
+                    "Parse error at line %1, column %2 :\n"
+                    "%3")
+                    .arg(exception.lineNumber())
+                    .arg(exception.columnNumber())
+                    .arg(exception.message());
+  qDebug() << m_errorString;
+  return false;
+}
+
+
+bool
+XtandemInputSaxHandler::fatalError(const QXmlParseException &exception)
+{
+  m_errorString = QObject::tr(
+                    "Parse error at line %1, column %2 :\n"
+                    "%3")
+                    .arg(exception.lineNumber())
+                    .arg(exception.columnNumber())
+                    .arg(exception.message());
+  qDebug() << m_errorString;
+  return false;
+}
+
+QString
+XtandemInputSaxHandler::errorString() const
+{
+  return m_errorString;
+}
+
+
+bool
+XtandemInputSaxHandler::startElement_note(QXmlAttributes attributes)
+{
+  // qDebug() << "XtandemParamSaxHandler::startElement_note begin " <<
+  // <note type="input"
+  // label="output,path">/gorgone/pappso/jouy/users/Celine/2019_Lumos/20191222_107_Juste_APD/metapappso_condor/test_run/20191222_18_EF1_third_step_test_condor_22janv.xml</note>
+
+  writeOpenTag("note", attributes);
+  m_currentLabel = "";
+
+  if(attributes.value("type") == "input")
+    {
+      m_currentLabel = attributes.value("label");
+    }
+
+  //  qDebug() << "XtandemParamSaxHandler::startElement_note _current_label " <<
+  //  _current_label;
+  return true;
+}
+
+bool
+XtandemInputSaxHandler::endElement_note()
+{
+  //    qDebug() << "XtandemParamSaxHandler::endElement_note begin " <<
+  //    _current_label << " " << _current_text.simplified();
+  if(m_currentLabel == "output, path")
+    {
+      m_originTandemOutpuFileName = m_currentText;
+      p_writeXmlTandemInput->writeCharacters(m_destinationTandemOutputFileName);
+    }
+  else if(m_currentLabel == "spectrum, path")
+    {
+      //<note type="input"
+      // label="spectrum,path">/gorgone/pappso/jouy/raw/2019_Lumos/20191222_107_Juste/20191222_18_EF1.mzXML</note>
+      m_originMzDataFileName = m_currentText;
+      p_writeXmlTandemInput->writeCharacters(m_destinationMzXmlFileName);
+    }
+  else
+    {
+      p_writeXmlTandemInput->writeCharacters(m_currentText);
+    }
+  p_writeXmlTandemInput->writeEndElement();
+  return true;
+}
+
+
+const QString &
+XtandemInputSaxHandler::getOriginalMsDataFileName() const
+{
+  return m_originMzDataFileName;
+}
+
+const QString &
+XtandemInputSaxHandler::getOriginalTandemOutputFileName() const
+{
+  return m_originTandemOutpuFileName;
+}
diff --git a/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.h b/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..697f3296f31f9d7942016dda28a947dee252fd52
--- /dev/null
+++ b/src/pappsomspp/processing/tandem/tandemwrapperlib/xtandeminputsaxhandler.h
@@ -0,0 +1,95 @@
+/**
+ * \file lib/xtandeminputsaxhandler.h
+ * \date 25/01/2020
+ * \author Olivier Langella
+ * \brief rewrites tandem xml input file with temporary files
+ */
+
+/*******************************************************************************
+ * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
+ *
+ * This file is part of PAPPSOms-tools.
+ *
+ *     PAPPSOms-tools 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.
+ *
+ *     PAPPSOms-tools 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 PAPPSOms-tools.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <QDebug>
+#include <QXmlDefaultHandler>
+#include <QXmlStreamWriter>
+/**
+ * @todo write docs
+ */
+class XtandemInputSaxHandler : public QXmlDefaultHandler
+{
+  public:
+  /**
+   * Default constructor
+   */
+  XtandemInputSaxHandler(const QString &destinationMzXmlFile,
+                         const QString &destinationTandemInputFile,
+                         const QString &destinationTandemOutputFile);
+
+  /**
+   * Destructor
+   */
+  ~XtandemInputSaxHandler();
+
+
+  bool startElement(const QString &namespaceURI,
+                    const QString &localName,
+                    const QString &qName,
+                    const QXmlAttributes &attributes) override;
+
+  bool endElement(const QString &namespaceURI,
+                  const QString &localName,
+                  const QString &qName) override;
+
+  bool startDocument() override;
+
+  bool endDocument() override;
+
+  bool characters(const QString &str) override;
+
+  bool fatalError(const QXmlParseException &exception) override;
+  bool error(const QXmlParseException &exception) override;
+
+  QString errorString() const;
+
+  const QString &getOriginalMsDataFileName() const;
+  const QString &getOriginalTandemOutputFileName() const;
+
+  private:
+  bool startElement_note(QXmlAttributes attributes);
+  bool endElement_note();
+  void writeOpenTag(const QString &qName, const QXmlAttributes &attributes);
+
+
+  private:
+  QString m_destinationMzXmlFileName;
+  QString m_originMzDataFileName;
+  QString m_destinationTandemInputFileName;
+  QString m_originTandemOutpuFileName;
+  QString m_destinationTandemOutputFileName;
+  QFile m_destinationTandemInputFile;
+  QXmlStreamWriter *p_writeXmlTandemInput;
+  std::vector<QString> m_tagStack;
+  bool m_isTandemParameter = false;
+  QString m_errorString;
+  QString m_currentText;
+  QString m_currentLabel;
+  QString m_labelName;
+};