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 #include "AshtechMBEN.hpp"
40 #include "AshtechStream.hpp"
41 #include "GNSSconstants.hpp"
42 
43 using namespace std;
44 
45 namespace gpstk
46  {
47    const char* AshtechMBEN::mpcId = "MPC";
48    const char* AshtechMBEN::mcaId = "MCA";
49 
50 
51    //---------------------------------------------------------------------------
reallyGetRecord(FFStream & ffs)52    void AshtechMBEN::reallyGetRecord(FFStream& ffs)
53    {
54       AshtechStream& stream=dynamic_cast<AshtechStream&>(ffs);
55 
56       // make sure the object is reset before starting the search
57       clear(fmtbit | lenbit | crcbit);
58       string& rawData = stream.rawData;
59 
60       // If this object doesn't have an id set yet, assume that the streams
61       // most recent read id is what we need to be
62       if (id == "" && rawData.size()>=11 &&
63           rawData.substr(0,7) == preamble &&
64           rawData[10]==',')
65          id = rawData.substr(7,3);
66 
67       // If that didn't work, or this is object is not of the right type,
68       // then give up.
69       if (id == "" || !checkId(id))
70          return;
71 
72       readBody(stream);
73    }
74 
75 
76    //---------------------------------------------------------------------------
decode(const std::string & data)77    void AshtechMBEN::decode(const std::string& data)
78    {
79       using gpstk::BinUtils::decodeVar;
80 
81       string str(data);
82 
83       uint8_t csum=0;
84       if (str.length() == 108 || str.length()==52)
85       {
86          ascii=false;
87          header = str.substr(0,11); str.erase(0,11);
88 
89          seq    = decodeVar<uint16_t>(str);
90          left   = decodeVar<uint8_t>(str);
91          svprn  = decodeVar<uint8_t>(str);
92          el     = decodeVar<uint8_t>(str);
93          az     = decodeVar<uint8_t>(str);
94          chid   = decodeVar<uint8_t>(str);
95 
96          ca.decodeBIN(str);
97 
98          if (id == mpcId)
99          {
100             p1.decodeBIN(str);
101             p2.decodeBIN(str);
102          }
103 
104          checksum = decodeVar<uint8_t>(str);
105 
106          clear();
107 
108          int end = data.size() - 3;
109          for (int i=11; i<end; i++)
110             csum ^= data[i];
111       }
112       else
113       {
114          ascii=true;
115          header = str.substr(0,11); str.erase(0,11);
116          stringstream iss(str);
117          char c;
118          iss >> seq >> c
119              >> left >> c
120              >> svprn >> c
121              >> el >> c
122              >> az >> c
123              >> chid >> c;
124 
125          ca.decodeASCII(iss);
126 
127          if (id == mpcId)
128          {
129             p1.decodeASCII(iss);
130             p2.decodeASCII(iss);
131          }
132 
133          iss >> checksum;
134 
135          if (iss)
136             clear();
137 
138          int end=data.rfind(',');
139          for (int i=11; i<=end; i++)
140             csum ^= data[i];
141       }
142 
143 
144       if (csum != checksum)
145       {
146          setstate(crcbit);
147          if (debugLevel)
148             cout << "checksum error, computed:" << hex << (uint16_t) csum
149                  << " received:" << checksum << dec << endl;
150       }
151 
152       if (seq>36000)
153          setstate(fmtbit);
154    }
155 
156 
157    //---------------------------------------------------------------------------
decodeASCII(stringstream & str)158    void AshtechMBEN::code_block::decodeASCII(stringstream& str)
159    {
160       char c;
161       str >> warning >> c
162           >> goodbad >> c
163           >> polarity_known>> c
164           >> ireg >> c
165           >> qa_phase >> c
166           >> full_phase >> c
167           >> raw_range >> c
168           >> doppler >> c
169           >> smoothing >> c
170           >> smooth_cnt >> c;
171 
172       // The ashtech docs say this field should be in 1e-4 Hz
173       // The data sure doesn't look like it, however
174       //doppler *= 1e-4;
175       raw_range *= 1e-3; //convert ms to sec
176    }
177 
178 
179    //---------------------------------------------------------------------------
decodeBIN(string & str)180    void AshtechMBEN::code_block::decodeBIN(string& str)
181    {
182       using gpstk::BinUtils::decodeVar;
183       uint32_t smo;
184       warning        = decodeVar<uint8_t>(str);
185       goodbad        = decodeVar<uint8_t>(str);
186       polarity_known = decodeVar<uint8_t>(str);
187       ireg           = decodeVar<uint8_t>(str);
188       qa_phase       = decodeVar<uint8_t>(str);
189       full_phase     = decodeVar<double>(str);
190       raw_range      = decodeVar<double>(str);
191       doppler        = decodeVar<int32_t>(str);
192       smo            = decodeVar<uint32_t>(str);
193 
194       doppler *= 1e-4;
195       smoothing = (smo & 0x800000 ? -1e-3 : 1e-3) * (smo & 0x7fffff);
196       smooth_cnt = (smo >> 24) & 0xff;
197    }
198 
199 
200    //---------------------------------------------------------------------------
dump(ostream & out) const201    void AshtechMBEN::code_block::dump(ostream& out) const
202    {
203       using gpstk::StringUtils::asString;
204       out << hex
205           << "warn:" << (int)warning
206           << " gb:" << (int)goodbad
207           << " pol:" << (int)polarity_known
208           << dec
209           << " ireg:" << (int)ireg
210           << " qa:" << (int)qa_phase
211           << " phase:" << asString(full_phase, 1)
212           << " range:" << asString(raw_range*1e3, 3)
213           << " doppler:" << doppler
214           << " smo:" << smoothing
215           << " smo_cnt:" << smooth_cnt;
216    }
217 
218 
219    //---------------------------------------------------------------------------
snr(float chipRate,float m) const220    float AshtechMBEN::code_block::snr(float chipRate, float m) const throw()
221    {
222       const float n = 20000;     // number of samples in 1 ms
223       const float bw = 0.9 * chipRate; // equivalent noise bandwidth (Hz)
224 
225       const float d = PI/(n*n*m*m*4.0);
226       float snr=0;
227       // Note that ireg is as output from the MBEN which is 0-255, not the 0-99
228       // as displayed on the front panel.
229       if (ireg)
230       {
231          snr = exp(((float)ireg)/25.0);
232          snr = snr*snr*bw*d;
233          snr = 10 * log10(snr);
234       }
235 
236       return snr;
237    }
238 
239    //---------------------------------------------------------------------------
dump(ostream & out) const240    void AshtechMBEN::dump(ostream& out) const throw()
241    {
242       ostringstream oss;
243       using gpstk::StringUtils::asString;
244 
245       AshtechData::dump(oss);
246       oss << getName() << "1:"
247           << " seq:" << 0.05 * seq
248           << " left:" << (int)left
249           << " prn:" << (int)svprn
250           << " el:" << (int)el
251           << " az:" << (int)az
252           << " chid:" << (int)chid
253           << " " << (ascii?"ascii":"bin")
254           << endl;
255 
256       oss << getName() << "2: ca ";
257       ca.dump(oss);
258       oss << endl;
259 
260       if (id == mpcId)
261       {
262          oss << getName() << "3: p1 ";
263          p1.dump(oss);
264          oss << endl;
265          oss << getName() << "4: p2 ";
266          p2.dump(oss);
267          oss << endl;
268       }
269       out << oss.str() << flush;
270    }
271 } // namespace gpstk
272