Major update:

1) Galileo E1 Acquisition adapter block added (Gnuradio block modification to use the same block with the 2 systems with 2 adapters)
2) Tests and signal samples for Galileo E1 Acquisition signal block
3) Library for Galileo E1 signal processing
4) Galileo_E1.h with constant variables for this system

git-svn-id: https://svn.code.sf.net/p/gnss-sdr/code/trunk@209 64b25241-fba3-4117-9849-534c7e92360d
This commit is contained in:
Luis Esteve 2012-07-12 21:17:37 +00:00
parent aab40c963d
commit c9a06f702a
42 changed files with 1444 additions and 107050 deletions

View File

@ -3,7 +3,7 @@
;######### GLOBAL OPTIONS ##################
;internal_fs_hz: Internal signal sampling frequency after the signal conditioning stage [Hz].
GNSS-SDR.internal_fs_hz=8000000
GNSS-SDR.internal_fs_hz=4000000
;######### CONTROL_THREAD CONFIG ############
ControlThread.wait_for_flowgraph=false
@ -13,13 +13,14 @@ ControlThread.wait_for_flowgraph=false
SignalSource.implementation=File_Signal_Source
;#filename: path to file with the captured GNSS signal samples to be processed
SignalSource.filename=/media/DATA/Proyectos/Signals/spirent scenario 2/data/sc2_d8.dat
;SignalSource.filename=/media/DATA/Proyectos/Signals/spirent scenario 2/data/sc2_d8.dat
SignalSource.filename=/media/DATA/Proyectos/Signals/Agilent/cap2/agilent_cap2.dat
;#item_type: Type and resolution for each of the signal samples. Use only gr_complex in this version.
SignalSource.item_type=gr_complex
;#sampling_frequency: Original Signal sampling frequency in [Hz]
SignalSource.sampling_frequency=8000000
SignalSource.sampling_frequency=4000000
;#freq: RF front-end center frequency in [Hz]
SignalSource.freq=1575420000
@ -246,7 +247,7 @@ Channel4.repeat_satellite=false
Channel5.system=GPS
Channel5.signal=1C
;Channel5.satellite=21
;Channel5.repeat_satellite=true
;Channel5.repeat_satellite=false
;######### ACQUISITION GLOBAL CONFIG ############

View File

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>GNSS Track</name>
<description>GNSS-SDR Receiver position log file created at Fri Jun 22 16:02:49 2012
</description>
<Style id="yellowLineGreenPoly">
<LineStyle>
<color>7f00ffff</color>
<width>1</width>
</LineStyle>
<PolyStyle>
<color>7f00ff00</color>
</PolyStyle>
</Style>
<Placemark>
<name>GNSS-SDR PVT</name>
<description>GNSS-SDR position log</description>
<styleUrl>#yellowLineGreenPoly</styleUrl>
<LineString>
<extrude>0</extrude>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<coordinates>
</coordinates>
</LineString>
</Placemark>
</Document>
</kml>

View File

@ -1,9 +1,8 @@
/*!
* \file gps_l1_ca_tong_pcps_acquisition.cc
* \brief Brief description of the file here
* \author Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
* \file galileo_e1_pcps_ambiguous_acquisition.cc
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* Galileo E1 Signals
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
@ -30,85 +29,81 @@
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_tong_pcps_acquisition.h"
#include "GPS_L1_CA.h"
#include "galileo_e1_pcps_ambiguous_acquisition.h"
#include "galileo_e1_signal_processing.h"
#include "Galileo_E1.h"
#include "configuration_interface.h"
#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <gnuradio/gr_io_signature.h>
#include <gnuradio/gr_stream_to_vector.h>
#include <gnuradio/gr_vector_to_stream.h>
#include <gnuradio/gr_complex_to_interleaved_short.h>
#include <gnuradio/gr_interleaved_short_to_complex.h>
#include <glog/log_severity.h>
#include <glog/logging.h>
using google::LogMessage;
GpsL1CaTongPcpsAcquisition::GpsL1CaTongPcpsAcquisition(
GalileoE1PcpsAmbiguousAcquisition::GalileoE1PcpsAmbiguousAcquisition(
ConfigurationInterface* configuration, std::string role,
unsigned int in_streams, unsigned int out_streams,
gr_msg_queue_sptr queue) :
role_(role), in_streams_(in_streams), out_streams_(out_streams), queue_(
queue)
role_(role), in_streams_(in_streams), out_streams_(out_streams), queue_(queue)
{
configuration_ = configuration;
std::string default_item_type = "gr_complex";
std::string default_dump_filename = "./data/acquisition.dat";
std::string default_dump_filename = "../data/acquisition.dat";
DLOG(INFO) << "role " << role;
item_type_ = configuration->property(role + ".item_type",
default_item_type);
std::cout << "item type " << item_type_ << std::endl;
satellite_ = Gnss_Satellite();
fs_in_ = configuration->property("GNSS-SDR.internal_fs_hz", 2048000);
if_ = configuration->property(role + ".ifreq", 0);
dump_ = configuration->property(role + ".dump", false);
doppler_max_ = configuration->property(role + ".doppler_max", 10);
sampled_ms_ = configuration->property(role + ".sampled_ms", 1);
dump_filename_ = configuration->property(role + ".dump_filename",
fs_in_ = configuration_->property("GNSS-SDR.internal_fs_hz", 4000000);
if_ = configuration_->property(role + ".ifreq", 0);
dump_ = configuration_->property(role + ".dump", false);
shift_resolution_ = configuration_->property(role + ".doppler_max", 15);
sampled_ms_ = configuration_->property(role + ".sampled_ms", 4);
dump_filename_ = configuration_->property(role + ".dump_filename",
default_dump_filename);
//--- Find number of samples per spreading code ----------------------------
vector_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS));
//--- Find number of samples per spreading code (4 ms) ----------------------------
vector_length_ = round(fs_in_ / (Galileo_E1_CODE_CHIP_RATE_HZ / Galileo_E1_B_CODE_LENGTH_CHIPS));
int samples_per_ms = vector_length_/4;
code_= new gr_complex[vector_length_];
if (item_type_.compare("gr_complex") == 0)
{
item_size_ = sizeof(gr_complex);
acquisition_cc_ = gps_l1_ca_tong_pcps_make_acquisition_cc(
sampled_ms_, doppler_max_, if_, fs_in_, vector_length_,
queue_, dump_, dump_filename_);
acquisition_cc_ = pcps_make_acquisition_cc(sampled_ms_,
shift_resolution_, if_, fs_in_, samples_per_ms, queue_,
dump_, dump_filename_);
stream_to_vector_ = gr_make_stream_to_vector(item_size_,
vector_length_);
DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id()
<< ")";
DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id()
<< ")";
}
else
{
LOG_AT_LEVEL(WARNING) << item_type_
<< " unknown acquisition item type";
}
DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id()
<< ")";
}
GpsL1CaTongPcpsAcquisition::~GpsL1CaTongPcpsAcquisition()
GalileoE1PcpsAmbiguousAcquisition::~GalileoE1PcpsAmbiguousAcquisition()
{
delete[] code_;
}
void GpsL1CaTongPcpsAcquisition::set_satellite(Gnss_Satellite satellite)
{
satellite_ = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_satellite(satellite_);
}
}
void GpsL1CaTongPcpsAcquisition::set_channel(unsigned int channel)
void GalileoE1PcpsAmbiguousAcquisition::set_channel(unsigned int channel)
{
channel_ = channel;
@ -118,7 +113,17 @@ void GpsL1CaTongPcpsAcquisition::set_channel(unsigned int channel)
}
}
void GpsL1CaTongPcpsAcquisition::set_doppler_max(unsigned int doppler_max)
void GalileoE1PcpsAmbiguousAcquisition::set_threshold(float threshold)
{
threshold_ = threshold;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_threshold(threshold_);
}
}
void GalileoE1PcpsAmbiguousAcquisition::set_doppler_max(unsigned int doppler_max)
{
doppler_max_ = doppler_max;
@ -129,7 +134,7 @@ void GpsL1CaTongPcpsAcquisition::set_doppler_max(unsigned int doppler_max)
}
void GpsL1CaTongPcpsAcquisition::set_doppler_step(unsigned int doppler_step)
void GalileoE1PcpsAmbiguousAcquisition::set_doppler_step(unsigned int doppler_step)
{
doppler_step_ = doppler_step;
@ -140,7 +145,7 @@ void GpsL1CaTongPcpsAcquisition::set_doppler_step(unsigned int doppler_step)
}
void GpsL1CaTongPcpsAcquisition::set_channel_queue(
void GalileoE1PcpsAmbiguousAcquisition::set_channel_queue(
concurrent_queue<int> *channel_internal_queue)
{
channel_internal_queue_ = channel_internal_queue;
@ -151,44 +156,17 @@ void GpsL1CaTongPcpsAcquisition::set_channel_queue(
}
}
signed int GpsL1CaTongPcpsAcquisition::prn_code_phase()
void GalileoE1PcpsAmbiguousAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro)
{
gnss_synchro_ = gnss_synchro;
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->prn_code_phase();
}
else
{
return 0;
acquisition_cc_->set_gnss_synchro(gnss_synchro_);
}
}
unsigned long int GpsL1CaTongPcpsAcquisition::get_sample_stamp()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->get_sample_stamp();
}
else
{
return 0;
}
}
float GpsL1CaTongPcpsAcquisition::doppler_freq_shift()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->doppler_freq();
}
else
{
return 0;
}
}
signed int GpsL1CaTongPcpsAcquisition::mag()
signed int GalileoE1PcpsAmbiguousAcquisition::mag()
{
if (item_type_.compare("gr_complex") == 0)
{
@ -200,17 +178,27 @@ signed int GpsL1CaTongPcpsAcquisition::mag()
}
}
void GpsL1CaTongPcpsAcquisition::reset()
void GalileoE1PcpsAmbiguousAcquisition::init(){
if (item_type_.compare("gr_complex") == 0)
{
bool cboc = configuration_->property("Acquisition"
+ boost::lexical_cast<std::string>(channel_) + ".cboc", false);;
galileo_e1_code_gen_complex_sampled(code_, gnss_synchro_->Signal, cboc, gnss_synchro_->PRN, fs_in_, 0);
acquisition_cc_->set_local_code(code_);
acquisition_cc_->init();
}
}
void GalileoE1PcpsAmbiguousAcquisition::reset()
{
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_active(true);
acquisition_cc_->set_dwells(0);
acquisition_cc_->set_doppler(0);
}
}
void GpsL1CaTongPcpsAcquisition::connect(gr_top_block_sptr top_block)
void GalileoE1PcpsAmbiguousAcquisition::connect(gr_top_block_sptr top_block)
{
if (item_type_.compare("gr_complex") == 0)
@ -220,7 +208,7 @@ void GpsL1CaTongPcpsAcquisition::connect(gr_top_block_sptr top_block)
}
void GpsL1CaTongPcpsAcquisition::disconnect(gr_top_block_sptr top_block)
void GalileoE1PcpsAmbiguousAcquisition::disconnect(gr_top_block_sptr top_block)
{
if (item_type_.compare("gr_complex") == 0)
@ -229,12 +217,12 @@ void GpsL1CaTongPcpsAcquisition::disconnect(gr_top_block_sptr top_block)
}
}
gr_basic_block_sptr GpsL1CaTongPcpsAcquisition::get_left_block()
gr_basic_block_sptr GalileoE1PcpsAmbiguousAcquisition::get_left_block()
{
return stream_to_vector_;
}
gr_basic_block_sptr GpsL1CaTongPcpsAcquisition::get_right_block()
gr_basic_block_sptr GalileoE1PcpsAmbiguousAcquisition::get_right_block()
{
return acquisition_cc_;
}

View File

@ -1,7 +1,7 @@
/*!
* \file gps_l1_ca_tong_pcps_acquisition.h
* \brief Brief description of the file here
* \author Luis Esteve, 2011. luis(at)epsilon-formacion.com
* \file galileo_e1_pcps_ambiguous_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for Galileo E1 Signals
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
@ -30,25 +30,26 @@
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_L1_CA_TONG_PCPS_ACQUISITION_H_
#define GNSS_SDR_L1_CA_TONG_PCPS_ACQUISITION_H_
#ifndef GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_
#define GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_
#include "gnss_synchro.h"
#include "acquisition_interface.h"
#include "gps_l1_ca_tong_pcps_acquisition_cc.h"
#include "pcps_acquisition_cc.h"
#include <gnuradio/gr_msg_queue.h>
class ConfigurationInterface;
class GpsL1CaTongPcpsAcquisition: public AcquisitionInterface
class GalileoE1PcpsAmbiguousAcquisition: public AcquisitionInterface
{
public:
GpsL1CaTongPcpsAcquisition(ConfigurationInterface* configuration,
GalileoE1PcpsAmbiguousAcquisition(ConfigurationInterface* configuration,
std::string role, unsigned int in_streams,
unsigned int out_streams, gr_msg_queue_sptr queue);
virtual ~GpsL1CaTongPcpsAcquisition();
virtual ~GalileoE1PcpsAmbiguousAcquisition();
std::string role()
{
@ -56,7 +57,7 @@ public:
}
std::string implementation()
{
return "Acquisition";
return "Galileo_E1_PCPS_Ambiguous_Acquisition";
}
size_t item_size()
{
@ -68,30 +69,29 @@ public:
gr_basic_block_sptr get_left_block();
gr_basic_block_sptr get_right_block();
void set_satellite(Gnss_Satellite satellite);
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro);
void set_channel(unsigned int channel);
void set_threshold(float threshold){};
void set_threshold(float threshold);
void set_doppler_max(unsigned int doppler_max);
void set_doppler_step(unsigned int doppler_step);
void set_channel_queue(concurrent_queue<int> *channel_internal_queue);
signed int prn_code_phase();
float doppler_freq_shift();
unsigned long int get_sample_stamp();
void init();
signed int mag();
void reset();
private:
gps_l1_ca_tong_pcps_acquisition_cc_sptr acquisition_cc_;
ConfigurationInterface* configuration_;
pcps_acquisition_cc_sptr acquisition_cc_;
gr_block_sptr stream_to_vector_;
gr_block_sptr complex_to_short_;
gr_block_sptr short_to_complex_;
size_t item_size_;
std::string item_type_;
unsigned int vector_length_;
Gnss_Satellite satellite_;
//unsigned int satellite_;
unsigned int channel_;
float threshold_;
unsigned int doppler_max_;
@ -102,6 +102,9 @@ private:
long if_;
bool dump_;
std::string dump_filename_;
std::complex<float> * code_;
Gnss_Synchro * gnss_synchro_;
std::string role_;
unsigned int in_streams_;
@ -111,4 +114,4 @@ private:
};
#endif /* GNSS_SDR_L1_CA_TONG_PCPS_ACQUISITION_H_ */
#endif /* GALILEO_E1_PCPS_AMBIGUOUS_ACQUISITION_H_ */

View File

@ -1,241 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition.cc
* \brief Implementation of an adapter of an acquisition module based
* on the method in Gregory Heckler's GPS-SDR (see http://github.com/gps-sdr/gps-sdr)
* to an AcquisitionInterface
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_gps_sdr_acquisition.h"
#include "GPS_L1_CA.h"
#include "configuration_interface.h"
#include <gnuradio/gr_io_signature.h>
#include <gnuradio/gr_stream_to_vector.h>
#include <gnuradio/gr_vector_to_stream.h>
#include <gnuradio/gr_complex_to_interleaved_short.h>
#include <gnuradio/gr_interleaved_short_to_complex.h>
#include <glog/log_severity.h>
#include <glog/logging.h>
using google::LogMessage;
// Constructor
GpsL1CaGpsSdrAcquisition::GpsL1CaGpsSdrAcquisition(
ConfigurationInterface* configuration, std::string role,
unsigned int in_streams, unsigned int out_streams,
gr_msg_queue_sptr queue) :
role_(role), in_streams_(in_streams), out_streams_(out_streams), queue_(
queue)
{
std::string default_item_type = "gr_complex";
std::string default_dump_filename = "./data/acquisition_";
DLOG(INFO) << "role " << role;
item_type_ = configuration->property(role + ".item_type",
default_item_type);
//vector_length_ = configuration->property(role + ".vector_length", 2048);
gnss_satellite_ = Gnss_Satellite();
fs_in_ = configuration->property("GNSS-SDR.internal_fs_hz", 2048000);
if_ = configuration->property(role + ".ifreq", 0);
dump_ = configuration->property(role + ".dump", false);
doppler_max_ = 0;
acquisition_ms_ = configuration->property(role + ".sampled_ms", 1);
dump_filename_ = configuration->property(role + ".dump_filename",
default_dump_filename);
//vector_length_=ceil((float)fs_in_*((float)acquisition_ms_/1000));
//--- Find number of samples per spreading code ----------------------------
vector_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS));
printf("vector_length_ %i\n\r", vector_length_);
if (item_type_.compare("gr_complex") == 0)
{
item_size_ = sizeof(gr_complex);
acquisition_cc_ = gps_l1_ca_gps_sdr_make_acquisition_cc(
acquisition_ms_, if_, fs_in_, vector_length_, queue_, dump_,
dump_filename_);
stream_to_vector_ = gr_make_stream_to_vector(item_size_,
vector_length_);
}
else
{
LOG_AT_LEVEL(WARNING) << item_type_ << " unknown item type.";
}
DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id()
<< ")";
}
// Destructor
GpsL1CaGpsSdrAcquisition::~GpsL1CaGpsSdrAcquisition()
{}
// Set satellite
void GpsL1CaGpsSdrAcquisition::set_satellite(Gnss_Satellite satellite)
{
gnss_satellite_ = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_satellite(gnss_satellite_);
}
}
// Set channel
void GpsL1CaGpsSdrAcquisition::set_channel(unsigned int channel)
{
channel_ = channel;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_channel(channel_);
}
}
// Set acquisition threshold
void GpsL1CaGpsSdrAcquisition::set_threshold(float threshold)
{
threshold_ = threshold;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_threshold(threshold_);
}
}
// Set maximum Doppler shift
void GpsL1CaGpsSdrAcquisition::set_doppler_max(unsigned int doppler_max)
{
doppler_max_ = doppler_max;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_doppler_max(doppler_max_);
}
}
// Set Channel Queue
void GpsL1CaGpsSdrAcquisition::set_channel_queue(
concurrent_queue<int> *channel_internal_queue)
{
channel_internal_queue_ = channel_internal_queue;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_channel_queue(channel_internal_queue_);
}
}
signed int GpsL1CaGpsSdrAcquisition::prn_code_phase()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->prn_code_phase();
}
}
float GpsL1CaGpsSdrAcquisition::doppler_freq_shift()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->doppler_freq_phase();
}
}
signed int GpsL1CaGpsSdrAcquisition::mag()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->mag();
}
}
void GpsL1CaGpsSdrAcquisition::reset()
{
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_active(true);
}
}
unsigned long int GpsL1CaGpsSdrAcquisition::get_sample_stamp()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_->get_sample_stamp();
}
}
void GpsL1CaGpsSdrAcquisition::connect(gr_top_block_sptr top_block)
{
if (item_type_.compare("gr_complex") == 0)
{
//top_block->connect(stream_to_vector_, 0, vector_to_stream_,0);
top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0);
//top_block->connect(acquisition_cc_, 0, vector_to_stream_, 0);
}
}
void GpsL1CaGpsSdrAcquisition::disconnect(gr_top_block_sptr top_block)
{
if (item_type_.compare("gr_complex") == 0)
{
top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0);
//top_block->disconnect(acquisition_cc_, 0, vector_to_stream_, 0);
}
}
gr_basic_block_sptr GpsL1CaGpsSdrAcquisition::get_left_block()
{
return stream_to_vector_;
}
gr_basic_block_sptr GpsL1CaGpsSdrAcquisition::get_right_block()
{
if (item_type_.compare("gr_complex") == 0)
{
return acquisition_cc_;
}
}

View File

@ -1,123 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition.h
* \brief Interface of an adapter of an acquisition module based
* on the method in Gregory Heckler's GPS-SDR (see http://github.com/gps-sdr/gps-sdr)
* to an AcquisitionInterface
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_H_
#define GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_H_
#include "acquisition_interface.h"
#include "gps_l1_ca_gps_sdr_acquisition_cc.h"
//#include "gps_l1_ca_gps_sdr_acquisition_ss.h"
#include <gnuradio/gr_msg_queue.h>
class ConfigurationInterface;
/*!
* \brief Adapts the GPS-SDR acquisition implementation to
* an Acquisition Interface
*
* Derived from AcquisitionInterface, this class wraps the implementation
* of the acquisition algorithm proposed by Gregory Heckler at https://github.com/gps-sdr/gps-sdr
*
*/
class GpsL1CaGpsSdrAcquisition: public AcquisitionInterface
{
public:
GpsL1CaGpsSdrAcquisition(ConfigurationInterface* configuration,
std::string role, unsigned int in_streams,
unsigned int out_streams, gr_msg_queue_sptr queue);
virtual ~GpsL1CaGpsSdrAcquisition();
std::string role()
{
return role_;
}
std::string implementation()
{
return "Acquisition";
}
size_t item_size()
{
return item_size_;
}
void connect(gr_top_block_sptr top_block);
void disconnect(gr_top_block_sptr top_block);
gr_basic_block_sptr get_left_block();
gr_basic_block_sptr get_right_block();
void set_satellite(Gnss_Satellite gnss_satellite);
void set_channel(unsigned int channel);
void set_threshold(float threshold);
void set_doppler_max(unsigned int doppler_max);
void set_channel_queue(concurrent_queue<int> *channel_internal_queue);
signed int prn_code_phase();
float doppler_freq_shift();
signed int mag();
void reset();
unsigned long int get_sample_stamp();
//Not used in this implementation
void set_doppler_step(unsigned int doppler_step)
{};
private:
gps_l1_ca_gps_sdr_acquisition_cc_sptr acquisition_cc_;
//gps_l1_ca_gps_sdr_acquisition_ss_sptr acquisition_ss_;
gr_block_sptr stream_to_vector_;
gr_block_sptr complex_to_short_;
gr_block_sptr short_to_complex_;
size_t item_size_;
std::string item_type_;
unsigned int vector_length_;
Gnss_Satellite gnss_satellite_;
unsigned int channel_;
float threshold_;
unsigned int doppler_max_;
unsigned int acquisition_ms_;
long fs_in_;
long if_;
bool dump_;
std::string dump_filename_;
std::string role_;
unsigned int in_streams_;
unsigned int out_streams_;
gr_msg_queue_sptr queue_;
concurrent_queue<int> *channel_internal_queue_;
};
#endif

View File

@ -1,8 +1,9 @@
/*!
* \file gps_l1_ca_pcps_acquisition.cc
* \brief Adapts a PCPS acquisition block for GPS L1 C/A to an AcquisitionInterface
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* GPS L1 C/A Signals
* \author Javier Arribas, 2011. jarribas(at)cttc.es
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
* Luis Esteve, 2011-2012. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
@ -30,6 +31,7 @@
*/
#include "gps_l1_ca_pcps_acquisition.h"
#include "gps_sdr_signal_processing.h"
#include "GPS_L1_CA.h"
#include "configuration_interface.h"
#include <iostream>
@ -70,10 +72,12 @@ GpsL1CaPcpsAcquisition::GpsL1CaPcpsAcquisition(
//--- Find number of samples per spreading code ----------------------------
vector_length_ = round(fs_in_ / (GPS_L1_CA_CODE_RATE_HZ / GPS_L1_CA_CODE_LENGTH_CHIPS));
code_= new gr_complex[vector_length_];
if (item_type_.compare("gr_complex") == 0)
{
item_size_ = sizeof(gr_complex);
acquisition_cc_ = gps_l1_ca_pcps_make_acquisition_cc(sampled_ms_,
acquisition_cc_ = pcps_make_acquisition_cc(sampled_ms_,
shift_resolution_, if_, fs_in_, vector_length_, queue_,
dump_, dump_filename_);
stream_to_vector_ = gr_make_stream_to_vector(item_size_,
@ -94,6 +98,7 @@ GpsL1CaPcpsAcquisition::GpsL1CaPcpsAcquisition(
GpsL1CaPcpsAcquisition::~GpsL1CaPcpsAcquisition()
{
delete[] code_;
}
@ -152,9 +157,10 @@ void GpsL1CaPcpsAcquisition::set_channel_queue(
void GpsL1CaPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro)
{
gnss_synchro_ = gnss_synchro;
if (item_type_.compare("gr_complex") == 0)
{
acquisition_cc_->set_gnss_synchro(gnss_synchro);
acquisition_cc_->set_gnss_synchro(gnss_synchro_);
}
}
@ -173,6 +179,8 @@ signed int GpsL1CaPcpsAcquisition::mag()
void GpsL1CaPcpsAcquisition::init(){
if (item_type_.compare("gr_complex") == 0)
{
gps_l1_ca_code_gen_complex_sampled(code_, gnss_synchro_->PRN, fs_in_, 0);
acquisition_cc_->set_local_code(code_);
acquisition_cc_->init();
}
}

View File

@ -1,8 +1,9 @@
/*!
* \file gps_l1_ca_pcps_acquisition.h
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for GPS L1 C/A
* \brief Adapts a PCPS acquisition block to an AcquisitionInterface for
* GPS L1 C/A signals
* \author Javier Arribas, 2011. jarribas(at)cttc.es
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
* Luis Esteve, 2011-2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
@ -36,7 +37,7 @@
#include "gnss_synchro.h"
#include "acquisition_interface.h"
#include "gps_l1_ca_pcps_acquisition_cc.h"
#include "pcps_acquisition_cc.h"
#include <gnuradio/gr_msg_queue.h>
class ConfigurationInterface;
@ -83,7 +84,7 @@ public:
private:
gps_l1_ca_pcps_acquisition_cc_sptr acquisition_cc_;
pcps_acquisition_cc_sptr acquisition_cc_;
gr_block_sptr stream_to_vector_;
gr_block_sptr complex_to_short_;
gr_block_sptr short_to_complex_;
@ -102,6 +103,9 @@ private:
long if_;
bool dump_;
std::string dump_filename_;
std::complex<float> * code_;
Gnss_Synchro * gnss_synchro_;
std::string role_;
unsigned int in_streams_;

View File

@ -1,5 +1,4 @@
project : build-dir ../../../../build ;
#obj gps_l1_ca_gps_sdr_acquisition : gps_l1_ca_gps_sdr_acquisition.cc ;
obj gps_l1_ca_pcps_acquisition : gps_l1_ca_pcps_acquisition.cc ;
#obj gps_l1_ca_tong_pcps_acquisition : gps_l1_ca_tong_pcps_acquisition.cc ;
obj galileo_e1_pcps_ambiguous_acquisition : galileo_e1_pcps_ambiguous_acquisition.cc ;

View File

@ -1,327 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition_cc.cc
* \brief Brief description of the file here
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_gps_sdr_acquisition_cc.h"
#include "control_message_factory.h"
#include "gps_sdr_x86.h"
#include <gnuradio/gr_io_signature.h>
#include <sstream>
#include <glog/log_severity.h>
#include <glog/logging.h>
#include <iostream>
using google::LogMessage;
gps_l1_ca_gps_sdr_acquisition_cc_sptr gps_l1_ca_gps_sdr_make_acquisition_cc(
unsigned int sampled_ms, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename)
{
return gps_l1_ca_gps_sdr_acquisition_cc_sptr(
new gps_l1_ca_gps_sdr_acquisition_cc(sampled_ms, freq, fs_in,
samples_per_ms, queue, dump, dump_filename));
}
gps_l1_ca_gps_sdr_acquisition_cc::gps_l1_ca_gps_sdr_acquisition_cc(
unsigned int sampled_ms, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename) :
gr_block("gps_l1_ca_gps_sdr_acquisition_cc", gr_make_io_signature(1, 1,
sizeof(gr_complex) * samples_per_ms), gr_make_io_signature(0, 0,
sizeof(gr_complex) * samples_per_ms))
{
// SAMPLE COUNTER
d_sample_counter = 0;
d_active = false;
d_dump = dump;
d_queue = queue;
d_dump_filename = dump_filename;
d_freq = freq;
d_fs_in = fs_in;
d_samples_per_ms = samples_per_ms;
d_sampled_ms = sampled_ms;
d_doppler_max = 0;
d_satellite = Gnss_Satellite();
d_fft_size = d_sampled_ms * d_samples_per_ms;
d_doppler_freq_phase = 0.0;
d_prn_code_phase = 0;
d_mag = 0;
d_mean = 0.0;
d_count = 0;
d_sine_if = new gr_complex[d_fft_size];
d_sine_250 = new gr_complex[d_fft_size];
d_sine_500 = new gr_complex[d_fft_size];
d_sine_750 = new gr_complex[d_fft_size];
d_fft_codes = (gr_complex*)malloc(sizeof(gr_complex) * d_samples_per_ms);
// Direct FFT
d_fft_if = new gri_fft_complex(d_fft_size, true);
d_fft_250 = new gri_fft_complex(d_fft_size, true);
d_fft_500 = new gri_fft_complex(d_fft_size, true);
d_fft_750 = new gri_fft_complex(d_fft_size, true);
// Inverse FFT
d_ifft = new gri_fft_complex(d_fft_size, false);
d_best_magnitudes = new float[d_fft_size];
sine_gen_complex(d_sine_if, -d_freq, d_fs_in, d_fft_size);
sine_gen_complex(d_sine_250, -d_freq - 250, d_fs_in, d_fft_size);
sine_gen_complex(d_sine_500, -d_freq - 500, d_fs_in, d_fft_size);
sine_gen_complex(d_sine_750, -d_freq - 750, d_fs_in, d_fft_size);
DLOG(INFO) << "fs in " << d_fs_in;
DLOG(INFO) << "Doppler max " << d_doppler_max;
DLOG(INFO) << "IF " << d_freq;
DLOG(INFO) << "Satellite " << d_satellite;
DLOG(INFO) << "Sampled_ms " << d_sampled_ms;
DLOG(INFO) << "FFT_size " << d_fft_size;
DLOG(INFO) << "dump filename " << d_dump_filename;
DLOG(INFO) << "dump " << d_dump;
}
gps_l1_ca_gps_sdr_acquisition_cc::~gps_l1_ca_gps_sdr_acquisition_cc()
{
delete[] d_sine_if;
delete[] d_sine_250;
delete[] d_sine_500;
delete[] d_sine_750;
delete[] d_fft_codes;
delete[] d_fft_if;
delete[] d_fft_250;
delete[] d_fft_500;
delete[] d_fft_750;
if (d_dump)
{
d_dump_file.close();
}
}
void gps_l1_ca_gps_sdr_acquisition_cc::set_satellite(Gnss_Satellite satellite)
{
d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
d_prn_code_phase = 0;
d_doppler_freq_phase = 0;
d_mag = 0;
// Now the GPS codes are generated on the fly using a custom version of the GPS code generator
code_gen_complex_sampled(d_fft_if->get_inbuf(), satellite.get_PRN(), d_fs_in, 0);
d_fft_if->execute(); // We need the FFT of GPS C/A code
memcpy(d_fft_codes, d_fft_if->get_outbuf(), sizeof(gr_complex)
* d_samples_per_ms);
}
signed int gps_l1_ca_gps_sdr_acquisition_cc::prn_code_phase()
{
return d_prn_code_phase;
}
int gps_l1_ca_gps_sdr_acquisition_cc::general_work(int noutput_items,
gr_vector_int &ninput_items, gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
if (!d_active)
{
d_sample_counter += d_fft_size * noutput_items; // sample counter
consume_each(noutput_items);
return 0;
}
d_sample_counter += d_fft_size; // sample counter
bool positive_acquisition = false;
int acquisition_message = -1; //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL
LOG_AT_LEVEL(INFO) << "Channel: " << d_channel
<< " , doing acquisition of satellite: " << d_satellite
<< " ,at sample stamp: " << d_sample_counter;
const gr_complex *in = (const gr_complex *)input_items[0];
//Perform the carrier wipe-off
for (unsigned int j = 0; j < d_fft_size; j++)
{
d_fft_if->get_inbuf()[j] = in[j] * d_sine_if[j];
d_fft_250->get_inbuf()[j] = in[j] * d_sine_250[j];
d_fft_500->get_inbuf()[j] = in[j] * d_sine_500[j];
d_fft_750->get_inbuf()[j] = in[j] * d_sine_750[j];
}
// Perform the FFT of the resulting signal
d_fft_if->execute();
d_fft_250->execute();
d_fft_500->execute();
d_fft_750->execute();
//----------------------------------------------------------------------
//---Calculate cross-correlation magnitudes using:
//----> the circular convolution property of FFT:
//----> the frequency-shift property of the FFT: y(t)=exp(jw0t)x(t) -> Y(w)=X(w-w0)
for (int j = (int)-(d_doppler_max / 1000); j < (int)d_doppler_max / 1000; j++)
{ // doppler search steps
calculate_magnitudes(d_fft_if->get_outbuf(), j, 0);
calculate_magnitudes(d_fft_250->get_outbuf(), j, 1);
calculate_magnitudes(d_fft_500->get_outbuf(), j, 2);
calculate_magnitudes(d_fft_750->get_outbuf(), j, 3);
}
if (d_dump)
{
std::stringstream ss;
ss << "./data/acquisition_" << d_channel << "_" << d_satellite << "_"
<< d_count << ".dat";
d_dump_file.open(ss.str().c_str(), std::ios::out | std::ios::binary);
std::streamsize n = sizeof(float) * (d_fft_size);
d_dump_file.write((char*)d_best_magnitudes, n);
d_dump_file.close();
}
if (d_test_statistics > d_threshold)
{ //TODO: Include in configuration parameters and check value!!
positive_acquisition = true;
d_acq_sample_stamp = d_sample_counter;
LOG_AT_LEVEL(INFO) << "POSITIVE ACQUISITION of channel " << d_channel;
LOG_AT_LEVEL(INFO) << "Satellite " << d_satellite;
LOG_AT_LEVEL(INFO) << "Code Phase " << d_prn_code_phase;
LOG_AT_LEVEL(INFO) << "Doppler " << d_doppler_freq_phase;
LOG_AT_LEVEL(INFO) << "Magnitude " << d_mag;
LOG_AT_LEVEL(INFO) << "Mean " << d_mean;
LOG_AT_LEVEL(INFO) << "Test statistics value " << d_test_statistics;
LOG_AT_LEVEL(INFO) << "Test statistics threshold " << d_threshold;
LOG_AT_LEVEL(INFO) << "Acq sample stamp " << d_acq_sample_stamp;
}
else
{
LOG_AT_LEVEL(INFO) << "NEGATIVE ACQUISITION of channel " << d_channel;
LOG_AT_LEVEL(INFO) << "Satellite " << d_satellite;
LOG_AT_LEVEL(INFO) << "Test statistics value " << d_test_statistics;
LOG_AT_LEVEL(INFO) << "Test statistics threshold " << d_threshold;
LOG_AT_LEVEL(INFO) << "Acq sample stamp " << d_acq_sample_stamp;
}
d_active = false;
LOG_AT_LEVEL(INFO) << "d_count " << d_count;
if (positive_acquisition)
{
acquisition_message = 1;
}
else
{
acquisition_message = 2;
}
d_channel_internal_queue->push(acquisition_message);
return 0;
}
void gps_l1_ca_gps_sdr_acquisition_cc::calculate_magnitudes(
gr_complex* fft_signal, int doppler_shift, int doppler_offset)
{
unsigned int indext = 0;
float magt = 0.0;
std::complex<float> tmp_cpx;
// FFT frequency-shift property
if (doppler_shift != 0)
{
for (unsigned int i = 0; i < d_fft_size; i++)
{
//complex conjugate (optimize me!)
tmp_cpx = std::complex<float>(d_fft_codes[i].real(),
-d_fft_codes[i].imag());
d_ifft->get_inbuf()[i] = fft_signal[(doppler_shift + i
+ d_fft_size) % d_fft_size] * tmp_cpx;
}
}
else
{
for (unsigned int i = 0; i < d_fft_size; i++)
{
//complex conjugate (optimize me!)
tmp_cpx = std::complex<float>(d_fft_codes[i].real(),
-d_fft_codes[i].imag());
d_ifft->get_inbuf()[i] = fft_signal[i] * tmp_cpx;
}
}
d_ifft->execute(); // inverse FFT of the result = convolution in time
x86_gr_complex_mag(d_ifft->get_outbuf(), d_fft_size); // d_ifft->get_outbuf()=|abs(·)|^2
x86_float_max((float*)d_ifft->get_outbuf(), &indext, &magt, d_fft_size); // find max of |abs(<28>)|^2 -> index and magt
if (magt > d_mag)
{ // if the magnitude is > threshold
// save the synchronization parameters
d_mag = magt;
d_prn_code_phase = indext;
d_doppler_freq_phase = -((doppler_shift * 1000.0) + (doppler_offset
* 250.0));
// save the circular correlation of this Doppler shift
memcpy(d_best_magnitudes, d_ifft->get_outbuf(), sizeof(float)
* d_fft_size);
// Remove the maximum and its neighbors to calculate the mean
((float*)d_ifft->get_outbuf())[indext] = 0.0;
if (indext != 0)
{
((float*)d_ifft->get_outbuf())[indext - 1] = 0.0;
}
if (indext != d_fft_size - 1)
{
((float*)d_ifft->get_outbuf())[indext + 1] = 0.0;
}
for (unsigned int i = 0; i < d_fft_size; i++)
{
d_mean += ((float*)d_ifft->get_outbuf())[i];
}
d_mean = d_mean / d_fft_size;
d_test_statistics = d_mag / d_mean;
}
}

View File

@ -1,165 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition_cc.h
* \brief Brief description of the file here
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_CC_H
#define GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_CC_H
#include <fstream>
#include <gnuradio/gr_block.h>
#include <gnuradio/gr_msg_queue.h>
#include <gnuradio/gr_complex.h>
#include <gnuradio/gri_fft.h>
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "concurrent_queue.h"
#include "gnss_satellite.h"
class gps_l1_ca_gps_sdr_acquisition_cc;
typedef boost::shared_ptr<gps_l1_ca_gps_sdr_acquisition_cc>
gps_l1_ca_gps_sdr_acquisition_cc_sptr;
gps_l1_ca_gps_sdr_acquisition_cc_sptr
gps_l1_ca_gps_sdr_make_acquisition_cc(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
class gps_l1_ca_gps_sdr_acquisition_cc: public gr_block
{
private:
friend gps_l1_ca_gps_sdr_acquisition_cc_sptr
gps_l1_ca_gps_sdr_make_acquisition_cc(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue,
bool dump, std::string dump_filename);
gps_l1_ca_gps_sdr_acquisition_cc(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue,
bool dump, std::string dump_filename);
void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift,
int doppler_offset);
long d_fs_in;
long d_freq;
int d_samples_per_ms;
Gnss_Satellite d_satellite;
float d_threshold;
std::string d_satellite_str;
unsigned int d_doppler_max;
unsigned int d_sampled_ms;
unsigned int d_fft_size;
unsigned long int d_sample_counter;
unsigned long int d_acq_sample_stamp;
gr_complex* d_baseband_signal;
gr_complex* d_sine_if;
gr_complex* d_sine_250;
gr_complex* d_sine_500;
gr_complex* d_sine_750;
gr_complex* d_fft_codes;
gri_fft_complex* d_fft_if;
gri_fft_complex* d_fft_250;
gri_fft_complex* d_fft_500;
gri_fft_complex* d_fft_750;
gri_fft_complex* d_ifft;
float* d_best_magnitudes;
unsigned int d_prn_code_phase;
float d_doppler_freq_phase;
float d_mag;
float d_mean;
float d_test_statistics;
gr_msg_queue_sptr d_queue;
concurrent_queue<int> *d_channel_internal_queue;
std::ofstream d_dump_file;
unsigned int d_count;
bool d_active;
bool d_dump;
unsigned int d_channel;
std::string d_dump_filename;
public:
~gps_l1_ca_gps_sdr_acquisition_cc();
signed int prn_code_phase();
float doppler_freq_phase()
{
return d_doppler_freq_phase;
}
unsigned int mag()
{
return d_mag;
}
unsigned long int get_sample_stamp()
{
return d_acq_sample_stamp;
}
void set_satellite(Gnss_Satellite satellite);
void set_active(bool active)
{
d_active = active;
}
void set_channel(unsigned int channel)
{
d_channel = channel;
}
void set_threshold(float threshold)
{
d_threshold = threshold;
}
void set_doppler_max(unsigned int doppler_max)
{
d_doppler_max = doppler_max;
}
void set_channel_queue(concurrent_queue<int> *channel_internal_queue)
{
d_channel_internal_queue = channel_internal_queue;
}
int general_work(int noutput_items, gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_CC_H */

View File

@ -1,284 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition_ss.cc
* \brief Brief description of the file here
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_gps_sdr_acquisition_ss.h"
#include "gps_sdr_fft.h"
#include "gps_sdr_prn_codes_short.h"
#include "control_message_factory.h"
#include "gps_sdr_x86.h"
#ifndef NO_SIMD
#include "gps_sdr_simd.h"
#endif
#include <gnuradio/gr_io_signature.h>
#include <sstream>
#include <glog/log_severity.h>
#include <glog/logging.h>
using google::LogMessage;
gps_l1_ca_gps_sdr_acquisition_ss_sptr gps_l1_ca_gps_sdr_make_acquisition_ss(
unsigned int sampled_ms, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename)
{
return gps_l1_ca_gps_sdr_acquisition_ss_sptr(
new gps_l1_ca_gps_sdr_acquisition_ss(sampled_ms, freq, fs_in,
samples_per_ms, queue, dump, dump_filename));
}
gps_l1_ca_gps_sdr_acquisition_ss::gps_l1_ca_gps_sdr_acquisition_ss(
unsigned int sampled_ms, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename) :
gr_block("gps_l1_ca_gps_sdr_acquisition_ss", gr_make_io_signature(1, 1,
sizeof(short) * 2 * samples_per_ms), gr_make_io_signature(0, 0,
sizeof(short) * 2 * samples_per_ms))
{
// SAMPLE COUNTER
d_sample_counter = 0;
d_active = false;
d_dump = dump;
d_queue = queue;
d_dump_filename = dump_filename;
d_fs_in = fs_in;
d_samples_per_ms = samples_per_ms;
d_doppler_resolution = 4;
d_freq = freq;
d_satellite = Gnss_Satellite();
d_doppler_max = 0;
d_sampled_ms = sampled_ms;
d_fft_size = d_sampled_ms * d_samples_per_ms;
d_doppler_freq_shift = 0.0;
d_prn_code_phase = 0;
d_mag = 0;
d_sine_if = new CPX[d_fft_size];
d_sine_250 = new CPX[d_fft_size];
d_sine_500 = new CPX[d_fft_size];
d_sine_750 = new CPX[d_fft_size];
d_baseband_signal = new CPX[d_doppler_resolution * d_fft_size];
d_baseband_signal_shift = new CPX[d_doppler_resolution * (d_fft_size
+ 201)];
signed int R1[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
d_pFFT = new FFT(d_fft_size, R1);
signed int R2[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1 };
d_piFFT = new FFT(d_fft_size, R2);
for (int i = 0; i < 32; i++)
{
d_fft_codes[i] = (CPX *)&PRN_Codes_Short[sizeof(short) * i
* d_samples_per_ms];
}
sine_gen(d_sine_if, -d_freq, d_fs_in, d_fft_size);
sine_gen(d_sine_250, -d_freq - 250, d_fs_in, d_fft_size);
sine_gen(d_sine_500, -d_freq - 500, d_fs_in, d_fft_size);
sine_gen(d_sine_750, -d_freq - 750, d_fs_in, d_fft_size);
DLOG(INFO) << "fs in " << d_fs_in;
DLOG(INFO) << "samples per ms " << d_samples_per_ms;
DLOG(INFO) << "doppler resolution " << d_doppler_resolution;
DLOG(INFO) << "freq " << d_freq;
DLOG(INFO) << "satellite " << d_satellite;
DLOG(INFO) << "sampled_ms " << d_sampled_ms;
DLOG(INFO) << "fft_size " << d_fft_size;
DLOG(INFO) << "dump filename " << d_dump_filename;
DLOG(INFO) << "dump " << d_dump;
}
gps_l1_ca_gps_sdr_acquisition_ss::~gps_l1_ca_gps_sdr_acquisition_ss()
{
delete[] d_baseband_signal;
delete[] d_baseband_signal_shift;
delete[] d_sine_if;
delete[] d_sine_250;
delete[] d_sine_500;
delete[] d_sine_750;
delete d_pFFT;
delete d_piFFT;
if (d_dump)
{
d_dump_file.close();
}
}
void gps_l1_ca_gps_sdr_acquisition_ss::set_satellite(Gnss_Satellite satellite)
{
d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
d_prn_code_phase = 0;
d_doppler_freq_shift = 0;
d_mag = 0;
DLOG(INFO) << "satellite set to " << d_satellite;
}
signed int gps_l1_ca_gps_sdr_acquisition_ss::prn_code_phase()
{
return d_prn_code_phase;
}
int gps_l1_ca_gps_sdr_acquisition_ss::general_work(int noutput_items,
gr_vector_int &ninput_items, gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
if (!d_active)
{
d_sample_counter += d_fft_size * noutput_items; // sample counter
consume_each(noutput_items);
}
else
{
d_sample_counter += d_fft_size; // sample counter
const CPX *in = (const CPX *)input_items[0];
CPX* buffer = new CPX[d_fft_size];
signed int index = 0;
unsigned int indext = 0;
unsigned int magt = 0;
DLOG(INFO) << "copied " << (d_fft_size * sizeof(CPX))
<< " bytes into buffer (" << d_fft_size << " samples)";
memcpy(d_baseband_signal, in, d_fft_size * sizeof(CPX));
#ifdef NO_SIMD
x86_cmulsc(d_baseband_signal, d_sine_250,
&d_baseband_signal[d_fft_size], d_fft_size, 14);
x86_cmulsc(d_baseband_signal, d_sine_500, &d_baseband_signal[2
* d_fft_size], d_fft_size, 14);
x86_cmulsc(d_baseband_signal, d_sine_750, &d_baseband_signal[3
* d_fft_size], d_fft_size, 14);
x86_cmuls(d_baseband_signal, d_sine_if, d_fft_size, 14);
#else
sse_cmulsc(d_baseband_signal, d_sine_250,
&d_baseband_signal[d_fft_size], d_fft_size, 14);
sse_cmulsc(d_baseband_signal, d_sine_500, &d_baseband_signal[2
* d_fft_size], d_fft_size, 14);
sse_cmulsc(d_baseband_signal, d_sine_750, &d_baseband_signal[3
* d_fft_size], d_fft_size, 14);
sse_cmuls(d_baseband_signal, d_sine_if, d_fft_size, 14);
#endif
for (unsigned int i = 0; i < d_doppler_resolution; i++)
{
d_pFFT->doFFT(&d_baseband_signal[i * d_fft_size], true);
memcpy(&d_baseband_signal_shift[i * (d_fft_size + 201)],
&d_baseband_signal[(i + 1) * d_fft_size - 100], 100
* sizeof(CPX));
memcpy(&d_baseband_signal_shift[(i * (d_fft_size + 201)) + 100],
&d_baseband_signal[i * d_fft_size], d_fft_size
* sizeof(CPX));
memcpy(&d_baseband_signal_shift[(i * (d_fft_size + 201)) + 100
+ d_fft_size], &d_baseband_signal[i * d_fft_size], 100
* sizeof(CPX));
}
// Here begins the actual acquisition process.
for (int i = -d_doppler_max / 1000; i < (int)d_doppler_max / 1000; i++)
{
for (unsigned int j = 0; j < d_doppler_resolution; j++)
{
#ifdef NO_SIMD
x86_cmulsc(&d_baseband_signal_shift[(j * (d_fft_size + 201))
+ 100 + i], d_fft_codes[d_satellite.get_PRN()], buffer,
d_fft_size, 10);
#else
sse_cmulsc(&d_baseband_signal_shift[(j * (d_fft_size + 201))
+ 100 + i], d_fft_codes[d_satellite.get_PRN()], buffer,
d_fft_size, 10);
#endif
d_piFFT->doiFFT(buffer, true);
x86_cmag(buffer, d_fft_size);
x86_max((unsigned int *)buffer, &indext, &magt, d_fft_size);
if (magt > d_mag)
{
d_mag = magt;
index = indext;
d_prn_code_phase = ceil((index * d_samples_per_ms)
/ d_fft_size);
d_doppler_freq_shift = (i * 1000.0) + (j * 250.0);
if (d_dump)
{
d_dump_file.open(d_dump_filename.c_str(),
std::ios::out | std::ios::binary);
std::streamsize n = sizeof(unsigned int) * d_fft_size;
d_dump_file.write((char*)buffer, n);
d_dump_file.close();
}
}
}
}
DLOG(INFO) << "satellite " << d_satellite;
//result->code_phase = 2048 - index;
DLOG(INFO) << "code phase " << d_prn_code_phase;
//result->doppler = (lcv*1000) + (float)lcv2*250;
DLOG(INFO) << "doppler " << d_doppler_freq_shift;
//result->magnitude = mag;
DLOG(INFO) << "magnitude " << d_mag;
d_active = false;
delete buffer;
DLOG(INFO) << "Acquisition done";
int acquisition_message = -1; //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL
if (d_mag > d_threshold)
{
d_acq_sample_stamp = d_sample_counter;
acquisition_message = 1; //ACQ_SUCCES
}
else
{
acquisition_message = 2; //ACQ_FAIL
}
d_channel_internal_queue->push(acquisition_message);
consume_each(1);
}
return 0;
}

View File

@ -1,153 +0,0 @@
/*!
* \file gps_l1_ca_gps_sdr_acquisition_ss.h
* \brief Brief description of the file here
* \author Carlos Aviles, 2010. carlos.avilesr(at)googlemail.com
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_SS_H
#define GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_SS_H
#include <fstream>
#include <gnuradio/gr_block.h>
#include <gnuradio/gr_msg_queue.h>
#include "gps_sdr_signal_processing.h"
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "concurrent_queue.h"
#include "gnss_satellite.h"
class FFT;
class gps_l1_ca_gps_sdr_acquisition_ss;
typedef boost::shared_ptr<gps_l1_ca_gps_sdr_acquisition_ss>
gps_l1_ca_gps_sdr_acquisition_ss_sptr;
gps_l1_ca_gps_sdr_acquisition_ss_sptr
gps_l1_ca_gps_sdr_make_acquisition_ss(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
class gps_l1_ca_gps_sdr_acquisition_ss: public gr_block
{
private:
friend gps_l1_ca_gps_sdr_acquisition_ss_sptr
gps_l1_ca_gps_sdr_make_acquisition_ss(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue,
bool dump, std::string dump_filename);
gps_l1_ca_gps_sdr_acquisition_ss(unsigned int sampled_ms, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue,
bool dump, std::string dump_filename);
long d_fs_in;
long d_freq;
int d_samples_per_ms;
Gnss_Satellite d_satellite;
float d_threshold;
unsigned int d_doppler_max;
unsigned int d_doppler_resolution;
unsigned int d_sampled_ms;
unsigned int d_fft_size;
unsigned long int d_sample_counter;
unsigned long int d_acq_sample_stamp;
CPX* d_baseband_signal;
CPX* d_baseband_signal_shift;
CPX* d_sine_if;
CPX* d_sine_250;
CPX* d_sine_500;
CPX* d_sine_750;
FFT* d_pFFT;
FFT* d_piFFT;
CPX* d_fft_codes[32];
signed int d_prn_code_phase;
float d_doppler_freq_shift;
unsigned int d_mag;
gr_msg_queue_sptr d_queue;
concurrent_queue<int> *d_channel_internal_queue;
std::ofstream d_dump_file;
bool d_active;
bool d_dump;
unsigned int d_channel;
std::string d_dump_filename;
public:
~gps_l1_ca_gps_sdr_acquisition_ss();
signed int prn_code_phase();
float doppler_freq_phase()
{
return d_doppler_freq_shift;
}
unsigned int mag()
{
return d_mag;
}
unsigned long int get_sample_stamp()
{
return d_acq_sample_stamp;
}
void set_satellite(Gnss_Satellite satellite);
void set_active(bool active)
{
d_active = active;
}
void set_channel(unsigned int channel)
{
d_channel = channel;
}
void set_threshold(float threshold)
{
d_threshold = threshold;
}
void set_doppler_max(unsigned int doppler_max)
{
d_doppler_max = doppler_max;
}
void set_channel_queue(concurrent_queue<int> *channel_internal_queue)
{
d_channel_internal_queue = channel_internal_queue;
}
int general_work(int noutput_items, gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* GNSS_SDR_GPS_L1_CA_GPS_SDR_ACQUISITION_SS_H */

View File

@ -1,164 +0,0 @@
/*!
* \file gps_l1_ca_pcps_acquisition_cc.h
* \brief Brief description of the file here
* \author Javier Arribas, 2011. jarribas(at)cttc.es
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_A_CC_H
#define GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_A_CC_H
#include <fstream>
#include <gnuradio/gr_block.h>
#include <gnuradio/gr_msg_queue.h>
#include <gnuradio/gr_complex.h>
#include <gnuradio/gri_fft.h>
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "concurrent_queue.h"
#include "gnss_synchro.h"
class gps_l1_ca_pcps_acquisition_cc;
typedef boost::shared_ptr<gps_l1_ca_pcps_acquisition_cc>
gps_l1_ca_pcps_acquisition_cc_sptr;
gps_l1_ca_pcps_acquisition_cc_sptr
gps_l1_ca_pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename);
/*!
* \brief This class implements a PCPS acquisition block for GPS L1 C/A
*/
class gps_l1_ca_pcps_acquisition_cc: public gr_block
{
private:
friend gps_l1_ca_pcps_acquisition_cc_sptr
gps_l1_ca_pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
gps_l1_ca_pcps_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift,
int doppler_offset);
long d_fs_in;
long d_freq;
int d_samples_per_ms;
unsigned int d_doppler_resolution;
float d_threshold;
std::string d_satellite_str;
unsigned int d_doppler_max;
unsigned int d_doppler_step;
unsigned int d_sampled_ms;
unsigned int d_fft_size;
unsigned long int d_sample_counter;
gr_complex* d_sine_if;
gr_complex* d_fft_codes;
gri_fft_complex* d_fft_if;
gri_fft_complex* d_ifft;
Gnss_Synchro *d_gnss_synchro;
unsigned int d_code_phase;
float d_doppler_freq;
float d_mag;
float d_input_power;
float d_test_statistics;
gr_msg_queue_sptr d_queue;
concurrent_queue<int> *d_channel_internal_queue;
std::ofstream d_dump_file;
bool d_active;
bool d_dump;
unsigned int d_channel;
std::string d_dump_filename;
public:
~gps_l1_ca_pcps_acquisition_cc();
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro)
{
d_gnss_synchro = p_gnss_synchro;
}
unsigned int mag()
{
return d_mag;
}
void init();
void set_active(bool active)
{
d_active = active;
}
void set_channel(unsigned int channel)
{
d_channel = channel;
}
void set_threshold(float threshold)
{
d_threshold = threshold;
}
void set_doppler_max(unsigned int doppler_max)
{
d_doppler_max = doppler_max;
}
void set_doppler_step(unsigned int doppler_step)
{
d_doppler_step = doppler_step;
}
void set_channel_queue(concurrent_queue<int> *channel_internal_queue)
{
d_channel_internal_queue = channel_internal_queue;
}
int general_work(int noutput_items, gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* GNSS_SDR_GPS_L1_CA_PCPS_ACQUISITION_CC_H*/

View File

@ -1,360 +0,0 @@
/*!
* \file gps_l1_ca_tong_pcps_acquisition_cc.cc
* \brief Brief description of the file here
* \author Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_tong_pcps_acquisition_cc.h"
#include "gps_sdr_signal_processing.h"
#include "control_message_factory.h"
#include "gps_sdr_x86.h"
#include <gnuradio/gr_io_signature.h>
#include <sstream>
#include <glog/log_severity.h>
#include <glog/logging.h>
using google::LogMessage;
gps_l1_ca_tong_pcps_acquisition_cc_sptr gps_l1_ca_tong_pcps_make_acquisition_cc(
unsigned int sampled_ms, unsigned int doppler_max, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename)
{
return gps_l1_ca_tong_pcps_acquisition_cc_sptr(
new gps_l1_ca_tong_pcps_acquisition_cc(sampled_ms, doppler_max,
freq, fs_in, samples_per_ms, queue, dump, dump_filename));
}
gps_l1_ca_tong_pcps_acquisition_cc::gps_l1_ca_tong_pcps_acquisition_cc(
unsigned int sampled_ms, unsigned int doppler_max, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename) :
gr_block("gps_l1_ca_tong_pcps_acquisition_cc", gr_make_io_signature(1, 1,
sizeof(gr_complex) * samples_per_ms), gr_make_io_signature(0, 0,
sizeof(gr_complex) * samples_per_ms))
{
// SAMPLE COUNTER
d_sample_counter = 0;
d_active = false;
d_dump = dump;
d_queue = queue;
d_dump_filename = dump_filename;
d_freq = freq;
d_fs_in = fs_in;
d_samples_per_ms = samples_per_ms;
d_sampled_ms = sampled_ms;
d_doppler_max = doppler_max;
d_satellite = Gnss_Satellite();
d_samples = d_sampled_ms * d_samples_per_ms;
d_doppler_freq = 0.0;
d_code_phase = 0;
d_mag = 0.0;
d_noise_power = 0.0;
d_fbins = 0;
d_doppler = 0;
d_pfa = 0.2;
d_A = 8;
d_B = 1;
d_max_dwells = 15;
d_K = d_B;
d_if_sin = new gr_complex[d_samples];
d_fft_codes = (gr_complex*)malloc(sizeof(gr_complex) * d_samples_per_ms);
// Direct FFT
d_fft_if = new gri_fft_complex(d_samples, true);
// Inverse FFT
d_ifft = new gri_fft_complex(d_samples, false);
d_ca_codes = new gr_complex[d_samples];
d_aux_ca_code = new gr_complex[d_samples];
//generates a unused PRN code to calculate the noise envelope
code_gen_complex_sampled(d_aux_ca_code, 33, d_fs_in, 0);
DLOG(INFO) << "fs in " << d_fs_in;
DLOG(INFO) << "samples per ms " << d_samples_per_ms;
DLOG(INFO) << "doppler max " << d_doppler_max;
DLOG(INFO) << "freq " << d_freq;
DLOG(INFO) << "satellite " << d_satellite;
DLOG(INFO) << "sampled_ms " << d_sampled_ms;
DLOG(INFO) << "Samples_for_processing " << d_samples;
DLOG(INFO) << "dump filename " << d_dump_filename;
DLOG(INFO) << "dump " << d_dump;
}
gps_l1_ca_tong_pcps_acquisition_cc::~gps_l1_ca_tong_pcps_acquisition_cc()
{
delete[] d_if_sin;
delete[] d_ca_codes;
delete[] d_aux_ca_code;
delete d_fft_if;
delete d_ifft;
if (d_dump)
{
d_dump_file.close();
}
}
void gps_l1_ca_tong_pcps_acquisition_cc::set_satellite(Gnss_Satellite satellite)
{
d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
d_code_phase = 0;
d_doppler_freq = 0;
d_mag = 0.0;
d_noise_power = 0.0;
// The GPS codes are generated on the fly using a custom version of the GPS code generator
//! \TODO In-memory codes instead of generated on the fly
code_gen_complex_sampled(d_fft_if->get_inbuf(), satellite.get_PRN(), d_fs_in, 0);
d_fft_if->execute(); // We need the FFT of GPS C/A code
//Conjugate the local code
//! \TODO Optimize it ! Try conj() or Armadillo
for (unsigned int i = 0; i < d_samples; i++)
{
d_fft_codes[i] = std::complex<float>(
d_fft_if->get_outbuf()[i].real(),
-d_fft_if->get_outbuf()[i].imag());
}
}
signed int gps_l1_ca_tong_pcps_acquisition_cc::prn_code_phase()
{
return d_code_phase;
}
int gps_l1_ca_tong_pcps_acquisition_cc::general_work(int noutput_items,
gr_vector_int &ninput_items, gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
if (!d_active)
{
// sample counter
d_sample_counter += d_samples * noutput_items;
consume_each(noutput_items);
}
else
{
d_sample_counter += d_samples;
// initialize acquisition algorithm
bool positive_acquisition = false;
int acquisition_message = -1; //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL
//float noise_envelope = 0.0;
float vt = 20000;
//float peak = 0.0;
float magt = 0.0;
unsigned int max_freq_step = 2 * (unsigned int)(d_doppler_max
/ d_doppler_step);
unsigned int indext = 0;
// Get the input samples pointer
const gr_complex *in = (const gr_complex *)input_items[0];
// aux vars
std::stringstream filename;
//unsigned int consume_items = 1;
// complex file write
// std::streamsize n = 2 * sizeof(float) * (d_samples);
// 1 - Compute the input noise envelope estimation and the threshold vt
// sine_gen_complex( d_if_sin, d_freq + doppler, d_fs_in, d_samples );
//
// noise_envelope = calculate_envelope( in, d_aux_ca_code, d_if_sin );
// vt = noise_envelope * sqrt( -2 * log( d_pfa ) );
// 1- Compute the input signal power estimation
for (unsigned int i = 0; i < d_samples; i++)
{
d_noise_power += std::abs(in[i]);
}
d_noise_power = sqrt(d_noise_power / (float)d_samples);
//2. Perform the carrier wipe-off
sine_gen_complex(d_if_sin, d_freq + d_doppler, d_fs_in, d_samples);
for (unsigned int i = 0; i < d_samples; i++)
{
d_fft_if->get_inbuf()[i] = in[i] * d_if_sin[i];
}
//3- Perform the FFT-based circular convolution (parallel time search)
d_fft_if->execute();
//TODO Optimize me: use Armadillo!
for (unsigned int i = 0; i < d_samples; i++)
{
d_ifft->get_inbuf()[i] = d_fft_if->get_outbuf()[i]
* d_fft_codes[i];
}
d_ifft->execute();
x86_gr_complex_mag(d_ifft->get_outbuf(), d_samples); // d_ifft->get_outbuf()=|abs(·)|^2 and the array is converted from CPX->Float
x86_float_max((float*)d_ifft->get_outbuf(), &d_indext, &magt,
d_samples); // find max of |abs(·)|^2 -> index and magt
magt = sqrt(magt) / (float)d_samples;
d_test_statistics = magt / d_noise_power;
LOG_AT_LEVEL(INFO) << "Channel: " << d_channel
<< ", doing Tong PCSS acquisition of satellite: "
<< d_satellite << ", sample stamp: " << d_sample_counter
<< ", bin_freq " << d_doppler << ", doppler_max: "
<< d_doppler_max << ", K " << d_K << ", sigma: "
<< d_noise_power << ", mag: " << d_test_statistics
<< ", vt: " << vt;
if ((d_test_statistics > vt) && (indext = d_indext))
{
d_K++;
if (d_K == d_A)
{
d_code_phase = d_indext;
positive_acquisition = true;
d_doppler_freq = d_doppler;
d_acq_sample_stamp = d_sample_counter;
LOG_AT_LEVEL(INFO) << "positive acquisition";
LOG_AT_LEVEL(INFO) << "satellite " << d_satellite;
LOG_AT_LEVEL(INFO) << "sample_stamp " << d_sample_counter;
LOG_AT_LEVEL(INFO) << "test statistics value "
<< d_test_statistics;
LOG_AT_LEVEL(INFO) << "test statistics threshold " << vt;
LOG_AT_LEVEL(INFO) << "code phase " << d_code_phase;
LOG_AT_LEVEL(INFO) << "doppler " << d_doppler_freq;
LOG_AT_LEVEL(INFO) << "magnitude " << magt;
LOG_AT_LEVEL(INFO) << "input signal power " << d_noise_power;
d_dwells = 0;
d_active = false;
}
else d_dwells++;
}
else
{
d_K--;
if ((d_K == 0) || (d_dwells > d_max_dwells))
{
d_K = d_B;
d_dwells = 0;
d_fbins++;
if (d_fbins > max_freq_step)
{
d_fbins = 0;
LOG_AT_LEVEL(INFO) << "negative acquisition";
LOG_AT_LEVEL(INFO) << "satellite " << d_satellite;
LOG_AT_LEVEL(INFO) << "sample_stamp" << d_sample_counter;
LOG_AT_LEVEL(INFO) << "test statistics value "
<< d_test_statistics;
LOG_AT_LEVEL(INFO) << "test statistics threshold " << vt;
LOG_AT_LEVEL(INFO) << "input signal power "
<< d_noise_power;
d_active = false;
}
else
{
d_doppler = d_doppler + pow(-1, d_fbins + 1) * d_fbins
* d_doppler_step;
}
}
else d_dwells++;
}
// Record results to files
// if( d_dump )
// {
// filename.str( "" );
// filename << "./data/fft_" << doppler << "_.dat";
// std::cout << filename.str().c_str();
// std::cout << ".\n";
// d_dump_file.open( filename.str().c_str(), std::ios::out
// | std::ios::binary );
// d_dump_file.write( (char*) d_ifft->get_outbuf(), n ); //write directly |abs(·)|^2 in this Doppler bin
// d_dump_file.close();
// }
if (d_active == false)
{
if (positive_acquisition)
{
acquisition_message = 1;
}
else
{
acquisition_message = 2;
}
d_channel_internal_queue->push(acquisition_message);
}
consume_each(1);
}
return 0;
}
float gps_l1_ca_tong_pcps_acquisition_cc::calculate_envelope(
const gr_complex* _input_signal, std::complex<float>* _local_code,
std::complex<float>* _local_if_sin)
{
float mag = 0.0;
std::complex<float> tmp_cpx = 0.0;
//std::cout << "tmp_cpx " << tmp_cpx << std::endl;
for (unsigned int i = 0; i < d_samples; i++)
{
tmp_cpx = tmp_cpx + _input_signal[i] * _local_code[i]
* _local_if_sin[i];
}
//std::cout << "tmp_cpx " << tmp_cpx << std::endl;
mag = abs(tmp_cpx);
return mag;
}

View File

@ -1,187 +0,0 @@
/*!
* \file gps_l1_ca_tong_pcps_acquisition_cc.h
* \brief Brief description of the file here
* \author Luis Esteve, 2011. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_GPS_L1_CA_TONG_PCPS_ACQUISITION_CC_H
#define GNSS_SDR_GPS_L1_CA_TONG_PCPS_ACQUISITION_CC_H
#include <fstream>
#include <gnuradio/gr_block.h>
#include <gnuradio/gr_msg_queue.h>
#include <gnuradio/gr_complex.h>
#include <gnuradio/gri_fft.h>
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "concurrent_queue.h"
#include "gnss_satellite.h"
class gps_l1_ca_tong_pcps_acquisition_cc;
typedef boost::shared_ptr<gps_l1_ca_tong_pcps_acquisition_cc>
gps_l1_ca_tong_pcps_acquisition_cc_sptr;
gps_l1_ca_tong_pcps_acquisition_cc_sptr
gps_l1_ca_tong_pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename);
class gps_l1_ca_tong_pcps_acquisition_cc: public gr_block
{
private:
friend gps_l1_ca_tong_pcps_acquisition_cc_sptr
gps_l1_ca_tong_pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
gps_l1_ca_tong_pcps_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
long d_fs_in;
long d_freq;
long d_doppler;
int d_samples_per_ms;
unsigned int d_doppler_resolution;
Gnss_Satellite d_satellite;
std::string d_satellite_str;
unsigned int d_doppler_max;
unsigned int d_doppler_step;
unsigned int d_sampled_ms;
unsigned int d_samples;
unsigned long int d_sample_counter;
unsigned long int d_acq_sample_stamp;
unsigned int d_fbins;
unsigned int d_indext;
unsigned d_dwells;
float d_pfa;
unsigned int d_A;
unsigned int d_B;
unsigned int d_max_dwells;
unsigned int d_K;
gr_complex* d_if_sin;
gr_complex* d_fft_codes;
gri_fft_complex* d_fft_if;
gri_fft_complex* d_ifft;
gr_complex* d_ca_codes;
gr_complex* d_aux_ca_code;
unsigned int d_code_phase;
float d_doppler_freq;
float d_mag;
float d_noise_power;
float d_test_statistics;
gr_msg_queue_sptr d_queue;
concurrent_queue<int> *d_channel_internal_queue;
std::ofstream d_dump_file;
bool d_active;
bool d_dump;
unsigned int d_channel;
std::string d_dump_filename;
public:
~gps_l1_ca_tong_pcps_acquisition_cc();
signed int prn_code_phase();
float doppler_freq()
{
return d_doppler_freq;
}
unsigned int mag()
{
return d_mag;
}
unsigned long int get_sample_stamp()
{
return d_acq_sample_stamp;
}
void set_satellite(Gnss_Satellite satellite);
void set_active(bool active)
{
d_active = active;
}
void set_channel(unsigned int channel)
{
d_channel = channel;
}
void set_doppler_max(unsigned int doppler_max)
{
d_doppler_max = doppler_max;
}
void set_doppler_step(unsigned int doppler_step)
{
d_doppler_step = doppler_step;
}
void set_doppler(unsigned int doppler)
{
d_doppler = doppler;
}
void set_dwells(unsigned int dwells)
{
d_dwells = dwells;
}
float calculate_envelope(const gr_complex* _input_signal, std::complex<
float>* _local_code, std::complex<float>* _local_if_sin);
void set_channel_queue(concurrent_queue<int> *channel_internal_queue)
{
d_channel_internal_queue = channel_internal_queue;
}
int general_work(int noutput_items, gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* GNSS_SDR_GPS_L1_CA_TONG_PCPS_ACQUISITION_CC_H*/

View File

@ -1,6 +1,3 @@
project : build-dir ../../../../build ;
#obj gps_l1_ca_gps_sdr_acquisition_cc : gps_l1_ca_gps_sdr_acquisition_cc.cc ;
#obj gps_l1_ca_gps_sdr_acquisition_ss : gps_l1_ca_gps_sdr_acquisition_ss.cc : <toolset>darwin:<define>NO_SIMD <toolset>gcc:<define>USE_SIMD ;
obj gps_l1_ca_pcps_acquisition_cc : gps_l1_ca_pcps_acquisition_cc.cc ;
#obj gps_l1_ca_tong_pcps_acquisition_cc : gps_l1_ca_tong_pcps_acquisition_cc.cc ;
obj pcps_acquisition_cc : pcps_acquisition_cc.cc ;

View File

@ -1,8 +1,8 @@
/*!
* \file gps_l1_ca_pcps_acquisition_cc.h
* \brief Brief description of the file here
* \file pcps_acquisition_cc.cc
* \brief This class implements a Parallell Code Phase Search Acquisition
* \author Javier Arribas, 2011. jarribas(at)cttc.es
* Luis Esteve, 2011. luis(at)epsilon-formacion.com
* Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
@ -31,8 +31,8 @@
* -------------------------------------------------------------------------
*/
#include "gps_l1_ca_pcps_acquisition_cc.h"
#include "gps_sdr_signal_processing.h"
#include "pcps_acquisition_cc.h"
#include "gnss_signal_processing.h"
#include "control_message_factory.h"
#include <gnuradio/gr_io_signature.h>
#include <sstream>
@ -41,24 +41,24 @@
using google::LogMessage;
gps_l1_ca_pcps_acquisition_cc_sptr gps_l1_ca_pcps_make_acquisition_cc(
pcps_acquisition_cc_sptr pcps_make_acquisition_cc(
unsigned int sampled_ms, unsigned int doppler_max, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename)
{
return gps_l1_ca_pcps_acquisition_cc_sptr(
new gps_l1_ca_pcps_acquisition_cc(sampled_ms, doppler_max, freq,
return pcps_acquisition_cc_sptr(
new pcps_acquisition_cc(sampled_ms, doppler_max, freq,
fs_in, samples_per_ms, queue, dump, dump_filename));
}
gps_l1_ca_pcps_acquisition_cc::gps_l1_ca_pcps_acquisition_cc(
pcps_acquisition_cc::pcps_acquisition_cc(
unsigned int sampled_ms, unsigned int doppler_max, long freq,
long fs_in, int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename) :
gr_block("gps_l1_ca_pcps_acquisition_cc", gr_make_io_signature(1, 1,
sizeof(gr_complex) * samples_per_ms), gr_make_io_signature(0, 0,
sizeof(gr_complex) * samples_per_ms))
gr_block("pcps_acquisition_cc", gr_make_io_signature(1, 1,
sizeof(gr_complex) * sampled_ms *samples_per_ms), gr_make_io_signature(0, 0,
sizeof(gr_complex) * sampled_ms *samples_per_ms))
{
d_sample_counter = 0; // SAMPLE COUNTER
d_active = false;
@ -69,15 +69,12 @@ gps_l1_ca_pcps_acquisition_cc::gps_l1_ca_pcps_acquisition_cc(
d_sampled_ms = sampled_ms;
d_doppler_max = doppler_max;
d_fft_size = d_sampled_ms * d_samples_per_ms;
//d_doppler_freq = 0.0;
//d_code_phase = 0;
d_mag = 0;
d_input_power = 0.0;
d_sine_if = new gr_complex[d_fft_size];
d_fft_codes = (gr_complex*)malloc(sizeof(gr_complex) * d_samples_per_ms);
d_fft_codes = (gr_complex*)malloc(sizeof(gr_complex) * d_fft_size);
// Direct FFT
d_fft_if = new gri_fft_complex(d_fft_size, true);
@ -90,72 +87,57 @@ gps_l1_ca_pcps_acquisition_cc::gps_l1_ca_pcps_acquisition_cc(
}
gps_l1_ca_pcps_acquisition_cc::~gps_l1_ca_pcps_acquisition_cc()
pcps_acquisition_cc::~pcps_acquisition_cc()
{
delete[] d_sine_if;
delete[] d_fft_codes;
delete d_ifft;
delete d_fft_if;
delete[] d_sine_if;
delete[] d_fft_codes;
delete d_ifft;
delete d_fft_if;
if (d_dump)
{
d_dump_file.close();
}
{
d_dump_file.close();
}
}
void pcps_acquisition_cc::set_local_code(std::complex<float> * code)
{
memcpy(d_fft_if->get_inbuf(),code,sizeof(gr_complex)*d_fft_size);
}
void gps_l1_ca_pcps_acquisition_cc::init()
void pcps_acquisition_cc::init()
{
d_gnss_synchro->Acq_delay_samples = 0.0;
d_gnss_synchro->Acq_doppler_hz = 0.0;
d_gnss_synchro->Acq_samplestamp_samples = 0;
d_gnss_synchro->Acq_delay_samples=0.0;
d_gnss_synchro->Acq_doppler_hz=0.0;
d_gnss_synchro->Acq_samplestamp_samples=0;
//d_code_phase = 0;
//d_doppler_freq = 0;
d_mag = 0.0;
d_input_power = 0.0;
// Now the GPS codes are generated on the fly using a custom version of the GPS code generator
code_gen_complex_sampled(d_fft_if->get_inbuf(), d_gnss_synchro->PRN, d_fs_in, 0);
d_fft_if->execute(); // We need the FFT of local code
d_fft_if->execute(); // We need the FFT of GPS C/A code
//Conjugate the local code
//TODO Optimize it !
for (unsigned int i = 0; i < d_fft_size; i++)
{
d_fft_codes[i] = std::complex<float>(conj(d_fft_if->get_outbuf()[i]));
d_fft_codes[i] = d_fft_codes[i] / (float)d_fft_size; //to correct the scale factor introduced by FFTW
}
//memcpy(d_fft_codes,d_fft_if->get_outbuf(),sizeof(gr_complex)*d_samples_per_ms);
// std::stringstream filename;
// std::streamsize n = 2*sizeof(float)*(d_fft_size); // complex file write
// filename.str("");
// filename << "./data/code.dat";
// std::cout<<filename.str().c_str();
// std::cout<<".\n";
// d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary);
//
//
// d_dump_file.write((char*)d_ifft->get_inbuf(), n); //write directly |abs(·)|^2 in this Doppler bin
// //d_dump_file.write((char*)d_sine_if, n); //to be read with read_complex_binary() -> test OK
// d_dump_file.close();
}
int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
int pcps_acquisition_cc::general_work(int noutput_items,
gr_vector_int &ninput_items, gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
/*
* By J.Arribas
* By J.Arribas and L.Esteve
* Acquisition strategy (Kay Borre book + CFAR threshold):
* 1. Compute the input signal power estimation
* 2. Doppler serial search loop
@ -175,16 +157,12 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
d_sample_counter += d_fft_size; // sample counter
//restart acquisition variables
d_gnss_synchro->Acq_delay_samples = 0.0;
d_gnss_synchro->Acq_doppler_hz = 0.0;
//d_code_phase = 0;
//d_doppler_freq = 0;
d_gnss_synchro->Acq_delay_samples=0.0;
d_gnss_synchro->Acq_doppler_hz=0.0;
d_mag = 0.0;
d_input_power = 0.0;
// initialize acquisition algorithm
int doppler;
unsigned int indext = 0;
float magt = 0.0;
@ -195,6 +173,7 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
int acquisition_message = -1; //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL
//aux vars
unsigned int i;
DLOG(INFO) << "Channel: " << d_channel
@ -214,9 +193,9 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
// 2- Doppler frequency search loop
for (doppler = (int)(-d_doppler_max); doppler < (int)d_doppler_max; doppler += d_doppler_step)
{
// doppler search steps
//doppler search steps
//Perform the carrier wipe-off
sine_gen_complex(d_sine_if, d_freq + doppler, d_fs_in, d_fft_size);
complex_exp_gen(d_sine_if, d_freq + doppler, d_fs_in, d_fft_size);
for (i = 0; i < d_fft_size; i++)
{
d_fft_if->get_inbuf()[i] = in[i] * d_sine_if[i];
@ -250,12 +229,12 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
std::stringstream filename;
std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write
filename.str("");
filename << "./data/fft_" << doppler << "_.dat";
std::cout << filename.str().c_str();
std::cout << ".\n";
filename << "../data/fft_galileo_e1_sat_" << d_gnss_synchro->PRN << "_doppler_"<< doppler << ".dat";
//std::cout << filename.str().c_str();
//std::cout << ".\n";
d_dump_file.open(filename.str().c_str(), std::ios::out
| std::ios::binary);
d_dump_file.write((char*)d_ifft->get_outbuf(), n); //write directly |abs(<EFBFBD>)|^2 in this Doppler bin?
d_dump_file.write((char*)d_ifft->get_outbuf(), n); //write directly |abs(x)|^2 in this Doppler bin?
d_dump_file.close();
}
@ -263,8 +242,10 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
if (d_mag < magt)
{
d_mag = magt;
d_gnss_synchro->Acq_delay_samples = (double)indext;
d_gnss_synchro->Acq_doppler_hz = (double)doppler;
d_gnss_synchro->Acq_delay_samples= (double)indext;
d_gnss_synchro->Acq_doppler_hz= (double)doppler;
//d_code_phase = indext;
//d_doppler_freq = doppler;
}
}
@ -273,7 +254,6 @@ int gps_l1_ca_pcps_acquisition_cc::general_work(int noutput_items,
// 6- Declare positive or negative acquisition using a message queue
if (d_test_statistics > d_threshold)
{
positive_acquisition = true;

View File

@ -0,0 +1,169 @@
/*!
* \file pcps_acquisition_cc.h
* \brief This class implements a Parallell Code Phase Search Acquisition
*
* Acquisition strategy (Kay Borre book + CFAR threshold):
* 1. Compute the input signal power estimation
* 2. Doppler serial search loop
* 3. Perform the FFT-based circular convolution (parallel time search)
* 4. Record the maximum peak and the associated synchronization parameters
* 5. Compute the test statistics and compare to the threshold
* 6. Declare positive or negative acquisition using a message queue
*
* \author Javier Arribas, 2011. jarribas(at)cttc.es
* Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef PCPS_ACQUISITION_CC_H_
#define PCPS_ACQUISITION_CC_H_
#include <fstream>
#include <gnuradio/gr_block.h>
#include <gnuradio/gr_msg_queue.h>
#include <gnuradio/gr_complex.h>
#include <gnuradio/gri_fft.h>
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "concurrent_queue.h"
#include "gnss_synchro.h"
class pcps_acquisition_cc;
typedef boost::shared_ptr<pcps_acquisition_cc>
pcps_acquisition_cc_sptr;
pcps_acquisition_cc_sptr
pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in, int samples_per_ms,
gr_msg_queue_sptr queue, bool dump, std::string dump_filename);
class pcps_acquisition_cc: public gr_block {
private:
friend pcps_acquisition_cc_sptr
pcps_make_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
pcps_acquisition_cc(unsigned int sampled_ms,
unsigned int doppler_max, long freq, long fs_in,
int samples_per_ms, gr_msg_queue_sptr queue, bool dump,
std::string dump_filename);
void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift,
int doppler_offset);
long d_fs_in;
long d_freq;
int d_samples_per_ms;
unsigned int d_doppler_resolution;
float d_threshold;
std::string d_satellite_str;
unsigned int d_doppler_max;
unsigned int d_doppler_step;
unsigned int d_sampled_ms;
unsigned int d_fft_size;
unsigned long int d_sample_counter;
gr_complex* d_sine_if;
gr_complex* d_fft_codes;
gri_fft_complex* d_fft_if;
gri_fft_complex* d_ifft;
Gnss_Synchro *d_gnss_synchro;
unsigned int d_code_phase;
float d_doppler_freq;
float d_mag;
float d_input_power;
float d_test_statistics;
gr_msg_queue_sptr d_queue;
concurrent_queue<int> *d_channel_internal_queue;
std::ofstream d_dump_file;
bool d_active;
bool d_dump;
unsigned int d_channel;
std::string d_dump_filename;
public:
~pcps_acquisition_cc();
void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro)
{
d_gnss_synchro = p_gnss_synchro;
}
unsigned int mag()
{
return d_mag;
}
void init();
void set_local_code(std::complex<float> * code);
void set_active(bool active)
{
d_active = active;
}
void set_channel(unsigned int channel)
{
d_channel = channel;
}
void set_threshold(float threshold)
{
d_threshold = threshold;
}
void set_doppler_max(unsigned int doppler_max)
{
d_doppler_max = doppler_max;
}
void set_doppler_step(unsigned int doppler_step)
{
d_doppler_step = doppler_step;
}
void set_channel_queue(concurrent_queue<int> *channel_internal_queue)
{
d_channel_internal_queue = channel_internal_queue;
}
int general_work(int noutput_items, gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* PCPS_ACQUISITION_CC_H_*/

View File

@ -49,7 +49,6 @@ class ConfigurationInterface;
class AcquisitionInterface;
class TrackingInterface;
class TelemetryDecoderInterface;
//class GpsL1CaChannelFsm;
/*!
* \brief This class represents a GNSS channel.

View File

@ -0,0 +1,189 @@
/*!
* \file galileo_e1_signal_processing.cc
* \brief This library implements various functions for Galileo E1 signals
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "galileo_e1_signal_processing.h"
void galileo_e1_code_gen_int(int* _dest, char _Signal[3], signed int _prn,
unsigned int _chip_shift)
{
std::string _galileo_signal = _Signal;
signed int prn = _prn - 1;
int* dest = _dest;
/* A simple error check */
if ((_prn < 1) || (_prn > 50))
{
return;
}
if (_galileo_signal.compare("1B") == 0)
{
for (size_t i = 0; i < Galileo_E1_B_PRIMARY_CODE[prn].length(); i++)
{
hex_to_binary_converter(dest,
Galileo_E1_B_PRIMARY_CODE[prn].at(i));
dest = dest + 4;
}
}
else if (_galileo_signal.compare("1C") == 0)
{
for (size_t i = 0; i < Galileo_E1_C_PRIMARY_CODE[prn].length(); i++)
{
hex_to_binary_converter(dest,
Galileo_E1_C_PRIMARY_CODE[prn].at(i));
dest = dest + 4;
}
}
else
{
return;
}
}
void galileo_e1_sinboc_11_gen(std::complex<float>* _dest, int* _prn,
unsigned int _length_out)
{
const unsigned int _length_in = Galileo_E1_B_CODE_LENGTH_CHIPS;
unsigned int _period = (unsigned int) (_length_out / _length_in);
for (unsigned int i = 0; i < _length_in; i++)
{
for (unsigned int j = 0; j < (_period / 2); j++)
_dest[i * _period + j] = std::complex<float>((float) _prn[i],
0.0);
for (unsigned int j = (_period / 2); j < _period; j++)
_dest[i * _period + j] = std::complex<float>((float) (-_prn[i]),
0.0);
}
}
void galileo_e1_sinboc_61_gen(std::complex<float>* _dest, int* _prn,
unsigned int _length_out)
{
const unsigned int _length_in = Galileo_E1_B_CODE_LENGTH_CHIPS;
unsigned int _period = (unsigned int) (_length_out / _length_in);
for (unsigned int i = 0; i < _length_in; i++)
{
for (unsigned int j = 0; j < _period; j += 2)
_dest[i * _period + j] = std::complex<float>((float) _prn[i],
0.0);
for (unsigned int j = 1; j < _period; j += 2)
_dest[i * _period + j] = std::complex<float>((float) (-_prn[i]),
0.0);
}
}
void galileo_e1_gen(std::complex<float>* _dest, int* _prn, char _Signal[3])
{
std::string _galileo_signal = _Signal;
const unsigned int _codeLength = 12 * Galileo_E1_B_CODE_LENGTH_CHIPS;
const float alpha = sqrt(10.0 / 11.0);
const float beta = sqrt(1.0 / 11.0);
std::complex<float> sinboc_11[_codeLength];
std::complex<float> sinboc_61[_codeLength];
galileo_e1_sinboc_11_gen(sinboc_11, _prn, _codeLength); //generate sinboc(1,1) 12 samples per chip
galileo_e1_sinboc_61_gen(sinboc_61, _prn, _codeLength); //generate sinboc(6,1) 12 samples per chip
if (_galileo_signal.compare("1B") == 0)
{
for (unsigned int i = 0; i < _codeLength; i++)
{
_dest[i] = alpha * sinboc_11[i] + beta * sinboc_61[i];
}
}
else if (_galileo_signal.compare("1C") == 0)
{
for (unsigned int i = 0; i < _codeLength; i++)
{
_dest[i] = alpha * sinboc_11[i] - beta * sinboc_61[i];
}
}
else
return;
}
void galileo_e1_code_gen_complex_sampled(std::complex<float>* _dest,
char _Signal[3], bool _cboc, unsigned int _prn, signed int _fs,
unsigned int _chip_shift)
{
// This function is based on the GNU software GPS for MATLAB in the Kay Borre book
unsigned int _samplesPerCode;
const unsigned int _codeFreqBasis = Galileo_E1_CODE_CHIP_RATE_HZ; //Hz
unsigned int _codeLength = Galileo_E1_B_CODE_LENGTH_CHIPS;
int primary_code_E1_chips[_codeLength];
_samplesPerCode = round(_fs / (_codeFreqBasis / _codeLength));
galileo_e1_code_gen_int(primary_code_E1_chips, _Signal, _prn, 0); //generate Galileo E1 code, 1 sample per chip
if (_cboc == true)
{
_codeLength = 12 * Galileo_E1_B_CODE_LENGTH_CHIPS;
std::complex<float> _signal_E1[_codeLength];
galileo_e1_gen(_signal_E1, primary_code_E1_chips, _Signal); //generate cboc 12 samples per chip
resampler(_signal_E1, _dest, 12 * _codeFreqBasis, _fs, _codeLength,
_samplesPerCode); //resamples code to fs
}
else
{
//--- Find number of samples per spreading code ----------------------------
_codeLength = 2 * Galileo_E1_B_CODE_LENGTH_CHIPS;
std::complex<float> _signal_E1[_codeLength];
galileo_e1_sinboc_11_gen(_signal_E1, primary_code_E1_chips,
_codeLength); //generate sinboc(1,1) 2 samples per chip
resampler(_signal_E1, _dest, 2 * _codeFreqBasis, _fs, _codeLength,
_samplesPerCode); //resamples code to fs
}
}

View File

@ -0,0 +1,75 @@
/*!
* \file galileo_e1_signal_processing.h
* \brief This library implements various functions for Galileo E1 signals
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GALILEO_E1_SIGNAL_PROCESSING_H_
#define GALILEO_E1_SIGNAL_PROCESSING_H_
#include <complex>
#include <iostream>
#include "Galileo_E1.h"
#include "gnss_signal_processing.h"
/*!
* \brief This function generates Galileo E1 code (one sample per chip).
*
*/
void galileo_e1_code_gen_int(int* _dest, char _Signal[3], signed int _prn,
unsigned int _chip_shift);
/*!
* \brief This function generates Galileo E1 sinboc(1,1) code (minimum 2 samples per chip),
* the _codeLength variable must be a multiple of 2*4092.
*
*/
void galileo_e1_sinboc_11_gen(std::complex<float>* _dest, int* _prn,
unsigned int _codeLength);
/*!
* \brief This function generates Galileo E1 sinboc(6,1) code (minimum 12 samples per chip),
* the _codeLength variable must be a multiple of 12*4092.
*
*/
void galileo_e1_sinboc_61_gen(std::complex<float>* _dest, int* _prn,
unsigned int _codeLength);
/*!
* \brief This function generates Galileo E1 cboc code (12 samples per chip).
*
*/
void galileo_e1_cboc_gen(std::complex<float>* _dest, int* _prn, char _Signal[3]);
/*!
* \brief This function generates Galileo E1 code (can select E1B or E1C, cboc or sinboc
* and the sample frequency _fs).
*
*/
void galileo_e1_code_gen_complex_sampled(std::complex<float>* _dest, char _Signal[3],
bool _cboc, unsigned int _prn, signed int _fs,
unsigned int _chip_shift);
#endif /* GALILEO_E1_SIGNAL_PROCESSING_H_ */

View File

@ -0,0 +1,187 @@
/*!
* \file gnss_signal_processing.cc
* \brief This library gathers a few functions used by the algorithms of gnss-sdr,
* regardless of system used
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2012 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gnss_signal_processing.h"
void complex_exp_gen(std::complex<float>* _dest, double _f, double _fs, unsigned int _samps) {
double phase, phase_step;
phase_step = (GPS_TWO_PI*_f)/_fs;
for(unsigned int i = 0; i < _samps; i++) {
_dest[i] = std::complex<float>(cos(phase),sin(phase));
phase += phase_step;
}
}
void hex_to_binary_converter(int * _dest, char _from)
{
switch(_from)
{
case '0':
*(_dest)=1;
*(_dest+1)=1;
*(_dest+2)=1;
*(_dest+3)=1;
break;
case '1':
*(_dest)=1;
*(_dest+1)=1;
*(_dest+2)=1;
*(_dest+3)=-1;
break;
case '2':
*(_dest)=1;
*(_dest+1)=1;
*(_dest+2)=-1;
*(_dest+3)=1;
break;
case '3':
*(_dest)=1;
*(_dest+1)=1;
*(_dest+2)=-1;
*(_dest+3)=-1;
break;
case '4':
*(_dest)=1;
*(_dest+1)=-1;
*(_dest+2)=1;
*(_dest+3)=1;
break;
case '5':
*(_dest)=1;
*(_dest+1)=-1;
*(_dest+2)=1;
*(_dest+3)=-1;
break;
case '6':
*(_dest)=1;
*(_dest+1)=-1;
*(_dest+2)=-1;
*(_dest+3)=1;
break;
case '7':
*(_dest)=1;
*(_dest+1)=-1;
*(_dest+2)=-1;
*(_dest+3)=-1;
break;
case '8':
*(_dest)=-1;
*(_dest+1)=1;
*(_dest+2)=1;
*(_dest+3)=1;
break;
case '9':
*(_dest)=-1;
*(_dest+1)=1;
*(_dest+2)=1;
*(_dest+3)=-1;
break;
case 'A':
*(_dest)=-1;
*(_dest+1)=1;
*(_dest+2)=-1;
*(_dest+3)=1;
break;
case 'B':
*(_dest)=-1;
*(_dest+1)=1;
*(_dest+2)=-1;
*(_dest+3)=-1;
break;
case 'C':
*(_dest)=-1;
*(_dest+1)=-1;
*(_dest+2)=1;
*(_dest+3)=1;
break;
case 'D':
*(_dest)=-1;
*(_dest+1)=-1;
*(_dest+2)=1;
*(_dest+3)=-1;
break;
case 'E':
*(_dest)=-1;
*(_dest+1)=-1;
*(_dest+2)=-1;
*(_dest+3)=1;
break;
case 'F':
*(_dest)=-1;
*(_dest+1)=-1;
*(_dest+2)=-1;
*(_dest+3)=-1;
break;
}
}
void resampler(std::complex<float>* _from, std::complex<float>* _dest, float _fs_in,
float _fs_out, unsigned int _length_in, unsigned int _length_out)
{
unsigned int _codeValueIndex;
//--- Find time constants --------------------------------------------------
float _t_in = 1/_fs_in; // Incoming sampling period in sec
float _t_out = 1/_fs_out; // Out sampling period in sec
for (unsigned int i=0; i<_length_out; i++)
{
//=== Digitizing =======================================================
//--- Make index array to read sampled values -------------------------
_codeValueIndex = ceil((_t_out * ((float)i + 1)) / _t_in) - 1;
if (i == _length_out - 1)
{
//--- Correct the last index (due to number rounding issues) -----------
_dest[i] = _from[_length_in - 1];
}
else
{
//if repeat the chip -> upsample by nearest neighbourhood interpolation
_dest[i] = _from[_codeValueIndex];
}
}
}

View File

@ -0,0 +1,65 @@
/*!
* \file gnss_signal_processing.h
* \brief This library gathers a few functions used by the algorithms of gnss-sdr,
* regardless of system used
*
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2012 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GNSS_SIGNAL_PROCESSING_H_
#define GNSS_SIGNAL_PROCESSING_H_
#include <complex>
#include <iostream>
#include "GPS_L1_CA.h"
/*!
* \brief This function generates a complex exponential in _dest.
*
*/
void complex_exp_gen(std::complex<float>* _dest, double _f, double _fs,
unsigned int _samps);
/*!
* \brief This function makes a conversion from hex (the input is a char)
* to binary (the output are 4 ints with +1 or -1 values).
*
*/
void hex_to_binary_converter(int * _dest, char _from);
/*!
* \brief This function resamples a sequence of complex values.
*
*/
void resampler(std::complex<float>* _from, std::complex<float>* _dest,
float _fs_in, float _fs_out, unsigned int _length_in,
unsigned int _length_out);
#endif /* GNSS_SIGNAL_PROCESSING_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,43 @@
/*!
* \file gps_sdr_signal_processing.cc
* \brief This class implements various functions for GPS L1 CA signals
* \author Javier Arribas, 2011. jarribas(at)cttc.es
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include "gps_sdr_signal_processing.h"
#include <math.h>
#include <stdlib.h>
#include <cmath>
/*!
* The SV ID is _prn=ID -1
*/
void code_gen_conplex(std::complex<float>* _dest, signed int _prn, unsigned int _chip_shift)
void gps_l1_ca_code_gen_complex(std::complex<float>* _dest, signed int _prn, unsigned int _chip_shift)
{
unsigned int G1[1023];
@ -72,82 +102,14 @@ void code_gen_conplex(std::complex<float>* _dest, signed int _prn, unsigned int
}
/*----------------------------------------------------------------------------------------------*/
/*!
* code_gen, generate the given prn code
* */
//signed int code_gen(CPX *_dest, signed int _prn)
//{
//
// unsigned int G1[1023];
// unsigned int G2[1023];
// unsigned int G1_register[10], G2_register[10];
// unsigned int feedback1, feedback2;
// unsigned int lcv, lcv2;
// unsigned int delay;
// signed int prn = _prn-1; //Move the PRN code to fit an array indices
//
// /* G2 Delays as defined in GPS-ISD-200D */
// signed int delays[51] = {5, 6, 7, 8, 17, 18, 139, 140, 141, 251, 252, 254 ,255, 256, 257, 258, 469, 470, 471, 472,
// 473, 474, 509, 512, 513, 514, 515, 516, 859, 860, 861, 862, 145, 175, 52, 21, 237, 235, 886, 657, 634, 762,
// 355, 1012, 176, 603, 130, 359, 595, 68, 386};
//
// /* A simple error check */
// if((prn < 0) || (prn > 51))
// return(0);
//
// for(lcv = 0; lcv < 10; lcv++)
// {
// G1_register[lcv] = 1;
// G2_register[lcv] = 1;
// }
//
// /* Generate G1 & G2 Register */
// for(lcv = 0; lcv < 1023; lcv++)
// {
// G1[lcv] = G1_register[0];
// G2[lcv] = G2_register[0];
//
// feedback1 = G1_register[7]^G1_register[0];
// feedback2 = (G2_register[8] + G2_register[7] + G2_register[4] + G2_register[2] + G2_register[1] + G2_register[0]) & 0x1;
//
// for(lcv2 = 0; lcv2 < 9; lcv2++)
// {
// G1_register[lcv2] = G1_register[lcv2+1];
// G2_register[lcv2] = G2_register[lcv2+1];
// }
//
// G1_register[9] = feedback1;
// G2_register[9] = feedback2;
// }
//
// /* Set the delay */
// delay = 1023 - delays[prn];
//
// /* Generate PRN from G1 and G2 Registers */
// for(lcv = 0; lcv < 1023; lcv++)
// {
// _dest[lcv].i = G1[lcv]^G2[delay];
// _dest[lcv].q = 0;
//
// delay++;
// delay %= 1023;
// }
//
// return(1);
//
//}
///*----------------------------------------------------------------------------------------------*/
/*!
* \
* code_gen_complex_sampled, generate GPS L1 C/A code complex for the desired SV ID and sampled to specific sampling frequency
* \
*/
void code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, signed int _fs, unsigned int _chip_shift)
void gps_l1_ca_code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, signed int _fs, unsigned int _chip_shift)
{
// This function is based on the GNU software GPS for MATLAB in the Kay Borre book
// This function is based on the GNU software GPS for MATLAB in the Kay Borre book
std::complex<float> _code[1023];
signed int _samplesPerCode, _codeValueIndex;
float _ts;
@ -161,7 +123,7 @@ void code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, sig
//--- Find time constants --------------------------------------------------
_ts = 1/(float)_fs; // Sampling period in sec
_tc = 1/(float)_codeFreqBasis; // C/A chip period in sec
code_gen_conplex(_code,_prn, _chip_shift); //generate C/A code 1 sample per chip
gps_l1_ca_code_gen_complex(_code,_prn, _chip_shift); //generate C/A code 1 sample per chip
//std::cout<<"ts="<<_ts<<std::endl;
//std::cout<<"tc="<<_tc<<std::endl;
//std::cout<<"sv="<<_prn<<std::endl;
@ -195,195 +157,4 @@ void code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, sig
/*----------------------------------------------------------------------------------------------*/
/*!
* sine_gen, generate a full scale sinusoid of frequency f with sampling frequency fs for _samps samps and put it into _dest
* */
//void sine_gen(CPX *_dest, double _f, double _fs, signed int _samps) {
//
// signed int lcv;
// signed short c, s;
// float phase, phase_step;
//
// phase = 0;
// phase_step = (float)TWO_PI*_f/_fs;
//
// for(lcv = 0; lcv < _samps; lcv++) {
// c = (signed short)floor(16383.0*cos(phase));
// s = (signed short)floor(16383.0*sin(phase));
// _dest[lcv].i = c;
// _dest[lcv].q = s;
//
// phase += phase_step;
// }
//}
/*----------------------------------------------------------------------------------------------*/
void sine_gen_complex(std::complex<float>* _dest, double _f, double _fs, unsigned int _samps) {
double phase, phase_step;
phase_step = (GPS_TWO_PI*_f)/_fs;
for(unsigned int i = 0; i < _samps; i++) {
//_dest[i] = std::complex<float>(16383.0*cos(phase), 16383.0*sin(phase));
_dest[i] = std::complex<float>(cos(phase),sin(phase));
phase += phase_step;
}
}
/*----------------------------------------------------------------------------------------------*/
//void sine_gen(CPX *_dest, double _f, double _fs, signed int _samps, double _p)
//{
//
// signed int lcv;
// int16 c, s;
// double phase, phase_step;
//
// phase = _p;
// phase_step = (double)TWO_PI*_f/_fs;
//
// for(lcv = 0; lcv < _samps; lcv++)
// {
// c = (int16)floor(16383.0*cos(phase));
// s = (int16)floor(16383.0*sin(phase));
// _dest[lcv].i = c;
// _dest[lcv].q = s;
//
// phase += phase_step;
// }
//
//}
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
/*!
* wipeoff_gen, generate a full scale sinusoid of frequency f with sampling frequency fs for _samps samps and put it into _dest
* */
//void wipeoff_gen(MIX *_dest, double _f, double _fs, signed int _samps)
//{
//
// signed int lcv;
// int16 c, s;
// double phase, phase_step;
//
// phase = 0;
// phase_step = (double)TWO_PI*_f/_fs;
//
// for(lcv = 0; lcv < _samps; lcv++)
// {
// c = (int16)floor(16383.0*cos(phase));
// s = (int16)floor(16383.0*sin(phase));
// _dest[lcv].i = _dest[lcv].ni = c;
// _dest[lcv].q = s;
// _dest[lcv].nq = -s;
//
// phase += phase_step;
// }
//
//}
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
//void downsample(CPX *_dest, CPX *_source, double _fdest, double _fsource, signed int _samps)
//{
//
// signed int lcv, k;
// unsigned int phase_step;
// unsigned int lphase, phase;
//
// phase_step = (unsigned int)floor((double)4294967296.0*_fdest/_fsource);
//
// k = lphase = phase = 0;
//
// for(lcv = 0; lcv < _samps; lcv++)
// {
// if(phase <= lphase)
// {
// _dest[k] = _source[lcv];
// k++;
// }
//
// lphase = phase;
// phase += phase_step;
// }
//
//}
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
/*!
* Gather statistics and run AGC
* */
//signed int run_agc(CPX *_buff, signed int _samps, signed int _bits, signed int _scale)
//{
// signed int lcv, num;
// int16 max, *p;
// int16 val;
//
// p = (int16 *)&_buff[0];
//
// val = (1 << (_scale - 1));
// max = 1 << _bits;
// num = 0;
//
// if(_scale)
// {
// for(lcv = 0; lcv < 2*_samps; lcv++)
// {
// p[lcv] += val;
// p[lcv] >>= _scale;
// if(abs(p[lcv]) > max)
// num++;
// }
// }
// else
// {
// for(lcv = 0; lcv < 2*_samps; lcv++)
// {
// if(abs(p[lcv]) > max)
// num++;
// }
// }
//
// return(num);
//
//}
/*----------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------*/
/*!
* Get a rough first guess of scale value to quickly initialize agc
* */
//void init_agc(CPX *_buff, signed int _samps, signed int bits, signed int *scale)
//{
// signed int lcv;
// int16 *p;
// signed int max;
//
// p = (int16 *)&_buff[0];
//
// max = 0;
// for(lcv = 0; lcv < 2*_samps; lcv++)
// {
// if(p[lcv] > max)
// max = p[lcv];
// }
//
// scale[0] = (1 << 14) / max;
//
//}
/*----------------------------------------------------------------------------------------------*/

View File

@ -1,58 +1,44 @@
/*!
* \file gps_sdr_signal_processing.h
* \brief This class implements various functions for GPS L1 CA signals
* \author Javier Arribas, 2011. jarribas(at)cttc.es
*
* Detailed description of the file here if needed.
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2011 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GPS_SDR_SIGNAL_PROCESSING_H_
#define GPS_SDR_SIGNAL_PROCESSING_H_
//#include "gps_sdr_defines.h"
#include "GPS_L1_CA.h"
#include <complex>
#include <iostream>
#include "GPS_L1_CA.h"
///*
// * Signal processing functions from gps-sdr.
// */
//
///*----------------------------------------------------------------------------------------------*/
///*! @ingroup STRUCTS
// * @brief Define the CPX structure, used in the Fine_Acquisition FFT */
//typedef struct CPX
//{
// int16 i; //!< Real value
// int16 q; //!< Imaginary value
//} CPX;
//
///*! \ingroup STRUCTS
// * @brief Format of complex accumulations */
//typedef struct CPX_ACCUM {
//
// int32 i; //!< Inphase (real)
// int32 q; //!< Quadrature (imaginary)
//
//} CPX_ACCUM;
//
//
///*! \ingroup STRUCTS
// *
// */
//typedef struct MIX {
//
// int16 i; //!< Inphase (real)
// int16 nq; //!< Quadrature (imaginary)
// int16 q; //!< Quadrature (imaginary)
// int16 ni; //!< Inphase (real)
//
//} MIX;
/*----------------------------------------------------------------------------------------------*/
void code_gen_conplex(std::complex<float>* _dest, signed int _prn, unsigned int _chip_shift);
void code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, signed int _fs, unsigned int _chip_shift);
//signed int code_gen(CPX *_dest, signed int _prn);
//void sine_gen(CPX *_dest, double _f, double _fs, signed int _samps);
void sine_gen_complex(std::complex<float>* _dest, double _f, double _fs, unsigned int _samps);
//void sine_gen(CPX *_dest, double _f, double _fs, signed int _samps, double _p);
//void wipeoff_gen(MIX *_dest, double _f, double _fs, signed int _samps);
//void downsample(CPX *_dest, CPX *_source, double _fdest, double _fsource, signed int _samps);
//void init_agc(CPX *_buff, signed int _samps, signed int bits, signed int *scale);
//signed int run_agc(CPX *_buff, signed int _samps, signed int bits, signed int scale);
void gps_l1_ca_code_gen_complex(std::complex<float>* _dest, signed int _prn, unsigned int _chip_shift);
void gps_l1_ca_code_gen_complex_sampled(std::complex<float>* _dest, unsigned int _prn, signed int _fs, unsigned int _chip_shift);
#endif /* GPS_SDR_SIGNAL_PROCESSING_H_ */

View File

@ -1,6 +1,8 @@
project : build-dir ../../../build ;
obj gnss_signal_processing : gnss_signal_processing.cc ;
obj gps_sdr_signal_processing : gps_sdr_signal_processing.cc ;
obj galileo_e1_signal_processing : galileo_e1_signal_processing.cc ;
obj gnss_sdr_valve : gnss_sdr_valve.cc ;
obj pass_through : pass_through.cc ;
#obj gps_sdr_fft : gps_sdr_fft.cc : <toolset>darwin:<define>NO_SIMD <toolset>gcc:<define>USE_SIMD ;

View File

@ -36,6 +36,7 @@
#include "gnss_synchro.h"
#include "gps_l1_ca_dll_fll_pll_tracking_cc.h"
//#include "gnss_signal_processing.h"
#include "gps_sdr_signal_processing.h"
#include "GPS_L1_CA.h"
#include "tracking_discriminators.h"
@ -221,7 +222,7 @@ void Gps_L1_Ca_Dll_Fll_Pll_Tracking_cc::start_tracking()
d_FLL_wait = 1;
// generate local reference ALWAYS starting at chip 1 (1 sample per chip)
code_gen_conplex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
gps_l1_ca_code_gen_complex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
d_ca_code[0] = d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS];
d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS + 1] = d_ca_code[1];

View File

@ -230,7 +230,7 @@ void Gps_L1_Ca_Dll_Pll_Tracking_cc::start_tracking()
d_code_loop_filter.initialize(d_acq_code_phase_samples); //initialize the code filter
// generate local reference ALWAYS starting at chip 1 (1 sample per chip)
code_gen_conplex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
gps_l1_ca_code_gen_complex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
d_ca_code[0] = d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS];
d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS + 1] = d_ca_code[1];

View File

@ -254,7 +254,7 @@ void Gps_L1_Ca_Tcp_Connector_Tracking_cc::start_tracking()
d_code_loop_filter.initialize(d_acq_code_phase_samples); //initialize the code filter
// generate local reference ALWAYS starting at chip 1 (1 sample per chip)
code_gen_conplex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
gps_l1_ca_code_gen_complex(&d_ca_code[1], d_acquisition_gnss_synchro->PRN, 0);
d_ca_code[0] = d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS];
d_ca_code[(int)GPS_L1_CA_CODE_LENGTH_CHIPS + 1] = d_ca_code[1];

View File

@ -51,9 +51,7 @@
#include "signal_conditioner.h"
#include "direct_resampler_conditioner.h"
#include "fir_filter.h"
#include "gps_l1_ca_gps_sdr_acquisition.h"
#include "gps_l1_ca_pcps_acquisition.h"
#include "gps_l1_ca_tong_pcps_acquisition.h"
#include "gps_l1_ca_dll_pll_tracking.h"
#include "gps_l1_ca_dll_fll_pll_tracking.h"
#include "gps_l1_ca_tcp_connector_tracking.h"
@ -221,14 +219,12 @@ std::vector<GNSSBlockInterface*>* GNSSBlockFactory::GetChannels(
}
/*
* Returns the block with the required configuration and implementation
*
* PLEASE ADD YOUR NEW BLOCK HERE!!
*/
GNSSBlockInterface* GNSSBlockFactory::GetBlock(
ConfigurationInterface *configuration, std::string role,
std::string implementation, unsigned int in_streams,
@ -276,21 +272,11 @@ GNSSBlockInterface* GNSSBlockFactory::GetBlock(
// ACQUISITION BLOCKS ---------------------------------------------------------
// else if (implementation.compare("GPS_L1_CA_GPS_SDR_Acquisition") == 0)
// {
// block = new GpsL1CaGpsSdrAcquisition(configuration, role, in_streams,
// out_streams, queue);
// }
else if (implementation.compare("GPS_L1_CA_PCPS_Acquisition") == 0)
{
block = new GpsL1CaPcpsAcquisition(configuration, role, in_streams,
out_streams, queue);
}
// else if (implementation.compare("GPS_L1_CA_TONG_PCPS_Acquisition") == 0)
// {
// block = new GpsL1CaTongPcpsAcquisition(configuration, role,
// in_streams, out_streams, queue);
// }
// TRACKING BLOCKS -------------------------------------------------------------

View File

@ -0,0 +1,156 @@
/*!
* \file Galileo_E1.h
* \brief Defines system parameters for Galileo E1 signal and NAV data
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2012 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#ifndef GALILEO_E1_H_
#define GALILEO_E1_H_
#include <complex>
#include <gnss_satellite.h>
#include <string>
// carrier and code frequencies
const double Galileo_E1_FREQ_HZ = 1.57542e9; //!< E1 [Hz]
const double Galileo_E1_CODE_CHIP_RATE_HZ = 1.023e6; //!< Galileo E1 code rate [chips/s]
const double Galileo_E1_SUB_CARRIER_A_RATE_HZ = 1.023e6; //!< Galileo E1 sub-carrier 'a' rate [Hz]
const double Galileo_E1_SUB_CARRIER_B_RATE_HZ = 6.138e6; //!< Galileo E1 sub-carrier 'b' rate [Hz]
const double Galileo_E1_B_CODE_LENGTH_CHIPS = 4092.0; //!< Galileo E1-B code length [chips]
const double Galileo_E1_B_SYMBOL_RATE_BPS = 250.0; //!< Galileo E1-B symbol rate [bits/second]
const int Galileo_E1_NUMBER_OF_CODES = 50;
// Galileo E1 primary codes
const std::string Galileo_E1_B_PRIMARY_CODE[Galileo_E1_NUMBER_OF_CODES] = {
"F5D710130573541B9DBD4FD9E9B20A0D59D144C54BC7935539D2E75810FB51E494093A0A19DD79C70C5A98E5657AA578097777E86BCC4651CC72F2F974DC766E07AEA3D0B557EF42FF57E6A58E805358CE9257669133B18F80FDBDFB38C5524C7FB1DE079842482990DF58F72321D9201F8979EAB159B2679C9E95AA6D53456C0DF75C2B4316D1E2309216882854253A1FA60CA2C94ECE013E2A8C943341E7D9E5A8464B3AD407E0AE465C3E3DD1BE60A8C3D50F831536401E776BE02A6042FC4A27AF653F0CFC4D4D013F115310788D68CAEAD3ECCCC5330587EB3C22A1459FC8E6FCCE9CDE849A5205E70C6D66D125814D698DD0EEBFEAE52CC65C5C84EEDF207379000E169D318426516AC5D1C31F2E18A65E07AE6E33FDD724B13098B3A444688389EFBBB5EEAB588742BB083B679D42FB26FF77919EAB21DE0389D9997498F967AE05AF0F4C7E177416E18C4D5E6987ED3590690AD127D872F14A8F4903A12329732A9768F82F295BEE391879293E3A97D51435A7F03ED7FBE275F102A83202DC3DE94AF4C712E9D006D182693E9632933E6EB773880CF147B922E74539E4582F79E39723B4C80E42EDCE4C08A8D02221BAE6D17734817D5B531C0D3C1AE723911F3FFF6AAC02E97FEA69E376AF4761E6451CA61FDB2F9187642EFCD63A09AAB680770C1593EEDD4FF4293BFFD6DD2C3367E85B14A654C834B6699421A",
"96B856A629F581D1344FEF597835FE60434625D077ECF0D95FBE1155EA0431979E5AFF544AF591A332FDAEF98AB1EDD847A73F3AF15AAEE7E9A05C9D82C59EC325EF4CF264B8ADF2A8E8BA459354CB4B415CC50BF239ADBC31B3A9C87B0843CF3B9E6D646BA43F866276B053826F3A2334CC5E2EFB9F8F195B382E75EEA63F58A06B3F82A3B5C77C1800FD9498F803E524435B321210BB84690BED0BBBE16D363B3A90656A73720E27008852FB7DACC8284411B177728D9527C560859084A395A6F11A96AD9DB6B43E00642B000ED12BFD967868EAB1108552CD4FC89FBC408ACE7678C381EC91DD000319124EB5D5EF52C4CAC9AADEE2FA045C16CE492D7F43743CA77924C78696FCBF2F9F7F36D8E623752200C6FCBBD71ABBB6877F3C5D6E6740AB0389458A6B66440858B2D383244E853646FE2714211DEA9E6196252815BB704A20BFE556AC474F8998944E0CABBBE21A6400B87BFDCF937D12B2821D59298AF4AD378F0F42BD8C41693B8D993CF37C8B478F3BB5D33AD2A9FA24AD7B8FA895FDBC04964192F7BA3FF74E0E3A435B5DFE042E3115CACF29624C0645E9C917534A2EBC1F5665E4E1B1BC56208DBCD8A27CCB6474D5D0E20CA4072C960E5ACE41BDA3770DF3B681F2B318F6F8E1CB17C2857350FB6009AED665E13B2780D79217F73FAC7A8A48048DB0FB8A8A5007CDDC9A7B2DA8257C99F1CB605A18204",
"E57DE19A3E4A8C122FCB1DD6584B3D2DAE364D800F9C5A9E957B38F624CBD3ACC58FA3ED070B5E44857CCB813FBC0BB83B5D157C6C562422E5963CC4DD753C45B0264F8E136A0F1774D77A543E44D51EF8C6B9408B6E3B5CEE1347A94F13ECDC94DC764976E5A50B4CB0AE7557553B47EDFE03EC2CD32EA8D125A341E1EDFC77E75330D6E7B23DC838EBCE7E5567F5B8C80C3D15E7404B4E10F0BEB0C69626A814AF91334199864FC77E0FF548DC2A6FA6A71C3C0561F2B085CC05E8512E27B9DBA60B93D114B87935776C8E9A67905C429D48BF3AB1B0A56FAFBFD5D9C8D8C8A9E5918BFF273CF5E8664FF2B90314BDBFDAD5AB8C22A0E45C104ECE75EA43FE9BDCE306A5A28AE464628163D249D8056005F1A900951808CC8620F81768153436F741667A8E271DD986C7A1E5046FCC74C7CEBBF9A1296D6CF0B2FF85BE412D87214BB368DFF462AD649D7324A117252311C664D33E4DAFBD830FBCEB6EFBDD7391D4BADA7A775FD1949D981F619655DB3C22BAC34E5AE41222905C0C7E80D6EA28471EC0468756531C09A471EDBE200472E78F1701FEE96E5769A9893C0F11E7906B064442E06E21ED8B0D70AF288690C532A2D03B373E1E0085F62F7AAA658B569C5184E3DDC40ECAA88B887118601691892F9F55E2DE79E49DFF11D434C2BA3AA6447522A7C99DC215CAD2ED0114ED62CBDAE9D315E48AE14D2014B7F8E",
"C0FC4C72A12023BA7093C86775DF3D2F42C7CEDE616876340BE4301361B9DC9DFF4F1DEC6A62E165927BDE4F809E969AAD085437496BB95904719820F4CA8ABBA0B84C34B06DD7E268BA10E386FA7DB9FCFCDAF2B6AFBA46A8A299153B4E11582FBA7F28F0A0F9DE41830AB33335062C57D81DC361EDFE491939100FC827F36273760043D1C35B74E36C6C4DBE1D307847D55AC07D8B212C2DBA632A86AB15BD0FAFFA43070644C7E50623195A3796AA8E8D6E4E964FA0E4488A500B9063FBBFB1204A0E33C6CF2879AC2BA7C86CAB57E3E8A497836194E65C5C39B950F1AFC3B58E850A5EC39F4190D55351D16529CD52B36DF4A2DC68EE202BB758CF19C54B0E1461D547B5D06C2F9DC09C2B15458C3140860E4C6F3FE4F417FDFCEDE00F71212EE137E6669E569A7845470CA564F85CB4772808D65D2B48D409B709BD7AC5F7E28AA804CE9DAC3ABB5A5B768C6A184B5A974E933F2C1772FF64AB26BA2D5A165744E314EFB2238AC4858A8B82723DAE8865478EAA261F35DD4D98A9C07ACB0B822AFF1AD3E739CB214CE737196FEF2DD0B0D45BAC423935670BCF71C2EC04CCB98943786173C309E75A02BB78A788A5E6F8A8F407E57B8403841A9E1FCB3A7AB80D1F6529770E52C173E2C47EDED4400D5E665E325ED845C9E8D0E66FDA16B17D61EDBB336F22688C3F0FB040A55F33B65FA9F3D45F5B22C445CBF9DEB22",
"EA9596357B343DFC31D5875CC0E94117A33651472E476D3892D8112EB6CB6E0151D409C5A514DCDA38A773C58F18B590EF9017B6EDF0192AB7EB29DD6E1E7E7390C13E9B10209D5775F3B066F7B2DBB7307FB44F726DD2F368A5FDBE75BA7248762E1EC7E4589DF1A353A16D6B3CAC1C9ACDB89890ED2C4F44AFEFC763DB51D102230C37E1ED0943CD6F4176B2F5C19119588911ACF81A7A29320AD579C1BFAED1A70DEE1B87037138ADE411E0BB92F5B3148DFA11F2F84CA6C019124B922837503AA9823A97E443A66378D5CB3130A7EC9B05670E85D095D5E6F603092C632E51FD9013FE7FB9F08448FD09F1219A4744CDAF82BF9C60039C8185C7E9559FCE301C6D3F46A2E514AAD44D3889C8CB4ED7439BF47019194F2644363770F8BBD0AE92B6F5F43CBBB503A885239DA636903D4C264B3FF09AB77E3FDBA7EFC63E0792B6D5183759E57D8A694CDB133B4A9E301CEEEB978050AD9A9E410091AD29E389829E2F24BE1E3B24F4540C4A6533EBA72E8AD540BAAE43A0CB82F971F3A51DD77FE9E1956E2EE7553E050A1D10B99552DDD5B68F2E2859712835BD2AD6B08881753B4833FB04740E3364D2CD4921B939393E7EA91B854FA1E5A8EE79FF0A83F111F78435481D462E0E1CBC0C921D190A435A1BA755E4B7021244FC5E3F0630F2A1F439C02AE619393E5624834B05ED7DEDE5F0AFC7A40899424E75D4EE792",
"90E92279CD4F60D98F6E8FCB3E9263DB60FAB146A835AAC2E96B3BE3FF07119032DEE0521C731117E90C2943B389DD6B65C5E21C34F86F5A7ADE04072DFD1479EA36528D340736B0FED4F6207BE9F6CFC971D5EA11781AC2DA25DBEEB6B903EF8BB0AC0CD2E29F94B8CB67874A7B7441045758E09EA061181A50E0AB7BCCF801554E0644780BC137436E3FB7784C182856A790D6943BB53DB40D13D6A2F7B83A5C521073883B90FB8DB1C0F954D132943C09156A09984B822079FB8FD09BC07C1D6336C7CEAE8CC3162760B9838CA6A38FD0044FDF099E416D57BF9F33A551043F34EBF9BAA90901E62D2D981065F977852072F692535DDE24EE8946387B4E5B0FEFEBD75552C1FC325A608A78079A9AC864F2F30010A3304CB16A26AF98D9BFD3B8D128541190B2BBEE275A6F53B9BC5108306985ECBB983B56E34F18B48A12AEAB88271F4F780CFDFA83E05E35C12464F4350597CCAE9B4498F5A5454DCC3218D3336763674934ADCBCB5EA52891EB240C362248226DE64899BE30735F6495E94AA61ABEF62B803C57FDD045B724ED1966B6E7DFDFCA5B36F7B0FACEDAC62DE8E10B12DFC84B1A9CEB407BDE63CDB5208ABBE5E066AAF262187E94502B1701B2CC8681CB616773DA2B7AF49443CFF528F45DD7F25959836771908C2519171CAED2BCDCFCEA46301E7D99A5AF7199155772E92BAD85F35EDB656F0999EE828",
"A91F570102961D62CA6CB55144AFCCEAF3910F3336DCB029CDCBA164ADA72732771B6ECD1C58E49F468A2BFD23E1B996DABABBAF5AB3A4C74926187B5833006F8BEF7F9CD0F05A2A0B9BD9073C4C3976E8660CE7BF81634CF0B31C3DDD806A6A0C15BC552B83A86789CC675A6D137BE27BC86DF68FEC5D268119EB9E965260FE1F5C56AEF60A8622CDA8C42F24CBA7F5B07A7416917277323314AFD3ECD10F74BEE7B22DC760EFA7F935FC9963411353782547FAEED32E69A4FB5756C1A73CCDFFEDE50F4B2D9B5D2ED5C59C9A52D80CD27B989B8DAA14C569E763C08FD42358CD064B2DE0526607C9536D75E1617EC80615EF5EE2314FAC29907B61B61F8696CB80B14B3A0148EEBC825C91150A08A23FC7B38B5982AA02A18BF6E91B3A1F2EEF360F682A34AB36CAFCAD556841073F219910F7BC2F07CE45E98F77F50475DF9EDFE2DC9E3D7280193D61AB5076A14887E9D9193C3B83C5773BDECA067CA1BC3D4561C3A8B4E30072A6269B529760CA1B5FE9D3DB2B5D1202CE8B18E9E2E80FAFF47108168D3C7EB3C940B1A35A1D1B968A5A9DC0686DD8336E498C240F20871600FF995B9E33169DCFCFCB58E75C94D82F843C60A7118F0D7B40064A8A4176C5158E86AF0BE4C1D5D73D1C051132A85CC0628486AFD660502A515D6353B674B1D4E61750C13E8A3AD48FE1F89F201C288A8F443867C2BAC23C706EE7A2D2C",
"C6E00978E351164532EEA256ECBE0D4F8FCE02A276BD19666DE93936F7A242FC4C7E879791314B043ABF1D5F9B0036ED22AA92028C800C4D62BD6640431170EA77311865074D670AF2847AA47CB94584A793FA82F51574BD7C62BF14386F14A3D7DBD129FDE64EAD67EB35D5E13FF214D7D163B770D4A77A62D02D88C0FCF3FA5EC306EB7F85539105FA2CE5F53D182E58FBBC1C57CFBCD2D2F7FC8A067D6FA0BC834DAB8F370B0971BF6D068CD4D3A32C11C6598DEBBAEA046528C5EF762828CC84D003847069FA18743A809A004431E83924B8FDF0AC78699B905ACCFF82E83FDAFEC8648DF64042FC9438B261B73F0541498ACAD67D702AB631BECEF8680D33CE8F4F0CE29B95132591A350DD68B36734B97D4B3E84A76497F702312F2A8370DCF26A7C3C8EB91DD8699C48F551750712683E0397083714A6CAC3457C0FA70BB3A036C6E0BEF24E6B20BA5565B351C2EFD56BD9455FF7728BE07A097208E73DE4CD0CB4E215B4642365123CDEA419B28459D50E864B762554E7C1D7CAF73DA7D40EDEF5D824A2FE1A6CA473B07370932A8A5D441DEE3C9A60DB68E27A9D3E9C8229B44E5B434C6D18A8CADB6D17BC4614DEBEAD670C73132CE2F999C8716D1098C69277E8ECAC546EF8002E5182E25F31A354DF112E97F8733DD20893B430CD7130E69ED4A0FE4D6C2E4FA479001E42EBC9F36E5DFD3E0BE35A64B89745E",
"821BBB3FB91E50253A9E71AC379ED57AEF394C2CC59587B2D0337CE74002EEAD17AB5D504BCA68BDAE9061C3DBAE2985EBE292B9BEC9D3542015225F44ED3C2C3FFB036A515BF33DA1690F3438FD225A5034106C5F4BCC43301EEC2245D73F63038E2A7D9B8CF95A9FD813FFA071FFDE423E0CE737969578BEB909764A8D6DAA9E15A4FA0867831652C0F6E9AAA39A63F0AEEF62A433476CC7380460ECFB8B7F3B2FE8C4C42A3EF1CDB808FC9747FB4F044B3B47A4EDFCC9463ABB72C55399B2F79EE5FEDA270D6358B27F8466969DE4A5F2E6A5F2C4CF0813C09F468DC97FC0E5DD057A8A0355767B698F8A79BF0350C4200413A15E6591DE70A1B502E19FF515C3DF36935974A4764895B9E3CA2626BD39B7ADB780AAF7E2E914E804CA923089A51F3876649C73CA3C2623A8C95D11EF4B3F941E9772EBA1F47212C666F03F01509FF699F74EDE27182B6E98AF49D1BAACB41A328A8C34D6E8AA3553DA3962B27B041495F269328B6BFB4A385CBB118953F3F009920EC4C8590003290DD60AC89177BB8C4BF753CE723AECA392B8D9E5E9E4113DD062F294A77B6EA9A0477E697C04C787CE78A92C704409D37D37B6B392128698D0D8D4CA101EB38B92F467F0D86EFD8759A14162CAB55F8C457E82392790A5BDDC8DD2663944F880C95EC02FE5363B064623994EE5D4396C0E44DE2A3D225830BA6160270BCD110A942B0",
"92A0DEABA9875D4AFAF99A24C1D5F10EBBE6DEF9CAE5B0C85B2A0417C1CC5D1A5F71CD8F8A4B013C3F012C0A19EE4A23106CAB8662C5A2A93A971D0B6E487FC05BAF5C355A9520C9148584CFED3EDD0F38696E161E64378C831C586D9178A0CE289A67F33AE68C02A3CD138FA09DF1CAD01EFADFC8BF6F5407B79B18D09C82804736752D08A1FE09EB35F544E9F797EA36DB493BA947AA82513EB1615A356B5AA4308B0B4183E070EB494D628159D2D4BC3CB110AB0CCB2E9E73B5B7EB567187621E72D99F1FB78565917B28464A5F29DD8D6F98B6ED703040A44B0ACD97F15049E009E8533FDB0B6DB2F2582E6BBF81D7B0EADC8F402508F6B8531AD13FD1C55978A8A70DF4E053DD475132D348AE27581370EC14A3E0F96E0D70DA4946DEEC0760011404FDC5B436CA7419D05895F5E0EAEEBC88C74947733BE9919F18CE702887A6C4DF7C19279B82FB646090822DA9CD9C7653F6B931A337A28F7A4A01DE0CC0744F22961045F8EF8D4B30B07E5EDF5FA944EDCFB9841A9088AE82444FCB6E90B0E9C567A80E8C42EC713D78132F37AD1D2592C31C93D2EAEFF38AD94E5C0D94F949F47B88B03BC1EA4E5EC9C7D9DF19ED208B8E44FFDEB0B625F633C7DB1C826AA9E1C1309E5B14A0DDDB79714DFDCB52221CEAD7E8A140DF7806F127156478AFBEE922B8ECF322D66B48BEC434299BBB36B3BD9030467B7F2EBBDF358",
"AFA7FBAC93326D0C36A388831B99DF4D527BCE7C9070F7B46B5FFCDEB07384801AE5F86A89934DE23DFE2C1AD117797D4FA1BBA6175823B41166DBE9D126F17B3761E2C352AB396A5A9CCEA42A5E9EA1BE3497C0A5BA9121DB97F64159AAC78E62D7DEFF3BF4CF73F8CFBE045C9D39E41D5D208DCC4B47CA27E900C3CD8FD1408DC5E0F5114F2FE65817D37CD1452C4967ACAA2119FB8D60E5E2FD8A820D0AADD88B94D40435C095568AE6394D3B97C835BA868A83083316C49C75D36EFDD85165BE74A4F2B2D21295EBCE085D9C4A4758FDD9CF71B97FDF34B7B63A5E9691DBDAB834D87D5B52CA9A53032FFE821398616EA92625C2DB633E37911987083A3B49A86FC562FB1264A75643A5FB6E97162E16ACCE353227FE61A859E094C2359BC4645946AD12AE5C39C70F59EA7B597A9B3372C23AA578146781A61163C92816627DD9C4BF1788087821F9F5D41B75A0F251B06BBD3E29ABD41E72A1D48323D24E2AD6F11C2D49678CC04FCF6B0EFD33BE6DDCD444F5CA02FE158112631F782CA7B0C5F3607ED807495BF8E82C5EA51A922FE28C8168D9844859E7A3EE3038C5D1D4BB4B13406C340894DF46406836739E31D01082BC84489592DA0E985630CEC40702A36DDC301B3AE1E8101786FEDBF752F9E175287C239C18FC25795BCB479DEF59C58C373313C02A1BC5F16355E2B50EFB58855670868728B902653ED80",
"943CAEB680AA3E630755DF32F406F403D7AF5E48A710274D3887A7AAC8EA6744B889F2E0CD2033DEC0B434A9591254A0AA68C5C9BF11D35765E86B437497D84E5DCBBC0C0C580CE9BC50EC6382AD74DB02C2C233B7BB07517D48056226C505ABF2DD244F6BBAA23313D570558B065E42327768078EFDB53DC465DA038E3B216D990EE951B3E13D3C1CD559998F77BCDCD2B9522B6F1DC5E12C912EAEF574AFD69C251F9B2532501AB9F4B3B2223D0F8920BD562B0D358A14AB0D196DF6337D1C96CDB47AFEC6F81DED4B5773864DA32FCCD06B9AC53C122B2C6327E6E5EFE227DE4893FF15BBB2257FAEA836E99676EE32BF6FC14D4F56EA191B8A3870374A0867C49EB0015D1C6D07B87A36BFDD1DCEF20EA7B80D997CBE2D83EB5630F2EE6F73B0D50700C89E4F32438F5541360683DF11DA6E7A3C1E7DB2A87800D9245BF04278C990A8DC9CD86DEF39CBC6D4BC00FF13BBE132F9D86681A8913BE787CFC69C35304824788716D52DC74CEA399E06DE6241780447C74DA8E947134D8B2FAA9648D6D5F34C9D60AE5973B5BB0187796D589C8FDDD7675671F28C04AC1038D09251980683CB712F694D7C5B0D5B1DE86CD10EAC4EA04A55BA8803D78249BEF516D38067890105A23212E72879FA267A8B4F0455A81F17CFD3E5DDC55E5D4FE00F83E18626C676DAF00E6AAFCC23D209DEE0B0FC6C2AE4DE161D13017ADB5D8",
"E5E70E7837D094416558C044D758383EDF5755C80921218ABE76E51FB93249E211A38FE6D07A7DFD2263E6E3D8DA0F921A06A606B804DE7AC3FD097E5F96EFCC0F544D623FD6F43FB88CEA7C341E901CD47A7E24AB141E998FE41CA87CD6CE8C1870D9ABB6503BF7E8B659084BAF2237DFC94F35C9884C7F44B87120BFCB298696E613C1656AC4899781A94869EC603B4D38665337CA8593AAC83AD8BECE10302E4B4694237E96CCD3AD9CD5F8EC039A1D1A4210716371404C5C3FF375CB3A33559B1C1A239F2E442C8EB033501BB290434BE73489F716965393989422CF4D57E5B4F3C76AF3C5E8999E61805134B9D7C40BFB59D0D0FD30F98567E66D6148D6AA64F74A22C50AE49D6B1ECC6BB5A002ABF38FF2E2436766B86BDDE7D95DD6E02AB0FF06E7BC22CEC98D55AA2BC4D7B91C36B2FF9F525A74423498D548318509320FCCBCA582A6C2996AF6538422FF0DF060C0BC7356B0850A139AC3914338127B786F4BC58CEB6064DA881376A147DFF53C6700BD13316A5874A75D7B9713DF54FBB393BAFAAD7F7B0710C049A0B6A8B76A9956BF6185BA39D9C347D179FBB97D4FED68F47DB5AC8E0D40122EA51C4A1F88D23153DF651A180C2AD456ABD7F851B65B220A72BA48FAD0436332E4EE7EDC554B7D75481EE05C3D3453D760E9099DD27B324DD84C0C0C4DEC4C674D25284B16410F959FBD09D9DF09CE875601E",
"BFDBC82ACB4FBCD5A90C5967EB2FED597A02607F426002128AF4B38942C85AF4472B3CBF3B183F240E049B251713740A31117F108936631FD0F11C5F79325BD6677A2C2B242965AEFC147D93358730AA782491209CBE600976F56030753CC979C240A196647CD9EAB1DD0380E59BC7905EF740C3411AD9DD72027D0D3DD6DEB0F5F3C18F6D6F7BC59B758E7E262937B4599B38567C147ED2689BA2CF23736CAF55B6925827E2B70E47D3813C94C85298BD6B49C97B5D0221BE9E3164B6FA3D95AECF53AF170966090F19A69E75F188BD2556B4E8FA7DC4AC6C34F54297C06C2A96DD1C45B42E6175B5E8784568F7FEF0B6C124C5019CB577B374941E8515CCFC21F46D188BDD2C2284C688879A5BEC50CCB97FAEE1F75580577498D509D3DE161BE216C873B29E178CE17DCACC5E9E2224D05ECC842FBEAB82A75AAA20769FD81131CFB69D5E35409273CA106FFB27F63FF997CB500F161F6DD3A8BFA5719F004EC17860152D3290951678A131E4F3D3AB34CFFCAB2967ED9D8F1BB987950306BD28751D2AEAB05F071B08574EFCA01E5386E04F727BF413A8279E9392EFB64D9AEE00877C76C81EBC861E2B484A2D35E592A131726CAE61BC010B954721A82C968CC6F384D9BBB99B4E87846D10B94EE31F64846A5834DF73A67A267B894B1C06242D750F15F3E1E850A11CB2E2B16155008F91493AB3BC77CF9BE56F9DB20",
"D64F3D1CB54CDB9143D9E701BD313779C09DA064D9A85674CCB53B0C5B4446C122098961D5EFFD6A85537486D5EB26B5E18FFBFB8E6EF16C2DD2C02EC7C07DB15CE33015A636E225F744C963BF0653A89A48F1AF04819E273A3AE1F5538AD574D553C5A0DEF47B552957037BCA921970C76DDEF74BA083ED55363760A6780612C075964B083B4F674EA0012FD1DF09F0445CE75A698852098206868AD8241E3B319FA8D2D86DE6E7631DF1AEB571F9676323E0627307F6D8F569536A758DE5EDAAEDF80F4335E3AFCAD07F70AAD5CD08CCA1E71B84D4D97931F924AC0010C0811972ACAA414B89FFF7917E653BB31E9CDFC72595066C662CDB9BBC96152D46BF4E8C15A8D34809C4B9D79871BDF0B63FA294F2D667624F6E0210CD40C92F1C033C3D8BF089EF85C4F571CA727C71B23128A9B0FFD70CEA93C316FC4D69D79B089107F292E03425B2552AF5AA18FDB9AF86EA1972B66B1276B09119437E4DFB8F8E3972D91A93816EBD7D8D715CB47EFA742938B0B49FA27A291B0DEA1DF0B8F878332103F45A99936896181E51FF65C6995F57C2C54B8002DEFF54B0EB3131EE7D61030C33B5502C49CF398FEC4B7615D16FCEA3E8EA12BFB311D426331A06606CA5A066707C4AF8D1048F1CA6065FBE506D06C6C00D5D250E227265551867A6816F05155FCBDE24D4AD115BDA98AFE08B12A1F32E7C2ADA801FFB78BA05726",
"9D6AD9889EA02FC9A58949290975DB0F512EB37C8156CC9F1242B9E45F22CC1D6ED1CBCB6CB245811CE729261641FDF7A8F389BAFD7311B8BD689E02409F6E8C5202F466349EA466E5398B29C8CB126D9600D89697A07A6900FE8D95951903DAA3419839C2D9E35E9F4EABC04C9006EA585F544C7163A33D7E78DE28256B7B8978FE018CB529F7F79BBF66DC4F0DECE80AE3C2CD479D78C4480E4DE2F06C70E5FEBDFB4ECAEDC2E7BD891AD6C91A7C2446F1B13B340B7160782F6CC5B45F9787CF1B0985202DDF02EC552A6DC41325FD8D31A4316C13C56F7157134F66E1D103CC3AA7EB951C92094EB4409E6E7BC494434FAD80999D46D824A5A57390599052025F7DA4838F7D16A8DACDAFA06D175546FADD1E3F7975265230F6C01B9C1FB1B7AB1F2FDD43A5778E3C88FBEA70575CA26D94D249670E4D9FF28EC67D15829776D7BC6754D2A2BB01554E5FF0C3FAD8A1CB546E8AD5E5314103D086D14ABD30EA95DDC591C13D96C1CC3F60FD18D216B67181B6324AC09A97C0C45E50EE8380ED42F6E0430639373E7760C708248EE7D74830E9594114879748883F247D056B2BA94A0FC54CECF6F5C6AB4DCB7CFC8C224F40D886427504233DDBEDCE160DEFDFFD69EE2B75746D9CF71676DC453FD01C315ACA96373ED387B040BDEBA7FF3CE00D915F90AE6E1796971F8052160154E8986913AD7BA291188EC49A60BE27C",
"B5184F7D580935ACFF18201CE8B5D54CD0A1CACF102FBC8AADF391C4CA5807BAEEF4E5E47F7459E74485E48E0C42D27CADE6970714FD97C08F9592FDD387C859FC12C1CCCFC3EBF510D66FBD8C448C25A322CC5887F94A55D48ECA362C690F24833C3B032A047D12BDA2ADC6824A1F6EA9320BED27968E9CFBDEC60D041EF538F1740C0519003FAA89CD4224293167E05344998FD396EEF618E8F547990BC06A8B76D0FD6FAC13284601AB7191CEB813C46C45CE7B3FC09EDF08DAFE136BFBDD63E6CE7E4BCBB16C5DA68AC71A1298FD27363349A261C2F2CA8CB799E8604ADF70092BDBD6A04CB80568776A537AD1711891B251C74E42FCB095B23EEF70F167E8B4856BB7F92E3A43C79FF4437262DD70BAF9B16CBF5F10D1AD7559AB0F8CEE1B9FAD058E84FCC342D9F0D9FBE4207D40E281416506242CA1B8DAB28DE88D2D00BA21AA7FDDC25940CB29F02811F8DC6850A6A87D72CA9F3476A73649FB4A254B1204CC1261E7D512BFE7B0D0091AD5CB0FBBB765FB5AFDFAB0D701941DA54832FE8253BC0CF61924BCA2CA231A196C7C32A350AC9A5FA2884D8571FEEEDB7D29632E71898BB62B5E4E0104F73AA6A9C6B8CDA816872805D75ECA64F961641077B259C9D39E2F3CCD9FCFB1E6B6E2692EA34336A967E587F32E49B961B91311198A204D11874B4BEBC6C04DDB5B82D5B741D3CEDC03A56A2017B3D2C4FBBD4",
"CFDD6B78AEB21CDCD6AF8C349F6DF8FF8B96BC8246A672A16E45B5D0AB7D992570EC45A534B77F204039FE200D4C5E7C78FE24941F578097B216177D8AD4E1844B2E52D843256D0BE8504CF2D5B639E2CD501A6FE39B8AA7DB7DEA924B38692E43195DB7E5F25E25152DF0FB7E0D4EF63F99CD95F699E16576702B651C29583645070011B2A1F88C947BAE7C94D48EB07A132DB38D4FE2B77EEAFB31AFB442710BD0AE4E6102DA69A454517B6F148D97DBFBAC7305979B5D74D7D7568A0CA56CA89F23D8330261025CC741F9D7A4BDB356B544C68C89CCC2C125F5C71E18C4EA102343AE4A44F6FC695810E6F28C86BF53F4C8B8AAE46DF6006B1679EBEA790266D4D02A2095074ADA634EE60C7070285C316E1F191BC5A88B80D673F144D65B870A65FC93D8B4BB29B80FD58F9FE95F5994878308CAC5394781E4D5A3F5EA2A8ED834EE5BD31D2058C843F22EB778C4C25144193DAA65F9B57AEC4A344713E9EDF913F3CD29196B42E71BB182AC3B1A60AFDBF1112A86A20BFC1D28D3E0DBBABF38E8F12651C207C951654FE8C4CECB6C6F93EC46456DAFFD7320DEC8D08F2F712CEB4D82407D61CC47B333F69310C06EE1FB5ED84F83945F05D4A87CF5A68D78B5536880DE3443E804040E599BC5837E22150C93CC1E5E711F9B889C78C6FF882D80857EF41ABC5F12E99105E6C894EC0B796E0A645780341CBD039E8C6EE",
"ABA759AE16B9D8778FAC203FADF48015331D6499B8CD74BD71ABEBD3E53ED90625E3057EA47BE587600F308D38743A686EF6FA189A4D86E4A35EB798FD2307345FBD10FA701265F6417603365FCC4CE7635924428167115BA372294C27A23CE6C27C506603C5A6618A2B3344BAC50AB7FDC29D36BCBDFCE0D48D088EFD8EA1DE492C543093C30AB7694627C01B334CE3368AEB4BB3267EBB1096450BDFC2571977D7EF78D6E288FCE0388A041838EC2031248F5FD659C70180634A1DC7196C8D9111C75B51C50F854CEC63DEBF9FFE1AB9406735EC3187276DE7CA2FAD4287027956C93B8E84B7C0C3A9C3F7E82B3DB35EB6D2CEBDFE0708FEDD764C839954F2CC9044B652D0A01D28BD6B9D3DD9740CAE39AA52597FFC1227FAD8B78EAFFC31BE94A632A1AA7A60AA5A9E090DA2B62F6DBDFDC50DF6EBE1D9949619FE9B2302248D6C801DD2D6C01FF8206A93C0AD22C6990C4EECA7D4BDF36C3246A5D2D2B3982C608E6AD6BDD85C92682EBDC9E4117F8B7F841239C2A5AD7977E11E4E9CA73A55859EADF7C9C2F1B28A6B4AC7202019230063331FC5586756CEA1F8478173A0A4964D00C1AC099590152125A4D01592C54DC2555E1BA34C7AC039394D1979AEA2BF7B2B2A8CB9D62E89132CE9E3B325F023AC6E8117CE57AD4B271EFB0C172FBFF8FA6A17A490B67CA7B15F865A8AEEF37651A622390E82AFD418C7AFD48",
"CEA29601B96AD3A831646922000BBFF02C014A9136D9A151A0E61A51F9FC2EC0C3A8F4C83E64BDE569A33B4CD653C1345B7CBEA3B3AC0411B6145727B1DBF6066ABCE9DAA8B0DE58ADC2510C02C2619A542A139FA3EF7A03AD3467345D9573C107A13E7FCD43C0D51DB5EC1A09D409DA75462F9C71F0C9E36C2742C279C910F07CFC5CF7F98AD48D67232A2DF29A66B78209557357A4BC91922D4195DA9533CD3501F388AF6EE2BB3AD08BC7D53015059988F5B9BF7824D066DCBDC61CA588DCCF0EBDE4A96632DBA22CA0D770C61A1DD66EDA882D02C5FA284798E12296E89C45906D315EFDBA816FD869DF869A65DD8BA4E0B13C441EEB052EF3D0FD436E4AC68EFC749E0CF4C7E15599D5514E136ABD134BA638A02E9EC1FE66CC9ACBCE5082C8734196BADC21F4DA7621D9FA725362C411127836A26CB44CB3851D53C599B94A5E67862665D7092C43D9B4AD3FE20B8AFACCEDE920F440F3BF5552CFAFAD04A7D7E0A9CEA18D497282D44778FB7D5072832C0B77C4C51F4DCFD7AC07DC7A9863DB8A38F1C003CB852F6119BE801AD12B8BC7393B00640F125C734447DB2FD8B02F7F7FC7A23B84FB80F9CC08E3EF888634FFB6F51ECEE9B20A89941FBF2B49314DBDD67CB7A1B5BD8D629FA327AF2CBB47B5419A0A8CB807D30152FA560690DBAC49D6B043D5BC9D51E82C3B1CF4ED69E997050C65197F3D93E21CBE91E",
"D358BFC8C6AD1DC94E71D1F5D05589424275875AF8CDA2ABCC6404D6FCB7A2E0A74C68024827E02621C10CD5FB149FBA373AE32DFFF275CF386C3D7A04E3FE10B6F1A6F4782B4823242F29672E847CCE760BA005D6852A3459E7576A254B10A9A78A9F8112BEA39BA65898CFED1179D68211D98E6950ED06399E39433ACD898E2F6C87F5FB9D99518EF36429D447B0EF0C5B7D834ACFA388578BDF60D4B1FB5A0CEE7D1D613BB9B99E36DC9636E70A543BA6BF0B3A448DBDF80469494239D4B7C4979D82E80C08EF36EA67560C86665D458040CE31BA009BCDC30CCBAC50259E4485E570F190613CB010563F6BD24C2F1CF73F6A6844AB8350D23BBC3D1361E73DCE94AF83697BB817BA366C9855A754EFC2F007D99A964125682E6F5CF7FBBF687D221B5A0FD844477A2F87D5370F4469F76073A93AEF7812275FD4F70B2040C12A83ADE5E5D862684D119DCA0F75AE2B56C794968A68566291B731579A1055A84F083B3072B7BD5AC9D520F64F0829B592875613BDD81C11622B331289C98501B01EE1D813C0E97CF36878260F80BF88071D258B9DE02F3F90B4C12BB56CBC731550B5EFDE6D97A1283EEFE61CD6E5DF312D0F0153A32DD65B143EC6A3F2B64E2B8FFB47EAE46BD92A6EB9ACBDD11A2D730D027A3EDEADBA5965198FD59BBC8574B680B96AD48586E5B17625251BF4374E28C6AB956C6818183FDC119499E",
"FE69433233B6067B0EACF1F47BD3AAD9783FA30F684110D1152459233896479D08A976B853E4B7B52A34511239961048B7C1B9009095327C86F2EA291FAC1734ED2596EF19D04528F3D8F2A3430A0C19DA6A70A37DB6DC034BA0053B57ACB9E7C00ED9BD6AC11339EA169D9D54E6739B051AF40EE79A1034D6294261E1AFFCD61B9CA5016C56B2D1172D9B2A7283E4EE0A06C8149E5A2DAA263A5D2429C2B1FCE75C41887DD02E056EF8724645FEC6FE7FC1EF180529B1E894773CF3E2E1D938EFE9CD824D91454116797F5A84746537FED5F0EBF0583C8508EA0745B4989954EBC4F215BE3D515687BCDD5DFDAB9814358B07038E0CB869A8C34F916FC67773191679C60A15A0A399E224D0B0168439386C0AEE8F5EF77185AC847A66D934CB0ED6A3467C3B386BA7F115877F36B49E111DE49E409468F343A98974F4EF1EEEDD282F73013EC2727518DB46C6751A58AE3E0D5F9D2B966D4465BC5595BC31B2712AE1E1BF9915CC0E02CA7240EBB9A045F959E77DFCDADAB6248D58B47BBEF3C775DEFD629A2EED15201A21ADCA470B1AD3084924FABCDAB6B12FA6201E2A239AE8F1BCD7CC39FEC62587E58C84AAC15935D45261E3AFEB60016AFA0902DB98DCFE586513FF70EF4E3F47773635D475754A158FACC9C470921FB0186BD6EEDEFCBEE9C803118851F82CACBF8C0A544B0562E2E27286CEA5FBAF83AA5C1F97A",
"C7386F9FF39FDDBFEB223AD8B856EA2E7F3AFEDE197A61F183FF7DF2FD6DE208E71E6E1063FB3774B696913524F7488EFC2CA54E8B653EF5BCB7A8F4994E312DCEE99A316C2ABF3FDF85B8FA9BBD4366ABBD7B3D3D433C14710A95EBB3D0FCDA2D37A443D62A8361DA78ACA781CEC04542D01DE7B6C6D14CDD4EA709264251D46C42AAF404094286DA5BFF8E81FA2F8C54B172821054F4CED82287F29EA3D3AA798C9CF5C5A909B9FBA641A8D9E310248B0F9A1375CE4DAA98EB62286B4EF4DFC58B877A73D017B17AFD7F1F58D3D2CAD3B7AF2F06699B08B88FB4EB70D2511190158BB4928ED1735C94400980144EF9ED06E06074E2F29325C1AA316A46E8E617B3CE916CFCF05A389052DE120498341EE26A27A3D757AAE763046B8CBC841350292F06AFF97C9707CE5561F5C119E2FF6C137094F62573EB80DC13862797C3319158DDD465FBC033CAD81BFBBBB54D9467599D751B9980A9AE8BFC6715C5EA74859E6A10DB369D5DF83A92655A9A5908228B33B36F55DE563005B886EB324CEC4160F0D18938E9FE41D39234C29E13B814DDCD13CA6450774800924B0848735C5DE076F66EDC973FC83B13938811CD9887371470AC5DD985481185F1191EA8C1D3A7DC65E1E82E2318D0FF0C9AF65EA1515DDC536C5A8BD0AF481789838DA54A39BA56D014E12242600AC78D28ADAC3FFD3600E896445868064D1D2ACF22E",
"BF5202D3599D2DDAAE5F526B6B6AC469D4BA0D0BA5D79B1DB89173320F0EB68F5D9DA495AA0981F8022426F68519B548B19B5F8CF068A6CA1442AF77C83B7D8649DC281BF438F9576F7A719A902A860B9ECE9AE9C14B98859B282010A5DC90DCE612AFEFD44E0E9E7666A461AE50C2656BC036648B826CA9C3C7C53B30976335B097C19390716A41FD437A2098BCFA2B2975F1EAE5BDBB8192024C20136D2542FD89FB8F2F94C08F765109279BC4E511787496233F15F52D7C3BC3E98A6DC39AFA1818B9533EDE72FDAF021E2C9B7D6C74E49B849F372B1A131F4C532DBE3B63635E0E1334C87DDB6F3D73883D2B43E87CF19E40D6B404E581E807E6EC1A94F5261C7F7EFD4CF043C90A1A7E97465022ABAA1DC21588FD285E7158FD9B67EC5FE7C9E84029E961E045EB5227E4726154F4F057FA337BB20DDA25D11632A7995B810764084EBDE01AF07372EA82FBAFE0434401FCFE05CE8FE3C20C01ACF4E9B8EAF4D50C73D5C42A95526CDC8313DBCA6ECEACB457D9673565A1CC0AAE23FD6261A8943E8FB84CCEC676601A4B302A9CACDEC8998EDC847A53B3CB0E12C8B4A7897D5680CB14A3D11BDBF4826C3938EBEEFA0075B6494CC714D3C0DDA2F5F783CF23AD2D2545C899867C1115BF4A4F559F63E68098955550BFA1EF7771598EF86A08C0C634B291674BB77615121BF0838DA96D6E7C53BFE6A58A382FD9721CC",
"BF8903A3918B3FDC06CAB4EF675F7BE3962CD7E3C6ED643386EE533C3B24A3D94D2EA2CFB83F0A346FF2875DB07BA647492D47A807E7FD9717CF12BC97B3C1BE1361E598850B39D50CF7BE700507863BC4BBF26620FAC11D97128049BD96C5E09DC8FF3F62655D660FE66D31AB0B0F6D4F8420E3D2E633C571D7FE2AF1CB4E3BEE95E092B00EFD2796A3DEF376F75B7EFCBB141337D81AE52939D87956C41B1E42C1CCA4317D31AB4F53DC9502A3DC774E05E1ED5008CD931DDDB98DFA69960A6ACD45B60895C4FBA2BDAE8BC7DB8C821697558B1E0A3111F156738409FD180C5A4A33B24C5EE4991B84133CE9AC089724D62DA9D9827A2A04FC103652F216A0895E78A96086270814C2699F475CEFD6359428D8C505BBE8C1A96D2793802219144CA6B3EDB455929B39A3E9F3AB74D685608CE3F301FE38202ADFEF529CCFF46AF36DC24956A7CD07CEBA55AA4C89F7913A8A4B844FD8F152C8A823CB9888E3BFEA97D7E4AAFA07125DA4F51D974A5DAFF0045BCE5B868177A91BD932963451EE2673A85AA8B7D493BDF25BCC2F64AEC3150D8C40C835AB4F5D0B7F259DF099BD6FA9F5CB198B61018B1448035CCD34E7E7A2138F437490026050BBE3CE2D4CF4F4F095CB97548E5731A338CB3903519D6B13A029727F047A7D00904A556C883745410360FC878F77707A716D549ACD6A70A18F9EE0AA8A6EE2080608E10AC",
"F58CDE0EFE2356F429B0F2F9A7869A4142A6173188DD75B570F1D1ECD282E4AFBAD11370C5B4CCF3C98535D27D73C0111F11A84711F732441EAECAB684F2F0D7FD4FC4070749574922A906E84B3350CDE5957DC388FDA23BF45F05951A393DA253EAF691940897B57ACE655E9630F09856E76958D6BF7B830E0CB8182AE226F39D48036C867BEFA7E7ADBAD17C1AB45297C757DA4AFFBAE677B05677D60DE1D975A4F3D7EB3461B424B67B61025AAC257A69FF720CB9DAC007C50C69A7ACDBBCE210BAD4DC2E629A039D98E7EA037A5C344B5CAEDCDA035F28677A41D55A0E3E6E480CCB12B8F17062A983F4E651B4F7CB217FD06BE46747CD5418C0C81916465A4F5660152B3E4781DA8040D4246F9BC47366BF663CF9DA3BB247D9238873CCDC6FC62D1D8F669EFBA42527112FF4072262F7E65AEAC328871DDF47588A0A0DD13A4139F4145822A5917F624B881BFC354F37B6D59C566823F629A21C973324F7167BC39FBD2C121D2A849308D13DA1A28948EB59F7DE97E364223E17A30119BBC7F43E21E7DC3093F7505055ADAB4654194A77C1CCB61898840125455A275A8F071273D8C13934915D379CC603657D99CE4075C1F1DCAB60B6BD62ABA1A10B5402A59706798002EF30ADED2F354E38CE0B57900FDAD31E7F684E53D097B4313DB552EA66F6D337F29594470D3DC0BC6CD361831251004DD3C5357BC0BECFE",
"D9086F7C272AA317C64C00AF43C924DB5DAC97F8EE3ED2296252FC4756FCE6928BB009D4488B9BAB757411BBA52BA6F61AF1181CC7BBA94257593FA1BD26D52AD5014C3F1A1832FC4F7445C8BBB77C8FD31C88F0C5D4736D49DCDFBEEF2B8301E31185793BFF87CFD9E6F7E084D343AB98BA3518A87A5F915BC0D76B01AF7DC1CE45F1C5280BD39D3E3D94D0A0286F8BD9FA942849664E08F2BE0B93C6E3B89061193FADA0FA9485F62CA87F3E68E204186EF1187642D651162E4D8E7DA049F462362D8C94539CAAD09AE4768C96ED6C2CAB8025EBB6901CBB26865E1F19FA1B193D47ECE390B881233578950175C85B928582D5B439EEF2F56A8C7EA09278E47741051223AC182456C4FA04D025BDB33FA10C48C70EC91BC709E3CB0FA3E01DCE5FE5ECB9018130A8DE5D0583EDD68EA2EF227A612748B2F785A30A01014BD479DEC6256C8AD88470F79DE0E1432CAE448DD7049E5B7D4DF3C978F65E708CA3759AAB9D329C11FAD71204E1E92322E3EA1BBDD9D034E2A23ACAFA21CF490AA5E2E419197DBE990667BCF277ED61B264632F694392EF52F0A27C38E478257AEC8D2542938BF0713EBE60779C95A0EEC8F32A5202A849CEE8CE0F99702F595AEA839531D4CFB5F5A6166B06EB64387552A1F9BC6BB97B9B99D19C3D2E1E8E9B305D525E7413496E40FF50CF77D4D4E2D41B1D5929848FB2F1FDDA5A39DEA0546",
"AE4E3B30560A50DA55AB3E59FFF512844A2700D2D763D85D5C3FD8CFEFACD4D023BD926D3EF2E55EB1B3831F2276EB07E5C07B44FD7D79333699BED0804B678915FE0F092DA9A62F69CB020DA21932F9FDF9AF332E1B400C6B7E7880508E840D62FBA07E827A23A2575AE68E15AC444A1CE35DF3C3F7CA49DEF2966DF3BA89C8E90ED5E2421A6407F2EC51A3E92A3608FCBD6AD9FF9E5C7817E79A0C09FE9014F7AC291448263E4346CBC4BAA6EABFB59B4526B654070084F52B864F9769181DC6EA91B576956397CE55CCDDBE41F94E5DC366E775C86ADB1C807B66D08696A2BEE45B90E8736469A371F05929D9D9FD34980DE08E00BDE2CD0EAB6AF2165D76519F8F2D894AC70740D2372B37407BDA4D943EDF1CBD35CCE4D81340CC97751C568731C009DF65571F28B7F58106AE67279E83C3A0C130DE0C5B6C99117099548661D290C4CAF3BC60EF719E2F7B210FCD4381C33904AFDF96DC3A6557B42B6EE895B4D604F5F8985F454C51E32B2C874E90926CBC58D044D483D6D2A7C26C7AC4D190531F79993D07B2E830FEB99BFDB00AE8C008DB1B762F3F4A81D41295FDDA37F3056B1110D4F0CF385F9FCC7E14C34F6752A2FB17F5CD3FC4AF0D51E4A0AF7D28DB0D4D651156189209480054F8287266B1CB26C9E8CACAA0BE5A69C696300025D160F9DA29F9EC79838941459B7B8164AAD95577A0C532EC2EDB35250",
"9CF0CC00B5788DD743A5F33D87E8FA5733B72EDBCD61AA4B8D0B81213DB52E7EF17AE90934F5EC0711ADD19E881CC330F696179C1BA464FFE6D7B04EEC383A4106BE5892C5DD1BD719AB3739A909A384FACA455E6AF96600AC6FF809788700DD2AB93DD228483759BD903EC002D4C1278808B764F018E3B740EFD821A61F5BEA2948A653041FB31F6D5D0DE0A045DA366E44112C820FD7FA966B2CCFD5A6816AF84DC0A3EEB8F9D2F0A912586F91D50B1AE3D930A680A8FB7435B6875ED2E599B87598A7C20245296C4965E2E0CF372B6ED1219BA68CB646D3E73D52665AAF2E3D1C4DE8D264578299B166FA0E148281C877FA9B14818759CBF7FF575307E80B73933599D94EAD2FB1C08A30006330BF0AC1F1C0A4EE6B07F9F3381AD7E2E469E8DA9C2D22CFC0A208B58924D2F994AFC0268EFE206E0A9EB79BB51CA26FB49013B9A17017E0C08F9FFC6C319BB1B5AE41771443BC670EEB91D7769F9890A9B80F52CB0167EAAF850FAF2A52B74ABB1792E7CEFF68C0D38B01F244AC0CC0EF0731E3BDDCDAB89DF376973A7ED5D4264EE82C334671FCD39ECD6E2CF869493914F332767BBE461707166A9164776D29F5EC9291F505AF291254D7319AA594B5F397D5BDF00BB840C4DDCB425F4325ED8AB77E57BECA3441B89414616671692EA88A89D2690A4B5FE958F990BD84A3884A60FADD5DA57EDF01865F85829195460",
"B85B6E754CC8F6805A8A19DA104418D9C134C8B0DBCFD5DAAF5A71BC047A73BEDBC192A453674BC624959BB76E44C5B34244D4736ED3F0F3C9658FEC0DA5437E01E128795EDD7593D636CD73FC1780B37A381502633CCF2EFDA0BBB494C1D0FC7F602DF8C282F55E3828E81A92458EB16B74835040D8A9C8F2DDF180A617B0592344B4373E1B526C9706B843B0CED4D25D7324C6FDD0F33133C00443638E6249061C56A116CEC7822F4512AFAEE52CE8F94D8547F72612EA8C7D160C65FA3BCC92BE01493706EC4E5F203F0BF85C52F417BAF8AF490E50133505685CE63AC5B173E07D8DABB2D439C6DC18B41B9CF37D02C92AB5C2F27EC83AB6B2DDCB7ABCEA30A95BBC39E9FD0CBB28118823F7D0342F1EB7B45FA6BB3A50223D0D7B14E975E7658352BC9288B48AF1346955F4551F2ECA47D423EFC63D20681057E5EF234D061A5E6E234ED01F3DF223A0E8B4DEDDC552C7DC3ECF663D5011FC907EB4A7CF746AB9E07C2929B7427DFE9E00B0A1308881912635A72EA99927F343EBAD32436A9B8EB1934AC29E79BB80AB3ED9F5CE39D1E43C251564654365DA43FB8A0FBA27F2328D82445A1EAAED67B92716147E859064AC326A42DC7880DE82FA782AFFF9C59FBDCE088746F8CEDBA288BC8C2C4B458782CC9BE63A86168B671BE99A09F2217B7BB2A7BC88651C1BCE8A0B89316ABFE72B22722273AF570974D8EDEE4",
"DD40DD438251E401FC926CC6968393415D52D521A5BB34D4272D6BC7B5431062B35112CA709C0680CBB18EEE053AAD62B2391C9E9D580562541A453ED936CE8E88DFA61A88CA3BEE66CFFF801785CCE863ED9C36A04D2DC8742A81CA55127B44314AB4E687ED921B4881CB363AFB3CCE7EB774E3205D4591939ED7D3C0C508A31786421F49669E120F01D35D467B40F85F2454F13F591F3B830937421B5C8A6C20EA878971AEC941FD99CEA92FEE00E5DC2264987DBC549EFF3E4A26AF0CAD7421C4256D107A3E8908F67450960E4E41FD7E2E84F754BAC81C8F5F1D6F650DEB3E6EFF6059836643209E3880D7BDA701869208D8E4BC8D0614066414DB3F93D6EA187950285F55BB7A1B026EA4BFCAB4671B07704828D5CBF9730EFC99E68E91F1FE9664DFA73297F2D6BD9497DE04982C9FF3730BB6FC3EA2053B3F45DC7FB587BA19B3C6B7E780EA5F25B45BB727174D4CD3B401FE1906360BF0B15DB13B62752F82EC62226AABC83C1C26376F8366BB849DDB65958AD969B25654DEF1841518993033AF47EABEE3CAAA936F19E28A205F3CDDB5CAC649DB6A90483ACB63A24EA46D397508EEB5DA94E9C883EB0451D036E28CC303D52B1BB31FFF582605F340D449508959ED1FE2FF0BD22FDF77F9680D6B5647D59E7E6A003AF0C6A95092F0DE43D1252EA6DE00F288BCCE3ED9CE273DCB4F3BA7E8D17353B8ECA24F03A",
"FE38B1ACA366B4C15F3FDD4DF0E0274FBEFDA0042BB203A4F6627ED9E29F405379B2F2DDC0F3B02A0CA70A9499F3CE82B87603FAA347B7052CB5D13D9DE84C114EF3B8F62418FB1F3E374B997127667FD6BCA2E2F9DBC04ECA9D908CD37C62F08EEA6F44B3FDC149465AA8037D65A6C8B9B8B3D5E9A40578E5EA3AE1209BA49E5E2AC615C59A2D71AC1605B98E39A5E66A890754C7D1C07E06DE78632587BADAF7FAAB0A529AB791095DB0A708B691E9D81F2CEA8F07B05495528B9FD56F77A4C8209DB972FAADD9791BA59F47C06F241F50C0619FC04F8456339E0AF331310FA4DCCBEA0E5DC2795CA6B3ADD0174AE4B30AC0428320ACEAFF68F73ED11DC1BC9F0237BDC75F7F48BE518EB3305CF2BB898B329716FC9ECF7E99B510B3309808735FD0A77B15731C233998F9ECEF46E2CAA6E6EDC8D05B943ABD17027A80D636E535038FAE44D60AAEC5406A372D62479192FA84D844520C6774CC589FEE16A3A5549495D968AABAABFE4DB94F5AE0C54E603D6DA5C3056769A064890533EA8EA1E5D1CD410CC8DD4B1D7E0F5F787232439AA4B3911C5DC792ECB873E8105A1AA61C627BE57E809C6863073E1E19AD8B987DE97D88A817FB43ADBB7751E36D1F0E7B70B3759D6EA8F2350D10AF38C331E22703B2B5103C908E1D35A8E814E45BAE81DCA0530FC3525CD640548245C259738E749E195B006081A18C45475F906",
"961408BD52EA1C6A9F340D9109B2388CC358BBA2D35BB6AB672A9C16F3AF968BE4613BA6B13D115B896BE71CFEC4A4AFC0BF5D2BB1B5DC19405F40E60FDF361E6CC362CDC28B75B8C30B468D3BBB77F3FC62869FBB635A3F7AEA63420CF1B80A4B3813B2240B83ECE999808E1394DA2881DE2DE62C870EC163AD8D81495DBE2C7383B78E19AC506AED9F3BD1280A77F2D9C55600BFA258E76761391145D45F74253C6E14BAE16179884F0F0EEF8150A445BE1B5AA4FEDC2198CC39763A3DB473C1CC4263CE2DD587447DDA6BD0A496E8DF60859CFCDAFED2EEC5B1E77E68F449ECF129AA17395BA39392EB610DF45134571BBCBB26162C83FFE77D188160EBCF598EF6F092881612A04BA9F4215D429BB521E737F6C3048B5D95B20AC37F79AA99A12CD0469268228463C317A1F31E63E4754890F20B4516D179342A76201402BC0DB2AD091A70B4232473343FE9E0002E59044C5F3B6E3D7368DB7E7F9F42E7A4A1942B1161552C5C84390197A54079F570B57E8EA8A30659FF5A61048142E4368D5542B968966E54DBF91D3970B9A3A278B951D6F914246FA5CCCC20DE53713D4830AA86D758ADA7A8747FCD455B2C320FF9E5E7FE1CCCFD6F7928884FC0B35F34118B4E7E6F6A5D6E3FB4E3DF90CEEBCEEF9D7D595A4C456C373C5356EF0DB0E6F8D61E413F80E4C32532BAA39170AAE2FC606B7206C379C4155031AF918",
"E03A069F63220109A77232F2BAF29D345737AAFED2E5E8D5C846B937277DC88392DB2D55073CE063F5FFC5717BB89B481B8C86EC01808A85ADD78517CE12DF776CD3F02D948BA795215599CFCCD9B4FE56DD681A59C71D24946225171DC18E47BFA9068302BA428929790EE62306C5FC8C10E71F6F372105C9421A563A4DA704E01FFD71E46B28C6EE1B7CDF95BCA6A794E8CFCB6BCA6AFBB67DED5C1267A12A4752176C3C1E6C2F665194C7F11C1CE6CC8481A5A966719B57124CA33D8EC9862AE5485788106562A0BEBD3980AFC4469BB1FD1653678192B3022E26CE8788C68F202D506DF098D49EBAFF7741CA96A02BD68BFC4FDBE24D34770FBBF8EAD9647C911B2E6AA705F0236301162CD2B41B88BDCFC2A79EC54698276126D6EC0213931609985224BF515AD3221FB5E8A1C4CECD51DB4AFF612157ABA6640866DC6D3602C446B8C6EAEF359BC8703D5EF9FE8EC7F5F2685C53924C6F1F71519E0AB589EEFB393A12C06B906402FE0A2FBD793272BC101D3B57F10A52C172E505B74F2A00BD5D2F7C7EA2883908434C125969A62F1B09F42E578BE22C08E88B11767D97C4C0CBD2BBD98508AFD591EB0B0C846A95FB72159E561F9D87DB446E9A19AF378B6DB4DCCB49FF8DB547B3040FFD0171B87245CF73A0747287B6FA1F1E4AE99518E8C53DBEEA942AFEF75B69E4790D75CC8A5181C609BEFAB641E28C07082",
"D8D7DD9242E54C6DCDB2A717A6F33226A94D5794FA0BDC401F4ED842A5CFA4AAB462F703239F684DB9B95E5101A5DC6067C7062AED9259CDB2067BC815C157A7E150F8557C3A54DDDAE94E5C569A1E09C383A062B601F920EF4D957F4BCA8E329123BD6FDC3B731361864CC139EBE3C68FED0F40FB127D9D1DC071DFA1552ABC6703494A632AA1314984A2D4B7A8BB32C555B79DEE013A66745AE15E8E5E4E129E44A119203425F2ABF1F03CE9CB33C0BDC3285ADFAAF4D7B51EC31F02D1E654BB10F0CD97EC3E389CAA34398166B4D5C9FCBC6E957FFEC9CEE4071F90EE2926FACC8FDE884CC6FF1F6EF1D3CC681FB44E45C5CF681AAD13226DFE19E22CE81265F3088D193EDB098988B2640EBD8D9D66708E1E9880DC41A72FD3D64792B14AA18C13E371CA17FB46B21DA9C59FFA2FD075852D42E2A578744792EC02F9A35F869912BBB44DCC648CE6075DBDF457A9AA891488A5450FC719778739AF323E87F9633E621B404F70614F77C65697E71F281675C843C523740AB66756E4DA784F555B5B4D797A06A0AFD35A69DEEA6E948B9B3C2A62D7B1D56832D9CCF56F2680A5A0A3037F4E0252413FB86520F2815C8824975634C0889A486963A2C874638559E14F780A7F3E2318B88B2C2010174150AFD4BECE2C5FF2D37BC2FF791F4A3136E19C373FCF03E471DFCCED19471182A93E91ED3EA68C402234AB2B00ECF62",
"87F0D84B811E4635AE8023B74306DE789ADDF1CABAF5F47885CF7F9A33F2C533093A339EB0D5E05C0763A215459CFF0D31CA92573EF2074CE2B6A11FDD6BAA3C6FA100D23A9AA413BAAFDA22F746CF74562F9DB0EF2F7CDF266142F177681CF2EF388E2E9AA012459ECCA332B779E48CA44E536082D59C3951ECA42F66B600D2621BF5F3584B59DC0DB76EC66ED7D00E943BFDE703E7D5050A8F263366948253B3576311CE88B463791DFA6F401ABDCFD7FA44B158AA97EF1CFD7A8802662A633EED958052DEDB12A6FB353BF2224ECC2322937DE3D39DCF82650B18FC0BC2BCB8CC456141C9F3FB09A0906AC1EB77E50E8D260041E4B3FF4BF3A53F7EB62E0FC503E8E38F185AF795F67FCE17665AEE29BD0D5521024A70B61446CA4CD2B3DC274FBE72A9AE29EF67A2217D6CC81BD8831B5160E4E81238B379B657FFB49023B040B6D504B287F684A0A0C07304E6BFA21E8D0A7629BA32F3F2BC9A33DEE2EAC1A2C22462EA0D24162543945B78FB6E26B86E12621588735B32A4F9A50157F20BDB7A4B6D151B3F28B40A03CFEB3CCA635261ADA2295B74947F1B1D676F6C014AB362C1F5AEBB439DB137D034D00591235B6F8D11C735A0C1964B29D3002D5243374628FB488A04EF245E64F598EB6ACBDE8B87F2FC5D1ACD105460C26BC6E1C7DCEC2E92E33F722E0A613A86356343EB111D0B166AFEC5C7A44B81A607A24",
"DDEC47E0FE3E2F4205206CD673EC66D7435E8BD4A523A8681ED77F51453B904E4468C70C2224C5F1D01A1C5ACA89BCD72673F82955FACFDDC4DA499EFD8D18A2BCC5035B0C69D095AE0EF1AFDC389B6253BD6FC83D3C37E809EE732A87D065207684332CBB9BB0519CFF51B553B3689246EC1EA42E236773A5AC4A7BCD37381615F78A41E5B181AF5C502E22FE79436D6EEC07F7FFAA739356CD9544C7DDBC42C56BB1C965441903962703986C93C6F8135A8EC42A89DCB46E754EF5B5250E1171398FD31793594831B0775F2A39E63FAD99929A0F257ADF332C078B0B7E209229ADA46D69512FF2DE930F962B8F81EEFCFDD358FEF8E4DA0D49F39C43AE9D99D8052FCD60305FF4D9FD4CAAA3FA6BF258B867E3F266F1BCC77390D02132E370AC79B34FB37F12D102985950FA5FD53D3D4783DA5B284494EBFAB51DE6CE259E27712A0EB4D78EFD6573A03D629F29166B902795FC8DF59050C9FA48584F0EB69293C7FACEA8972098E1367D183C0B91C3550852AEE6721A84341612E820CA4F53C71A51774134923FA00FAFD84811C07620917C1966F6A26A032B125D76BFA149FD66EBD18F7536285CF94A8A750680D6BE0F4A4E7F2433018796A358CE465E7507F186752EA2FFB01941902129936F7B18321902780E2C6A5EA7D3607227D6315C570815BD0E808EAE242B554824ABAF30614CC72B74E647D5B5A6306632E",
"DB29CD5101A3FB7A651BAB94E0CEF2BF737BBEBF0F755AA42F1C0B2E6A2E00422458CD8E2244576F139E811B2469E1B8F10FD95A988FCF872607A9B5CF81EB5875C427515D8D6D4FC58F3C69A92AB4932ED3D1CE6191E648276AD746F12A0D7B1ACE96B80CD2C3B99BD1094ED29F7020121076A6E6D5D750C39633B00F3DCB4793A27B4838C492EFCFDF94D955ED33814C954FFE8B8D29A81C3C62BB2E95ECB393F06EBAB479494628027CD02E59F1AA32F78C53142137965E662DF4B3EEB0B92A6FEBDC91F8B31D41E2EF69ACDE1CFF2AB49E2A4B12C275A20439E30C690D14D4F661C81308F11EFA3D014009D80D794D9F2C8B51E2E6C83686DBB07D1790F56D7C8EE495657BDC081A63B1353F4C4CA74CC0D02CFD7BE60E063A33D1A4D9050A9100F0E181704799357532DE152CA54FFC089E8DF80F29DFA14C19C9D6B7855482622504F95A8948A5DD640EE88B87CA98F8248DF148AE4F992BC7D1FEB3BE07E70F22CC052D92FB263337528C41ABA642C6B1C6F8883B21ACDC69196733FFB684451D188107C1DD219AB8B0AE5F49628A1CE32FDBA4B8CF02F38513E37542D020F4BAFE03EAFD7DCCAAEE9E28258322E43DD47DA3AD625DC2795ADBBBB9FE061917573936E31DE6355023F8D74500D2D032B7A53630F37FFA80567BA36F771C4ACD71D76DE528FF47E8281ABCD325E4C4620D0B73B2BE2787A7F6F485492",
"E8F24D8DFB54C5BF909CA2CC5496AEA76E583D2D865259C356E64E76793290BB00E7029A146E1E90DC0C45356E13EF59D60F20F080A0FEA743FC1C222AAEFA3E55876F2C9E6F7BD29C09AE32E80B15DBA0E6B594E951EAA3BFD166DFCC17AFD2621EAE6539C74FD776BB998C24C30EDB3A8B6814DE088E7E6B7CE9A64EEE9ED8C9D987A21A3BF35A9D59171DD456D8D0D7D1BEB3F37D4A31812BE00077F0F0064DA56EF9C4D36D1D3CAB4D1D4C024665BBE1DC227DD29E1CF814EA65B64DE60ECA4AB9B19F937DF15914F3CA577EB3A6A0A2C8ADDA53DE536721FFFD519FC5E7CD9A3BF4F030B1EB638D6A0F8C4E24085012D758414211585EF6E0DB8C670064885B67256BB6AD7E12E380C5E25BF58EB1731E935899C1FD2B20008CF87529AA3E714BFFA86D8B66AA94811A43ABD868A7711C4FFB339C01D72D4974ED53E7DDACCD36B5F459DDC05E9D475D3E2AE383F6FFC9C2A0D1791B4675DA843303C96A98ECA88B54735293C4E1906AF30221EE71AB58F3E38271608DA6AC0A488C0850DD86F8B6588C91589632EB781637C14D9D24B57ABFC301790B3C11C1B2938845F08F7280388E0B9D5A9682A6FD40374542634590F336E42D8FDD92F3F6C82BE4D3B953CCDAC984F6C94D8F2FF70BDFBA63C922060C322FE34188FF70A37648C362BB68B06D74A2012050FC007F276A54882A8A04DCE014655AE43E448639F56",
"FE35174BF6B56B67D39271D92AE0DC2B64FE31AAE8D1135BD8FD308D7E281F3ECE84784423A48C1362B5852719023F8861E861AD8B22219259E357EB9CD8505A66EF7CCEB53636B47D38A2AF5506E1FD72D3E30A29EF897C5C44A271EE562D67B279806E8A5DCD78DE538D8121CAF4C217F8A969AEA50D6FAC68066277242B1979F1A6B3051CE5B9949A11719F556EBEF844C808E5C1AFCE5E312C53AA9DDBFAF7280A7FB8502D2C7D1B91614157AE0C6C962F868D05D0463131DC841169946B732F8000E686467BD5D8086CC249693FF9794374266BD6A5C8AFEC65C66A834012365D60179450C58FAFE724B8B9E99C33900A86649B04CD54351D6C4CC7068B28F070417CC9C4430390493BEA50799FCBCD7A13BF75947C597B3D7AD486E3AF99CDD743B6230BC473DA35E6D05FDD17F7CB8D04A2B00EFBAA30946EDA96BED467A45EBA54578D9001637702F1DBAFAD16D2608C475B8DC7994DAB93FB34DF2237E4A13D0C04A6CACD42FA9463674AF8ABA97CF511A82E8E61F8330004E165D753323F4AFFE598E4D108DD8EA8EFA45693A2F9EC8335C756393585E052FE5D150A58F058CF8C3C720F37112DCBC6324877E87541F06C968C46FF846CB512EFE65CC401174BBD1C977694ED7BD1702EBD2D1EF23BB56D0D0C1EC2D90A27CC63F0A09D83F8B0A5ECC181D5D4265911BEE77EB1DCB4780B53CE74DC7A42252F3FE",
"8FC54C96AC76BECE7284BB32C63FCCABED194E82FBBCFBDFDC6AD09AF95DCE6A5D6E13CC06B247E38253D359A6C0EEE00A660BC3BE6FB217B9B554C90186711B1E85117DF0CA17463E132333B8469ADF72C3BC6F218A96697E172CE2E6D6F4E70EEC2A0CB48F0A2E7991B1B90A85088CD2C59E3C9AE7FD939592C14AFE9A13829DD97E345D1AF92AB46BF196DF906BBCAE16F5A58A4D99C7D586869C81E182352C210E3B1EE822F4A95DD0BB3B285632978B18FC1CC29BD58C2152FD3BE73482DE1A6A79C3929DDA1D20B6E99E25A666AB20CD371A85DD20BF0A76C5A81041EC943A89A94AA64C5207B3166441FFFBDF4AA28A2A88173F280F3A2838A98667775F597D0368DC0BC53C1AB8B7670EE23E0ECA08CB09FCA68FE2153F01FCB7061CA1B6AEDCD815D611FF71868F50855054085AA1B7CA3309581532D658C7D2D6069C3B44E0E3CDE45222DD9EB40C369762CDDAC6D9A6923FE0EA7DFA1DF73BFF8B60EFDDB8863538A38528803F3EC27E09C87A2D0E160C2480F7D2003DA331FD5C7FE05B582DC9AFB114D2AD2F822922067F1FDB3261078F33879C497035003171165FB139F79BC508AB9D3250D1B53508410A01C35B53179076D9F46C5BE1A26DEAA2F9F71F442FD7D19D34979F6ADF96BECF1124551D4806E7136551291352748DD2D58443978C3DEA5ED0544E6596769A760B476B9CE7BB09543991EADFAD8",
"BA63AF12FA5F7D03F714CA9AFBF19375D2A7E31EEF4A9E10C7C2F8650552A6CD22FDE0E012D46CEBC773C87729C7E746FA4C0361099D4876701C0B2A16EA2B5A6B750CDCEB573DC711F95CEF06B8DF516CA2F9BE6387550F22502E53A772436F324569B25BBACCC781D30DE25B806D369AD1EB1D1137EFACD0BE765DF4D06E177D3E3F13E9A3165F269244FE8B130E9066B23474418A5191ED481BC8974074336E71BEB02B1BC34CC620BDBC24CDA8BCEBE068416E5F5B5A263A51CB3F6589F77D4E607939F7A0FA637858B96189A014B5688A9DA32FE2ABFC31FB00401DE475BA07265B3FD1890ED0FDA487D7C616E099B4EF4E47C9F9114E6BF8CC757C92C02C46546130ECF7CD44160F55A72831692A5E69146086C43A5F043BD1184965E2A6B154BD7B8BFB3B4B28C081B0F349900492C703913885428A82A8D2EF1240414CC0B6D56D969BA1A121D9D61584C6A880D533AB58B94C85F07732B5EBEE7FC87FF3AA279ED703858957A1C1501D71DC5420C24BB2D570F7589F0AC5B6EAA87AF68442FBB38EF693D2200E73DC73F58CFCBBE43FFCBF76DB4241FC4A7B131F29C8BC0F77C95843D2FD2DE39E3D4D44237DB39994380C5F1A1A3AFD927F6B736D585112920296B90CE31D58BCAAF8CCBCBF15BB36199B48F00F308F7E8264D039D6DAA6E848CECDD1544323C9654232F45689076B7C4B1123AE0FF152124E930",
"F2CE3D09E701F60716A413AF06E14E6FB7BBC29DCCD8273A083B00D429B57EBE9FD5270EC0C299D243A9FF0FF73DE19977EC16C2772C6B7FF88F83481924C921F49EEE41121DC79B7C23295EFA50292B11C7D45D66BCFADC3C3A390E03E13CCCD51309BD4061782273CE5D0E1BD3E11271A3C52654D2B846B5600D3E68D2C234807BBC32F1A350839500DEB6D387AECBFF5344606CD04BE3614262F068FBD256B2855EABD5A7C0AB5C28D5719C844F2A6FFC500628CDBCE869D2F090E588B57B796029A84E2319FB6E59960553B62BD30DE99AA7ACF508168EEED8DAB9996041BDD78BAA2A1CAAD2EF366F4A3244DCAA3F06EC9A24572CC80C3CF44B557CDCF6279165414066D6B700DAEA8D361034D9DE455A55E55CBE39898B65651FE709506D1A1FF67585D0D3E1B2C5EB599704B3925CEFDA45C33D92C9ED9F0C45B7A80706E6629594F66A0F74A4A767493B73B23AF323D519B0D05EEC62C6123A0829CF612DEDFB7F275A55F1629DC64D2F77125A1BDFD7B9C213C51DD9FFD83DEF42E87AD4E0F9E5ABBAA6B120E131F9E5A3097F7EC766539C733522FEB0F02604613CA1190024CC1C3297E7CDC3C514F8C3787943EE8CE457B516677437B2212A19629A0A04B0DAB8258A17B28F52BCE915A680152225304301FD2BB1BA2A557B3E3CFE6553CABBF79A47FEC31DA590308D156C3537B97116224F3E21EA3841A505E",
"AB2F844287488AB6B0F47218F2A4C54E6BE79A80F1209CD747FD88A575EBB11F2F0756E2C263E753D9388876E159EF3BBF99448D0865173572F4B6A03FB72244334E4E861E0495AAEF15276379F8DD88AF9313096805D5596446B48EF1F1BF2ABEDDABCB1FFC98ED408846D85732F807196C9CC9B283EA4E0D78681C73CB6AF89E5C361476F84E979814F30C0AB03E97156015A493E091EC5D854E8B08328581D80091564C25D6A714407D6B591F17D376953F18308B8BC12EAB5B5C9FC11AFEF293109785CCE29C978955CAE2601A1DBAA274B72CC18CF27FE077A0123258A1B879E1ABF84458BB10652CF7310278C7FA11BDC5D00DB65E5D6A13D77FBF9D02DD0574DC7AE3849C7C47820126FC99B9766920516AD1D63E7EAA366005714D6C1695E731E88D43A38B615407A99B32A101C4D417D5E36B886C26EA1CBA9FAB0C040468781897145489121EE47BB2FBD6A064CB325AF5CECE5D5A4BD590C70C7A9B068D318247D8FE16A3609C94A8D431E9872E26A3CDB19DD971971AE1FBEB2FB76F7A71AFC5815CF7871BB5EFE3BD0C49BAC441ACD9CE1DA4AFBDB972659D13775AE77F843259EED57A62CEA93FC449EEE1EFFD9AA2D36D8FDBC06A95A6AC9B067468F1D813F2DB95D3456063D5B23395824385D5C68D3F869C4F639962D599AEB26FE2D5148A51579EF385D31E744450E11307F0C803C70ECCA93F831EBFC",
"9B1066F98BA2299266D8C1351E6945D7EAA658BCEA46D5AB353C2CE48D01E915E740DC90E9CC487126AF9FA722A335FA1A8D11C2719F07CCA23B1DC5E5B9AB198077177BAE0B5F35C14666FF32926AE0239F21811921C77FFB56F7B218A353F8EA67448F61ECA318196B1186514D27416EE2F61764940A70300B69EBD02719191ACAD969B1075EDD09EAE26F883AC99DED1AE9355C2F30CC656934C0175E824E76947F83D791CE7A2960FEF547CD3BA94CA064659D1F68C0DA8C13AFACC07F3D5A653F259DD141E2ECE9B6D25F67CD38AF3F802CCE332049DCDAE1504061C2437C7D3BF4E15D88185D4114E96BFF7EC673A7AF8AFD53C979C89FEFACDDF873686D892DEF5FB67877734CD981C16684320AF392A464C7216FC5C8B22BC29C8430151FACC94AA1513A08E1FF2F6A965F68E368245510B975595EFDA4E80B7FAE432FCE9737962974905F367FF637121237B2A404C42A5A9318DC5ED9CFBBC2EA9C17A6CA37BBD98F8771CFA7EF58B1EEE40BA2D6C2031EBDE4AF0590AAE8FD10B2BC02A254C97726521E4D4D4D9FFB74FA5D06F8AA0BD43631FAAFFCBF01FCA87C9360C5E6A62A4B025B894E2C30F9003D29B642335DB0A3F2A3436A917B61776F96292A06A6B4DACE4F6F0EE0232E1C206427A0386075BF50D6074006D45E3A3CAB085431032AC20D2690F0435D257D3E2C93FAE49D75FF0F320CB4D348C6288",
"8A7F8C99EA79E1B2118E3B203F72FD8921BAA5C336B006ED66AC7181575D66ADE21D00FC8C7DCCA7DC9E430D1D086F2922487B5B025AD38750B4F60993368D829CC361B600ABA990E570370AFFF9BF171610F2A0B0B93A0A3AC54244073A0E816DD691BF1B0041BDC165125A14C621E01C8F069C3E05F48FB77E66A998C27A87FEAF07B5912B303A98AE5796F1B5D4E2EF52F38E68F0EC5786C19ED93612D7152BCF0CA1A3044898F9347FCA8370CE6E4A0510750DF6A42C1C35FDBD91BD2A26A60FB229CC35FC45525D12A092505D901A4F9E1E8D42D25C41B8FDBA13AFED8BC5566D4F3BA13A779D7BAAC1E25B6710D7718B73A641F23E1D22CBDED3B7BC3AF7D92B2DA1CF874D908CD8590C80F1D5055CB8228AD964DE099A4D037202C65D197A35D8A268D8A3BFFFAB39B93615DA295A09AB979AF925D895CB60B5DC5580055BC4347F0DDCB1090DAEF46C8ADD1009A5126362B0B4F1FFDDAB4A00AD8290EB84F76AA345DD73FFF7733035026E3CC1D99428CA68ADEB8CBE98E4A630F99F4F33E8AB66895AE7435D2E84EAC95CF19E9B440373EC0B4D4B2CFAD672C7FE1FE8CE5F34F55B016F8B115FAD6CC7B53DA7555E67672FBF6BDE324AA09FA18F13D9FF1041A12B04F30304774B07659F397554E66CA589D9D9F1F262CF659F718CA7F443B8331BDEA8C3980045562D909EA44E917FF5D47812A0390139B2A87D0",
"80B35D641CF6EEF705D51DAACE1EA5EE92057FD497B0D937C7CE9A546BB32DE580F8E434D644F191798A518785BFB9471AAA5D03700CB0B7B2635A6A14750F03DA4FCCB1B363C254A5294DD8E7943E4CDDA318C4B92B57B14EB0F8EB250686CE129BBB2B18EBE7FF53C9DA9C0B664C527A3C69D905CC6FAAE8BEBA7D83294C1656DA4B8308968EE49DDDC2D0C71A17B02053027D7DB8F4E77E3AD1C80FFDBD37938876B671D80E99F5F1C7BAEF50B7E05CC0CAB8979A3A2A852A7018673292CDBCECAABC74B839FD3C084682357A5414E431C1F25E34850FBC779285854FC6AFDCFB7B7749E0DFFF5F93A8AC146C873B407F2CD6CCB461312AE35DEE8D6D51347B0824156DDD60762807A5C132C0667FBBCA7489058C47A156B2A50CA5C24B894C1EE7C44B87179176905B7657A8E95AF7F2EA6C6D2A12384CAC9E6D14253DC5C31BB8FDD2462581C109D2DAF72238E4B63F436DFD7DD5571548D2206BCB8B837D8CE8C9C3B3066E46A1655E3D84AF42DE1051DDDEF438821E0F0C1EDBFE148356D707036B269C19C4CEF4C4BB4048364E2A3886E38B42EFA15F22CC8F92D802EAF3FFAF9BF45247DB76C03E99F662884DC2A29EE488023BF0EBD46539DAC307410846B25280349106CBAADFD658A066C3664E35C4C696726140275AF7611D2369ADB8258EBD2279DD24DBFE002377B9A3C1B120890A6FDF20597417F88F15C",
"ED6621A6BAAF64578BD988D08FA8D3D2873C87C38AD7EB38C0922FF3F7E59F0A8DE00744698939D42D459E6105ADD3C77E9FC4911F572B3856C9AF6C15ACE704F026C6B2FC8BDCD9D84022DC10C47E11E934BEB7E1DCE8B6A2BAECD384D810BC3AE587E7EDE57CDEA908DDB020885624BE042DE0ACCDE1511CE38AFB6C9DD812FEC339B8137D88108F07035A3869CDCFCB9402CF96B9E331297B644DB13DFA88F60605E067B9F35607D2D75573E0913F8080EF603AC4B7133D836B84F32F48534237CC559ACABA53A96885D297CBB572BAB1C3275F7CC7A3AAD10F29E727B28BC29B038F4003F8C93FEDC88C63D72609A5330F36F4986CD9536812676A89305C6CE58D4C49C088B5E273F2AF77A8D31D1B8574B9DF02CAD4930D7A7F76C067E7160ECD1A845FC6A3B508708C279A2C94F29108FCE34A63E5D5554FB530522394BCA00508DDAE6039ED9F47609781CC60F6E211BDF29E6F44ECC873DD24F1D37E49D7BB7D6279FA7B9D08B5FA8F9364EF6D4C8D129A5A97A8BBF17DBD5E64C4A31426881687BB79285B09290DE61AE40C295CB1BF3C8AF756E88068A777ACCE3C4B6E78C62AC1E8EAEBEED0AF3153983214D7459AA8E254633B52E5C0ABF4647B906AC50A62543710EE92B335A7162B540AC70F2B2F49FEA892BD72DBFC5F7A35C3AE56636AF2887BE680FA63768C27040E888202F700007DE648482F5307C56",
"914BDB196CD56E3B7D7D3F1D7A5E4B0A1389578F111449DC2DF643E6E29F688227C3C07033C2A3818342B229F63C229FAC11EE1AB6F0FCE8608E03B46DC983318DF15FD8DBF2970EB342BE2E534BB0455BE58290A48FC60973553E94C4CB53566CE0250D9FCF055936523A8ABFC9287DB9DDEC54710859DF62829D2B6A100358EB64E6219451868D6BBC2AE4DCEA0C0E338B26B748D4A1A34AC16233046CB7D346D0D79A3CCDD4CDCB435B9B3075AEBEDB4C0F18C5DC006F5C208D882308510C75E729D08C779CA99D5A685E78D5628094AD137BAA635B7FC0F492C48A9CDBE63209C8231455012EB3E830B5B2A79ACD8FEA8016243EBC85BF5D6F46A48FE013D2B3B789BC5F743200BCDE03995BB2B6A640CFB099788E380B4E01D75409A8D8B3887DF2B1CD34960091653EEA6C52EDD745B9363BFFF666891D9C8BF511C3C07D38F49DA2892DCCEC81E1722F6EACB3214E3335C93E6141AB94E5EC31BABF8108F6BEBC3E60B1BFE37579B4D5DC8B77A347940CC1F6BFB5B46097B1EEEC4C354159BB3475E05FAB6BDE5672014D9489CB70DDF537F7209BB9EBF1FC6B8B94564AAAD5ADDD83CE6E51EFCF73DC6080D738C4FF1CBC87ED420A0B92FA459AD7BE58789F0A191D149F88173184A22874DF6D39DC1BCD4413648B178ECB03F8358547A68DE7B672BE9BA1FFC8BA392F8A58ED2806155C00F86B7669BEE4220D420",
"97051FC67ACA30E8AEE73D3A8CF38BB13524D4E0EBD9BE68398C7C16227CABB1D0B0A0ABE7B6384ABA02905BA0C3C7363599D059C7B4C99DB165CD14FA12FA7912449CA7DD5E346D8010C85A757382270DAD15BA3CE36A76EF55F81A1E80BF366B37FE3A88EC722028C25E234E624040450A99CD808F942568AA7133981D72E7F2928894670AD5399482DF1B90E7E64062F830B736C79C30F36281495C76699CD48404673FA334F042F9E0E67DD7F3853BF71ABEAF6A9A5546855E840CE42B224D8F6490C6CE5FC02EBAF4FFC390107058F54CD635D4A7F2878099C1EF495750E6921BE2F39AD808C4210F287319F811A254CEF8CF153FC50AB2F3D694A530949E5F578D075DB96DDCF2BB90ED3DE09D9CA8E08662FD8982741DE1CE0A6B64C3D3D5004B5C04B2B0DFD976A20FACC94D1762D41EE03B40D2CF367612812EF4CC41D1BFE9CEB51AE3A22AF1BE7B85A057D3048D0E73FA0FDAF1119EFD76F0A41BE63128B22D64A5553E9549D411483BBCA1483EF30CF6A6D317AD2C7973EFA6D4C1121F703D2F48FCDA3177AD450D75D2A28D2C244AEA13F0E60AEED8ACBAB444D400DF5E280DB799B2D9A984DF1E2567D39D1DE58EF78CA6B4D8BC172B07DCB02D156CA96EEFAC69E556CFCE0AAB617C7FBB8C34871C1D35E74B7BD307D3F2E424C7A9AD676A1A69E0FE735EA50887A1DFAE6CA2FE4460FC7EF323ADE493020"
};
const std::string Galileo_E1_C_PRIMARY_CODE[Galileo_E1_NUMBER_OF_CODES] = {
"B39340CA1C817D81EF4FAE4E95BF3504A7709089FB48560E9E3EF802180E85EB2194E05902C6C4C52021FEB7EC64FD416BCEBC8E39D64A4B5EE345291911AB8204A888C25B1CD3D9342A56C538636D3EAB957037D09E879AE5F3A39834FBB84A3D8D5090D7814246B62E9CA68533D2EC403B4FB9488467FF9758B0D15A8CEF89187A1D5897880040B6C3C5244E85A2AD14BCF2F5ABC44A7B1D4A87E8BDA05766218773ED4F70F8D1D07CBB1E8CA6298E64EE6DC5886D37495BA2EDB3E0B0B68AD9F300310B88898DDEEFD484538C31A9BCAA76ECAD0C16607D32189058B0862EE9D70CEA9D304755CE8037BA4C46C2573181748A212E4B2BDD04F9BC240518273DC17CBAFF21A03E9120FA7DCA18D56DD1D9A7E510C90CF219104385F531F2EFAFD185ECB6B911F9B7809D98D86F15516FFDDBE9BD1CF8662EB777C3F94EA3F962D7B79449FAAD39935429E92CAE5637E9BCF4E94D413D27934952409AB536BE4055AFBC4330CD1E4B5509EFE5F8EFC9ECBE9EF377DE7E37C479BB9D3EE7745E4609B0A6D2C5D92EB3C9E2278C1F2221FF907596AA5E096ACF8990EBA907E43AD320F8019CB6355A2BA8670EE5A4F463E8E56F8F1D3E7F4922510FB668E32C4CF23AD8496399638B095B47833E0CBB34977EB3E4242EAF870D86660D6A73F83E45D6E8A41EDCA3815079649544597C5C43B6C93FEBAD5700D22EDAF431FD340",
"A64F94BB47BD4033C76D4924305907EC1F618B43C7535F3CFC093E5AF5DDD5C4339F3BB6D835B5C2C2053CD3D5693368D4E1A7CAC59425D1FD96809C67285CFD3FC05B01053CB0773221D7205778022F487BF99D1650566BE287FD7AE882AA8E8F52E5D4E3C0C2F971C9FF70AA378691EBD8ADE45CF213822D09FD05243F9726F6C69893845E57C37A7643E16B770E26F431FF69D437271905D270EB85D8D229D7D87662121F0BEEB1E895ED9589A9CF5833408A04197AC9025D8570AD9B75DB7E192EA0A089504996E9DC652975D83633619CFF80667D8B519536B3475248BA8213C8A4C66DE69B4B3774BF9142425C57F34A27B1E288119E3FFCC6AF6A21087F9394F09DDFBD42F32D059B8CD4104A519BA640765D5CDE490E62F10E695FBFD33CBC9D2208A532C8EC25DA28B8CC1B6850AB43D9B5C00B6E74B7A148791AB07B328D347058C7E6233E18C5ED172C9F9E9ACF29D913E2A1614BFC0893D4967ED033B2B9AE6B51F908F1CED57C14FEEA85CD4D9711216BE7F79FA6721B7DCCA033C80127AC6E5FCF58EB4005EC24CB4886D787355362D5E7031B9B2AC2A86D730AD734181E723A811FF510A4DF868001973FE83288D78E6F9B9441DAF5BE2974A2848FD917C3BCD346A431922246EC852E4AAD467E60C15D61DD3BF4A207BB57DB45DCADEFE3210BE74B9DACC918A394469F2E2C95AD1E211947948FE24F5E4",
"FD1F6976002C39C87187C44E3D224ED4DF0B67750105944C651A5E57798F168A136AC0FB5979C4E847A82B20A2E6C45DB42EF2B930A80D3257BCCC53EDA966F5DCD9AD47CFB226EED9B62A874E9F6404D4087798A1005F4131171D3A47907A3CD602B83DABE094D2CB031867DF4595F3ED59FD8C4D76EDEEE59E422CE5C7D0A5F720BE94FA24DF05F758348EADD5EFE9197C6BB2292E2B14DB8C6DB24AA94C5FF0F5106D2B566058D32C58B63A150784F7B02478D9973DD4CFD2E84059AE0F4F1320754B7EE83F04A51C67EFFC2EB1C301C0C58DBAEBE95474E3484A76500103C14C40BB0B7D3A04D8BDABB605C1EF9FD4A6564934DEC50BD5878243AEE80F9796EED70CE1B1E8B55725DF76472D12D4A7A487989F42E6705818B1F7E149E97153A7B05A82FA3FBE51763E61171A4E12931472E94CCBA74CC09483DF93623FC60945070FDDF3A00B561650427E4BD64D675B1EB398B35EF057A66FD0B48EDBABBDCD57C32ABAE46F5CDD0CB1FCF17765258236F3DE40BD5D0A3C5C978D81DEB07367AB20B2CAA9834B9576161C4F20FB9C184A01DC9021A4E92B71333354E05BBEA9015E5AC4C66312E8B79F0B92279AC7EF1936BCC30802B83DB3D113BEF64452CAD7ACF6674FDA44023A661019841A101BE80FDA4E3210AE774E433A9ABD97F2755259AECE21F7A8C3B1A3D471F874D2EEC85B9B21BC0C2E2EC9016F847C6",
"EE38BAF6F61704B01509B5210A0534E4702F93190C392E749869B5572BB7AC4D7120E2BECD6618CD376C4C1B4965F7D9D73400824E88A5C7B5B66BA88C3E0065F9628A9AC6B91A1882192FC553E3140349934D20698C9F291B5370948AF6CC90C837B9F3607F13CAFD492CEF1723376E6A5B813A56301B88A8799519CB7646F33F91C44CDBE7F768D7DD9B323A5002D2F784C4101AF90D6E4C5ADE7D085C79E827D43E10DF63AC70BCDF13DCE0471B487C5ECB752B9C3E20F75DBD243790C91355ADFD7199081BFEA03D80E82445EC2831FB5014B85EFC2A52748A8ABFAC1BA3904E178DFBAB26C1750228C9A031104F58BB3B91905EDB9EADF7B0F6DF22ACEB0DE944E277809D77507D18EAEDAA1767697398421115D04AB2EBFC466E99F0AA540482A49C6AC8FF95E3F962734B03EF39873A93B70470B46FFFDFDC15C89F8FE2F4637B59F9BF9C5752D9F8AE7EA75D1EAF1C22CA27E5D5C9499624105D61BE2A691F9194D277414532A5E6C63875F7F20DD13C6EE73B0C3568392B14A5042843926472ABA343D2C427792199B543BE1D43A178FAA7ECF53B98AB7528D8E1B8B82C52D973CA0427636505837F94284E8D6B4F496FC5A48B7958D4681DA00651B8A7BC56EC859C071E4396A05F33588B8087EFE9635E565E6B5A8A70DA70F50ECAD1A85E6E36FF07B4FB3B9119EDE0B611CFA91D9D4C58C1F4815B07B9EB1DE",
"CD37D0FB0043D03444A939E93676B9DAF5F2D19A2615E3D97D624E62ACAC8098099FDB9A5A2F4B3ACF20F75B6807A5A3F157C2C0F479158F4A10FB4972855F3AE2FDCBDEEC00A4D470AADF5F5E571818AD6E872D897E2DDC402006965ADF16582B1E06B1861BF7D0C7E7BA491C79E86224AF6B246317F725FA74DD8376D63D7993FE2F2BBBB2F1DA9238C6F3FFCAEC50FF61E645FADEB6E03F883892C42CCCF904708B123C9271A670D4DCFCD602951D12F5213937CA2C05ADDE9EE3908E99AAE8DA31951C36D36D671CD7BF15DF60B707F00BF6EBBE5476926D015628A85758BFF35C4AC540F39E761B2ED3CA9116E8680E28BC387058E0F69345CC6AB3AD160E9F2BC4D6047A1934E15D3D7A242A296333C09296981BBF3B8577E4B8ED2A3624866111F6638F8955431195B60C5C089F9897DDF0D34A3DC627CE337AC8128C28B63A394908E4C083BCC4522DB8CE5720C45EF76B2716225E53405FCAAAA72AC198226575D5225195F106C1249E4B87AC05287A3ABE6C51A2A41E07F56ECDC46E989A8568D35669B525A6FFCA90DC91D3013967F6A5F4C022FFCC17751B68FB0D8F16FC9229851DFDCC060838F923BD44C1AD70A993E8EBAC1667DA80F91B66F8F5B375D35275188E3C7702C2312CEAC5B20D67BB34400401BDF1DBFE79DFA0EB73F173A04807215DA5CE8E1D28F2126424C3DB44ADCD7A961260FDBCAB31E",
"CAA02DD19DB9C721EB35AB7D64B8A387796427242698A47D832C3F1AD4DDA0B5926FFCE9319EEEDA1565ECB0FA1EEDB424414120AAE8CFD0BE88D4D248899A0BCE31F9BEE7A4DC4DB3C3B10444FAD6ADCCE28F0EDF7B808536ACF5EB05AADAE92693EE02C9512B3EEF000844BA35E24620A2E8935354B8432C07C8FD615534BCFD0D8E3B572BF2CF06AD343997590FE8B244A32BBE69125B5D7C5E513A493724EEA8DA6CB0FFF3ACF1C5085A8120694CBC40FAE1A6326FD71487CC3BE7C10A34315CDFFA8C618B68EA93D330945586B080381F0076351B888087F56B969E6D6A311AE03CC79FF6861E715C9DA9AEE751F1220661581C75DCEC0515A1C9259B9CF8E944CEC4B1754E5809E985D6F43FE45710893242ADE0D3B84F1E1942B7A95648611595FED13F546CA11DB8E5A55A3C3C78C3793C6689E1B3AFB5F67526A480DF923A586A779F94A09CF963594FF4B0A387876EBB3E8FAB888C97F6773E7F0317B038E47DD7D109545BB07263B1AA84284B86E47FFB9784A171D101E7B0A6D38BCAE7E63D827C999BF551728FFC642EE690B01D486CB6EBEEB9D5C888112589EA5CBC9BDF49E675965223416D6DA02D2333BFD4614706BF13373973207C849A0DE41EBA137FDF79A1EB25D74E30CF60B577C2787DF04740BA8CADE3F9DA55D3F0084F02809E37543239E0A71E99751EEB21CB3B41488244193A4868CBA9276",
"FB227530F82BD527E648619E532D7646A5ABBD15DB91A6E7033DFECCC65D095A3D83AB77EDD2F3FEC52659CB3AD1BEB009D7A1C9BFB544291EC1C67B75DD6DAB06E70C32C714983139DE4A41EE07B4F3C03BF566558484F19A3BB674B6795F0D8537BC31BC8D7A38B2FF1B2EC8B78539B2251D0E385DE484B05A411477681A3AE7527AC98BC2943AF1CF7F09ACF2DDE4530AE896BDE1266FE916E833A1C0CAA2B2D2F5985AD47B2D0D1D3AFB6E50D4B3DA7DEEC4385E6CA8FE22760F92807AC55556AAF7973E8016ADFD43A3919088B768351B1057498D2D668D7C1E8C63438055FDF7D36C5E7DF02FCAFCBD9291A2149E7B429B3202D329E47CED51EA5771772E308C5BEBA7B934597540D83DBEC6C3BC61A96EA4CB2D7530D9D760AA9403338CD95B829F17547C5A90D161F7B8CE0037EBF403C91C0D0C70C589BA87CAE8DF26CF14281E235A686CCD10E2D520A76265C4C2780EDFD0705E89EFE3C953FE760DE45A8CF1F2D3F36DE3164D5BC2CF32204228ADD7C182EC55F1158AFA9358BE179C722ADAF1D0BF1306A0B56218857FC5C21001499F61E273442281E585B3E6DCE148AA97B6622B23BDAECF983BF186F1B34962764758AC3C20C84036061D49CA33B3C3FCDF03F47F7E53B940DBB6E1E4A26702A118E525A9A0EC229085C925D133750ED0B200CB28A113289DE143D1D5839D2AF8B0525E0027F34FF32106A",
"9E5DA18A19514CCC849E9697AE4BD1B317BB34927D0461A96A7AF4A5D6C13107FFB9DE38C5E8CB7C5682827F57D94ED2E77D36F9F1CB05E4C2C62B1DE254C7B1CB236FC4ED70BF8DD1F43AC773C16A37392B895F8B157578C477C85E53FA7CA58BE70D9187AF5F7A18D5A1E5642335E46C2F8F4691AEEE6A9692E21B9668E2C083D9F45C2DB3E991588BA87A0A23808732EE39E8B3C876BE79227C782F07EE3FB3086AF913D71D71910A0F56D62B5DE5E224F7856A42A4A1B2AFE380827BE86E381FCE486FD08A91B22BD91D09615F417E178C5593E41B0917E075133960AD28B4DD4096D1E84BEF1363098DDE92C29CD508C40BA7E785F46C1E0DC72E729D394911DA919EA6F94D14567FFADC61CEB8DCA2821B1CF048477E2433E9DC718DE618EDEEF302CDCB5DE472656D6687DC41EA34C2BB4DF1CA08DCB933BE3EF4B419158BA0B68AE82A64ADD58559214FD88A4CB34D99F646310697DA982C2FD4EE069DC1CB102125C34A89AB20F17B6EF648A834627320410FF6881C7919AE4E71CBAE5F8200E523934D84BFA897C44B89B9BC6BC0129F7F97EE0EC049BA1AFD67D00CD624A75FF5A30514399BE4801CED057B498B9DBBF0EB9944295D5B6AE968C4B8BBD2B9A9E17A3039C5FA35A0D30AA54CA426C58353943DDDD3FD185895C0DAEE950455FC131F520B46AE118C7406D0A72BE6127C5307730AD441B6FC3D1E0",
"8589F8396F5B1C54CAF2B17D4C152CEF347E66EC7903C878F2823D4ADB9E7CCFAFEBB926B7EEB4AE1BECA339A027CE8EF997957532FA871F356E0326ECE0BCE3399F81179BF78C5C7D135018ABC340C0BE58D3063DD7CDA4C1918A0187BACF830C8B6900D43B62E04DF6E831CFEFA13BDB5E873A527F24327C95DB4BBDB65C81A20F959F828F5DAE4DC13E5CAC7417EE089401FB497ABE10144E28EA383E61D4A9B63B618AA7CEA4588B2911EC581F506062B05E7BEF723A5A465C9FBE70E313753BDE3102845A79A206BF7D996F49A21752D534B73EE83B48C1A225F85F5103DDB9B6B8380F61AAF26E5CA643EB62EAF58AFEE0D3494E4F7A4F642A3454F4F56A406A264148FF5DAC9DF5F151C12E89ED9D4FDCC04EC5F0022DF8CBAF3CBC67CED2853FB4F8C5894C96CD00550950E7EA2A26C80A72DF533270A0E23EDBAA4D0BE935D62CC885E1CCE653D66C51E49C43952042E1B2D043BDA1CFFC1E98A3F806EB587A4EC9AE299BD838C68B9BBF7C420C12B23AA2793FA0248C932A91BCDD641DCB38F0B2D7187D8986928DF4602B381BA13B263291134628FC91C8EDE92594B39650B877D9A91DAAA05295457DFB2C5D8207BBCDFE16AC5B93600E33BC970B38E18808B1A732889320352B524B109560136E605D32784CA01F8B11D077C81EAD6B7A5741C82D76CEEF764FD07E361D531B75106AF1572AD1375B2BBAB68",
"A3E17A4CAD2ABE76E32D18501899F8D60D293BB1AC3ADB64F81148AF56741790F87F8B7A2D9A6E7645EA50B75514C394508884CBF9E320B24D41D8246EB3C163B9101240776C312DB63C33889E3C1218435850471C454486DF7FF4D2DC0AAA14980F394CC8EB7B828A60C53A2FEC3315BEAEB30045B3E65006C6EBB23B47A8A069EAD45E32E771B9C467B4359EBB681AB48C891ABB796544169178203BCC4BC6B4A278DCEFACE5E9385C059346A23DCCA001FC9E47CFEED4BCBDD947B12A3F7E5FF8B9372D9497EE1A508D8BD3392BF3CFAD58F0191B18F6A300FF9CB8D914FDF37B48BF24C2C5CA76ABDFCCF833D51D48FC90E06E7B972944BCBAD169232A8429B6100BA562F7F3C55A625A1870A7C7D7BC9BD4C4783278CD95D07F89E8010E78876547F9AEC44322B0029A922B2922634ECCF2BBB47BF87909C494049550F1E6D03BB5354DEA7E777F499D2D6239BFA5C1CFA536F8CB16F4DB9EAD96F83A4AD34AE2C6893ECD6994C89E7F4FE426D95A18F93B88CB357996B8E5A34C43533EDB1F28A8162FCBEF03704FCCCD80C32874F345D34E81EE813DF5CC9B9C299362F8443AABE91BD0EAB9746E431804B6129FD32916303A570323FA121F7AEB2829F2A50A82CACCF6D273FFBD7AC6FFC5807771D216F50742F7091946F91460115989C87E8BBBC8402B4C8B95C102CAB53843D581FA9F16C0ECCE8944E5FC4BF4C",
"9D7B1CF0029261D65AE1F021DAFA81CF1673C9E0B47FF2C37D1B1AF46E7A91BC5E529C8F93EE3BC74E92B2743AAB1EDE16A6523B5B8A591C617C1FD0150E63F3B7EF0494162437B0FD555A83A3BDB519B3BB209EF7924D6BCDE5992BA6248690442E72CD5EB64B4C3D3F7DA339108A18B61AD88ABE87BB7C85A3A352D7B882FD683B2637A17A2D9CB0B7F41456DCFA66D62913F145600BAAEEE7EFA5071C3C9E6FDD0A6779A737071FA6965978CBC89776386B108DD7216FCE962FA87A26B29FE0E732309C0124B0C1E99E5642E5EAE670005B078C097D16C58B8923633C18FDB0E8FF8C4610B789387ACB5A2DD0B6AE7E0DF43A6A9E8C3B89C7E5D628D59759C58D07E0687812AEDAEEDBC63B4FEE8524D10E4B467696957E6791C1E94B13CADCD0ED60752C2DB1B65E035EA72F89FC679138D3609FD2A30E4DD1A946418253C67AA69B07EBB95D4973F562CE3773430007A6DB77271D5F2B342CC5E76E115178F9C7B1600554F5C794961BAE81A5E9B621BA17851008BED9B556E461A553FE9BE00A40891750E4EA4B475216283B530CB8D479DC70B026E07889229F6017552AB9E01EDE6703FD1E2D59AF0B71E0F1DC9A42ACC5823324BEFC52CA0DCD25FE8B10C999152AA3676A30602D3506F78751477033DB7AB1A2EDC21A6FE51273B6B2890088703CEFE74F9EA89881896E5BE124B1FC9430B92F0C0568F5A068A80",
"F23088E3EAA0A6BA04D0633AAFE85203E8B1829223FA6B730F6DEE6799B521F2E8323B8793D0F7F2BB9305B3EF4F5B4F1CB822836E4D92C8E4928A851BCE688329DECA6F7285DCC85195E5BDA3B503B8AEE6F1CD7FBB158444E7DE8BF6A9A3CDA311787755A827BCAD3DA5621908EA913C0316B9B52BFB07ADADEFF17D3766BB450DD71328A0353B09DC24DE93CF83A2E5F98BA9D612187B601157D6B140E675228B58C9398618C3BF0D11A226E489366102B9C35A916653F0DB36711ACBA5F32B327F5789F3EF48A338E4676F4BC2C6A1308597171903D2AA299CE7E523C2ABE4B15AA4FC48954187E0097583EB099419047244B4931326E5923B6313DE08423DB00866374ABBF5C31A00542CB97CDFB8F71046AA2A6DBFD7E1A71C068ED70E8D7C3268EA3E0EEF2262BD7991B6C59FF471F73A4E85F4FA015E164F9C15FE0AA5F4772BF2D62B26D3EAA25CE83EAEC5EB3577CA83A68168FB64C40A7A155905CBA6E64159E55EBC928D125E55165C639F545B0071EE3CF1A3F58B4994BB4BF50C2B24F2E06E4ADC90BC1C0954A257D88444347AAECF136C15242633463DCF984BB6736666E38F1A45150B1B7D1C31DE06EB9C2F4097E9D9B4D21EBC9F3A918000DE2449DCB3F5FDDC3C773A645DF560F7E013E847E2356D33EFF1E215782638F58034B09F4739F98915BFB0B1DC124681492F58021670D03CBF5E8F962351E",
"EB07F9EDF03596ADC2A3B7EB6DB1CFC911E9A4C42336A57309F7B6C3389282E557D94BCC71827D7C5737B1C530D2A087E3F507242F3DA5BD1BBCA4DF8B78BEEC1DBF7EBB2EA1CF1DFA79E60785BAFA23658490C9A64AC61C45779DFAFC6C55CB5C9FE457BF47E45A3FEF092E178ED4495C0357B459E95AAC82132FF1C8044F4EC84EB882DC195D9CE996B1CCF523098E9E1A57C37C2E2D0ACB0EAA34B0B56FE5A0747130B1E75AA923F6F94C0D024A7FCD22E7A4ED8B201966C417AE864420767AB3223BFF56C64D4F8F557DD950F7C50D9A39AB2C742CE686C8F92B35711904C600A9D4D3DD83F3DF1ED7DB8042C76B0B7D5D9BCD6E0B5524184BF99D8D0B4F14967FA48A93A2F44E2275ED7E59F3991EFB0CBF2E26AC1F8D9A41AAE4563179254BA37028867E68C8179454B8B71FAB49DBD1F889104CFB64C8121151364BDB64BAF854B0DA22B8620BD7EE3D4302A88A115F8BFBA649CAA9EE7EF5BC95CFAB26503A9D26033370A4EF3CB8A5D094C63305A833387B4F8371C6FE1987514BB458C571E6CB5DF5FC900631652D3FA4444F8F1F0312204340FDB2092F709FDC51D2680753131ABC33712B4F1067EA1CC87C40B281E69209EDEC42C22A88950E9C1CE8130DA9291897BF2D8D1D106911743E7A9DA36220FA90A02A34EB0B28543217839374EBE79F40B3B612236C902E4CD05CE2E1C07F3DA10E2AEE8E387494E",
"E9D537A821DEDE526B441BA4252785779B54DE76F82747F8607B8952DF990F268C039CC792883B1C76C297D81C6C0CF17DA8BA2C71110B16741728725839D33B5942BC0A5614A3650675FDA5D70F29154A429A42819D6EDE324C64596F93E84CC9B2C9DA3717AA6DFFCD03B75AC96543020A9F2024620353E1364E4320FD44933799FFF083E73F5D20B83BF77EC2247964ECE442C3213DE99026F8FAF0E96302EC60067EA38C5CA0CD989475205FA38869E349FC7F79EB81F8457CA3D1A875A8D166C96EBAF1F39C88815E2258EA1A14943298DA39EB9B738AAA4E0035F9567A0A9D572785594496316D56EB3D39E1F3F243D4F16111E194FC537A635FAEB2FB4401CAA9EE0091CF3CB28B366CB5446A6D3B10AB86B4B1A0714D107FCCBBD50EAE520D56A1161E03849192F5096346FBE5150B6D04025A564A43A3D22BD4B7E10DD4061CE20FA2ECDD36F66BAAD7EA96CDBAA0F063B814707718F47278F8570F77F3B15799D0E354CCA50DAA38C31C746B17482297D9C089FF379454FCCCB8730D89B1462AD95426370AC37DE50B775B952663B97AFBC403F6F729BB9CC1D21DD89EE78AF09DF8558F7E68B3711A7D9075DD4754174802F52CB9683FFE746471C7E543FF388D024327D1866CC5CA6775C58A14D70A3ECCD3EFAB52F9AE6CCE146766A8419FB546E39EB604F43B15AB88C72741F8C7D0A7FE2F462D360676D6E",
"D79D916241BBE52B61BE8210A02543F75A47032E9C0CC128524A675E94D8F79A69B6842B0C5CFF5C1AC98D2085299BDBAEA67A41C724CA36B6275A80D377DC3A6EB4C8D0B6B88241334A95300B53FFB546163D2889D7C85F1D1397924F126DA76085BEF131A65C7DDF60DDF4086BD33B44D25025D689FF41E0C256EA12F4353D9E722EE37907AA8BED0A5A606333A031AC6B9A16614250916759B72FE6C1828BC6C1966C9EBCD51413A77F41F808BCA2534AC49DB1D32D37878DF5CC0BEFCC099C56CAF50D8B92E7CE616AA026EA1D81DC7ABC17C4705F9B57A0F99FA749F30F93DFA982A083EAE6582C8461A11ABA74B11663ED7D66EB4F8DE14F090EB1CA6D8D81CB6B063A391FD354DCEAF7DB71C277D0E92B4B463873DCBEFFB698BDCA17F80845EFD5F0FF150ADDC9D7797E21E4279B54BDD4B7C9D403D9FA6101604B79AC377780A5461499714082942313CF74AD1147CD10571A31D82871B6B3A055D50C6CDA4BDDF3871F41EFDAEBE8ABB995344DB6366E35C6E506907AD7FC76632F99124A58A32C86360FD6DDBF50324D86694518AC44F1FA19662C0EF0C0860811B5B976A96EC2A1449E53A7E4A07923E9F85794F228E441D92903922E5783F2FA21C677251B6B8DB02AC2E242C0C8652E0C17C9E3858E52DE78DC712B2DD5D2AF9A42DB2E2BEB3FB6E0FFF13DB9A1E02C8F84FCEF3F7C4D2DDC09F2A2813E8C2",
"F8E2DACDD88277D482951555C657B3E3C5DB79E5A43500F7A2C8B30C854DBE611FAC1087FA03D439AC4635D39211E234B82A91248DEE5D4FE67A02D5AE25C676E64C4843E419EBB3C4D81FB606B9CA0836F8207CD19D106C0E287EFD8F8DB5C1A3A22886C2765FED26B5189153657B7C47D5590F11C6340067B800669B05A0849BCD2005DFEE6DF95833C9E94328D72F931D69CFBB2BDA81AC83DD660B3B17D2BA4023491DED324FC4F22510ECA4A5194B1245F4F3FE334DA9C1E6BF83A3FB30897BE54C688D2A7C5845F425866F25DD0A9852BA6DAAF8437DD80BCC72B3E258A906DE079A2D33EC5C5F6927503BA13158305DFFD3F86345524394151AA557D6242060F276BB6BB25586F632942ACF5E0883CD3F8393688F360323A000B82BD89414E9C807994B0234D730BC6D7CD0A2BF75D9F510786E83EE98D4CACF20EFF86EE9C38B8D52455D8A694B689F0D9A632E7A6AC6675E190A12ADD716D2C6322657B878FA97267C1BA4631584356768EBBD1F13FD2F37EBDCD1DF96FB943942E8A5188666235B455BE2F770C9759A8F070971CBA49789744FD2F64DC4DC6E003B3F9BEC7617C7EEDF6BACA94D374400499CA6813C90A03DFE2C537261DA93A1C0F6D8BA93D1EB5FB17255DF28B78737582FD675D056A4C474A71CA8EF0D77BAEBE5637711AEA3FF2B014700448C3D74E3DF264D773360F45CCC3342987169C9A",
"94741D7F05B0CA50908E6BC14801A28E353551F01769451B1482FAD0043D5C72331246D9AC3344F0FA2E28FD00E86B38F5E0452F46CA111E92D01B37E966455DF1374883DB8B055C4DF25B42182280F86D0D825C096018D2949B4BFCEB7BB2C8A5BFA2C79E27F11A7F9B43A50AF928D81FA95CEC86A114222B99786072311025672AB04B2593C5AF50100B71D052AE268FBA992BF7868E58EFCD07A24D2111774A36115C1C527B5192EA955722EAE849EF83817FE8595C96EA2D76FECF6476D89F65A262D94B3F5E89A5DE8B1A7333EFCDFDED17FE1CCADEBA0D1E7B73E67491B413A862E34A308D5C211787E6ED8683C6E1DDEB8EE2D281166C03E7A72D7D7BD8B878D07D2216C21B855CCDA76B7B75DD1B2CB876E59F91F040D42B97050043499DCFFC65AF803E2F7455C9669DD9896FE1F62227936DF905835A644D31130A39479DE75B4DC4361E41202D51D50E0E4B4B218AF7F5CAF264DCD060C296E777DF1EED6AE8147E9B6CA73184C345FBDD89DE4A999C42AB4681D9EA3B86DD75031A33DCDC807F8FB14EE0CE61B16068AF01CCE7378C9D965943476AD21A469D8B0CAE15BA8FE04971FE1EC61D3AAD3386DF71B33FD0B4F324F3DA518F0CC0353182B3D76CF4EF5AB150FB9E74C28234CB3D907AC81CB6D3B99D510B481E1F0423D6F4987F5517ABBBEEC07F46AECEBA5F15D91AEB0FE91490E91F739D465225C",
"839A01464B473A64A3D1EA24EB363EAAA590F4BD0E4492FEC4E3D4DB5883E4873BBA17595FF48134893F16F5C4A43659C46484A268C3303B2DC345E8C98FBBA6D06946F997074AE15680EC9423D6464585D98804B3541662E183F6540503BEC204749D58E3DB9ECF11C80CD3A38F8D66FFE6CC8A003BDD35F547E5039DE9A21F70A8A07B2DD89B68E43B42C2E021A11909817C543F839E6862268E38DCE712B4D49C39A5035F3D6BA19AE028AE70CCF557720794FEF6442999E740CD6AFE6235F165515FDC24AB6F578DB2549C8065E008577FCF8B8DD8A3BA679BABBC9A747A4E2DABD91501424E4191097E689A741EB6644A771CABDBFE6B74ED3ED171DF8DE641C1D42213B9D0F8CAD1E11FF63670F5587F1FB7FF92276AB48F31751E7A591AF4F0966F3909883EE6015639671BDC3D1378750F66F5DD165912CFF1A54ED463905404EB7D3412EE2B0F0D9E6B99EC81678ABCD1789BD8F1D72D3DF8754A16DC2106B83B325807E27BCBD22A25DAC32F27EACAB6A4CB6CBA4CC90D5302BE5E9827B7AB48BB696B2902975C48B3A4BA4630B14E0FD8A050B0718C2829371BEC597387172B0B3192EF958BD1F7977EF9A3A6C80D53BC961315F97B714253B9731A017BE2CA1D43024F75E26BBE989C4D514D01538956FE4B90BE17B3407B55BD08BA50FA807D0E448B7CAC65EB3FF856772A933F0C5F3E6F41E051015C6F9B8",
"BDA2B72F0BB0265269F198207FB061DA29DE43E30847E7C062A581A7EB53491EA51B51EDD36F991D15AF89AB53198537988350FD5FDF8E003019BE115840B9BA55C238C3CBC72C0E24E25090A3D6A59BEA9FED0FAC9EAD40451A95649638FE0BB0F8FFE61AF5B9A8AB84BE84C65EA1E12E9F6650ADB59A824E608E80D1FC3AC19F418169B3879CC946165511D5AA280AE644AF360C42F7A3EEDF27E368E46480E3353E67F536E02B33505341BAF3941069567B723D7C125C8F066F9A6255436AAFDCAA8C554FDAFB0A9AAD91F1263DC62EF91A748FFB29F57E325D65A38ECB4F2851923DC6E9B7296064148A9BA2D938116266C597D9E1F11A46BE0EF526225BE750F0F3E5B0AEB7DC2140FA3A48B7238D0F5A872000782CB6F7751443EC6A1B7FA1ED02B9ABCD1C1DE4FC85E9B405C7851913C60F85582B1529276AD475AE52BD8115B6E73A53506E7A0244E1C29BCEF4CF20CFDF883392BB3990BE2A11B3213B68EC4A166C77D724CFAEBDC34C45ED09848A994BCE1FF6A9BB80C7F5CA8FD44D3FDF8DEC8BA6552C234EF8DC52382D52D2B01BB23404FC453725C7C9269A785FE09C712D4ADE7072B66295CA0C6405D9859E134FBBD3737F2956DD1D718A9F8242CE95BDB1E49F265EBF19976BC46E29F7DE0EE5C89A43AF2E107588A46E1B6762E6F8E48B8FC4F4FF93EC60938B8E5C3719022C750C4309FC62ADA4E9028",
"D240216C5C4A70742CAA03AE910E8859C92E5A90A352CB8B45847BAC7793E1F75720D44919E896AD4581E1FD83986FF235C9834BEECAA1556794BE49033E79D4CCDB4DC67C5200E8B6A3EE891E700B348CBF092E4D3FA5E648B620E34E491D7B628A1FE7E2C45586B6577E50788687F0858C10F78F371B25C712ED2760C3D605D4ED4F052E8B66FC308D3ADD4A9B86F00CE4257EED085EAE95FBB1E113FCB42CE12BB6076178A20903C55DA570EF8A25BA7AC8B7E134B8D4E35AB172CA33CC97294A5E7E579B9361B92B49B63BB1982740015DFEC16882989C917F50D5FDD9166FE1001F3282D3C54A28AC7FD773CCC0634AF7CDF225F94107C169D2F2BB757EEB55933CCE0FF116D7FFBA992F9A075A2439CCB369D5B5DE460CADC9F8C81D98E71651AEBFC2A918C551082D85F75675CDC8CCA1D3E486CFFB3B025D27C8D67C451FDFCF59C3BFA163EB791152390E9488C604B9B8116C329453A98F7A104527BC677411034CC49686108E569B7595E1DDC85918D90BBCB337855860D6E4718C0679DAB6982D23FCB6648E8561F44BCF9B052D8B58384523BC592C9B7F824B96AD1A39AEBD2232D6D34DC171E8FBF933900960F207B55597759D23E1E794507586114228A2FC100CC200D2B862DF3F26E6D1C9370373FE165C326D8C29FD2F0B3071AFD5215781BFB589F605263FF065B7A5CA3F6AA9DE3FD8BF5589BDE3526",
"8E7752C52805DD0A723D61F0BBE0122DF576A42B5AFDF9F196A766C9B3BFE296DC16A892FAECEEDD8256D2B1AE6BFE5437D4A2691803043B59862B30D68E4FF94A0700D735CFE967299724DA9D680200C898EED1C785E7B8CEB14F1DCDC73FC625F9678B407603587220C2FDFE0A47E82ADF36C26F942797D608BA6B38A3AD1A967315E1F2D665B27D51E350F075531A179DB2EED55547EA61761CD2B3962FCB347279117D1C7A7574B49FFE0991AF572A2B0C962A8A79800CFD524AAF9E6401C44569600F41F04422DB891D25B9F714713086BBFD0FB268E66A4FB10C0ABEEB31D0FBFBA20B0E4FFF404051596FC6F6C8093AD01807FA52041CD33007B205D15D47AF733966411A36F4C7B846D0BE049ADC21B89EA4CE0FBA414C005E66F36FACF3C43B474D47DAD78AC114D0171C031DFBE4A15FE1A22603CD79B6BB448B67A4DEDC97262F7B869C54F385F3682C744ED5AD6C0B6E16793920E6B45A024010896D5FECFA111CC9F0C34E728B32F2C4D45B8AA69B621AB9AC3D9D79B38BF205E8D0D19FAC44A76B9F5644526E06858F76B3EE2D74AEB1971D6B6E68B83773399AC32203164564B102B26C370A9FEC673C285AE0D1D3DF239D48B6492B89846EBED4618AEC940DC62AF4C3FF0D56FC9FBE23EE3B0A4890BA2665A88E9F40C4B6A770F9630234ED10A3A7FF3C5BCCBA836F3EDC8B821AB18D4B1D51D9962C328",
"E682E9D8E92A7837823C9B7714D267F9CE290E9FA6CC0A8432D3F7507DAF6CF681246AA4C2323C6B53BCC6E53B31F49742EE5F4E6F79DC36727E98B06D0300ED21F0CF5F2B51D8304A51D0B498F4BFA39C0049B8117DAD334D4B2E37676EC42DFE0EED63B3726872CCF9A10223A8A4563BE8AC266E0697004921DCCEEA5DD80C62567FDEBF2AFDF030192831A6FD871F63D5DADA4B270AA9EC0ACE47E75BD19018CB809B548D4F2C24831C384DD2B807852F596BD4FE32CAB3A16899D0B100E9F96D06AACB8DA8D51DB0B0F600F3B614461F5238188B5EDA68EA753B6ACC58569E841BAF92CEE04E6E2626B1FBD01B9B67D1311B1C3D67427298E2D193F0647EA17D16FD7FD6A40A1BDBB320A1F5FC64B97759AF4EA92AAEB759B5DD30A726E9B8EAFA372FBD83CBFF0000CA75F219A95D6A3CDE38B8DFA9281609A20EE39B73FEBDF6A155359476D073E7153BC918C1191C9BAAF0E0F161384DAD8AFC31A3FC1E9EAFA495E22D18C05194EB85298AB0F042E447DD627904B73E6E505712DF010531C88E695F6510C78B443C731D7FDCD62EB7C4015AB5D530BD09CE5229FA4DC5642AF176C39D60FE070DF635CC5435136C7BB9C4DC83B0D382B9BB636A6C2B3838542904D53B862585FE6EC8960A9A77783D17B2D90506F5D60998602AE5430E86025C8864883CECD7CE51B49CC2953A2A41D7EF8027F1A83815BBEF6F6B2",
"F6BD4204243CBA14DAA15A256FBCD138B5D875E28BCC0BA36855E648434CD04F49935C3D074DD5BA2EB82AB14E82C30991A1159E990D1D36DAF794853A23C499AB6B3DC02A89F014310372813643F786BF19D3FA8C463EE50D9FA87107E91C461AD2E5DF2FC99630D2005894CB7698123111FAFC0C5BC9D1E8E84FCCA5179A6C9AFE3E369222D66854F90D2668A57FDEE00C300AEA4E88F03F05C4D7695B206DE9F7E1D429E5E6B65DFE05D4C861F4E7844DDB9062C0B6DB46B27AD0368992F54A44829DD11A05AB97BA8AD854E428B87F20C4E5E4BB1FF3803809A81F2E4C109572006729A5E490E0AA40BA55F4391C9FB758EFA79B97E6D413BCB02D33A00DA6705BFBADED66CFC21291C494B7C3293810012ECC61415E609DD97AAFFDEB795DE36026B4602DD546A1AD937F1A6DEACD3393F5530C48A7974E2882CB327AE600C05A535BDE5D15AC524859582EEE2D62194B73E01643359E7B2625F3EB9FE7137514ED549A3196FFCBC8072B4F6C18CC67AAFA0ED6029A805EF0987E2F27A3260F849C68F3EF91DAA9E579AA16FDA698CC18AE8706E28C6D84CB3F593273D763C2969933D8EFA564E8C06C427809E6A5A6F76DE7C8B07FF4EDDF6CF2B7595066DFB15F5C6F3839DEE642FC86BC1F3AED7ED2E65B665198AA034817DBBBE0FE30E662B2161276CBD969FDA05AFD6D6A570C1E3CF7E324634441983F257E2BA",
"A9366308475F2D8D0C2D451C4A65A01EE58A0AF19B791D97382EC59A52616C7480B86EB1D0A83E93224B0DF73DE1D7EE6D51088F3B20B7937E6C0144E0DACA6324F0C8E5F9D93A8CBA1045E5B509D7DF98619FDDFDD7892C3082D69008D9D3ED6C9C1367D9DB7C04621D7CDD8A5A2599EE45B87A82F8CE8D60293E7A71D11700CA9AF117D630C5D8B876A9DCE519BD653114448C68B265813C608435B96CD642A420A15FBAB467692931BCA74F1F9D23F5BFDDC5B8651139B5A73F04FEF3DA64B7BD56E49235069EE5E8A136B921051F1D1C7D5993E6EEEEA2D58583152ADCD87AA89CF5962BC8341EF99CEB3682A2D0686602CE140ABC2FDF79A778A9D75AFFDBBA00C0BD6A8A8AFF9B5D1F30C8373572C81BD9594890102F46B5A393ED126C36AEF6A66E231A246FDFCBD3DED198ABC54CF357ABC67AC83680C048932D7C902AB7DB16952B3C95DF4E845B46A362FFE1A27CD1388483FFA41AA563933371C0180848F9E3C03AFC1F00D6ABA29A953327A4E3D9FAD4616C8546C9AF89FB4D08D4256923B736A8F68FEA5A097E0640C16E0F7F942E6A6F5CBA76BB00D81C606C7FED908789A63F01F9B5FC7B7BE434E85A0A44B2070BE71AB2BA0132D9D7B32E2D2FE229619F85643E75B4141D355386D1A09F45738455BC21607086C7BBCD4B73F87DD83E905BCE8FC6C5BF1824E904C4F5C26518B2FEBF8EB06B22437270C",
"92D87BF3F54B0445C05E508E80F9CBC0502F0897D717CA232004362F394A023BFBFE3322C1D331AFC6454FC756FB48768693FD5C46DDB40DCBF14C726C24ED67D8F3EB613BA80B0E39CF0747DF62D258613640D881E085C377DE1C3D149C8359407C2C6ABC0D2718A2D42439A8E7B38CD7DCED72AE750B2BE88D0069FBE94BD69A9A4B4AD42FEC5E651A31F86B90DC2FEBAA6FA6E5F6368B620C1750278DF393F7C5035D47897FC05FBC419A61330135F24365F13D653D77CA2930DBB05A3815FE83F75BB1BD8B2DE12A2FAADCD1ED62329C55B87FB32CC8F3B42D888981B4192480D1F57CEB0C55897BDA6B9C0ACE1E7E4595E30C7368306243208444FCF4574C47B07725B25EC2E28F4C50B744B3860B361DDDD22D949AA94EBA4F97606FCAD91394B6FC0E634BD15E099E697403B2AE84CDF5DBDF36D91FB82C0BC12B984FEE83CA9E97C194CADF8382CECAAF49EB3BD446F660F94C188C074CC312E186BEE0F6585535B050C226659A94B4C4974DA32CDFF30DBEB4DEA588C6F490F7432DA5FA2408BBC931EAF60EADD7B891A61C157147B8DDE7A45F909BD20D5B12009783DE410940245FE4E91ACCF72942E486AE773CD665912173EA29875A1722F8658C414CD08CBFDFE1DD356E167A9D7B20BF7441562EE816435A78BAE7E5A5EB4DA6AAAC36F594C93E2851D76B6A18B0B03B30CD38B97E38109C494C557643D58",
"BAA2716F115D72D2037841EF9138D19833C7C5FF40F058A960826E690315577710EFE64BB37691564B3B0B6C577DA603CC3ACDFE1785541AAD23904758A5A13BDB018E7169D479A1FAA031CA72FA6D6AE9613D6B2F82AB07500B49DF535F86A76350C140F9CD25295D6BC2F38C5D13C99540E2363862F06DDCC486D884999BCB840BCCAF2AB84F5906B9AA0F77D6432F6531558392641C52FEAF9D8ED86BF0158134129F34ECD0768BC02ED442254515A74999C6B8052A1FC797F5720738C69DD9B3FFABDDC8515CD279B246EA7C67754920038C5A4C8D301119CEB95FAB2765DE39DDA84180CEBAAFBF4976118A8373FF6BBFC7FEBC3CFEAB1DA69DD3DB9E428C594950FD51F4D98A393BAB96001461F2765834ED70C60BC56406CFCB3E784C59B91C19783E67CE6C86713C43DCDA9512B2E7173AFC2EF9A172C9CFDDD3000D7A981440AD994C39DAE6FC0B645BA0FD49ECAA19E572ED0FAC748EC837A7D6F28A8D004402F71CA209BB9403B21E29836C5FE897268DE0736E985F9631DFDD1AC59D5411E684BE082F41108E33D2B92B2D45ED70FA52EA2D6DE121EB9F9C886DA479464A9DFD9970A406491E334372D7B78936095A7459BFAFF0E9090C2C6B6D62624A79334F879A5C92C685B50F75F04BA664EC95893FF40D62EEB24DCDD288729D0C297DF5ABB83C77FC11D0EA3EF18E3BC7C2C065CAC51390C610B591D24",
"98CDFCBAD056240E180F347C00912F2D9ABEBCF5464D410BE6A50404B830F744D78F7D97180404FB3BCCC2288B7991810B2562C4D509200CE1F9C4DF6DCA4C600D9ED49C9C1456141C7B71513E728D41970ACDB6C15B4A4E327B9A87ADA73D1D46EB0A21F2F5481C3B42931C51B780FA526C29B98E6B9C714B20049F7A05252CBB84B8E36026DB2379C9632A0843436ECB72D15EA2950ACDE18DBDC6DFB01BF08F7E191EC885F11D1D8B7BC96E9836B395108F6854545082A694D5974CC36C8A658349186C1BA892DAA85D3F156BFBE94C73BCD815E7652C38E178AAF02014F0E6F23A4E7EF689EBF3ABDCCDD40E2DECED316F07E20716927C8F7B203D51D957EE6EAB062B99ACA0D28E0AB50B516CD92CBB9BA90333E73D58DE0B4B633D81EC93D15EBCCC813EE63D63BD18517F4FE85C37469574B8122FB91388123E1D5E805166FB7157494F8559F90A4FA3DE9E71DA6FA7CCC6086E638BDD4FD3E4487506ACCF84F1E1678D714B86FAAD57A6B76E085CFAC30DE469BE32E2D203C63B43F073DD24F4A1E039B941E7A97F8BB28B516217455268B6EFBB0E1745C23D6D12A8CD13E5D242F562F56FE92496342000A731BF3DB0A7D3110705DFD0D8DEFB85665B77347CEFC8629F3757304F6129DA9845F6509FE3D32DE9FA86EA4FA9BF86FF7CC8E726C0FA9F93F889C467642C5E944501BEF8ED59793AF8804A9951B4B88",
"906F6C5A1D3BD03A03802EEF5937E214E87B5E2F0182BA2C258F44B516EC66EACB705E06EA6DFDB56600B8463A421DB03A51460091D7FE889E6DAE32EC19190E7211F08D37846CEE7364B6ECC07C1740CE990141C4DC4CB0AC9F25CAFCA6BC9111102EABA250ADFD505201FFF638B31A77CCE7A1ECB273F9C8ED84EC2F403C1191596A53EAD823421EC47DC5E78F3BD1339532C97E4EAA024CCC906EBFB870C1467C3D845A178EB07C11BE8D57E4EDEA7ADEF162923E9521451B871DF6E357DCEEA7F62022106F647DD8A23074AC10AA632C56DC32B34A4A184FACC64E5D1E8FD69269660543EEA2FD584117A3EBCF6268352F0212ABCE7CD28A93C9AF76722FB5A71FF9E5AC4579A2BA32B91818CDCB62C77A6A8EB1F4C34132EB463812B329B6B22108AC36E71F38338AE3A52C632796E45189632B73FDC0BD37A457204757261B7CFC01E06BC767A57A5FA7CFE43794F65398A94B4EF09D6DC2A8691BD0CB018BBE7B66E0C37BAA4723247AF3424BDE22614A9A581A7982E8C2323178BD2D46E6912A2FB2D2531819A180689D7F2C9B5C5AFC2DCF1C7FAEB1927EB79A72EB1203BB0FF17DAAF27D66022195890BBDDA786CF1C36ABFD96BC36FA1D2A5A0CC3D7EEE1A1050CA840209903CB9FF429C7EE9DF9CBC2BAB84CF28FAEF5BB45AE9588970A28B6BD9ADF8DF134C1FAB0DE274B5C7452C4836A573A26A0B4C14B74",
"C6D5046A5000ECDB54C872F2DC494F2DEB88430007C9BE8EC39FFB148F00F7861D8277589AC839AAD30AF7D7A2E0F9EE8217A39C521311E9BD59A71BC6663A7738669D6D3BB28124A80ABDF905DFE2C9539CCF0C8FA39EF84E9633D63BE0C32F3B2AA9FCDC18AC38C3C00924E9D54977BDAE61410F997038BE066DA6C945D8258B7DD133EECBA836A7A6A2907C431C522619D466430E6ACF15030F7FBA4F3D6BB545CAD85678E81898D2DE358CFF3951C8184066B18930DDA867890871AF6F4133B492FC894DBE4AA5F1E44BD361C4560ABBCD3101B4AA4E065FD60308795DDAEBADBB604A3D58776006CD074389AF49A0EF09586410015C7DE4FEFEEBEA6262B23571B93BEE15CDA2BBA60B6CC72A7DC9C80C81C9A25FE3D149C7A8BB2F704BE11177F92E2CEF0BBD12C0766D691CCF093D456AFEA411A8FE5F1C1A44F31017760F0D0CC3B271FB15F56D9F51A594C34FDFF8F8ADC91584ED8D7E1B6DAB27B2BE1BBDC4486FB1C822F23704BB2EF4B521E02E42FDCABF69588B0B9D92AAA73116D26E8E9E48DE94F6267414AC845467597B4C1F2A9A8E1E82C0A1C05955022CDF87386098EAFC5BF1A040716A89BE53A36B143376927028A561BBC07AFAF42494DF5BC0D95170D853DCCCCB22FD36B7947712EB369077D02BF85B0A4F57757ED80B247E521AC640D1B1CE30F93DEBBE2389D364A8B7971A51AFA4F557A8E12",
"FDCF36E6C842D2ABCCE9D8783D0D7A7EB74992EACEEF6C618AC7DED4E457B1A708BE2C82B28A9563F4A088FF7DB146B16B47A900DF49A4F3FA8EDAAFCA09F408B025D04EB673E105E0F55959B7951CF0E999CDF68EA9B32333DBFE0516D272111CBBD9933CA8AD8AA6025E5F9A062D8305344CAFC3CA391BD8DEBDC58F7FDBC041B349900E397609C71E4EA3A9D8407C63E8A6BBEEAEFC92E9C939147920E48E35DAC6D123DA46E4F0838FD732E43FE4EEF6BD68D5AF0C9BA3A0CB28233743B291D4E10054F695DC10A847E661F39C4C133289B07ACA8B544EE3E2EC288CB18C40CD9A8E48A93378FD50E077EFBC21996424B539A397B3D2A6C7DE58112CF55E82E8FF10F75571A15DC248E6B77CBB91D8BF2D53E5C4E9A85C7EB8FB690F74BE029CE1B569EFACFC16872C5008820FC6A7D12AB43E08B4AFF57DE6B43B613DF8480ACA556E29D792C6C81CB1CB54A67245C571A04965267BA0F9CD3FA0950B9A5B393B4A230A41E455267CD396F42285F0E49C5AFA0B53EC7B60C1C317EDA3FAE4B1713A80D4EBAD32FC685C13649C4806D6FD887A24A4F7AE801405EF28F058B37112A680F9E9AD0456314E9F490393CA25775797E4CCF9184FF0C6A237AFFA8DE1B84C420A6183B1D49D6F2AC1E673E7FDE161A8159DCEB00D85F032EE76E3931C459CE935DFE4AD6C6110591EE58496B82A16630E82320B9510880BE4E72",
"94964FC9F66389FE3880283C4250E6E19F195DFEBD2104FC0959E084308BC9CFDC6E5ED1C4B48B4ECAEB4FDE5F215FBED85A6CD4D1C1466E68A4CF21AEF29F77933549A3A6FF7ACD8AB6E6C689F1E8DF0AD8AB289D5C33023DF90B21A26320CE8C1CEB2C099FC1DB58737665855DCD20D587E176483E33EF14C80AA4760F751EE5B28460811E5110FEC3D689AE2A6E91D0A3F1E22623E88571F4DAC895AA428D42634EC142E56D0D57CE68D7949BE13AF234229E546E9D66D5C58E510BF3EAC7B73309BE16DCE6E2280AA80247D9EDEDD20E06295C9876B412B786CF7E5F1073792158131AFA002FE7750A17015A9C2580646A9A0D2A3F0243AF1AB4FEFB3D028504553AF9C5C34D1A4A2FE3B8DD8BF8CEADA82AE63C319BD7981D97155AA2F105D724A8C09310D5C316877062152419A006ABF56AADED74DF0DF325D666C31DF51F194CFEB331E7DAF00410372999D2D05B023B2C3067E6CE4A472FED3B8BE1C15C24DFBF4956A5B670FFCF128E5A23039764BE39CBE55636B83674060B3CCF5EF9A7B7EAB0813ADEE82E271C422FB78A9820007753B1E62BF4CCC074F7796D5B2008FE6542DC0C77ECA3810120ABE9F90BE5934E8EAE365D02B3D2DF4EA4A827E033263B113EEE5823DD3912FB31E3C4B46B274D7115F34CDA793DB6AD2CD8BCAF4B13B832AB605BE42B2877EE2E66B411668EA29A7DBA5BD969B9F152638",
"9B8071D96E7D361B2462CA93748DE4D31746972DAE582AD4F70A188CB40C2E6E418288B6A713ED4B647013B3EC31C9EA6217DE55D016A1977A0B285224129CDC59A9E54F3E5094258F11C0C995F60785614E560764312CD86C6969B3274236EE602EFAE392C015E4C3972D6FA2A47AB48D5C5F6836AFA54F28CCC03BB4DAA0A1DC0DCA3FD3F2B15FB2ADD907D3BF7719D1D9A8284A47C30F32712A8CD440148B8DFDB851FFD25ECA2864150B832F8B5DC3A7C701371785A66285601E96D285FF88947804AA4D88665B3E15760CDE327FBD21393042BAF62FDCE6EC41955E877ECAC331D594ED40547AFED34D410714CE57FCB4F01C8826519ACB85F447306C86BD1BA8189E0621DD09451E8F341AE47E7FCF1FD2DE2AF78E0AFA27A4B6DD51A0710FC1FC4A5998234EDAA1D4CF0786B779F637EE1A72058774C1B4BF5E125DEBB4230645ECF87E3C6FDC91E1D14397FA72686784815D9654839AE8FA43864709EE0F4A336E3C399CA20B2E652E2AB17719F9253F772EB7A9E8838FED4EBCD0F8CD977583BDCEEBBD925676F56AAB0C36F3DD915F6691A30D60D523216FB233CBBEDE7FDFBA827450E595AB51237F9E77058E40F862D3A5A96E4AA3DA74503812EDEFA501E526DB6B4C642222D7F33B06D9CD0023471DF5730CF8E2BEE834D108A25729C1C1484C207ECEB0E4598965EAB5216D4E7C30577A89FB8BEC0B118F4",
"E43093167F77AAA20EF9F4AB7D715CD0E86542A0523BDF0256BCCFB1FA987A6565F808C03C71E6343EF41D2ED4C4FDBBB1AB1D617BBBDB776F07B000922FCBB3718F5FF326A7BBF4BDF148206411E86506FE5CEB8EC0F7AAD1DAE3BB46482F5E85D3B46DDF93F4BA19DF9DFD700C35DC038CF4E499A058995AF06BEE8535631966751A85571271E6877BE6216AFF16AA2B9046F1AB9126E1DCADBF40C4B390D23379D52C80C10CA05079D1F05CA294EAF7E2C66B884DDBB52DC85A45FFABB2BA6EB95869689486DDDE372DCCDFCB408496E647F33575BB99FF1699219D00D2608AEA1A47CF52DD21A3CEB3F043AAEB40EB27F042D276227C09CC8201304C55D254AC228FF309FE8DB74C85430FCF9441EF1B51A1192703B140CE3703E5848477FB0BBC2E82DC38702D4C6A78A41729B7EC86F9890EAD369DE4F25EAD61DB0B5B0ED764EC84FDAB904FAD9DBEDEBBE89F338DF52F7A6FAC8EA1D76001846802B328E8D1F110219BD6BB51214814026BFD6E32627B696C7990F53591CC811A9A09A1154A50585B882DE0B404D7B5846DBDC8C66E4AA0C7B90B08CC96A5CF2352A6592BCA536359C863D639CDA60FD23326619A9014FD878EBFF3E73E4424B045CBD506ED744D3243725EF9491D6165D332FB12985022A71C7DD4E3F942AF5CDF2396F6C346088EAE89535E1B21DA8F7B2E1E12DC8A37738310248808B9973041E",
"F5B9B9204186466AF61609CC856B6B2B33D6F526EF1E4BE4DCAA1E1055F033072B3801B642700249C024414B3217D160513E5A804B3AE8425852CF5FB9F51D2601F9BFF9F5E4512A45ED8F615A6671315590DEC31720387533A4E090646F17244EB571DBD6E6A10890FF19809D935A5E88228D424FF2E54B1A37576C059A756290BAAEA5E36AC977526339CDFF915A763B2CD0657F3A3D385A5D92466A44980AD8A1B4E1593DC177E2447ADB53B1EAE15A91BD6703E46F01AE91D6C316C1DB983688576B3B2BAB280FF196D5BF545755097249C517E1271202E3856475A3C95324F827961DD7A827709DA2924B21949B6BF032675D80FD120180F6850BFC385E72399F840B80E5525EE70B89E1C524D580390D487D0CD31AF3CA5691067BDBE8E2E91E0099F59700CDFCFDF8B3ACBEBCB61DB23A28E80C3E463D9E53328C37EF8FF0D4FCBD287932C8197DF57EEBC9DDB29E2046C7FC14970F9F37CED37D8BB0958155192C8595E2A9369944DAF58FCC1D9E1987DC4FA77ACB256A65DA78B733178FED14DA3E3E41DB808849132EB05E48BCA03ED499A314C857FF7C7C1FC8BD2478635396BD7F252ED984C102E23F55450F57ADC7EAC50A57B78BF4F988565A172E878638ADB1D992703D0BB742605EB76E3C85319F567D71B9F2E40251EEB936B323DE98496229B1F8A996AE4F6C0C2FBB3B2244F3BC57EB3FD067F535102",
"A7F21431EF8C75A1E9723B1FBB602EB4B92BE5FC8DE70183659A45A31EDDC46643ED1287A69DC876634231D3AF95F2D02FC9C0C7BBBB12319CBDBFDF6CBF94E47A05204AB14A490C6FBABCF329B5B0EFD8C2FA28845764510624398BFDED1B250003FBA61A67D2127550FF07A6FAE2CB5B8FEB23D289FA235BB4959F8845B25405A75A30E7D05D7BDEB721BFD4A998DAA6F1A54712901551E30D618DC80FADF7CB7863EADB7EEE708F1050AB0510A956C3C7706756B6CEAF611F02656C3CACBFD9E4507AC2B4B78D0EB0BF757496C84C7260CA972F0F45A393A89AC2BD70F15E629473D61FFA29BA18A468D8D4B1F72DDFF99608AC9E6A6D6A9231F24388991E80FBE6A9269AD6D31182E79235A0E33CFEE9E3526FC068A9253789407AF8CAECE14BC337FCE02333B63224A61E913367CB36641A74E2DB2892C4E939A50127DBEFED7456F7E61454FA5016A6B75E6133B0417C2860B2F640ED8EE62D5100EA172BBF8FB02E2022D29DF33C25654C96F8FB2305B72A92105E027444EE0F27769946A67E58D9A26AD01483FCA4BCDA4C00A8119E355CE733474349DC8311AE9853A064931FD68D598B02E0724A13FF63D88416FD5D03A962B5D8F79586E696C484D7F287773851831EBF33B0158F51C89C493C116157D20DE66F3990061135F5CBC46862BCFDA5EB0322793BD6AE188698A2E1DEDDF71702B6CB01EC2BDF85680",
"FB2C4002173D1E1581890C14672C1F232E8A1332655108F24F78E53638EFECA3DAD18D19FBB1E91C6AA4761017DB37FA76F70BBBC4C8E8015384DC9E9EF14C6AE60DE55974D448DB8C36B030DD55DF5EC6F6C56DA8CB9BBAACF6784628B669913A7B291632730C11710D7E181401FB535FE8A9A0B81578CE54123A6C3897496375CD7C60BB4E05AD16BDD4CB8F652448E3020EDF4D51FF5EBB9FA93A740AE779419002994348358CD7827A63BABCD7C4501CF8ACD77A2200059DFF5B180AFFBF3CE52133C7451CD108AF9CC8FA32428AE9ABC55D89E1931E6607160BF96015901DC097B56299F9F358DBBE921FBD7D0DB43C9C5BF97468813C0AC632D35C858D8384C32EBDEE933C9037EC36D2322F54FB0D3CF305801DE42B4EBCB5F83F3D3FCC1AF56910B7A547F50F851079B6D0D262676145C27DB362AF383037FFAD5E3B26BD2095143B361F82425734842E2153C5F428D89DBA45EF59BD9DF1FE1BA941F172A685F08C18B76B6A8FDC86EED2867A57AF48E00467993864EF70D80CE5550C5EDF085E94435ECA89F6513D23A56235739534C777C17BC6DC7BF6E04BDAA9B0C94C4D7AF1368C6B4B9D3C97D25A26B05703EBC4D1F8869C5DDA359D189F4A3E548D53731FB429F3536880CAD9FE13F70DB0A080B949C3636611CBFF0F644ABA8B56D2C6E988B3392A7452A8C8E52F2288560331A78F4CF021D2A41B2ECB6",
"CB7A566070A06E759017D62CF78A429913370E6498BCDC038C1D3079A0A3E6FD4FDCC851DCBEA9EB3A7D266479F75C40452A282CB406E9A2581372BE2FFBAF6E7F3359EAA594631CEF876FD62DEFBB3B816EC98A1F72B55F6023B572A51F320CD7ABF489AA905CE2274729C0EE0F677A04D7449A489BD02704CA65BCD8B753B689CB90C87C73FB4A45219DCD817E9AB584EFF049EA711CD3E24CE41423FEE6F258A65CA8EC3A00D45371EC5B846CF1549CB5CA4FF14B696140D3483DDC7801BFE6CFEFB9189B5C7AC3DB9C0709F630D80F361442BBD22636B00530EEF21D96D4A12CCF85E0124939B025CF35A0A5F1B4C2FA5B5DFCB3772CBFEC1F0C1C824B5CC22BA8FB2DC92164FCBC60333EBF920A14599F1E8EFD4E8CB0257134947331D820595BF5630EDF00814C32994039B47E8784C29D0D1C5B5D219A9F55FC14CC18DACBE6A79698A8073276A424BA9A6606DFF369CCF5F9F4AEC9605F6E44807507B36A4918FDAC448E3134106683EBA80A2D97E12EC2BC1EBBCBADBDC631CE7D61BB9DDF143C3EEA4E36EAAEAFC001849812A7CDE3EBC194A51EA2A9A2AA15A0A4F29C2FFD8B2CB4ADEE99B59553BB2398273C04329A5C884989D417C131D68E9B505594C9B9CF1C13AABF05093A9E4224D71B54C6F61F1255E25FF7527E47768FBD51523F42915133E02CD7E404147E7AAE95BE651BFBBD65E29130D0CA31C2A",
"D90A7D104DEF06F51CA5FE107C115C130B8823C423423608DEE00E0C2E6C202B60C5D20BCC9CDC18767D89E5E7857BABC9531C21B08436AC14C5B34EB986E5521369BC3D9B1FCD07C99240FD0DA306C69303B310B8F78D2BF97D6E9E88843234D73214431F6BAEFE5109AFDA22C4C348A469370BBF9B0A81CF27ED919A0A48DA3E9061113C56659F7561175A608B7FB37A76EB9E5ABA5F4A6B485EA5D097482A28F4EF7DE785D31DDEBFE41DFF7C51CA2B64A0D2A7A70269FDE399FF7628202289EA97141F995FF51DB5AF7C9C5CA76F0AFB4981D021FEA9EE6A49ECCCD18AF290B6C231265247DECE33B3E165FDE4FCF82EA84A4A8AE3FA90972D96DB68550AEF21FCE82E0FC75FCDE33C2B6F659A873AF09067D94B068DCEEDCA4D392BE5077587785CB6A1F1947841F483BE5080E85B49F1073D4778A39239266DB2A24B57B5F51B1F021D05C634D66DAD081D1668EF0520424581E5E0B62ED085D005F169950F971E1493A49A60A595342AACBA236EB16D5EF94E749889E50656D98F94EEF205445AC027387AE1363696185E24749EB67518B89057899548BABE29F3487D273C54EE2BE6254C7F3ED3D3B22FA3FC94791A7BC7C499A391179368D9B1F0DDC325F55022F5AB7B91142E2880BAC761D154C11F2C58F4A42E8B3DBCFD44777F08A74676DFB7039F24C9A4C4620726FE12849C9D3C15293A3DF7F4870DCCF56",
"9D238D97CE04FEC0D292181D145EA0EFA727DBB7BADDCC60357762D262DD7D7A3BB0189E05BE490018ABBB61B154F94A4FA81985E774019CCCE4B8ECE9858C0C2BDFFD7923B3354A2144ACCC5C9DA34E50299EBF68E9BA0A269760386E5531F8964F8024ACC99E7AC3F40BFA9D94ECA1B14A1B173A06867100C57883FE7333C079C5871A8A7603395A87387EFB92A77C5812EF548CBC3B9178E874E45DFA8796BA6FECB5D81687EDC0AD5F804FF36A9CDDC58A5F991A2A0BA999F86E663715CC79A439DEC44C394ECF1F14801D8F9F292A648D29D2E910032F224ECE51D0708E4ED6D3E65FC788A2C1D286750F4B6D768CBF1B6EDE8524C2A7457A82404A491DB5EC868E6B95B02167A0C4E785290156943FA091F28234C4CAAB586F8095895FD21582E0356BC15EC7CEE893E70902CE4D01217474E0226FFF30CB78597726C74B95732E44D03A626F1D1DBA7785F55859B71C2DAFCF71D99EB99FFEF4877518C579D2623FF6EB326B8D6C7674826984CCAF98CE41724183952EAD69F05F44750F3B857EA01475555FD25A828BE1DBEF5E8B1C1237779E9E69D35E7BA9ADA124CBE710490280B64B2DC5EA2C41D6644012A18B369E3CA2FD93A254C493506C54FA6CB4DFE24E0B5035230F2BDE6FF21101164F322B4B066ECBCA3BB4F3C476B41BD02D8948E555AF74EA13228F07A1BEDBF95FA7E16F2BDC5D178E74FE5A1A0",
"8DD8032BC8D741905EDAE449212146878D8FA15AB037E41C36D21B0064D65BEBB74A18F4370C80DB44EE93C2AB06DCC6C6FE7DEA83F270C9FB52CE00BDBF546348F0E6285A35A32A47512823DD6E22FB7949071D4B58B46241544AB97B0F6023C70AFCFF0C7541B4B9E1FC6B3C3C55500251665D964871B9D1247B3A9728AE02FF47368053B1DE928B6A8D01ED3BCB18A3180EFC9700871F480959137CA1480C3640547497ED7CDB4912ADE1385F9F35B6D27BE64E362BD71E0981BE952B00AA20DC57465DA89B87BBE07F27454FB24B3B86766F95A5A5B4EAECA2620C3BF87C0654E6561398F27F96178EB1062E42F6E6AE0A669FC6C7170F217C850E822B4E70C9033A375BBCF6D62D11B3DAF7DA976D4A9D955B924E7F0A19CE77A53EDD424ADFDEE8558F06887E82A936193AD5E508938DD3FBFE02DFEBD98C2272DEAAD8AD3B0FC86C3637A6CE694DD95E4FD55F433E151D35DD4C930177CA66322001110B9BD0B89A096C8C3A3431C100E39E3D2E65504A770F4EB2F19E9D8FCCAF15B8E58DFB52A0B88406A48A036193F3EA9F8ED2322FC69CEAEB9E2DD6AC627DCF4CB109EA05AB5DCEA5F92902F3E7BD457C240C958FCA7B17F2EDA1248961C9C827E99BE0A3D60B3E27E42B1FF696DD58E48C7B832D8FE95529B9C8F075E67294C1B1060EC6736DB73FEF7407B320D850B049D80F9F4D536361038C4BAEAD92F24",
"C10051089B11B578F56E6C24FAB973C5D1B4A2F937E4A402FFCC45267F4A3F4CF4EED3DFF530838A570000F7E523D1B97FF798A3C6E12F9704126A9B6FDF606031E4D8E1AE39A470FE929124FC95961B85A5A6E10A79CBA60F54F37D4DCCB13FA3B0A3A1729184C59036428532E3DDB3F7B8D373DC08842EBC2ABBFDEB5B73B2F9B7E83C81D5D0EB3037A32AC0E5B6CDCC7CD84B9289BE16106D1D019D47209AFD8DA78EFA0F9108377C187B170CD53F366B17899EB41903FA1627AA245C07EEA5D6DA78039B3C4C1D70DD28870A40EE3A048D8209C4088A0847D6859513E01E0F4172C383D64F0B307D721C01495070336A753A75E1CA433FA973BB131E558FACC298C0A6E934E9F717E19EBCEA11EDF3736D4EAF593A8E5AC8EDFFE4D2B17B613E024B41AAD19BC0A27A1E9BE9D25344D4350596B9B0A48AA014191511B26E61A9753D8A38C4E3BECAC93E67CAB5696888C06B9099E9AE856486436FDD1AF1C193057AD1DFBBB14AF772F7D9B00F37A6DC9E805A8DFD9D53A161885E5C2C2A6DED0C54D00A8E0704877DA2F1EB6F6D8EB93B5604A4E2545D454C3D4F0D606AC192E170D44E85E2ADC91E54F56906909024DEDB6F9050BF3D3928D9AE23D9C42576AE4DAD1336F47519EDE436C3B1829CD2C5A0AF63476C7928B32F5E51826D844A1DF765C5578F9C2E5F79024BFDA96FB3199A3C7B47D3755FAF376761B4E",
"9C6674915677A62A7AFD4FCB27B45F8E6DDDD08980B7DBE1131E6E2425C5ADBEE8474E93A8A2D4E495B195A6B83F2CF6472A36E690928362B9A1FA994A9FEB7BCBA7FDCB771F59FB434ED289CCEABDAFAE29113389EB98D6F17E5508D5976E11BC8A1E93AF9F7B1C81686265930B4D334568E3F29E1C2F58A62572A610016C1C1C1C9E1D0EB3FEB2B3A210C59EB3980C44BC656FA7C5E05A4472D4255B40B8A1604FE39D8B5026A976310648D5C84CEBC87A8BF6545DC843A3A0B64DC4CCAF2D2203122DDD75BA42E096844899A35A899FDFD72C26E3392EA03351DC78BB9F62F51D913F8008DB00969C64003773FB2014FAF97E794A45792495BD52D7BE7ACA47FF2BF570CF88303377092B5B6BFF3B01D38A53E8A68B0D81FC2D1D375EB27C7AEEDD70679E8DDBA6DE656442ED951478FB96A979B4A9091F344EF39AF23DAA886C6FBAA8611C61686332C630690109E2869D18EE7A2C21B22921B9E3DE40BF063E370FF64E7AFE160B7EBFC4AF6AEDA043042552F5F36C2CABD339FE1442242EAD931D1B83968D1A31A7E32A0838401DBB9C1034D56ADCAF5942462EF63440FD70F91520137A50372D0D125A6285F7D715FD9225D03A109E1FC5EB547303CD7708F88FEED2814607171930436B249924714E8D8E024C24B3C0C9E40127DECE1AD966C3F9DF01793864615F291B73F73D27B624ACBEAD3D371B8D4FDA823C0",
"A1B9E8AB6C44A3610768CC17E3B899CEDDB44B746319EC50BA7E006EA0F3C09E9D67EF8A20154B2F93EC265B800503BC72831800CE0C9FB6CCADC8327A840F69DDF29BE212BEFE4AB1810026FF786D9D3A88B29745EB61131FB47B385F83CA211B4A2BC0449F7ECE43B2DDAC94C10955FF2E078AF573FFA7349907571AF501FA6A2FCF24E4B5676F8213475351C0398748FBD60A5EE8A1128C0998E57D076A201453EA7C70B1F171E7D084F444311C9829CAD03E0C2330D7E8D39DB6B7901C30787FF123CFBAD4E974A5F6412FB1BC927FD65CAD1F3AA4ED52E2D84AF6257EF311222467DE179E89438524116D8E0A4A94C19A4D66D0AE0FC535D69C4953C2E43136AC07D6A5774D59DEF1471B4097146C2124DF83D36678FBD7BEA6E258D826645DD88AC9A7EA5E05FB49F2CB29EA9081323FFB2A00F3915D1F36675BA1C9AAA3B166D9FB2529150D3573E502668E33DFA5BE95AA6C2F1F106D69218839BA590406B4FE4A03C4B16E29E6BC31335234BCA55D34955880B702F08203198EB7BAE381B231FAFE513F24C8BA9E58798F35465C2679DD5BA8B16EEDAFB04E5A807B9BE6EE4B3AD77609D2B9E0985BB23397BBDE08F4D0B21494A3129857E37D13862C1FF77A57AC663C206A92F8358E6C05FDE1A9D68131BC04B76F6865E8DF5C48F9424CD9AB6FEFB3B49BA8D32C923CDB8602C5367AA9ECAE48AEE6A276CF25C",
"ED1196222AA470CB4FC8A0B7D2C785EB45D232E06453B77DB76F60E5F7BAA0FAE80467EC8C656FC27FFDE8BFAADD2368B41AFF69460495923942ACF85C09EF01BCED0076960E5CDB1D36061933C1B6037548D27932CDF969FD5A910D5564F3A6BAF896A2A69F40EDA76E813AA261530F686042235A39A76081F58D952662894E6E648BA72098D840235EC4A2B963F8E74B52F2D0088DACA7EDF48115618DBBABC698C04AAAC0632475BEE65BB26A4EAA08F0209B75EB259B826724F580209F8B991808411F823819CC5F42A9FC1F0CEDBDC54C046EB1D1658FFF7252908B09F8C1D82E5F6D605F0B17B25506FAF91D4B3D4EBCB7A705E5BCB2A6FB409ABE4B1B0B47630F1AB0B40A3455181678573F82A7C139F072659D80671961D614D9F7DA72914BD5D6F417EBC152595972FDFAE876176923CF1F2F745A6FC27E134CEBC2D9AD5CC733F619C1B296026B7C4717D2086D91765D83AB76605050B810C542819BBB7CD87EA2305AB4A567B2C500D9913B7C6046F748DEECFE3C3AF829EF97E03799EA50F7C9EB38990F0D466498C091C3ABAA18453B173F0AA3A486DFAFEEE68E7998EA30E5C1349F2A54AE753963CEF58332A114F964B83B4E446A44147B11FFC60DA25B5FA705F34725B1388B17E99018236BD646DC714F8B7CD3BF2648063B2D3FB713AB94D82ED81D571D5BB4D3E5F2F067E2F2C90E8B6857E6A651B8E",
"EBE597534C0A6FEE9CE6AD10A80D2DFD160D7DE7A20154F62AFD7C3187D51D09DA39A958432CDCCBD83FCE19131E65A30DE99DB796D93D0944223D960BA233798323D368DDBD407D51B80350976951AE0E50CF409279301939624C741D20D5CDBF4BC3ABC8723E267BE75707024F6398CC6D32A7BCD0989555E0EE49C1A2354D1E961CA9910A5169ADFA71D56745181E0CF13469D8F3C6020637B8118ED8421A680FD4D515DF6B31C39ACFA36B601027AFBC861493D34ACEACFB501DF9A311B7EB9D38D7107E6B5ECBE74FD35CC8BC8319DF9F1948653C94C868DC24AB8D7E650D8F70A3D1D878558AC9BFA50D425604037F3DA323192654F1296C8320AF934AF60DACAD7F9630EA4ED0A635678213F6D09D73A2544C779596990DA42079C23581E22F541E6A3B6D3FA383597A8BDD7CA42930816D74FE8B12F92CBB9303CA5FA12BF5FE954FA7B53E0D88B79BE263B4B55B42A7672F5C4F7E6E2DB4C02AFD6034B7371E01215CED3F73AB8E3419D7447781AE5DEB4F4547C5549E9B0AD59940C162790650344E5ACAB33A187A58E29C9032963F088C044ABE73A855FF1FC93AB8DC258E419777311B7997EA95D32CF1F0140CAB9E6707E73E4FE575E0752816122C39B8D12F34ED6771EAEE192D830797DBE97A0BFD00E39CA382D44F080BB4F8FF6FAAFF2A82744C6E14D3036E00EC2ED5C9D184ECCA300030D7BD77B27DA",
"A56990B45E9AA5F9523D563D53E854A47629A4183E85CA1995BB374EE5601BDBCE5C7407999D28FF0F1B3EC7A463D3C941C57B7EB71E6262DA3A7136627814EA6D28C8D16E91680B0D961AAFACE2CA4099F986CD45A2AB387E39497B13678447480514B4008F0FA3B95AD7D2B4751C680F0C7D3651F9621E1355CEC76F10468ED33DE4C7D7D68B786B7F0006C604BCF893739FFE6AE56AD5AAD07D5BE76DE1CD581B10EDFEC9DF65AF925C4C1289837BE1854A62FCCEECCC4E2AB47B4864C467F5E4C276047501EABE444E051DEA933863E0FCB2356CC0C3817BA59B2E0AD78BBFEF4C3E4C35343331CD81C7F4C428B0418DFA4D6FF655BC50AF49C9A5E8A67BC265A3F30800B93E42C0E66F117E6C59C85BA5E273234EC7212C7B7AC3D87FC0E0EC5FBAE6654461F24BBDF9A063FD0E3F98741C5622ACC8BC008EB0E3F3D57680F12E52FB94CD957EFDD49BF512D9131757161A73DD9650E3561FB31509A908D3EA8DBD1C3AA95AC32E6960E0D17A218A25784332A88E85303DEBE117BEFEDF46882995AEBD704AEAE032668AEC6B8E5BD28D3F110C9F8C1DCA8FACFDC1188A073037796555DC4FAA26ACB9F51B545D32C224424BAC3DAB20D3C08D784E9FEDB47547C25671C269A62163742B55AB4CF9406D2C5047F92C76356F0B90221E019B31890C0B8FCFD04E6B5D06114B967D1358797238D5B14D824C8F0BD7B8BD0",
"D85AAA53F2DE2946EB0CA09DEBB6CB61D91D27C8907B90C89E20C01F681D33BF0DC70B6C79F19E4DB64793505A0E055C33D1E07B7F5AE09F7EFF0C8CEBE80C84373804A48A945CA406D3A1B17CA787DA265C1D8FC735CB098C1AB37A0452F1A287B8E19E3A57E59EF8F2E6CCE2F0AA7772955977982B1F6880AE8717520753636591BA36E351310E98ABB77E26E2CA5BF267935FC38DE173CA20C3A964B56924A5E82A9E36D005EDE6680D543C021C7A9F53DB69BFA3201880B7797E90D31011AFA17BAE836505F5B337C23A07D6AF167258C314C3001291DD9FC565F6B7FB74D99C1B42B496422B3D3E1A564769C2306E40264641EC14724301795B4D54F5FF8D6C4AB3645B56C1D0FFE9B977FC5017E2A53392E7458BAA308C343AAFF5B46B808BDA1FE5C284299A9EEEFC755132F9D3B475E2FCD6BA84917C601B5E8340B5A68148A33563E40AC5A1E49BD8D5FA77BEB6B99E6F4F68A10ECA112475531E7C1942AB9504B8F76EA7BCCAE4A19EF859FD6B6753F07BDD9A19421A2A0B8F27643B2311FAD7978F4366C41BD9F609486FC6EC7CE833A00D7A48AD816412BD201F40C93D136ABB8E329DDB816D1F59A1F3124D6314C64C9B640E4D5BB201CDB4490A65C73228E78055AD6632D81709AA443EBF29A4607A504DD5CBC0D271FE595677B8E6A5E29E23D171D50B878D0D3F9416657E3997D077677B87399A6D1936E",
"A0E629454DE594F7693888C9ADB907F83EC6818D6FC0019B70077BF1B0E3C2CB16A263BE145EA8129FED6BD98F3D9120BAD28379EC8E36DEC69C59B37BD6C2B65D172C70C69C23E29A6143121AEB9E6A9AB46C3DBC3979BC876F4357F6BAA5A0CB33C3AFF1654FE547D5D760E8CE095A9B324E15CEC1596BB7E0D56433D8908EA69925A5CCDCF01702811AD1103F237D36C5F12A3CD938A61EF76A3387340499E4EF1855F892F57AC39AB419F1A397CD43FE7F4C5E609ADF3A2A8E401FFAC870985826CA0DE211996A3549A203B798A5162E98FFC8B5CC2CFA68D618AE80D399D8DFE0FE4AA81ED9B586EA89F646D5C0AA8B107506C7F86879419D0F717E8F70C5A9DC2C3F122CB52F3B691087EF5971C27086512F1890494ABA38B1619969F8846B8876B95C56142F5B171502FA44ACD0299E82FC94EBCD4CDDBE1F5DF978871128F79C1758BB512D5C3BDE59A89786CF4E4716F0515F45A1CA31B06B23E4883F99EE0488848EB0A60FA14AABBD41EC5421D0C162590FEFFEBB4DD0300221471DE8DC0C87DB3F1D54DB11744F8A7115DCA0595F6CD99B74A6FB10C0D14E430F8B40E3E7105C89B2C8F12B37A8879AD9BEAD49AFA1BD56569713CCDA3FB634C3011D51A8DCF84437251A7B19EB61FCE5DF6E2278784722BABE384BA5424646FA23F6D10CE23DC5E0D8D512E7C5C094B1627D6285B57EDF38A7DB1BD57B72044",
"CD7AAC98501F29507EA4E0183E8A40D2E5117E47BB5D18D01A3732DE4C821DFE86521CBEA7DB29BE1148BD544ECC681689BCD1B41EAF755310B7659342F8EE11CB41550CC30E566E192796B66C1A83C0B28BACCFA6C393043A0A2CB89712BC1CCB174DE58E66896AF39C1CEED1E05B0435F8CF6FD920D100F51584FE24879987399481DBF27DDB6286B6353919E552E669290CE02AB4CD5113D7F484229F379C7332767EC69E4336439B05DE1C1E3563DD303A4F580BFF20A40E49CB0822F715ED0221EBCDB5DBAD751124B1715E82F37488265135B6C8BBCF4F801ECC4D3525FF189493AD4EFF0C042B070C4CA8FB1FDF43D79F06A6E4E3D35D7B07D4B728D5DC54EEDACBBBA1EDDCDC07ADF7DFCFEF835E44DF1FF66DAF2A7BAEBE218AC3B15E183044D6A8A89B3C101B40BED97ED5DF93BBC1B84931D56B8C822A6D058AC74CFA4C85D8B456698E82D5B7574C17B041E5F4BEED09F75012355CBC322B822C63F10C18A8F279E9A0E18E1FEF183D23E13894E31F6D046956FE8A647558228F6D4D6910151EC03937876B6ED7A078D33DAEB3F2239353BB8181E62B286BBC41588DE10F478A5CE5B508F205A41820356767B0A0ED4B8DB9EFE348362E9A90D6C30218B295B338B51C09239D02FC8A1E7DAAAB60AC37F5E67CFC88EEF69567B5C81A03B449F4ED38B9D295A36AA3503173F6F6F66D93CE72D753076040FACDE",
"ADDCEDB50E907D20E826E6E8A0D30C20C74B2DF204EA784BAE9F618CAE33A3C937729DF9CB10BA2A4C33E0182A37200C0CC509729D828B8A2A20F283AC4F9306596684EA3FB5492A4C9F2DB459E7531C9F9C0950E7D9E93B3EE5912AE7E39AC8F4EC14B18F24E325003F477E347C5AC1B67CDB11AF3BBBBCD0AC3703024B5767AA67A208254F798684BFD1D3EACD757EEC77254950A146620400DB95E694574F739A991EBA771EBBDFF1056BB39A77DBE0636A032E17141332F951C57C6C90F348F165E3ABDD60D429D5D6BEC7E3E3463806F819EB2D212B3528A5EDE51F235AD100A35E890955F8A1DC51FDCB53EABCA2540997DD054C1F5B29462995B876B44D085904E55E1B838BEF600A992EB49CE078DF75AF3D0F137685AC0D07F0BE1EB87B63A41E74DDE869C8A683BDE60AF5D77FF18F7137495BCEFD0ED28F62F9C3E25D332B5F861D999FCDC0B4851A984A4DBB53401FD40351ADA4335C702BCC8D900C737507B990BDDBE91D201E3A0946DC968D43FD10D04B0B76667FF5B4291C2124B0124C6B710A6D1BCFAEB016B9DEEB0F7A4FE044CA4EA0CCD84B7682617C3A545071EC295B0663B3F577D562DE1D9DD80DE6A1EFD6D5991EB5246F1597B86D0E9A90CF6DB0EB2B8E7BAE9431E567F01AA98502C773742246467ABF911A91A51F6C1B9E0C3233DC1A37D17DB91A5F0F661B0EB5886964456C7818601BD0C"
};
#endif /* GALILEO_E1_H_ */

View File

@ -245,28 +245,56 @@ signed long int Gps_Navigation_Message::read_navigation_signed(std::bitset<GPS_S
{
signed long int value = 0;
// read the MSB and perform the sign extension
if (bits[GPS_SUBFRAME_BITS-slices[0].position]==1)
{
value^=0xFFFFFFFF;
}
else
{
value&=0;
}
// Bug correction: Discriminate between 64 bits and 32 bits compiler
int long_int_size_bytes = sizeof(signed long int);
if (long_int_size_bytes==8)
{
// read the MSB and perform the sign extension
if (bits[GPS_SUBFRAME_BITS-slices[0].position]==1)
{
value^=0xFFFFFFFFFFFFFFFF; //64 bits variable
}
else
{
value&=0;
}
for (int i=0; i<num_of_slices; i++)
{
for (int j=0; j<slices[i].length; j++)
{
value<<=1; //shift left
value&=0xFFFFFFFE; //reset the corresponding bit
if (bits[GPS_SUBFRAME_BITS - slices[i].position-j] == 1)
{
value+=1; // insert the bit
}
}
}
for (int i=0; i<num_of_slices; i++)
{
for (int j=0; j<slices[i].length; j++)
{
value<<=1; //shift left
value&=0xFFFFFFFFFFFFFFFE; //reset the corresponding bit (for the 64 bits variable)
if (bits[GPS_SUBFRAME_BITS - slices[i].position-j] == 1)
{
value+=1; // insert the bit
}
}
}
}else{
// read the MSB and perform the sign extension
if (bits[GPS_SUBFRAME_BITS-slices[0].position]==1)
{
value^=0xFFFFFFFF;
}
else
{
value&=0;
}
for (int i=0; i<num_of_slices; i++)
{
for (int j=0; j<slices[i].length; j++)
{
value<<=1; //shift left
value&=0xFFFFFFFE; //reset the corresponding bit
if (bits[GPS_SUBFRAME_BITS - slices[i].position-j] == 1)
{
value+=1; // insert the bit
}
}
}
}
return value;
}

View File

@ -1,17 +1,15 @@
project : build-dir ../../build ;
exe gnss-sdr : main.cc
#../algorithms/acquisition/adapters//gps_l1_ca_gps_sdr_acquisition
../algorithms/acquisition/adapters//gps_l1_ca_pcps_acquisition
#../algorithms/acquisition/adapters//gps_l1_ca_tong_pcps_acquisition
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_gps_sdr_acquisition_cc
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_gps_sdr_acquisition_ss
../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_pcps_acquisition_cc
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_tong_pcps_acquisition_cc
../algorithms/acquisition/adapters//galileo_e1_pcps_ambiguous_acquisition
../algorithms/acquisition/gnuradio_blocks//pcps_acquisition_cc
../algorithms/channel/adapters//channel
../algorithms/channel/libs//gps_l1_ca_channel_fsm
../algorithms/conditioner/adapters//signal_conditioner
../algorithms/input_filter/adapters//fir_filter
../algorithms/libs//gnss_signal_processing
../algorithms/libs//gps_sdr_signal_processing
../algorithms/libs//galileo_e1_signal_processing
../algorithms/libs//gnss_sdr_valve
../algorithms/libs//pass_through
../algorithms/observables/adapters//gps_l1_ca_observables

View File

@ -0,0 +1,262 @@
/*!
* \file galileo_e1_pcps_ambiguous_acquisition_test.cc
* \brief This class implements an acquisition test based on some input parameters.
* \author Luis Esteve, 2012. luis(at)epsilon-formacion.com
*
*
* -------------------------------------------------------------------------
*
* Copyright (C) 2010-2012 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* GNSS-SDR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* GNSS-SDR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNSS-SDR. If not, see <http://www.gnu.org/licenses/>.
*
* -------------------------------------------------------------------------
*/
#include <gtest/gtest.h>
#include <sys/time.h>
#include <iostream>
#include <gnuradio/gr_top_block.h>
#include <gnuradio/gr_file_source.h>
#include <gnuradio/gr_sig_source_c.h>
#include <gnuradio/gr_msg_queue.h>
#include <gnuradio/gr_null_sink.h>
#include "gnss_block_factory.h"
#include "gnss_block_interface.h"
#include "in_memory_configuration.h"
#include "gnss_sdr_valve.h"
#include "gnss_signal.h"
#include "gnss_synchro.h"
#include "galileo_e1_pcps_ambiguous_acquisition.h"
class GalileoE1PcpsAmbiguousAcquisitionTest: public ::testing::Test {
protected:
GalileoE1PcpsAmbiguousAcquisitionTest() {
queue = gr_make_msg_queue(0);
top_block = gr_make_top_block("Acquisition test");
factory = new GNSSBlockFactory();
config = new InMemoryConfiguration();
item_size = sizeof(gr_complex);
stop = false;
message = 0;
}
~GalileoE1PcpsAmbiguousAcquisitionTest() {
delete factory;
delete config;
}
void init();
void start_queue();
void wait_message();
void stop_queue();
gr_msg_queue_sptr queue;
gr_top_block_sptr top_block;
GNSSBlockFactory* factory;
InMemoryConfiguration* config;
Gnss_Synchro gnss_synchro;
size_t item_size;
concurrent_queue<int> channel_internal_queue;
bool stop;
int message;
boost::thread ch_thread;
};
void GalileoE1PcpsAmbiguousAcquisitionTest::init(){
config->set_property("GNSS-SDR.internal_fs_hz", "4000000");
config->set_property("Channel1.system", "Galileo");
config->set_property("Channel1.signal", "1B");
config->set_property("Channel1.satellite", "1");
config->set_property("Acquisition.item_type", "gr_complex");
config->set_property("Acquisition.if", "0");
config->set_property("Acquisition.sampled_ms", "4");
config->set_property("Acquisition.dump", "false");
config->set_property("Acquisition.implementation", "Galileo_E1_PCPS_Ambiguous_Acquisition");
config->set_property("Acquisition.threshold", "70");
config->set_property("Acquisition.doppler_max", "7000");
config->set_property("Acquisition.doppler_step", "125");
config->set_property("Acquisition.repeat_satellite", "false");
config->set_property("Acquisition1.cboc", "true");
}
void GalileoE1PcpsAmbiguousAcquisitionTest::start_queue()
{
ch_thread = boost::thread(&GalileoE1PcpsAmbiguousAcquisitionTest::wait_message, this);
}
void GalileoE1PcpsAmbiguousAcquisitionTest::wait_message()
{
while (!stop)
{
channel_internal_queue.wait_and_pop(message);
stop_queue();
}
}
void GalileoE1PcpsAmbiguousAcquisitionTest::stop_queue()
{
stop = true;
}
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, Instantiate)
{
init();
GalileoE1PcpsAmbiguousAcquisition *acquisition = new GalileoE1PcpsAmbiguousAcquisition(config, "Acquisition", 1, 1, queue);
delete acquisition;
}
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, ConnectAndRun)
{
int fs_in = 4000000;
int nsamples = 4*fs_in;
struct timeval tv;
long long int begin;
long long int end;
init();
GalileoE1PcpsAmbiguousAcquisition *acquisition = new GalileoE1PcpsAmbiguousAcquisition(config, "Acquisition", 1, 1, queue);
ASSERT_NO_THROW( {
acquisition->connect(top_block);
gr_sig_source_c_sptr source = gr_make_sig_source_c(fs_in,GR_SIN_WAVE, 1000, 1, gr_complex(0));
gr_block_sptr valve = gnss_sdr_make_valve(sizeof(gr_complex), nsamples, queue);
top_block->connect(source, 0, valve, 0);
top_block->connect(valve, 0, acquisition->get_left_block(), 0);
}) << "Failure connecting the blocks of acquisition test."<< std::endl;
EXPECT_NO_THROW( {
gettimeofday(&tv, NULL);
begin = tv.tv_sec *1000000 + tv.tv_usec;
top_block->run(); // Start threads and wait
gettimeofday(&tv, NULL);
end = tv.tv_sec *1000000 + tv.tv_usec;
}) << "Failure running he top_block."<< std::endl;
delete acquisition;
std::cout << "Processed " << nsamples << " samples in " << (end-begin) << " microseconds" << std::endl;
}
TEST_F(GalileoE1PcpsAmbiguousAcquisitionTest, ValidationOfResults)
{
struct timeval tv;
long long int begin;
long long int end;
double expected_delay_samples = 2920; //18250;
double expected_doppler_hz = 632;
init();
GalileoE1PcpsAmbiguousAcquisition *acquisition = new GalileoE1PcpsAmbiguousAcquisition(config, "Acquisition", 1, 1, queue);
std::string default_system = "Galileo";
std::string default_signal = "1C";
std::string gnss_system = config->property("Channel1.system", default_system);
std::string gnss_signal = config->property("Channel1.signal", default_signal);
unsigned int sat = config->property("Channel1.satellite", 0);
Gnss_Signal signal_value = Gnss_Signal(Gnss_Satellite(gnss_system, (unsigned int)sat), gnss_signal);
signal_value.get_signal().copy(gnss_synchro.Signal,2,0);
gnss_synchro.PRN = sat;
gnss_synchro.System = signal_value.get_satellite().get_system_short().c_str()[0];
gnss_synchro.Channel_ID=1;
ASSERT_NO_THROW( {
acquisition->set_channel(1);
}) << "Failure setting channel."<< std::endl;
ASSERT_NO_THROW( {
acquisition->set_gnss_synchro(&gnss_synchro);
}) << "Failure setting gnss_synchro."<< std::endl;
ASSERT_NO_THROW( {
acquisition->set_channel_queue(&channel_internal_queue);
}) << "Failure setting channel_internal_queue."<< std::endl;
ASSERT_NO_THROW( {
acquisition->set_threshold(config->property("Acquisition.threshold", 0.0));
}) << "Failure setting threshold."<< std::endl;
ASSERT_NO_THROW( {
acquisition->set_doppler_max(config->property("Acquisition.doppler_max",
10000));
}) << "Failure setting doppler_max."<< std::endl;
ASSERT_NO_THROW( {
acquisition->set_doppler_step(config->property("Acquisition.doppler_step",
500));
}) << "Failure setting doppler_step."<< std::endl;
ASSERT_NO_THROW( {
acquisition->connect(top_block);
}) << "Failure connecting acquisition to the top_block."<< std::endl;
ASSERT_NO_THROW( {
std::string file = "../src/tests/signal_samples/Galileo_E1_ID_1_Fs_4Msps_8ms.dat";
const char * file_name = file.c_str();
gr_file_source_sptr file_source = gr_make_file_source(sizeof(gr_complex),file_name,false);
top_block->connect(file_source, 0, acquisition->get_left_block(), 0);
}) << "Failure connecting the blocks of acquisition test."<< std::endl;
start_queue();
acquisition->init();
acquisition->reset();
EXPECT_NO_THROW( {
gettimeofday(&tv, NULL);
begin = tv.tv_sec *1000000 + tv.tv_usec;
top_block->run(); // Start threads and wait
gettimeofday(&tv, NULL);
end = tv.tv_sec *1000000 + tv.tv_usec;
}) << "Failure running he top_block."<< std::endl;
ch_thread.join();
unsigned long int nsamples = gnss_synchro.Acq_samplestamp_samples;
std::cout << "Acquired " << nsamples << " samples in " << (end-begin) << " microseconds" << std::endl;
EXPECT_EQ(1, message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
double delay_error_samples = abs(expected_delay_samples-gnss_synchro.Acq_delay_samples);
float delay_error_chips = (float)(delay_error_samples*1023/4000000);
double doppler_error_hz = abs(expected_doppler_hz-gnss_synchro.Acq_doppler_hz);
EXPECT_LE(doppler_error_hz, 166) << "Doppler error exceeds the expected value: 166 Hz = 2/(3*integration period)";
EXPECT_LT(delay_error_chips, 0.175) << "Delay error exceeds the expected value: 0.175 chips";
delete acquisition;
}

View File

@ -48,9 +48,9 @@
#include "gps_l1_ca_pcps_acquisition.h"
class Gps_L1_Ca_Pcps_Acquisition_Test: public ::testing::Test {
class GpsL1CaPcpsAcquisitionTest: public ::testing::Test {
protected:
Gps_L1_Ca_Pcps_Acquisition_Test() {
GpsL1CaPcpsAcquisitionTest() {
queue = gr_make_msg_queue(0);
top_block = gr_make_top_block("Acquisition test");
factory = new GNSSBlockFactory();
@ -60,7 +60,7 @@ protected:
message = 0;
}
~Gps_L1_Ca_Pcps_Acquisition_Test() {
~GpsL1CaPcpsAcquisitionTest() {
delete factory;
delete config;
}
@ -82,7 +82,7 @@ protected:
boost::thread ch_thread;
};
void Gps_L1_Ca_Pcps_Acquisition_Test::init(){
void GpsL1CaPcpsAcquisitionTest::init(){
gnss_synchro.Channel_ID=1;
gnss_synchro.System = 'G';
@ -103,13 +103,13 @@ void Gps_L1_Ca_Pcps_Acquisition_Test::init(){
}
void Gps_L1_Ca_Pcps_Acquisition_Test::start_queue()
void GpsL1CaPcpsAcquisitionTest::start_queue()
{
ch_thread = boost::thread(&Gps_L1_Ca_Pcps_Acquisition_Test::wait_message, this);
ch_thread = boost::thread(&GpsL1CaPcpsAcquisitionTest::wait_message, this);
}
void Gps_L1_Ca_Pcps_Acquisition_Test::wait_message()
void GpsL1CaPcpsAcquisitionTest::wait_message()
{
while (!stop)
{
@ -118,14 +118,14 @@ void Gps_L1_Ca_Pcps_Acquisition_Test::wait_message()
}
}
void Gps_L1_Ca_Pcps_Acquisition_Test::stop_queue()
void GpsL1CaPcpsAcquisitionTest::stop_queue()
{
stop = true;
}
TEST_F(Gps_L1_Ca_Pcps_Acquisition_Test, Instantiate)
TEST_F(GpsL1CaPcpsAcquisitionTest, Instantiate)
{
init();
@ -136,7 +136,7 @@ TEST_F(Gps_L1_Ca_Pcps_Acquisition_Test, Instantiate)
}
TEST_F(Gps_L1_Ca_Pcps_Acquisition_Test, ConnectAndRun)
TEST_F(GpsL1CaPcpsAcquisitionTest, ConnectAndRun)
{
int fs_in = 4000000;
int nsamples = 4000;
@ -170,7 +170,7 @@ TEST_F(Gps_L1_Ca_Pcps_Acquisition_Test, ConnectAndRun)
}
TEST_F(Gps_L1_Ca_Pcps_Acquisition_Test, ValidationOfResults)
TEST_F(GpsL1CaPcpsAcquisitionTest, ValidationOfResults)
{
struct timeval tv;
long long int begin;

View File

@ -1,18 +1,16 @@
project : build-dir ../../build ;
exe run_tests : test_main.cc
#../algorithms/acquisition/adapters//gps_l1_ca_gps_sdr_acquisition
../algorithms/acquisition/adapters//gps_l1_ca_pcps_acquisition
#../algorithms/acquisition/adapters//gps_l1_ca_tong_pcps_acquisition
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_gps_sdr_acquisition_cc
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_gps_sdr_acquisition_ss
../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_pcps_acquisition_cc
#../algorithms/acquisition/gnuradio_blocks//gps_l1_ca_tong_pcps_acquisition_cc
../algorithms/acquisition/adapters//galileo_e1_pcps_ambiguous_acquisition
../algorithms/acquisition/gnuradio_blocks//pcps_acquisition_cc
../algorithms/channel/adapters//channel
../algorithms/channel/libs//gps_l1_ca_channel_fsm
../algorithms/conditioner/adapters//signal_conditioner
../algorithms/input_filter/adapters//fir_filter
../algorithms/libs//gnss_signal_processing
../algorithms/libs//gps_sdr_signal_processing
../algorithms/libs//galileo_e1_signal_processing
../algorithms/libs//gnss_sdr_valve
../algorithms/libs//pass_through
../algorithms/observables/adapters//gps_l1_ca_observables

View File

@ -0,0 +1,26 @@
Matlab Signal Generator:
function [E1] = fcboc_E1_baseband(Fs,BB_BW,CN0_IN,Tsig,FI,Doppler,Delay,numsat,flag_Datos,flag_E1_B,flag_E1_C,flag_local,flag_second_code,flag_noise)
%Fs= Sampling frequency [Hz]
%BB_BW= BaseBand bandwidth [Hz]
%CN0_IN= Carrier-to-noise-density ratio [dB]
%Tsig= Total signal time to generate [s]
%FI= Intermediate Frequency, 0 means baseband [Hz]
%Doppler = Doppler frequency [Hz]
%Delay = Code delay [s]
%numsat= Satellite Vehicle number(1-32)
%flag_Datos = 1 -> Signal contains random telemetry bits 0 -> No telemetry
%flag_E1_B = 1 -> Activates Galileo E1B primary spreading code
%flag_E1_C = 1 -> Activates Galileo E1C primary spreading code
%flag_local = 1 -> Signal will be used as a local replica
%flag_second_code = 1 -> Activates Galileo E1C secondary spreading code
%flag_noise = 1 -> Signal contains noise 0 -> signal is noise-free
x = fcboc_E1_baseband(25e6,12.276e6,40,0.2,0,632,0.73e-3,1,1,1,1,0,1,0);
Fs: 4e06
CN0: 40 dbHz
Doppler: 632 Hz
Delay: 0.73e-3 s x 4e6 sps/s = 2920 sps

View File

@ -62,6 +62,7 @@
#include "gnss_block/file_signal_source_test.cc"
#include "gnss_block/fir_filter_test.cc"
#include "gnss_block/gps_l1_ca_pcps_acquisition_test.cc"
#include "gnss_block/galileo_e1_pcps_ambiguous_acquisition_test.cc"
#include "gnss_block/file_output_filter_test.cc"
#include "gnss_block/gnss_block_factory_test.cc"
//#include "gnss_block/direct_resampler_conditioner_test.cc"