ParseInfo.java

/* Copyright 2002-2025 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.files.rinex.navigation.parsers;

import org.hipparchus.util.FastMath;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.rinex.navigation.EarthOrientationParameterMessage;
import org.orekit.files.rinex.navigation.IonosphereBDGIMMessage;
import org.orekit.files.rinex.navigation.IonosphereGlonassCdmsMessage;
import org.orekit.files.rinex.navigation.IonosphereKlobucharMessage;
import org.orekit.files.rinex.navigation.IonosphereNavICKlobucharMessage;
import org.orekit.files.rinex.navigation.IonosphereNavICNeQuickNMessage;
import org.orekit.files.rinex.navigation.IonosphereNequickGMessage;
import org.orekit.files.rinex.navigation.IonosphericCorrectionType;
import org.orekit.files.rinex.navigation.KlobucharIonosphericCorrection;
import org.orekit.files.rinex.navigation.NeQuickGIonosphericCorrection;
import org.orekit.files.rinex.navigation.RecordType;
import org.orekit.files.rinex.navigation.RinexNavigation;
import org.orekit.files.rinex.navigation.RinexNavigationHeader;
import org.orekit.files.rinex.navigation.SystemTimeOffsetMessage;
import org.orekit.files.rinex.navigation.parsers.ephemeris.BeidouCnv123Parser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.BeidouD1D2Parser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.GPSCnavParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.GPSLnavParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.GalileoParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.GlonassCdmaParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.GlonassFdmaParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.NavICL1NvParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.NavICLnavParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.QzssCnavParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.QzssLnavParser;
import org.orekit.files.rinex.navigation.parsers.ephemeris.SbasParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.BdgimParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.GlonassCdmsGParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.KlobucharParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.NavICKlobucharParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.NavICNeQuickNParser;
import org.orekit.files.rinex.navigation.parsers.ionosphere.NeQuickGParser;
import org.orekit.files.rinex.utils.ParsingUtils;
import org.orekit.gnss.PredefinedGnssSignal;
import org.orekit.gnss.SatelliteSystem;
import org.orekit.propagation.analytical.gnss.data.BeidouCivilianNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.BeidouLegacyNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.GLONASSFdmaNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.GPSCivilianNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.GPSLegacyNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.NavICL1NvNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.NavICLegacyNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.QZSSCivilianNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage;
import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.time.TimeScales;
import org.orekit.utils.units.Unit;

/** Container for parsing data.
 * @author Bryan Cazabonne
 * @author Luc Maisonobe
 * @since 14.0
 */
public class ParseInfo {

    /** Index of field 1 (not counting variable initial spaces). */
    private static  final int INDEX_1 = 0;

    /** Index of field 2 (not counting variable initial spaces). */
    private static  final int INDEX_2 = 19;

    /** Index of field 3 (not counting variable initial spaces). */
    private static  final int INDEX_3 = 38;

    /** Index of field 4 (not counting variable initial spaces). */
    private static  final int INDEX_4 = 57;

    /** Field length. */
    private static  final int LENGTH = 19;

    /** Name of the data source. */
    private final String name;

    /** Set of time scales for parsing dates. */
    private final TimeScales timeScales;

    /** The corresponding navigation messages file object. */
    private final RinexNavigation file;

    /** Number of initial spaces in messages lines. */
    private int initialSpaces;

    /** Flag indicating header has been completely parsed. */
    private boolean headerParsed;

    /** Ionospheric correction type. */
    private IonosphericCorrectionType ionosphericCorrectionType;

    /** Ionospheric correction time mark. */
    private char timeMark;

    /** Klobuchar α coefficients. */
    private double[] klobucharAlpha;

    /** Klobuchar β coefficients. */
    private double[] klobucharBeta;

    /** Record line parser. */
    private RecordLineParser recordLineParser;

    /** Current line. */
    private String line;

    /** Current global line number. */
    private int lineNumber;

    /** Current line number within the navigation message. */
    private int recordLineNumber;

    /** Constructor, build the ParseInfo object.
     * @param name name of the data source
     * @param timeScales set of time scales for parsing dates
     */
    public ParseInfo(final String name, final TimeScales timeScales) {
        // Initialize default values for fields
        this.name       = name;
        this.timeScales = timeScales;
        this.file       = new RinexNavigation();

        // reset the default values set by header constructor
        this.file.getHeader().setProgramName(null);
        this.file.getHeader().setRunByName(null);
        this.file.getHeader().setCreationDateComponents(null);

    }

    /** Parse a date.
     * @param system satellite system
     * @return parsed date
     * @since 14.0
     */
    public AbsoluteDate parseDate(final SatelliteSystem system) {
        return parseDate(system.getObservationTimeScale().getTimeScale(timeScales));
    }

    /** Parse a date.
     * @param timeScale time scale
     * @return parsed date
     * @since 14.0
     */
    public AbsoluteDate parseDate(final TimeScale timeScale) {
        final int year  = ParsingUtils.parseInt(line, 4, 4);
        final int month = ParsingUtils.parseInt(line, 9, 2);
        final int day   = ParsingUtils.parseInt(line, 12, 2);
        final int hours = ParsingUtils.parseInt(line, 15, 2);
        final int min   = ParsingUtils.parseInt(line, 18, 2);
        final int sec   = ParsingUtils.parseInt(line, 21, 2);
        return new AbsoluteDate(year, month, day, hours, min, sec, timeScale);
    }

    /** Parse field 1 of a message line.
     * @param unit unit to apply
     * @return parsed field
     * @since 14.0
     */
    public double parseDouble1(final Unit unit) {
        return parseDouble(unit, initialSpaces + INDEX_1);
    }

    /** Parse field 1 of a message line.
     * @return parsed field
     * @since 14.0
     */
    public int parseInt1() {
        return parseInt(initialSpaces + INDEX_1);
    }

    /** Parse field 2 of a message line.
     * @param unit unit to apply
     * @return parsed field
     * @since 14.0
     */
    public double parseDouble2(final Unit unit) {
        return parseDouble(unit, initialSpaces + INDEX_2);
    }

    /** Parse field 2 of a message line.
     * @return parsed field
     * @since 14.0
     */
    public int parseInt2() {
        return parseInt(initialSpaces + INDEX_2);
    }

    /** Parse field 3 of a message line.
     * @param unit unit to apply
     * @return parsed field
     * @since 14.0
     */
    public double parseDouble3(final Unit unit) {
        return parseDouble(unit, initialSpaces + INDEX_3);
    }

    /** Parse field 3 of a message line.
     * @return parsed field
     * @since 14.0
     */
    public int parseInt3() {
        return parseInt(initialSpaces + INDEX_3);
    }

    /** Parse field 4 of a message line.
     * @param unit unit to apply
     * @return parsed field
     * @since 14.0
     */
    public double parseDouble4(final Unit unit) {
        return parseDouble(unit, initialSpaces + INDEX_4);
    }

    /** Parse field 4 of a message line.
     * @return parsed field
     * @since 14.0
     */
    public int parseInt4() {
        return parseInt(initialSpaces + INDEX_4);
    }

    /** Parse raw field n of a message line.
     * @param index index of first field character
     * @return parsed field
     */
    private double rawDouble(final int index) {
        return ParsingUtils.parseDouble(line, index, LENGTH);
    }

    /** Parse field n of a message line.
     * @param unit unit to apply
     * @param index index of first field character
     * @return parsed field
     */
    private double parseDouble(final Unit unit, final int index) {
        return unit.toSI(rawDouble(index));
    }

    /** Parse field n of a message line.
     * @param index index of first field character
     * @return parsed field
     */
    private int parseInt(final int index) {
        return (int) FastMath.rint(rawDouble(index));
    }

    /** Parse a comment.
     */
    public void parseComment() {
        ParsingUtils.parseComment(lineNumber, line, file);
    }

    /** Ensure navigation record has been closed.
     */
    public void closePendingRecord() {
        if (recordLineParser != null) {
            recordLineParser.closeRecord(file);
            recordLineParser = null;
        }
    }

    /** Get the file name.
     * @return file name
     */
    public String getName() {
        return name;
    }

    /** Get the time scales.
     * @return time scales
     */
    public TimeScales getTimeScales() {
        return timeScales;
    }

    /** Get the completed file.
     * @return completed file
     */
    public RinexNavigation getCompletedFile() {

        // check the header has been properly parsed
        if (!headerParsed) {
            throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE, name);
        }

        // close the last message
        closePendingRecord();

        return file;

    }

    /** Get the navigation file header.
     * @return navigation file header
     */
    public RinexNavigationHeader getHeader() {
        return file.getHeader();
    }

    /** Set the header parsing indicator.
     * @param headerParsed if true, header has been parsed
     */
    public void setHeaderParsed(final boolean headerParsed) {
        this.headerParsed = headerParsed;
    }

    /** Set the number of initial spaces in messages lines.
     * @param initialSpaces number of initial spaces in messages lines
     */
    public void setInitialSpaces(final int initialSpaces) {
        this.initialSpaces = initialSpaces;
    }

    /** Get the current line.
     * @return current line
     */
    public String getLine() {
        return line;
    }

    /** set the current line.
     * @param line current line
     */
    public void setLine(final String line) {
        this.line = line;
        ++lineNumber;
    }

    /** Get the line number.
     * @return line number
     */
    public int getLineNumber() {
        return lineNumber;
    }

    /** Get the line number within the navigation record.
     * @return line number within the navigation record
     */
    public int getRecordLineNumber() {
        return recordLineNumber;
    }

    /** Set the record line parser.
     * @param recordType retord type
     */
    public void setRecordLineParser(final RecordType recordType) {
        final SatelliteSystem system  = SatelliteSystem.parseSatelliteSystem(ParsingUtils.parseString(line, 6, 1));
        final int             prn     = ParsingUtils.parseInt(line, 7, 2);
        final String          type    = ParsingUtils.parseString(line, 10, 4);
        final String          subtype = ParsingUtils.parseString(line, 15, 4);
        setRecordLineParser(recordType, system, prn, type, subtype);
    }

    /** Set the record line parser.
     * @param recordType retord type
     * @param system satellite system
     * @param prn satellite number
     * @param messageType message type
     * @param subType subtype
     */
    public void setRecordLineParser(final RecordType recordType,
                                    final SatelliteSystem system, final int prn,
                                    final String messageType, final String subType) {

        // Set the line number to 0
        recordLineNumber = 0;

        closePendingRecord();

        recordLineParser = null;
        switch (recordType) {
            case STO:
                recordLineParser = buildStoRecordLineParser(system, prn, messageType, subType);
                break;
            case EOP:
                recordLineParser = buildEopRecordLineParser(system, prn, messageType, subType);
                break;
            case ION:
                recordLineParser = buildIonRecordLineParser(system, prn, messageType, subType);
                break;
            case ORBIT :
                recordLineParser = buildEphRecordLineParser(system, messageType);
                break;
            default :
                // do nothing, handle error after the switch
        }

        if (recordLineParser == null) {
            throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
                                      lineNumber, name, line);
        }

    }

    /** Build a line parser for system time offset record.
     * @param system satellite system
     * @param prn satellite number
     * @param messageType message type
     * @param subType subtype
     * @return built record container
     */
    private RecordLineParser buildStoRecordLineParser(final SatelliteSystem system, final int prn,
                                                      final String messageType, final String subType) {
        return new SystemTimeOffsetParser(this, new SystemTimeOffsetMessage(system, prn,
                                                                            messageType, subType));
    }

    /** Build a line parser for Earth Orientation Parameter record.
     * @param system satellite system
     * @param prn satellite number
     * @param messageType message type
     * @param subType subtype
     * @return built record container
     */
    private RecordLineParser buildEopRecordLineParser(final SatelliteSystem system, final int prn,
                                                      final String messageType, final String subType) {
        return new EarthOrientationParameterParser(this, new EarthOrientationParameterMessage(system, prn,
                                                                                              messageType, subType));
    }

    /** Build a line parser for ionosphere record.
     * @param system satellite system
     * @param prn satellite number
     * @param messageType message type
     * @param subType subtype
     * @return built record container
     */
    private RecordLineParser buildIonRecordLineParser(final SatelliteSystem system, final int prn,
                                                      final String messageType, final String subType) {
        if (system == SatelliteSystem.GALILEO) {
            return new NeQuickGParser(this, new IonosphereNequickGMessage(system, prn, messageType, subType));
        } else if (system == SatelliteSystem.BEIDOU && "CNVX".equals(messageType)) {
            // in Rinex 4.00, tables A32 and A34 (A35 and A37 in Rinex 4.02) are ambiguous
            // as both seem to apply to Beidou CNVX messages; we consider BDGIM is the
            // proper model in this case
            return new BdgimParser(this, new IonosphereBDGIMMessage(system, prn, messageType, subType));
        } else if (system == SatelliteSystem.NAVIC &&
                   NavICL1NvNavigationMessage.L1NV.equals(messageType) &&
                   "KLOB".equals(subType)) {
            return new NavICKlobucharParser(this, new IonosphereNavICKlobucharMessage(system, prn, messageType, subType));
        } else if (system == SatelliteSystem.NAVIC &&
                   NavICL1NvNavigationMessage.L1NV.equals(messageType) &&
                  "NEQN".equals(subType)) {
            return new NavICNeQuickNParser(this, new IonosphereNavICNeQuickNMessage(system, prn, messageType, subType));
        } else if (system == SatelliteSystem.GLONASS) {
            return new GlonassCdmsGParser(this, new IonosphereGlonassCdmsMessage(system, prn, messageType, subType));
        } else  {
            return new KlobucharParser(this, new IonosphereKlobucharMessage(system, prn, messageType, subType));
        }
    }

    /** Build a line parser for ephemeris records.
     * @param system satellite system
     * @param messageType message type
     * @return record parser for ephemeris message
     */
    private RecordLineParser buildEphRecordLineParser(final SatelliteSystem system, final String messageType) {
        switch (system) {
            case GPS:
                if (messageType == null || messageType.equals(GPSLegacyNavigationMessage.LNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new GPSLnavParser(this,
                                             new GPSLegacyNavigationMessage(timeScales,
                                                                            SatelliteSystem.GPS,
                                                                            GPSLegacyNavigationMessage.LNAV));
                } else if (messageType.equals(GPSCivilianNavigationMessage.CNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new GPSCnavParser(this,
                                             new GPSCivilianNavigationMessage(false,
                                                                              timeScales,
                                                                              SatelliteSystem.GPS,
                                                                              GPSCivilianNavigationMessage.CNAV));
                } else if (messageType.equals(GPSCivilianNavigationMessage.CNV2)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new GPSCnavParser(this,
                                             new GPSCivilianNavigationMessage(true,
                                                                              timeScales,
                                                                              SatelliteSystem.GPS,
                                                                              GPSCivilianNavigationMessage.CNV2));
                }
                break;
            case GALILEO:
                if (messageType == null || messageType.equals(GalileoNavigationMessage.INAV) || messageType.equals(
                    GalileoNavigationMessage.FNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new GalileoParser(this, new GalileoNavigationMessage(timeScales,
                                                                                SatelliteSystem.GPS,
                                                                                messageType));
                }
                break;
            case GLONASS:
                if (messageType == null || messageType.equals("FDMA")) {
                    return new GlonassFdmaParser(this, new GLONASSFdmaNavigationMessage());
                } else if (messageType.equals("L1OC") || messageType.equals("L3OC")) {
                    return new GlonassCdmaParser(this);
                }
                break;
            case QZSS:
                if (messageType == null || messageType.equals(QZSSLegacyNavigationMessage.LNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new QzssLnavParser(this,
                                              new QZSSLegacyNavigationMessage(timeScales,
                                                                              SatelliteSystem.GPS,
                                                                              QZSSLegacyNavigationMessage.LNAV));
                } else if (messageType.equals(QZSSCivilianNavigationMessage.CNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new QzssCnavParser(this,
                                              new QZSSCivilianNavigationMessage(false,
                                                                                timeScales,
                                                                                SatelliteSystem.GPS,
                                                                                QZSSCivilianNavigationMessage.CNAV));
                } else if (messageType.equals(QZSSCivilianNavigationMessage.CNV2)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new QzssCnavParser(this,
                                              new QZSSCivilianNavigationMessage(true,
                                                                                timeScales,
                                                                                SatelliteSystem.GPS,
                                                                                QZSSCivilianNavigationMessage.CNV2));
                }
                break;
            case BEIDOU:
                if (messageType == null || messageType.equals(BeidouLegacyNavigationMessage.D1) || messageType.equals(
                    BeidouLegacyNavigationMessage.D2)) {
                    // in Rinex, week number for Beidou is really aligned to Beidou week!
                    return new BeidouD1D2Parser(this,
                                                new BeidouLegacyNavigationMessage(timeScales,
                                                                                  SatelliteSystem.BEIDOU,
                                                                                  messageType));
                } else if (messageType.equals(BeidouCivilianNavigationMessage.CNV1)) {
                    // in Rinex, week number for Beidou is really aligned to Beidou week!
                    return new BeidouCnv123Parser(this,
                                                  new BeidouCivilianNavigationMessage(PredefinedGnssSignal.B1C,
                                                                                      timeScales,
                                                                                      SatelliteSystem.BEIDOU,
                                                                                      BeidouCivilianNavigationMessage.CNV1));
                } else if (messageType.equals(BeidouCivilianNavigationMessage.CNV2)) {
                    // in Rinex, week number for Beidou is really aligned to Beidou week!
                    return new BeidouCnv123Parser(this,
                                                  new BeidouCivilianNavigationMessage(PredefinedGnssSignal.B2A,
                                                                                      timeScales,
                                                                                      SatelliteSystem.BEIDOU,
                                                                                      BeidouCivilianNavigationMessage.CNV2));
                } else if (messageType.equals(BeidouCivilianNavigationMessage.CNV3)) {
                    // in Rinex, week number for Beidou is really aligned to Beidou week!
                    return new BeidouCnv123Parser(this,
                                                  new BeidouCivilianNavigationMessage(PredefinedGnssSignal.B2B,
                                                                                      timeScales,
                                                                                      SatelliteSystem.BEIDOU,
                                                                                      BeidouCivilianNavigationMessage.CNV3));
                }
                break;
            case NAVIC:
                if (messageType == null || messageType.equals(NavICLegacyNavigationMessage.LNAV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new NavICLnavParser(this,
                                               new NavICLegacyNavigationMessage(timeScales,
                                                                                SatelliteSystem.GPS,
                                                                                NavICLegacyNavigationMessage.LNAV));
                } else if (messageType.equals(NavICL1NvNavigationMessage.L1NV)) {
                    // in Rinex, week number is aligned to GPS week!
                    return new NavICL1NvParser(this,
                                               new NavICL1NvNavigationMessage(timeScales,
                                                                              SatelliteSystem.GPS,
                                                                              NavICL1NvNavigationMessage.L1NV));
                }
                break;
            case SBAS:
                if (messageType == null || messageType.equals("SBAS")) {
                    return new SbasParser(this, new SBASNavigationMessage());
                }
                break;
            default:
                // do nothing, handle error after the switch
        }

        // no parse could be set up
        return null;

    }

    /** Get the message line parser.
     * @return message line parser
     */
    public RecordLineParser getRecordLineParser() {
        return recordLineParser;
    }

    /** Parse next record line.
     */
    public void parseRecordLine() {
        switch (++recordLineNumber) {
            case 1: recordLineParser.parseLine01();
            break;
            case 2: recordLineParser.parseLine02();
            break;
            case 3: recordLineParser.parseLine03();
            break;
            case 4: recordLineParser.parseLine04();
            break;
            case 5: recordLineParser.parseLine05();
            break;
            case 6: recordLineParser.parseLine06();
            break;
            case 7: recordLineParser.parseLine07();
            break;
            case 8: recordLineParser.parseLine08();
            break;
            case 9: recordLineParser.parseLine09();
            break;
            default:
                // this should never happen
                throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
                                          lineNumber, name, line);
        }
    }

    /**
     * Set the ionospheric correction type.
     * @param ionosphericCorrectionType ionospheric correction type
     * @since 14.0
     */
    public void setIonosphericCorrectionType(final IonosphericCorrectionType ionosphericCorrectionType) {
        this.ionosphericCorrectionType = ionosphericCorrectionType;
    }

    /**
     * Set the ionospheric correction time mark.
     * @param timeMark ionospheric correction time mark
     * @since 14.0
     */
    public void setTimeMark(final char timeMark) {
        this.timeMark = timeMark;
    }

    /**
     * Set the α ionospheric parameters.
     * @param klobucharAlpha the α ionospheric parameters to set
     */
    public void setKlobucharAlpha(final double[] klobucharAlpha) {
        this.klobucharAlpha = klobucharAlpha.clone();
        setKlobucharIfComplete();
    }

    /**
     * Set the β ionospheric parameters.
     * @param klobucharBeta the β ionospheric parameters to set
     */
    public void setKlobucharBeta(final double[] klobucharBeta) {
        this.klobucharBeta = klobucharBeta.clone();
        setKlobucharIfComplete();
    }

    /** Add the Klobuchar correction if it is complete.
     */
    private void setKlobucharIfComplete() {
        if (klobucharAlpha != null && klobucharBeta != null) {
            file.getHeader().addIonosphericCorrection(new KlobucharIonosphericCorrection(ionosphericCorrectionType,
                                                                                         timeMark,
                                                                                         klobucharAlpha,
                                                                                         klobucharBeta));
            ionosphericCorrectionType = null;
            timeMark                  = 0;
            klobucharAlpha            = null;
            klobucharBeta             = null;
        }
    }

    /**
     * Set the α ionospheric parameters.
     * @param neQuickAlpha the α ionospheric parameters to set
     */
    public void setNeQuickAlpha(final double[] neQuickAlpha) {
        file.getHeader().addIonosphericCorrection(new NeQuickGIonosphericCorrection(ionosphericCorrectionType,
                                                                                    timeMark,
                                                                                    neQuickAlpha));
        ionosphericCorrectionType = null;
        timeMark                  = 0;
    }

}