! --------------------------------------------------------------------------
! Bernese GPS Software Version 5.3
! --------------------------------------------------------------------------

MODULE o_rxoWrap

! --------------------------------------------------------------------------
! Purpose:    Allow legacy code to fully use RINEX 3 format.
!
!             This adapter routines emulate the interface of subroutines
!             r2RdOh, r2RdOr, r2WtOh, r2WtOr as close as reasonable
!             while using the new RINEX modules.
! --------------------------------------------------------------------------
!
! Author:     P. Stebler
!
! Created:    06-Jun-2019
!
! Changes:    06-Jun-2019 ST: Extract code from legacy r2RdOh of W. Gurtner
!             08-Jun-2020 ST: Add USE for constr and destr.
!             12-Jun-2020 ST: Continue data extraction.
!             09-Jul-2020 ST: Add wrtFromR2RdOh for debugging.
!             10-Jul-2020 ST: Add rxoBaseConf as dummy variable to wrapR2RdOh.
!                             Add changeActionOnEpoEvt and changeReqSys
!                             to o_rxoWrap.
!             28-Jul-2020 ST: Also port RINEX3 part of old code.
!             29-Jul-2020 ST: Continue and refactor.
!                             Handle indxs.
!             19-Aug-2020 ST: Extract code from legacy r2RdOr of W. Gurtner
!             21-Aug-2020 ST: Continue and refactor.
!             24-Aug-2020 ST: Continue and refactor.
!             27-Aug-2020 ST: Bugfix: t_nObsPerPrnAndTypeHash%nObss is the
!                             wrong place to search for the observation
!                             statistics from the RINEX header.
!                             Add and use t_obsTypeDefs::getNbObs for this.
!             04-Sep-2020 ST: Add extractFromHdr.
!             05-Sep-2020 ST: Extract setActionsOnEpoEvt from RXOBV3.f.
!             06-Oct-2020 ST: Add processRxoWithObsSel to o_rxoWrap.
!                             Add updHdrAndStatWithFullInpContent.
!             08-Oct-2020 ST: Add deeper diagnostics for
!                             number of observation types.
!                             Restrict number of observation types to 4 only
!                             if gObsDef is active for the wrapper.
!                             Add optional dummy variable allowSilentFail
!                             to o_rxoBase::convertObsTypeR3ToR2.
!             14-Oct-2020 ST: Add more description to weirdConcatParse.
!             15-Oct-2020 ST: Add setReqSysGnss to o_rxoWrap.
!                             Add setDefaultRxoBaseConfValues to o_rxoWrap.
!                             Bugfix prn range for t_sysStat%sat(prn) access.
!             16-Oct-2020 ST: Remove RINEX header comment lines from wrapped
!                             interface. It's simpler to use them directly
!                             from the new data structure in the calling software
!                             than to depend on and play around with maxCom.
!                             Bugfix: properly initialize rinStat
!                               in collectHdrStatistics.
!                             Bugfix sysPrnStr in collectObsStatistics.
!                             Bugfix: The definition of the datetime format
!                               in the PGM / RUN BY / DATE header entry is
!                               different for RINEX version 2 and 3.
!             19-Oct-2020 ST: Convert various header entries to upper case.
!             21-Oct-2020 ST: Rename t_rxoRdConf to t_rxoRdState.
!                             Split t_rxoBaseConf to t_rxoRdConfStatic
!                             and t_rxoWrtConf.
!                             Add t_rxoRdState::applyPhaseShiftCorrsOnRd.
!             22-Oct-2020 ST: Add prepareRxoOutHdlr, prepareRxoObsAtEpo
!                             and wrapR2WtOr.
!             23-Oct-2020 ST: Allow for alphabetical GNSS char order
!                             when writing RINEX observation records.
!                             Bugfix extract RINEX version in extractHdrInfo.
!             26-Oct-2020 ST: Split t_rxoRdConfStatic to t_rxoRdHdrConf
!                             and t_rxoRdRecConf.
!                             Split setDefaultRxoBaseConfValues
!                             to setDefaultRxoRdHdrConfValues
!                             and setDefaultRxoRdRecConfValues.
!             28-Oct-2020 ST: Remove no more needed routines from o_rxoWrap:
!                               changeActionOnEpoEvt, changeReqSys,
!                               setActionsOnEpoEvt, setDefaultRxoRdHdrConfValues,
!                               setDefaultRxoRdRecConfValues, setReqSysGnss
!                             and wrapR2RdOh.
!                             Make collectHdrStatistics
!                             and translateRdHdrRetCode public.
!                             Rename wrtFromR2RdOh to wrtExtractFromHdrData.
!                             Rename wrtFromR2RdOr to wrtWrapR2RdOrData.
!                             Subsitute RINEX version integers by t_rnxFmtVers.
!                             Call d_rxoType::toUpperCase outside instead
!                             of upperC in extractFromHdr.
!             29-Oct-2020 ST: Split extractHdrInfo to extractPosEccsFromHdr
!                             and  extractWlfsFromHdr.
!                             Rename extractFromHdr to extractObsTypesFromHdr.
!                             Split prepareRxoOutHdlr to setWlfsInHdr,
!                             setObsTypesInHdr and setObsTypesAndNbrInHdr.
!             30-Oct-2020 ST: Further simplification by
!                             using o_rinexO::nextRxoRec
!                             instead of o_rinexO::rdRxoRec in wrapR2RdOr.
!                             Remove no more needed routines from o_rxoWrap:
!                               translateRdHdrRetCode, translateRdRecRetCode.
!                             Expose collectObsStatistics.
!                             Add optional iHdrChanged flag to wrapR2RdOr.
!             04-Nov-2020 ST: Remove no more needed routines from o_rxoWrap:
!                               collectHdrStatistics, collectObsStatistics.
!             06-Nov-2020 ST: Bugfix: call clearObsRecData
!                             in extractObsRecDataWithoutObsDef.
!             11-Nov-2020 ST: Use unskippedNewHdr and newHdrParts flags.
!                             Don't use full t_rxoInpHdlr if not necessairy.
!                             No more handle d_rinex3::indxs
!                             Call setObsTypesAndNbrInHdr grants header comment
!                             for trusted observation statistics.
!             12-Nov-2020 ST: Gather RINEX 3 to RINEX 2 observation conversion
!                             warnings in extractHdrObsInfoWithoutObsDef.
!                             Use trustedStat in processRxoWithObsSel.
!                             Add RINEX file name to warnings and info.
!             16-Nov-2020 ST: To speed up wrapR2RdOr and wrapR2WtOr,
!                             take out constr/destr of t_rxoObsAtEpo,
!                             pass rxoObsAtEpo as dummy variable,
!                             only clear rxoObsAtEpo in the wrap routines.
!             19-Nov-2020 ST: Refactor most prt*ToLfnPrt to wrtOut* to pass lfn.
!             20-Nov-2020 ST: Use of asterisks as format is compiler dependent.
!                             Mainly replaced by '()' and '(1X,A)'.
!             27-Nov-2020 ST: Refactor t_rnxFmtVers to t_rnxVers.
!             07-Dec-2020 ST: Bugfix: obsType has to contain the full list
!                             of original observation types.
!             08-Dec-2020 ST: Rename o_rxoBase::updStatInHdr
!                             to o_rxoBase::cpyStat_obsStatPerSys_infoPerSys.
!                             Generalize o_rxoWrap::processRxoWithObsSel
!                             to o_rinexO::updStatProcessingRxo.
!                             Move updHdrAndStatWithFullInpContent
!                             from o_rxoWrap to o_rinexO.
!             11-Dec-2020 ST: Mute non converted RINEX 3 observation types
!                             message in extractObsTypesFromHdr_merge.
!             30-Jun-2021 ST: Clean use statements.
!                             Removed superfluous maxNbWrapObsTypes at one place.
!             10-Feb-2022 ST: Cosmetics.
!             21-Feb-2022 ST: Move convertObsTypeR2ToR3 and convertObsTypeR3ToR2
!                             from o_rxoBase to d_rinex3.
!                             Move maxObsTypeHash (and rel.), calcObsTypeFromHash
!                             and calcObsTypeHash from d_rxoType to d_rinex3.
!                             Rename isRx2RecvWithC2 to isRx2RecvWithGpsC2.
!             22-Feb-2022 ST: Bugfix non-solvable situations with loss of
!                             observations by making RINEX 2 observation position
!                             independent from RINEX 3 observation position.
!                             Add t_rxoHdr%rx2ObsTypes
!                             and t_obsTypeDefs%rx2PosPerObsTypeHash.
!                             Rename posPerObsTypeHash to rx3PosPerObsTypeHash.
!             12-Mar-2022 ST: Revive non-SMT RINEX version 2 (2.00).
!             04-Apr-2022 ST: Add comments on isSmt.
!             27-Apr-2022 AV/ST: Replace calcObsTypeHash with calcObsTypeHash_internal.
!             22-Aug-2022 MK: Remove unused module
!             20-Nov-2022 ST: Unify name of RINEX observation type control variables.
!             29-Sep-2023 ST: Replace LOGICAL isSmt with enum rxo2OpMode.
!             26-Feb-2025 ST: Do not initialize large, epoch-wise arrays
!                             (wrapR2RdOr and subroutines).
!                             Remove some genData pointers which were not used.
!                             Consider size of svnPerSatAtEpo for maxWrapSat.
!
! Copyright:  Astronomical Institute
!             University of Bern
!             Switzerland
! --------------------------------------------------------------------------

  USE m_bern,     ONLY: i4b, r8b, lfnPrt
  USE m_cpu,      ONLY: cpu_enter, cpu_exit
  USE l_basFun,   ONLY: str
  USE m_epoch,    ONLY: ASSIGNMENT(=), OPERATOR(.epochToReal.)
  USE m_string,   ONLY: OPERATOR(==)
  USE m_strList,  ONLY: t_stringList, &
                        clear, constr, destr, getIdx, nb, push_back, str, &
                        wrtSrErrToLfnErr, wrtSrWarnToLfnErr
  USE m_tools,    ONLY: comprFileNameLen
  USE m_global,   ONLY: maxRnxSys, maxSys, &
                        g_svnSys
  USE m_epoch,    ONLY: t_epoch
  USE d_rinex3,   ONLY: maxObsTypeHash, obsListBasic, &
                        t_gObsDef, t_satSig, t_rxo2OpMode, &
                        calcObsTypeFromHash, calcObsTypeHash_internal, &
                        convertObsTypeR2ToR3, convertObsTypeR3ToR2
  USE d_rnxType,  ONLY: maxRnxPrn, rnxVers_2_00, &
                        t_rnxGenHdrData, t_rnxVers, &
                        str
  USE d_rxoType,  ONLY: t_infoForSys, t_infoPerSys, t_obsTypeDef, &
                        t_obsTypeDefs, t_rxoHdr, t_rxoInpHdlr, t_rxoObsAtEpo, &
                        t_rxoObsOfType, t_rxoObsPerPrn, t_rxoObsPerType, &
                        t_rxoOutHdlr, t_rxoPlainHdrData, t_rxoRdHdrConf, &
                        t_rxoRdRecConf, t_rxoRdState, t_rxoWrtConf, t_wlfs, &
                        appendObsTypeHash, clear, constr, destr, getIdx, &
                        getNbObs, nb, reserve
  USE o_rinexO,   ONLY: nextRxoRec, wrtRxoRec
  USE s_dimTst
  USE s_exitRc

  IMPLICIT NONE

#include "COM_CPU.inc"

  ! Save variables
  SAVE

  ! Access rights
  PRIVATE
  PUBLIC :: extractObsTypesFromHdr, extractPosEccsFromHdr, extractWlfsFromHdr
  PUBLIC :: setObsTypesInHdr, setObsTypesAndNbrInHdr, setWlfsInHdr
  PUBLIC :: wrapR2RdOr, wrapR2WtOr, wrtExtractObsTypesFromHdrData
  PUBLIC :: wrtWrapR2RdOrData

  ! Module parameters
  ! -----------------

  ! Module name
  CHARACTER(LEN=*), PARAMETER :: modNam = 'O_RXOWRAP'

  INTEGER(i4b), PARAMETER :: maxNbWrapObsTypesWithObsDef = 4

CONTAINS

  ! -------------------------------------------------------------------------
  SUBROUTINE extractPosEccsFromHdr(rxoPlainHdrData, posEccs_m)
    ! Plain RINEX input handler data
    TYPE(t_rxoPlainHdrData),   INTENT(IN)    :: rxoPlainHdrData
    ! Positioning eccentricites in local system [m]
    !
    ! If marker type is not "SPACEBORNE":
    !   Antenna bottom plane to marker
    !   posEccs_m(1, 1): Ecc. in latitude
    !   posEccs_m(2, 1): Ecc. in longitude
    !   posEccs_m(3, 1): Ecc. in height
    !
    ! If marker type is "SPACEBORNE":
    !   posEccs_m(1:3, 1): DELTA X/Y/Z
    !   posEccs_m(1:3, 2): BORESIGHT X/Y/Z
    !   posEccs_m(1:3, 3): CENTER OF MASS X/Y/Z
    REAL(r8b), DIMENSION(:,:), INTENT(OUT)   :: posEccs_m
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':extractPosEccsFromHdr'

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    IF (rxoPlainHdrData%antMrkType == 'SPACEBORNE') THEN
      ! posEccs_m(1:3, 1): DELTA X/Y/Z
      ! posEccs_m(1:3, 2): BORESIGHT X/Y/Z
      ! posEccs_m(1:3, 3): CENTER OF MASS X/Y/Z
      posEccs_m(1:3, 1) = rxoPlainHdrData%antRelRpXyz
      posEccs_m(1:3, 2) = rxoPlainHdrData%antBsightXyz
      posEccs_m(1:3, 3) = rxoPlainHdrData%ctrOfMassXyz
    ELSE
      ! Corresponds to the RINEX header record 'ANTENNA: DELTA H/E/N'.
      ! Antenna bottom plane to marker
      ! posEccs_m(1, 1): Ecc. in latitude
      ! posEccs_m(2, 1): Ecc. in longitude
      ! posEccs_m(3, 1): Ecc. in height
      posEccs_m(1, 1) = rxoPlainHdrData%antRelRpHeightEcc%eccNorth
      posEccs_m(2, 1) = rxoPlainHdrData%antRelRpHeightEcc%eccEast
      posEccs_m(3, 1) = rxoPlainHdrData%antRelRpHeightEcc%height

      posEccs_m(1:3, 2:3) = 0d0
    ENDIF

    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractPosEccsFromHdr

  ! -------------------------------------------------------------------------
  SUBROUTINE extractWlfsFromHdr( &
    rxoHdr, defaultWlfs, wlfsPerSat, nWlfsPerSat)
  ! Extracts the header information of a RINEX file.
  !
  ! Relation to the original r2RdOh dummy variables:
  !
  !   old name   new name
  !   --------   --------
  !   IWLFAC     defaultWlfs
  !   IWLSAT     wlfsPerSat
  !   NWLSAT     nWlfsPerSat
  ! -------------------------------------------------------------------------
    ! RINEX header
    TYPE(t_rxoHdr),       TARGET, INTENT(IN)    :: rxoHdr
    ! Default wavelength factors (L1/L2)
    !   defaultWlfs(1): Cycle ambiguities
    !   defaultWlfs(2): Half-cycle ambiguities
    INTEGER(i4b), DIMENSION(2),   INTENT(OUT)   :: defaultWlfs
    ! Non-default wavelength factors (L1/L2) per satellite
    !   wlfsPerSat(1, :) : Cycle ambiguities
    !   wlfsPerSat(2, :) : Half-cycle ambiguities
    !   wlfsPerSat(3, :) : SVN
    !
    ! Additional comment from legacy file:
    !   I=1: WLFAC(L1), I=2: WLFAC(L2), I=3: SVN
    INTEGER(i4b), DIMENSION(:,:), INTENT(OUT)   :: wlfsPerSat
    ! Number of entries (satellites) in wlfsPerSat
    INTEGER(i4b),                 INTENT(OUT)   :: nWlfsPerSat
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':extractWlfsFromHdr'

    ! Local variables
    TYPE(t_rxoPlainHdrData), POINTER :: plainData
    TYPE(t_infoPerSys),      POINTER :: infoPerSys
    TYPE(t_infoForSys),      POINTER :: infoForSys
    TYPE(t_wlfs),            POINTER :: wlfs

    INTEGER(i4b) :: iSys, prn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(infoForSys, wlfs)

    plainData  => rxoHdr%plainData
    infoPerSys => rxoHdr%infoPerSys

    IF (SIZE(wlfsPerSat, 1) < 3) THEN
      CALL wrtSrErrToLfnErr(srName, 'SIZE(wlfsPerSat, 1) < 3')
      CALL exitRc(2)
    ENDIF

    defaultWlfs(1) = plainData%defaultWlfs%l1
    defaultWlfs(2) = plainData%defaultWlfs%l2

    nWlfsPerSat = 0
    wlfsPerSat(:,:) = 0

    DO iSys = 0, maxRnxSys - 1
      infoForSys => infoPerSys%ar(iSys)

      DO prn = 1, maxRnxPrn
        wlfs => infoForSys%infoPerPrn%ar(prn)%wlfs

        IF (wlfs%l1 /= 1 .OR. wlfs%l2 /= 1) THEN
          IF (nWlfsPerSat >= SIZE(wlfsPerSat, 2)) THEN
            CALL wrtSrErrToLfnErr(srName, 'nWlfsPerSat >= SIZE(wlfsPerSat, 2)')
            CALL exitRc(2)
          ENDIF

          nWlfsPerSat = nWlfsPerSat + 1
          wlfsPerSat(1, nWlfsPerSat) = wlfs%l1
          wlfsPerSat(2, nWlfsPerSat) = wlfs%l2
          wlfsPerSat(3, nWlfsPerSat) = iSys * 100 + prn
        ENDIF

      ENDDO
    ENDDO

    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractWlfsFromHdr

  ! -------------------------------------------------------------------------
  SUBROUTINE extractObsTypesFromHdr( &
    inpFileName_compr, rxoHdr, rxo2OpMode, nObsTypes, obsTypes, &
    nSats, svnPerSat, nObsPerSatPerObsType, useObsDefCode, gObsDef)
  ! Extracts the header information of a RINEX file.
  !
  ! For marker type "SPACEBORNE",
  ! special declarations are needed for obsTypes and posEccs_m
  !
  ! Relation to the original r2RdOh dummy variables:
  ! Extracts the header information of a RINEX file.
  !
  ! For marker type "SPACEBORNE",
  ! special declarations are needed for obsTypes and posEccs_m
  !
  ! Relation to the original r2RdOh dummy variables:
  !
  !   old name   in new RINEX 3 structures
  !   --------   -------------------------
  !   PRGNAM     rxoHdr%genData%pgCreatingFile
  !   RUNBY      rxoHdr%genData%agencyCreatingFile
  !   NCOM       rxoHdr%genData%cmtLines
  !   COMENT     rxoHdr%genData%cmtLines
  !   STANAM     rxoHdr%plainData%antMrkName
  !   STANUM     Characters  1:20 : Station/Marker number
  !                rxoHdr%plainData%antMrkNbr
  !              Characters 21:40 : Station/Marker type
  !                rxoHdr%plainData%antMrkType
  !   OPRNAM     rxoHdr%plainData%observerName
  !   AGENCY     rxoHdr%plainData%agencyName
  !   IRUNIT     rxoHdr%genData%rcvNbr
  !   RECTYP     rxoHdr%genData%rcvType
  !              rxoHdr%genData%rcvNbr
  !   RCVERS     rxoHdr%genData%rcvVers
  !   IANTEN     rxoHdr%plainData%antNbr
  !   ANTTYP     rxoHdr%plainData%antType
  !              rxoHdr%plainData%antNbr
  !   POSXYZ     rxoHdr%plainData%approxAntMrkPosXyz(:)
  !   IDELTT     rxoHdr%plainData%obsEpoInfo%interval_s
  !   TFIRST     rxoHdr%plainData%obsEpoInfo%obsWin%t(1)
  !   TLAST      rxoHdr%plainData%obsEpoInfo%obsWin%t(2)
  !   IRXVRS     rxoHdr%genData%rnxVers
  !
  !   old name   new name              remark
  !   --------   --------              ------
  !   MAXSAT     -                     Now derived from sizes of svnPerSat
  !                                      and nObsPerSatPerObsType
  !   NUMTYP     nObsTypes             -
  !   OBSTYP     obsTypes              -
  !   NSATEL     nSats                 -
  !   NUMSAT     svnPerSat             -
  !   NUMOBS     nObsPerSatPerObsType  -
  !   USEGEOS    useObsDefCode         -
  !   GOBSDEF    gObsDef               Independent of the internally used
  !                                    gObsDef of the new RINEX tools!
  ! -------------------------------------------------------------------------
    CHARACTER(LEN=*),                 INTENT(IN)    :: inpFileName_compr
    ! RINEX header
    TYPE(t_rxoHdr),           TARGET, INTENT(IN)    :: rxoHdr
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),               INTENT(IN)    :: rxo2OpMode
    ! Number of (different) observation types
    INTEGER(i4b),                     INTENT(OUT)   :: nObsTypes
    ! List of observation types
    ! If marker type is "SPACEBORNE":
    !   obsTypes(nObsTypes + K) may contain a factor for obsTypes(K)
    !   (2 CHARACTERS HEX DATA).
    !   Therefore, please declarate at least obsTypes(2*nObsTypes)
    !
    ! Very special behavior heritage from original R2RDOH.f:
    !   Even if the observation selection is used,
    !   this one contains the full list of original observation types
    !   and therefore is then not in the same order as
    !   nObsPerSatPerObsType.
    !
    !   For RINEX 3 the observation types of the various GNSS
    !   are somehow merged. This is new behavior to get RINEX 3.
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(OUT)   :: obsTypes
    ! Number of satellites
    INTEGER(i4b),                     INTENT(OUT)   :: nSats
    ! Satellite number (SVN) per satellite
    ! svnPerSat(iSat)
    INTEGER(i4b),     DIMENSION(:),   INTENT(OUT)   :: svnPerSat
    ! Number of observations per satellite and per observation type
    ! nObsPerSatPerObsType(iSat, iWrapObsType)
    !
    ! If observation selection is used, the second index goes from 1 to 4.
    INTEGER(i4b),     DIMENSION(:,:), INTENT(OUT)   :: nObsPerSatPerObsType
    ! gObsDef is used only if useObsDefCode == 1
    INTEGER(i4b),           OPTIONAL, INTENT(IN)    :: useObsDefCode
    ! Observation selection
    TYPE(t_gObsDef),        OPTIONAL, INTENT(INOUT) :: gObsDef
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':extractObsTypesFromHdr'

    ! Local variables
    INTEGER(i4b) :: irc, maxNbSat

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    CALL dimTst(1, 1, 2, &
      srName, 'obsTypes', 'observation types for wrapper', '', &
      maxNbWrapObsTypesWithObsDef, SIZE(obsTypes), irc)

    CALL dimTst(1, 1, 2, &
      srName, 'nObsPerSatPerObsType', 'second dim of nObs for wrapper', '', &
      maxNbWrapObsTypesWithObsDef, SIZE(nObsPerSatPerObsType, 2), irc)

    IF (useObsDef(gObsDef, useObsDefCode)) THEN

      nObsTypes = maxNbWrapObsTypesWithObsDef
      obsTypes(1:maxNbWrapObsTypesWithObsDef) = &
        obsListBasic(1:maxNbWrapObsTypesWithObsDef)

      CALL extractHdrObsInfoWithObsDef( &
        rxoHdr, gObsDef, nSats, svnPerSat, nObsPerSatPerObsType)
    ELSE
      CALL extractObsTypesFromHdr_merge( &
        inpFileName_compr, rxoHdr, rxo2OpMode, nObsTypes, obsTypes)

      CALL extractHdrObsInfoWithoutObsDef( &
        rxoHdr, rxo2OpMode, nObsTypes, obsTypes, &
        nSats, svnPerSat, nObsPerSatPerObsType)
    ENDIF

    maxNbSat = MIN(SIZE(svnPerSat), SIZE(nObsPerSatPerObsType, 1))

    IF (nSats > maxNbSat) THEN
      CALL wrtSrWarnToLfnErr(srName, &
        'Too many satellites (' // str(nSats) // ').', &
        'Array svnPerSat has size ' // str(SIZE(svnPerSat)) // ',', &
        'array nObsPerSatPerObsType has first size' &
        // str(SIZE(nObsPerSatPerObsType, 1)) // '.', &
        'Some satellites are ignorred.', &
        'RINEX observation file name:', &
        ' ' // TRIM(inpFileName_compr))
    ENDIF

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractObsTypesFromHdr

  ! -------------------------------------------------------------------------
  FUNCTION wrapR2RdOr( &
    rxoInpHdlr, rxoRdState, rxoObsAtEpo, rxoRdHdrConf, rxo2OpMode, &
    rxoRdRecConf, nObsTypes, obsTypes, obsEpo, rcvClkOffset, &
    nSatsAtEpo, svnPerSatAtEpo, obsPerSatPerObsType, snrPerSatPerObsType, &
    lliPerSatPerObsType, useObsDefCode, gObsDef, unskippedNewHdr, newHdrParts) &
    RESULT(succeeded)
  ! Reads the observation information of a RINEX file.
  !
  ! Could cause complications:
  ! In contrast to the original r2RdOr, rdRxoRec also reads header
  ! information which is written between observations and modifies
  ! rxoInpHdlr accordingly.
  !
  ! Differences to the original r2RdOr signature per dummy variable:
  !
  !   old name   new name              remark
  !   --------   --------              ------
  !   -          rxoInpHdlr            New RINEX routines need more information
  !   -          rxoRdState            New RINEX routines need more information
  !   -          rxoObsAtEpo           New RINEX routines need more information
  !   -          rxoRdHdrConf          New RINEX routines need more information
  !   -          rxoRdRecConf          New RINEX routines need more information
  !   LFNOBS     -                     Now passed within rxoInpHdlr
  !   LFNERR     -                     Callers put m_bern::lfnErr
  !                                      (except for CCRINEXO which fixes to 6)
  !   MAXSAT     -                     Now passed within rxoInpHdlr
  !   IRXVRS     -                     Now passed within rxoInpHdlr
  !   NUMTYP     nObsTypes             -
  !   OBSTYP     obsTypes              -
  !   SATSYS     -                     Now passed within rxoInpHdlr
  !   EPOCH      obsEpo                -
  !   EPOCOR     rcvClkOffset          -
  !   IFLAG      -                     Handled internally by nextRxoRec.
  !   NSATEP     nSatsAtEpo            -
  !   SATEPO     svnPerSatAtEpo        -
  !   OBSEPO     obsPerSatPerObsType   -
  !   ISIGN      snrPerSatPerObsType   -
  !   LLI        lliPerSatPerObsType   -
  !   IRCODE     succeeded             Logical is enough.
  !   USEGEOS    useObsDefCode         -
  !   GOBSDEF    gObsDef               Independent of the internally used
  !                                    gObsDef of the new RINEX tools!
  !   RINSTAT    -                     Use collectObsStatistics
  ! -------------------------------------------------------------------------
    ! RINEX input handler
    TYPE(t_rxoInpHdlr),       TARGET, INTENT(INOUT) :: rxoInpHdlr
    ! RINEX observation read state.
    TYPE(t_rxoRdState),       TARGET, INTENT(INOUT) :: rxoRdState
    ! Data holder for observation data.
    TYPE(t_rxoObsAtEpo),      TARGET, INTENT(INOUT) :: rxoObsAtEpo
    ! Configuration for reading RINEX observation header.
    TYPE(t_rxoRdHdrConf),             INTENT(IN)    :: rxoRdHdrConf
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),               INTENT(IN)    :: rxo2OpMode
    ! Configuration for reading RINEX observation records.
    TYPE(t_rxoRdRecConf),             INTENT(IN)    :: rxoRdRecConf
    ! Number of (different) observation types
    INTEGER(i4b),                     INTENT(IN)    :: nObsTypes
    ! List of observation types
    ! If marker type is "SPACEBORNE":
    !   obsTypes(nObsTypes + K) may contain a factor for obsTypes(K)
    !   (2 CHARACTERS HEX DATA).
    !   Therefore, please declarate at least obsTypes(2*nObsTypes)
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(IN)    :: obsTypes
    ! Observation epoch (receiver time)
    TYPE(t_epoch),                    INTENT(OUT)   :: obsEpo
    ! Receiver clock offset
    REAL(r8b),                        INTENT(OUT)   :: rcvClkOffset
    ! Number of satellites related to the obs. epoch
    INTEGER(i4b),                     INTENT(OUT)   :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b),     DIMENSION(:),   INTENT(OUT)   :: svnPerSatAtEpo
    ! Observation per satellite per obs. type
    ! obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! If observation selection is used, the second index goes from 1 to 4.
    REAL(r8b),        DIMENSION(:,:), INTENT(OUT)   :: obsPerSatPerObsType
    ! S/N rate per satellite per obs. type
    ! snrPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! If observation selection is used, the second index goes from 1 to 4.
    INTEGER(i4b),     DIMENSION(:,:), INTENT(OUT)   :: snrPerSatPerObsType
    ! Loss of Lock Indicator per satellite per obs. type
    ! lliPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! If observation selection is used, the second index goes from 1 to 4.
    INTEGER(i4b),     DIMENSION(:,:), INTENT(OUT)   :: lliPerSatPerObsType
    ! gObsDef is used only if useObsDefCode == 1
    INTEGER(i4b),           OPTIONAL, INTENT(IN)    :: useObsDefCode
    ! Observation selection
    TYPE(t_gObsDef),        OPTIONAL, INTENT(IN)    :: gObsDef
    ! There was a new header, e.g. from directly concatenating files.
    ! And the user decided not to skip the file.
    LOGICAL,                OPTIONAL, INTENT(OUT)   :: unskippedNewHdr
    ! There were new header parts inbetween observations.
    LOGICAL,                OPTIONAL, INTENT(OUT)   :: newHdrParts
    ! Returns .TRUE. if succeeded
    LOGICAL :: succeeded
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':wrapR2RdOr'

    INTEGER(i4b) :: maxWrapSat, maxWrapObs

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    maxWrapSat = MIN( &
      SIZE(svnPerSatAtEpo), &
      SIZE(obsPerSatPerObsType, 1), &
      SIZE(snrPerSatPerObsType, 1), &
      SIZE(lliPerSatPerObsType, 1))

    maxWrapObs = MIN( &
      SIZE(obsPerSatPerObsType, 2), &
      SIZE(snrPerSatPerObsType, 2), &
      SIZE(lliPerSatPerObsType, 2))

    succeeded = nextRxoRec( &
      rxoInpHdlr, rxoRdState, rxoRdHdrConf, rxo2OpMode, rxoRdRecConf, &
      rxoObsAtEpo, unskippedNewHdr, newHdrParts)

    IF (.NOT. succeeded) THEN
      IF (srfDebug) CALL cpu_exit(srName)
      RETURN
    ENDIF

    CALL extractRecInfo( &
      rxoInpHdlr, maxWrapSat, rxoObsAtEpo, &
      obsEpo, rcvClkOffset, nSatsAtEpo, svnPerSatAtEpo)

    IF (nSatsAtEpo <= 0) THEN
      IF (srfDebug) CALL cpu_exit(srName)
      RETURN
    ENDIF

    obsPerSatPerObsType(:nSatsAtEpo,:) = 0d0
    snrPerSatPerObsType(:nSatsAtEpo,:) = 0
    lliPerSatPerObsType(:nSatsAtEpo,:) = 0

    IF (useObsDef(gObsDef, useObsDefCode)) THEN
      CALL extractObsRecDataWithObsDef( &
        gObsDef, nSatsAtEpo, svnPerSatAtEpo, maxWrapObs, rxoObsAtEpo, &
        obsPerSatPerObsType, snrPerSatPerObsType, lliPerSatPerObsType)
    ELSE
      CALL extractObsRecDataWithoutObsDef( &
        rxo2OpMode, rxoInpHdlr%inpHdrAndStat%hdr%genData, nObsTypes, obsTypes, &
        nSatsAtEpo, svnPerSatAtEpo, maxWrapObs, rxoObsAtEpo, &
        obsPerSatPerObsType, snrPerSatPerObsType, lliPerSatPerObsType)
    ENDIF

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END FUNCTION wrapR2RdOr

  ! -------------------------------------------------------------------------
  SUBROUTINE wrtExtractObsTypesFromHdrData( &
    nObsTypes, obsTypes, nSats, svnPerSat, nObsPerSatPerObsType)

    INTEGER(i4b),                     INTENT(IN) :: nObsTypes
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(IN) :: obsTypes
    INTEGER(i4b),                     INTENT(IN) :: nSats
    INTEGER(i4b),     DIMENSION(:),   INTENT(IN) :: svnPerSat
    INTEGER(i4b),     DIMENSION(:,:), INTENT(IN) :: nObsPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':wrtExtractObsTypesFromHdrData'

    ! Local variables
    INTEGER(i4b) :: iWrite1, iWrite2

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    WRITE(lfnPrt, '(1X,A)') 'nObsTypes = ' // str(nObsTypes)

    DO iWrite1 = 1, MIN(nObsTypes, SIZE(obsTypes))
      WRITE(lfnPrt, '(1X,A)') &
        'obsTypes(' // TRIM(str(iWrite1)) // ') = ' &
        // obsTypes(iWrite1)
    ENDDO

    WRITE(lfnPrt, '(1X,A)') 'nSats = ' // str(nSats)

    DO iWrite1 = 1, MIN(nSats, SIZE(svnPerSat))
      WRITE(lfnPrt, '(1X,A)') &
        'svnPerSat(' // TRIM(str(iWrite1)) // ') = ' &
        // str(svnPerSat(iWrite1))
    ENDDO

    DO iWrite1 = 1, MIN(nSats, SIZE(nObsPerSatPerObsType, 1))
      DO iWrite2 = 1, MIN(nObsTypes, SIZE(nObsPerSatPerObsType, 2))
        WRITE(lfnPrt, '(1X,A)') &
          'nObsPerSatPerObsType(' // TRIM(str(iWrite1)) &
          // ',' // TRIM(str(iWrite2)) // ') = ' &
          // str(nObsPerSatPerObsType(iWrite1, iWrite2))
      ENDDO
    ENDDO

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE wrtExtractObsTypesFromHdrData

  ! -------------------------------------------------------------------------
  SUBROUTINE wrtWrapR2RdOrData( &
    nObsTypes, obsEpo, rcvClkOffset, nSatsAtEpo, svnPerSatAtEpo, &
    obsPerSatPerObsType, snrPerSatPerObsType, lliPerSatPerObsType, &
    useObsDefCode)

    INTEGER(i4b),                  INTENT(IN) :: nObsTypes
    TYPE(t_epoch),                 INTENT(IN) :: obsEpo
    REAL(r8b),                     INTENT(IN) :: rcvClkOffset
    INTEGER(i4b),                  INTENT(IN) :: nSatsAtEpo
    INTEGER(i4b),  DIMENSION(:),   INTENT(IN) :: svnPerSatAtEpo
    REAL(r8b),     DIMENSION(:,:), INTENT(IN) :: obsPerSatPerObsType
    INTEGER(i4b),  DIMENSION(:,:), INTENT(IN) :: snrPerSatPerObsType
    INTEGER(i4b),  DIMENSION(:,:), INTENT(IN) :: lliPerSatPerObsType
    INTEGER(i4b),        OPTIONAL, INTENT(IN) :: useObsDefCode
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':wrtWrapR2RdOrData'

    ! Local variables
    INTEGER(i4b) :: iWrite1, iWrite2

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    WRITE(lfnPrt, '(1X,A)') 'obsEpo%day    = ' // str(obsEpo%day)
    WRITE(lfnPrt, '(1X,A)') 'obsEpo%frac   = ' // str(obsEpo%frac)
    WRITE(lfnPrt, '(1X,A)') 'rcvClkOffset  = ' // TRIM(str(rcvClkOffset))
    WRITE(lfnPrt, '(1X,A)') 'nSatsAtEpo    = ' // str(nSatsAtEpo)

    DO iWrite1 = 1, MIN(nSatsAtEpo, SIZE(svnPerSatAtEpo))
      WRITE(lfnPrt, '(1X,A)') &
        'svnPerSatAtEpo(' // TRIM(str(iWrite1)) // ') = ' &
        // str(svnPerSatAtEpo(iWrite1))
    ENDDO

    DO iWrite1 = 1, MIN(nSatsAtEpo, SIZE(obsPerSatPerObsType, 1))
      DO iWrite2 = 1, MIN(nObsTypes, SIZE(obsPerSatPerObsType, 2))
        WRITE(lfnPrt, '(1X,A)') &
          'obsPerSatPerObsType(' // TRIM(str(iWrite1)) &
          // ',' // TRIM(str(iWrite2)) // ') = ' &
          // TRIM(str(obsPerSatPerObsType(iWrite1, iWrite2)))
      ENDDO
    ENDDO

    DO iWrite1 = 1, MIN(nSatsAtEpo, SIZE(snrPerSatPerObsType, 1))
      DO iWrite2 = 1, MIN(nObsTypes, SIZE(snrPerSatPerObsType, 2))
        WRITE(lfnPrt, '(1X,A)') &
          'snrPerSatPerObsType(' // TRIM(str(iWrite1)) &
          // ',' // TRIM(str(iWrite2)) // ') = ' &
          // str(snrPerSatPerObsType(iWrite1, iWrite2))
      ENDDO
    ENDDO

    DO iWrite1 = 1, MIN(nSatsAtEpo, SIZE(lliPerSatPerObsType, 1))
      DO iWrite2 = 1, MIN(nObsTypes, SIZE(lliPerSatPerObsType, 2))
        WRITE(lfnPrt, '(1X,A)') &
          'lliPerSatPerObsType(' // TRIM(str(iWrite1)) &
          // ',' // TRIM(str(iWrite2)) // ') = ' &
          // str(lliPerSatPerObsType(iWrite1, iWrite2))
      ENDDO
    ENDDO

    IF (PRESENT(useObsDefCode)) THEN
      WRITE(lfnPrt, '(1X,A)') 'useObsDefCode = ' // str(useObsDefCode)
    ELSE
      WRITE(lfnPrt, '(1X,A)') 'useObsDefCode not present'
    ENDIF

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE wrtWrapR2RdOrData

  ! -------------------------------------------------------------------------
  SUBROUTINE setWlfsInHdr(rxoOutHdlr, defaultWlfs, wlfsPerSat, nWlfsPerSat)
    ! RINEX observation output handler to prepare.
    TYPE(t_rxoOutHdlr),   TARGET, INTENT(INOUT) :: rxoOutHdlr
    ! Default wavelength factors (L1/L2).
    !   defaultWlfs(1): Cycle ambiguities
    !   defaultWlfs(2): Half-cycle ambiguities
    INTEGER(i4b), DIMENSION(2),   INTENT(IN)    :: defaultWlfs
    ! Non-default wavelength factors (L1/L2) per satellite.
    !   wlfsPerSat(1, :) : Cycle ambiguities
    !   wlfsPerSat(2, :) : Half-cycle ambiguities
    !   wlfsPerSat(3, :) : SVN
    !
    ! Additional comment from legacy file:
    !   I=1: WLFAC(L1), I=2: WLFAC(L2), I=3: SVN
    INTEGER(i4b), DIMENSION(:,:), INTENT(IN)    :: wlfsPerSat
    ! Number of entries (satellites) in wlfsPerSat.
    INTEGER(i4b),                 INTENT(IN)    :: nWlfsPerSat
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':setWlfsInHdr'

    ! Local variables
    TYPE(t_rxoPlainHdrData), POINTER :: plainData
    TYPE(t_infoPerSys),      POINTER :: infoPerSys
    TYPE(t_infoForSys),      POINTER :: infoForSys
    TYPE(t_wlfs),            POINTER :: wlfs

    INTEGER(i4b) :: iSys, iWlfs, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(infoForSys, wlfs)

    plainData  => rxoOutHdlr%outHdrAndStat%hdr%plainData
    infoPerSys => rxoOutHdlr%outHdrAndStat%hdr%infoPerSys

    IF (SIZE(wlfsPerSat, 1) < 3) THEN
      CALL wrtSrErrToLfnErr(srName, 'SIZE(wlfsPerSat, 1) < 3')
      CALL exitRc(2)
    ENDIF

    IF (nWlfsPerSat >= SIZE(wlfsPerSat, 2)) THEN
      CALL wrtSrErrToLfnErr(srName, 'nWlfsPerSat >= SIZE(wlfsPerSat, 2)')
      CALL exitRc(2)
    ENDIF

    plainData%defaultWlfs%l1 = defaultWlfs(1)
    plainData%defaultWlfs%l2 = defaultWlfs(2)

    DO iWlfs = 1, nWlfsPerSat
      svn = wlfsPerSat(3, nWlfsPerSat)
      iSys = svn /100
      prn  = mod(svn, 100)

      IF (iSys < 0) CYCLE
      IF (iSys >= maxRnxSys) CYCLE
      IF (prn > maxRnxPrn) CYCLE

      infoForSys => infoPerSys%ar(iSys)
      wlfs => infoForSys%infoPerPrn%ar(prn)%wlfs
      wlfs%l1 = wlfsPerSat(1, nWlfsPerSat)
      wlfs%l2 = wlfsPerSat(2, nWlfsPerSat)
    ENDDO

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE setWlfsInHdr

  ! -------------------------------------------------------------------------
  SUBROUTINE setObsTypesInHdr(rxoOutHdlr, rxo2OpMode, nObsTypes, obsTypes)
    ! RINEX observation output handler to prepare.
    TYPE(t_rxoOutHdlr),     TARGET, INTENT(INOUT) :: rxoOutHdlr
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),             INTENT(IN)    :: rxo2OpMode
    ! Number of (different) observation types.
    INTEGER(i4b),                   INTENT(IN)    :: nObsTypes
    ! List of observation types.
    CHARACTER(LEN=2), DIMENSION(:), INTENT(IN)    :: obsTypes
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':setObsTypesInHdr'

    ! Local variables
    TYPE(t_rxoHdr),      POINTER :: rxoHdr
    TYPE(t_obsTypeDefs), POINTER :: obsTypeDefs

    TYPE(t_rnxVers) :: rnxVers

    CHARACTER(LEN=3) :: rx2ObsType, rx3ObsType

    INTEGER(i4b) :: iSys, iWrapObsType, obsTypeHash

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(obsTypeDefs)

    rxoHdr => rxoOutHdlr%outHdrAndStat%hdr

    rnxVers = rxoHdr%genData%rnxVers

    CALL clear(rxoHdr%rx2ObsTypes)

    IF (rnxVers%mainVers < 3) THEN
      DO iWrapObsType = 1, MIN(nObsTypes, SIZE(obsTypes))
        rx2ObsType  = ' ' // obsTypes(iWrapObsType)
        CALL push_back(rxoHdr%rx2ObsTypes, rx2ObsType)
      ENDDO
    ENDIF

    DO iSys = 0, maxRnxSys -1
      obsTypeDefs => rxoHdr%infoPerSys%ar(iSys)%obsTypeDefs

      CALL clear(obsTypeDefs)

      ! It's for RNXSMT, we do crazy things!
      ! Well, for compatibility we still want L1 L2 P1 P2 as observation types
      ! in the smoothed RINEX 2.00 file.
      ! However, for some of the other systems than GPS, they are not defined,
      ! even not both frequencies 1 and 2 :-).

      DO iWrapObsType = 1, MIN(nObsTypes, SIZE(obsTypes))
        rx2ObsType  = ' ' // obsTypes(iWrapObsType)

        rx3ObsType  = convertObsTypeR2ToR3(rx2ObsType, rxo2OpMode, rnxVers, 'G')

        obsTypeHash = calcObsTypeHash_internal(rx3ObsType)

        CALL appendObsTypeHash(obsTypeDefs, obsTypeHash)

        IF (rnxVers%mainVers < 3) THEN
          obsTypeDefs%rx2PosPerObsTypeHash(obsTypeHash) = iWrapObsType
        ENDIF
      ENDDO
    ENDDO

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE setObsTypesInHdr

  ! -------------------------------------------------------------------------
  SUBROUTINE setObsTypesAndNbrInHdr( &
    rxoOutHdlr, &
    rxo2OpMode, nObsTypes, obsTypes, nSats, svnPerSat, nObsPerSatPerObsType)

    ! RINEX observation output handler to prepare.
    TYPE(t_rxoOutHdlr),       TARGET, INTENT(INOUT) :: rxoOutHdlr
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),               INTENT(IN)    :: rxo2OpMode
    ! Number of (different) observation types.
    INTEGER(i4b),                     INTENT(IN)    :: nObsTypes
    ! List of observation types.
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(IN)    :: obsTypes
    ! Number of satellites.
    INTEGER(i4b),                     INTENT(IN)    :: nSats
    ! Satellite number (SVN) per satellite.
    ! svnPerSat(iSat)
    INTEGER(i4b),     DIMENSION(:),   INTENT(IN)    :: svnPerSat
    ! Number of observations per satellite and per observation type.
    ! nObsPerSatPerObsType(iSat, iWrapObsType)
    INTEGER(i4b),     DIMENSION(:,:), INTENT(IN)    :: nObsPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':setObsTypesAndNbrInHdr'

    ! Local variables
    TYPE(t_infoPerSys),  POINTER :: infoPerSys
    TYPE(t_obsTypeDefs), POINTER :: obsTypeDefs
    TYPE(t_obsTypeDef),  POINTER :: obsTypeDef

    INTEGER(i4b) :: iSat, iSys, iWrapObsType
    INTEGER(i4b) :: maxSatIdx, maxWrapObsTypeIdx, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(obsTypeDefs, obsTypeDef)

    infoPerSys => rxoOutHdlr%outHdrAndStat%hdr%infoPerSys

    CALL setObsTypesInHdr(rxoOutHdlr, rxo2OpMode, nObsTypes, obsTypes)

    maxSatIdx = &
      MIN(MIN(nSats, SIZE(svnPerSat)), SIZE(nObsPerSatPerObsType, 1))

    maxWrapObsTypeIdx = &
      MIN(MIN(nObsTypes, SIZE(obsTypes)), SIZE(nObsPerSatPerObsType, 2))

    DO iSat = 1, maxSatIdx
      svn = svnPerSat(iSat)
      iSys = svn /100
      prn  = mod(svn, 100)

      IF (iSys < 0) CYCLE
      IF (iSys >= maxRnxSys) CYCLE
      IF (prn > maxRnxPrn) CYCLE

      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      DO iWrapObsType = 1, maxWrapObsTypeIdx
        obsTypeDef => obsTypeDefs%ar(iWrapObsType)

        obsTypeDef%obsTypeDefPerPrn%ar(prn)%nObs = &
          nObsPerSatPerObsType(iSat, iWrapObsType)
      ENDDO
    ENDDO

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE setObsTypesAndNbrInHdr

  ! -------------------------------------------------------------------------
  SUBROUTINE wrapR2WtOr( &
    rxoOutHdlr, rxoWrtConf, rxo2OpMode, rxoObsAtEpo, obsEpo, rcvClkOffset, &
    epoFlag, nSatsAtEpo, svnPerSatAtEpo, obsPerSatPerObsType, &
    signalPerSatPerObsType, lliPerSatPerObsType)
  ! Differences to the original r2WtOr signature per dummy variable:
  !
  !   old name   new name               remark
  !   --------   --------               ------
  !   -          rxoOutHdlr             New RINEX routines need more information
  !   -          rxoWrtConf             New RINEX routines need more information
  !   -          rxoObsAtEpo            New RINEX routines need more information
  !   LFNOBS     -                      Now passed within rxoOutHdlr
  !   LFNERR     -                      Callers put m_bern::lfnErr
  !   MAXSAT     -                      Now passed within rxoOutHdlr
  !   IRXVRS     -                      Now passed within rxoOutHdlr
  !   NUMTYP     -                      Now passed within rxoOutHdlr
  !   ISNMIN     -                      Was constantly 1
  !   ISNTHR     -                      Was constantly 5
  !   ISNMAX     -                      Was constantly 9
  !   EPOCH      obsEpo                 -
  !   EPOCOR     rcvClkOffset           -
  !   IFLAG      epoFlag                -
  !   NSATEP     nSatsAtEpo             -
  !   SATEPO     svnPerSatAtEpo         -
  !   OBSEPO     obsPerSatPerObsType    -
  !   SIGNAL     signalPerSatPerObsType -
  !   LLI        lliPerSatPerObsType    -
  !   SATSYS     -                      No more used
  !   IRCODE     -                      No more used
  ! -------------------------------------------------------------------------
    ! RINEX observation output handler to use.
    TYPE(t_rxoOutHdlr),   TARGET, INTENT(IN)    :: rxoOutHdlr
    ! Configuration for writing RINEX observation files.
    TYPE(t_rxoWrtConf),           INTENT(IN)    :: rxoWrtConf
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),           INTENT(IN)    :: rxo2OpMode
    ! Data holder for observation data.
    TYPE(t_rxoObsAtEpo),  TARGET, INTENT(INOUT) :: rxoObsAtEpo
    ! Observation epoch (receiver time).
    TYPE(t_epoch),                INTENT(IN)    :: obsEpo
    ! Receiver clock offset.
    REAL(r8b),                    INTENT(IN)    :: rcvClkOffset
    ! Epoch flag of current record.
    INTEGER(i4b),                 INTENT(IN)    :: epoFlag
    ! Number of satellites related to the obs. epoch.
    ! <0: obsEpo(2) contains delta-T ???
    INTEGER(i4b),                 INTENT(IN)    :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b), DIMENSION(:),   INTENT(IN)    :: svnPerSatAtEpo
    ! Observation per satellite per obs. type
    ! obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
    REAL(r8b),    DIMENSION(:,:), INTENT(IN)    :: obsPerSatPerObsType
    ! S/N rate per satellite per obs. type
    ! signalPerSatPerObsType(iSatAtEpo, iWrapObsType)
    REAL(r8b),    DIMENSION(:,:), INTENT(IN)    :: signalPerSatPerObsType
    ! Loss of Lock Indicator per satellite per obs. type
    ! lliPerSatPerObsType(iSatAtEpo, iWrapObsType)
    INTEGER(i4b), DIMENSION(:,:), INTENT(IN)    :: lliPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':wrapR2WtOr'

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    CALL prepareRxoObsAtEpo( &
      rxoObsAtEpo, rxoOutHdlr, obsEpo, rcvClkOffset, epoFlag, &
      nSatsAtEpo, svnPerSatAtEpo, &
      obsPerSatPerObsType, signalPerSatPerObsType, lliPerSatPerObsType)

    CALL wrtRxoRec(rxoOutHdlr, rxoWrtConf, rxo2OpMode, rxoObsAtEpo)

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE wrapR2WtOr

  ! -------------------------------------------------------------------------
  SUBROUTINE prepareRxoObsAtEpo( &
    rxoObsAtEpo, rxoOutHdlr, obsEpo, rcvClkOffset, epoFlag, nSatsAtEpo, &
    svnPerSatAtEpo, obsPerSatPerObsType, signalPerSatPerObsType, &
    lliPerSatPerObsType)
  ! Differences to the original r2WtOr signature per dummy variable:
  !
  !   old name   new name               remark
  !   --------   --------               ------
  !   -          rxoObsAtEpo            Use this to directly call
  !                                     o_rinexO::wrtRxoRec
  !   -          rxoOutHdlr             New RINEX routines need more information
  !   LFNOBS     -                      Now passed within rxoOutHdlr
  !   LFNERR     -                      Callers put m_bern::lfnErr
  !   MAXSAT     -                      Now passed within rxoOutHdlr
  !   IRXVRS     -                      Now passed within rxoOutHdlr
  !   NUMTYP     -                      Now passed within rxoOutHdlr
  !   ISNMIN     -                      Was constantly 1
  !   ISNTHR     -                      Was constantly 5
  !   ISNMAX     -                      Was constantly 9
  !   EPOCH      obsEpo                 -
  !   EPOCOR     rcvClkOffset           -
  !   IFLAG      epoFlag                -
  !   NSATEP     nSatsAtEpo             -
  !   SATEPO     svnPerSatAtEpo         -
  !   OBSEPO     obsPerSatPerObsType    -
  !   SIGNAL     signalPerSatPerObsType -
  !   LLI        lliPerSatPerObsType    -
  !   SATSYS     -                      No more used
  !   IRCODE     -                      No more used
  ! -------------------------------------------------------------------------
    ! RINEX observation output handler to prepare.
    TYPE(t_rxoObsAtEpo),  TARGET, INTENT(INOUT) :: rxoObsAtEpo
    ! RINEX observation output handler to use.
    TYPE(t_rxoOutHdlr),   TARGET, INTENT(IN)    :: rxoOutHdlr
    ! Observation epoch (receiver time).
    TYPE(t_epoch),                INTENT(IN)    :: obsEpo
    ! Receiver clock offset.
    REAL(r8b),                    INTENT(IN)    :: rcvClkOffset
    ! Epoch flag of current record.
    INTEGER(i4b),                 INTENT(IN)    :: epoFlag
    ! Number of satellites related to the obs. epoch.
    ! <0: obsEpo(2) contains delta-T ???
    INTEGER(i4b),                 INTENT(IN)    :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b), DIMENSION(:),   INTENT(IN)    :: svnPerSatAtEpo
    ! Observation per satellite per obs. type
    ! obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
    REAL(r8b),    DIMENSION(:,:), INTENT(IN)    :: obsPerSatPerObsType
    ! S/N rate per satellite per obs. type
    ! signalPerSatPerObsType(iSatAtEpo, iWrapObsType)
    REAL(r8b),    DIMENSION(:,:), INTENT(IN)    :: signalPerSatPerObsType
    ! Loss of Lock Indicator per satellite per obs. type
    ! lliPerSatPerObsType(iSatAtEpo, iWrapObsType)
    INTEGER(i4b), DIMENSION(:,:), INTENT(IN)    :: lliPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':prepareRxoObsAtEpo'

    ! Local variables
    TYPE(t_infoPerSys),    POINTER :: infoPerSys
    TYPE(t_obsTypeDefs),   POINTER :: obsTypeDefs
    TYPE(t_rxoObsPerType), POINTER :: obsPerType
    TYPE(t_rxoObsOfType),  POINTER :: obsOfType

    INTEGER(i4b) :: iSatAtEpo, iSys, iWrapObsType
    INTEGER(i4b) :: maxSatEpoIdx, maxWrapObsTypeIdx
    INTEGER(i4b) :: nObs, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(obsTypeDefs, obsPerType)

    infoPerSys => rxoOutHdlr%outHdrAndStat%hdr%infoPerSys

    CALL clear(rxoObsAtEpo)

    maxSatEpoIdx = MIN(IABS(nSatsAtEpo), MIN( &
      MIN(SIZE(svnPerSatAtEpo), SIZE(obsPerSatPerObsType, 1)), &
      MIN(SIZE(signalPerSatPerObsType, 1), SIZE(lliPerSatPerObsType, 1))))

    maxWrapObsTypeIdx = MIN(SIZE(obsPerSatPerObsType, 2), &
      MIN(SIZE(signalPerSatPerObsType, 2), SIZE(lliPerSatPerObsType, 2)))

    rxoObsAtEpo%epoch      = obsEpo
    rxoObsAtEpo%epoFlg%ef  = epoFlag
    rxoObsAtEpo%rcvClkOffs = rcvClkOffset

    DO iSatAtEpo = 1, maxSatEpoIdx
      svn  = svnPerSatAtEpo(iSatAtEpo)
      iSys = svn /100
      prn  = mod(svn, 100)

      IF (iSys < 0) CYCLE
      IF (iSys >= maxRnxSys) CYCLE
      IF (prn > maxRnxPrn) CYCLE

      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs
      obsPerType => rxoObsAtEpo%obsPerSys%ar(iSys)%ar(prn)

      nObs = MIN(maxWrapObsTypeIdx, nb(obsTypeDefs))

      CALL reserve(obsPerType, nObs)
      obsPerType%nb = nObs

      DO iWrapObsType = 1, nObs
        obsOfType => obsPerType%ar(iWrapObsType)

        obsOfType%obsTypeHash  = obsTypeDefs%ar(iWrapObsType)%obsTypeHash
        obsOfType%values%value = obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
        obsOfType%values%lli   = lliPerSatPerObsType(iSatAtEpo, iWrapObsType)

        obsOfType%values%snr   = &
          IDNINT(signalPerSatPerObsType(iSatAtEpo, iWrapObsType))
      ENDDO
    ENDDO

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE prepareRxoObsAtEpo

  ! -------------------------------------------------------------------------
  FUNCTION useObsDef(gObsDef, useObsDefCode) RESULT (res)
    TYPE(t_gObsDef), OPTIONAL, INTENT(IN) :: gObsDef
    ! gObsDef is used only if useObsDefCode == 1
    INTEGER(i4b),    OPTIONAL, INTENT(IN) :: useObsDefCode
    ! Returns
    LOGICAL :: res
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':useObsDef'

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    res = .FALSE.

    IF (PRESENT(useObsDefCode) .AND. PRESENT(gObsDef)) THEN
      IF (useObsDefCode == 1 .AND. gObsDef%noRec > 0) res = .TRUE.
    ENDIF

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END FUNCTION useObsDef

  ! -------------------------------------------------------------------------
  SUBROUTINE extractObsTypesFromHdr_merge( &
    inpFileName_compr, rxoHdr, rxo2OpMode, nObsTypes, obsTypes)
  ! Without satellite-specific obstype selection
  ! -------------------------------------------------------------------------
    CHARACTER(LEN=*),               INTENT(IN)    :: inpFileName_compr
    ! RINEX header
    TYPE(t_rxoHdr),         TARGET, INTENT(IN)    :: rxoHdr
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),             INTENT(IN)    :: rxo2OpMode
    ! Number of (different) observation types
    INTEGER(i4b),                   INTENT(OUT)   :: nObsTypes
    ! List of observation types
    ! If marker type is "SPACEBORNE":
    !   obsTypes(nObsTypes + K) may contain a factor for obsTypes(K)
    !   (2 CHARACTERS HEX DATA).
    !   Therefore, please declarate at least obsTypes(2*nObsTypes)
    !
    ! Very special behavior heritage from original R2RDOH.f:
    !   Even if the observation selection is used,
    !   this one contains the full list of original observation types
    !   and therefore is then not in the same order as
    !   nObsPerSatPerObsType.
    !
    !   For RINEX 3 the observation types of the various GNSS
    !   are somehow merged. This is new behavior to get RINEX 3.
    CHARACTER(LEN=2), DIMENSION(:), INTENT(OUT)   :: obsTypes
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':extractObsTypesFromHdr_merge'

    ! Local variables
    ! ---------------
    TYPE(t_infoPerSys),  POINTER :: infoPerSys
    TYPE(t_obsTypeDefs), POINTER :: obsTypeDefs

    TYPE(t_stringList) :: obsTypeR2List, obsTypeR3List
    TYPE(t_stringList) :: errR3ListOfSys, errR3ListPerSys
    TYPE(t_stringList) :: finalObsTypes

    CHARACTER(LEN=3) :: rx2ObsType, rx3ObsType
    CHARACTER(LEN=2) :: rx2ObsType23
    CHARACTER(LEN=1) :: sysChar

    INTEGER(i4b) :: iObsTypeDef, iSys, iWrapObsType
    INTEGER(i4b) :: obsTypeHash, obsTypeHash2, maxNbWrapObsTypes, nObs

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY (obsTypeDefs)

    infoPerSys => rxoHdr%infoPerSys

    maxNbWrapObsTypes = SIZE(obsTypes, 1)

    nObsTypes = 0
    obsTypes(:) = ''

    CALL constr(obsTypeR2List, 0)
    CALL constr(obsTypeR3List, 0)
    CALL constr(errR3ListOfSys, 0)
    CALL constr(errR3ListPerSys, maxRnxSys +5)
    CALL constr(finalObsTypes, 0)

    CALL push_back( &
      errR3ListPerSys, 'No rule to convert RINEX3 observation type(s)')

    CALL push_back( &
      errR3ListPerSys, 'to RINEX2 observation type(s):')

    sysLoop1: DO iSys = 0, maxRnxSys - 1
      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      sysChar = g_svnSys(iSys)

      nObs = MAXVAL(obsTypeDefs%rx3PosPerObsTypeHash(:))

      CALL clear(errR3ListOfSys)
      CALL reserve(obsTypeR2List, nObs)
      CALL reserve(obsTypeR3List, nObs)
      CALL reserve(finalObsTypes, nObs)
      CALL reserve(errR3ListOfSys, nObs)

      obsTypeLoop1: DO iObsTypeDef = 1, nObs
        obsTypeHash = 0
        DO obsTypeHash2 = 1, maxObsTypeHash
          IF (obsTypeDefs%rx3PosPerObsTypeHash(obsTypeHash2) == iObsTypeDef) THEN
            obsTypeHash = obsTypeHash2
            EXIT
          ENDIF
        ENDDO

        IF (obsTypeHash <= 0) CYCLE obsTypeLoop1

        rx3ObsType = calcObsTypeFromHash(obsTypeHash)

        rx2ObsType = convertObsTypeR3ToR2( &
          rx3ObsType, rxo2OpMode, rnxVers_2_00, sysChar)

        rx2ObsType23 = rx2ObsType(2:3)

        IF (rx2ObsType23 == '') THEN
          CALL push_back(errR3ListOfSys, rx3ObsType)
          CYCLE obsTypeLoop1
        ENDIF

        IF (getIdx(obsTypeR2List, rx2ObsType23) > 0) CYCLE obsTypeLoop1

        CALL push_back(obsTypeR3List, rx3ObsType)
        CALL push_back(obsTypeR2List, rx2ObsType23)

      ENDDO obsTypeLoop1

      IF (nb(errR3ListOfSys) > 0) THEN
        CALL push_back(errR3ListPerSys, &
          "  System '" // sysChar // "': " // TRIM(str(errR3ListOfSys)))
      ENDIF

    ENDDO sysLoop1

    nObsTypes = MIN(nb(obsTypeR2List), maxNbWrapObsTypes)

    DO iWrapObsType = 1, nObsTypes
      rx2ObsType23 = str(obsTypeR2List%ar(iWrapObsType))
      CALL push_back(finalObsTypes, rx2ObsType23)
      obsTypes(iWrapObsType) = rx2ObsType23
    ENDDO

! muted
!     IF (nb(errR3ListPerSys) > 2) THEN
!       CALL push_back( &
!         errR3ListPerSys, 'RINEX version ' // str(genData%rnxVers))
!       CALL push_back( &
!         errR3ListPerSys, 'RINEX observation file name:')
!       CALL push_back( &
!         errR3ListPerSys, ' ' // TRIM(inpFileName_compr))
!
!       CALL wrtSrWarnToLfnErr(srName, errR3ListPerSys)
!     ENDIF

    IF (nObsTypes > maxNbWrapObsTypes) THEN

      CALL wrtSrWarnToLfnErr(srName, &
        'Too many observation types ' &
        // '(' // str(nObsTypes) // ' > ' // str(maxNbWrapObsTypes) // ' ).', &
        'Some observations are ignorred.', &
        'RINEX 3 observation types:', &
        '  ' // TRIM(str(obsTypeR3List)), &
        'RINEX 2 observation types:', &
        '  ' // TRIM(str(obsTypeR2List)), &
        'Returned observation types:', &
        '  ' // TRIM(str(finalObsTypes)), &
        'RINEX observation file name:', &
        ' ' // TRIM(inpFileName_compr))

! muted
!     ELSE
!
!       CALL wrtSrInfoToLfnErr(srName, &
!         'RINEX 3 observation types:', &
!         '  ' // TRIM(str(obsTypeR3List)), &
!         'RINEX 2 observation types:', &
!         '  ' // TRIM(str(obsTypeR2List)), &
!         'Returned observation types:', &
!         '  ' // TRIM(str(finalObsTypes)), &
!         'RINEX observation file name:', &
!         ' ' // TRIM(inpFileName_compr))

    ENDIF

    CALL destr(obsTypeR2List)
    CALL destr(obsTypeR3List)
    CALL destr(errR3ListPerSys)
    CALL destr(errR3ListOfSys)
    CALL destr(finalObsTypes)

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractObsTypesFromHdr_merge

  ! -------------------------------------------------------------------------
  SUBROUTINE extractHdrObsInfoWithObsDef( &
    rxoHdr, gObsDef, nSats, svnPerSat, nObsPerSatPerObsType)
  ! With satellite-specific obstype selection
  ! -------------------------------------------------------------------------
    ! RINEX header
    TYPE(t_rxoHdr),           TARGET, INTENT(IN)    :: rxoHdr
    TYPE(t_gObsDef),          TARGET, INTENT(INOUT) :: gObsDef
    ! Number of satellites
    INTEGER(i4b),                     INTENT(OUT)   :: nSats
    ! Satellite number (SVN) per satellite
    ! svnPerSat(iSat)
    INTEGER(i4b),     DIMENSION(:),   INTENT(OUT)   :: svnPerSat
    ! Number of observations per satellite and per observation type
    ! nObsPerSatPerObsType(iSat, iWrapObsType)
    !
    ! The second index goes from 1 to 4.
    INTEGER(i4b),     DIMENSION(:,:), INTENT(OUT)   :: nObsPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':extractHdrObsInfoWithObsDef'

    ! Local variables
    ! ---------------
    TYPE(t_infoPerSys),    POINTER :: infoPerSys
    TYPE(t_obsTypeDefs),   POINTER :: obsTypeDefs
    TYPE(t_obsTypeDef),    POINTER :: obsTypeDef
    TYPE(t_satSig),        POINTER :: satSig

    CHARACTER(LEN=3) :: obsType

    INTEGER(i4b) :: iObsDefRec, iSys, iObsTypeDef, iWrapObsType
    INTEGER(i4b) :: nObs, obsTypeHash, prn

    LOGICAL :: newSat

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY (obsTypeDefs, obsTypeDef, satSig)

    infoPerSys => rxoHdr%infoPerSys

    nSats = 0
    svnPerSat(:) = 0
    nObsPerSatPerObsType(:,:) = 0

    ! Update nFreqC, nFreqP, obsTyp2 and factor of satSig's.
    obsDefLoop1: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)

      satSig%nFreqC     = 0
      satSig%nFreqP     = 0
      satSig%obsTyp2(:) = ''
      satSig%factor(:)  = 1

      iSys = satSig%sysNum

      IF (iSys < 0 .OR. iSys >= maxSys) CYCLE obsDefLoop1

      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      IF (nb(obsTypeDefs) <= 0) CYCLE obsDefLoop1

      prn = satSig%satNum

      IF (prn < 1 .OR. prn >= maxRnxPrn) CYCLE obsDefLoop1

      obsLoop1: DO iWrapObsType = 1, maxNbWrapObsTypesWithObsDef

        obsType = satSig%obsTyp(iWrapObsType)

        IF (obsType == '') CYCLE obsLoop1

        obsTypeHash = calcObsTypeHash_internal(obsType)

        iObsTypeDef = getIdx(obsTypeDefs, obsTypeHash)

        IF (iObsTypeDef <= 0) CYCLE obsLoop1

        obsTypeDef => obsTypeDefs%ar(iObsTypeDef)

        IF (iWrapObsType == 1 .OR. iWrapObsType == 2) THEN
          satSig%nFreqC = satSig%nFreqC + 1
        ENDIF

        IF (iWrapObsType == 3 .OR. iWrapObsType == 4) THEN
          satSig%nFreqP = satSig%nFreqP + 1
        ENDIF

        satSig%obsTyp2(iWrapObsType) = obsListBasic(iWrapObsType)

        ! Apply observation scale factor from RINEX file
        IF (satSig%factor(iWrapObsType) < obsTypeDef%scaleFactor) THEN
          satSig%factor(iWrapObsType) = obsTypeDef%scaleFactor
        ENDIF

      ENDDO obsLoop1
    ENDDO obsDefLoop1

    ! Remove unavailable observation types
    obsDefLoop2: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)
      obsLoop2: DO iWrapObsType = 1, maxNbWrapObsTypesWithObsDef
        IF (satSig%obsTyp2(iWrapObsType) == '') satSig%obsTyp(iWrapObsType) = ''
      ENDDO obsLoop2
    ENDDO obsDefLoop2

    ! Check whether one of the satellites has observations on two frequencies
    obsDefLoop3: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)

      IF (gObsDef%nfreqc == 0) THEN
        IF (satSig%nfreqc == 1) gObsDef%nfreqc = 1
      ENDIF

      IF (satSig%nfreqc == 2) THEN
        gObsDef%nfreqc = 2
        EXIT obsDefLoop3
      ENDIF

    ENDDO obsDefLoop3

    obsDefLoop4: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)

      IF (gObsDef%nfreqp == 0) THEN
        IF (satSig%nfreqp == 1) gObsDef%nfreqp = 1
      ENDIF

      IF (satSig%nfreqp == 2) THEN
        gObsDef%nfreqp = 2
        EXIT obsDefLoop4
      ENDIF

    ENDDO obsDefLoop4

    ! Obstype counter (info from RINEX header) adapted to gObsDef
    newSat = .TRUE.

    ! Fill nObsPerSatPerObsType
    obsDefLoop5: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)

      iSys = satSig%sysNum

      IF (iSys < 0 .OR. iSys >= maxSys) CYCLE obsDefLoop5

      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      IF (nb(obsTypeDefs) <= 0) CYCLE obsDefLoop5

      prn  = satSig%satNum

      IF (prn < 1 .OR. prn >= maxRnxPrn) CYCLE obsDefLoop5

      newSat = .TRUE.

      obsLoop5: DO iWrapObsType = 1, maxNbWrapObsTypesWithObsDef

        obsType = satSig%obsTyp(iWrapObsType)

        IF (obsType == '') CYCLE obsLoop5

        obsTypeHash = calcObsTypeHash_internal(obsType)

        nObs = getNbObs(obsTypeDefs, prn, obsTypeHash)

        IF (nObs <= 0) CYCLE obsLoop5

        IF (newSat) THEN
          nSats = nSats + 1
          newSat = .FALSE.

          IF (nSats <= SIZE(svnPerSat)) svnPerSat(nSats) = iSys * 100 + prn
        ENDIF

        IF (nSats <= SIZE(nObsPerSatPerObsType, 1)) THEN
          nObsPerSatPerObsType(nSats, iWrapObsType) = nObs
        ENDIF

      ENDDO obsLoop5
    ENDDO obsDefLoop5

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractHdrObsInfoWithObsDef

  ! -------------------------------------------------------------------------
  SUBROUTINE extractHdrObsInfoWithoutObsDef( &
    rxoHdr, rxo2OpMode, &
    nObsTypes, obsTypes, nSats, svnPerSat, nObsPerSatPerObsType)
  ! Without satellite-specific obstype selection
  ! -------------------------------------------------------------------------
    ! RINEX header
    TYPE(t_rxoHdr),           TARGET, INTENT(IN)    :: rxoHdr
    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),               INTENT(IN)    :: rxo2OpMode
    ! Number of (different) observation types
    INTEGER(i4b),                     INTENT(IN)    :: nObsTypes
    ! List of observation types
    ! If marker type is "SPACEBORNE":
    !   obsTypes(nObsTypes + K) may contain a factor for obsTypes(K)
    !   (2 CHARACTERS HEX DATA).
    !   Therefore, please declarate at least obsTypes(2*nObsTypes)
    !
    ! Very special behavior heritage from original R2RDOH.f:
    !   Even if the observation selection is used,
    !   this one contains the full list of original observation types
    !   and therefore is then not in the same order as
    !   nObsPerSatPerObsType.
    !
    !   For RINEX 3 the observation types of the various GNSS
    !   are somehow merged. This is new behavior to get RINEX 3.
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(IN)    :: obsTypes
    ! Number of satellites
    INTEGER(i4b),                     INTENT(OUT)   :: nSats
    ! Satellite number (SVN) per satellite
    ! svnPerSat(iSat)
    INTEGER(i4b),     DIMENSION(:),   INTENT(OUT)   :: svnPerSat
    ! Number of observations per satellite and per observation type
    ! nObsPerSatPerObsType(iSat, iWrapObsType)
    INTEGER(i4b),     DIMENSION(:,:), INTENT(OUT)   :: nObsPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':extractHdrObsInfoWithoutObsDef'

    ! Local variables
    ! ---------------
    TYPE(t_infoPerSys),  POINTER :: infoPerSys
    TYPE(t_obsTypeDefs), POINTER :: obsTypeDefs
    TYPE(t_obsTypeDef),  POINTER :: obsTypeDef

    CHARACTER(LEN=3) :: rx2ObsType, rx3ObsType
    CHARACTER(LEN=2) :: rx2ObsType23
    CHARACTER(LEN=1) :: sysChar

    INTEGER(i4b) :: iObsTypeDef, iSat, iSat2, iSys, iWrapObsType
    INTEGER(i4b) :: obsTypeHash, nObs, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY (obsTypeDef, obsTypeDefs)

    infoPerSys => rxoHdr%infoPerSys

    nSats = 0
    svnPerSat(:) = 0
    nObsPerSatPerObsType(:,:) = 0

    ! Fill nSats and svnPerSat
    sysLoop2: DO iSys = 0, maxRnxSys - 1
      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      prnLoop2: DO prn = 1, maxRnxPrn

        svn = iSys * 100 + prn

        obsTypeLoop2: DO obsTypeHash = 1, maxObsTypeHash

          nObs = getNbObs(obsTypeDefs, prn, obsTypeHash)

          IF (nObs > 0) THEN
            nSats = nSats + 1
            IF (nSats <= SIZE(svnPerSat)) svnPerSat(nSats) = svn
            CYCLE prnLoop2
          ENDIF

        ENDDO obsTypeLoop2

      ENDDO prnLoop2

    ENDDO sysLoop2

    ! Fill nObsPerSatPerObsType
    sysLoop3: DO iSys = 0, maxRnxSys - 1
      obsTypeDefs => infoPerSys%ar(iSys)%obsTypeDefs

      sysChar = g_svnSys(iSys)

      prnLoop3: DO prn = 1, maxRnxPrn

        svn = iSys * 100 + prn

        iSat = 0

        DO iSat2 = 1, MIN(nSats, SIZE(svnPerSat))
          IF (svnPerSat(iSat2) == svn) THEN
            iSat = iSat2
            EXIT
          ENDIF
        ENDDO

        IF (iSat <= 0) CYCLE prnLoop3
        IF (iSat > SIZE(nObsPerSatPerObsType, 1)) CYCLE prnLoop3

        obsTypeLoop3: DO iObsTypeDef = 1, nb(obsTypeDefs)
          obsTypeDef => obsTypeDefs%ar(iObsTypeDef)

          obsTypeHash = obsTypeDef%obsTypeHash

          IF (obsTypeHash <= 0) CYCLE obsTypeLoop3

          nObs = getNbObs(obsTypeDefs, prn, obsTypeHash)

          IF (nObs <= 0) CYCLE obsTypeLoop3

          rx3ObsType = calcObsTypeFromHash(obsTypeHash)

          rx2ObsType = convertObsTypeR3ToR2( &
            rx3ObsType, rxo2OpMode, rnxVers_2_00, sysChar)

          rx2ObsType23 = rx2ObsType(2:3)

          obsTypeLoop4: DO iWrapObsType = 1, nObsTypes
            IF (obsTypes(iWrapObsType) /= rx2ObsType23) CYCLE obsTypeLoop4
            nObsPerSatPerObsType(iSat, iWrapObsType) = nObs
          ENDDO obsTypeLoop4

        ENDDO obsTypeLoop3
      ENDDO prnLoop3
    ENDDO sysLoop3

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractHdrObsInfoWithoutObsDef

  ! -------------------------------------------------------------------------
  SUBROUTINE extractRecInfo( &
    rxoInpHdlr, maxWrapSat, obsAtEpo, &
    obsEpo, rcvClkOffset, nSatsAtEpo, svnPerSatAtEpo)

    TYPE(t_rxoInpHdlr),          INTENT(IN)  :: rxoInpHdlr
    INTEGER(i4b),                INTENT(IN)  :: maxWrapSat
    TYPE(t_rxoObsAtEpo), TARGET, INTENT(IN)  :: obsAtEpo
    ! Observation epoch (receiver time)
    TYPE(t_epoch),               INTENT(OUT) :: obsEpo
    ! Receiver clock offset
    REAL(r8b),                   INTENT(OUT) :: rcvClkOffset
    ! Number of satellites related to the obs. epoch
    INTEGER(i4b),                INTENT(OUT) :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b), DIMENSION(:),  INTENT(OUT) :: svnPerSatAtEpo
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = modNam // ':extractRecInfo'

    ! Local variables
    TYPE(t_rxoObsPerPrn),  POINTER :: obsPerPrn
    TYPE(t_rxoObsPerType), POINTER :: obsPerType
    TYPE(t_rxoObsOfType),  POINTER :: obsOfType

    CHARACTER(LEN=comprFileNameLen) :: fileName_compr

    CHARACTER(LEN=60) :: antMrkName

    INTEGER(i4b) :: iObsType, iSys, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(obsPerPrn, obsPerType, obsOfType)

    obsEpo       = obsAtEpo%epoch
    rcvClkOffset = obsAtEpo%rcvClkOffs
    nSatsAtEpo   = 0

    sysLoop: DO iSys = 0, maxRnxSys - 1
      obsPerPrn => obsAtEpo%obsPerSys%ar(iSys)

      prnLoop: DO prn = 1, maxRnxPrn
        obsPerType => obsPerPrn%ar(prn)

        svn = iSys * 100 + prn

        obsLoop: DO iObsType = 1, obsPerType%nb
          obsOfType => obsPerType%ar(iObsType)

          IF (obsOfType%obsTypeHash > 0) THEN
            nSatsAtEpo = nSatsAtEpo + 1

            IF (nSatsAtEpo <= SIZE(svnPerSatAtEpo)) THEN
              svnPerSatAtEpo(nSatsAtEpo) = svn
            ENDIF

            EXIT obsLoop
          ENDIF

        ENDDO obsLoop

      ENDDO prnLoop

    ENDDO sysLoop

    IF(nSatsAtEpo > maxWrapSat) THEN

      nSatsAtEpo        = 0
      svnPerSatAtEpo(:) = 0
      antMrkName        = rxoInpHdlr%inpHdrAndStat%hdr%plainData%antMrkName
      fileName_compr    = rxoInpHdlr%rdHdlr%baseRdHdlr%fh%fileName_compr

      CALL wrtSrWarnToLfnErr(srName, &
        'Maximum number of satellites exceeded for observation record.', &
        'Nb. of satellites in epoch : ' // str(nSatsAtEpo), &
        'Maximum number allowed     : ' // str(maxWrapSat), &
        'Station/marker name        : ' // TRIM(antMrkName), &
        'Epoch skipped.', &
        'RINEX observation file name: ' // TRIM(fileName_compr))

    ENDIF

    ! DEBUG: exit
    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractRecInfo

  ! -------------------------------------------------------------------------
  SUBROUTINE extractObsRecDataWithObsDef( &
    gObsDef, nSatsAtEpo, svnPerSatAtEpo, maxWrapObs, obsAtEpo, &
    obsPerSatPerObsType, snrPerSatPerObsType, lliPerSatPerObsType)

    TYPE(t_gObsDef),      TARGET, INTENT(IN)    :: gObsDef
    ! Number of satellites related to the obs. epoch
    INTEGER(i4b),                 INTENT(IN)    :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b), DIMENSION(:),   INTENT(IN)    :: svnPerSatAtEpo
    INTEGER(i4b),                 INTENT(IN)    :: maxWrapObs
    TYPE(t_rxoObsAtEpo),  TARGET, INTENT(IN)    :: obsAtEpo
    ! Observation per satellite per obs. type
    ! obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! The second index goes from 1 to 4.
    REAL(r8b),    DIMENSION(:,:), INTENT(INOUT) :: obsPerSatPerObsType
    ! S/N rate per satellite per obs. type
    ! snrPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! The second index goes from 1 to 4.
    INTEGER(i4b), DIMENSION(:,:), INTENT(INOUT) :: snrPerSatPerObsType
    ! Loss of Lock Indicator per satellite per obs. type
    ! snrPerSatPerObsType(iSatAtEpo, iWrapObsType)
    !
    ! The second index goes from 1 to 4.
    INTEGER(i4b), DIMENSION(:,:), INTENT(INOUT) :: lliPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':extractObsRecDataWithObsDef'

    ! Local variables
    TYPE(t_satSig),        POINTER :: satSig
    TYPE(t_rxoObsPerType), POINTER :: obsPerType
    TYPE(t_rxoObsOfType),  POINTER :: obsOfType

    CHARACTER(LEN=3) :: obsType

    INTEGER(i4b) :: iObsDefRec, iObsType, iSatAtEpo, iSatAtEpo2, iSys, iWrapObsType
    INTEGER(i4b) :: obsTypeHash, prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(satSig, obsPerType, obsOfType)

    obsDefLoop: DO iObsDefRec = 1, gObsDef%noRec
      satSig => gObsDef%sat(iObsDefRec)

      iSys = satSig%sysNum
      prn  = satSig%satNum

      IF (iSys < 0 .OR. iSys >= maxSys) CYCLE obsDefLoop
      IF (prn < 1 .OR. prn >= maxRnxPrn) CYCLE obsDefLoop

      svn = iSys * 100 + prn

      iSatAtEpo = 0

      satLoop: DO iSatAtEpo2 = 1, nSatsAtEpo
        IF (svnPerSatAtEpo(iSatAtEpo2) /= svn) CYCLE satLoop
        iSatAtEpo = iSatAtEpo2
      ENDDO satLoop

      IF (iSatAtEpo <= 0) CYCLE obsDefLoop

      obsPerType => obsAtEpo%obsPerSys%ar(iSys)%ar(prn)

      wrapObsLoop: DO iWrapObsType = &
        1, MIN(maxWrapObs, maxNbWrapObsTypesWithObsDef)

        IF (satSig%obsTyp2(iWrapObsType) == '') CYCLE wrapObsLoop

        obsType = satSig%obsTyp(iWrapObsType)

        IF (obsType == '') CYCLE wrapObsLoop

        obsTypeHash = calcObsTypeHash_internal(obsType)

        IF (obsTypeHash <= 0) CYCLE wrapObsLoop

        obsLoop: DO iObsType = 1, obsPerType%nb
          obsOfType => obsPerType%ar(iObsType)

          IF (obsOfType%obsTypeHash /= obsTypeHash) CYCLE obsLoop

          obsPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%value
          lliPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%lli
          snrPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%snr

        ENDDO obsLoop

      ENDDO wrapObsLoop

    ENDDO obsDefLoop

    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractObsRecDataWithObsDef

  ! -------------------------------------------------------------------------
  SUBROUTINE extractObsRecDataWithoutObsDef( &
    rxo2OpMode, genData, nObsTypes, obsTypes, &
    nSatsAtEpo, svnPerSatAtEpo, maxWrapObs, obsAtEpo, &
    obsPerSatPerObsType, snrPerSatPerObsType, lliPerSatPerObsType)

    ! Operation mode for RINEX 2 observation files.
    TYPE(t_rxo2OpMode),               INTENT(IN)    :: rxo2OpMode
    TYPE(t_rnxGenHdrData),            INTENT(IN)    :: genData
    ! Number of (different) observation types
    INTEGER(i4b),                     INTENT(IN)    :: nObsTypes
    ! List of observation types
    ! If marker type is "SPACEBORNE":
    !   obsTypes(nObsTypes + K) may contain a factor for obsTypes(K)
    !   (2 CHARACTERS HEX DATA).
    !   Therefore, please declarate at least obsTypes(2*nObsTypes)
    CHARACTER(LEN=2), DIMENSION(:),   INTENT(IN)    :: obsTypes
    ! Number of satellites related to the obs. epoch
    INTEGER(i4b),                     INTENT(IN)    :: nSatsAtEpo
    ! Satellite number (SVN) per satellite related to the obs. epoch
    ! svnPerSatAtEpo(iSatAtEpo)
    INTEGER(i4b),     DIMENSION(:),   INTENT(IN)    :: svnPerSatAtEpo
    INTEGER(i4b),                     INTENT(IN)    :: maxWrapObs
    TYPE(t_rxoObsAtEpo),      TARGET, INTENT(IN)    :: obsAtEpo
    ! Observation per satellite per obs. type
    ! obsPerSatPerObsType(iSatAtEpo, iWrapObsType)
    REAL(r8b),        DIMENSION(:,:), INTENT(INOUT) :: obsPerSatPerObsType
    ! S/N rate per satellite per obs. type
    ! snrPerSatPerObsType(iSatAtEpo, iWrapObsType)
    INTEGER(i4b),     DIMENSION(:,:), INTENT(INOUT) :: snrPerSatPerObsType
    ! Loss of Lock Indicator per satellite per obs. type
    ! snrPerSatPerObsType(iSatAtEpo, iWrapObsType)
    INTEGER(i4b),     DIMENSION(:,:), INTENT(INOUT) :: lliPerSatPerObsType
  ! -------------------------------------------------------------------------

    ! Local parameters
    CHARACTER(LEN=*), PARAMETER :: srName = &
      modNam // ':extractObsRecDataWithoutObsDef'

    ! Local variables
    TYPE(t_rxoObsPerPrn),  POINTER :: obsPerPrn
    TYPE(t_rxoObsPerType), POINTER :: obsPerType
    TYPE(t_rxoObsOfType),  POINTER :: obsOfType

    CHARACTER(LEN=3) :: rx2ObsType, rx3ObsType

    CHARACTER(LEN=1) :: sysChar

    INTEGER(i4b) :: iObsType, iSatAtEpo, iSatAtEpo2, iSys, iWrapObsType
    INTEGER(i4b) :: prn, svn

    ! DEBUG: entry
    IF (srfDebug) CALL cpu_enter(srName)

    NULLIFY(obsPerPrn, obsPerType, obsOfType)

    sysLoop: DO iSys = 0, maxRnxSys - 1
      obsPerPrn => obsAtEpo%obsPerSys%ar(iSys)

      sysChar = g_svnSys(iSys)

      prnLoop: DO prn = 1, maxRnxPrn
        obsPerType => obsPerPrn%ar(prn)

        svn = iSys * 100 + prn

        iSatAtEpo = 0

        satLoop: DO iSatAtEpo2 = 1, nSatsAtEpo
          IF (svnPerSatAtEpo(iSatAtEpo2) /= svn) CYCLE satLoop
          iSatAtEpo = iSatAtEpo2
        ENDDO satLoop

        obsLoop: DO iObsType = 1, obsPerType%nb
          obsOfType => obsPerType%ar(iObsType)

          rx3ObsType = calcObsTypeFromHash(obsOfType%obsTypeHash)

          IF (rx3ObsType == '') CYCLE obsLoop

          rx2ObsType = convertObsTypeR3ToR2( &
            rx3ObsType, rxo2OpMode, genData%rnxVers, sysChar)

          IF (rx2ObsType == '') CYCLE obsLoop

          wrapObsLoop: DO iWrapObsType = 1, MIN(maxWrapObs, nObsTypes)
            IF (obsTypes(iWrapObsType) /= rx2ObsType(2:3)) CYCLE wrapObsLoop

            obsPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%value
            lliPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%lli
            snrPerSatPerObsType(iSatAtEpo, iWrapObsType) = obsOfType%values%snr

          ENDDO wrapObsLoop

        ENDDO obsLoop

      ENDDO prnLoop

    ENDDO sysLoop

    IF (srfDebug) CALL cpu_exit(srName)

    RETURN
  END SUBROUTINE extractObsRecDataWithoutObsDef

END MODULE o_rxoWrap
