1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 /// @file AntexData.hpp
40 /// Encapsulate data from ANTEX (Antenna Exchange) format files, including both
41 /// receiver and satellite antennas, ANTEX file I/O, discrimination between different
42 /// satellite antennas based on system, PRN and time, and computation of phase center
43 /// offsets and variations.
44 
45 #ifndef ANTEX_DATA_HPP
46 #define ANTEX_DATA_HPP
47 
48 #include <string>
49 #include <vector>
50 #include <map>
51 
52 #include "AntexBase.hpp"
53 #include "FFStream.hpp"
54 #include "CommonTime.hpp"
55 #include "Triple.hpp"
56 #include "CivilTime.hpp"
57 
58 namespace gpstk
59 {
60    /// @ingroup Antex
61    //@{
62 
63       /// Antex antenna data record: PCOs and PCVs for one antenna.
64       /// Do not attempt to use an object that is not valid (cf. isValid()).
65       ///
66       /// NB. Optional data should be accessed only if the corresponding 'valid'
67       /// string is true; e.g. if(valid & validFromValid) then validFrom may be used.
68       ///
69       /// NB. In calls to the 'get' routines,
70       ///        freq = string("G01");
71       ///        double total_PCO = getTotalPhaseCenterOffset(freq, az, el_nad);
72       ///        Triple PCO = getPhaseCenterOffset(freq);
73       ///        double PCV = getPhaseCenterVariation(freq, az, el_nad);
74       /// receivers and satellites (transmitters) are treated differently, in that
75       /// receivers call with elevation angle (from North-East plane toward Up) while
76       /// satellites call with nadir angle (from Z axis - the bore-sight direction).
77       ///
78       /// NB. The return value of getPhaseCenterOffset is a vector (Triple) PCO, in
79       /// the appropriate coordinate system (NEU for Rx, XYZ or body for SV), that is
80       /// defined as the vector from the reference point (RP) to the actual phase
81       /// center (PC). The RP is the ARP (Antenna Reference Point) for receivers and
82       /// the COM (Center Of Mass) for satellites.
83       ///
84       /// NB. The PCV and total_PCO value returned by the other two routines has the
85       /// same sense as the PCO vector; that is the total offset is defined as
86       ///   PCO vector - PCV * LOS
87       /// where LOS is a unit vector along the line of sight (defined by the azimuth
88       /// and elevation/nadir angle passed into the routines), all in the appropriate
89       /// coordinate system (receiver NEU or satellite body XYZ).
90       ///
91       /// NB. Thus when correcting a measured range for the Receiver's total phase
92       /// center offset one would subtract the total_PCO value (along the line of
93       /// sight) from the measured range, that is (scalar equation, millimeters):
94       ///   Range(corr) = Range(meas) + total_PCO; // OR
95       ///                                -->     -->
96       ///   Range(corr) = Range(meas) + [PCO dot LOS - PCV];
97       /// however when correcting the satellite (COM) position for the Satellite's
98       /// total phase center offset one would add the total vector offset
99       /// (PCO - PCV*LOS) to the satellite COM (vector) position (in a consistent
100       /// coordinate system, e.g. ECEF XYZ), that is (vector equation):
101       ///   -->        -->        -->         -->
102       ///   SV(corr) = SV(COM) + [PCO - PCV * LOS]
103       ///
104       /// NB. the PCV data is stored in a map <zenith angle, value> and the
105       /// getPhaseCenterVariation() routine simply interpolates this map WITHOUT
106       /// changing the sign of the value - it is the same as that in the ANTEX file.
107       ///
108       /// @sa gpstk::AntexStream and gpstk::AntexHeader.
109    class AntexData : public AntexBase
110    {
111    public:
112       /// @name AntexDataFormatStrings
113       /// ANTEX Data Formatting Strings
114       //@{
115       static const std::string startAntennaString;    ///< "START OF ANTENNA"
116       static const std::string typeSerNumString;      ///< "TYPE / SERIAL NO"
117       static const std::string methodString;          ///< "METH / BY / # / DATE"
118       static const std::string daziString;            ///< "DAZI"
119       static const std::string zenithString;          ///< "ZEN1 / ZEN2 / DZEN"
120       static const std::string numFreqString;         ///< "# OF FREQUENCIES"
121       static const std::string validFromString;       ///< "VALID FROM"
122       static const std::string validUntilString;      ///< "VALID UNTIL"
123       static const std::string sinexCodeString;       ///< "SINEX CODE"
124       static const std::string dataCommentString;     ///< "COMMENT"
125       static const std::string startFreqString;       ///< "START OF FREQUENCY"
126       static const std::string neuFreqString;         ///< "NORTH / EAST / UP"
127       static const std::string endOfFreqString;       ///< "END OF FREQUENCY"
128       static const std::string startFreqRMSString;    ///< "START OF FREQ RMS"
129       static const std::string neuFreqRMSString;      ///< "NORTH / EAST / UP"
130       static const std::string endOfFreqRMSString;    ///< "END OF FREQ RMS"
131       static const std::string endOfAntennaString;    ///< "END OF ANTENNA"
132       //@}
133 
134          /// Validity bits for the ANTEX Data
135          /// NB. if version is updated, add allValid<ver> and update isValid()
136       enum validBits
137       {
138          startAntennaValid = 0x00001,  ///< "START OF ANTENNA"       Required
139          typeSerNumValid   = 0x00002,  ///< "TYPE / SERIAL NO"       Required
140          methodValid       = 0x00004,  ///< "METH / BY / # / DATE"   Required
141          daziValid         = 0x00008,  ///< "DAZI"                   Required
142          zenithValid       = 0x00010,  ///< "ZEN1 / ZEN2 / DZEN"     Required
143          numFreqValid      = 0x00020,  ///< "# OF FREQUENCIES"       Required
144          validFromValid    = 0x00040,  ///< "VALID FROM"
145          validUntilValid   = 0x00080,  ///< "VALID UNTIL"
146          sinexCodeValid    = 0x00100,  ///< "SINEX CODE"
147          dataCommentValid  = 0x00200,  ///< "COMMENT"
148          startFreqValid    = 0x00400,  ///< "START OF FREQUENCY"     Required
149          neuFreqValid      = 0x00800,  ///< "NORTH / EAST / UP"      Required
150          endOfFreqValid    = 0x01000,  ///< "END OF FREQUENCY"       Required
151          startFreqRMSValid = 0x02000,  ///< "START OF FREQ RMS"
152          neuFreqRMSValid   = 0x04000,  ///< "NORTH / EAST / UP"
153          endOfFreqRMSValid = 0x08000,  ///< "END OF FREQ RMS"
154          endOfAntennaValid = 0x10000,  ///< "END OF ANTENNA"         Required
155          allValid13        = 0x11C3F   ///< mask for all required valid fields
156       };
157 
158       /// Values of 'type' that are satellites
159       /// NB. keep this updated from the IGS file 'rcvr_ant.tab'
160       static const std::vector<std::string> SatelliteTypes;
161 
162       /// map from zenith angle (degrees) to PC offset (millimeters)
163       typedef std::map<double, double> zenOffsetMap;
164 
165       /// map from azimuth angle (deg) to zenOffsetMap
166       /// the zenOffsetMap WITHOUT azimuth dependence (NOAZI) will be
167       /// azimZenMap[-1.0] (this may be the only entry)
168       typedef std::map<double, zenOffsetMap> azimZenMap;
169 
170       /// class encapsulating the PCOs and PCVs of the antenna. See the ANTEX
171       /// documentation for discussion of how the PCO/Vs are defined, sign conventions
172       /// and how to apply the PCOs.
173       class antennaPCOandPCVData {
174       public:
175          /// nominal phase center offsets in mm, and RMS values,
176          /// in NEU coordinates (for Receiver antennas)
177          /// or XYZ (for Satellite antennas); from "NORTH / EAST / UP" record
178          /// RMS values are OPTIONAL
179          double PCOvalue[3],PCOrms[3];
180 
181          /// if false, there is no azimuth dependence in the PCVs
182          /// and only PCV[0.0] is defined.
183          bool hasAzimuth;
184 
185          /// map from azimuth to <zenith,offset> map:
186          /// PCVvalues[azim][zen] = offset in mm from the nominal
187          /// PCVrms[azim][zen] = RMS of these values, also in mm.
188          /// if there is no azimuth dependence, there will be
189          /// only one entry in this map, with azimuth = -1.0
190          /// RMS values are OPTIONAL
191          azimZenMap PCVvalue, PCVrms;
192 
193       }; // end of class antennaPCOandPCVData
194 
195       // member data
196       /// Bits of valid are set when corresponding labels are found and data defined
197       unsigned long valid;
198 
199       /// if true, PCOs are absolute, else they are relative to another antenna
200       bool absolute;
201 
202       /// if true, this is a receiver antenna, otherwise its a satellite;
203       /// this flag is set based on the IGS codes kept in array SatelliteTypes.
204       /// NB. this flag need not be used, if you know which antenna you have;
205       ///     however if used, the array SatelliteTypes must be kept updated.
206       bool isRxAntenna;
207 
208       /// PRN and SVN numbers; used only in the case of satellite antennas, and
209       /// may not be present, in which case these are both -1.
210       /// NB. PRNs apply to GLONASS as well as GPS
211       int PRN, SVN;
212 
213       /// system character: G or blank GPS, R GLONASS, E GALILEO, etc
214       /// used only in the case of satellite antennas
215       char systemChar;
216 
217       /// number of frequencies stored, equal to number of keys in map
218       /// from "# OF FREQUENCIES" record
219       unsigned int nFreq;
220 
221       /// delta azimuth (degrees) stored in azimZenMap
222       /// equal to 0 if there is no azimuth dependence
223       /// from "DAZI" record
224       double azimDelta;
225 
226       /// minimum, maximum and delta zenith (degrees) stored in zenOffsetMap
227       /// from "ZEN1 / ZEN2 / DZEN" record
228       double zenRange[3];
229 
230       /// time limits of validity (OPTIONAL); otherwise set to BEGINNING and END
231       /// from "VALID FROM" and "VALID UNTIL" records
232       /// keep the string version for file I/O b/c sometimes the time is of the form
233       /// 1994     4    17    23    59   59.9999999                 VALID UNTIL
234       /// and converting this to CommonTime replaces it with ... 24 0 0.000
235       CommonTime validFrom,validUntil;
236       std::string stringValidFrom, stringValidUntil;
237 
238       /// map from frequency to antennaPCOandPCVData
239       std::map<std::string, antennaPCOandPCVData> freqPCVmap;
240 
241       std::string type;     ///< antenna type from "TYPE / SERIAL NO"
242       std::string serialNo; ///< antenna serial number from "TYPE / SERIAL NO"
243       std::string satCode;  ///< satellite code from "TYPE / SERIAL NO"
244       std::string cospar;   ///< satellite COSPAR ID from "TYPE / SERIAL NO"
245       std::string method;   ///< calibration method from "METH / BY / # / DATE"
246       std::string agency;   ///< agency from "METH / BY / # / DATE"
247       int noAntCalibrated;  ///< num. of ant. calibrated from "METH / BY / # / DATE"
248       std::string date;     ///< date from "METH / BY / # / DATE"
249       std::string sinexCode;///< name of ant. cal. model from "SINEX CODE" OPTIONAL
250 
251       /// comments found in the data portion of the file
252       std::vector<std::string> commentList;  ///< Comments in data (OPTIONAL)
253 
254       //------------------------------------------------------------------------------
255       // member functions
256 
257       /// Constructor.
AntexData()258       AntexData() : valid(0), absolute(true), PRN(0), SVN(0), nFreq(0),
259                     validFrom(CommonTime::BEGINNING_OF_TIME),
260                     validUntil(CommonTime::END_OF_TIME) {}
261       /// Destructor
~AntexData()262       virtual ~AntexData() {}
263 
264       /// AntexData is a "data", so this function always returns true.
isData() const265       virtual bool isData() const {return true;}
266 
267       /// Convenience function returns true only if a valid object
isValid(void) const268       bool isValid(void) const { return ((valid & allValid13) == allValid13); }
269 
270       /// @return true if the antenna object is valid at the given time.
271       /// Base on the 'validFrom' and 'validUntil' fields.
272       /// @return true if the input time is either BEGINNING_ or END_OF_TIME
273       /// @return true if the 'valid' time limits are not given.
274       /// NB. useful when adding satellite antennas for processing with a dataset;
275       /// pass any time tag from the dataset.
276       bool isValid(CommonTime& time) const throw();
277 
278       /// Generate a name from type and serial number
279       std::string name(void) const throw();
280 
281       /// Compute the total phase center offset at the given azimuth and elev_nadir,
282       /// including both nominal offset (PCO) and variation (PCV).
283       /// NB. see documentation of the class for coordinates, signs and application.
284       /// @param freq frequency e.g. G01
285       /// @param azimuth the azimuth angle in degrees, from N going toward E for
286       ///        receivers, or from X going toward Y for satellites
287       /// @param elev_nadir elevation in deg from horizontal (North-East) plane for
288       //         receivers, or nadir angle in degrees from Z axis for satellites
289       /// @return total phase center offset in millimeters
290       /// @throw Exception if this object is invalid
291       ///         if frequency does not exist for this data
292       ///         if azimuth is out of range; azimuth is replaced with azim mod 360
293       double getTotalPhaseCenterOffset(const std::string freq,
294                                        const double azimuth,
295                                        const double elevation) const;
296 
297       /// Get the PC offset values in mm (only, NOT the phase center variations, which
298       /// should be computed using getPhaseCenterVariations() and added to the PCOs
299       /// to get the total phase center offset).
300       /// NB. see documentation of the class for coordinates, signs and application.
301       /// @param freq frequency (usually G01 or G02)
302       /// @return Triple containing offsets in millimeters, in appropriate coordinate
303       ///                system (satellite-based XYZ or receiver-based NEU).
304       /// @throw Exception if this object is invalid
305       ///         if frequency does not exist for this data
306       Triple getPhaseCenterOffset(const std::string freq) const;
307 
308       /// Compute the phase center variation at the given azimuth and elev_nadir
309       /// NB. see documentation of the class for coordinates, signs and application.
310       /// @param freq frequency (usually G01 or G02)
311       /// @param azimuth the azimuth angle in degrees, from N going toward E for
312       ///        receivers, or from X going toward Y for satellites
313       /// @param elev_nadir elevation in deg from horizontal (North-East) plane for
314       //         receivers, or nadir angle in degrees from Z axis for satellites
315       /// @return phase center offset in millimeters
316       /// @throw Exception if this object is invalid
317       ///         if frequency does not exist for this data
318       ///         if azimuth is out of range, azimuth is replaced with azim % 360
319       double getPhaseCenterVariation(const std::string freq,
320                                      const double azimuth,
321                                      const double elev_nadir) const;
322 
323       /// Dump AntexData. Set detail = 0 for type, serial no., sat codes only;
324       /// = 1 for all information except phase center offsets, = 2 for all data.
325 #pragma clang diagnostic push
326 #pragma clang diagnostic ignored "-Woverloaded-virtual"
327       virtual void dump(std::ostream& s, int detail=0) const;
328 #pragma clang diagnostic pop
329    protected:
330       /// Find zenith angles bracketing the input zenith angle within the given map,
331       /// and the corresponding PCOs.
332       void evaluateZenithMap(const double& zen,
333                              const zenOffsetMap& eomap,
334                              double& zen_lo, double& zen_hi,
335                              double& pco_lo, double& pco_hi) const throw();
336 
337          /** Writes a correctly formatted record from this data to stream \a s.
338           * @throw std::exception
339           * @throw FFStreamError
340           * @throw StringUtils::StringException
341           */
342       virtual void reallyPutRecord(FFStream& s) const;
343 
344       /// This functions obtains Antex antenna record from the given FFStream.
345       /// If there is an error in reading from the stream, it is reset
346       /// to its original position and its fail-bit is set.
347       /// @throw std::exception
348       /// @throw StringException when a StringUtils function fails
349       /// @throw FFStreamError when exceptions(failbit) is set and
350       ///  a read or formatting error occurs.  This also resets the
351       ///  stream to its pre-read position.
352       virtual void reallyGetRecord(FFStream& s);
353 
354    private:
355       /// helper routine to throw when records are out of order
356       /// throws if valid contains test (test & valid), otherwise does nothing
357       void throwRecordOutOfOrder(unsigned long test, std::string& label);
358 
359          /** parse a line from the Antex file, filling the data object
360           * @throw FFStreamError
361           */
362       void ParseDataRecord(std::string& line);
363 
364       /// Writes the CommonTime object into Antex ('VALID FROM') format.
365       /// If it's a bad time, it will return blanks.
366       /// @throw StringUtils::StringException
367       std::string writeTime(const CommonTime& dt) const;
368 
369       /// This function constructs a CommonTime object from the line for VALID FROM
370       /// and VALID UNTIL records; default is to return BEGINNING_OF_TIME
371       /// @param line the encoded time string found in the Antex record.
372       /// @throw FFStreamError
373       CommonTime parseTime(const std::string& line) const;
374 
375    }; // class AntexData
376 
377    //@}
378 
379 } // namespace
380 
381 #endif
382