1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
/***********************************************/
/**
* @file gnssSimulateReceiver.cpp
*
* @brief GNSS receiver simulation.
*
* @author Sebastian Strasser
* @author Torsten Mayer-Gürr
* @date 2014-09-25
*/
/***********************************************/
// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
This program simulates observations from receivers to GNSS satellites.
These simulated observations can then be used in \program{GnssProcessing}, for example to conduct closed-loop simulations.
One or more GNSS constellations must be defined via \configClass{transmitter}{gnssTransmitterGeneratorType}.
Receivers such as ground station networks or Low Earth Orbit (LEO) satellites can be defined via \configClass{receiver}{gnssReceiverGeneratorType}.
If multiple receivers defined an \configFile{outputfileGnssReceiver}{instrument} and \configFile{outputfileClock}{instrument}
are written for each single receiver with the \reference{variable}{general.parser} \verb|{station}| being replaced by the receiver name.
A list of simulated observation types can be defined via \configClass{observationType}{gnssType}. Noise can be added to both observations and clock errors
via \configClass{noiseObervation}{noiseGeneratorType} and \configClass{noiseClockReceiver}{noiseGeneratorType}, respectively. Observation noise is
interpreted as a factor that is multiplied to the accuracy derived from the accuracy pattern of the respective observation type
(see \configFile{inputfileAccuracyDefinition}{gnssAntennaDefinition} in \configClass{receiver}{gnssReceiverGeneratorType}).
The \configClass{parametrization}{gnssParametrizationType} are used to simulate a priori models (e.g. troposphere, signal biases).
Parameter settings and outputfiles are ignored.
If the program is run on multiple processes the \configClass{receiver}{gnssReceiverGeneratorType}s
(stations or LEO satellites) are distributed over the processes.
)";
/***********************************************/
#include "programs/program.h"
#include "files/fileInstrument.h"
#include "classes/earthRotation/earthRotation.h"
#include "classes/noiseGenerator/noiseGenerator.h"
#include "classes/timeSeries/timeSeries.h"
#include "gnss/gnss.h"
#include "gnss/gnssObservation.h"
#include "gnss/gnssTransmitterGenerator/gnssTransmitterGenerator.h"
#include "gnss/gnssReceiverGenerator/gnssReceiverGenerator.h"
/***** CLASS ***********************************/
/** @brief GNSS receiver simulation.
* @ingroup programsGroup */
class GnssSimulateReceiver
{
public:
void run(Config &config, Parallel::CommunicatorPtr comm);
};
GROOPS_REGISTER_PROGRAM(GnssSimulateReceiver, PARALLEL, "GNSS receiver simulation", Gnss, Simulation)
/***********************************************/
void GnssSimulateReceiver::run(Config &config, Parallel::CommunicatorPtr comm)
{
try
{
FileName fileNameReceiver, fileNameClock;
TimeSeriesPtr timeSeries;
Double marginSeconds;
GnssTransmitterGeneratorPtr transmitterGenerator;
GnssReceiverGeneratorPtr receiverGenerator;
GnssParametrizationPtr gnssParametrization;
EarthRotationPtr earthRotation;
NoiseGeneratorPtr noiseClock, noiseObs;
std::vector<GnssType> obsTypes;
readConfig(config, "outputfileGnssReceiver", fileNameReceiver, Config::MUSTSET, "gnssReceiver_{loopTime:%D}.{station}.dat.gz", "variable {station} available, simulated observations");
readConfig(config, "outputfileClock", fileNameClock, Config::OPTIONAL, "clock_{loopTime:%D}.{station}.dat", "variable {station} available, simulated receiver clock errors");
readConfig(config, "timeSeries", timeSeries, Config::MUSTSET, "", "defines observation epochs");
readConfig(config, "timeMargin", marginSeconds, Config::DEFAULT, "0.1", "[seconds] margin to consider two times identical");
readConfig(config, "transmitter", transmitterGenerator, Config::MUSTSET, "", "constellation of GNSS satellites");
readConfig(config, "receiver", receiverGenerator, Config::MUSTSET, "", "ground station network or LEO satellite");
readConfig(config, "earthRotation", earthRotation, Config::MUSTSET, "", "apriori earth rotation");
readConfig(config, "parametrization", gnssParametrization, Config::DEFAULT, R"(["troposphere"])", "models and parameters");
readConfig(config, "observationType", obsTypes, Config::MUSTSET, "", "simulated observation types");
readConfig(config, "noiseObservation", noiseObs, Config::DEFAULT, "", "[-] noise is multiplied with type accuracy pattern of receiver");
readConfig(config, "noiseClockReceiver", noiseClock, Config::DEFAULT, "", "[m] noise added to the simulated receiver clock");
if(isCreateSchema(config)) return;
// ============================
// init the GNSS system
// --------------------
logInfo<<"Init GNSS"<<Log::endl;
std::sort(obsTypes.begin(), obsTypes.end());
std::vector<Time> times = timeSeries->times();
Gnss gnss;
gnss.init(obsTypes, times, seconds2time(marginSeconds), transmitterGenerator, receiverGenerator, earthRotation, gnssParametrization, comm);
receiverGenerator->simulation(noiseClock, noiseObs, &gnss, comm);
gnss.synchronizeTransceivers(comm);
logInfo<<" transmitter: "<<std::count_if(gnss.transmitters.begin(), gnss.transmitters.end(), [](auto t) {return t->useable();})<<Log::endl;
logInfo<<" receiver: "<<std::count_if(gnss.receivers.begin(), gnss.receivers.end(), [](auto r) {return r->useable();})<<Log::endl;
if(!std::any_of(gnss.transmitters.begin(), gnss.transmitters.end(), [](auto trans){return trans->useable();}))
{
logWarningOnce<<times.front().dateTimeStr()<<" - "<<times.back().dateTimeStr()<<": no useable transmitters"<<Log::endl;
return;
}
if(!std::any_of(gnss.receivers.begin(), gnss.receivers.end(), [](auto recv){return recv->useable();}))
{
logWarningOnce<<times.front().dateTimeStr()<<" - "<<times.back().dateTimeStr()<<": no useable receivers"<<Log::endl;
return;
}
// count observation types
// -----------------------
logInfo<<"types and number of observations:"<<Log::endl;
std::vector<GnssType> types = gnss.types(~(GnssType::PRN + GnssType::FREQ_NO));
Vector countTypes(types.size());
for(auto recv : gnss.receivers)
if(recv->isMyRank())
for(UInt idEpoch=0; idEpoch<recv->idEpochSize(); idEpoch++)
for(UInt idTrans=0; idTrans<recv->idTransmitterSize(idEpoch); idTrans++)
{
auto obs = recv->observation(idTrans, idEpoch);
if(obs)
for(UInt idType=0; idType<obs->size(); idType++)
{
const UInt idx = GnssType::index(types, obs->at(idType).type);
if(idx != NULLINDEX)
countTypes(idx)++;
}
}
Parallel::reduceSum(countTypes, 0, comm);
for(UInt idType=0; idType<types.size(); idType++)
logInfo<<" "<<types.at(idType).str()<<":"<<countTypes(idType)%"%10i"s<<Log::endl;
logInfo<<" + ========="<<Log::endl;
logInfo<<" total:"<<sum(countTypes)%"%11i"s<<Log::endl;
UInt countTracks = 0;
for(auto recv : gnss.receivers)
if(recv->isMyRank())
countTracks += recv->tracks.size();
Parallel::reduceSum(countTracks, 0, comm);
logInfo<<" number of tracks: "<<countTracks<<Log::endl;
// ============================
// Write observations
// ------------------
if(!fileNameReceiver.empty())
{
VariableList fileNameVariableList;
fileNameVariableList.setVariable("station", "****");
logStatus<<"write receiver observations to files <"<<fileNameReceiver(fileNameVariableList)<<">"<<Log::endl;
for(auto recv : gnss.receivers)
if(recv->isMyRank())
{
GnssReceiverArc arc;
for(UInt idEpoch=0; idEpoch<gnss.times.size(); idEpoch++)
if(recv->useable(idEpoch))
{
GnssReceiverEpoch epoch;
epoch.time = gnss.times.at(idEpoch);
epoch.clockError = recv->clockError(idEpoch);
// get types
for(UInt idTrans=0; idTrans<recv->idTransmitterSize(idEpoch); idTrans++)
if(recv->observation(idTrans, idEpoch) && gnss.transmitters.at(idTrans)->useable(idEpoch))
for(UInt idType=0; idType<recv->observation(idTrans, idEpoch)->size(); idType++)
if(!recv->observation(idTrans, idEpoch)->at(idType).type.isInList(epoch.obsType))
epoch.obsType.push_back(recv->observation(idTrans, idEpoch)->at(idType).type & ~(GnssType::PRN+GnssType::FREQ_NO));
std::sort(epoch.obsType.begin(), epoch.obsType.end());
if(!epoch.obsType.size())
continue;
for(UInt idTrans=0; idTrans<recv->idTransmitterSize(idEpoch); idTrans++)
if(recv->observation(idTrans, idEpoch) && gnss.transmitters.at(idTrans)->useable(idEpoch))
{
const GnssObservation &obs = *recv->observation(idTrans, idEpoch);
const GnssType prn = obs.at(0).type & (GnssType::SYSTEM + GnssType::PRN + GnssType::FREQ_NO);
UInt idType = std::distance(epoch.obsType.begin(), std::find(epoch.obsType.begin(), epoch.obsType.end(), prn));
epoch.satellite.push_back(prn);
for(; (idType<epoch.obsType.size()) && (epoch.obsType.at(idType) == prn); idType++)
{
epoch.observation.push_back(NAN_EXPR);
for(UInt i=0; i<obs.size(); i++)
if(obs.at(i).type == epoch.obsType.at(idType))
{
epoch.observation.back() = obs.at(i).observation;
break;
}
}
} // for(idTrans)
if(epoch.satellite.size())
arc.push_back(epoch);
} // for(idEpoch)
fileNameVariableList.setVariable("station", recv->name());
InstrumentFile::write(fileNameReceiver(fileNameVariableList), arc);
} // for(recv)
} // if(fileNameReceiver)
// ============================
// write clock errors
// ------------------
if(!fileNameClock.empty())
{
VariableList fileNameVariableList;
fileNameVariableList.setVariable("station", "****");
logStatus<<"write receiver clocks to files <"<<fileNameClock(fileNameVariableList)<<">"<<Log::endl;
for(auto recv : gnss.receivers)
if(recv->isMyRank())
{
MiscValueArc arc;
for(UInt idEpoch=0; idEpoch<gnss.times.size(); idEpoch++)
if(recv->useable(idEpoch))
{
MiscValueEpoch epoch;
epoch.time = gnss.times.at(idEpoch);
epoch.value = recv->clockError(idEpoch);
arc.push_back(epoch);
}
fileNameVariableList.setVariable("station", recv->name());
InstrumentFile::write(fileNameClock(fileNameVariableList), arc);
} // for(recv)
} // if(fileNameClock)
}
catch(std::exception &e)
{
GROOPS_RETHROW(e)
}
}
/***********************************************/
|