Index: trunk/BNC/CHANGELOG.md
===================================================================
--- trunk/BNC/CHANGELOG.md	(revision 10977)
+++ trunk/BNC/CHANGELOG.md	(revision 10979)
@@ -8,4 +8,5 @@
 Galileo / BDS orbit-normal mode switching (Kouba 2017, Dai et al. 2015, Steigenberger et al. 2018). (2) Nominal: a simplified, continuous Sun-pointing model is used without any manoeuvre modelling. (3) SSR: the yaw angle transmitted in the SSR phase bias message is used directly, if present for the satellite and epoch.  [(#210)](https://software.rtcm-ntrip.org/ticket/210)
 - FIXED: BNC is now able to work with com ports with higher numbers, e.g. COM11 or COM17  [(#205)](https://software.rtcm-ntrip.org/ticket/205)
+- ADDED: RINEX version 2 files can be written now from RINEX version 3/4 skl files too. But not vice versa.
 - FIXED: Handling of ionospheric constraints [(#220)](https://software.rtcm-ntrip.org/ticket/220)
 - FIXED: SP3/Clock RINEX clock values produced by the Upload Corrections feature no longer depend on whether 'Center of Mass' is ticked, since that setting should only affect the uploaded SSR Broadcast Correction stream [(#210)](https://software.rtcm-ntrip.org/ticket/210)
Index: trunk/BNC/src/bnchelp.html
===================================================================
--- trunk/BNC/src/bnchelp.html	(revision 10977)
+++ trunk/BNC/src/bnchelp.html	(revision 10979)
@@ -1691,4 +1691,12 @@
 <p>Note that a skeleton file contains RINEX header information such as receiver and antenna types. In case of stream conversion to RINEX Version 3, a skeleton file should also contain information on potentially available observation types. A missing skeleton file will force BNC to only save a default set of RINEX 3 observation types.
 </p>
+<p>
+A skeleton file carrying only RINEX Version 2 style observation types (2-character codes, e.g. 'C1', 'L1') cannot supply the tracking-mode
+attribute a RINEX Version 3/4 header needs, so BNC will not use such a skeleton when producing Version 3/4 files. It is treated the same
+way as a missing skeleton: with 'Skeleton mandatory' ticked, no RINEX file is produced for the affected stream; otherwise BNC falls back
+to a default set of RINEX 3/4 observation types. Conversely, a skeleton carrying RINEX Version 3/4 style observation types
+(3-character codes) is always usable when producing RINEX Version 2 files, because the Version 3/4 codes can be unambiguously mapped
+down to Version 2 codes (see section 'Version 2').
+</p>
 
 <p><h4 id="sklDir">2.4.7 Skeleton Directory - optional</h4></p>
@@ -1718,4 +1726,9 @@
 However, this is not recommended, because such stream cannot be precisely mapped to RINEX Version 3
 as the required information on tracking modes (observation attributes) is not part of RTCM Version 2.
+</p>
+<p>
+For the same reason, a RINEX header skeleton file that only carries RINEX Version 2 style observation types
+(2-character codes) cannot be used to build a RINEX Version 3/4 header. BNC ignores such a skeleton for that
+purpose and falls back to a default set of RINEX 3/4 observation types instead, see section 'Skeleton Mandatory'.
 </p>
 
Index: trunk/BNC/src/bncrinex.cpp
===================================================================
--- trunk/BNC/src/bncrinex.cpp	(revision 10977)
+++ trunk/BNC/src/bncrinex.cpp	(revision 10979)
@@ -447,5 +447,26 @@
   // Read Skeleton Header
   // --------------------
-  if (readSkeleton()) {
+  bool sklUsable = readSkeleton();
+  if (sklUsable && intHeaderVers >= 3) {
+    // A skeleton carrying only 2-char (v2-style) observation codes cannot
+    // supply the attribute-specific codes a v3 file needs. Judge this from
+    // the actual codes rather than _sklHeader.version(), which is often
+    // left at 0.0 for skeleton files that omit "RINEX VERSION / TYPE".
+    bool haveV3Types = false;
+    for (int iSys = 0; iSys < _sklHeader.numSys() && !haveV3Types; iSys++) {
+      char sys = _sklHeader.system(iSys);
+      QStringList types = _sklHeader.obsTypes(sys);
+      for (int iType = 0; iType < types.size(); iType++) {
+        if (types.at(iType).length() >= 3) {
+          haveV3Types = true;
+          break;
+        }
+      }
+    }
+    if (!haveV3Types) {
+      sklUsable = false;
+    }
+  }
+  if (sklUsable) {
     _header.set(_sklHeader, intHeaderVers);
   }
Index: trunk/BNC/src/rinex/rnxobsfile.cpp
===================================================================
--- trunk/BNC/src/rinex/rnxobsfile.cpp	(revision 10977)
+++ trunk/BNC/src/rinex/rnxobsfile.cpp	(revision 10979)
@@ -444,5 +444,21 @@
   if (!useObsTypes || useObsTypes->size() == 0) {
     if (int(_version) == int(header._version)) {
-      _obsTypes = header._obsTypes;
+      if (_version < 3.0) {
+        // Sanitize: a source header labeled v2 could still carry malformed
+        // (e.g. leftover 3-char) codes; always force them through type3to2.
+        QMap<char, QStringList>::const_iterator it = header._obsTypes.constBegin();
+        for ( ; it != header._obsTypes.constEnd(); ++it) {
+          char sys = it.key();
+          for (int iType = 0; iType < it.value().size(); iType++) {
+            QString conv = t_rnxObsFile::type3to2(sys, it.value().at(iType));
+            if (!_obsTypes[sys].contains(conv)) {
+              _obsTypes[sys].push_back(conv);
+            }
+          }
+        }
+      }
+      else {
+        _obsTypes = header._obsTypes;
+      }
     }
     else {
