Index: trunk/BNC/src/bnccore.cpp
===================================================================
--- trunk/BNC/src/bnccore.cpp	(revision 11075)
+++ trunk/BNC/src/bnccore.cpp	(revision 11136)
@@ -76,13 +76,6 @@
   // Eph file(s)
   // -----------
-  _rinexVers        = 0;
-  _ephFileGPS       = 0;
-  _ephStreamGPS     = 0;
-  _ephFileGlonass   = 0;
-  _ephStreamGlonass = 0;
-  _ephFileGalileo   = 0;
-  _ephStreamGalileo = 0;
-  _ephFileSBAS      = 0;
-  _ephStreamSBAS    = 0;
+  _rinexVers         = 0;
+  _ephFilePerStation = false;
 
   _portEph    = 0;
@@ -129,15 +122,9 @@
   delete _logStream; _logStream = 0;
   delete _logFile;   _logFile   = 0;
-  delete _ephStreamGPS;
-  delete _ephFileGPS;
+  qDeleteAll(_ephFiles);
   delete _serverEph;
   delete _socketsEph;
   delete _serverCorr;
   delete _socketsCorr;
-  if (_rinexVers == 2) {
-    delete _ephStreamGlonass;
-    delete _ephFileGlonass;
-  }
-
   delete _dateAndTimeGPS;
   if (_rawFile) {
@@ -230,6 +217,14 @@
   }
 #endif
-  printEphHeader(eph->receptStaID());
-  printEph(*eph, bool(ircPut == success));
+  t_ephFile* ephFile = printEphHeader(eph->receptStaID());
+  bool printFile = (ircPut == success);
+  if (ephFile && ephFile->_ephUser) {
+    // file per stream: new ephemerides of this stream, independent of other streams;
+    // the check state of the emitted ephemeris stays that of the common check
+    t_eph::e_checkState checkState = eph->checkState();
+    printFile = (ephFile->_ephUser->putNewEph(eph, true) == success);
+    eph->setCheckState(checkState);
+  }
+  printEph(ephFile, *eph, printFile);
   return success;
 }
@@ -275,12 +270,71 @@
 }
 
-// Print Header of the output File(s)
-////////////////////////////////////////////////////////////////////////////
-void t_bncCore::printEphHeader(QString receptStaID) {
+// Close RINEX Navigation File(s)
+////////////////////////////////////////////////////////////////////////////
+void t_bncCore::t_ephFile::close() {
+  delete _streamN; _streamN = 0;
+  delete _fileN;   _fileN   = 0;
+  delete _streamG; _streamG = 0;
+  delete _fileG;   _fileG   = 0;
+}
+
+// Destructor
+////////////////////////////////////////////////////////////////////////////
+t_bncCore::t_ephFile::~t_ephFile() {
+  close();
+  delete _ephUser;
+}
+
+// Station part of a RINEX navigation file name: "BRDC" (+ "00WRD" in RINEX 3/4)
+// for the single file, otherwise derived from the stream like the observation files
+////////////////////////////////////////////////////////////////////////////
+QString t_bncCore::ephFileNameBase(const QString& staID, const QString& country) const {
+  QString ID = staID.isEmpty() ? QString("BRDC") : staID.left(4).toUpper();
+  if (_rinexVers <= 2) {
+    return ID;
+  }
+  if (staID.size() >= 9 &&
+      isdigit((unsigned char) staID[4].toLatin1()) &&
+      isdigit((unsigned char) staID[5].toLatin1())) {
+    return staID.left(9).toUpper();   // e.g. BCEP00BKG0 -> BCEP00BKG
+  }
+  return ID + "00" + ((!staID.isEmpty() && country.size() >= 3) ? country.left(3).toUpper()
+                                                                : QString("WRD")); // WORLD
+}
+
+// Print Header of the output File(s), returns the file of the stream
+////////////////////////////////////////////////////////////////////////////
+t_bncCore::t_ephFile* t_bncCore::printEphHeader(QString receptStaID) {
 
   bncSettings settings;
+
+  // Initialization
+  // --------------
+  if (_rinexVers == 0) {
+
+    _rinexVers = settings.value("ephVersion").toInt();
+
+    _ephFilePerStation = Qt::CheckState(settings.value("ephFilePerStation").toInt()) == Qt::Checked;
+
+    _ephPath = settings.value("ephPath").toString();
+
+    if ( !_ephPath.isEmpty() ) {
+      if ( _ephPath[_ephPath.length()-1] != QDir::separator() ) {
+        _ephPath += QDir::separator();
+      }
+      expandEnvVar(_ephPath);
+    }
+  }
+
+  if (_ephPath.isEmpty()) {
+    return 0;
+  }
+
+  QString key = _ephFilePerStation ? receptStaID : QString();
+
+  // Source comments and station part of the file name
+  // --------------------------------------------------
   QStringList comments;
-  bool separateFiles = false;// Qt::CheckState(settings.value("ephFilePerStation").toInt());
-
+  QMap<QString, QString> nameBase;  // staID -> station part of its file name
   QListIterator<QString> it(settings.value("mountPoints").toStringList());
   while (it.hasNext()) {
@@ -290,24 +344,32 @@
     QUrl url(hlp[0]);
     QString decoder = hlp[1];
-    if (!separateFiles) {
+    QString staID   = url.path().mid(1);
+    if (key.isEmpty() || staID == key) {
       comments.append("Source: " + decoder +
                       " " + QUrl::toAce(url.host()) +
                       "/" + url.path().mid(1).toLatin1());
     }
-  }
-
-  // Initialization
-  // --------------
-  if (_rinexVers == 0) {
-
-    _rinexVers = settings.value("ephVersion").toInt();
-
-    _ephPath = settings.value("ephPath").toString();
-
-    if ( !_ephPath.isEmpty() ) {
-      if ( _ephPath[_ephPath.length()-1] != QDir::separator() ) {
-        _ephPath += QDir::separator();
-      }
-      expandEnvVar(_ephPath);
+    nameBase[staID] = ephFileNameBase(staID, hlp[2]);
+  }
+
+  QString base = (!key.isEmpty() && nameBase.contains(key)) ? nameBase[key]
+                                                           : ephFileNameBase(key, QString());
+  QString distStr;
+  if (!key.isEmpty()) {
+    QMapIterator<QString, QString> im(nameBase);
+    while (im.hasNext()) {
+      im.next();
+      if (im.key() != key && im.value() == base) { // name conflict with another stream
+        distStr = "_" + key;
+        break;
+      }
+    }
+  }
+
+  t_ephFile*& ephFile = _ephFiles[key];
+  if (!ephFile) {
+    ephFile = new t_ephFile;
+    if (_ephFilePerStation) {
+      ephFile->_ephUser = new bncEphUser(false);
     }
   }
@@ -315,180 +377,153 @@
   // (Re-)Open output File(s)
   // ------------------------
-  if (!_ephPath.isEmpty()) {
-
-    QDateTime datTim = currentDateAndTimeGPS();
-
-    QString ephFileNameGPS = _ephPath; // + "BRDC";
-
-    (separateFiles) ?  ephFileNameGPS += receptStaID.left(4) :
-                       ephFileNameGPS += "BRDC";
-
-    QString hlpStr = bncRinex::nextEpochStr(datTim,
-                         settings.value("ephIntr").toString(), _rinexVers);
-
-    if (_rinexVers > 2) {
-      QString country = "WRD"; // WORLD
-      QString monNum = "0";
-      QString recNum = "0";
-      if (separateFiles && receptStaID.size() >= 9) {
-        monNum = receptStaID.mid(4,1);
-        recNum = receptStaID.mid(5,1);
-        country = receptStaID.mid(6,3);
-      }
-      ephFileNameGPS += QString("%1").arg(monNum, 1, 10) +
-                        QString("%1").arg(recNum, 1, 10) +
-                        country +
-                        "_S_" +     // stream
-                        QString("%1").arg(datTim.date().year()) +
-                        QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
-                        hlpStr +   // HM_period
-                        "_MN.rnx"; // mixed BRDC
-    }
-    else { // RNX v2.11
-      ephFileNameGPS += QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
-                        hlpStr + datTim.toString(".yyN");
-    }
-
-    if (_ephFileNameGPS == ephFileNameGPS) {
-      return;
-    }
-    else {
-      _ephFileNameGPS = ephFileNameGPS;
-    }
-
-    delete _ephStreamGPS;
-    delete _ephFileGPS;
-
-    QFlags<QIODevice::OpenModeFlag> appendFlagGPS;
-    QFlags<QIODevice::OpenModeFlag> appendFlagGlonass;
+  QDateTime datTim = currentDateAndTimeGPS();
+
+  QString hlpStr = bncRinex::nextEpochStr(datTim,
+                       settings.value("ephIntr").toString(), _rinexVers);
+
+  QString ephFileNameGPS = _ephPath + base;
+
+  if (_rinexVers > 2) {
+    ephFileNameGPS += "_S_" +     // stream
+                      QString("%1").arg(datTim.date().year()) +
+                      QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
+                      hlpStr +   // HM_period
+                      "_MN" +    // mixed BRDC
+                      distStr + ".rnx";
+  }
+  else { // RNX v2.11
+    ephFileNameGPS += QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
+                      hlpStr + distStr + datTim.toString(".yyN");
+  }
+
+  if (ephFile->_name == ephFileNameGPS) {
+    return ephFile;
+  }
+  ephFile->_name = ephFileNameGPS;
+  ephFile->close();
+
+  QFlags<QIODevice::OpenModeFlag> appendFlagGPS;
+  QFlags<QIODevice::OpenModeFlag> appendFlagGlonass;
+
+  if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked &&
+       QFile::exists(ephFileNameGPS) ) {
+    appendFlagGPS = QIODevice::Append;
+  }
+
+  ephFile->_fileN = new QFile(ephFileNameGPS);
+  ephFile->_fileN->open(QIODevice::WriteOnly | appendFlagGPS);
+  ephFile->_streamN = new QTextStream();
+  ephFile->_streamN->setDevice(ephFile->_fileN);
+  QTextStream* streamGPS = ephFile->_streamN;
+
+  if (_rinexVers == 2) {
+    QString ephFileNameGlonass = _ephPath + base +
+        QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
+        hlpStr + distStr + datTim.toString(".yyG");
 
     if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked &&
-         QFile::exists(ephFileNameGPS) ) {
-      appendFlagGPS = QIODevice::Append;
-    }
-
-    _ephFileGPS = new QFile(ephFileNameGPS);
-    _ephFileGPS->open(QIODevice::WriteOnly | appendFlagGPS);
-    _ephStreamGPS = new QTextStream();
-    _ephStreamGPS->setDevice(_ephFileGPS);
-
-    if      (_rinexVers > 2) {
-      _ephFileGlonass   = _ephFileGPS;
-      _ephStreamGlonass = _ephStreamGPS;
-      _ephFileGalileo   = _ephFileGPS;
-      _ephStreamGalileo = _ephStreamGPS;
-      _ephFileSBAS      = _ephFileGPS;
-      _ephStreamSBAS    = _ephStreamGPS;
-    }
-    else {
-      QString ephFileNameGlonass = _ephPath + "BRDC" +
-          QString("%1").arg(datTim.date().dayOfYear(), 3, 10, QChar('0')) +
-          hlpStr + datTim.toString(".yyG");
-
-      delete _ephStreamGlonass;
-      delete _ephFileGlonass;
-
-      if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked &&
-           QFile::exists(ephFileNameGlonass) ) {
-        appendFlagGlonass = QIODevice::Append;
-      }
-
-      _ephFileGlonass = new QFile(ephFileNameGlonass);
-      _ephFileGlonass->open(QIODevice::WriteOnly | appendFlagGlonass);
-      _ephStreamGlonass = new QTextStream();
-      _ephStreamGlonass->setDevice(_ephFileGlonass);
-    }
-
-    // Header - RINEX Version 3
-    // ------------------------
-    if (_rinexVers > 2) {
-      if ( ! (appendFlagGPS & QIODevice::Append)) {
-        double rinexVersion = defaultRnxNavVersion4;
-        if (_rinexVers < 4.0) {
-          rinexVersion = defaultRnxNavVersion3;
-        }
-        QString line = QString().asprintf("%9.2f%11sN: GNSS NAV DATA    M: Mixed%12sRINEX VERSION / TYPE\n",
-            rinexVersion, "", "");
-        *_ephStreamGPS << line;
-        line.clear();
-
-        QString hlp = currentDateAndTimeGPS().toString("yyyyMMdd hhmmss UTC").leftJustified(20, ' ', true);
-        *_ephStreamGPS << _pgmName.toLatin1().data()
-                       << _userName.toLatin1().data()
-                       << hlp.toLatin1().data()
-                       << "PGM / RUN BY / DATE\n";
-
-        QStringListIterator it(comments);
-        while (it.hasNext()) {
-          *_ephStreamGPS << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
-        }
-
-        if (_rinexVers == 4) {
-          int leapSecs = gnumleap(datTim.date().year(), datTim.date().month(), datTim.date().day());
-          *_ephStreamGPS << QString("%1").arg(leapSecs, 6, 10, QLatin1Char(' ')).left(60).leftJustified(60)
-                  << "LEAP SECONDS\n";
-        }
-
-        line = QString().asprintf("%60sEND OF HEADER\n", "");
-        *_ephStreamGPS << line;
-
-        _ephStreamGPS->flush();
-      }
-    }
-
-    // Headers - RINEX Version 2
-    // -------------------------
-    else {
-      if (! (appendFlagGPS & QIODevice::Append)) {
-        QString line = QString().asprintf("%9.2f%11sN: GPS NAV DATA%25sRINEX VERSION / TYPE\n",
-                     defaultRnxNavVersion2, "", "");
-        *_ephStreamGPS << line;
-        line.clear();
-
-        QString hlp = currentDateAndTimeGPS().date().toString("dd-MMM-yyyy").leftJustified(20, ' ', true);
-        *_ephStreamGPS << _pgmName.toLatin1().data()
-                       << _userName.toLatin1().data()
-                       << hlp.toLatin1().data()
-                       << "PGM / RUN BY / DATE\n";
-
-        QStringListIterator it(comments);
-        while (it.hasNext()) {
-          *_ephStreamGPS << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
-        }
-
-        line = QString().asprintf("%60sEND OF HEADER\n", "");
-        *_ephStreamGPS << line;
-
-        _ephStreamGPS->flush();
-      }
-      if (! (appendFlagGlonass & QIODevice::Append)) {
-        QString line = QString().asprintf("%9.2f%11sG: GLONASS NAV DATA%21sRINEX VERSION / TYPE\n",
-                     defaultRnxNavVersion2, "", "");
-        *_ephStreamGlonass << line;
-        line.clear();
-
-        QString hlp = currentDateAndTimeGPS().date().toString("dd-MMM-yyyy").leftJustified(20, ' ', true);
-        *_ephStreamGlonass << _pgmName.toLatin1().data()
-                           << _userName.toLatin1().data()
-                           << hlp.toLatin1().data()
-                           << "PGM / RUN BY / DATE\n";
-
-        QStringListIterator it(comments);
-        while (it.hasNext()) {
-          *_ephStreamGlonass << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
-        }
-
-        line = QString().asprintf("%60sEND OF HEADER\n", "");
-        *_ephStreamGlonass << line;
-
-        _ephStreamGlonass->flush();
-      }
-    }
-  }
+         QFile::exists(ephFileNameGlonass) ) {
+      appendFlagGlonass = QIODevice::Append;
+    }
+
+    ephFile->_fileG = new QFile(ephFileNameGlonass);
+    ephFile->_fileG->open(QIODevice::WriteOnly | appendFlagGlonass);
+    ephFile->_streamG = new QTextStream();
+    ephFile->_streamG->setDevice(ephFile->_fileG);
+  }
+  QTextStream* streamGlonass = ephFile->_streamG;
+
+  // Header - RINEX Version 3
+  // ------------------------
+  if (_rinexVers > 2) {
+    if ( ! (appendFlagGPS & QIODevice::Append)) {
+      double rinexVersion = defaultRnxNavVersion4;
+      if (_rinexVers < 4.0) {
+        rinexVersion = defaultRnxNavVersion3;
+      }
+      QString line = QString().asprintf("%9.2f%11sN: GNSS NAV DATA    M: Mixed%12sRINEX VERSION / TYPE\n",
+          rinexVersion, "", "");
+      *streamGPS << line;
+      line.clear();
+
+      QString hlp = currentDateAndTimeGPS().toString("yyyyMMdd hhmmss UTC").leftJustified(20, ' ', true);
+      *streamGPS << _pgmName.toLatin1().data()
+                 << _userName.toLatin1().data()
+                 << hlp.toLatin1().data()
+                 << "PGM / RUN BY / DATE\n";
+
+      QStringListIterator it(comments);
+      while (it.hasNext()) {
+        *streamGPS << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
+      }
+
+      if (_rinexVers == 4) {
+        int leapSecs = gnumleap(datTim.date().year(), datTim.date().month(), datTim.date().day());
+        *streamGPS << QString("%1").arg(leapSecs, 6, 10, QLatin1Char(' ')).left(60).leftJustified(60)
+                   << "LEAP SECONDS\n";
+      }
+
+      line = QString().asprintf("%60sEND OF HEADER\n", "");
+      *streamGPS << line;
+
+      streamGPS->flush();
+    }
+  }
+
+  // Headers - RINEX Version 2
+  // -------------------------
+  else {
+    if (! (appendFlagGPS & QIODevice::Append)) {
+      QString line = QString().asprintf("%9.2f%11sN: GPS NAV DATA%25sRINEX VERSION / TYPE\n",
+                   defaultRnxNavVersion2, "", "");
+      *streamGPS << line;
+      line.clear();
+
+      QString hlp = currentDateAndTimeGPS().date().toString("dd-MMM-yyyy").leftJustified(20, ' ', true);
+      *streamGPS << _pgmName.toLatin1().data()
+                 << _userName.toLatin1().data()
+                 << hlp.toLatin1().data()
+                 << "PGM / RUN BY / DATE\n";
+
+      QStringListIterator it(comments);
+      while (it.hasNext()) {
+        *streamGPS << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
+      }
+
+      line = QString().asprintf("%60sEND OF HEADER\n", "");
+      *streamGPS << line;
+
+      streamGPS->flush();
+    }
+    if (! (appendFlagGlonass & QIODevice::Append)) {
+      QString line = QString().asprintf("%9.2f%11sG: GLONASS NAV DATA%21sRINEX VERSION / TYPE\n",
+                   defaultRnxNavVersion2, "", "");
+      *streamGlonass << line;
+      line.clear();
+
+      QString hlp = currentDateAndTimeGPS().date().toString("dd-MMM-yyyy").leftJustified(20, ' ', true);
+      *streamGlonass << _pgmName.toLatin1().data()
+                     << _userName.toLatin1().data()
+                     << hlp.toLatin1().data()
+                     << "PGM / RUN BY / DATE\n";
+
+      QStringListIterator it(comments);
+      while (it.hasNext()) {
+        *streamGlonass << it.next().trimmed().left(60).leftJustified(60) << "COMMENT\n";
+      }
+
+      line = QString().asprintf("%60sEND OF HEADER\n", "");
+      *streamGlonass << line;
+
+      streamGlonass->flush();
+    }
+  }
+
+  return ephFile;
 }
 
 // Print One Ephemeris
 ////////////////////////////////////////////////////////////////////////////
-void t_bncCore::printEph(const t_eph& eph, bool printFile) {
+void t_bncCore::printEph(t_ephFile* ephFile, const t_eph& eph, bool printFile) {
 
   QString strV2 = eph.toString(defaultRnxNavVersion2);
@@ -496,12 +531,15 @@
   QString strV4 = eph.toString(defaultRnxNavVersion4);
 
+  QTextStream* streamN = ephFile ? ephFile->_streamN : 0;
+  QTextStream* streamG = ephFile ? ephFile->_streamG : 0;
+
   if      (_rinexVers == 2 && eph.system() == t_eph::GLONASS) {
-    printOutputEph(printFile, _ephStreamGlonass, strV2, strV3, strV4);
+    printOutputEph(printFile, streamG, strV2, strV3, strV4);
   }
   else if (_rinexVers == 2 && eph.system() == t_eph::GPS) {
-    printOutputEph(printFile, _ephStreamGPS, strV2, strV3, strV4);
+    printOutputEph(printFile, streamN, strV2, strV3, strV4);
   }
   else if (_rinexVers >= 3) {
-    printOutputEph(printFile, _ephStreamGPS, strV2, strV3, strV4);
+    printOutputEph(printFile, streamN, strV2, strV3, strV4);
   }
 }
