Repository: Mictronics/multi-sdr-gps-sim Branch: master Commit: 56b87765d25c Files: 24 Total size: 321.5 KB Directory structure: gitextract__cfhgz44/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── almanac.c ├── almanac.h ├── circle.csv ├── fifo.c ├── fifo.h ├── gps-sim.c ├── gps-sim.h ├── gps.c ├── gps.h ├── gui.c ├── gui.h ├── help.h ├── sdr.c ├── sdr.h ├── sdr_hackrf.c ├── sdr_hackrf.h ├── sdr_iqfile.c ├── sdr_iqfile.h ├── sdr_pluto.c └── sdr_pluto.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Object files *.o *.ko *.obj *.elf # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.so *.so.* # Executables *.out *.hex gps-sim # Debug files *.dSYM/ # Output *.bin # Netbeans project folder nbproject/* # Rinex files *.gz # Almanac files *.sem *.sem.txt *.yuma *.alm ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2021 Mictronics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ HACKRFSDR ?= no PLUTOSDR ?= no DIALECT = -std=c11 CFLAGS += $(DIALECT) -Og -g -W -Wall -D_GNU_SOURCE LIBS = -lm -pthread -lpthread -lcurl -lz -lpanel -lncurses LDFLAGS = SDR_OBJ = sdr_iqfile.o ifeq ($(HACKRFSDR), yes) SDR_OBJ += sdr_hackrf.o CPPFLAGS += -DENABLE_HACKRFSDR CFLAGS += $(shell pkg-config --cflags libhackrf) LIBS_SDR += $(shell pkg-config --libs libhackrf) endif ifeq ($(PLUTOSDR), yes) SDR_OBJ += sdr_pluto.o CPPFLAGS += -DENABLE_PLUTOSDR CFLAGS += $(shell pkg-config --cflags libiio libad9361) LIBS_SDR += $(shell pkg-config --libs libiio libad9361) endif all: gps-sim %.o: %.c *.h $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ gps-sim: fifo.o almanac.o gps.o gui.o sdr.o gps-sim.o $(SDR_OBJ) $(COMPAT) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_SDR) clean: rm -f *.o gps-sim ================================================ FILE: README.md ================================================ # multi-sdr-gps-sim multi-sdr-gps-sim generates a GPS L1 baseband signal IQ data stream, which is then transmitted by a software-defined radio (SDR) platform. Supported at the moment are HackRF, ADLAM-Pluto and binary IQ file output. The software interacts with the user through a curses based text user interface (TUI) in terminal. Project based on work of [Takuji Ebinuma](https://github.com/osqzss/) and [IvanKor](https://github.com/IvanKor). ### Generating the GPS signal The user is able to assign a static location directly through the command line or sends motion data through a predefined input file. In addition the simulated position can be modified in realtime by user inputs. The user also specifies the GPS satellite constellation through a GPS broadcast ephemeris file. The daily GPS broadcast ephemeris file is a merge of the individual site navigation files into one. These files are then used to generate the simulated pseudorange and Doppler for the GPS satellites in view. This simulated range data is then used to generate the digitized I/Q samples for the GPS signal which are then feed into ADALM-Pluto SDR to transmit the GPS L1 frequency. The simulation start time can be specified if the corresponding set of ephemerides is available. Otherwise the first time of ephemeris in the RINEX navigation file is selected. RINEX input format 2 and 3 are supported. __Note__ The SDR internal oscillator must be precise enough (frequency offset) and stable (temperature drift) for GPS signal generation. Ideally the SDR shall be clocked from either by TCXO or more expensive (but highest precision) OCXO. Best experience with HackRF. ### Build instructions #### Dependencies Build depends on libm, libpthread, libcurl4-openssl-dev, libncurses, libz and libhackrf. You will also need the latest build and install of libhackrf, libad9361-dev and libiio-dev. The Debian packages libad9361-dev that is available up to Debian 9 (stretch) is outdated and missing a required function. So you have to build packages from source: The first step is to fetch the dependencies, which as of now is only libxml2. On a Debian-flavoured GNU/Linux distribution, like Ubuntu for instance: ``` $ sudo apt-get install libxml2 libxml2-dev bison flex libcdk5-dev cmake ``` Depending on the backend (how you want to attach the IIO device), you may need at least one of: ``` $ sudo apt-get install libaio-dev libusb-1.0-0-dev libserialport-dev libxml2-dev libavahi-client-dev doxygen graphviz ``` Then build in this order: ``` $ git clone https://github.com/analogdevicesinc/libiio.git $ cd libiio $ cmake ./ $ make $ sudo make install ``` ``` $ git clone https://github.com/analogdevicesinc/libad9361-iio.git $ cd libad9361-iio $ cmake ./ $ make $ sudo make install ``` #### Building under Linux with GCC ``` $ git clone https://github.com/mictronics/multi-sdr-gps-sim $ cd multi-sdr-gps-sim ``` With HackRF support: `make all HACKRFSDR=yes` (depends on libhackrf) With ADLAM-PLUTO support: `make all PLUTOSDR=yes` (depends on libiio and libad9361-iio) Full SDR support: `make all HACKRFSDR=yes PLUTOSDR=yes` ### Usage ```` gps-sim [options] Options: --nav-file -e RINEX navigation file for GPS ephemeris (required) --geo-loc -l Latitude, Longitude, Height (static mode) e.g. 35.681298,139.766247,10.0 --start -s Scenario start time YYYY/MM/DD,hh:mm:ss (use 'now' for actual time) --gain -g Set TX gain, HackRF: 0-47dB, Pluto: -80-0dB (default 0) --duration -d Duration in seconds --target -t Target distance [m], bearing [°] and height [m] --ppb -p Set oscillator error in ppb (default 0) --radio -r Set the SDR device type name (default none) --uri -U ADLAM-Pluto URI --network -N ADLAM-Pluto network IP or hostname (default pluto.local) --motion -m User motion file (dynamic mode) --iq16 Set IQ sample size to 16 bit (default 8 bit) --disable-iono -I Disable ionospheric delay for spacecraft scenario --verbose -v Show verbose output and details about simulated channels --interactive -i Use interactive mode --amplifier -a Enable TX amplifier (default OFF) --use-ftp -f Pull actual RINEX navigation file from FTP server --rinex3 -3 Use RINEX v3 navigation data format --disable-almanac Disable transmission of almanac information --help -? Give this help list --usage Give a short usage message --version -V Print program version SDR device types (use with --radio or -r option): none iqfile hackrf plutosdr ```` ### License Copyright © 2021 Mictronics Distributed under the [MIT License](http://www.opensource.org/licenses/mit-license.php). ================================================ FILE: almanac.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include "gps-sim.h" #include "almanac.h" static almanac_gps_t almanac_gps; struct sem_file { const char *filename; FILE *stream; }; /** * Initialize empty almanac. */ almanac_gps_t* almanac_init(void) { almanac_gps.valid = 0; almanac_prn_t* a = NULL; for (int prn = 0; prn < MAX_SAT; prn++) { a = &almanac_gps.sv[prn]; a->ura = 0; a->health = 0; a->config_code = 0; a->svid = 0; a->svn = 0; a->valid = 0; a->toa.sec = 0.0; a->toa.week = 0; a->e = 0.0; a->delta_i = 0.0; a->omegadot = 0.0; a->sqrta = 0.0; a->omega0 = 0.0; a->aop = 0.0; a->m0 = 0.0; a->af0 = 0.0; a->af1 = 0.0; } return &almanac_gps; } /** * Curl file writer callback. */ static size_t fwrite_sem(void *buffer, size_t size, size_t nmemb, void *stream) { struct sem_file *out = (struct sem_file *) stream; if (out && !out->stream) { /* open file for writing */ out->stream = fopen(out->filename, "wb"); if (!out->stream) return -1; /* failure, can't open file to write */ } return fwrite(buffer, size, nmemb, out->stream); } /** * Read almanac from local file. * sem format expected. */ CURLcode almanac_read_file(void) { char buf[100]; char title[24]; char *pbuf; unsigned int n, week, sec, id; FILE *fp = fopen("almanac.sem", "rt"); almanac_prn_t* a = NULL; // Start with empty almanac almanac_init(); if (!fp) { return CURLE_READ_ERROR; } pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL || sscanf(buf, "%u %24s", &n, title) != 2) goto error; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL || sscanf(buf, "%u %u", &week, &sec) != 2) goto error; n -= 1; // PRN in file counts 1-32, array counts 0-31 if (n > 31) n = 31; // Max 32 PRN's to read (0-31) for (unsigned int j = 0; j <= n; j++) { pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; // Check and skip blank line if (buf[0] == '\n' || buf[0] == '\r') { pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; } if (sscanf(buf, "%u", &id) != 1) goto error; if (id == 0) id = 1; if (id > 32) id = 32; a = &almanac_gps.sv[id - 1]; a->svid = (unsigned short) id; // SVN is optional, could be a blank line pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (buf[0] == '\n' || buf[0] == '\r') { a->svn = 0; } else { if (sscanf(buf, "%hu", &a->svn) != 1) goto error; } pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%hhu", &a->ura) != 1) goto error; if (a->ura > 15) a->ura = 15; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%lf %lf %lf", &a->e, &a->delta_i, &a->omegadot) != 3) goto error; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%lf %lf %lf", &a->sqrta, &a->omega0, &a->aop) != 3) goto error; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%lf %lf %lf", &a->m0, &a->af0, &a->af1) != 3) goto error; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%hhu", &a->health) != 1) goto error; if (a->health > 63) a->health = 63; pbuf = fgets(buf, sizeof (buf), fp); if (pbuf == NULL) goto error; if (sscanf(buf, "%hhu", &a->config_code) != 1) goto error; if (a->config_code > 15) a->config_code = 15; /** * ublox u-center software provides the full GPS week number whereas Celestrak follows * IC-GPS-200L, p. 116, 20.3.3.5.1.5 Almanac Reference Week: * The WNa term consists of eight bits which shall be a modulo 256 binary * representation of the GPS week number... * Celestrak almanac files use a modulo 256 week number in file name. * See https://celestrak.com/GPS/almanac/SEM/2021/ * * NOTE: It seems like sometime in the last four years this changed. * a->toa.week = (int) week % 256; results in incorrect date parsing. */ a->toa.week = (int) week; a->toa.sec = (double) sec; // GPS week rollover a->toa.week += 2048; a->valid = 1; almanac_gps.valid = 1; // We have at least one valid record } fclose(fp); return CURLE_OK; error: if (!feof(fp)) { // Not end of file, something wrong // Drop all parsed almanac data on error almanac_init(); } /** * If this is the end of file we may read less records than what * field "number of records" announced. * Found this happening when saving almanac.sem in ublox u-center software. */ fclose(fp); return CURLE_READ_ERROR; } /** * Read almanac from online source. * sem format expected. * */ CURLcode almanac_download(void) { CURL *curl; CURLcode res = CURLE_GOT_NOTHING; struct sem_file sem = { "almanac.sem", NULL }; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, ALMANAC_DOWNLOAD_SEM_URL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_sem); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sem); curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); res = curl_easy_perform(curl); curl_easy_cleanup(curl); } if (sem.stream) fclose(sem.stream); curl_global_cleanup(); if (res != CURLE_OK) { return res; } return (almanac_read_file()); } ================================================ FILE: almanac.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef ALMANAC_H #define ALMANAC_H #include #include "gps.h" #define ALMANAC_DOWNLOAD_SEM_URL "https://www.celestrak.com/GPS/almanac/SEM/almanac.sem.txt" typedef struct { unsigned char ura; // User Range Accuracy lookup code, [0-15], see p. 91 IS-GPS-200L, 0 <2.4m, 15 is >6144m unsigned char health; // 0=healthy, unhealthy otherwise [], subframe 4 and 5, page 25 six-bit health code unsigned char config_code; // configuration code [], if >=9 Anti-Spoofing is on unsigned short svid; // GPS SV id or prn number 1-32 [] unsigned short svn; // Satellite vehicle number [] unsigned int valid; // Validity of this almanac double e; // eccentricity [] double delta_i; // orbital inclination at reference time [rad] double omegadot; // rate of right ascension [rad/s] double sqrta; // square root of the semi-major axis [m^(1/2)] double omega0; // longitude of ascending node of orbit plane at weekly epoch [rad] double aop; // argument of perigee [rad] double m0; // mean anomaly at reference time [rad] double af0; // polynomial clock correction coefficient (clock bias) [s], Note: parameters from ephemeris preferred vs almanac (22 vs 11 bits) double af1; // polynomial clock correction coefficient (clock drift) [s/s], Note: parameters from ephemeris preferred vs almanac (16 vs 11 bits) gpstime_t toa; // almanac time of applicability (reference time) [s] } almanac_prn_t; typedef struct { unsigned int valid; // Almanac validity almanac_prn_t sv[MAX_SAT]; // 32 SV's almanac } almanac_gps_t; almanac_gps_t* almanac_init(void); CURLcode almanac_read_file(void); CURLcode almanac_download(void); #endif /* ALMANAC_H */ ================================================ FILE: circle.csv ================================================ 0.0,-3813477.954, 3554276.552, 3662785.237 0.1,-3813477.599, 3554276.226, 3662785.918 0.2,-3813477.240, 3554275.906, 3662786.598 0.3,-3813476.876, 3554275.590, 3662787.278 0.4,-3813476.508, 3554275.280, 3662787.958 0.5,-3813476.135, 3554274.975, 3662788.638 0.6,-3813475.757, 3554274.675, 3662789.318 0.7,-3813475.375, 3554274.381, 3662789.997 0.8,-3813474.988, 3554274.091, 3662790.676 0.9,-3813474.597, 3554273.807, 3662791.355 1.0,-3813474.201, 3554273.528, 3662792.033 1.1,-3813473.800, 3554273.255, 3662792.711 1.2,-3813473.396, 3554272.986, 3662793.388 1.3,-3813472.986, 3554272.723, 3662794.065 1.4,-3813472.573, 3554272.466, 3662794.741 1.5,-3813472.155, 3554272.213, 3662795.416 1.6,-3813471.733, 3554271.967, 3662796.091 1.7,-3813471.306, 3554271.725, 3662796.764 1.8,-3813470.875, 3554271.489, 3662797.438 1.9,-3813470.440, 3554271.258, 3662798.110 2.0,-3813470.001, 3554271.033, 3662798.781 2.1,-3813469.557, 3554270.814, 3662799.452 2.2,-3813469.110, 3554270.599, 3662800.121 2.3,-3813468.658, 3554270.391, 3662800.790 2.4,-3813468.202, 3554270.187, 3662801.457 2.5,-3813467.742, 3554269.990, 3662802.123 2.6,-3813467.278, 3554269.798, 3662802.788 2.7,-3813466.810, 3554269.611, 3662803.452 2.8,-3813466.338, 3554269.430, 3662804.114 2.9,-3813465.862, 3554269.254, 3662804.776 3.0,-3813465.383, 3554269.084, 3662805.436 3.1,-3813464.899, 3554268.920, 3662806.094 3.2,-3813464.412, 3554268.761, 3662806.751 3.3,-3813463.920, 3554268.608, 3662807.407 3.4,-3813463.425, 3554268.461, 3662808.061 3.5,-3813462.927, 3554268.319, 3662808.713 3.6,-3813462.424, 3554268.183, 3662809.364 3.7,-3813461.918, 3554268.053, 3662810.013 3.8,-3813461.409, 3554267.928, 3662810.660 3.9,-3813460.895, 3554267.809, 3662811.306 4.0,-3813460.379, 3554267.695, 3662811.950 4.1,-3813459.858, 3554267.587, 3662812.592 4.2,-3813459.335, 3554267.485, 3662813.232 4.3,-3813458.807, 3554267.389, 3662813.870 4.4,-3813458.277, 3554267.298, 3662814.506 4.5,-3813457.743, 3554267.213, 3662815.140 4.6,-3813457.205, 3554267.134, 3662815.772 4.7,-3813456.665, 3554267.061, 3662816.402 4.8,-3813456.121, 3554266.993, 3662817.030 4.9,-3813455.574, 3554266.931, 3662817.655 5.0,-3813455.023, 3554266.875, 3662818.278 5.1,-3813454.470, 3554266.825, 3662818.899 5.2,-3813453.913, 3554266.780, 3662819.518 5.3,-3813453.354, 3554266.741, 3662820.134 5.4,-3813452.791, 3554266.708, 3662820.748 5.5,-3813452.226, 3554266.680, 3662821.359 5.6,-3813451.657, 3554266.659, 3662821.968 5.7,-3813451.085, 3554266.643, 3662822.575 5.8,-3813450.511, 3554266.633, 3662823.178 5.9,-3813449.934, 3554266.628, 3662823.779 6.0,-3813449.354, 3554266.630, 3662824.378 6.1,-3813448.771, 3554266.637, 3662824.973 6.2,-3813448.186, 3554266.650, 3662825.566 6.3,-3813447.598, 3554266.669, 3662826.156 6.4,-3813447.007, 3554266.693, 3662826.744 6.5,-3813446.414, 3554266.723, 3662827.328 6.6,-3813445.818, 3554266.759, 3662827.910 6.7,-3813445.220, 3554266.801, 3662828.488 6.8,-3813444.619, 3554266.849, 3662829.064 6.9,-3813444.016, 3554266.902, 3662829.636 7.0,-3813443.410, 3554266.961, 3662830.206 7.1,-3813442.802, 3554267.026, 3662830.772 7.2,-3813442.192, 3554267.096, 3662831.335 7.3,-3813441.579, 3554267.173, 3662831.895 7.4,-3813440.965, 3554267.255, 3662832.451 7.5,-3813440.348, 3554267.342, 3662833.005 7.6,-3813439.729, 3554267.436, 3662833.555 7.7,-3813439.108, 3554267.535, 3662834.102 7.8,-3813438.485, 3554267.640, 3662834.645 7.9,-3813437.859, 3554267.750, 3662835.185 8.0,-3813437.232, 3554267.867, 3662835.721 8.1,-3813436.603, 3554267.988, 3662836.254 8.2,-3813435.973, 3554268.116, 3662836.783 8.3,-3813435.340, 3554268.249, 3662837.309 8.4,-3813434.706, 3554268.388, 3662837.831 8.5,-3813434.069, 3554268.533, 3662838.350 8.6,-3813433.432, 3554268.683, 3662838.865 8.7,-3813432.792, 3554268.839, 3662839.376 8.8,-3813432.151, 3554269.000, 3662839.883 8.9,-3813431.508, 3554269.167, 3662840.387 9.0,-3813430.864, 3554269.340, 3662840.886 9.1,-3813430.219, 3554269.518, 3662841.382 9.2,-3813429.572, 3554269.702, 3662841.874 9.3,-3813428.923, 3554269.891, 3662842.362 9.4,-3813428.274, 3554270.086, 3662842.846 9.5,-3813427.623, 3554270.287, 3662843.326 9.6,-3813426.970, 3554270.493, 3662843.802 9.7,-3813426.317, 3554270.704, 3662844.274 9.8,-3813425.662, 3554270.921, 3662844.742 9.9,-3813425.007, 3554271.143, 3662845.206 10.0,-3813424.350, 3554271.371, 3662845.665 10.1,-3813423.692, 3554271.605, 3662846.121 10.2,-3813423.033, 3554271.843, 3662846.572 10.3,-3813422.374, 3554272.087, 3662847.019 10.4,-3813421.713, 3554272.337, 3662847.461 10.5,-3813421.052, 3554272.592, 3662847.900 10.6,-3813420.390, 3554272.852, 3662848.334 10.7,-3813419.727, 3554273.118, 3662848.763 10.8,-3813419.063, 3554273.389, 3662849.188 10.9,-3813418.399, 3554273.665, 3662849.609 11.0,-3813417.734, 3554273.946, 3662850.025 11.1,-3813417.069, 3554274.233, 3662850.437 11.2,-3813416.403, 3554274.525, 3662850.844 11.3,-3813415.737, 3554274.822, 3662851.247 11.4,-3813415.070, 3554275.125, 3662851.645 11.5,-3813414.403, 3554275.432, 3662852.038 11.6,-3813413.735, 3554275.745, 3662852.427 11.7,-3813413.068, 3554276.063, 3662852.811 11.8,-3813412.400, 3554276.386, 3662853.190 11.9,-3813411.731, 3554276.714, 3662853.565 12.0,-3813411.063, 3554277.047, 3662853.935 12.1,-3813410.395, 3554277.385, 3662854.300 12.2,-3813409.726, 3554277.728, 3662854.661 12.3,-3813409.058, 3554278.077, 3662855.016 12.4,-3813408.390, 3554278.430, 3662855.367 12.5,-3813407.721, 3554278.788, 3662855.713 12.6,-3813407.053, 3554279.151, 3662856.054 12.7,-3813406.385, 3554279.519, 3662856.390 12.8,-3813405.718, 3554279.892, 3662856.721 12.9,-3813405.050, 3554280.269, 3662857.047 13.0,-3813404.383, 3554280.652, 3662857.368 13.1,-3813403.717, 3554281.039, 3662857.684 13.2,-3813403.050, 3554281.431, 3662857.996 13.3,-3813402.384, 3554281.828, 3662858.302 13.4,-3813401.719, 3554282.230, 3662858.603 13.5,-3813401.054, 3554282.636, 3662858.899 13.6,-3813400.390, 3554283.047, 3662859.189 13.7,-3813399.727, 3554283.462, 3662859.475 13.8,-3813399.064, 3554283.882, 3662859.755 13.9,-3813398.402, 3554284.307, 3662860.031 14.0,-3813397.741, 3554284.736, 3662860.301 14.1,-3813397.080, 3554285.170, 3662860.566 14.2,-3813396.421, 3554285.608, 3662860.826 14.3,-3813395.762, 3554286.051, 3662861.080 14.4,-3813395.105, 3554286.498, 3662861.329 14.5,-3813394.448, 3554286.949, 3662861.573 14.6,-3813393.792, 3554287.405, 3662861.812 14.7,-3813393.138, 3554287.865, 3662862.045 14.8,-3813392.485, 3554288.330, 3662862.273 14.9,-3813391.833, 3554288.798, 3662862.495 15.0,-3813391.182, 3554289.271, 3662862.713 15.1,-3813390.532, 3554289.749, 3662862.924 15.2,-3813389.884, 3554290.230, 3662863.131 15.3,-3813389.237, 3554290.715, 3662863.332 15.4,-3813388.592, 3554291.205, 3662863.527 15.5,-3813387.948, 3554291.698, 3662863.718 15.6,-3813387.305, 3554292.196, 3662863.902 15.7,-3813386.664, 3554292.698, 3662864.082 15.8,-3813386.025, 3554293.203, 3662864.255 15.9,-3813385.388, 3554293.713, 3662864.424 16.0,-3813384.752, 3554294.226, 3662864.586 16.1,-3813384.118, 3554294.743, 3662864.744 16.2,-3813383.485, 3554295.264, 3662864.895 16.3,-3813382.855, 3554295.789, 3662865.042 16.4,-3813382.226, 3554296.318, 3662865.182 16.5,-3813381.599, 3554296.850, 3662865.318 16.6,-3813380.974, 3554297.386, 3662865.447 16.7,-3813380.351, 3554297.925, 3662865.571 16.8,-3813379.731, 3554298.469, 3662865.690 16.9,-3813379.112, 3554299.015, 3662865.802 17.0,-3813378.495, 3554299.566, 3662865.910 17.1,-3813377.881, 3554300.119, 3662866.011 17.2,-3813377.269, 3554300.676, 3662866.107 17.3,-3813376.659, 3554301.237, 3662866.198 17.4,-3813376.051, 3554301.801, 3662866.283 17.5,-3813375.446, 3554302.368, 3662866.362 17.6,-3813374.843, 3554302.939, 3662866.435 17.7,-3813374.243, 3554303.513, 3662866.503 17.8,-3813373.644, 3554304.090, 3662866.565 17.9,-3813373.049, 3554304.670, 3662866.622 18.0,-3813372.456, 3554305.253, 3662866.673 18.1,-3813371.866, 3554305.840, 3662866.718 18.2,-3813371.278, 3554306.429, 3662866.758 18.3,-3813370.693, 3554307.021, 3662866.792 18.4,-3813370.111, 3554307.617, 3662866.820 18.5,-3813369.531, 3554308.215, 3662866.843 18.6,-3813368.954, 3554308.816, 3662866.860 18.7,-3813368.380, 3554309.420, 3662866.871 18.8,-3813367.809, 3554310.027, 3662866.877 18.9,-3813367.241, 3554310.637, 3662866.877 19.0,-3813366.676, 3554311.249, 3662866.871 19.1,-3813366.114, 3554311.864, 3662866.860 19.2,-3813365.554, 3554312.482, 3662866.843 19.3,-3813364.998, 3554313.102, 3662866.820 19.4,-3813364.445, 3554313.725, 3662866.792 19.5,-3813363.895, 3554314.350, 3662866.758 19.6,-3813363.349, 3554314.978, 3662866.718 19.7,-3813362.805, 3554315.608, 3662866.673 19.8,-3813362.265, 3554316.240, 3662866.622 19.9,-3813361.728, 3554316.875, 3662866.565 20.0,-3813361.195, 3554317.512, 3662866.503 20.1,-3813360.664, 3554318.152, 3662866.435 20.2,-3813360.138, 3554318.793, 3662866.361 20.3,-3813359.614, 3554319.437, 3662866.282 20.4,-3813359.094, 3554320.082, 3662866.197 20.5,-3813358.578, 3554320.730, 3662866.106 20.6,-3813358.065, 3554321.380, 3662866.010 20.7,-3813357.556, 3554322.032, 3662865.909 20.8,-3813357.051, 3554322.686, 3662865.801 20.9,-3813356.549, 3554323.341, 3662865.689 21.0,-3813356.050, 3554323.999, 3662865.570 21.1,-3813355.556, 3554324.658, 3662865.446 21.2,-3813355.065, 3554325.319, 3662865.316 21.3,-3813354.578, 3554325.981, 3662865.181 21.4,-3813354.095, 3554326.646, 3662865.040 21.5,-3813353.616, 3554327.312, 3662864.894 21.6,-3813353.141, 3554327.979, 3662864.742 21.7,-3813352.669, 3554328.648, 3662864.585 21.8,-3813352.202, 3554329.318, 3662864.422 21.9,-3813351.739, 3554329.990, 3662864.254 22.0,-3813351.279, 3554330.663, 3662864.080 22.1,-3813350.824, 3554331.338, 3662863.901 22.2,-3813350.373, 3554332.014, 3662863.716 22.3,-3813349.926, 3554332.691, 3662863.526 22.4,-3813349.483, 3554333.369, 3662863.330 22.5,-3813349.044, 3554334.048, 3662863.129 22.6,-3813348.609, 3554334.729, 3662862.923 22.7,-3813348.179, 3554335.410, 3662862.711 22.8,-3813347.753, 3554336.093, 3662862.493 22.9,-3813347.331, 3554336.776, 3662862.271 23.0,-3813346.914, 3554337.460, 3662862.043 23.1,-3813346.501, 3554338.146, 3662861.809 23.2,-3813346.092, 3554338.831, 3662861.571 23.3,-3813345.688, 3554339.518, 3662861.327 23.4,-3813345.288, 3554340.206, 3662861.078 23.5,-3813344.893, 3554340.894, 3662860.823 23.6,-3813344.502, 3554341.582, 3662860.564 23.7,-3813344.116, 3554342.272, 3662860.299 23.8,-3813343.735, 3554342.961, 3662860.028 23.9,-3813343.357, 3554343.652, 3662859.753 24.0,-3813342.985, 3554344.342, 3662859.472 24.1,-3813342.617, 3554345.033, 3662859.187 24.2,-3813342.254, 3554345.725, 3662858.896 24.3,-3813341.896, 3554346.416, 3662858.600 24.4,-3813341.542, 3554347.108, 3662858.299 24.5,-3813341.193, 3554347.800, 3662857.993 24.6,-3813340.849, 3554348.492, 3662857.682 24.7,-3813340.509, 3554349.185, 3662857.366 24.8,-3813340.174, 3554349.877, 3662857.044 24.9,-3813339.845, 3554350.569, 3662856.718 25.0,-3813339.520, 3554351.261, 3662856.387 25.1,-3813339.199, 3554351.954, 3662856.051 25.2,-3813338.884, 3554352.646, 3662855.710 25.3,-3813338.574, 3554353.337, 3662855.364 25.4,-3813338.269, 3554354.029, 3662855.013 25.5,-3813337.968, 3554354.720, 3662854.658 25.6,-3813337.673, 3554355.411, 3662854.297 25.7,-3813337.383, 3554356.101, 3662853.932 25.8,-3813337.097, 3554356.791, 3662853.562 25.9,-3813336.817, 3554357.481, 3662853.187 26.0,-3813336.542, 3554358.170, 3662852.808 26.1,-3813336.272, 3554358.858, 3662852.423 26.2,-3813336.006, 3554359.546, 3662852.035 26.3,-3813335.747, 3554360.233, 3662851.641 26.4,-3813335.492, 3554360.919, 3662851.243 26.5,-3813335.242, 3554361.605, 3662850.840 26.6,-3813334.998, 3554362.290, 3662850.433 26.7,-3813334.758, 3554362.973, 3662850.021 26.8,-3813334.524, 3554363.656, 3662849.605 26.9,-3813334.296, 3554364.338, 3662849.185 27.0,-3813334.072, 3554365.019, 3662848.759 27.1,-3813333.854, 3554365.699, 3662848.330 27.2,-3813333.641, 3554366.378, 3662847.896 27.3,-3813333.433, 3554367.056, 3662847.458 27.4,-3813333.230, 3554367.732, 3662847.015 27.5,-3813333.033, 3554368.407, 3662846.568 27.6,-3813332.841, 3554369.081, 3662846.117 27.7,-3813332.655, 3554369.754, 3662845.661 27.8,-3813332.474, 3554370.425, 3662845.202 27.9,-3813332.298, 3554371.094, 3662844.738 28.0,-3813332.128, 3554371.763, 3662844.270 28.1,-3813331.963, 3554372.429, 3662843.798 28.2,-3813331.803, 3554373.094, 3662843.322 28.3,-3813331.649, 3554373.758, 3662842.842 28.4,-3813331.500, 3554374.420, 3662842.358 28.5,-3813331.357, 3554375.080, 3662841.870 28.6,-3813331.219, 3554375.738, 3662841.378 28.7,-3813331.087, 3554376.395, 3662840.882 28.8,-3813330.960, 3554377.049, 3662840.382 28.9,-3813330.838, 3554377.702, 3662839.879 29.0,-3813330.722, 3554378.353, 3662839.371 29.1,-3813330.612, 3554379.002, 3662838.860 29.2,-3813330.507, 3554379.648, 3662838.345 29.3,-3813330.407, 3554380.293, 3662837.827 29.4,-3813330.313, 3554380.936, 3662837.305 29.5,-3813330.225, 3554381.576, 3662836.779 29.6,-3813330.142, 3554382.214, 3662836.249 29.7,-3813330.065, 3554382.850, 3662835.716 29.8,-3813329.993, 3554383.484, 3662835.180 29.9,-3813329.926, 3554384.115, 3662834.640 30.0,-3813329.866, 3554384.744, 3662834.097 30.1,-3813329.810, 3554385.371, 3662833.550 30.2,-3813329.761, 3554385.995, 3662833.000 30.3,-3813329.717, 3554386.616, 3662832.446 30.4,-3813329.678, 3554387.235, 3662831.890 30.5,-3813329.645, 3554387.851, 3662831.330 30.6,-3813329.618, 3554388.465, 3662830.767 30.7,-3813329.596, 3554389.076, 3662830.200 30.8,-3813329.579, 3554389.684, 3662829.631 30.9,-3813329.569, 3554390.290, 3662829.059 31.0,-3813329.563, 3554390.892, 3662828.483 31.1,-3813329.564, 3554391.492, 3662827.904 31.2,-3813329.570, 3554392.089, 3662827.323 31.3,-3813329.581, 3554392.683, 3662826.739 31.4,-3813329.598, 3554393.274, 3662826.151 31.5,-3813329.621, 3554393.862, 3662825.561 31.6,-3813329.649, 3554394.446, 3662824.968 31.7,-3813329.683, 3554395.028, 3662824.372 31.8,-3813329.723, 3554395.607, 3662823.774 31.9,-3813329.768, 3554396.182, 3662823.173 32.0,-3813329.818, 3554396.754, 3662822.569 32.1,-3813329.874, 3554397.323, 3662821.963 32.2,-3813329.936, 3554397.889, 3662821.354 32.3,-3813330.003, 3554398.451, 3662820.743 32.4,-3813330.075, 3554399.010, 3662820.129 32.5,-3813330.154, 3554399.566, 3662819.512 32.6,-3813330.237, 3554400.118, 3662818.894 32.7,-3813330.327, 3554400.666, 3662818.273 32.8,-3813330.421, 3554401.211, 3662817.650 32.9,-3813330.522, 3554401.752, 3662817.024 33.0,-3813330.627, 3554402.290, 3662816.396 33.1,-3813330.739, 3554402.824, 3662815.766 33.2,-3813330.855, 3554403.355, 3662815.134 33.3,-3813330.978, 3554403.881, 3662814.500 33.4,-3813331.105, 3554404.404, 3662813.864 33.5,-3813331.239, 3554404.923, 3662813.226 33.6,-3813331.377, 3554405.439, 3662812.586 33.7,-3813331.521, 3554405.950, 3662811.944 33.8,-3813331.671, 3554406.458, 3662811.300 33.9,-3813331.826, 3554406.961, 3662810.654 34.0,-3813331.986, 3554407.461, 3662810.007 34.1,-3813332.152, 3554407.956, 3662809.358 34.2,-3813332.323, 3554408.448, 3662808.707 34.3,-3813332.500, 3554408.935, 3662808.055 34.4,-3813332.681, 3554409.419, 3662807.401 34.5,-3813332.869, 3554409.898, 3662806.745 34.6,-3813333.061, 3554410.373, 3662806.088 34.7,-3813333.259, 3554410.844, 3662805.430 34.8,-3813333.462, 3554411.310, 3662804.770 34.9,-3813333.671, 3554411.773, 3662804.109 35.0,-3813333.885, 3554412.231, 3662803.446 35.1,-3813334.104, 3554412.684, 3662802.782 35.2,-3813334.328, 3554413.134, 3662802.117 35.3,-3813334.558, 3554413.579, 3662801.451 35.4,-3813334.792, 3554414.019, 3662800.784 35.5,-3813335.032, 3554414.455, 3662800.115 35.6,-3813335.278, 3554414.886, 3662799.446 35.7,-3813335.528, 3554415.313, 3662798.775 35.8,-3813335.784, 3554415.736, 3662798.104 35.9,-3813336.044, 3554416.153, 3662797.432 36.0,-3813336.310, 3554416.567, 3662796.759 36.1,-3813336.581, 3554416.975, 3662796.085 36.2,-3813336.857, 3554417.379, 3662795.410 36.3,-3813337.138, 3554417.778, 3662794.735 36.4,-3813337.424, 3554418.173, 3662794.059 36.5,-3813337.715, 3554418.562, 3662793.382 36.6,-3813338.011, 3554418.947, 3662792.705 36.7,-3813338.312, 3554419.327, 3662792.027 36.8,-3813338.618, 3554419.703, 3662791.349 36.9,-3813338.929, 3554420.073, 3662790.670 37.0,-3813339.245, 3554420.438, 3662789.991 37.1,-3813339.566, 3554420.799, 3662789.312 37.2,-3813339.892, 3554421.155, 3662788.632 37.3,-3813340.222, 3554421.505, 3662787.952 37.4,-3813340.557, 3554421.851, 3662787.272 37.5,-3813340.898, 3554422.192, 3662786.592 37.6,-3813341.243, 3554422.528, 3662785.912 37.7,-3813341.592, 3554422.858, 3662785.231 37.8,-3813341.947, 3554423.184, 3662784.551 37.9,-3813342.306, 3554423.504, 3662783.871 38.0,-3813342.670, 3554423.819, 3662783.191 38.1,-3813343.038, 3554424.130, 3662782.511 38.2,-3813343.411, 3554424.435, 3662781.831 38.3,-3813343.789, 3554424.734, 3662781.151 38.4,-3813344.171, 3554425.029, 3662780.472 38.5,-3813344.558, 3554425.318, 3662779.793 38.6,-3813344.950, 3554425.602, 3662779.114 38.7,-3813345.346, 3554425.881, 3662778.436 38.8,-3813345.746, 3554426.155, 3662777.758 38.9,-3813346.151, 3554426.423, 3662777.081 39.0,-3813346.560, 3554426.686, 3662776.404 39.1,-3813346.974, 3554426.943, 3662775.728 39.2,-3813347.392, 3554427.196, 3662775.053 39.3,-3813347.814, 3554427.442, 3662774.378 39.4,-3813348.241, 3554427.684, 3662773.704 39.5,-3813348.672, 3554427.920, 3662773.031 39.6,-3813349.107, 3554428.150, 3662772.359 39.7,-3813349.546, 3554428.376, 3662771.688 39.8,-3813349.990, 3554428.595, 3662771.017 39.9,-3813350.437, 3554428.809, 3662770.348 40.0,-3813350.889, 3554429.018, 3662769.680 40.1,-3813351.345, 3554429.221, 3662769.012 40.2,-3813351.805, 3554429.419, 3662768.346 40.3,-3813352.269, 3554429.611, 3662767.681 40.4,-3813352.737, 3554429.798, 3662767.017 40.5,-3813353.209, 3554429.979, 3662766.355 40.6,-3813353.685, 3554430.154, 3662765.693 40.7,-3813354.164, 3554430.324, 3662765.034 40.8,-3813354.648, 3554430.488, 3662764.375 40.9,-3813355.136, 3554430.647, 3662763.718 41.0,-3813355.627, 3554430.800, 3662763.063 41.1,-3813356.122, 3554430.947, 3662762.409 41.2,-3813356.620, 3554431.089, 3662761.756 41.3,-3813357.123, 3554431.225, 3662761.105 41.4,-3813357.629, 3554431.356, 3662760.456 41.5,-3813358.139, 3554431.480, 3662759.809 41.6,-3813358.652, 3554431.599, 3662759.163 41.7,-3813359.169, 3554431.713, 3662758.520 41.8,-3813359.689, 3554431.821, 3662757.878 41.9,-3813360.213, 3554431.923, 3662757.238 42.0,-3813360.740, 3554432.019, 3662756.600 42.1,-3813361.271, 3554432.109, 3662755.963 42.2,-3813361.805, 3554432.194, 3662755.329 42.3,-3813362.342, 3554432.273, 3662754.697 42.4,-3813362.883, 3554432.347, 3662754.068 42.5,-3813363.427, 3554432.414, 3662753.440 42.6,-3813363.974, 3554432.476, 3662752.814 42.7,-3813364.524, 3554432.532, 3662752.191 42.8,-3813365.078, 3554432.583, 3662751.570 42.9,-3813365.634, 3554432.628, 3662750.952 43.0,-3813366.194, 3554432.666, 3662750.335 43.1,-3813366.757, 3554432.700, 3662749.721 43.2,-3813367.322, 3554432.727, 3662749.110 43.3,-3813367.891, 3554432.749, 3662748.501 43.4,-3813368.462, 3554432.764, 3662747.895 43.5,-3813369.037, 3554432.774, 3662747.291 43.6,-3813369.614, 3554432.779, 3662746.690 43.7,-3813370.194, 3554432.777, 3662746.092 43.8,-3813370.777, 3554432.770, 3662745.496 43.9,-3813371.362, 3554432.757, 3662744.903 44.0,-3813371.950, 3554432.738, 3662744.313 44.1,-3813372.541, 3554432.714, 3662743.726 44.2,-3813373.134, 3554432.683, 3662743.142 44.3,-3813373.730, 3554432.647, 3662742.560 44.4,-3813374.329, 3554432.605, 3662741.982 44.5,-3813374.929, 3554432.558, 3662741.406 44.6,-3813375.533, 3554432.505, 3662740.834 44.7,-3813376.138, 3554432.445, 3662740.264 44.8,-3813376.746, 3554432.381, 3662739.698 44.9,-3813377.356, 3554432.310, 3662739.135 45.0,-3813377.969, 3554432.234, 3662738.575 45.1,-3813378.584, 3554432.152, 3662738.019 45.2,-3813379.201, 3554432.064, 3662737.465 45.3,-3813379.820, 3554431.970, 3662736.915 45.4,-3813380.441, 3554431.871, 3662736.369 45.5,-3813381.064, 3554431.766, 3662735.825 45.6,-3813381.689, 3554431.656, 3662735.285 45.7,-3813382.316, 3554431.539, 3662734.749 45.8,-3813382.945, 3554431.417, 3662734.216 45.9,-3813383.576, 3554431.290, 3662733.687 46.0,-3813384.208, 3554431.156, 3662733.161 46.1,-3813384.843, 3554431.017, 3662732.639 46.2,-3813385.479, 3554430.873, 3662732.120 46.3,-3813386.117, 3554430.723, 3662731.606 46.4,-3813386.756, 3554430.567, 3662731.095 46.5,-3813387.397, 3554430.405, 3662730.587 46.6,-3813388.040, 3554430.238, 3662730.084 46.7,-3813388.684, 3554430.065, 3662729.584 46.8,-3813389.330, 3554429.887, 3662729.088 46.9,-3813389.977, 3554429.703, 3662728.596 47.0,-3813390.625, 3554429.514, 3662728.108 47.1,-3813391.275, 3554429.319, 3662727.624 47.2,-3813391.926, 3554429.118, 3662727.144 47.3,-3813392.578, 3554428.912, 3662726.668 47.4,-3813393.232, 3554428.701, 3662726.196 47.5,-3813393.886, 3554428.484, 3662725.729 47.6,-3813394.542, 3554428.261, 3662725.265 47.7,-3813395.199, 3554428.034, 3662724.805 47.8,-3813395.857, 3554427.800, 3662724.350 47.9,-3813396.515, 3554427.561, 3662723.899 48.0,-3813397.175, 3554427.317, 3662723.452 48.1,-3813397.836, 3554427.068, 3662723.010 48.2,-3813398.497, 3554426.813, 3662722.571 48.3,-3813399.159, 3554426.553, 3662722.138 48.4,-3813399.822, 3554426.287, 3662721.708 48.5,-3813400.486, 3554426.016, 3662721.283 48.6,-3813401.150, 3554425.740, 3662720.862 48.7,-3813401.815, 3554425.458, 3662720.446 48.8,-3813402.480, 3554425.171, 3662720.034 48.9,-3813403.146, 3554424.879, 3662719.627 49.0,-3813403.812, 3554424.582, 3662719.225 49.1,-3813404.479, 3554424.280, 3662718.827 49.2,-3813405.146, 3554423.972, 3662718.433 49.3,-3813405.814, 3554423.659, 3662718.045 49.4,-3813406.481, 3554423.341, 3662717.661 49.5,-3813407.149, 3554423.018, 3662717.281 49.6,-3813407.817, 3554422.690, 3662716.906 49.7,-3813408.486, 3554422.357, 3662716.537 49.8,-3813409.154, 3554422.019, 3662716.171 49.9,-3813409.822, 3554421.675, 3662715.811 50.0,-3813410.491, 3554421.327, 3662715.456 50.1,-3813411.159, 3554420.974, 3662715.105 50.2,-3813411.827, 3554420.616, 3662714.759 50.3,-3813412.495, 3554420.253, 3662714.418 50.4,-3813413.163, 3554419.885, 3662714.082 50.5,-3813413.831, 3554419.512, 3662713.751 50.6,-3813414.498, 3554419.134, 3662713.425 50.7,-3813415.166, 3554418.752, 3662713.104 50.8,-3813415.832, 3554418.364, 3662712.788 50.9,-3813416.498, 3554417.972, 3662712.477 51.0,-3813417.164, 3554417.575, 3662712.171 51.1,-3813417.830, 3554417.174, 3662711.870 51.2,-3813418.494, 3554416.767, 3662711.574 51.3,-3813419.158, 3554416.357, 3662711.283 51.4,-3813419.822, 3554415.941, 3662710.997 51.5,-3813420.485, 3554415.521, 3662710.717 51.6,-3813421.147, 3554415.096, 3662710.442 51.7,-3813421.808, 3554414.667, 3662710.172 51.8,-3813422.468, 3554414.233, 3662709.907 51.9,-3813423.128, 3554413.795, 3662709.647 52.0,-3813423.787, 3554413.352, 3662709.393 52.1,-3813424.444, 3554412.905, 3662709.144 52.2,-3813425.101, 3554412.454, 3662708.900 52.3,-3813425.756, 3554411.998, 3662708.661 52.4,-3813426.411, 3554411.538, 3662708.428 52.5,-3813427.064, 3554411.073, 3662708.200 52.6,-3813427.716, 3554410.604, 3662707.978 52.7,-3813428.367, 3554410.131, 3662707.761 52.8,-3813429.016, 3554409.654, 3662707.549 52.9,-3813429.665, 3554409.173, 3662707.342 53.0,-3813430.311, 3554408.687, 3662707.141 53.1,-3813430.957, 3554408.198, 3662706.946 53.2,-3813431.601, 3554407.704, 3662706.756 53.3,-3813432.243, 3554407.207, 3662706.571 53.4,-3813432.884, 3554406.705, 3662706.392 53.5,-3813433.523, 3554406.199, 3662706.218 53.6,-3813434.161, 3554405.690, 3662706.050 53.7,-3813434.797, 3554405.176, 3662705.887 53.8,-3813435.431, 3554404.659, 3662705.730 53.9,-3813436.063, 3554404.138, 3662705.578 54.0,-3813436.694, 3554403.613, 3662705.432 54.1,-3813437.323, 3554403.085, 3662705.291 54.2,-3813437.949, 3554402.552, 3662705.156 54.3,-3813438.574, 3554402.016, 3662705.027 54.4,-3813439.197, 3554401.477, 3662704.903 54.5,-3813439.818, 3554400.934, 3662704.784 54.6,-3813440.436, 3554400.387, 3662704.672 54.7,-3813441.053, 3554399.836, 3662704.564 54.8,-3813441.667, 3554399.283, 3662704.463 54.9,-3813442.280, 3554398.726, 3662704.367 55.0,-3813442.889, 3554398.165, 3662704.276 55.1,-3813443.497, 3554397.601, 3662704.192 55.2,-3813444.102, 3554397.034, 3662704.113 55.3,-3813444.705, 3554396.463, 3662704.039 55.4,-3813445.306, 3554395.889, 3662703.971 55.5,-3813445.904, 3554395.312, 3662703.909 55.6,-3813446.499, 3554394.732, 3662703.853 55.7,-3813447.092, 3554394.149, 3662703.802 55.8,-3813447.682, 3554393.562, 3662703.756 55.9,-3813448.270, 3554392.973, 3662703.717 56.0,-3813448.855, 3554392.380, 3662703.683 56.1,-3813449.437, 3554391.785, 3662703.655 56.2,-3813450.017, 3554391.186, 3662703.632 56.3,-3813450.594, 3554390.585, 3662703.615 56.4,-3813451.168, 3554389.981, 3662703.604 56.5,-3813451.739, 3554389.374, 3662703.598 56.6,-3813452.307, 3554388.765, 3662703.598 56.7,-3813452.872, 3554388.152, 3662703.604 56.8,-3813453.434, 3554387.537, 3662703.615 56.9,-3813453.993, 3554386.920, 3662703.633 57.0,-3813454.550, 3554386.299, 3662703.655 57.1,-3813455.103, 3554385.677, 3662703.684 57.2,-3813455.652, 3554385.051, 3662703.718 57.3,-3813456.199, 3554384.424, 3662703.757 57.4,-3813456.742, 3554383.793, 3662703.803 57.5,-3813457.283, 3554383.161, 3662703.854 57.6,-3813457.819, 3554382.526, 3662703.911 57.7,-3813458.353, 3554381.889, 3662703.973 57.8,-3813458.883, 3554381.250, 3662704.041 57.9,-3813459.410, 3554380.608, 3662704.115 58.0,-3813459.933, 3554379.965, 3662704.194 58.1,-3813460.453, 3554379.319, 3662704.279 58.2,-3813460.969, 3554378.671, 3662704.369 58.3,-3813461.482, 3554378.021, 3662704.465 58.4,-3813461.991, 3554377.369, 3662704.567 58.5,-3813462.497, 3554376.716, 3662704.675 58.6,-3813462.999, 3554376.060, 3662704.787 58.7,-3813463.497, 3554375.402, 3662704.906 58.8,-3813463.991, 3554374.743, 3662705.030 58.9,-3813464.482, 3554374.082, 3662705.160 59.0,-3813464.969, 3554373.420, 3662705.295 59.1,-3813465.452, 3554372.755, 3662705.436 59.2,-3813465.931, 3554372.089, 3662705.582 59.3,-3813466.406, 3554371.422, 3662705.734 59.4,-3813466.878, 3554370.753, 3662705.891 59.5,-3813467.345, 3554370.083, 3662706.054 59.6,-3813467.808, 3554369.411, 3662706.223 59.7,-3813468.268, 3554368.738, 3662706.397 59.8,-3813468.723, 3554368.063, 3662706.576 59.9,-3813469.174, 3554367.387, 3662706.761 60.0,-3813469.621, 3554366.710, 3662706.951 60.1,-3813470.064, 3554366.032, 3662707.147 60.2,-3813470.503, 3554365.353, 3662707.348 60.3,-3813470.937, 3554364.672, 3662707.554 60.4,-3813471.368, 3554363.991, 3662707.766 60.5,-3813471.793, 3554363.308, 3662707.984 60.6,-3813472.215, 3554362.625, 3662708.206 60.7,-3813472.632, 3554361.940, 3662708.434 60.8,-3813473.045, 3554361.255, 3662708.668 60.9,-3813473.454, 3554360.569, 3662708.906 61.0,-3813473.858, 3554359.883, 3662709.150 61.1,-3813474.258, 3554359.195, 3662709.399 61.2,-3813474.653, 3554358.507, 3662709.654 61.3,-3813475.044, 3554357.818, 3662709.914 61.4,-3813475.430, 3554357.129, 3662710.179 61.5,-3813475.812, 3554356.439, 3662710.449 61.6,-3813476.189, 3554355.749, 3662710.724 61.7,-3813476.561, 3554355.059, 3662711.005 61.8,-3813476.929, 3554354.367, 3662711.291 61.9,-3813477.292, 3554353.676, 3662711.582 62.0,-3813477.650, 3554352.984, 3662711.878 62.1,-3813478.004, 3554352.293, 3662712.179 62.2,-3813478.353, 3554351.601, 3662712.485 62.3,-3813478.697, 3554350.908, 3662712.796 62.4,-3813479.037, 3554350.216, 3662713.112 62.5,-3813479.371, 3554349.524, 3662713.434 62.6,-3813479.701, 3554348.832, 3662713.760 62.7,-3813480.026, 3554348.139, 3662714.091 62.8,-3813480.346, 3554347.447, 3662714.427 62.9,-3813480.661, 3554346.755, 3662714.768 63.0,-3813480.972, 3554346.064, 3662715.114 63.1,-3813481.277, 3554345.372, 3662715.465 63.2,-3813481.577, 3554344.681, 3662715.821 63.3,-3813481.873, 3554343.990, 3662716.181 63.4,-3813482.163, 3554343.300, 3662716.546 63.5,-3813482.448, 3554342.610, 3662716.916 63.6,-3813482.728, 3554341.920, 3662717.291 63.7,-3813483.004, 3554341.231, 3662717.671 63.8,-3813483.274, 3554340.543, 3662718.055 63.9,-3813483.539, 3554339.855, 3662718.444 64.0,-3813483.799, 3554339.168, 3662718.837 64.1,-3813484.053, 3554338.482, 3662719.235 64.2,-3813484.303, 3554337.796, 3662719.638 64.3,-3813484.547, 3554337.111, 3662720.045 64.4,-3813484.786, 3554336.427, 3662720.457 64.5,-3813485.020, 3554335.744, 3662720.873 64.6,-3813485.249, 3554335.063, 3662721.294 64.7,-3813485.473, 3554334.382, 3662721.719 64.8,-3813485.691, 3554333.702, 3662722.149 64.9,-3813485.904, 3554333.023, 3662722.583 65.0,-3813486.112, 3554332.345, 3662723.021 65.1,-3813486.314, 3554331.669, 3662723.464 65.2,-3813486.511, 3554330.994, 3662723.911 65.3,-3813486.703, 3554330.320, 3662724.362 65.4,-3813486.889, 3554329.647, 3662724.818 65.5,-3813487.071, 3554328.976, 3662725.277 65.6,-3813487.246, 3554328.307, 3662725.741 65.7,-3813487.417, 3554327.638, 3662726.209 65.8,-3813487.581, 3554326.972, 3662726.681 65.9,-3813487.741, 3554326.307, 3662727.157 66.0,-3813487.895, 3554325.643, 3662727.637 66.1,-3813488.044, 3554324.981, 3662728.121 66.2,-3813488.187, 3554324.321, 3662728.609 66.3,-3813488.325, 3554323.663, 3662729.101 66.4,-3813488.457, 3554323.007, 3662729.597 66.5,-3813488.584, 3554322.352, 3662730.097 66.6,-3813488.706, 3554321.699, 3662730.601 66.7,-3813488.821, 3554321.048, 3662731.108 66.8,-3813488.932, 3554320.400, 3662731.619 66.9,-3813489.037, 3554319.753, 3662732.134 67.0,-3813489.136, 3554319.108, 3662732.653 67.1,-3813489.230, 3554318.466, 3662733.175 67.2,-3813489.319, 3554317.825, 3662733.701 67.3,-3813489.402, 3554317.187, 3662734.230 67.4,-3813489.479, 3554316.551, 3662734.763 67.5,-3813489.551, 3554315.917, 3662735.300 67.6,-3813489.617, 3554315.286, 3662735.840 67.7,-3813489.678, 3554314.657, 3662736.383 67.8,-3813489.733, 3554314.031, 3662736.930 67.9,-3813489.783, 3554313.407, 3662737.480 68.0,-3813489.827, 3554312.785, 3662738.033 68.1,-3813489.865, 3554312.167, 3662738.590 68.2,-3813489.898, 3554311.550, 3662739.150 68.3,-3813489.925, 3554310.937, 3662739.713 68.4,-3813489.947, 3554310.326, 3662740.280 68.5,-3813489.964, 3554309.717, 3662740.849 68.6,-3813489.974, 3554309.112, 3662741.421 68.7,-3813489.979, 3554308.509, 3662741.997 68.8,-3813489.979, 3554307.910, 3662742.576 68.9,-3813489.973, 3554307.313, 3662743.157 69.0,-3813489.961, 3554306.719, 3662743.742 69.1,-3813489.944, 3554306.128, 3662744.329 69.2,-3813489.921, 3554305.540, 3662744.919 69.3,-3813489.893, 3554304.955, 3662745.512 69.4,-3813489.859, 3554304.374, 3662746.108 69.5,-3813489.820, 3554303.795, 3662746.706 69.6,-3813489.775, 3554303.220, 3662747.307 69.7,-3813489.724, 3554302.647, 3662747.911 69.8,-3813489.668, 3554302.079, 3662748.517 69.9,-3813489.607, 3554301.513, 3662749.126 70.0,-3813489.539, 3554300.951, 3662749.738 70.1,-3813489.467, 3554300.392, 3662750.352 70.2,-3813489.389, 3554299.836, 3662750.968 70.3,-3813489.305, 3554299.285, 3662751.587 70.4,-3813489.215, 3554298.736, 3662752.208 70.5,-3813489.121, 3554298.191, 3662752.831 70.6,-3813489.020, 3554297.650, 3662753.457 70.7,-3813488.914, 3554297.112, 3662754.084 70.8,-3813488.803, 3554296.578, 3662754.714 70.9,-3813488.686, 3554296.048, 3662755.346 71.0,-3813488.564, 3554295.521, 3662755.980 71.1,-3813488.436, 3554294.998, 3662756.617 71.2,-3813488.303, 3554294.479, 3662757.255 71.3,-3813488.164, 3554293.964, 3662757.895 71.4,-3813488.020, 3554293.452, 3662758.537 71.5,-3813487.871, 3554292.945, 3662759.181 71.6,-3813487.716, 3554292.441, 3662759.826 71.7,-3813487.555, 3554291.942, 3662760.474 71.8,-3813487.389, 3554291.446, 3662761.123 71.9,-3813487.218, 3554290.955, 3662761.774 72.0,-3813487.042, 3554290.467, 3662762.426 72.1,-3813486.860, 3554289.984, 3662763.080 72.2,-3813486.672, 3554289.505, 3662763.736 72.3,-3813486.480, 3554289.030, 3662764.393 72.4,-3813486.282, 3554288.559, 3662765.051 72.5,-3813486.079, 3554288.092, 3662765.711 72.6,-3813485.870, 3554287.630, 3662766.372 72.7,-3813485.656, 3554287.172, 3662767.035 72.8,-3813485.437, 3554286.719, 3662767.699 72.9,-3813485.213, 3554286.269, 3662768.364 73.0,-3813484.983, 3554285.825, 3662769.030 73.1,-3813484.748, 3554285.384, 3662769.697 73.2,-3813484.508, 3554284.948, 3662770.366 73.3,-3813484.263, 3554284.517, 3662771.035 73.4,-3813484.013, 3554284.090, 3662771.706 73.5,-3813483.757, 3554283.668, 3662772.377 73.6,-3813483.496, 3554283.250, 3662773.049 73.7,-3813483.230, 3554282.837, 3662773.722 73.8,-3813482.960, 3554282.428, 3662774.396 73.9,-3813482.684, 3554282.024, 3662775.071 74.0,-3813482.402, 3554281.625, 3662775.746 74.1,-3813482.116, 3554281.231, 3662776.422 74.2,-3813481.825, 3554280.841, 3662777.099 74.3,-3813481.529, 3554280.456, 3662777.776 74.4,-3813481.228, 3554280.076, 3662778.454 74.5,-3813480.922, 3554279.701, 3662779.132 74.6,-3813480.611, 3554279.331, 3662779.811 74.7,-3813480.295, 3554278.965, 3662780.490 74.8,-3813479.974, 3554278.605, 3662781.169 74.9,-3813479.648, 3554278.249, 3662781.849 75.0,-3813479.318, 3554277.898, 3662782.529 75.1,-3813478.982, 3554277.553, 3662783.209 75.2,-3813478.642, 3554277.212, 3662783.889 75.3,-3813478.297, 3554276.876, 3662784.569 75.4,-3813477.947, 3554276.546, 3662785.250 75.5,-3813477.593, 3554276.220, 3662785.930 75.6,-3813477.234, 3554275.900, 3662786.610 75.7,-3813476.870, 3554275.585, 3662787.290 75.8,-3813476.501, 3554275.275, 3662787.970 75.9,-3813476.128, 3554274.970, 3662788.650 76.0,-3813475.750, 3554274.670, 3662789.330 76.1,-3813475.368, 3554274.375, 3662790.009 76.2,-3813474.981, 3554274.086, 3662790.688 76.3,-3813474.590, 3554273.802, 3662791.367 76.4,-3813474.194, 3554273.523, 3662792.045 76.5,-3813473.793, 3554273.250, 3662792.723 76.6,-3813473.388, 3554272.982, 3662793.400 76.7,-3813472.979, 3554272.719, 3662794.077 76.8,-3813472.565, 3554272.461, 3662794.753 76.9,-3813472.147, 3554272.209, 3662795.428 77.0,-3813471.725, 3554271.962, 3662796.103 77.1,-3813471.298, 3554271.721, 3662796.776 77.2,-3813470.867, 3554271.485, 3662797.450 77.3,-3813470.432, 3554271.254, 3662798.122 77.4,-3813469.993, 3554271.029, 3662798.793 77.5,-3813469.549, 3554270.810, 3662799.464 77.6,-3813469.102, 3554270.596, 3662800.133 77.7,-3813468.650, 3554270.387, 3662800.801 77.8,-3813468.194, 3554270.184, 3662801.469 77.9,-3813467.734, 3554269.986, 3662802.135 78.0,-3813467.270, 3554269.794, 3662802.800 78.1,-3813466.802, 3554269.608, 3662803.464 78.2,-3813466.330, 3554269.427, 3662804.126 78.3,-3813465.854, 3554269.251, 3662804.787 78.4,-3813465.374, 3554269.081, 3662805.447 78.5,-3813464.890, 3554268.917, 3662806.106 78.6,-3813464.403, 3554268.759, 3662806.763 78.7,-3813463.912, 3554268.606, 3662807.418 78.8,-3813463.417, 3554268.458, 3662808.072 78.9,-3813462.918, 3554268.317, 3662808.725 79.0,-3813462.415, 3554268.181, 3662809.375 79.1,-3813461.909, 3554268.050, 3662810.024 79.2,-3813461.400, 3554267.926, 3662810.672 79.3,-3813460.886, 3554267.807, 3662811.317 79.4,-3813460.369, 3554267.693, 3662811.961 79.5,-3813459.849, 3554267.586, 3662812.603 79.6,-3813459.325, 3554267.484, 3662813.243 79.7,-3813458.798, 3554267.387, 3662813.881 79.8,-3813458.267, 3554267.297, 3662814.517 79.9,-3813457.733, 3554267.212, 3662815.151 80.0,-3813457.196, 3554267.133, 3662815.783 80.1,-3813456.655, 3554267.060, 3662816.413 80.2,-3813456.111, 3554266.992, 3662817.041 80.3,-3813455.564, 3554266.930, 3662817.666 80.4,-3813455.014, 3554266.874, 3662818.289 80.5,-3813454.460, 3554266.824, 3662818.910 80.6,-3813453.903, 3554266.779, 3662819.529 80.7,-3813453.344, 3554266.740, 3662820.145 80.8,-3813452.781, 3554266.707, 3662820.759 80.9,-3813452.215, 3554266.680, 3662821.370 81.0,-3813451.647, 3554266.658, 3662821.979 81.1,-3813451.075, 3554266.642, 3662822.585 81.2,-3813450.501, 3554266.632, 3662823.189 81.3,-3813449.924, 3554266.628, 3662823.790 81.4,-3813449.344, 3554266.630, 3662824.388 81.5,-3813448.761, 3554266.637, 3662824.984 81.6,-3813448.175, 3554266.650, 3662825.577 81.7,-3813447.587, 3554266.669, 3662826.167 81.8,-3813446.997, 3554266.694, 3662826.754 81.9,-3813446.403, 3554266.724, 3662827.339 82.0,-3813445.807, 3554266.760, 3662827.920 82.1,-3813445.209, 3554266.802, 3662828.498 82.2,-3813444.608, 3554266.850, 3662829.074 82.3,-3813444.005, 3554266.903, 3662829.646 82.4,-3813443.399, 3554266.962, 3662830.216 82.5,-3813442.791, 3554267.027, 3662830.782 82.6,-3813442.181, 3554267.098, 3662831.345 82.7,-3813441.568, 3554267.174, 3662831.905 82.8,-3813440.954, 3554267.256, 3662832.461 82.9,-3813440.337, 3554267.344, 3662833.015 83.0,-3813439.718, 3554267.437, 3662833.565 83.1,-3813439.097, 3554267.537, 3662834.111 83.2,-3813438.473, 3554267.642, 3662834.654 83.3,-3813437.848, 3554267.752, 3662835.194 83.4,-3813437.221, 3554267.869, 3662835.731 83.5,-3813436.592, 3554267.991, 3662836.264 83.6,-3813435.961, 3554268.118, 3662836.793 83.7,-3813435.329, 3554268.252, 3662837.319 83.8,-3813434.694, 3554268.391, 3662837.841 83.9,-3813434.058, 3554268.535, 3662838.359 84.0,-3813433.420, 3554268.686, 3662838.874 84.1,-3813432.781, 3554268.842, 3662839.385 84.2,-3813432.140, 3554269.003, 3662839.892 84.3,-3813431.497, 3554269.170, 3662840.396 84.4,-3813430.853, 3554269.343, 3662840.895 84.5,-3813430.207, 3554269.521, 3662841.391 84.6,-3813429.560, 3554269.705, 3662841.883 84.7,-3813428.912, 3554269.895, 3662842.371 84.8,-3813428.262, 3554270.090, 3662842.855 84.9,-3813427.611, 3554270.290, 3662843.335 85.0,-3813426.959, 3554270.496, 3662843.811 85.1,-3813426.305, 3554270.708, 3662844.283 85.2,-3813425.651, 3554270.925, 3662844.750 85.3,-3813424.995, 3554271.147, 3662845.214 85.4,-3813424.338, 3554271.375, 3662845.674 85.5,-3813423.680, 3554271.609, 3662846.129 85.6,-3813423.022, 3554271.848, 3662846.580 85.7,-3813422.362, 3554272.092, 3662847.027 85.8,-3813421.701, 3554272.341, 3662847.469 85.9,-3813421.040, 3554272.596, 3662847.907 86.0,-3813420.378, 3554272.857, 3662848.341 86.1,-3813419.715, 3554273.122, 3662848.771 86.2,-3813419.051, 3554273.393, 3662849.196 86.3,-3813418.387, 3554273.670, 3662849.616 86.4,-3813417.722, 3554273.951, 3662850.032 86.5,-3813417.057, 3554274.238, 3662850.444 86.6,-3813416.391, 3554274.530, 3662850.851 86.7,-3813415.725, 3554274.828, 3662851.254 86.8,-3813415.058, 3554275.130, 3662851.652 86.9,-3813414.391, 3554275.438, 3662852.045 87.0,-3813413.723, 3554275.751, 3662852.434 87.1,-3813413.056, 3554276.068, 3662852.818 87.2,-3813412.388, 3554276.392, 3662853.197 87.3,-3813411.720, 3554276.720, 3662853.572 87.4,-3813411.051, 3554277.053, 3662853.942 87.5,-3813410.383, 3554277.391, 3662854.307 87.6,-3813409.715, 3554277.735, 3662854.667 87.7,-3813409.046, 3554278.083, 3662855.023 87.8,-3813408.378, 3554278.436, 3662855.373 87.9,-3813407.710, 3554278.794, 3662855.719 88.0,-3813407.041, 3554279.158, 3662856.060 88.1,-3813406.374, 3554279.526, 3662856.396 88.2,-3813405.706, 3554279.898, 3662856.727 88.3,-3813405.038, 3554280.276, 3662857.053 88.4,-3813404.371, 3554280.659, 3662857.374 88.5,-3813403.705, 3554281.046, 3662857.690 88.6,-3813403.038, 3554281.438, 3662858.001 88.7,-3813402.373, 3554281.835, 3662858.307 88.8,-3813401.707, 3554282.237, 3662858.608 88.9,-3813401.043, 3554282.643, 3662858.904 89.0,-3813400.378, 3554283.054, 3662859.194 89.1,-3813399.715, 3554283.470, 3662859.480 89.2,-3813399.052, 3554283.890, 3662859.760 89.3,-3813398.390, 3554284.315, 3662860.036 89.4,-3813397.729, 3554284.744, 3662860.306 89.5,-3813397.069, 3554285.178, 3662860.571 89.6,-3813396.409, 3554285.616, 3662860.830 89.7,-3813395.750, 3554286.059, 3662861.084 89.8,-3813395.093, 3554286.506, 3662861.333 89.9,-3813394.436, 3554286.957, 3662861.577 90.0,-3813393.781, 3554287.413, 3662861.816 90.1,-3813393.126, 3554287.874, 3662862.049 90.2,-3813392.473, 3554288.338, 3662862.277 90.3,-3813391.821, 3554288.807, 3662862.499 90.4,-3813391.170, 3554289.280, 3662862.716 90.5,-3813390.521, 3554289.757, 3662862.928 90.6,-3813389.872, 3554290.238, 3662863.134 90.7,-3813389.226, 3554290.724, 3662863.335 90.8,-3813388.580, 3554291.213, 3662863.531 90.9,-3813387.936, 3554291.707, 3662863.721 91.0,-3813387.294, 3554292.205, 3662863.906 91.1,-3813386.653, 3554292.706, 3662864.085 91.2,-3813386.014, 3554293.212, 3662864.258 91.3,-3813385.376, 3554293.722, 3662864.427 91.4,-3813384.740, 3554294.235, 3662864.589 91.5,-3813384.106, 3554294.752, 3662864.746 91.6,-3813383.474, 3554295.274, 3662864.898 91.7,-3813382.843, 3554295.798, 3662865.044 91.8,-3813382.215, 3554296.327, 3662865.185 91.9,-3813381.588, 3554296.859, 3662865.320 92.0,-3813380.963, 3554297.395, 3662865.449 92.1,-3813380.340, 3554297.935, 3662865.573 92.2,-3813379.720, 3554298.478, 3662865.692 92.3,-3813379.101, 3554299.025, 3662865.804 92.4,-3813378.484, 3554299.575, 3662865.911 92.5,-3813377.870, 3554300.129, 3662866.013 92.6,-3813377.258, 3554300.686, 3662866.109 92.7,-3813376.648, 3554301.247, 3662866.199 92.8,-3813376.040, 3554301.811, 3662866.284 92.9,-3813375.435, 3554302.378, 3662866.363 93.0,-3813374.832, 3554302.949, 3662866.436 93.1,-3813374.232, 3554303.523, 3662866.504 93.2,-3813373.634, 3554304.100, 3662866.566 93.3,-3813373.038, 3554304.680, 3662866.623 93.4,-3813372.446, 3554305.264, 3662866.674 93.5,-3813371.855, 3554305.850, 3662866.719 93.6,-3813371.268, 3554306.439, 3662866.759 93.7,-3813370.683, 3554307.032, 3662866.792 93.8,-3813370.100, 3554307.627, 3662866.821 93.9,-3813369.521, 3554308.226, 3662866.843 94.0,-3813368.944, 3554308.827, 3662866.860 94.1,-3813368.370, 3554309.431, 3662866.871 94.2,-3813367.799, 3554310.038, 3662866.877 94.3,-3813367.231, 3554310.648, 3662866.877 94.4,-3813366.666, 3554311.260, 3662866.871 94.5,-3813366.104, 3554311.875, 3662866.859 94.6,-3813365.544, 3554312.493, 3662866.842 94.7,-3813364.988, 3554313.113, 3662866.819 94.8,-3813364.435, 3554313.736, 3662866.791 94.9,-3813363.886, 3554314.361, 3662866.757 95.0,-3813363.339, 3554314.989, 3662866.717 95.1,-3813362.796, 3554315.619, 3662866.672 95.2,-3813362.255, 3554316.252, 3662866.621 95.3,-3813361.719, 3554316.886, 3662866.564 95.4,-3813361.185, 3554317.524, 3662866.501 95.5,-3813360.655, 3554318.163, 3662866.433 95.6,-3813360.128, 3554318.804, 3662866.360 95.7,-3813359.605, 3554319.448, 3662866.280 95.8,-3813359.085, 3554320.094, 3662866.195 95.9,-3813358.569, 3554320.742, 3662866.105 96.0,-3813358.056, 3554321.392, 3662866.009 96.1,-3813357.547, 3554322.043, 3662865.907 96.2,-3813357.042, 3554322.697, 3662865.799 96.3,-3813356.540, 3554323.353, 3662865.686 96.4,-3813356.042, 3554324.010, 3662865.568 96.5,-3813355.547, 3554324.670, 3662865.444 96.6,-3813355.057, 3554325.331, 3662865.314 96.7,-3813354.570, 3554325.993, 3662865.179 96.8,-3813354.087, 3554326.658, 3662865.038 96.9,-3813353.608, 3554327.323, 3662864.892 97.0,-3813353.132, 3554327.991, 3662864.740 97.1,-3813352.661, 3554328.660, 3662864.582 97.2,-3813352.194, 3554329.330, 3662864.419 97.3,-3813351.730, 3554330.002, 3662864.251 97.4,-3813351.271, 3554330.675, 3662864.077 97.5,-3813350.816, 3554331.350, 3662863.897 97.6,-3813350.365, 3554332.026, 3662863.713 97.7,-3813349.918, 3554332.703, 3662863.522 97.8,-3813349.475, 3554333.381, 3662863.327 97.9,-3813349.036, 3554334.060, 3662863.125 98.0,-3813348.602, 3554334.741, 3662862.919 98.1,-3813348.171, 3554335.422, 3662862.707 98.2,-3813347.746, 3554336.105, 3662862.489 98.3,-3813347.324, 3554336.788, 3662862.267 98.4,-3813346.907, 3554337.473, 3662862.039 98.5,-3813346.494, 3554338.158, 3662861.805 98.6,-3813346.085, 3554338.844, 3662861.567 98.7,-3813345.681, 3554339.530, 3662861.323 98.8,-3813345.281, 3554340.218, 3662861.073 98.9,-3813344.886, 3554340.906, 3662860.819 99.0,-3813344.496, 3554341.595, 3662860.559 99.1,-3813344.109, 3554342.284, 3662860.294 99.2,-3813343.728, 3554342.974, 3662860.024 99.3,-3813343.351, 3554343.664, 3662859.748 99.4,-3813342.978, 3554344.355, 3662859.467 99.5,-3813342.611, 3554345.046, 3662859.182 99.6,-3813342.248, 3554345.737, 3662858.891 99.7,-3813341.889, 3554346.429, 3662858.595 99.8,-3813341.536, 3554347.120, 3662858.294 99.9,-3813341.187, 3554347.813, 3662857.987 100.0,-3813340.842, 3554348.505, 3662857.676 100.1,-3813340.503, 3554349.197, 3662857.360 100.2,-3813340.168, 3554349.889, 3662857.039 100.3,-3813339.839, 3554350.582, 3662856.712 100.4,-3813339.514, 3554351.274, 3662856.381 100.5,-3813339.194, 3554351.966, 3662856.045 100.6,-3813338.879, 3554352.658, 3662855.704 100.7,-3813338.569, 3554353.350, 3662855.358 100.8,-3813338.263, 3554354.041, 3662855.007 100.9,-3813337.963, 3554354.732, 3662854.651 101.0,-3813337.668, 3554355.423, 3662854.291 101.1,-3813337.377, 3554356.114, 3662853.925 101.2,-3813337.092, 3554356.804, 3662853.555 101.3,-3813336.812, 3554357.493, 3662853.180 101.4,-3813336.537, 3554358.182, 3662852.801 101.5,-3813336.267, 3554358.870, 3662852.417 101.6,-3813336.002, 3554359.558, 3662852.028 101.7,-3813335.742, 3554360.245, 3662851.634 101.8,-3813335.487, 3554360.931, 3662851.236 101.9,-3813335.238, 3554361.617, 3662850.833 102.0,-3813334.993, 3554362.302, 3662850.426 102.1,-3813334.754, 3554362.986, 3662850.014 102.2,-3813334.520, 3554363.669, 3662849.598 102.3,-3813334.292, 3554364.350, 3662849.177 102.4,-3813334.068, 3554365.031, 3662848.752 102.5,-3813333.850, 3554365.711, 3662848.322 102.6,-3813333.637, 3554366.390, 3662847.888 102.7,-3813333.429, 3554367.068, 3662847.450 102.8,-3813333.227, 3554367.744, 3662847.007 102.9,-3813333.030, 3554368.419, 3662846.560 103.0,-3813332.838, 3554369.093, 3662846.109 103.1,-3813332.652, 3554369.765, 3662845.653 103.2,-3813332.471, 3554370.437, 3662845.194 103.3,-3813332.295, 3554371.106, 3662844.730 103.4,-3813332.125, 3554371.774, 3662844.262 103.5,-3813331.960, 3554372.441, 3662843.790 103.6,-3813331.800, 3554373.106, 3662843.314 103.7,-3813331.646, 3554373.770, 3662842.833 103.8,-3813331.498, 3554374.431, 3662842.349 103.9,-3813331.355, 3554375.091, 3662841.861 104.0,-3813331.217, 3554375.750, 3662841.369 104.1,-3813331.084, 3554376.406, 3662840.873 104.2,-3813330.958, 3554377.061, 3662840.373 104.3,-3813330.836, 3554377.713, 3662839.870 104.4,-3813330.720, 3554378.364, 3662839.362 104.5,-3813330.610, 3554379.013, 3662838.851 104.6,-3813330.505, 3554379.660, 3662838.336 104.7,-3813330.406, 3554380.304, 3662837.818 104.8,-3813330.312, 3554380.947, 3662837.295 104.9,-3813330.223, 3554381.587, 3662836.769 105.0,-3813330.141, 3554382.226, 3662836.240 105.1,-3813330.063, 3554382.861, 3662835.707 105.2,-3813329.992, 3554383.495, 3662835.170 105.3,-3813329.925, 3554384.126, 3662834.630 105.4,-3813329.865, 3554384.755, 3662834.087 105.5,-3813329.809, 3554385.382, 3662833.540 105.6,-3813329.760, 3554386.006, 3662832.990 105.7,-3813329.716, 3554386.627, 3662832.437 105.8,-3813329.677, 3554387.246, 3662831.880 105.9,-3813329.644, 3554387.862, 3662831.320 106.0,-3813329.617, 3554388.476, 3662830.757 106.1,-3813329.595, 3554389.087, 3662830.190 106.2,-3813329.579, 3554389.695, 3662829.621 106.3,-3813329.569, 3554390.300, 3662829.048 106.4,-3813329.563, 3554390.903, 3662828.473 106.5,-3813329.564, 3554391.503, 3662827.894 106.6,-3813329.570, 3554392.099, 3662827.313 106.7,-3813329.582, 3554392.693, 3662826.728 106.8,-3813329.599, 3554393.284, 3662826.141 106.9,-3813329.622, 3554393.872, 3662825.551 107.0,-3813329.650, 3554394.457, 3662824.958 107.1,-3813329.684, 3554395.039, 3662824.362 107.2,-3813329.723, 3554395.617, 3662823.763 107.3,-3813329.768, 3554396.192, 3662823.162 107.4,-3813329.819, 3554396.765, 3662822.558 107.5,-3813329.875, 3554397.333, 3662821.952 107.6,-3813329.937, 3554397.899, 3662821.343 107.7,-3813330.004, 3554398.461, 3662820.732 107.8,-3813330.077, 3554399.020, 3662820.118 107.9,-3813330.155, 3554399.575, 3662819.502 108.0,-3813330.239, 3554400.127, 3662818.883 108.1,-3813330.328, 3554400.676, 3662818.262 108.2,-3813330.423, 3554401.221, 3662817.638 108.3,-3813330.523, 3554401.762, 3662817.013 108.4,-3813330.629, 3554402.300, 3662816.385 108.5,-3813330.741, 3554402.834, 3662815.755 108.6,-3813330.858, 3554403.364, 3662815.123 108.7,-3813330.980, 3554403.891, 3662814.489 108.8,-3813331.108, 3554404.414, 3662813.853 108.9,-3813331.241, 3554404.933, 3662813.215 109.0,-3813331.380, 3554405.448, 3662812.575 109.1,-3813331.524, 3554405.959, 3662811.933 109.2,-3813331.674, 3554406.467, 3662811.289 109.3,-3813331.829, 3554406.970, 3662810.643 109.4,-3813331.989, 3554407.470, 3662809.996 109.5,-3813332.155, 3554407.965, 3662809.346 109.6,-3813332.326, 3554408.457, 3662808.696 109.7,-3813332.503, 3554408.944, 3662808.043 109.8,-3813332.685, 3554409.427, 3662807.389 109.9,-3813332.872, 3554409.907, 3662806.734 110.0,-3813333.065, 3554410.381, 3662806.076 110.1,-3813333.263, 3554410.852, 3662805.418 110.2,-3813333.466, 3554411.319, 3662804.758 110.3,-3813333.675, 3554411.781, 3662804.097 110.4,-3813333.889, 3554412.239, 3662803.434 110.5,-3813334.108, 3554412.692, 3662802.770 110.6,-3813334.332, 3554413.142, 3662802.105 110.7,-3813334.562, 3554413.586, 3662801.439 110.8,-3813334.797, 3554414.027, 3662800.772 110.9,-3813335.037, 3554414.463, 3662800.103 111.0,-3813335.282, 3554414.894, 3662799.434 111.1,-3813335.533, 3554415.321, 3662798.763 111.2,-3813335.788, 3554415.743, 3662798.092 111.3,-3813336.049, 3554416.161, 3662797.420 111.4,-3813336.315, 3554416.574, 3662796.747 111.5,-3813336.586, 3554416.982, 3662796.073 111.6,-3813336.862, 3554417.386, 3662795.398 111.7,-3813337.143, 3554417.785, 3662794.723 111.8,-3813337.429, 3554418.180, 3662794.047 111.9,-3813337.720, 3554418.569, 3662793.370 112.0,-3813338.016, 3554418.954, 3662792.693 112.1,-3813338.318, 3554419.334, 3662792.015 112.2,-3813338.624, 3554419.709, 3662791.337 112.3,-3813338.935, 3554420.080, 3662790.658 112.4,-3813339.251, 3554420.445, 3662789.979 112.5,-3813339.572, 3554420.805, 3662789.300 112.6,-3813339.897, 3554421.161, 3662788.620 112.7,-3813340.228, 3554421.512, 3662787.940 112.8,-3813340.563, 3554421.857, 3662787.260 112.9,-3813340.904, 3554422.198, 3662786.580 113.0,-3813341.249, 3554422.533, 3662785.900 113.1,-3813341.599, 3554422.864, 3662785.219 113.2,-3813341.953, 3554423.189, 3662784.539 113.3,-3813342.312, 3554423.510, 3662783.859 113.4,-3813342.676, 3554423.825, 3662783.179 113.5,-3813343.045, 3554424.135, 3662782.499 113.6,-3813343.418, 3554424.440, 3662781.819 113.7,-3813343.796, 3554424.740, 3662781.139 113.8,-3813344.178, 3554425.034, 3662780.460 113.9,-3813344.565, 3554425.323, 3662779.781 114.0,-3813344.957, 3554425.607, 3662779.102 114.1,-3813345.353, 3554425.886, 3662778.424 114.2,-3813345.753, 3554426.160, 3662777.746 114.3,-3813346.158, 3554426.428, 3662777.069 114.4,-3813346.567, 3554426.691, 3662776.392 114.5,-3813346.981, 3554426.948, 3662775.716 114.6,-3813347.399, 3554427.200, 3662775.041 114.7,-3813347.822, 3554427.447, 3662774.366 114.8,-3813348.248, 3554427.688, 3662773.693 114.9,-3813348.679, 3554427.924, 3662773.019 115.0,-3813349.114, 3554428.155, 3662772.347 115.1,-3813349.554, 3554428.380, 3662771.676 115.2,-3813349.997, 3554428.599, 3662771.005 115.3,-3813350.445, 3554428.813, 3662770.336 115.4,-3813350.897, 3554429.022, 3662769.668 115.5,-3813351.353, 3554429.225, 3662769.000 115.6,-3813351.813, 3554429.422, 3662768.334 115.7,-3813352.277, 3554429.614, 3662767.669 115.8,-3813352.745, 3554429.801, 3662767.005 115.9,-3813353.217, 3554429.982, 3662766.343 116.0,-3813353.693, 3554430.157, 3662765.682 116.1,-3813354.173, 3554430.327, 3662765.022 116.2,-3813354.657, 3554430.491, 3662764.363 116.3,-3813355.144, 3554430.650, 3662763.706 116.4,-3813355.636, 3554430.803, 3662763.051 116.5,-3813356.131, 3554430.950, 3662762.397 116.6,-3813356.629, 3554431.092, 3662761.745 116.7,-3813357.132, 3554431.227, 3662761.094 116.8,-3813357.638, 3554431.358, 3662760.445 116.9,-3813358.148, 3554431.482, 3662759.798 117.0,-3813358.661, 3554431.601, 3662759.152 117.1,-3813359.178, 3554431.715, 3662758.508 117.2,-3813359.698, 3554431.822, 3662757.866 117.3,-3813360.222, 3554431.924, 3662757.226 117.4,-3813360.750, 3554432.020, 3662756.588 117.5,-3813361.280, 3554432.111, 3662755.952 117.6,-3813361.814, 3554432.196, 3662755.318 117.7,-3813362.352, 3554432.275, 3662754.686 117.8,-3813362.893, 3554432.348, 3662754.056 117.9,-3813363.437, 3554432.416, 3662753.429 118.0,-3813363.984, 3554432.477, 3662752.803 118.1,-3813364.534, 3554432.533, 3662752.180 118.2,-3813365.088, 3554432.584, 3662751.559 118.3,-3813365.644, 3554432.628, 3662750.941 118.4,-3813366.204, 3554432.667, 3662750.324 118.5,-3813366.767, 3554432.700, 3662749.711 118.6,-3813367.332, 3554432.727, 3662749.099 118.7,-3813367.901, 3554432.749, 3662748.491 118.8,-3813368.473, 3554432.765, 3662747.884 118.9,-3813369.047, 3554432.775, 3662747.281 119.0,-3813369.624, 3554432.779, 3662746.680 119.1,-3813370.204, 3554432.777, 3662746.081 119.2,-3813370.787, 3554432.770, 3662745.486 119.3,-3813371.373, 3554432.757, 3662744.893 119.4,-3813371.961, 3554432.738, 3662744.303 119.5,-3813372.552, 3554432.713, 3662743.716 119.6,-3813373.145, 3554432.683, 3662743.131 119.7,-3813373.741, 3554432.647, 3662742.550 119.8,-3813374.339, 3554432.605, 3662741.971 119.9,-3813374.940, 3554432.557, 3662741.396 120.0,-3813375.543, 3554432.504, 3662740.824 120.1,-3813376.149, 3554432.444, 3662740.254 120.2,-3813376.757, 3554432.379, 3662739.688 120.3,-3813377.367, 3554432.309, 3662739.125 120.4,-3813377.980, 3554432.232, 3662738.565 120.5,-3813378.595, 3554432.150, 3662738.009 120.6,-3813379.212, 3554432.062, 3662737.455 120.7,-3813379.831, 3554431.969, 3662736.905 120.8,-3813380.452, 3554431.869, 3662736.359 120.9,-3813381.075, 3554431.764, 3662735.816 121.0,-3813381.700, 3554431.654, 3662735.276 121.1,-3813382.327, 3554431.537, 3662734.740 121.2,-3813382.956, 3554431.415, 3662734.207 121.3,-3813383.587, 3554431.287, 3662733.677 121.4,-3813384.220, 3554431.154, 3662733.152 121.5,-3813384.854, 3554431.015, 3662732.630 121.6,-3813385.490, 3554430.870, 3662732.111 121.7,-3813386.128, 3554430.720, 3662731.597 121.8,-3813386.768, 3554430.564, 3662731.086 121.9,-3813387.409, 3554430.402, 3662730.578 122.0,-3813388.052, 3554430.235, 3662730.075 122.1,-3813388.696, 3554430.062, 3662729.575 122.2,-3813389.341, 3554429.884, 3662729.080 122.3,-3813389.988, 3554429.700, 3662728.588 122.4,-3813390.637, 3554429.510, 3662728.100 122.5,-3813391.287, 3554429.315, 3662727.616 122.6,-3813391.938, 3554429.115, 3662727.136 122.7,-3813392.590, 3554428.909, 3662726.660 122.8,-3813393.243, 3554428.697, 3662726.188 122.9,-3813393.898, 3554428.480, 3662725.720 123.0,-3813394.554, 3554428.257, 3662725.257 123.1,-3813395.211, 3554428.029, 3662724.797 123.2,-3813395.868, 3554427.796, 3662724.342 123.3,-3813396.527, 3554427.557, 3662723.891 123.4,-3813397.187, 3554427.313, 3662723.444 123.5,-3813397.847, 3554427.063, 3662723.002 123.6,-3813398.509, 3554426.808, 3662722.564 123.7,-3813399.171, 3554426.548, 3662722.130 123.8,-3813399.834, 3554426.282, 3662721.700 123.9,-3813400.497, 3554426.011, 3662721.275 124.0,-3813401.162, 3554425.735, 3662720.855 124.1,-3813401.826, 3554425.453, 3662720.439 124.2,-3813402.492, 3554425.166, 3662720.027 124.3,-3813403.158, 3554424.874, 3662719.620 124.4,-3813403.824, 3554424.577, 3662719.218 124.5,-3813404.491, 3554424.274, 3662718.820 124.6,-3813405.158, 3554423.967, 3662718.426 124.7,-3813405.825, 3554423.654, 3662718.038 124.8,-3813406.493, 3554423.336, 3662717.654 124.9,-3813407.161, 3554423.013, 3662717.274 125.0,-3813407.829, 3554422.684, 3662716.900 125.1,-3813408.497, 3554422.351, 3662716.530 125.2,-3813409.166, 3554422.013, 3662716.165 125.3,-3813409.834, 3554421.669, 3662715.805 125.4,-3813410.503, 3554421.321, 3662715.449 125.5,-3813411.171, 3554420.968, 3662715.099 125.6,-3813411.839, 3554420.609, 3662714.753 125.7,-3813412.507, 3554420.246, 3662714.412 125.8,-3813413.175, 3554419.878, 3662714.076 125.9,-3813413.843, 3554419.505, 3662713.745 126.0,-3813414.510, 3554419.127, 3662713.419 126.1,-3813415.177, 3554418.745, 3662713.098 126.2,-3813415.844, 3554418.357, 3662712.782 126.3,-3813416.510, 3554417.965, 3662712.471 126.4,-3813417.176, 3554417.568, 3662712.165 126.5,-3813417.841, 3554417.166, 3662711.864 126.6,-3813418.506, 3554416.760, 3662711.569 126.7,-3813419.170, 3554416.349, 3662711.278 126.8,-3813419.834, 3554415.934, 3662710.992 126.9,-3813420.496, 3554415.513, 3662710.712 127.0,-3813421.158, 3554415.089, 3662710.437 127.1,-3813421.820, 3554414.659, 3662710.167 127.2,-3813422.480, 3554414.225, 3662709.902 127.3,-3813423.140, 3554413.787, 3662709.643 127.4,-3813423.798, 3554413.344, 3662709.388 127.5,-3813424.456, 3554412.897, 3662709.139 127.6,-3813425.112, 3554412.446, 3662708.896 127.7,-3813425.768, 3554411.990, 3662708.657 127.8,-3813426.422, 3554411.529, 3662708.424 127.9,-3813427.076, 3554411.065, 3662708.196 128.0,-3813427.728, 3554410.596, 3662707.974 128.1,-3813428.378, 3554410.123, 3662707.757 128.2,-3813429.028, 3554409.646, 3662707.545 128.3,-3813429.676, 3554409.164, 3662707.339 128.4,-3813430.323, 3554408.679, 3662707.138 128.5,-3813430.968, 3554408.189, 3662706.942 128.6,-3813431.612, 3554407.695, 3662706.752 128.7,-3813432.255, 3554407.198, 3662706.568 128.8,-3813432.895, 3554406.696, 3662706.389 128.9,-3813433.535, 3554406.190, 3662706.215 129.0,-3813434.172, 3554405.681, 3662706.047 129.1,-3813434.808, 3554405.167, 3662705.884 129.2,-3813435.442, 3554404.650, 3662705.727 129.3,-3813436.075, 3554404.129, 3662705.576 129.4,-3813436.705, 3554403.604, 3662705.429 129.5,-3813437.334, 3554403.075, 3662705.289 129.6,-3813437.960, 3554402.543, 3662705.154 129.7,-3813438.585, 3554402.007, 3662705.024 129.8,-3813439.208, 3554401.467, 3662704.901 129.9,-3813439.829, 3554400.924, 3662704.782 130.0,-3813440.447, 3554400.377, 3662704.670 130.1,-3813441.064, 3554399.827, 3662704.563 130.2,-3813441.678, 3554399.273, 3662704.461 130.3,-3813442.290, 3554398.716, 3662704.365 130.4,-3813442.900, 3554398.155, 3662704.275 130.5,-3813443.508, 3554397.591, 3662704.190 130.6,-3813444.113, 3554397.024, 3662704.111 130.7,-3813444.716, 3554396.453, 3662704.038 130.8,-3813445.316, 3554395.879, 3662703.970 130.9,-3813445.914, 3554395.302, 3662703.908 131.0,-3813446.510, 3554394.722, 3662703.852 131.1,-3813447.103, 3554394.138, 3662703.801 131.2,-3813447.693, 3554393.552, 3662703.756 131.3,-3813448.280, 3554392.962, 3662703.716 131.4,-3813448.865, 3554392.370, 3662703.682 131.5,-3813449.448, 3554391.774, 3662703.654 131.6,-3813450.027, 3554391.176, 3662703.632 131.7,-3813450.604, 3554390.574, 3662703.615 131.8,-3813451.178, 3554389.970, 3662703.604 131.9,-3813451.749, 3554389.363, 3662703.598 132.0,-3813452.317, 3554388.754, 3662703.598 132.1,-3813452.882, 3554388.141, 3662703.604 132.2,-3813453.444, 3554387.526, 3662703.616 132.3,-3813454.003, 3554386.909, 3662703.633 132.4,-3813454.559, 3554386.288, 3662703.656 132.5,-3813455.112, 3554385.666, 3662703.684 132.6,-3813455.662, 3554385.040, 3662703.718 132.7,-3813456.209, 3554384.412, 3662703.758 132.8,-3813456.752, 3554383.782, 3662703.804 132.9,-3813457.292, 3554383.150, 3662703.855 133.0,-3813457.829, 3554382.515, 3662703.912 133.1,-3813458.362, 3554381.878, 3662703.974 133.2,-3813458.893, 3554381.238, 3662704.042 133.3,-3813459.419, 3554380.597, 3662704.116 133.4,-3813459.942, 3554379.953, 3662704.195 133.5,-3813460.462, 3554379.307, 3662704.280 133.6,-3813460.978, 3554378.659, 3662704.371 133.7,-3813461.491, 3554378.010, 3662704.467 133.8,-3813462.000, 3554377.358, 3662704.569 133.9,-3813462.506, 3554376.704, 3662704.676 134.0,-3813463.007, 3554376.048, 3662704.790 134.1,-3813463.506, 3554375.391, 3662704.908 134.2,-3813464.000, 3554374.732, 3662705.032 134.3,-3813464.491, 3554374.071, 3662705.162 134.4,-3813464.977, 3554373.408, 3662705.297 134.5,-3813465.460, 3554372.744, 3662705.438 134.6,-3813465.939, 3554372.078, 3662705.585 134.7,-3813466.415, 3554371.410, 3662705.737 134.8,-3813466.886, 3554370.741, 3662705.894 134.9,-3813467.353, 3554370.071, 3662706.057 135.0,-3813467.816, 3554369.399, 3662706.226 135.1,-3813468.276, 3554368.726, 3662706.400 135.2,-3813468.731, 3554368.051, 3662706.579 135.3,-3813469.182, 3554367.375, 3662706.764 135.4,-3813469.629, 3554366.698, 3662706.954 135.5,-3813470.072, 3554366.020, 3662707.150 135.6,-3813470.511, 3554365.341, 3662707.351 135.7,-3813470.945, 3554364.660, 3662707.558 135.8,-3813471.375, 3554363.979, 3662707.770 135.9,-3813471.801, 3554363.296, 3662707.987 136.0,-3813472.223, 3554362.613, 3662708.210 136.1,-3813472.640, 3554361.928, 3662708.438 136.2,-3813473.053, 3554361.243, 3662708.672 136.3,-3813473.461, 3554360.557, 3662708.911 136.4,-3813473.865, 3554359.870, 3662709.155 136.5,-3813474.265, 3554359.183, 3662709.404 136.6,-3813474.660, 3554358.495, 3662709.659 136.7,-3813475.051, 3554357.806, 3662709.918 136.8,-3813475.437, 3554357.117, 3662710.184 136.9,-3813475.818, 3554356.427, 3662710.454 137.0,-3813476.195, 3554355.737, 3662710.729 137.1,-3813476.568, 3554355.046, 3662711.010 137.2,-3813476.935, 3554354.355, 3662711.296 137.3,-3813477.298, 3554353.664, 3662711.587 137.4,-3813477.657, 3554352.972, 3662711.883 137.5,-3813478.010, 3554352.280, 3662712.184 137.6,-3813478.359, 3554351.588, 3662712.490 137.7,-3813478.703, 3554350.896, 3662712.802 137.8,-3813479.043, 3554350.204, 3662713.118 137.9,-3813479.377, 3554349.512, 3662713.439 138.0,-3813479.707, 3554348.819, 3662713.766 138.1,-3813480.032, 3554348.127, 3662714.097 138.2,-3813480.352, 3554347.435, 3662714.433 138.3,-3813480.667, 3554346.743, 3662714.774 138.4,-3813480.977, 3554346.051, 3662715.120 138.5,-3813481.282, 3554345.360, 3662715.471 138.6,-3813481.582, 3554344.669, 3662715.827 138.7,-3813481.878, 3554343.978, 3662716.188 138.8,-3813482.168, 3554343.287, 3662716.553 138.9,-3813482.453, 3554342.597, 3662716.923 139.0,-3813482.733, 3554341.908, 3662717.298 139.1,-3813483.008, 3554341.219, 3662717.678 139.2,-3813483.278, 3554340.530, 3662718.062 139.3,-3813483.543, 3554339.843, 3662718.451 139.4,-3813483.803, 3554339.156, 3662718.844 139.5,-3813484.058, 3554338.469, 3662719.243 139.6,-3813484.307, 3554337.784, 3662719.645 139.7,-3813484.552, 3554337.099, 3662720.053 139.8,-3813484.791, 3554336.415, 3662720.465 139.9,-3813485.025, 3554335.732, 3662720.881 140.0,-3813485.253, 3554335.050, 3662721.302 140.1,-3813485.477, 3554334.370, 3662721.727 140.2,-3813485.695, 3554333.690, 3662722.157 140.3,-3813485.908, 3554333.011, 3662722.591 140.4,-3813486.115, 3554332.333, 3662723.029 140.5,-3813486.318, 3554331.657, 3662723.472 140.6,-3813486.515, 3554330.982, 3662723.919 140.7,-3813486.706, 3554330.308, 3662724.370 140.8,-3813486.893, 3554329.635, 3662724.826 140.9,-3813487.074, 3554328.964, 3662725.285 141.0,-3813487.249, 3554328.295, 3662725.749 141.1,-3813487.420, 3554327.627, 3662726.217 141.2,-3813487.584, 3554326.960, 3662726.689 141.3,-3813487.744, 3554326.295, 3662727.166 141.4,-3813487.898, 3554325.631, 3662727.646 141.5,-3813488.046, 3554324.970, 3662728.130 141.6,-3813488.190, 3554324.310, 3662728.618 141.7,-3813488.327, 3554323.651, 3662729.110 141.8,-3813488.460, 3554322.995, 3662729.606 141.9,-3813488.586, 3554322.340, 3662730.106 142.0,-3813488.708, 3554321.688, 3662730.610 142.1,-3813488.823, 3554321.037, 3662731.117 142.2,-3813488.934, 3554320.388, 3662731.628 142.3,-3813489.039, 3554319.741, 3662732.143 142.4,-3813489.138, 3554319.097, 3662732.662 142.5,-3813489.232, 3554318.454, 3662733.184 142.6,-3813489.320, 3554317.814, 3662733.710 142.7,-3813489.403, 3554317.176, 3662734.240 142.8,-3813489.480, 3554316.540, 3662734.773 142.9,-3813489.552, 3554315.906, 3662735.309 143.0,-3813489.618, 3554315.275, 3662735.849 143.1,-3813489.679, 3554314.646, 3662736.393 143.2,-3813489.734, 3554314.020, 3662736.940 143.3,-3813489.783, 3554313.396, 3662737.490 143.4,-3813489.827, 3554312.774, 3662738.043 143.5,-3813489.866, 3554312.156, 3662738.600 143.6,-3813489.899, 3554311.539, 3662739.160 143.7,-3813489.926, 3554310.926, 3662739.723 143.8,-3813489.948, 3554310.315, 3662740.290 143.9,-3813489.964, 3554309.707, 3662740.859 144.0,-3813489.974, 3554309.101, 3662741.432 144.1,-3813489.979, 3554308.499, 3662742.007 144.2,-3813489.979, 3554307.899, 3662742.586 144.3,-3813489.973, 3554307.302, 3662743.168 144.4,-3813489.961, 3554306.708, 3662743.752 144.5,-3813489.944, 3554306.118, 3662744.339 144.6,-3813489.921, 3554305.530, 3662744.930 144.7,-3813489.893, 3554304.945, 3662745.523 144.8,-3813489.859, 3554304.363, 3662746.118 144.9,-3813489.819, 3554303.785, 3662746.717 145.0,-3813489.774, 3554303.209, 3662747.318 145.1,-3813489.723, 3554302.637, 3662747.922 145.2,-3813489.667, 3554302.068, 3662748.528 145.3,-3813489.606, 3554301.503, 3662749.137 145.4,-3813489.538, 3554300.941, 3662749.749 145.5,-3813489.465, 3554300.382, 3662750.363 145.6,-3813489.387, 3554299.827, 3662750.979 145.7,-3813489.303, 3554299.275, 3662751.598 145.8,-3813489.214, 3554298.726, 3662752.219 145.9,-3813489.119, 3554298.181, 3662752.842 146.0,-3813489.018, 3554297.640, 3662753.468 146.1,-3813488.913, 3554297.103, 3662754.095 146.2,-3813488.801, 3554296.569, 3662754.725 146.3,-3813488.684, 3554296.038, 3662755.358 146.4,-3813488.562, 3554295.512, 3662755.992 146.5,-3813488.434, 3554294.989, 3662756.628 146.6,-3813488.301, 3554294.470, 3662757.266 146.7,-3813488.162, 3554293.955, 3662757.906 146.8,-3813488.018, 3554293.443, 3662758.548 146.9,-3813487.868, 3554292.936, 3662759.192 147.0,-3813487.713, 3554292.432, 3662759.838 147.1,-3813487.552, 3554291.933, 3662760.485 147.2,-3813487.386, 3554291.437, 3662761.134 147.3,-3813487.215, 3554290.946, 3662761.785 147.4,-3813487.038, 3554290.459, 3662762.438 147.5,-3813486.856, 3554289.975, 3662763.092 147.6,-3813486.669, 3554289.496, 3662763.747 147.7,-3813486.476, 3554289.021, 3662764.404 147.8,-3813486.278, 3554288.551, 3662765.063 147.9,-3813486.075, 3554288.084, 3662765.723 148.0,-3813485.866, 3554287.622, 3662766.384 148.1,-3813485.652, 3554287.164, 3662767.047 148.2,-3813485.433, 3554286.711, 3662767.711 148.3,-3813485.209, 3554286.261, 3662768.376 148.4,-3813484.979, 3554285.817, 3662769.042 148.5,-3813484.744, 3554285.376, 3662769.709 148.6,-3813484.504, 3554284.941, 3662770.378 148.7,-3813484.259, 3554284.509, 3662771.047 148.8,-3813484.008, 3554284.082, 3662771.718 148.9,-3813483.752, 3554283.660, 3662772.389 149.0,-3813483.492, 3554283.242, 3662773.061 149.1,-3813483.226, 3554282.829, 3662773.734 149.2,-3813482.955, 3554282.421, 3662774.408 149.3,-3813482.679, 3554282.017, 3662775.083 149.4,-3813482.397, 3554281.618, 3662775.758 149.5,-3813482.111, 3554281.224, 3662776.434 149.6,-3813481.820, 3554280.834, 3662777.111 149.7,-3813481.524, 3554280.450, 3662777.788 149.8,-3813481.223, 3554280.070, 3662778.466 149.9,-3813480.916, 3554279.694, 3662779.144 150.0,-3813480.605, 3554279.324, 3662779.823 150.1,-3813480.289, 3554278.959, 3662780.502 150.2,-3813479.968, 3554278.598, 3662781.181 150.3,-3813479.643, 3554278.243, 3662781.861 150.4,-3813479.312, 3554277.892, 3662782.541 150.5,-3813478.976, 3554277.547, 3662783.221 150.6,-3813478.636, 3554277.206, 3662783.901 150.7,-3813478.291, 3554276.871, 3662784.581 150.8,-3813477.941, 3554276.540, 3662785.262 150.9,-3813477.587, 3554276.215, 3662785.942 151.0,-3813477.227, 3554275.894, 3662786.622 151.1,-3813476.863, 3554275.579, 3662787.302 151.2,-3813476.495, 3554275.269, 3662787.982 151.3,-3813476.121, 3554274.964, 3662788.662 151.4,-3813475.744, 3554274.665, 3662789.342 151.5,-3813475.361, 3554274.370, 3662790.021 151.6,-3813474.974, 3554274.081, 3662790.700 151.7,-3813474.583, 3554273.797, 3662791.379 151.8,-3813474.187, 3554273.518, 3662792.057 151.9,-3813473.786, 3554273.245, 3662792.735 152.0,-3813473.381, 3554272.977, 3662793.412 152.1,-3813472.972, 3554272.714, 3662794.089 152.2,-3813472.558, 3554272.457, 3662794.765 152.3,-3813472.140, 3554272.205, 3662795.440 152.4,-3813471.718, 3554271.958, 3662796.115 152.5,-3813471.291, 3554271.717, 3662796.788 152.6,-3813470.860, 3554271.481, 3662797.462 152.7,-3813470.425, 3554271.250, 3662798.134 152.8,-3813469.985, 3554271.025, 3662798.805 152.9,-3813469.541, 3554270.806, 3662799.475 153.0,-3813469.094, 3554270.592, 3662800.145 153.1,-3813468.642, 3554270.383, 3662800.813 153.2,-3813468.186, 3554270.180, 3662801.481 153.3,-3813467.726, 3554269.983, 3662802.147 153.4,-3813467.262, 3554269.791, 3662802.812 153.5,-3813466.793, 3554269.604, 3662803.475 153.6,-3813466.321, 3554269.424, 3662804.138 153.7,-3813465.845, 3554269.248, 3662804.799 153.8,-3813465.366, 3554269.079, 3662805.459 153.9,-3813464.882, 3554268.914, 3662806.117 154.0,-3813464.394, 3554268.756, 3662806.774 154.1,-3813463.903, 3554268.603, 3662807.430 154.2,-3813463.408, 3554268.456, 3662808.084 154.3,-3813462.909, 3554268.314, 3662808.736 154.4,-3813462.406, 3554268.178, 3662809.387 154.5,-3813461.900, 3554268.048, 3662810.036 154.6,-3813461.391, 3554267.923, 3662810.683 154.7,-3813460.877, 3554267.804, 3662811.329 154.8,-3813460.360, 3554267.691, 3662811.972 154.9,-3813459.840, 3554267.584, 3662812.614 155.0,-3813459.316, 3554267.482, 3662813.254 155.1,-3813458.788, 3554267.386, 3662813.892 155.2,-3813458.258, 3554267.295, 3662814.528 155.3,-3813457.724, 3554267.211, 3662815.162 155.4,-3813457.186, 3554267.132, 3662815.794 155.5,-3813456.645, 3554267.058, 3662816.424 155.6,-3813456.101, 3554266.991, 3662817.052 155.7,-3813455.554, 3554266.929, 3662817.677 155.8,-3813455.004, 3554266.873, 3662818.300 155.9,-3813454.450, 3554266.823, 3662818.921 156.0,-3813453.894, 3554266.778, 3662819.540 156.1,-3813453.334, 3554266.740, 3662820.156 156.2,-3813452.771, 3554266.707, 3662820.770 156.3,-3813452.205, 3554266.679, 3662821.381 156.4,-3813451.637, 3554266.658, 3662821.990 156.5,-3813451.065, 3554266.642, 3662822.596 156.6,-3813450.491, 3554266.632, 3662823.200 156.7,-3813449.913, 3554266.628, 3662823.801 156.8,-3813449.333, 3554266.630, 3662824.399 156.9,-3813448.751, 3554266.637, 3662824.995 157.0,-3813448.165, 3554266.650, 3662825.587 157.1,-3813447.577, 3554266.669, 3662826.177 157.2,-3813446.986, 3554266.694, 3662826.765 157.3,-3813446.393, 3554266.724, 3662827.349 157.4,-3813445.797, 3554266.761, 3662827.930 157.5,-3813445.198, 3554266.803, 3662828.509 157.6,-3813444.597, 3554266.850, 3662829.084 157.7,-3813443.994, 3554266.904, 3662829.656 157.8,-3813443.388, 3554266.963, 3662830.226 157.9,-3813442.780, 3554267.028, 3662830.792 158.0,-3813442.170, 3554267.099, 3662831.355 158.1,-3813441.557, 3554267.175, 3662831.915 158.2,-3813440.943, 3554267.258, 3662832.471 158.3,-3813440.326, 3554267.345, 3662833.024 158.4,-3813439.707, 3554267.439, 3662833.574 158.5,-3813439.086, 3554267.538, 3662834.121 158.6,-3813438.462, 3554267.644, 3662834.664 158.7,-3813437.837, 3554267.754, 3662835.204 158.8,-3813437.210, 3554267.871, 3662835.740 158.9,-3813436.581, 3554267.993, 3662836.273 159.0,-3813435.950, 3554268.121, 3662836.802 159.1,-3813435.318, 3554268.254, 3662837.328 159.2,-3813434.683, 3554268.393, 3662837.850 159.3,-3813434.047, 3554268.538, 3662838.368 159.4,-3813433.409, 3554268.688, 3662838.883 159.5,-3813432.769, 3554268.845, 3662839.394 159.6,-3813432.128, 3554269.006, 3662839.901 159.7,-3813431.486, 3554269.173, 3662840.405 159.8,-3813430.841, 3554269.346, 3662840.904 159.9,-3813430.196, 3554269.525, 3662841.400 160.0,-3813429.549, 3554269.709, 3662841.892 160.1,-3813428.900, 3554269.898, 3662842.380 160.2,-3813428.250, 3554270.093, 3662842.863 160.3,-3813427.599, 3554270.294, 3662843.343 160.4,-3813426.947, 3554270.500, 3662843.819 160.5,-3813426.294, 3554270.712, 3662844.291 160.6,-3813425.639, 3554270.929, 3662844.759 160.7,-3813424.983, 3554271.151, 3662845.222 160.8,-3813424.326, 3554271.380, 3662845.682 160.9,-3813423.669, 3554271.613, 3662846.137 161.0,-3813423.010, 3554271.852, 3662846.588 161.1,-3813422.350, 3554272.096, 3662847.035 161.2,-3813421.690, 3554272.346, 3662847.477 161.3,-3813421.028, 3554272.601, 3662847.915 161.4,-3813420.366, 3554272.861, 3662848.349 161.5,-3813419.703, 3554273.127, 3662848.778 161.6,-3813419.040, 3554273.398, 3662849.203 161.7,-3813418.375, 3554273.675, 3662849.624 161.8,-3813417.710, 3554273.956, 3662850.040 161.9,-3813417.045, 3554274.243, 3662850.451 162.0,-3813416.379, 3554274.535, 3662850.858 162.1,-3813415.713, 3554274.833, 3662851.261 162.2,-3813415.046, 3554275.135, 3662851.659 162.3,-3813414.379, 3554275.443, 3662852.052 162.4,-3813413.712, 3554275.756, 3662852.441 162.5,-3813413.044, 3554276.074, 3662852.825 162.6,-3813412.376, 3554276.397, 3662853.204 162.7,-3813411.708, 3554276.726, 3662853.578 162.8,-3813411.039, 3554277.059, 3662853.948 162.9,-3813410.371, 3554277.397, 3662854.313 163.0,-3813409.703, 3554277.741, 3662854.673 163.1,-3813409.034, 3554278.089, 3662855.029 163.2,-3813408.366, 3554278.442, 3662855.379 163.3,-3813407.698, 3554278.801, 3662855.725 163.4,-3813407.030, 3554279.164, 3662856.066 163.5,-3813406.362, 3554279.532, 3662856.402 163.6,-3813405.694, 3554279.905, 3662856.733 163.7,-3813405.027, 3554280.283, 3662857.059 163.8,-3813404.360, 3554280.666, 3662857.380 163.9,-3813403.693, 3554281.053, 3662857.696 164.0,-3813403.027, 3554281.445, 3662858.007 164.1,-3813402.361, 3554281.842, 3662858.312 164.2,-3813401.696, 3554282.244, 3662858.613 164.3,-3813401.031, 3554282.650, 3662858.909 164.4,-3813400.367, 3554283.061, 3662859.200 164.5,-3813399.703, 3554283.477, 3662859.485 164.6,-3813399.040, 3554283.897, 3662859.765 164.7,-3813398.378, 3554284.322, 3662860.040 164.8,-3813397.717, 3554284.752, 3662860.310 164.9,-3813397.057, 3554285.185, 3662860.575 165.0,-3813396.397, 3554285.624, 3662860.835 165.1,-3813395.739, 3554286.067, 3662861.089 165.2,-3813395.081, 3554286.514, 3662861.338 165.3,-3813394.425, 3554286.965, 3662861.582 165.4,-3813393.769, 3554287.421, 3662861.820 165.5,-3813393.115, 3554287.882, 3662862.053 165.6,-3813392.461, 3554288.346, 3662862.281 165.7,-3813391.809, 3554288.815, 3662862.503 165.8,-3813391.159, 3554289.288, 3662862.720 165.9,-3813390.509, 3554289.766, 3662862.932 166.0,-3813389.861, 3554290.247, 3662863.138 166.1,-3813389.214, 3554290.733, 3662863.339 166.2,-3813388.569, 3554291.222, 3662863.534 166.3,-3813387.925, 3554291.716, 3662863.724 166.4,-3813387.283, 3554292.214, 3662863.909 166.5,-3813386.642, 3554292.715, 3662864.088 166.6,-3813386.003, 3554293.221, 3662864.261 166.7,-3813385.365, 3554293.731, 3662864.430 166.8,-3813384.729, 3554294.244, 3662864.592 166.9,-3813384.095, 3554294.762, 3662864.749 167.0,-3813383.463, 3554295.283, 3662864.901 167.1,-3813382.832, 3554295.808, 3662865.047 167.2,-3813382.204, 3554296.336, 3662865.187 167.3,-3813381.577, 3554296.869, 3662865.322 167.4,-3813380.952, 3554297.405, 3662865.452 167.5,-3813380.329, 3554297.945, 3662865.575 167.6,-3813379.709, 3554298.488, 3662865.694 167.7,-3813379.090, 3554299.035, 3662865.806 167.8,-3813378.473, 3554299.585, 3662865.913 167.9,-3813377.859, 3554300.139, 3662866.015 168.0,-3813377.247, 3554300.696, 3662866.111 168.1,-3813376.637, 3554301.257, 3662866.201 168.2,-3813376.030, 3554301.821, 3662866.285 168.3,-3813375.424, 3554302.388, 3662866.364 168.4,-3813374.822, 3554302.959, 3662866.438 168.5,-3813374.221, 3554303.533, 3662866.505 168.6,-3813373.623, 3554304.110, 3662866.567 168.7,-3813373.028, 3554304.691, 3662866.624 168.8,-3813372.435, 3554305.274, 3662866.675 168.9,-3813371.845, 3554305.860, 3662866.720 169.0,-3813371.257, 3554306.450, 3662866.759 169.1,-3813370.672, 3554307.043, 3662866.793 169.2,-3813370.090, 3554307.638, 3662866.821 169.3,-3813369.510, 3554308.237, 3662866.844 169.4,-3813368.934, 3554308.838, 3662866.860 169.5,-3813368.360, 3554309.442, 3662866.871 169.6,-3813367.789, 3554310.049, 3662866.877 169.7,-3813367.221, 3554310.659, 3662866.877 169.8,-3813366.656, 3554311.271, 3662866.871 169.9,-3813366.094, 3554311.886, 3662866.859 170.0,-3813365.535, 3554312.504, 3662866.842 170.1,-3813364.978, 3554313.124, 3662866.819 170.2,-3813364.426, 3554313.747, 3662866.790 170.3,-3813363.876, 3554314.372, 3662866.756 170.4,-3813363.329, 3554315.000, 3662866.716 170.5,-3813362.786, 3554315.630, 3662866.671 170.6,-3813362.246, 3554316.263, 3662866.620 170.7,-3813361.709, 3554316.898, 3662866.563 170.8,-3813361.176, 3554317.535, 3662866.500 170.9,-3813360.646, 3554318.174, 3662866.432 171.0,-3813360.119, 3554318.816, 3662866.358 171.1,-3813359.596, 3554319.460, 3662866.279 171.2,-3813359.076, 3554320.105, 3662866.194 171.3,-3813358.560, 3554320.753, 3662866.103 171.4,-3813358.047, 3554321.403, 3662866.007 171.5,-3813357.538, 3554322.055, 3662865.905 171.6,-3813357.033, 3554322.709, 3662865.798 171.7,-3813356.531, 3554323.365, 3662865.684 171.8,-3813356.033, 3554324.022, 3662865.566 171.9,-3813355.539, 3554324.681, 3662865.441 172.0,-3813355.048, 3554325.342, 3662865.312 172.1,-3813354.561, 3554326.005, 3662865.176 172.2,-3813354.078, 3554326.669, 3662865.035 172.3,-3813353.599, 3554327.335, 3662864.889 172.4,-3813353.124, 3554328.003, 3662864.737 172.5,-3813352.653, 3554328.672, 3662864.579 172.6,-3813352.186, 3554329.342, 3662864.416 172.7,-3813351.722, 3554330.014, 3662864.248 172.8,-3813351.263, 3554330.687, 3662864.074 172.9,-3813350.808, 3554331.362, 3662863.894 173.0,-3813350.357, 3554332.038, 3662863.709 173.1,-3813349.910, 3554332.715, 3662863.519 173.2,-3813349.467, 3554333.393, 3662863.323 173.3,-3813349.028, 3554334.072, 3662863.122 173.4,-3813348.594, 3554334.753, 3662862.915 173.5,-3813348.164, 3554335.434, 3662862.703 173.6,-3813347.738, 3554336.117, 3662862.486 173.7,-3813347.316, 3554336.800, 3662862.263 173.8,-3813346.899, 3554337.485, 3662862.035 173.9,-3813346.486, 3554338.170, 3662861.801 174.0,-3813346.078, 3554338.856, 3662861.562 174.1,-3813345.674, 3554339.543, 3662861.318 174.2,-3813345.274, 3554340.230, 3662861.069 174.3,-3813344.879, 3554340.918, 3662860.814 174.4,-3813344.489, 3554341.607, 3662860.554 174.5,-3813344.103, 3554342.296, 3662860.289 174.6,-3813343.721, 3554342.986, 3662860.019 174.7,-3813343.344, 3554343.676, 3662859.743 174.8,-3813342.972, 3554344.367, 3662859.462 174.9,-3813342.604, 3554345.058, 3662859.177 175.0,-3813342.241, 3554345.749, 3662858.886 175.1,-3813341.883, 3554346.441, 3662858.589 175.2,-3813341.529, 3554347.133, 3662858.288 175.3,-3813341.180, 3554347.825, 3662857.982 175.4,-3813340.836, 3554348.517, 3662857.671 175.5,-3813340.497, 3554349.209, 3662857.354 175.6,-3813340.163, 3554349.902, 3662857.033 175.7,-3813339.833, 3554350.594, 3662856.706 175.8,-3813339.508, 3554351.286, 3662856.375 175.9,-3813339.188, 3554351.978, 3662856.039 176.0,-3813338.873, 3554352.670, 3662855.698 176.1,-3813338.563, 3554353.362, 3662855.352 176.2,-3813338.258, 3554354.053, 3662855.001 176.3,-3813337.958, 3554354.744, 3662854.645 176.4,-3813337.663, 3554355.435, 3662854.284 176.5,-3813337.372, 3554356.126, 3662853.919 176.6,-3813337.087, 3554356.816, 3662853.549 176.7,-3813336.807, 3554357.505, 3662853.174 176.8,-3813336.532, 3554358.194, 3662852.794 176.9,-3813336.262, 3554358.883, 3662852.410 177.0,-3813335.997, 3554359.570, 3662852.021 177.1,-3813335.737, 3554360.257, 3662851.627 177.2,-3813335.483, 3554360.944, 3662851.229 177.3,-3813335.233, 3554361.629, 3662850.826 177.4,-3813334.989, 3554362.314, 3662850.419 177.5,-3813334.750, 3554362.998, 3662850.007 177.6,-3813334.516, 3554363.681, 3662849.590 177.7,-3813334.288, 3554364.363, 3662849.169 177.8,-3813334.064, 3554365.043, 3662848.744 177.9,-3813333.846, 3554365.723, 3662848.314 178.0,-3813333.633, 3554366.402, 3662847.880 178.1,-3813333.426, 3554367.080, 3662847.442 178.2,-3813333.223, 3554367.756, 3662846.999 178.3,-3813333.026, 3554368.431, 3662846.552 178.4,-3813332.835, 3554369.105, 3662846.101 178.5,-3813332.648, 3554369.777, 3662845.645 178.6,-3813332.468, 3554370.448, 3662845.185 178.7,-3813332.292, 3554371.118, 3662844.722 178.8,-3813332.122, 3554371.786, 3662844.253 178.9,-3813331.957, 3554372.453, 3662843.781 179.0,-3813331.798, 3554373.118, 3662843.305 179.1,-3813331.644, 3554373.781, 3662842.825 179.2,-3813331.495, 3554374.443, 3662842.341 179.3,-3813331.352, 3554375.103, 3662841.852 179.4,-3813331.214, 3554375.761, 3662841.360 179.5,-3813331.082, 3554376.418, 3662840.864 179.6,-3813330.955, 3554377.072, 3662840.364 179.7,-3813330.834, 3554377.725, 3662839.861 179.8,-3813330.718, 3554378.376, 3662839.353 179.9,-3813330.608, 3554379.025, 3662838.842 180.0,-3813330.503, 3554379.671, 3662838.327 180.1,-3813330.404, 3554380.316, 3662837.808 180.2,-3813330.310, 3554380.958, 3662837.286 180.3,-3813330.222, 3554381.599, 3662836.760 180.4,-3813330.139, 3554382.237, 3662836.230 180.5,-3813330.062, 3554382.873, 3662835.697 180.6,-3813329.990, 3554383.506, 3662835.161 180.7,-3813329.924, 3554384.138, 3662834.621 180.8,-3813329.864, 3554384.766, 3662834.077 180.9,-3813329.809, 3554385.393, 3662833.531 181.0,-3813329.759, 3554386.017, 3662832.980 181.1,-3813329.715, 3554386.638, 3662832.427 181.2,-3813329.677, 3554387.257, 3662831.870 181.3,-3813329.644, 3554387.873, 3662831.310 181.4,-3813329.617, 3554388.487, 3662830.747 181.5,-3813329.595, 3554389.098, 3662830.180 181.6,-3813329.579, 3554389.706, 3662829.611 181.7,-3813329.568, 3554390.311, 3662829.038 181.8,-3813329.563, 3554390.914, 3662828.463 181.9,-3813329.564, 3554391.513, 3662827.884 182.0,-3813329.570, 3554392.110, 3662827.302 182.1,-3813329.582, 3554392.704, 3662826.718 182.2,-3813329.599, 3554393.295, 3662826.130 182.3,-3813329.622, 3554393.882, 3662825.540 182.4,-3813329.651, 3554394.467, 3662824.947 182.5,-3813329.685, 3554395.049, 3662824.351 182.6,-3813329.724, 3554395.627, 3662823.753 182.7,-3813329.769, 3554396.203, 3662823.152 182.8,-3813329.820, 3554396.775, 3662822.548 182.9,-3813329.876, 3554397.344, 3662821.941 183.0,-3813329.938, 3554397.909, 3662821.332 183.1,-3813330.005, 3554398.471, 3662820.721 183.2,-3813330.078, 3554399.030, 3662820.107 183.3,-3813330.156, 3554399.585, 3662819.491 183.4,-3813330.240, 3554400.137, 3662818.872 183.5,-3813330.330, 3554400.685, 3662818.251 183.6,-3813330.425, 3554401.230, 3662817.627 183.7,-3813330.525, 3554401.772, 3662817.002 183.8,-3813330.631, 3554402.309, 3662816.374 183.9,-3813330.743, 3554402.843, 3662815.744 184.0,-3813330.860, 3554403.373, 3662815.112 184.1,-3813330.982, 3554403.900, 3662814.478 184.2,-3813331.110, 3554404.423, 3662813.841 184.3,-3813331.243, 3554404.942, 3662813.203 184.4,-3813331.382, 3554405.457, 3662812.563 184.5,-3813331.527, 3554405.968, 3662811.921 184.6,-3813331.676, 3554406.476, 3662811.277 184.7,-3813331.831, 3554406.979, 3662810.632 184.8,-3813331.992, 3554407.479, 3662809.984 184.9,-3813332.158, 3554407.974, 3662809.335 185.0,-3813332.329, 3554408.465, 3662808.684 185.1,-3813332.506, 3554408.953, 3662808.032 185.2,-3813332.688, 3554409.436, 3662807.377 185.3,-3813332.875, 3554409.915, 3662806.722 185.4,-3813333.068, 3554410.390, 3662806.065 185.5,-3813333.266, 3554410.861, 3662805.406 185.6,-3813333.470, 3554411.327, 3662804.746 185.7,-3813333.678, 3554411.789, 3662804.085 185.8,-3813333.892, 3554412.247, 3662803.422 185.9,-3813334.112, 3554412.700, 3662802.759 186.0,-3813334.336, 3554413.150, 3662802.093 186.1,-3813334.566, 3554413.594, 3662801.427 186.2,-3813334.801, 3554414.034, 3662800.760 186.3,-3813335.041, 3554414.470, 3662800.091 186.4,-3813335.286, 3554414.902, 3662799.422 186.5,-3813335.537, 3554415.328, 3662798.751 186.6,-3813335.793, 3554415.751, 3662798.080 186.7,-3813336.054, 3554416.168, 3662797.408 186.8,-3813336.320, 3554416.581, 3662796.735 186.9,-3813336.591, 3554416.990, 3662796.061 187.0,-3813336.867, 3554417.393, 3662795.386 187.1,-3813337.148, 3554417.792, 3662794.711 187.2,-3813337.434, 3554418.187, 3662794.034 187.3,-3813337.725, 3554418.576, 3662793.358 187.4,-3813338.022, 3554418.961, 3662792.681 187.5,-3813338.323, 3554419.341, 3662792.003 187.6,-3813338.629, 3554419.716, 3662791.325 187.7,-3813338.940, 3554420.086, 3662790.646 187.8,-3813339.256, 3554420.451, 3662789.967 187.9,-3813339.577, 3554420.812, 3662789.288 188.0,-3813339.903, 3554421.167, 3662788.608 188.1,-3813340.234, 3554421.518, 3662787.928 188.2,-3813340.569, 3554421.863, 3662787.248 188.3,-3813340.910, 3554422.204, 3662786.568 188.4,-3813341.255, 3554422.539, 3662785.888 188.5,-3813341.605, 3554422.870, 3662785.207 188.6,-3813341.959, 3554423.195, 3662784.527 188.7,-3813342.319, 3554423.515, 3662783.847 188.8,-3813342.683, 3554423.831, 3662783.166 188.9,-3813343.051, 3554424.140, 3662782.486 189.0,-3813343.425, 3554424.445, 3662781.807 189.1,-3813343.803, 3554424.745, 3662781.127 189.2,-3813344.185, 3554425.039, 3662780.448 189.3,-3813344.572, 3554425.328, 3662779.769 189.4,-3813344.964, 3554425.612, 3662779.090 189.5,-3813345.360, 3554425.891, 3662778.412 189.6,-3813345.760, 3554426.164, 3662777.734 189.7,-3813346.165, 3554426.432, 3662777.057 189.8,-3813346.575, 3554426.695, 3662776.380 189.9,-3813346.988, 3554426.953, 3662775.704 190.0,-3813347.407, 3554427.205, 3662775.029 190.1,-3813347.829, 3554427.451, 3662774.354 190.2,-3813348.256, 3554427.692, 3662773.681 190.3,-3813348.687, 3554427.928, 3662773.007 190.4,-3813349.122, 3554428.159, 3662772.335 190.5,-3813349.562, 3554428.384, 3662771.664 190.6,-3813350.005, 3554428.603, 3662770.994 190.7,-3813350.453, 3554428.817, 3662770.324 190.8,-3813350.905, 3554429.025, 3662769.656 190.9,-3813351.361, 3554429.228, 3662768.989 191.0,-3813351.821, 3554429.426, 3662768.322 191.1,-3813352.285, 3554429.618, 3662767.657 191.2,-3813352.754, 3554429.804, 3662766.994 191.3,-3813353.226, 3554429.985, 3662766.331 191.4,-3813353.702, 3554430.160, 3662765.670 191.5,-3813354.182, 3554430.330, 3662765.010 191.6,-3813354.665, 3554430.494, 3662764.352 191.7,-3813355.153, 3554430.652, 3662763.695 191.8,-3813355.644, 3554430.805, 3662763.039 191.9,-3813356.139, 3554430.952, 3662762.385 192.0,-3813356.638, 3554431.094, 3662761.733 192.1,-3813357.141, 3554431.230, 3662761.082 192.2,-3813357.647, 3554431.360, 3662760.433 192.3,-3813358.157, 3554431.485, 3662759.786 192.4,-3813358.670, 3554431.604, 3662759.141 192.5,-3813359.187, 3554431.717, 3662758.497 192.6,-3813359.708, 3554431.824, 3662757.855 192.7,-3813360.232, 3554431.926, 3662757.215 192.8,-3813360.759, 3554432.022, 3662756.577 192.9,-3813361.290, 3554432.112, 3662755.941 193.0,-3813361.824, 3554432.197, 3662755.307 193.1,-3813362.361, 3554432.276, 3662754.675 193.2,-3813362.902, 3554432.349, 3662754.045 193.3,-3813363.446, 3554432.417, 3662753.418 193.4,-3813363.994, 3554432.478, 3662752.792 193.5,-3813364.544, 3554432.534, 3662752.169 193.6,-3813365.098, 3554432.585, 3662751.548 193.7,-3813365.654, 3554432.629, 3662750.930 193.8,-3813366.214, 3554432.668, 3662750.313 193.9,-3813366.777, 3554432.701, 3662749.700 194.0,-3813367.342, 3554432.728, 3662749.088 194.1,-3813367.911, 3554432.749, 3662748.480 194.2,-3813368.483, 3554432.765, 3662747.874 194.3,-3813369.057, 3554432.775, 3662747.270 194.4,-3813369.635, 3554432.779, 3662746.669 194.5,-3813370.215, 3554432.777, 3662746.071 194.6,-3813370.797, 3554432.770, 3662745.475 194.7,-3813371.383, 3554432.756, 3662744.882 194.8,-3813371.971, 3554432.737, 3662744.292 194.9,-3813372.562, 3554432.713, 3662743.705 195.0,-3813373.155, 3554432.682, 3662743.121 195.1,-3813373.751, 3554432.646, 3662742.540 195.2,-3813374.350, 3554432.604, 3662741.961 195.3,-3813374.951, 3554432.556, 3662741.386 195.4,-3813375.554, 3554432.503, 3662740.813 195.5,-3813376.160, 3554432.443, 3662740.244 195.6,-3813376.768, 3554432.378, 3662739.678 195.7,-3813377.378, 3554432.307, 3662739.115 195.8,-3813377.991, 3554432.231, 3662738.555 195.9,-3813378.606, 3554432.149, 3662737.999 196.0,-3813379.223, 3554432.061, 3662737.446 196.1,-3813379.842, 3554431.967, 3662736.896 196.2,-3813380.463, 3554431.868, 3662736.349 196.3,-3813381.086, 3554431.762, 3662735.806 196.4,-3813381.711, 3554431.652, 3662735.266 196.5,-3813382.338, 3554431.535, 3662734.730 196.6,-3813382.967, 3554431.413, 3662734.197 196.7,-3813383.598, 3554431.285, 3662733.668 196.8,-3813384.231, 3554431.152, 3662733.142 196.9,-3813384.865, 3554431.012, 3662732.620 197.0,-3813385.502, 3554430.868, 3662732.102 197.1,-3813386.140, 3554430.717, 3662731.587 197.2,-3813386.779, 3554430.561, 3662731.077 197.3,-3813387.420, 3554430.399, 3662730.569 197.4,-3813388.063, 3554430.232, 3662730.066 197.5,-3813388.707, 3554430.059, 3662729.566 197.6,-3813389.353, 3554429.881, 3662729.071 197.7,-3813390.000, 3554429.697, 3662728.579 197.8,-3813390.648, 3554429.507, 3662728.091 197.9,-3813391.298, 3554429.312, 3662727.607 198.0,-3813391.949, 3554429.111, 3662727.127 198.1,-3813392.602, 3554428.905, 3662726.652 198.2,-3813393.255, 3554428.693, 3662726.180 198.3,-3813393.910, 3554428.476, 3662725.712 198.4,-3813394.565, 3554428.253, 3662725.249 198.5,-3813395.222, 3554428.025, 3662724.789 198.6,-3813395.880, 3554427.792, 3662724.334 198.7,-3813396.539, 3554427.553, 3662723.883 198.8,-3813397.198, 3554427.309, 3662723.436 198.9,-3813397.859, 3554427.059, 3662722.994 199.0,-3813398.520, 3554426.804, 3662722.556 199.1,-3813399.183, 3554426.543, 3662722.122 199.2,-3813399.846, 3554426.277, 3662721.693 199.3,-3813400.509, 3554426.006, 3662721.268 199.4,-3813401.173, 3554425.730, 3662720.847 199.5,-3813401.838, 3554425.448, 3662720.431 199.6,-3813402.504, 3554425.161, 3662720.020 199.7,-3813403.170, 3554424.869, 3662719.613 199.8,-3813403.836, 3554424.571, 3662719.211 199.9,-3813404.503, 3554424.269, 3662718.813 200.0,-3813405.170, 3554423.961, 3662718.419 200.1,-3813405.837, 3554423.648, 3662718.031 200.2,-3813406.505, 3554423.330, 3662717.647 200.3,-3813407.173, 3554423.007, 3662717.268 200.4,-3813407.841, 3554422.678, 3662716.893 200.5,-3813408.509, 3554422.345, 3662716.524 200.6,-3813409.178, 3554422.007, 3662716.159 200.7,-3813409.846, 3554421.663, 3662715.798 200.8,-3813410.514, 3554421.315, 3662715.443 200.9,-3813411.183, 3554420.961, 3662715.092 201.0,-3813411.851, 3554420.603, 3662714.747 201.1,-3813412.519, 3554420.240, 3662714.406 201.2,-3813413.187, 3554419.872, 3662714.070 201.3,-3813413.855, 3554419.498, 3662713.739 201.4,-3813414.522, 3554419.121, 3662713.413 201.5,-3813415.189, 3554418.738, 3662713.092 201.6,-3813415.856, 3554418.350, 3662712.777 201.7,-3813416.522, 3554417.958, 3662712.466 201.8,-3813417.188, 3554417.561, 3662712.160 201.9,-3813417.853, 3554417.159, 3662711.859 202.0,-3813418.518, 3554416.753, 3662711.563 202.1,-3813419.182, 3554416.342, 3662711.273 202.2,-3813419.845, 3554415.926, 3662710.987 202.3,-3813420.508, 3554415.506, 3662710.707 202.4,-3813421.170, 3554415.081, 3662710.432 202.5,-3813421.831, 3554414.652, 3662710.162 202.6,-3813422.492, 3554414.218, 3662709.897 202.7,-3813423.151, 3554413.779, 3662709.638 202.8,-3813423.810, 3554413.336, 3662709.384 202.9,-3813424.467, 3554412.889, 3662709.135 203.0,-3813425.124, 3554412.437, 3662708.891 203.1,-3813425.780, 3554411.981, 3662708.653 203.2,-3813426.434, 3554411.521, 3662708.420 203.3,-3813427.087, 3554411.056, 3662708.192 203.4,-3813427.739, 3554410.588, 3662707.970 203.5,-3813428.390, 3554410.114, 3662707.753 203.6,-3813429.039, 3554409.637, 3662707.541 203.7,-3813429.688, 3554409.156, 3662707.335 203.8,-3813430.334, 3554408.670, 3662707.134 203.9,-3813430.980, 3554408.180, 3662706.939 204.0,-3813431.624, 3554407.687, 3662706.749 204.1,-3813432.266, 3554407.189, 3662706.565 204.2,-3813432.907, 3554406.687, 3662706.386 204.3,-3813433.546, 3554406.181, 3662706.212 204.4,-3813434.184, 3554405.672, 3662706.044 204.5,-3813434.819, 3554405.158, 3662705.881 204.6,-3813435.453, 3554404.641, 3662705.724 204.7,-3813436.086, 3554404.119, 3662705.573 204.8,-3813436.716, 3554403.595, 3662705.427 204.9,-3813437.345, 3554403.066, 3662705.286 205.0,-3813437.972, 3554402.533, 3662705.152 205.1,-3813438.596, 3554401.997, 3662705.022 205.2,-3813439.219, 3554401.457, 3662704.898 205.3,-3813439.840, 3554400.914, 3662704.780 205.4,-3813440.458, 3554400.367, 3662704.668 205.5,-3813441.075, 3554399.817, 3662704.561 205.6,-3813441.689, 3554399.263, 3662704.459 205.7,-3813442.301, 3554398.706, 3662704.364 205.8,-3813442.911, 3554398.145, 3662704.273 205.9,-3813443.519, 3554397.581, 3662704.189 206.0,-3813444.124, 3554397.013, 3662704.110 206.1,-3813444.727, 3554396.443, 3662704.037 206.2,-3813445.327, 3554395.869, 3662703.969 206.3,-3813445.925, 3554395.292, 3662703.907 206.4,-3813446.520, 3554394.711, 3662703.851 206.5,-3813447.113, 3554394.128, 3662703.800 206.6,-3813447.703, 3554393.541, 3662703.755 206.7,-3813448.291, 3554392.952, 3662703.715 206.8,-3813448.876, 3554392.359, 3662703.682 206.9,-3813449.458, 3554391.764, 3662703.654 207.0,-3813450.038, 3554391.165, 3662703.631 207.1,-3813450.614, 3554390.564, 3662703.615 207.2,-3813451.188, 3554389.960, 3662703.603 207.3,-3813451.759, 3554389.353, 3662703.598 207.4,-3813452.327, 3554388.743, 3662703.598 207.5,-3813452.892, 3554388.130, 3662703.604 207.6,-3813453.454, 3554387.515, 3662703.616 207.7,-3813454.013, 3554386.898, 3662703.633 207.8,-3813454.569, 3554386.277, 3662703.656 207.9,-3813455.122, 3554385.654, 3662703.685 208.0,-3813455.672, 3554385.029, 3662703.719 208.1,-3813456.218, 3554384.401, 3662703.759 208.2,-3813456.762, 3554383.771, 3662703.805 208.3,-3813457.302, 3554383.138, 3662703.856 208.4,-3813457.838, 3554382.504, 3662703.913 208.5,-3813458.372, 3554381.866, 3662703.975 208.6,-3813458.902, 3554381.227, 3662704.044 208.7,-3813459.429, 3554380.585, 3662704.117 208.8,-3813459.952, 3554379.942, 3662704.197 208.9,-3813460.471, 3554379.296, 3662704.282 209.0,-3813460.988, 3554378.648, 3662704.373 209.1,-3813461.500, 3554377.998, 3662704.469 209.2,-3813462.009, 3554377.346, 3662704.571 209.3,-3813462.515, 3554376.692, 3662704.678 209.4,-3813463.016, 3554376.037, 3662704.792 209.5,-3813463.514, 3554375.379, 3662704.910 209.6,-3813464.009, 3554374.720, 3662705.035 209.7,-3813464.499, 3554374.059, 3662705.164 209.8,-3813464.986, 3554373.396, 3662705.300 209.9,-3813465.469, 3554372.732, 3662705.441 210.0,-3813465.948, 3554372.066, 3662705.587 210.1,-3813466.423, 3554371.398, 3662705.739 210.2,-3813466.894, 3554370.729, 3662705.897 210.3,-3813467.361, 3554370.059, 3662706.060 210.4,-3813467.825, 3554369.387, 3662706.229 210.5,-3813468.284, 3554368.714, 3662706.403 210.6,-3813468.739, 3554368.039, 3662706.582 210.7,-3813469.190, 3554367.363, 3662706.767 210.8,-3813469.637, 3554366.686, 3662706.958 210.9,-3813470.080, 3554366.008, 3662707.154 211.0,-3813470.518, 3554365.328, 3662707.355 211.1,-3813470.953, 3554364.648, 3662707.562 211.2,-3813471.383, 3554363.966, 3662707.774 211.3,-3813471.809, 3554363.284, 3662707.991 211.4,-3813472.230, 3554362.601, 3662708.214 211.5,-3813472.647, 3554361.916, 3662708.442 211.6,-3813473.060, 3554361.231, 3662708.676 211.7,-3813473.468, 3554360.545, 3662708.915 211.8,-3813473.872, 3554359.858, 3662709.159 211.9,-3813474.272, 3554359.171, 3662709.408 212.0,-3813474.667, 3554358.483, 3662709.663 212.1,-3813475.058, 3554357.794, 3662709.923 212.2,-3813475.444, 3554357.105, 3662710.188 212.3,-3813475.825, 3554356.415, 3662710.459 212.4,-3813476.202, 3554355.725, 3662710.734 212.5,-3813476.574, 3554355.034, 3662711.015 212.6,-3813476.942, 3554354.343, 3662711.301 212.7,-3813477.305, 3554353.652, 3662711.592 212.8,-3813477.663, 3554352.960, 3662711.888 212.9,-3813478.017, 3554352.268, 3662712.189 213.0,-3813478.365, 3554351.576, 3662712.496 213.1,-3813478.709, 3554350.884, 3662712.807 213.2,-3813479.049, 3554350.192, 3662713.124 213.3,-3813479.383, 3554349.499, 3662713.445 213.4,-3813479.713, 3554348.807, 3662713.771 213.5,-3813480.038, 3554348.115, 3662714.103 213.6,-3813480.357, 3554347.423, 3662714.439 213.7,-3813480.672, 3554346.731, 3662714.780 213.8,-3813480.983, 3554346.039, 3662715.126 213.9,-3813481.288, 3554345.347, 3662715.477 214.0,-3813481.588, 3554344.656, 3662715.833 214.1,-3813481.883, 3554343.965, 3662716.194 214.2,-3813482.173, 3554343.275, 3662716.559 214.3,-3813482.458, 3554342.585, 3662716.930 214.4,-3813482.738, 3554341.896, 3662717.305 214.5,-3813483.013, 3554341.207, 3662717.684 214.6,-3813483.283, 3554340.518, 3662718.069 214.7,-3813483.548, 3554339.831, 3662718.458 214.8,-3813483.808, 3554339.143, 3662718.851 214.9,-3813484.062, 3554338.457, 3662719.250 215.0,-3813484.312, 3554337.772, 3662719.653 215.1,-3813484.556, 3554337.087, 3662720.060 215.2,-3813484.795, 3554336.403, 3662720.472 215.3,-3813485.029, 3554335.720, 3662720.888 215.4,-3813485.257, 3554335.038, 3662721.309 215.5,-3813485.481, 3554334.357, 3662721.735 215.6,-3813485.699, 3554333.678, 3662722.164 215.7,-3813485.912, 3554332.999, 3662722.599 215.8,-3813486.119, 3554332.321, 3662723.037 215.9,-3813486.321, 3554331.645, 3662723.480 216.0,-3813486.518, 3554330.970, 3662723.927 216.1,-3813486.710, 3554330.296, 3662724.378 216.2,-3813486.896, 3554329.624, 3662724.834 216.3,-3813487.077, 3554328.952, 3662725.294 216.4,-3813487.252, 3554328.283, 3662725.758 216.5,-3813487.423, 3554327.615, 3662726.226 216.6,-3813487.587, 3554326.948, 3662726.698 216.7,-3813487.747, 3554326.283, 3662727.174 216.8,-3813487.900, 3554325.620, 3662727.654 216.9,-3813488.049, 3554324.958, 3662728.139 217.0,-3813488.192, 3554324.298, 3662728.627 217.1,-3813488.330, 3554323.640, 3662729.119 217.2,-3813488.462, 3554322.983, 3662729.615 217.3,-3813488.589, 3554322.329, 3662730.115 217.4,-3813488.710, 3554321.676, 3662730.619 217.5,-3813488.825, 3554321.025, 3662731.126 217.6,-3813488.936, 3554320.377, 3662731.638 217.7,-3813489.040, 3554319.730, 3662732.153 217.8,-3813489.140, 3554319.085, 3662732.671 217.9,-3813489.233, 3554318.443, 3662733.194 218.0,-3813489.322, 3554317.803, 3662733.720 218.1,-3813489.404, 3554317.164, 3662734.249 218.2,-3813489.482, 3554316.529, 3662734.782 218.3,-3813489.553, 3554315.895, 3662735.319 218.4,-3813489.619, 3554315.264, 3662735.859 218.5,-3813489.680, 3554314.635, 3662736.402 218.6,-3813489.735, 3554314.009, 3662736.949 218.7,-3813489.784, 3554313.385, 3662737.500 218.8,-3813489.828, 3554312.763, 3662738.053 218.9,-3813489.866, 3554312.145, 3662738.610 219.0,-3813489.899, 3554311.528, 3662739.170 219.1,-3813489.926, 3554310.915, 3662739.733 219.2,-3813489.948, 3554310.304, 3662740.300 219.3,-3813489.964, 3554309.696, 3662740.869 219.4,-3813489.975, 3554309.091, 3662741.442 219.5,-3813489.979, 3554308.488, 3662742.018 219.6,-3813489.979, 3554307.888, 3662742.596 219.7,-3813489.973, 3554307.292, 3662743.178 219.8,-3813489.961, 3554306.698, 3662743.762 219.9,-3813489.943, 3554306.107, 3662744.350 220.0,-3813489.921, 3554305.519, 3662744.940 220.1,-3813489.892, 3554304.935, 3662745.533 220.2,-3813489.858, 3554304.353, 3662746.129 220.3,-3813489.818, 3554303.774, 3662746.728 220.4,-3813489.773, 3554303.199, 3662747.329 220.5,-3813489.722, 3554302.627, 3662747.933 220.6,-3813489.666, 3554302.058, 3662748.539 220.7,-3813489.604, 3554301.493, 3662749.148 220.8,-3813489.537, 3554300.931, 3662749.760 220.9,-3813489.464, 3554300.372, 3662750.374 221.0,-3813489.386, 3554299.817, 3662750.990 221.1,-3813489.302, 3554299.265, 3662751.609 221.2,-3813489.212, 3554298.717, 3662752.230 221.3,-3813489.117, 3554298.172, 3662752.853 221.4,-3813489.017, 3554297.631, 3662753.479 221.5,-3813488.911, 3554297.093, 3662754.107 221.6,-3813488.799, 3554296.559, 3662754.737 221.7,-3813488.682, 3554296.029, 3662755.369 221.8,-3813488.560, 3554295.502, 3662756.003 221.9,-3813488.432, 3554294.980, 3662756.639 222.0,-3813488.298, 3554294.461, 3662757.277 222.1,-3813488.159, 3554293.945, 3662757.918 222.2,-3813488.015, 3554293.434, 3662758.560 222.3,-3813487.865, 3554292.927, 3662759.203 222.4,-3813487.710, 3554292.423, 3662759.849 222.5,-3813487.549, 3554291.924, 3662760.497 222.6,-3813487.383, 3554291.429, 3662761.146 222.7,-3813487.212, 3554290.937, 3662761.797 222.8,-3813487.035, 3554290.450, 3662762.449 222.9,-3813486.853, 3554289.967, 3662763.103 223.0,-3813486.666, 3554289.488, 3662763.759 223.1,-3813486.473, 3554289.013, 3662764.416 223.2,-3813486.275, 3554288.542, 3662765.075 223.3,-3813486.071, 3554288.076, 3662765.735 223.4,-3813485.863, 3554287.614, 3662766.396 223.5,-3813485.648, 3554287.156, 3662767.058 223.6,-3813485.429, 3554286.703, 3662767.722 223.7,-3813485.205, 3554286.253, 3662768.387 223.8,-3813484.975, 3554285.809, 3662769.054 223.9,-3813484.740, 3554285.369, 3662769.721 224.0,-3813484.500, 3554284.933, 3662770.390 224.1,-3813484.254, 3554284.502, 3662771.059 224.2,-3813484.004, 3554284.075, 3662771.729 224.3,-3813483.748, 3554283.653, 3662772.401 224.4,-3813483.487, 3554283.235, 3662773.073 224.5,-3813483.221, 3554282.822, 3662773.746 224.6,-3813482.950, 3554282.414, 3662774.420 224.7,-3813482.674, 3554282.010, 3662775.095 224.8,-3813482.392, 3554281.611, 3662775.770 224.9,-3813482.106, 3554281.217, 3662776.446 225.0,-3813481.815, 3554280.827, 3662777.123 225.1,-3813481.518, 3554280.443, 3662777.800 225.2,-3813481.217, 3554280.063, 3662778.478 225.3,-3813480.911, 3554279.688, 3662779.156 225.4,-3813480.600, 3554279.318, 3662779.835 225.5,-3813480.284, 3554278.952, 3662780.514 225.6,-3813479.963, 3554278.592, 3662781.193 225.7,-3813479.637, 3554278.237, 3662781.873 225.8,-3813479.306, 3554277.886, 3662782.553 225.9,-3813478.970, 3554277.541, 3662783.233 226.0,-3813478.630, 3554277.200, 3662783.913 226.1,-3813478.285, 3554276.865, 3662784.593 226.2,-3813477.935, 3554276.534, 3662785.274 226.3,-3813477.580, 3554276.209, 3662785.954 226.4,-3813477.221, 3554275.889, 3662786.634 226.5,-3813476.857, 3554275.574, 3662787.315 226.6,-3813476.488, 3554275.264, 3662787.995 226.7,-3813476.115, 3554274.959, 3662788.674 226.8,-3813475.737, 3554274.659, 3662789.354 226.9,-3813475.354, 3554274.365, 3662790.033 227.0,-3813474.967, 3554274.076, 3662790.712 227.1,-3813474.576, 3554273.792, 3662791.391 227.2,-3813474.180, 3554273.513, 3662792.069 227.3,-3813473.779, 3554273.240, 3662792.747 227.4,-3813473.374, 3554272.972, 3662793.424 227.5,-3813472.965, 3554272.709, 3662794.101 227.6,-3813472.551, 3554272.452, 3662794.777 227.7,-3813472.133, 3554272.200, 3662795.452 227.8,-3813471.710, 3554271.954, 3662796.127 227.9,-3813471.283, 3554271.712, 3662796.800 228.0,-3813470.852, 3554271.477, 3662797.473 228.1,-3813470.417, 3554271.246, 3662798.146 228.2,-3813469.977, 3554271.021, 3662798.817 228.3,-3813469.533, 3554270.802, 3662799.487 228.4,-3813469.086, 3554270.588, 3662800.157 228.5,-3813468.634, 3554270.380, 3662800.825 228.6,-3813468.178, 3554270.177, 3662801.492 228.7,-3813467.717, 3554269.979, 3662802.159 228.8,-3813467.253, 3554269.787, 3662802.823 228.9,-3813466.785, 3554269.601, 3662803.487 229.0,-3813466.313, 3554269.420, 3662804.150 229.1,-3813465.837, 3554269.245, 3662804.811 229.2,-3813465.357, 3554269.076, 3662805.471 229.3,-3813464.873, 3554268.912, 3662806.129 229.4,-3813464.386, 3554268.753, 3662806.786 229.5,-3813463.894, 3554268.600, 3662807.441 229.6,-3813463.399, 3554268.453, 3662808.095 229.7,-3813462.900, 3554268.312, 3662808.748 229.8,-3813462.398, 3554268.176, 3662809.398 229.9,-3813461.891, 3554268.046, 3662810.047 230.0,-3813461.381, 3554267.921, 3662810.695 230.1,-3813460.868, 3554267.802, 3662811.340 230.2,-3813460.351, 3554267.689, 3662811.984 230.3,-3813459.830, 3554267.582, 3662812.626 230.4,-3813459.307, 3554267.480, 3662813.266 230.5,-3813458.779, 3554267.384, 3662813.904 230.6,-3813458.248, 3554267.294, 3662814.540 230.7,-3813457.714, 3554267.209, 3662815.174 230.8,-3813457.177, 3554267.130, 3662815.806 230.9,-3813456.636, 3554267.057, 3662816.435 231.0,-3813456.092, 3554266.990, 3662817.063 231.1,-3813455.544, 3554266.928, 3662817.688 231.2,-3813454.994, 3554266.872, 3662818.312 231.3,-3813454.440, 3554266.822, 3662818.932 231.4,-3813453.884, 3554266.778, 3662819.551 231.5,-3813453.324, 3554266.739, 3662820.167 231.6,-3813452.761, 3554266.706, 3662820.781 231.7,-3813452.195, 3554266.679, 3662821.392 231.8,-3813451.627, 3554266.658, 3662822.001 231.9,-3813451.055, 3554266.642, 3662822.607 232.0,-3813450.480, 3554266.632, 3662823.210 232.1,-3813449.903, 3554266.628, 3662823.811 232.2,-3813449.323, 3554266.630, 3662824.410 232.3,-3813448.740, 3554266.637, 3662825.005 232.4,-3813448.155, 3554266.651, 3662825.598 232.5,-3813447.566, 3554266.670, 3662826.188 232.6,-3813446.976, 3554266.695, 3662826.775 232.7,-3813446.382, 3554266.725, 3662827.359 232.8,-3813445.786, 3554266.761, 3662827.941 232.9,-3813445.188, 3554266.803, 3662828.519 233.0,-3813444.587, 3554266.851, 3662829.094 233.1,-3813443.983, 3554266.905, 3662829.667 233.2,-3813443.378, 3554266.964, 3662830.236 233.3,-3813442.770, 3554267.029, 3662830.802 233.4,-3813442.159, 3554267.100, 3662831.365 233.5,-3813441.547, 3554267.177, 3662831.925 233.6,-3813440.932, 3554267.259, 3662832.481 233.7,-3813440.315, 3554267.347, 3662833.034 233.8,-3813439.696, 3554267.441, 3662833.584 233.9,-3813439.075, 3554267.540, 3662834.131 234.0,-3813438.451, 3554267.645, 3662834.674 234.1,-3813437.826, 3554267.756, 3662835.213 234.2,-3813437.199, 3554267.873, 3662835.750 234.3,-3813436.570, 3554267.995, 3662836.282 234.4,-3813435.939, 3554268.123, 3662836.812 234.5,-3813435.306, 3554268.257, 3662837.337 234.6,-3813434.672, 3554268.396, 3662837.859 234.7,-3813434.036, 3554268.541, 3662838.377 234.8,-3813433.398, 3554268.691, 3662838.892 234.9,-3813432.758, 3554268.847, 3662839.403 235.0,-3813432.117, 3554269.009, 3662839.910 235.1,-3813431.474, 3554269.176, 3662840.413 235.2,-3813430.830, 3554269.349, 3662840.913 235.3,-3813430.184, 3554269.528, 3662841.409 235.4,-3813429.537, 3554269.712, 3662841.900 235.5,-3813428.889, 3554269.902, 3662842.388 235.6,-3813428.239, 3554270.097, 3662842.872 235.7,-3813427.588, 3554270.298, 3662843.352 235.8,-3813426.936, 3554270.504, 3662843.828 235.9,-3813426.282, 3554270.716, 3662844.299 236.0,-3813425.627, 3554270.933, 3662844.767 236.1,-3813424.972, 3554271.155, 3662845.231 236.2,-3813424.315, 3554271.384, 3662845.690 236.3,-3813423.657, 3554271.617, 3662846.145 236.4,-3813422.998, 3554271.856, 3662846.596 236.5,-3813422.338, 3554272.101, 3662847.043 236.6,-3813421.678, 3554272.350, 3662847.485 236.7,-3813421.016, 3554272.606, 3662847.923 236.8,-3813420.354, 3554272.866, 3662848.357 236.9,-3813419.691, 3554273.132, 3662848.786 237.0,-3813419.028, 3554273.403, 3662849.211 237.1,-3813418.364, 3554273.680, 3662849.631 237.2,-3813417.699, 3554273.961, 3662850.047 237.3,-3813417.033, 3554274.248, 3662850.459 237.4,-3813416.367, 3554274.541, 3662850.866 237.5,-3813415.701, 3554274.838, 3662851.268 237.6,-3813415.034, 3554275.141, 3662851.666 237.7,-3813414.367, 3554275.449, 3662852.059 237.8,-3813413.700, 3554275.762, 3662852.447 237.9,-3813413.032, 3554276.080, 3662852.831 238.0,-3813412.364, 3554276.403, 3662853.211 238.1,-3813411.696, 3554276.731, 3662853.585 238.2,-3813411.028, 3554277.065, 3662853.955 238.3,-3813410.359, 3554277.403, 3662854.320 238.4,-3813409.691, 3554277.747, 3662854.680 238.5,-3813409.022, 3554278.095, 3662855.035 238.6,-3813408.354, 3554278.449, 3662855.386 238.7,-3813407.686, 3554278.807, 3662855.731 238.8,-3813407.018, 3554279.170, 3662856.072 238.9,-3813406.350, 3554279.539, 3662856.408 239.0,-3813405.682, 3554279.912, 3662856.739 239.1,-3813405.015, 3554280.290, 3662857.064 239.2,-3813404.348, 3554280.673, 3662857.385 239.3,-3813403.681, 3554281.060, 3662857.701 239.4,-3813403.015, 3554281.452, 3662858.012 239.5,-3813402.349, 3554281.849, 3662858.318 239.6,-3813401.684, 3554282.251, 3662858.619 239.7,-3813401.019, 3554282.658, 3662858.914 239.8,-3813400.355, 3554283.069, 3662859.205 239.9,-3813399.691, 3554283.485, 3662859.490 240.0,-3813399.029, 3554283.905, 3662859.770 240.1,-3813398.367, 3554284.330, 3662860.045 240.2,-3813397.706, 3554284.759, 3662860.315 240.3,-3813397.045, 3554285.193, 3662860.580 240.4,-3813396.386, 3554285.632, 3662860.839 240.5,-3813395.727, 3554286.075, 3662861.093 240.6,-3813395.070, 3554286.522, 3662861.342 240.7,-3813394.413, 3554286.974, 3662861.586 240.8,-3813393.757, 3554287.430, 3662861.824 240.9,-3813393.103, 3554287.890, 3662862.057 241.0,-3813392.450, 3554288.355, 3662862.285 241.1,-3813391.798, 3554288.824, 3662862.507 241.2,-3813391.147, 3554289.297, 3662862.724 241.3,-3813390.498, 3554289.774, 3662862.936 241.4,-3813389.849, 3554290.256, 3662863.142 241.5,-3813389.203, 3554290.741, 3662863.342 241.6,-3813388.557, 3554291.231, 3662863.538 241.7,-3813387.913, 3554291.725, 3662863.728 241.8,-3813387.271, 3554292.223, 3662863.912 241.9,-3813386.630, 3554292.724, 3662864.091 242.0,-3813385.991, 3554293.230, 3662864.264 242.1,-3813385.354, 3554293.740, 3662864.432 242.2,-3813384.718, 3554294.253, 3662864.595 242.3,-3813384.084, 3554294.771, 3662864.752 242.4,-3813383.451, 3554295.292, 3662864.903 242.5,-3813382.821, 3554295.817, 3662865.049 242.6,-3813382.192, 3554296.346, 3662865.190 242.7,-3813381.566, 3554296.878, 3662865.325 242.8,-3813380.941, 3554297.414, 3662865.454 242.9,-3813380.318, 3554297.954, 3662865.578 243.0,-3813379.698, 3554298.498, 3662865.696 243.1,-3813379.079, 3554299.045, 3662865.808 243.2,-3813378.463, 3554299.595, 3662865.915 243.3,-3813377.848, 3554300.149, 3662866.017 243.4,-3813377.236, 3554300.706, 3662866.112 243.5,-3813376.626, 3554301.267, 3662866.202 243.6,-3813376.019, 3554301.831, 3662866.287 243.7,-3813375.414, 3554302.399, 3662866.366 243.8,-3813374.811, 3554302.969, 3662866.439 243.9,-3813374.211, 3554303.543, 3662866.507 244.0,-3813373.613, 3554304.121, 3662866.569 244.1,-3813373.017, 3554304.701, 3662866.625 244.2,-3813372.425, 3554305.284, 3662866.675 244.3,-3813371.834, 3554305.871, 3662866.720 244.4,-3813371.247, 3554306.460, 3662866.760 244.5,-3813370.662, 3554307.053, 3662866.793 244.6,-3813370.080, 3554307.649, 3662866.822 244.7,-3813369.500, 3554308.247, 3662866.844 244.8,-3813368.924, 3554308.849, 3662866.861 244.9,-3813368.350, 3554309.453, 3662866.872 245.0,-3813367.779, 3554310.060, 3662866.877 245.1,-3813367.211, 3554310.669, 3662866.877 245.2,-3813366.646, 3554311.282, 3662866.871 245.3,-3813366.084, 3554311.897, 3662866.859 245.4,-3813365.525, 3554312.515, 3662866.842 245.5,-3813364.969, 3554313.135, 3662866.819 245.6,-3813364.416, 3554313.758, 3662866.790 245.7,-3813363.866, 3554314.383, 3662866.756 245.8,-3813363.320, 3554315.011, 3662866.716 245.9,-3813362.776, 3554315.642, 3662866.670 246.0,-3813362.236, 3554316.274, 3662866.619 246.1,-3813361.700, 3554316.909, 3662866.562 246.2,-3813361.166, 3554317.546, 3662866.499 246.3,-3813360.636, 3554318.186, 3662866.431 246.4,-3813360.110, 3554318.827, 3662866.357 246.5,-3813359.586, 3554319.471, 3662866.277 246.6,-3813359.067, 3554320.117, 3662866.192 246.7,-3813358.551, 3554320.765, 3662866.102 246.8,-3813358.038, 3554321.415, 3662866.005 246.9,-3813357.529, 3554322.067, 3662865.903 247.0,-3813357.024, 3554322.721, 3662865.796 247.1,-3813356.522, 3554323.376, 3662865.682 247.2,-3813356.024, 3554324.034, 3662865.564 247.3,-3813355.530, 3554324.693, 3662865.439 247.4,-3813355.039, 3554325.354, 3662865.309 247.5,-3813354.553, 3554326.017, 3662865.174 247.6,-3813354.070, 3554326.681, 3662865.033 247.7,-3813353.591, 3554327.347, 3662864.886 247.8,-3813353.116, 3554328.015, 3662864.734 247.9,-3813352.644, 3554328.684, 3662864.576 248.0,-3813352.177, 3554329.354, 3662864.413 248.1,-3813351.714, 3554330.026, 3662864.245 248.2,-3813351.255, 3554330.699, 3662864.071 248.3,-3813350.800, 3554331.374, 3662863.891 248.4,-3813350.349, 3554332.050, 3662863.706 248.5,-3813349.902, 3554332.727, 3662863.515 248.6,-3813349.459, 3554333.405, 3662863.320 248.7,-3813349.021, 3554334.085, 3662863.118 248.8,-3813348.586, 3554334.765, 3662862.911 248.9,-3813348.156, 3554335.447, 3662862.699 249.0,-3813347.731, 3554336.129, 3662862.482 249.1,-3813347.309, 3554336.813, 3662862.259 249.2,-3813346.892, 3554337.497, 3662862.030 249.3,-3813346.479, 3554338.182, 3662861.797 249.4,-3813346.071, 3554338.868, 3662861.558 249.5,-3813345.667, 3554339.555, 3662861.314 249.6,-3813345.267, 3554340.242, 3662861.064 249.7,-3813344.872, 3554340.930, 3662860.810 249.8,-3813344.482, 3554341.619, 3662860.550 249.9,-3813344.096, 3554342.308, 3662860.284 250.0,-3813343.714, 3554342.998, 3662860.014 250.1,-3813343.338, 3554343.688, 3662859.738 250.2,-3813342.965, 3554344.379, 3662859.457 250.3,-3813342.598, 3554345.070, 3662859.171 250.4,-3813342.235, 3554345.762, 3662858.880 250.5,-3813341.877, 3554346.453, 3662858.584 250.6,-3813341.523, 3554347.145, 3662858.283 250.7,-3813341.174, 3554347.837, 3662857.976 250.8,-3813340.830, 3554348.529, 3662857.665 250.9,-3813340.491, 3554349.222, 3662857.349 251.0,-3813340.157, 3554349.914, 3662857.027 251.1,-3813339.827, 3554350.606, 3662856.701 251.2,-3813339.502, 3554351.298, 3662856.369 251.3,-3813339.183, 3554351.990, 3662856.033 251.4,-3813338.868, 3554352.682, 3662855.692 251.5,-3813338.558, 3554353.374, 3662855.345 251.6,-3813338.253, 3554354.066, 3662854.994 251.7,-3813337.952, 3554354.757, 3662854.638 251.8,-3813337.657, 3554355.448, 3662854.278 251.9,-3813337.367, 3554356.138, 3662853.912 252.0,-3813337.082, 3554356.828, 3662853.542 252.1,-3813336.802, 3554357.518, 3662853.167 252.2,-3813336.527, 3554358.206, 3662852.787 252.3,-3813336.257, 3554358.895, 3662852.403 252.4,-3813335.992, 3554359.583, 3662852.014 252.5,-3813335.733, 3554360.270, 3662851.620 252.6,-3813335.478, 3554360.956, 3662851.222 252.7,-3813335.229, 3554361.641, 3662850.819 252.8,-3813334.985, 3554362.326, 3662850.411 252.9,-3813334.746, 3554363.010, 3662849.999 253.0,-3813334.512, 3554363.693, 3662849.583 253.1,-3813334.284, 3554364.375, 3662849.162 253.2,-3813334.060, 3554365.056, 3662848.737 253.3,-3813333.842, 3554365.735, 3662848.307 253.4,-3813333.629, 3554366.414, 3662847.873 253.5,-3813333.422, 3554367.092, 3662847.434 253.6,-3813333.220, 3554367.768, 3662846.991 253.7,-3813333.023, 3554368.443, 3662846.544 253.8,-3813332.831, 3554369.117, 3662846.093 253.9,-3813332.645, 3554369.789, 3662845.637 254.0,-3813332.464, 3554370.460, 3662845.177 254.1,-3813332.289, 3554371.130, 3662844.713 254.2,-3813332.119, 3554371.798, 3662844.245 254.3,-3813331.954, 3554372.465, 3662843.773 254.4,-3813331.795, 3554373.130, 3662843.297 254.5,-3813331.641, 3554373.793, 3662842.816 254.6,-3813331.493, 3554374.455, 3662842.332 254.7,-3813331.350, 3554375.115, 3662841.844 254.8,-3813331.212, 3554375.773, 3662841.352 254.9,-3813331.080, 3554376.429, 3662840.855 255.0,-3813330.953, 3554377.084, 3662840.356 255.1,-3813330.832, 3554377.737, 3662839.852 255.2,-3813330.716, 3554378.387, 3662839.344 255.3,-3813330.606, 3554379.036, 3662838.833 255.4,-3813330.501, 3554379.683, 3662838.318 255.5,-3813330.402, 3554380.327, 3662837.799 255.6,-3813330.309, 3554380.970, 3662837.277 255.7,-3813330.220, 3554381.610, 3662836.751 255.8,-3813330.138, 3554382.248, 3662836.221 255.9,-3813330.061, 3554382.884, 3662835.688 256.0,-3813329.989, 3554383.518, 3662835.151 256.1,-3813329.923, 3554384.149, 3662834.611 256.2,-3813329.863, 3554384.777, 3662834.068 256.3,-3813329.808, 3554385.404, 3662833.521 256.4,-3813329.758, 3554386.028, 3662832.970 256.5,-3813329.714, 3554386.649, 3662832.417 256.6,-3813329.676, 3554387.268, 3662831.860 256.7,-3813329.643, 3554387.884, 3662831.300 256.8,-3813329.616, 3554388.498, 3662830.737 256.9,-3813329.595, 3554389.108, 3662830.170 257.0,-3813329.579, 3554389.716, 3662829.601 257.1,-3813329.568, 3554390.322, 3662829.028 257.2,-3813329.563, 3554390.924, 3662828.452 257.3,-3813329.564, 3554391.524, 3662827.874 257.4,-3813329.570, 3554392.121, 3662827.292 257.5,-3813329.582, 3554392.714, 3662826.707 257.6,-3813329.600, 3554393.305, 3662826.120 257.7,-3813329.623, 3554393.893, 3662825.530 257.8,-3813329.651, 3554394.478, 3662824.936 257.9,-3813329.685, 3554395.059, 3662824.341 258.0,-3813329.725, 3554395.638, 3662823.742 258.1,-3813329.770, 3554396.213, 3662823.141 258.2,-3813329.821, 3554396.785, 3662822.537 258.3,-3813329.877, 3554397.354, 3662821.930 258.4,-3813329.939, 3554397.919, 3662821.321 258.5,-3813330.006, 3554398.481, 3662820.710 258.6,-3813330.079, 3554399.040, 3662820.096 258.7,-3813330.158, 3554399.595, 3662819.480 258.8,-3813330.242, 3554400.147, 3662818.861 258.9,-3813330.331, 3554400.695, 3662818.240 259.0,-3813330.427, 3554401.240, 3662817.616 259.1,-3813330.527, 3554401.781, 3662816.991 259.2,-3813330.633, 3554402.319, 3662816.363 259.3,-3813330.745, 3554402.853, 3662815.733 259.4,-3813330.862, 3554403.383, 3662815.101 259.5,-3813330.984, 3554403.909, 3662814.466 259.6,-3813331.112, 3554404.432, 3662813.830 259.7,-3813331.246, 3554404.951, 3662813.192 259.8,-3813331.385, 3554405.466, 3662812.552 259.9,-3813331.529, 3554405.977, 3662811.910 260.0,-3813331.679, 3554406.485, 3662811.266 260.1,-3813331.834, 3554406.988, 3662810.620 260.2,-3813331.995, 3554407.487, 3662809.973 260.3,-3813332.161, 3554407.983, 3662809.323 260.4,-3813332.332, 3554408.474, 3662808.672 260.5,-3813332.509, 3554408.961, 3662808.020 260.6,-3813332.691, 3554409.444, 3662807.366 260.7,-3813332.879, 3554409.923, 3662806.710 260.8,-3813333.072, 3554410.398, 3662806.053 260.9,-3813333.270, 3554410.869, 3662805.395 261.0,-3813333.473, 3554411.335, 3662804.735 261.1,-3813333.682, 3554411.797, 3662804.073 261.2,-3813333.896, 3554412.255, 3662803.411 261.3,-3813334.116, 3554412.708, 3662802.747 261.4,-3813334.340, 3554413.157, 3662802.082 261.5,-3813334.570, 3554413.602, 3662801.415 261.6,-3813334.805, 3554414.042, 3662800.748 261.7,-3813335.045, 3554414.478, 3662800.080 261.8,-3813335.291, 3554414.909, 3662799.410 261.9,-3813335.542, 3554415.336, 3662798.740 262.0,-3813335.797, 3554415.758, 3662798.068 262.1,-3813336.058, 3554416.176, 3662797.396 262.2,-3813336.324, 3554416.589, 3662796.723 262.3,-3813336.595, 3554416.997, 3662796.049 262.4,-3813336.872, 3554417.400, 3662795.374 262.5,-3813337.153, 3554417.799, 3662794.699 262.6,-3813337.439, 3554418.194, 3662794.022 262.7,-3813337.731, 3554418.583, 3662793.346 262.8,-3813338.027, 3554418.968, 3662792.669 262.9,-3813338.328, 3554419.347, 3662791.991 263.0,-3813338.635, 3554419.722, 3662791.313 263.1,-3813338.946, 3554420.093, 3662790.634 263.2,-3813339.262, 3554420.458, 3662789.955 263.3,-3813339.583, 3554420.818, 3662789.276 263.4,-3813339.909, 3554421.174, 3662788.596 263.5,-3813340.240, 3554421.524, 3662787.916 263.6,-3813340.575, 3554421.869, 3662787.236 263.7,-3813340.916, 3554422.210, 3662786.556 263.8,-3813341.261, 3554422.545, 3662785.876 263.9,-3813341.611, 3554422.876, 3662785.195 264.0,-3813341.966, 3554423.201, 3662784.515 264.1,-3813342.325, 3554423.521, 3662783.835 264.2,-3813342.689, 3554423.836, 3662783.154 264.3,-3813343.058, 3554424.146, 3662782.474 264.4,-3813343.431, 3554424.451, 3662781.795 264.5,-3813343.809, 3554424.750, 3662781.115 264.6,-3813344.192, 3554425.044, 3662780.436 264.7,-3813344.579, 3554425.334, 3662779.757 264.8,-3813344.971, 3554425.617, 3662779.078 264.9,-3813345.367, 3554425.896, 3662778.400 265.0,-3813345.767, 3554426.169, 3662777.722 265.1,-3813346.172, 3554426.437, 3662777.045 265.2,-3813346.582, 3554426.700, 3662776.368 265.3,-3813346.996, 3554426.957, 3662775.692 265.4,-3813347.414, 3554427.209, 3662775.017 265.5,-3813347.837, 3554427.456, 3662774.342 265.6,-3813348.263, 3554427.697, 3662773.669 265.7,-3813348.695, 3554427.932, 3662772.996 265.8,-3813349.130, 3554428.163, 3662772.323 265.9,-3813349.570, 3554428.387, 3662771.652 266.0,-3813350.013, 3554428.607, 3662770.982 266.1,-3813350.461, 3554428.821, 3662770.312 266.2,-3813350.913, 3554429.029, 3662769.644 266.3,-3813351.369, 3554429.232, 3662768.977 266.4,-3813351.830, 3554429.429, 3662768.311 266.5,-3813352.294, 3554429.621, 3662767.646 266.6,-3813352.762, 3554429.807, 3662766.982 266.7,-3813353.234, 3554429.988, 3662766.319 266.8,-3813353.710, 3554430.163, 3662765.658 266.9,-3813354.190, 3554430.333, 3662764.998 267.0,-3813354.674, 3554430.497, 3662764.340 267.1,-3813355.162, 3554430.655, 3662763.683 267.2,-3813355.653, 3554430.808, 3662763.028 267.3,-3813356.148, 3554430.955, 3662762.374 267.4,-3813356.647, 3554431.096, 3662761.721 267.5,-3813357.150, 3554431.232, 3662761.071 267.6,-3813357.656, 3554431.362, 3662760.422 267.7,-3813358.166, 3554431.487, 3662759.775 267.8,-3813358.679, 3554431.606, 3662759.129 267.9,-3813359.196, 3554431.719, 3662758.485 268.0,-3813359.717, 3554431.826, 3662757.844 268.1,-3813360.241, 3554431.928, 3662757.204 268.2,-3813360.768, 3554432.024, 3662756.566 268.3,-3813361.299, 3554432.114, 3662755.930 268.4,-3813361.834, 3554432.199, 3662755.296 268.5,-3813362.371, 3554432.277, 3662754.664 268.6,-3813362.912, 3554432.350, 3662754.034 268.7,-3813363.456, 3554432.418, 3662753.406 268.8,-3813364.003, 3554432.479, 3662752.781 268.9,-3813364.554, 3554432.535, 3662752.158 269.0,-3813365.107, 3554432.585, 3662751.537 269.1,-3813365.664, 3554432.630, 3662750.919 269.2,-3813366.224, 3554432.668, 3662750.303 269.3,-3813366.787, 3554432.701, 3662749.689 269.4,-3813367.353, 3554432.728, 3662749.078 269.5,-3813367.921, 3554432.749, 3662748.469 269.6,-3813368.493, 3554432.765, 3662747.863 269.7,-3813369.067, 3554432.775, 3662747.259 269.8,-3813369.645, 3554432.779, 3662746.658 269.9,-3813370.225, 3554432.777, 3662746.060 270.0,-3813370.808, 3554432.769, 3662745.465 270.1,-3813371.393, 3554432.756, 3662744.872 270.2,-3813371.982, 3554432.737, 3662744.282 270.3,-3813372.573, 3554432.712, 3662743.695 270.4,-3813373.166, 3554432.682, 3662743.111 270.5,-3813373.762, 3554432.645, 3662742.529 270.6,-3813374.361, 3554432.603, 3662741.951 270.7,-3813374.961, 3554432.555, 3662741.376 270.8,-3813375.565, 3554432.502, 3662740.803 270.9,-3813376.171, 3554432.442, 3662740.234 271.0,-3813376.779, 3554432.377, 3662739.668 271.1,-3813377.389, 3554432.306, 3662739.105 271.2,-3813378.002, 3554432.229, 3662738.545 271.3,-3813378.617, 3554432.147, 3662737.989 271.4,-3813379.234, 3554432.059, 3662737.436 271.5,-3813379.853, 3554431.965, 3662736.886 271.6,-3813380.474, 3554431.866, 3662736.340 271.7,-3813381.097, 3554431.761, 3662735.796 271.8,-3813381.722, 3554431.650, 3662735.257 271.9,-3813382.349, 3554431.533, 3662734.721 272.0,-3813382.979, 3554431.411, 3662734.188 272.1,-3813383.609, 3554431.283, 3662733.659 272.2,-3813384.242, 3554431.149, 3662733.133 272.3,-3813384.877, 3554431.010, 3662732.611 272.4,-3813385.513, 3554430.865, 3662732.093 272.5,-3813386.151, 3554430.714, 3662731.578 272.6,-3813386.790, 3554430.558, 3662731.067 272.7,-3813387.432, 3554430.396, 3662730.560 272.8,-3813388.074, 3554430.229, 3662730.057 272.9,-3813388.719, 3554430.056, 3662729.558 273.0,-3813389.364, 3554429.877, 3662729.062 273.1,-3813390.011, 3554429.693, 3662728.570 273.2,-3813390.660, 3554429.504, 3662728.082 273.3,-3813391.310, 3554429.308, 3662727.599 273.4,-3813391.961, 3554429.108, 3662727.119 273.5,-3813392.613, 3554428.901, 3662726.643 273.6,-3813393.267, 3554428.689, 3662726.171 273.7,-3813393.921, 3554428.472, 3662725.704 273.8,-3813394.577, 3554428.249, 3662725.240 273.9,-3813395.234, 3554428.021, 3662724.781 274.0,-3813395.892, 3554427.788, 3662724.326 274.1,-3813396.551, 3554427.549, 3662723.875 274.2,-3813397.210, 3554427.304, 3662723.428 274.3,-3813397.871, 3554427.054, 3662722.986 274.4,-3813398.532, 3554426.799, 3662722.548 274.5,-3813399.194, 3554426.538, 3662722.115 274.6,-3813399.857, 3554426.273, 3662721.685 274.7,-3813400.521, 3554426.001, 3662721.260 274.8,-3813401.185, 3554425.725, 3662720.840 274.9,-3813401.850, 3554425.443, 3662720.424 275.0,-3813402.515, 3554425.156, 3662720.013 275.1,-3813403.181, 3554424.864, 3662719.606 275.2,-3813403.848, 3554424.566, 3662719.203 275.3,-3813404.515, 3554424.263, 3662718.806 275.4,-3813405.182, 3554423.955, 3662718.413 275.5,-3813405.849, 3554423.642, 3662718.024 275.6,-3813406.517, 3554423.324, 3662717.640 275.7,-3813407.185, 3554423.001, 3662717.261 275.8,-3813407.853, 3554422.673, 3662716.887 275.9,-3813408.521, 3554422.339, 3662716.517 276.0,-3813409.190, 3554422.001, 3662716.152 276.1,-3813409.858, 3554421.657, 3662715.792 276.2,-3813410.526, 3554421.309, 3662715.437 276.3,-3813411.195, 3554420.955, 3662715.086 276.4,-3813411.863, 3554420.597, 3662714.741 276.5,-3813412.531, 3554420.233, 3662714.400 276.6,-3813413.199, 3554419.865, 3662714.064 276.7,-3813413.867, 3554419.492, 3662713.734 276.8,-3813414.534, 3554419.114, 3662713.408 276.9,-3813415.201, 3554418.731, 3662713.087 277.0,-3813415.868, 3554418.343, 3662712.771 277.1,-3813416.534, 3554417.951, 3662712.460 277.2,-3813417.200, 3554417.554, 3662712.154 277.3,-3813417.865, 3554417.152, 3662711.854 277.4,-3813418.530, 3554416.746, 3662711.558 277.5,-3813419.194, 3554416.334, 3662711.268 277.6,-3813419.857, 3554415.919, 3662710.982 277.7,-3813420.520, 3554415.498, 3662710.702 277.8,-3813421.182, 3554415.073, 3662710.427 277.9,-3813421.843, 3554414.644, 3662710.157 278.0,-3813422.504, 3554414.210, 3662709.893 278.1,-3813423.163, 3554413.771, 3662709.633 278.2,-3813423.822, 3554413.328, 3662709.379 278.3,-3813424.479, 3554412.881, 3662709.131 278.4,-3813425.136, 3554412.429, 3662708.887 278.5,-3813425.791, 3554411.973, 3662708.649 278.6,-3813426.446, 3554411.513, 3662708.416 278.7,-3813427.099, 3554411.048, 3662708.188 278.8,-3813427.751, 3554410.579, 3662707.966 278.9,-3813428.402, 3554410.106, 3662707.749 279.0,-3813429.051, 3554409.629, 3662707.538 279.1,-3813429.699, 3554409.147, 3662707.331 279.2,-3813430.346, 3554408.661, 3662707.131 279.3,-3813430.991, 3554408.172, 3662706.936 279.4,-3813431.635, 3554407.678, 3662706.746 279.5,-3813432.277, 3554407.180, 3662706.561 279.6,-3813432.918, 3554406.678, 3662706.382 279.7,-3813433.557, 3554406.172, 3662706.209 279.8,-3813434.195, 3554405.663, 3662706.041 279.9,-3813434.831, 3554405.149, 3662705.879 280.0,-3813435.465, 3554404.631, 3662705.722 280.1,-3813436.097, 3554404.110, 3662705.570 280.2,-3813436.727, 3554403.585, 3662705.424 280.3,-3813437.356, 3554403.056, 3662705.284 280.4,-3813437.983, 3554402.524, 3662705.149 280.5,-3813438.607, 3554401.988, 3662705.020 280.6,-3813439.230, 3554401.448, 3662704.896 280.7,-3813439.851, 3554400.904, 3662704.778 280.8,-3813440.469, 3554400.358, 3662704.666 280.9,-3813441.086, 3554399.807, 3662704.559 281.0,-3813441.700, 3554399.253, 3662704.458 281.1,-3813442.312, 3554398.696, 3662704.362 281.2,-3813442.922, 3554398.135, 3662704.272 281.3,-3813443.529, 3554397.571, 3662704.187 281.4,-3813444.134, 3554397.003, 3662704.109 281.5,-3813444.737, 3554396.433, 3662704.035 281.6,-3813445.338, 3554395.859, 3662703.968 281.7,-3813445.935, 3554395.281, 3662703.906 281.8,-3813446.531, 3554394.701, 3662703.850 281.9,-3813447.124, 3554394.117, 3662703.799 282.0,-3813447.714, 3554393.531, 3662703.754 282.1,-3813448.301, 3554392.941, 3662703.715 282.2,-3813448.886, 3554392.349, 3662703.681 282.3,-3813449.468, 3554391.753, 3662703.653 282.4,-3813450.048, 3554391.154, 3662703.631 282.5,-3813450.624, 3554390.553, 3662703.614 282.6,-3813451.198, 3554389.949, 3662703.603 282.7,-3813451.769, 3554389.342, 3662703.598 282.8,-3813452.337, 3554388.732, 3662703.598 282.9,-3813452.902, 3554388.120, 3662703.604 283.0,-3813453.464, 3554387.504, 3662703.616 283.1,-3813454.023, 3554386.887, 3662703.634 283.2,-3813454.579, 3554386.266, 3662703.657 283.3,-3813455.132, 3554385.643, 3662703.685 283.4,-3813455.682, 3554385.018, 3662703.720 283.5,-3813456.228, 3554384.390, 3662703.760 283.6,-3813456.771, 3554383.760, 3662703.805 283.7,-3813457.311, 3554383.127, 3662703.857 283.8,-3813457.848, 3554382.492, 3662703.914 283.9,-3813458.381, 3554381.855, 3662703.976 284.0,-3813458.911, 3554381.216, 3662704.045 284.1,-3813459.438, 3554380.574, 3662704.119 284.2,-3813459.961, 3554379.930, 3662704.198 284.3,-3813460.481, 3554379.284, 3662704.283 284.4,-3813460.997, 3554378.636, 3662704.374 284.5,-3813461.509, 3554377.986, 3662704.471 284.6,-3813462.018, 3554377.334, 3662704.573 284.7,-3813462.524, 3554376.681, 3662704.680 284.8,-3813463.025, 3554376.025, 3662704.794 284.9,-3813463.523, 3554375.367, 3662704.912 285.0,-3813464.017, 3554374.708, 3662705.037 285.1,-3813464.508, 3554374.047, 3662705.167 285.2,-3813464.995, 3554373.384, 3662705.302 285.3,-3813465.477, 3554372.720, 3662705.443 285.4,-3813465.956, 3554372.054, 3662705.590 285.5,-3813466.431, 3554371.386, 3662705.742 285.6,-3813466.903, 3554370.717, 3662705.900 285.7,-3813467.370, 3554370.047, 3662706.063 285.8,-3813467.833, 3554369.375, 3662706.232 285.9,-3813468.292, 3554368.702, 3662706.406 286.0,-3813468.747, 3554368.027, 3662706.586 286.1,-3813469.198, 3554367.351, 3662706.771 286.2,-3813469.645, 3554366.674, 3662706.961 286.3,-3813470.088, 3554365.996, 3662707.157 286.4,-3813470.526, 3554365.316, 3662707.359 286.5,-3813470.960, 3554364.636, 3662707.565 286.6,-3813471.390, 3554363.954, 3662707.778 286.7,-3813471.816, 3554363.272, 3662707.995 286.8,-3813472.238, 3554362.588, 3662708.218 286.9,-3813472.655, 3554361.904, 3662708.447 287.0,-3813473.067, 3554361.219, 3662708.680 287.1,-3813473.476, 3554360.533, 3662708.919 287.2,-3813473.880, 3554359.846, 3662709.163 287.3,-3813474.279, 3554359.159, 3662709.413 287.4,-3813474.674, 3554358.470, 3662709.668 287.5,-3813475.065, 3554357.782, 3662709.928 287.6,-3813475.450, 3554357.092, 3662710.193 287.7,-3813475.832, 3554356.403, 3662710.464 287.8,-3813476.209, 3554355.712, 3662710.739 287.9,-3813476.581, 3554355.022, 3662711.020 288.0,-3813476.948, 3554354.331, 3662711.306 288.1,-3813477.311, 3554353.639, 3662711.597 288.2,-3813477.669, 3554352.948, 3662711.894 288.3,-3813478.023, 3554352.256, 3662712.195 288.4,-3813478.372, 3554351.564, 3662712.501 288.5,-3813478.716, 3554350.872, 3662712.813 288.6,-3813479.055, 3554350.179, 3662713.129 288.7,-3813479.389, 3554349.487, 3662713.451 288.8,-3813479.719, 3554348.795, 3662713.777 288.9,-3813480.043, 3554348.102, 3662714.109 289.0,-3813480.363, 3554347.410, 3662714.445 289.1,-3813480.678, 3554346.718, 3662714.786 289.2,-3813480.988, 3554346.027, 3662715.133 289.3,-3813481.293, 3554345.335, 3662715.484 289.4,-3813481.593, 3554344.644, 3662715.840 289.5,-3813481.888, 3554343.953, 3662716.200 289.6,-3813482.178, 3554343.263, 3662716.566 289.7,-3813482.463, 3554342.573, 3662716.936 289.8,-3813482.743, 3554341.883, 3662717.311 289.9,-3813483.018, 3554341.194, 3662717.691 290.0,-3813483.288, 3554340.506, 3662718.076 290.1,-3813483.553, 3554339.818, 3662718.465 290.2,-3813483.812, 3554339.131, 3662718.858 290.3,-3813484.067, 3554338.445, 3662719.257 290.4,-3813484.316, 3554337.759, 3662719.660 290.5,-3813484.560, 3554337.075, 3662720.067 290.6,-3813484.799, 3554336.391, 3662720.479 290.7,-3813485.033, 3554335.708, 3662720.896 290.8,-3813485.261, 3554335.026, 3662721.317 290.9,-3813485.485, 3554334.345, 3662721.742 291.0,-3813485.703, 3554333.666, 3662722.172 291.1,-3813485.915, 3554332.987, 3662722.606 291.2,-3813486.123, 3554332.309, 3662723.045 291.3,-3813486.325, 3554331.633, 3662723.488 291.4,-3813486.522, 3554330.958, 3662723.935 291.5,-3813486.713, 3554330.284, 3662724.386 291.6,-3813486.899, 3554329.612, 3662724.842 291.7,-3813487.080, 3554328.941, 3662725.302 291.8,-3813487.255, 3554328.271, 3662725.766 291.9,-3813487.425, 3554327.603, 3662726.234 292.0,-3813487.590, 3554326.936, 3662726.706 292.1,-3813487.749, 3554326.271, 3662727.183 292.2,-3813487.903, 3554325.608, 3662727.663 292.3,-3813488.052, 3554324.946, 3662728.147 292.4,-3813488.195, 3554324.286, 3662728.636 292.5,-3813488.332, 3554323.628, 3662729.128 292.6,-3813488.464, 3554322.972, 3662729.624 292.7,-3813488.591, 3554322.317, 3662730.124 292.8,-3813488.712, 3554321.665, 3662730.628 292.9,-3813488.827, 3554321.014, 3662731.135 293.0,-3813488.938, 3554320.365, 3662731.647 293.1,-3813489.042, 3554319.718, 3662732.162 293.2,-3813489.141, 3554319.074, 3662732.681 293.3,-3813489.235, 3554318.431, 3662733.203 293.4,-3813489.323, 3554317.791, 3662733.729 293.5,-3813489.406, 3554317.153, 3662734.259 293.6,-3813489.483, 3554316.517, 3662734.792 293.7,-3813489.554, 3554315.884, 3662735.328 293.8,-3813489.620, 3554315.253, 3662735.869 293.9,-3813489.681, 3554314.624, 3662736.412 294.0,-3813489.736, 3554313.998, 3662736.959 294.1,-3813489.785, 3554313.374, 3662737.509 294.2,-3813489.829, 3554312.752, 3662738.063 294.3,-3813489.867, 3554312.134, 3662738.620 294.4,-3813489.900, 3554311.517, 3662739.180 294.5,-3813489.927, 3554310.904, 3662739.743 294.6,-3813489.948, 3554310.293, 3662740.310 294.7,-3813489.964, 3554309.685, 3662740.879 294.8,-3813489.975, 3554309.080, 3662741.452 294.9,-3813489.979, 3554308.477, 3662742.028 295.0,-3813489.979, 3554307.878, 3662742.607 295.1,-3813489.972, 3554307.281, 3662743.188 295.2,-3813489.961, 3554306.687, 3662743.773 295.3,-3813489.943, 3554306.097, 3662744.360 295.4,-3813489.920, 3554305.509, 3662744.951 295.5,-3813489.891, 3554304.924, 3662745.544 295.6,-3813489.857, 3554304.343, 3662746.140 295.7,-3813489.818, 3554303.764, 3662746.738 295.8,-3813489.772, 3554303.189, 3662747.339 295.9,-3813489.722, 3554302.617, 3662747.943 296.0,-3813489.665, 3554302.048, 3662748.550 296.1,-3813489.603, 3554301.483, 3662749.159 296.2,-3813489.536, 3554300.921, 3662749.770 296.3,-3813489.463, 3554300.362, 3662750.384 296.4,-3813489.384, 3554299.807, 3662751.001 296.5,-3813489.300, 3554299.255, 3662751.620 296.6,-3813489.211, 3554298.707, 3662752.241 296.7,-3813489.115, 3554298.162, 3662752.864 296.8,-3813489.015, 3554297.621, 3662753.490 296.9,-3813488.909, 3554297.083, 3662754.118 297.0,-3813488.797, 3554296.550, 3662754.748 297.1,-3813488.680, 3554296.019, 3662755.380 297.2,-3813488.557, 3554295.493, 3662756.014 297.3,-3813488.429, 3554294.970, 3662756.650 297.4,-3813488.296, 3554294.451, 3662757.289 297.5,-3813488.157, 3554293.936, 3662757.929 297.6,-3813488.012, 3554293.425, 3662758.571 297.7,-3813487.863, 3554292.918, 3662759.215 297.8,-3813487.707, 3554292.415, 3662759.861 297.9,-3813487.547, 3554291.915, 3662760.508 298.0,-3813487.380, 3554291.420, 3662761.157 298.1,-3813487.209, 3554290.929, 3662761.808 298.2,-3813487.032, 3554290.441, 3662762.461 298.3,-3813486.850, 3554289.958, 3662763.115 298.4,-3813486.662, 3554289.479, 3662763.771 298.5,-3813486.469, 3554289.004, 3662764.428 298.6,-3813486.271, 3554288.534, 3662765.086 298.7,-3813486.068, 3554288.068, 3662765.746 298.8,-3813485.859, 3554287.606, 3662766.408 298.9,-3813485.645, 3554287.148, 3662767.070 299.0,-3813485.425, 3554286.695, 3662767.734 299.1,-3813485.201, 3554286.246, 3662768.399 299.2,-3813484.971, 3554285.801, 3662769.066 299.3,-3813484.736, 3554285.361, 3662769.733 299.4,-3813484.495, 3554284.925, 3662770.401 299.5,-3813484.250, 3554284.494, 3662771.071 299.6,-3813483.999, 3554284.067, 3662771.741 299.7,-3813483.743, 3554283.645, 3662772.413 299.8,-3813483.482, 3554283.228, 3662773.085 299.9,-3813483.216, 3554282.815, 3662773.758 ================================================ FILE: fifo.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include #include "fifo.h" static pthread_mutex_t fifo_mutex = PTHREAD_MUTEX_INITIALIZER; // mutex protecting the queues static pthread_cond_t fifo_notempty_cond = PTHREAD_COND_INITIALIZER; // condition used to signal FIFO-not-empty static pthread_cond_t fifo_empty_cond = PTHREAD_COND_INITIALIZER; // condition used to signal FIFO-empty static pthread_cond_t fifo_free_cond = PTHREAD_COND_INITIALIZER; // condition used to signal freelist-not-empty static pthread_cond_t fifo_full_cond = PTHREAD_COND_INITIALIZER; // condition used to signal FIFO-full static struct iq_buf *fifo_head; // head of queued buffers awaiting demodulation static struct iq_buf *fifo_tail; // tail of queued buffers awaiting demodulation static struct iq_buf *fifo_freelist; // freelist of preallocated buffers static bool fifo_halted; // true if queue has been halted // Create the queue structures. Not threadsafe. bool fifo_create(unsigned buffer_count, unsigned buffer_size, unsigned sample_size) { for (unsigned i = 0; i < buffer_count; ++i) { struct iq_buf *newbuf; if (!(newbuf = calloc(1, sizeof (*newbuf)))) { goto nomem; } if (sample_size == sizeof (signed short)) { if (!(newbuf->data16 = calloc(buffer_size, sizeof (newbuf->data16[0])))) { free(newbuf); goto nomem; } newbuf->data8 = NULL; } else { if (!(newbuf->data8 = calloc(buffer_size, sizeof (newbuf->data8[0])))) { free(newbuf); goto nomem; } newbuf->data16 = NULL; } newbuf->totalLength = buffer_size; newbuf->validLength = 0; newbuf->next = fifo_freelist; fifo_freelist = newbuf; } return true; nomem: fifo_destroy(); return false; } static void free_buffer_list(struct iq_buf *head) { while (head) { struct iq_buf *next = head->next; free(head->data8); free(head->data16); free(head); head = next; } } void fifo_destroy() { free_buffer_list(fifo_head); free_buffer_list(fifo_freelist); fifo_freelist = NULL; fifo_head = fifo_tail = NULL; pthread_cond_destroy(&fifo_notempty_cond); pthread_cond_destroy(&fifo_full_cond); pthread_cond_destroy(&fifo_free_cond); pthread_cond_destroy(&fifo_empty_cond); pthread_mutex_destroy(&fifo_mutex); } void fifo_wait_next() { pthread_mutex_lock(&fifo_mutex); while (fifo_head && !fifo_halted) { pthread_cond_wait(&fifo_empty_cond, &fifo_mutex); } pthread_mutex_unlock(&fifo_mutex); } void fifo_wait_full() { pthread_mutex_lock(&fifo_mutex); if (!fifo_halted) { pthread_cond_wait(&fifo_full_cond, &fifo_mutex); } pthread_mutex_unlock(&fifo_mutex); } void fifo_halt() { pthread_mutex_lock(&fifo_mutex); // Drain all enqueued buffers to the freelist while (fifo_head) { struct iq_buf *freebuf = fifo_head; fifo_head = freebuf->next; freebuf->next = fifo_freelist; fifo_freelist = freebuf; } fifo_tail = NULL; fifo_halted = true; // wake all waiters pthread_cond_broadcast(&fifo_notempty_cond); pthread_cond_broadcast(&fifo_empty_cond); pthread_cond_broadcast(&fifo_free_cond); pthread_cond_broadcast(&fifo_full_cond); pthread_mutex_unlock(&fifo_mutex); } struct iq_buf *fifo_acquire(void) { pthread_mutex_lock(&fifo_mutex); struct iq_buf *result = NULL; if (!fifo_halted && !fifo_freelist) { pthread_cond_signal(&fifo_full_cond); // No free buffers, wait for one pthread_cond_wait(&fifo_free_cond, &fifo_mutex); } if (!fifo_halted) { result = fifo_freelist; fifo_freelist = result->next; result->validLength = 0; result->next = NULL; } pthread_mutex_unlock(&fifo_mutex); return result; } void fifo_enqueue(struct iq_buf *buf) { assert(buf->validLength <= buf->totalLength); pthread_mutex_lock(&fifo_mutex); if (fifo_halted) { // Shutting down, just return the buffer to the freelist. buf->next = fifo_freelist; fifo_freelist = buf; goto done; } // enqueue and tell the main thread buf->next = NULL; if (!fifo_head) { fifo_head = fifo_tail = buf; pthread_cond_signal(&fifo_notempty_cond); } else { fifo_tail->next = buf; } done: pthread_mutex_unlock(&fifo_mutex); } struct iq_buf *fifo_dequeue(void) { pthread_mutex_lock(&fifo_mutex); struct iq_buf *result = NULL; if (!fifo_head && !fifo_halted) { // No data pending, wait for some pthread_cond_wait(&fifo_notempty_cond, &fifo_mutex); } if (!fifo_halted) { result = fifo_head; fifo_head = result->next; result->next = NULL; if (!fifo_head) { fifo_tail = NULL; pthread_cond_broadcast(&fifo_empty_cond); } } pthread_mutex_unlock(&fifo_mutex); return result; } void fifo_release(struct iq_buf *buf) { pthread_mutex_lock(&fifo_mutex); if (!fifo_freelist) { pthread_cond_signal(&fifo_free_cond); } buf->next = fifo_freelist; fifo_freelist = buf; pthread_mutex_unlock(&fifo_mutex); } ================================================ FILE: fifo.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef FIFO_H #define FIFO_H #include #include struct iq_buf { signed char *data8; // 8 bit IQ data signed short *data16; // 16 bit IQ data unsigned int totalLength; // Maximum number of samples (allocated size of "data") unsigned int validLength; // Number of valid samples in "data" struct iq_buf *next; // linked list forward link }; // Create the queue structures. Not threadsafe. Returns true on success. // // buffer_count - the number of buffers to preallocate // buffer_size - the size of each IQ buffer, in samples bool fifo_create(unsigned buffer_count, unsigned buffer_size, unsigned sample_size); // Destroy the fifo structures allocated in fifo_create. Not threadsafe; ensure all FIFO users // are done before calling. void fifo_destroy(); // Block until the FIFO is empty. void fifo_wait_next(); // Block until the FIFO is full. void fifo_wait_full(); // Mark the FIFO as halted. Move any buffers in FIFO to the freelist immediately. void fifo_halt(); // Get an unused buffer from the freelist and return it. // Block waiting for a free buffer. // free buffers available within the timeout, or if the FIFO is halted. struct iq_buf *fifo_acquire(void); // Put a filled buffer (previously obtained from fifo_acquire) onto the head of the FIFO. // The caller should have filled: // buf->validLength // buf->data[0 .. buf->validLength-1] void fifo_enqueue(struct iq_buf *buf); // Get a buffer from the tail of the FIFO. // If the FIFO is halted (or becomes halted), return NULL immediately. // return NULL if no data arrives struct iq_buf *fifo_dequeue(void); // Release a buffer previously returned by fifo_acquire() back to the freelist. void fifo_release(struct iq_buf *buf); #endif ================================================ FILE: gps-sim.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include #include #include #include #include "help.h" #include "gui.h" #include "sdr.h" #include "gps-sim.h" simulator_t simulator; static error_t parse_opt(int key, char *arg, struct argp_state *state); const char *argp_program_version = "v1.0"; const char args_doc[] = ""; const char doc[] = "gps-sim generates a GPS L1 baseband signal IQ data stream, which is then transmitted by a software-defined radio (SDR) platform."; static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; static error_t parse_opt(int key, char *arg, struct argp_state *state) { double d = 0.0; switch (key) { case 'e': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.nav_file_name = strdup(arg); break; case 'r': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.sdr_name = strdup(arg); break; case 'U': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.pluto_uri = strdup(arg); break; case 'N': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.pluto_hostname = strdup(arg); break; case 'm': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.motion_file_name = strdup(arg); simulator.interactive_mode = false; break; case 701: // --station if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.station_id = strndup(arg, 9); break; case 'f': simulator.use_ftp = true; break; case 'l': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } sscanf(arg, "%lf,%lf,%lf", &simulator.location.lat, &simulator.location.lon, &simulator.location.height); break; case 's': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } if (strncmp(arg, "now", 3) == 0) { simulator.time_overwrite = true; time_t timer; struct tm *gmt; time(&timer); gmt = gmtime(&timer); simulator.start.y = gmt->tm_year + 1900; simulator.start.m = gmt->tm_mon + 1; simulator.start.d = gmt->tm_mday; simulator.start.hh = gmt->tm_hour; simulator.start.mm = gmt->tm_min; simulator.start.sec = (double) gmt->tm_sec; } else { sscanf(arg, "%d/%d/%d,%d:%d:%lf", &simulator.start.y, &simulator.start.m, &simulator.start.d, &simulator.start.hh, &simulator.start.mm, &simulator.start.sec); } if (simulator.start.y <= 1980 || simulator.start.m < 1 || simulator.start.m > 12 || simulator.start.d < 1 || simulator.start.d > 31 || simulator.start.hh < 0 || simulator.start.hh > 23 || simulator.start.mm < 0 || simulator.start.mm > 59 || simulator.start.sec < 0.0 || simulator.start.sec >= 60.0) { fprintf(stderr, "ERROR: Invalid date and time.\n"); return ARGP_ERR_UNKNOWN; } break; case 'I': simulator.ionosphere_enable = false; break; case 'v': simulator.show_verbose = true; break; case 'a': simulator.enable_tx_amp = true; break; case 'g': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.tx_gain = (int) strtoul(arg, NULL, 10); break; case 'd': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } d = atof(arg); if (d < 0.0 || d > ((double) USER_MOTION_SIZE) / 10.0) { gui_status_wprintw(RED, "Error: Invalid duration.\n"); return -1; } simulator.duration = (int) (d * 10.0 + 0.5); break; case 't': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.target.valid = true; sscanf(arg, "%lf,%lf,%lf", &simulator.target.distance, &simulator.target.bearing, &simulator.target.height); simulator.target.bearing *= 1000; break; case 'i': simulator.interactive_mode = true; break; case '3': simulator.use_rinex3 = true; break; case 'p': if (arg == NULL) { return ARGP_ERR_UNKNOWN; } simulator.ppb = atoi(arg); break; case 700: // --iq16 simulator.sample_size = SC16; break; case 702: // --disable-almanac simulator.almanac_enable = false; break; case ARGP_KEY_END: if (state->arg_num > 0) /* We use only options but no arguments */ argp_usage(state); break; default: return ARGP_ERR_UNKNOWN; } return 0; } static void simulator_init(void) { simulator.main_exit = false; simulator.show_verbose = false; simulator.ionosphere_enable = true; simulator.gps_thread_running = false; simulator.interactive_mode = false; simulator.use_ftp = false; simulator.enable_tx_amp = false; simulator.use_rinex3 = false; simulator.time_overwrite = false; simulator.almanac_enable = true; simulator.duration = USER_MOTION_SIZE; simulator.tx_gain = 0; simulator.ppb = 0; simulator.location.lat = 0; simulator.location.lon = 0; simulator.location.height = 0; simulator.target.bearing = 0; simulator.target.distance = 0; simulator.target.lat = 0; simulator.target.lon = 0; simulator.target.height = 0; simulator.target.valid = false; simulator.nav_file_name = NULL; simulator.sdr_name = NULL; simulator.pluto_hostname = NULL; simulator.motion_file_name = NULL; simulator.pluto_uri = NULL; simulator.station_id = NULL; simulator.sdr_type = SDR_NONE; simulator.sample_size = SC08; pthread_cond_init(&simulator.gps_init_done, NULL); pthread_mutex_init(&simulator.gps_lock, NULL); } static void signal_handler(int sig) { signal(sig, SIG_DFL); // Reset signal handler simulator.main_exit = true; gui_status_wprintw(RED, "Caught signal %s, shutting down\n", strsignal(sig)); } static void cleanup_and_exit(int code) { simulator.gps_thread_exit = true; pthread_join(simulator.gps_thread, NULL); /* Wait on GPS read thread exit */ pthread_cond_destroy(&simulator.gps_init_done); pthread_mutex_destroy(&simulator.gps_lock); /* Free when pointing to string in heap (strdup allocated when given as run option) */ free(simulator.nav_file_name); free(simulator.sdr_name); free(simulator.pluto_hostname); free(simulator.pluto_uri); free(simulator.motion_file_name); free(simulator.station_id); sdr_close(); gui_destroy(); fflush(stdout); exit(code); } /* Set trhead name if supported. */ void set_thread_name(const char *name) { #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 12) pthread_setname_np(pthread_self(), name); #else NOTUSED(name); #endif } // Set affinity of calling thread to specific core on a multi-core CPU int thread_to_core(int core_id) { int num_cores = sysconf(_SC_NPROCESSORS_ONLN); if (core_id < 0 || core_id >= num_cores) return EINVAL; cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(core_id, &cpuset); pthread_t current_thread = pthread_self(); return pthread_setaffinity_np(current_thread, sizeof (cpu_set_t), &cpuset); } /* * */ int main(int argc, char** argv) { int ch = 0; bool is_info_shown = false; bool is_help_shown = false; // signal handlers: signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGABRT, signal_handler); // Initialize all simulator variables simulator_init(); /* On a multi-core CPU we run the main thread and reader thread on different cores. * Try sticking the main thread to core 1 */ thread_to_core(1); set_thread_name("simulator-thread"); // Parse the command line options if (argp_parse(&argp, argc, argv, 0, 0, 0)) { return (EXIT_FAILURE); } if (simulator.nav_file_name == NULL && simulator.use_ftp == false) { fprintf(stderr, "Error: GPS ephemeris file is not specified\n"); return (EXIT_FAILURE); } gui_init(); // No access to GUI until this point if (simulator.interactive_mode && simulator.motion_file_name != NULL) { simulator.interactive_mode = false; simulator.target.valid = false; gui_status_wprintw(YELLOW, "User motion file supplied. Interactive mode disabled!\n"); } // Wait maximum 30 seconds for GPS thread to become ready struct timespec timeout; struct timeval now; gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec + 30; timeout.tv_nsec = 0; // Init prior GPS thread, creates FIFO. if (sdr_init(&simulator) == 0) { // Start GPS baseband signal generation gui_top_panel(LS_FIX); pthread_create(&simulator.gps_thread, NULL, gps_thread_ep, &simulator); pthread_mutex_lock(&(simulator.gps_lock)); int ret = pthread_cond_timedwait(&(simulator.gps_init_done), &(simulator.gps_lock), &timeout); pthread_mutex_unlock(&(simulator.gps_lock)); if (ret == ETIMEDOUT) { gui_status_wprintw(RED, "Time out waiting for GPS thread. Running?\n"); } if (!simulator.gps_thread_exit) { if (sdr_run() != 0) { gui_status_wprintw(RED, "Starting SDR streaming failed.\n"); simulator.gps_thread_exit = true; } } } // Run this until we get a termination signal. while (!simulator.main_exit) { ch = gui_getch(); if (ch != -1) { switch (ch) { case 'x': case 'X': simulator.main_exit = true; break; case 'i': case 'I': gui_show_panel(INFO, ON); is_info_shown = true; break; case '?': case 'h': case 'H': gui_show_panel(HELP, ON); is_help_shown = true; break; case 9: // TAB gui_toggle_current_panel(); break; case 265: // F1 gui_top_panel(TRACK); break; case 266: // F2 gui_top_panel(LS_FIX); break; case 267: // F3 gui_top_panel(KF_FIX); break; case LEFT_KEY: simulator.target.bearing -= 127.0; if (simulator.target.bearing < 0) simulator.target.bearing = 360000.0; if (simulator.target.bearing > 360000) simulator.target.bearing = 0; gui_show_heading((float) (simulator.target.bearing / 1000)); break; case RIGHT_KEY: simulator.target.bearing += 127.0; if (simulator.target.bearing < 0) simulator.target.bearing = 360000.0; if (simulator.target.bearing > 360000) simulator.target.bearing = 0; gui_show_heading((float) (simulator.target.bearing / 1000)); break; case UP_KEY: simulator.target.vertical_speed += 1; gui_show_vertical_speed((float) simulator.target.vertical_speed); break; case DOWN_KEY: simulator.target.vertical_speed -= 1; gui_show_vertical_speed((float) simulator.target.vertical_speed); break; case UPSPEED_KEY: simulator.target.speed += 1.0; simulator.target.velocity = simulator.target.speed / 100.0; gui_show_speed((float) (simulator.target.velocity * 3.6)); break; case DOWNSPEED_KEY: simulator.target.speed -= 1.0; if (simulator.target.speed < 0) simulator.target.speed = 0; simulator.target.velocity = simulator.target.speed / 100.0; gui_show_speed((float) (simulator.target.velocity * 3.6)); break; case GAIN_INC_KEY: simulator.tx_gain = sdr_set_gain(simulator.tx_gain + 1); gui_status_wprintw(GREEN, "Gain: %ddB.\r", simulator.tx_gain); break; case GAIN_DEC_KEY: simulator.tx_gain = sdr_set_gain(simulator.tx_gain - 1); gui_status_wprintw(GREEN, "Gain: %ddB.\r", simulator.tx_gain); break; default: if (is_info_shown) { gui_show_panel(INFO, OFF); is_info_shown = false; } if (is_help_shown) { gui_show_panel(HELP, OFF); is_help_shown = false; } break; } } } cleanup_and_exit(EXIT_SUCCESS); return (EXIT_SUCCESS); } ================================================ FILE: gps-sim.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef GPS_SIM_H #define GPS_SIM_H #include #include #include #include #include #include #include "gps.h" #define NOTUSED(V) ((void) V) // Sampling data format #define SC08 sizeof(signed char) #define SC16 sizeof(signed short) /* SDR device types */ typedef enum { SDR_NONE = 0, SDR_IQFILE, SDR_HACKRF, SDR_PLUTOSDR } sdr_type_t; /* Target information. */ typedef struct { double bearing; double distance; double lat; double lon; double height; double velocity; double speed; double vertical_speed; bool valid; } target_t; /* Simulator location. */ typedef struct { double lat; // Latitude double lon; // Longitude double height; // Height/Elevation } location_t; /* All the GPS simulators variables. */ typedef struct { atomic_bool main_exit; atomic_bool gps_thread_exit; atomic_bool gps_thread_running; bool show_verbose; bool ionosphere_enable; bool interactive_mode; bool use_ftp; bool enable_tx_amp; bool use_rinex3; bool time_overwrite; bool almanac_enable; int duration; int tx_gain; int ppb; int sample_size; sdr_type_t sdr_type; char *nav_file_name; char *motion_file_name; char *sdr_name; char *pluto_uri; char *pluto_hostname; char *station_id; pthread_mutex_t gps_lock; pthread_t gps_thread; pthread_cond_t gps_init_done; // Condition signals GPS thread is running location_t location; // Simulator geo location target_t target; // Target information datetime_t start; // Simulation start time } simulator_t; void set_thread_name(const char *name); int thread_to_core(int core_id); #endif /* GPS_SIM_H */ ================================================ FILE: gps.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include #include #include #include #include "sdr.h" #include "gui.h" #include "fifo.h" #include "almanac.h" #include "gps-sim.h" /** * Note: * Not all stations provide RINEX data with ionosphere data. */ /** * Stations providing Rinex v3 format. * 4-characters station ID * 9-characters station ID * Station name * Including Ionosphere data in Rinex file */ const stations_t stations_v3[] = { {"func", "FUNC00PRT", "Funchal"}, {"flrs", "FLRS00PRT", "Santa Cruz das Flore"}, {"pdel", "PDEL00PRT", "PONTA DELGADA"}, {NULL, NULL, NULL} // Always last entry }; /** * Stations providing Rinex v2 format. * 4-characters station ID * 9-characters station ID * Station name */ const stations_t stations_v2[] = { {"abmf", "ABMF00GLP", "Aeroport du Raizet"}, {"aggo", "AGGO00ARG", "AGGO"}, {"ajac", "AJAC00FRA", "Ajaccio"}, {"ankr", "ANKR00TUR", "Ankara"}, {"areg", "AREG00PER", "Arequipa"}, {"ascg", "ASCG00SHN", "Ascension"}, {"bogi", "BOGI00POL", "Borowa Gora"}, {"bor1", "BOR100POL", "Borowiec"}, {"brst", "BRST00FRA", "Brest"}, {"chpg", "CHPG00BRA", "Cachoeira Paulista"}, {"cibg", "CIBG00IDN", "Cibinong"}, {"cpvg", "CPVG00CPV", "CAP-VERT"}, {"djig", "DJIG00DJI", "Djibouti"}, {"dlf1", "DLF100NLD", "Delft"}, {"ffmj", "FFMJ00DEU", "Frankfurt/Main"}, {"ftna", "FTNA00WLF", "Futuna"}, {"gamb", "GAMB00PYF", "Rikitea"}, {"gamg", "GAMG00KOR", "Geochang"}, {"glps", "GLPS00ECU", "Galapagos Permanent Station"}, {"glsv", "GLSV00UKR", "Kiev/Golosiiv"}, {"gmsd", "GMSD00JPN", "GUTS Masda"}, {"gop6", "GOP600CZE", "Pecny, Ondrejov"}, {"gop7", "GOP700CZE", "Pecny, Ondrejov"}, {"gope", "GOPE00CZE", "Pecny, Ondrejov"}, {"grac", "GRAC00FRA", "Grasse"}, {"gras", "GRAS00FRA", "Observatoire de Calern - OCA"}, {"holb", "HOLB00CAN", "Holberg"}, {"hueg", "HUEG00DEU", "Huegelheim"}, {"ieng", "IENG00ITA", "Torino"}, {"ista", "ISTA00TUR", "Istanbul"}, {"izmi", "IZMI00TUR", "Izmir"}, {"jfng", "JFNG00CHN", "Juifeng"}, {"joz2", "JOZ200POL", "Jozefoslaw"}, {"joze", "JOZE00POL", "Jozefoslaw"}, {"kerg", "KERG00ATF", "Kerguelen Islands"}, {"kitg", "KITG00UZB", "Kitab"}, {"koug", "KOUG00GUF", "Kourou"}, {"krgg", "KRGG00ATF", "Kerguelen Islands"}, {"krs1", "KRS100TUR", "Kars"}, {"lama", "LAMA00POL", "Lamkowo"}, {"leij", "LEIJ00DEU", "Leipzig"}, {"lmmf", "LMMF00MTQ", "Aeroport Aime CESAIRE-LE LAMENTIN"}, {"lroc", "LROC00FRA", "La Rochelle"}, {"mad2", "MAD200ESP", "Madrid Deep Space Tracking Station"}, {"madr", "MADR00ESP", "Madrid Deep Space Tracking Station"}, {"mayg", "MAYG00MYT", "Dzaoudzi"}, {"mers", "MERS00TUR", "Mersin"}, {"mikl", "MIKL00UKR", "Mykolaiv"}, {"morp", "MORP00GBR", "Morpeth"}, {"nklg", "NKLG00GAB", "N'KOLTANG"}, {"nyal", "NYAL00NOR", "Ny-Alesund"}, {"nya1", "NYA100NOR", "Ny-Alesund"}, {"ohi2", "OHI200ATA", "O'Higgins"}, {"orid", "ORID00MKD", "Ohrid"}, {"owmg", "OWMG00NZL", "Chatham Island"}, {"polv", "POLV00UKR", "Poltava"}, {"ptbb", "PTBB00DEU", "Braunschweig"}, {"ptgg", "PTGG00PHL", "Manilla"}, {"rabt", "RABT00MAR", "Rabat, EMI"}, {"reun", "REUN00REU", "La Reunion - Observatoire Volcanologique"}, {"rgdg", "RGDG00ARG", "Rio Grande"}, {"riga", "RIGA00LVA", "RIGA permanent GPS"}, {"seyg", "SEYG00SYC", "Mahe"}, {"sofi", "SOFI00BGR", "Sofia"}, {"stj3", "STJ300CAN", "STJ3 CACS-GSD"}, {"sulp", "SULP00UKR", "Lviv Polytechnic"}, {"svtl", "SVTL00RUS", "Svetloe"}, {"tana", "TANA00ETH", "ILA, Bahir Dar University"}, {"thtg", "THTG00PYF", "Papeete Tahiti"}, {"thti", "THTI00PYF", "Tahiti"}, {"tit2", "TIT200DEU", "Titz / Jackerath"}, {"tlse", "TLSE00FRA", "Toulouse"}, {"tro1", "TRO100NOR", "Tromsoe"}, {"warn", "WARN00DEU", "Warnemuende"}, {"whit", "WHIT00CAN", "WHIT CACS-GSD"}, {"wroc", "WROC00POL", "Wroclaw"}, {"wtza", "WTZA00DEU", "Wettzell"}, {"yel2", "YEL200CAN", "Yellow Knife"}, {"zeck", "ZECK00RUS", "Zelenchukskaya"}, {"zim2", "ZIM200CHE", "Zimmerwald"}, {"zimm", "ZIMM00CHE", "Zimmerwald L+T 88"}, {NULL, NULL, NULL} // Always last entry }; static char rinex_date[21]; struct ftp_file { const char *filename; FILE *stream; }; const int sinTable512[] = { 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 91, 94, 97, 100, 103, 105, 108, 111, 114, 116, 119, 122, 125, 127, 130, 132, 135, 138, 140, 143, 145, 148, 150, 153, 155, 157, 160, 162, 164, 167, 169, 171, 173, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 205, 207, 209, 210, 212, 214, 215, 217, 218, 220, 221, 223, 224, 225, 227, 228, 229, 230, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 245, 246, 247, 247, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 249, 249, 249, 249, 248, 248, 248, 247, 247, 246, 245, 245, 244, 244, 243, 242, 241, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 230, 229, 228, 227, 225, 224, 223, 221, 220, 218, 217, 215, 214, 212, 210, 209, 207, 205, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 173, 171, 169, 167, 164, 162, 160, 157, 155, 153, 150, 148, 145, 143, 140, 138, 135, 132, 130, 127, 125, 122, 119, 116, 114, 111, 108, 105, 103, 100, 97, 94, 91, 89, 86, 83, 80, 77, 74, 71, 68, 65, 62, 59, 56, 53, 50, 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, -2, -5, -8, -11, -14, -17, -20, -23, -26, -29, -32, -35, -38, -41, -44, -47, -50, -53, -56, -59, -62, -65, -68, -71, -74, -77, -80, -83, -86, -89, -91, -94, -97, -100, -103, -105, -108, -111, -114, -116, -119, -122, -125, -127, -130, -132, -135, -138, -140, -143, -145, -148, -150, -153, -155, -157, -160, -162, -164, -167, -169, -171, -173, -176, -178, -180, -182, -184, -186, -188, -190, -192, -194, -196, -198, -200, -202, -204, -205, -207, -209, -210, -212, -214, -215, -217, -218, -220, -221, -223, -224, -225, -227, -228, -229, -230, -232, -233, -234, -235, -236, -237, -238, -239, -240, -241, -241, -242, -243, -244, -244, -245, -245, -246, -247, -247, -248, -248, -248, -249, -249, -249, -249, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -249, -249, -249, -249, -248, -248, -248, -247, -247, -246, -245, -245, -244, -244, -243, -242, -241, -241, -240, -239, -238, -237, -236, -235, -234, -233, -232, -230, -229, -228, -227, -225, -224, -223, -221, -220, -218, -217, -215, -214, -212, -210, -209, -207, -205, -204, -202, -200, -198, -196, -194, -192, -190, -188, -186, -184, -182, -180, -178, -176, -173, -171, -169, -167, -164, -162, -160, -157, -155, -153, -150, -148, -145, -143, -140, -138, -135, -132, -130, -127, -125, -122, -119, -116, -114, -111, -108, -105, -103, -100, -97, -94, -91, -89, -86, -83, -80, -77, -74, -71, -68, -65, -62, -59, -56, -53, -50, -47, -44, -41, -38, -35, -32, -29, -26, -23, -20, -17, -14, -11, -8, -5, -2 }; const int cosTable512[] = { 250, 250, 250, 250, 250, 249, 249, 249, 249, 248, 248, 248, 247, 247, 246, 245, 245, 244, 244, 243, 242, 241, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 230, 229, 228, 227, 225, 224, 223, 221, 220, 218, 217, 215, 214, 212, 210, 209, 207, 205, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 173, 171, 169, 167, 164, 162, 160, 157, 155, 153, 150, 148, 145, 143, 140, 138, 135, 132, 130, 127, 125, 122, 119, 116, 114, 111, 108, 105, 103, 100, 97, 94, 91, 89, 86, 83, 80, 77, 74, 71, 68, 65, 62, 59, 56, 53, 50, 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, -2, -5, -8, -11, -14, -17, -20, -23, -26, -29, -32, -35, -38, -41, -44, -47, -50, -53, -56, -59, -62, -65, -68, -71, -74, -77, -80, -83, -86, -89, -91, -94, -97, -100, -103, -105, -108, -111, -114, -116, -119, -122, -125, -127, -130, -132, -135, -138, -140, -143, -145, -148, -150, -153, -155, -157, -160, -162, -164, -167, -169, -171, -173, -176, -178, -180, -182, -184, -186, -188, -190, -192, -194, -196, -198, -200, -202, -204, -205, -207, -209, -210, -212, -214, -215, -217, -218, -220, -221, -223, -224, -225, -227, -228, -229, -230, -232, -233, -234, -235, -236, -237, -238, -239, -240, -241, -241, -242, -243, -244, -244, -245, -245, -246, -247, -247, -248, -248, -248, -249, -249, -249, -249, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -249, -249, -249, -249, -248, -248, -248, -247, -247, -246, -245, -245, -244, -244, -243, -242, -241, -241, -240, -239, -238, -237, -236, -235, -234, -233, -232, -230, -229, -228, -227, -225, -224, -223, -221, -220, -218, -217, -215, -214, -212, -210, -209, -207, -205, -204, -202, -200, -198, -196, -194, -192, -190, -188, -186, -184, -182, -180, -178, -176, -173, -171, -169, -167, -164, -162, -160, -157, -155, -153, -150, -148, -145, -143, -140, -138, -135, -132, -130, -127, -125, -122, -119, -116, -114, -111, -108, -105, -103, -100, -97, -94, -91, -89, -86, -83, -80, -77, -74, -71, -68, -65, -62, -59, -56, -53, -50, -47, -44, -41, -38, -35, -32, -29, -26, -23, -20, -17, -14, -11, -8, -5, -2, 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 91, 94, 97, 100, 103, 105, 108, 111, 114, 116, 119, 122, 125, 127, 130, 132, 135, 138, 140, 143, 145, 148, 150, 153, 155, 157, 160, 162, 164, 167, 169, 171, 173, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 205, 207, 209, 210, 212, 214, 215, 217, 218, 220, 221, 223, 224, 225, 227, 228, 229, 230, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 245, 246, 247, 247, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250 }; // Receiver antenna attenuation in dB for boresight angle = 0:5:180 [deg] const double ant_pat_db[37] = { 0.00, 0.00, 0.22, 0.44, 0.67, 1.11, 1.56, 2.00, 2.44, 2.89, 3.56, 4.22, 4.89, 5.56, 6.22, 6.89, 7.56, 8.22, 8.89, 9.78, 10.67, 11.56, 12.44, 13.33, 14.44, 15.56, 16.67, 17.78, 18.89, 20.00, 21.33, 22.67, 24.00, 25.56, 27.33, 29.33, 31.56 }; // Page number to SVID conversion for subframe 4&5 // See IS-GPS-200L, p.110, table 20-V const unsigned long sbf4_svId[25] = { 57UL, 0UL, 0UL, 0UL, 0UL, 57UL, 0UL, 0UL, 0UL, 0UL, 57UL, 62UL, 52UL, 53UL, 54UL, 57UL, 55UL, 56UL, 58UL, 59UL, 57UL, 60UL, 61UL, 62UL, 63UL }; const unsigned long sbf5_svId[25] = { 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 51UL }; static int allocatedSat[MAX_SAT]; /* Subtract two vectors of double * y Result of subtraction * x1 Minuend of subtracion * x2 Subtrahend of subtracion */ static void subVect(double *y, const double *x1, const double *x2) { y[0] = x1[0] - x2[0]; y[1] = x1[1] - x2[1]; y[2] = x1[2] - x2[2]; return; } /* Compute Norm of Vector * x Input vector * Length (Norm) of the input vector */ static double normVect(const double *x) { return (sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2])); } /* Compute dot-product of two vectors * x1 First multiplicand * x2 Second multiplicand * Dot-product of both multiplicands */ static double dotProd(const double *x1, const double *x2) { return (x1[0] * x2[0] + x1[1] * x2[1] + x1[2] * x2[2]); } /* Generate the C/A code sequence for a given Satellite Vehicle PRN * prn PRN nuber of the Satellite Vehicle * ca Caller-allocated integer array of 1023 bytes */ static void codegen(int *ca, int prn) { int delay[] = { 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 }; int g1[CA_SEQ_LEN], g2[CA_SEQ_LEN]; int r1[N_DWRD_SBF], r2[N_DWRD_SBF]; int c1, c2; int i, j; if (prn < 1 || prn > 32) return; for (i = 0; i < N_DWRD_SBF; i++) r1[i] = r2[i] = -1; for (i = 0; i < CA_SEQ_LEN; i++) { g1[i] = r1[9]; g2[i] = r2[9]; c1 = r1[2] * r1[9]; c2 = r2[1] * r2[2] * r2[5] * r2[7] * r2[8] * r2[9]; for (j = 9; j > 0; j--) { r1[j] = r1[j - 1]; r2[j] = r2[j - 1]; } r1[0] = c1; r2[0] = c2; } for (i = 0, j = CA_SEQ_LEN - delay[prn - 1]; i < CA_SEQ_LEN; i++, j++) ca[i] = (1 - g1[i] * g2[j % CA_SEQ_LEN]) / 2; return; } /* Convert a UTC date into a GPS date * t input date in UTC form * g output date in GPS form */ static void date2gps(const datetime_t *t, gpstime_t *g) { int doy[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; int ye; int de; int lpdays; ye = t->y - 1980; // Compute the number of leap days since Jan 5/Jan 6, 1980. lpdays = ye / 4 + 1; if ((ye % 4) == 0 && t->m <= 2) lpdays--; // Compute the number of days elapsed since Jan 5/Jan 6, 1980. de = ye * 365 + doy[t->m - 1] + t->d + lpdays - 6; // Convert time to GPS weeks and seconds. g->week = de / 7; g->sec = (double) (de % 7) * SECONDS_IN_DAY + t->hh * SECONDS_IN_HOUR + t->mm * SECONDS_IN_MINUTE + t->sec; return; } static void gps2date(const gpstime_t *g, datetime_t *t) { // Convert Julian day number to calendar date int c = (int) (7 * g->week + floor(g->sec / 86400.0) + 2444245.0) + 1537; int d = (int) ((c - 122.1) / 365.25); int e = 365 * d + d / 4; int f = (int) ((c - e) / 30.6001); t->d = c - e - (int) (30.6001 * f); t->m = f - 1 - 12 * (f / 14); t->y = d - 4715 - ((7 + t->m) / 10); t->hh = ((int) (g->sec / 3600.0)) % 24; t->mm = ((int) (g->sec / 60.0)) % 60; t->sec = g->sec - 60.0 * floor(g->sec / 60.0); return; } /* Convert Earth-centered Earth-fixed (ECEF) into Lat/Long/Height * xyz Input Array of X, Y and Z ECEF coordinates * llh Output Array of Latitude, Longitude and Height */ static void xyz2llh(const double *xyz, double *llh) { double a, eps, e, e2; double x, y, z; double rho2, dz, zdz, nh, slat, n, dz_new; a = WGS84_RADIUS; e = WGS84_ECCENTRICITY; eps = 1.0e-3; e2 = e*e; if (normVect(xyz) < eps) { // Invalid ECEF vector llh[0] = 0.0; llh[1] = 0.0; llh[2] = -a; return; } x = xyz[0]; y = xyz[1]; z = xyz[2]; rho2 = x * x + y*y; dz = e2*z; while (1) { zdz = z + dz; nh = sqrt(rho2 + zdz * zdz); slat = zdz / nh; n = a / sqrt(1.0 - e2 * slat * slat); dz_new = n * e2*slat; if (fabs(dz - dz_new) < eps) break; dz = dz_new; } llh[0] = atan2(zdz, sqrt(rho2)); llh[1] = atan2(y, x); llh[2] = nh - n; return; } /* Convert Lat/Long/Height into Earth-centered Earth-fixed (ECEF) * llh Input Array of Latitude, Longitude and Height * xyz Output Array of X, Y and Z ECEF coordinates */ static void llh2xyz(const double *llh, double *xyz) { double n; double a; double e; double e2; double clat; double slat; double clon; double slon; double d, nph; double tmp; a = WGS84_RADIUS; e = WGS84_ECCENTRICITY; e2 = e*e; clat = cos(llh[0]); slat = sin(llh[0]); clon = cos(llh[1]); slon = sin(llh[1]); d = e*slat; n = a / sqrt(1.0 - d * d); nph = n + llh[2]; tmp = nph*clat; xyz[0] = tmp*clon; xyz[1] = tmp*slon; xyz[2] = ((1.0 - e2) * n + llh[2]) * slat; return; } /* Compute the intermediate matrix for LLH to ECEF * llh Input position in Latitude-Longitude-Height format * t Three-by-Three output matrix */ static void ltcmat(const double *llh, double t[3][3]) { double slat, clat; double slon, clon; slat = sin(llh[0]); clat = cos(llh[0]); slon = sin(llh[1]); clon = cos(llh[1]); t[0][0] = -slat*clon; t[0][1] = -slat*slon; t[0][2] = clat; t[1][0] = -slon; t[1][1] = clon; t[1][2] = 0.0; t[2][0] = clat*clon; t[2][1] = clat*slon; t[2][2] = slat; return; } /* Convert Earth-centered Earth-Fixed to ? * xyz Input position as vector in ECEF format * t Intermediate matrix computed by \ref ltcmat * neu Output position as North-East-Up format */ static void ecef2neu(const double *xyz, double t[3][3], double *neu) { neu[0] = t[0][0] * xyz[0] + t[0][1] * xyz[1] + t[0][2] * xyz[2]; neu[1] = t[1][0] * xyz[0] + t[1][1] * xyz[1] + t[1][2] * xyz[2]; neu[2] = t[2][0] * xyz[0] + t[2][1] * xyz[1] + t[2][2] * xyz[2]; return; } /* Convert North-Eeast-Up to Azimuth + Elevation * neu Input position in North-East-Up format * azel Output array of azimuth + elevation as double */ static void neu2azel(double *azel, const double *neu) { double ne; azel[0] = atan2(neu[1], neu[0]); if (azel[0] < 0.0) azel[0] += (2.0 * PI); ne = sqrt(neu[0] * neu[0] + neu[1] * neu[1]); azel[1] = atan2(neu[2], ne); return; } /* Compute Satellite position, velocity and clock at given time * eph Ephemeris data of the satellite * g GPS time at which position is to be computed * pos Computed position (vector) * vel Computed velociy (vector) * clk Computed clock */ static void satpos(ephem_t eph, gpstime_t g, double *pos, double *vel, double *clk) { // Computing Satellite Velocity using the Broadcast Ephemeris // http://www.ngs.noaa.gov/gps-toolbox/bc_velo.htm double tk; double mk; double ek; double ekold; double ekdot; double cek, sek; double pk; double pkdot; double c2pk, s2pk; double uk; double ukdot; double cuk, suk; double ok; double sok, cok; double ik; double ikdot; double sik, cik; double rk; double rkdot; double xpk, ypk; double xpkdot, ypkdot; double relativistic, OneMinusecosE, tmp; tk = g.sec - eph.toe.sec; if (tk > SECONDS_IN_HALF_WEEK) tk -= SECONDS_IN_WEEK; else if (tk<-SECONDS_IN_HALF_WEEK) tk += SECONDS_IN_WEEK; mk = eph.m0 + eph.n*tk; ek = mk; ekold = ek + 1.0; OneMinusecosE = 0; // Suppress the uninitialized warning. while (fabs(ek - ekold) > 1.0E-14) { ekold = ek; OneMinusecosE = 1.0 - eph.ecc * cos(ekold); ek = ek + (mk - ekold + eph.ecc * sin(ekold)) / OneMinusecosE; } sek = sin(ek); cek = cos(ek); ekdot = eph.n / OneMinusecosE; relativistic = -4.442807633E-10 * eph.ecc * eph.sqrta*sek; pk = atan2(eph.sq1e2*sek, cek - eph.ecc) + eph.aop; pkdot = eph.sq1e2 * ekdot / OneMinusecosE; s2pk = sin(2.0 * pk); c2pk = cos(2.0 * pk); uk = pk + eph.cus * s2pk + eph.cuc*c2pk; suk = sin(uk); cuk = cos(uk); ukdot = pkdot * (1.0 + 2.0 * (eph.cus * c2pk - eph.cuc * s2pk)); rk = eph.A * OneMinusecosE + eph.crc * c2pk + eph.crs*s2pk; rkdot = eph.A * eph.ecc * sek * ekdot + 2.0 * pkdot * (eph.crs * c2pk - eph.crc * s2pk); ik = eph.inc0 + eph.idot * tk + eph.cic * c2pk + eph.cis*s2pk; sik = sin(ik); cik = cos(ik); ikdot = eph.idot + 2.0 * pkdot * (eph.cis * c2pk - eph.cic * s2pk); xpk = rk*cuk; ypk = rk*suk; xpkdot = rkdot * cuk - ypk*ukdot; ypkdot = rkdot * suk + xpk*ukdot; ok = eph.omg0 + tk * eph.omgkdot - OMEGA_EARTH * eph.toe.sec; sok = sin(ok); cok = cos(ok); pos[0] = xpk * cok - ypk * cik*sok; pos[1] = xpk * sok + ypk * cik*cok; pos[2] = ypk*sik; tmp = ypkdot * cik - ypk * sik*ikdot; vel[0] = -eph.omgkdot * pos[1] + xpkdot * cok - tmp*sok; vel[1] = eph.omgkdot * pos[0] + xpkdot * sok + tmp*cok; vel[2] = ypk * cik * ikdot + ypkdot*sik; // Satellite clock correction tk = g.sec - eph.toc.sec; if (tk > SECONDS_IN_HALF_WEEK) tk -= SECONDS_IN_WEEK; else if (tk<-SECONDS_IN_HALF_WEEK) tk += SECONDS_IN_WEEK; clk[0] = eph.af0 + tk * (eph.af1 + tk * eph.af2) + relativistic - eph.tgd; clk[1] = eph.af1 + 2.0 * tk * eph.af2; return; } /* Compute Subframe from Ephemeris * eph Ephemeris of given SV * sbf Array of five sub-frames, 10 long words each */ void eph2sbf(const ephem_t eph, const ionoutc_t ionoutc, const almanac_gps_t *alm, unsigned long sbf[N_SBF_PAGE][N_DWRD_SBF]) { unsigned long wn; unsigned long toe; unsigned long toc; unsigned long iode; unsigned long iodc; long deltan; long cuc; long cus; long cic; long cis; long crc; long crs; unsigned long ecc; unsigned long sqrta; long m0; long omega0; long inc0; long aop; long omegadot; long idot; long af0; long af1; long af2; long tgd; unsigned long ura = 0UL; unsigned long dataId = 1UL; unsigned long wna; unsigned long toa; signed long alpha0, alpha1, alpha2, alpha3; signed long beta0, beta1, beta2, beta3; signed long A0, A1; signed long dtls, dtlsf; unsigned long tot, wnt, wnlsf, dn; int sv, i; unsigned long svId; signed long delta_i; // Relative to i0 = 0.30 semicircles // FIXED: This has to be the "transmission" week number, not for the ephemeris reference time //wn = (unsigned long)(eph.toe.week%1024); wn = 0UL; toe = (unsigned long) (eph.toe.sec / 16.0); toc = (unsigned long) (eph.toc.sec / 16.0); iode = (unsigned long) (eph.iode); iodc = (unsigned long) (eph.iodc); deltan = (long) (eph.deltan / POW2_M43 / PI); cuc = (long) (eph.cuc / POW2_M29); cus = (long) (eph.cus / POW2_M29); cic = (long) (eph.cic / POW2_M29); cis = (long) (eph.cis / POW2_M29); crc = (long) (eph.crc / POW2_M5); crs = (long) (eph.crs / POW2_M5); ecc = (unsigned long) (eph.ecc / POW2_M33); sqrta = (unsigned long) (eph.sqrta / POW2_M19); m0 = (long) (eph.m0 / POW2_M31 / PI); omega0 = (long) (eph.omg0 / POW2_M31 / PI); inc0 = (long) (eph.inc0 / POW2_M31 / PI); aop = (long) (eph.aop / POW2_M31 / PI); omegadot = (long) (eph.omgdot / POW2_M43 / PI); idot = (long) (eph.idot / POW2_M43 / PI); af0 = (long) (eph.af0 / POW2_M31); af1 = (long) (eph.af1 / POW2_M43); af2 = (long) (eph.af2 / POW2_M55); tgd = (long) (eph.tgd / POW2_M31); alpha0 = (signed long) round(ionoutc.alpha0 / POW2_M30); alpha1 = (signed long) round(ionoutc.alpha1 / POW2_M27); alpha2 = (signed long) round(ionoutc.alpha2 / POW2_M24); alpha3 = (signed long) round(ionoutc.alpha3 / POW2_M24); beta0 = (signed long) round(ionoutc.beta0 / 2048.0); beta1 = (signed long) round(ionoutc.beta1 / 16384.0); beta2 = (signed long) round(ionoutc.beta2 / 65536.0); beta3 = (signed long) round(ionoutc.beta3 / 65536.0); A0 = (signed long) round(ionoutc.A0 / POW2_M30); A1 = (signed long) round(ionoutc.A1 / POW2_M50); dtls = (signed long) (ionoutc.dtls); tot = (unsigned long) (ionoutc.tot / 4096); wnt = (unsigned long) (ionoutc.wnt % 256); // TO DO: Specify scheduled leap seconds in command options // 2016/12/31 (Sat) -> WNlsf = 1929, DN = 7 (http://navigationservices.agi.com/GNSSWeb/) // Days are counted from 1 to 7 (Sunday is 1). wnlsf = 1929 % 256; dn = 7; dtlsf = 18; // Subframe 1 sbf[0][0] = 0x8B0000UL << 6; sbf[0][1] = 0x1UL << 8; sbf[0][2] = ((wn & 0x3FFUL) << 20) | (ura << 14) | (((iodc >> 8)&0x3UL) << 6); sbf[0][3] = 0UL; sbf[0][4] = 0UL; sbf[0][5] = 0UL; sbf[0][6] = (tgd & 0xFFUL) << 6; sbf[0][7] = ((iodc & 0xFFUL) << 22) | ((toc & 0xFFFFUL) << 6); sbf[0][8] = ((af2 & 0xFFUL) << 22) | ((af1 & 0xFFFFUL) << 6); sbf[0][9] = (af0 & 0x3FFFFFUL) << 8; // Subframe 2 sbf[1][0] = 0x8B0000UL << 6; sbf[1][1] = 0x2UL << 8; sbf[1][2] = ((iode & 0xFFUL) << 22) | ((crs & 0xFFFFUL) << 6); sbf[1][3] = ((deltan & 0xFFFFUL) << 14) | (((m0 >> 24)&0xFFUL) << 6); sbf[1][4] = (m0 & 0xFFFFFFUL) << 6; sbf[1][5] = ((cuc & 0xFFFFUL) << 14) | (((ecc >> 24)&0xFFUL) << 6); sbf[1][6] = (ecc & 0xFFFFFFUL) << 6; sbf[1][7] = ((cus & 0xFFFFUL) << 14) | (((sqrta >> 24)&0xFFUL) << 6); sbf[1][8] = (sqrta & 0xFFFFFFUL) << 6; sbf[1][9] = (toe & 0xFFFFUL) << 14; // Subframe 3 sbf[2][0] = 0x8B0000UL << 6; sbf[2][1] = 0x3UL << 8; sbf[2][2] = ((cic & 0xFFFFUL) << 14) | (((omega0 >> 24)&0xFFUL) << 6); sbf[2][3] = (omega0 & 0xFFFFFFUL) << 6; sbf[2][4] = ((cis & 0xFFFFUL) << 14) | (((inc0 >> 24)&0xFFUL) << 6); sbf[2][5] = (inc0 & 0xFFFFFFUL) << 6; sbf[2][6] = ((crc & 0xFFFFUL) << 14) | (((aop >> 24)&0xFFUL) << 6); sbf[2][7] = (aop & 0xFFFFFFUL) << 6; sbf[2][8] = (omegadot & 0xFFFFFFUL) << 6; sbf[2][9] = ((iode & 0xFFUL) << 22) | ((idot & 0x3FFFUL) << 8); // Empty all the pages of subframes 4 and 5 for (i = 0; i < 25; i++) { svId = 0UL; // Dummy SV //svId = sbf4_svId[i]; sbf[3 + i * 2][0] = 0x8B0000UL << 6; // Preamble for TLM sbf[3 + i * 2][1] = 0x4UL << 8; // Subframe ID for HOW sbf[3 + i * 2][2] = (dataId << 28) | (svId << 22) | ((EMPTY_WORD & 0xFFFFUL) << 6); sbf[3 + i * 2][3] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][4] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][5] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][6] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][7] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][8] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[3 + i * 2][9] = (EMPTY_WORD & 0x3FFFFFUL) << 8; //svId = sbf5_svId[i]; sbf[4 + i * 2][0] = 0x8B0000UL << 6; // Preamble for TLM sbf[4 + i * 2][1] = 0x5UL << 8; // Subframe ID for HOW sbf[4 + i * 2][2] = (dataId << 28) | (svId << 22) | ((EMPTY_WORD & 0xFFFFUL) << 6); sbf[4 + i * 2][3] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][4] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][5] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][6] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][7] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][8] = (EMPTY_WORD & 0xFFFFFFUL) << 6; sbf[4 + i * 2][9] = (EMPTY_WORD & 0x3FFFFFUL) << 8; } // Subframe 4, pages 2-5 and 7-10: almanac data for PRN 25 through 32 for (sv = 24; sv < MAX_SAT; sv++) { if (sv >= 24 && sv <= 27) // PRN 25-28 i = sv - 23; // Pages 2-5 (i = 1-4) else if (sv >= 28 && sv < MAX_SAT) // PRN 29-32 i = sv - 22; // Page 7-10 (i = 6-9) if (alm->sv[sv].valid != 0) { svId = (unsigned long) (sv + 1); ecc = (unsigned long) (alm->sv[sv].e / POW2_M21); toa = (unsigned long) (alm->sv[sv].toa.sec / POW2_12); delta_i = (signed long) (alm->sv[sv].delta_i / POW2_M19); omegadot = (signed long) (alm->sv[sv].omegadot / POW2_M38); sqrta = (unsigned long) (alm->sv[sv].sqrta / POW2_M11); omega0 = (signed long) (alm->sv[sv].omega0 / POW2_M23); aop = (signed long) (alm->sv[sv].aop / POW2_M23); m0 = (signed long) (alm->sv[sv].m0 / POW2_M23); af0 = (signed long) (alm->sv[sv].af0 / POW2_M20); af1 = (signed long) (alm->sv[sv].af1 / POW2_M38); sbf[3 + i * 2][0] = 0x8B0000UL << 6; // Preamble for TLM sbf[3 + i * 2][1] = 0x4UL << 8; // Subframe ID for HOW sbf[3 + i * 2][2] = (dataId << 28) | (svId << 22) | ((ecc & 0xFFFFUL) << 6); sbf[3 + i * 2][3] = ((toa & 0xFFUL) << 22) | ((delta_i & 0xFFFFUL) << 6); sbf[3 + i * 2][4] = ((omegadot & 0xFFFFUL) << 14); // SV HEALTH = 000 (ALL DATA OK) sbf[3 + i * 2][5] = ((sqrta & 0xFFFFFFUL) << 6); sbf[3 + i * 2][6] = ((omega0 & 0xFFFFFFUL) << 6); sbf[3 + i * 2][7] = ((aop & 0xFFFFFFUL) << 6); sbf[3 + i * 2][8] = ((m0 & 0xFFFFFFUL) << 6); sbf[3 + i * 2][9] = ((af0 & 0x7F8UL) << 19) | ((af1 & 0x7FFUL) << 11) | ((af0 & 0x7UL) << 8); } } // Subframe 4, page 18: ionospheric and UTC data if (ionoutc.vflg == true) { sbf[3 + 17 * 2][0] = 0x8B0000UL << 6; sbf[3 + 17 * 2][1] = 0x4UL << 8; sbf[3 + 17 * 2][2] = (dataId << 28) | (sbf4_svId[17] << 22) | ((alpha0 & 0xFFUL) << 14) | ((alpha1 & 0xFFUL) << 6); sbf[3 + 17 * 2][3] = ((alpha2 & 0xFFUL) << 22) | ((alpha3 & 0xFFUL) << 14) | ((beta0 & 0xFFUL) << 6); sbf[3 + 17 * 2][4] = ((beta1 & 0xFFUL) << 22) | ((beta2 & 0xFFUL) << 14) | ((beta3 & 0xFFUL) << 6); sbf[3 + 17 * 2][5] = (A1 & 0xFFFFFFUL) << 6; sbf[3 + 17 * 2][6] = ((A0 >> 8)&0xFFFFFFUL) << 6; sbf[3 + 17 * 2][7] = ((A0 & 0xFFUL) << 22) | ((tot & 0xFFUL) << 14) | ((wnt & 0xFFUL) << 6); sbf[3 + 17 * 2][8] = ((dtls & 0xFFUL) << 22) | ((wnlsf & 0xFFUL) << 14) | ((dn & 0xFFUL) << 6); sbf[3 + 17 * 2][9] = (dtlsf & 0xFFUL) << 22; } // Subframe 4, page 25: SV health data for PRN 25 through 32 sbf[3 + 24 * 2][0] = 0x8B0000UL << 6; sbf[3 + 24 * 2][1] = 0x4UL << 8; sbf[3 + 24 * 2][2] = (dataId << 28) | (sbf4_svId[24] << 22); sbf[3 + 24 * 2][3] = 0UL; sbf[3 + 24 * 2][4] = 0UL; sbf[3 + 24 * 2][5] = 0UL; sbf[3 + 24 * 2][6] = 0UL; sbf[3 + 24 * 2][7] = 0UL; sbf[3 + 24 * 2][8] = 0UL; sbf[3 + 24 * 2][9] = 0UL; // Subframe 5, page 1-24: almanac data for PRN 1 through 24 for (sv = 0; sv < 24; sv++) { i = sv; if (alm->sv[sv].svid != 0) { svId = (unsigned long) (sv + 1); ecc = (unsigned long) (alm->sv[sv].e / POW2_M21); toa = (unsigned long) (alm->sv[sv].toa.sec / 4096.0); delta_i = (signed long) (alm->sv[sv].delta_i / POW2_M19); omegadot = (signed long) (alm->sv[sv].omegadot / POW2_M38); sqrta = (unsigned long) (alm->sv[sv].sqrta / POW2_M11); omega0 = (signed long) (alm->sv[sv].omega0 / POW2_M23); aop = (signed long) (alm->sv[sv].aop / POW2_M23); m0 = (signed long) (alm->sv[sv].m0 / POW2_M23); af0 = (signed long) (alm->sv[sv].af0 / POW2_M20); af1 = (signed long) (alm->sv[sv].af1 / POW2_M38); sbf[4 + i * 2][0] = 0x8B0000UL << 6; // Preamble sbf[4 + i * 2][1] = 0x5UL << 8; // Subframe ID sbf[4 + i * 2][2] = (dataId << 28) | (svId << 22) | ((ecc & 0xFFFFUL) << 6); sbf[4 + i * 2][3] = ((toa & 0xFFUL) << 22) | ((delta_i & 0xFFFFUL) << 6); sbf[4 + i * 2][4] = ((omegadot & 0xFFFFUL) << 14); // SV HEALTH = 000 (ALL DATA OK) sbf[4 + i * 2][5] = ((sqrta & 0xFFFFFFUL) << 6); sbf[4 + i * 2][6] = ((omega0 & 0xFFFFFFUL) << 6); sbf[4 + i * 2][7] = ((aop & 0xFFFFFFUL) << 6); sbf[4 + i * 2][8] = ((m0 & 0xFFFFFFUL) << 6); sbf[4 + i * 2][9] = ((af0 & 0x7F8UL) << 19) | ((af1 & 0x7FFUL) << 11) | ((af0 & 0x7UL) << 8); } } // Subframe 5, page 25: SV health data for PRN 1 through 24 wna = (unsigned long) (eph.toe.week % 256); toa = (unsigned long) (eph.toe.sec / 4096.0); for (sv = 0; sv < MAX_SAT; sv++) { if (alm->sv[sv].svid != 0) // Valid almanac is availabe { wna = (unsigned long) (alm->sv[sv].toa.week % 256); toa = (unsigned long) (alm->sv[sv].toa.sec / 4096.0); break; } } sbf[4 + 24 * 2][0] = 0x8B0000UL << 6; sbf[4 + 24 * 2][1] = 0x5UL << 8; sbf[4 + 24 * 2][2] = (dataId << 28) | (sbf5_svId[24] << 22) | ((toa & 0xFFUL) << 14) | ((wna & 0xFFUL) << 6); sbf[4 + 24 * 2][3] = 0UL; sbf[4 + 24 * 2][4] = 0UL; sbf[4 + 24 * 2][5] = 0UL; sbf[4 + 24 * 2][6] = 0UL; sbf[4 + 24 * 2][7] = 0UL; sbf[4 + 24 * 2][8] = 0UL; sbf[4 + 24 * 2][9] = 0UL; } /* Count number of bits set to 1 * v long word in which bits are counted * Count of bits set to 1 */ static unsigned long countBits(unsigned long v) { unsigned long c; const int S[] = {1, 2, 4, 8, 16}; const unsigned long B[] = { 0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF }; c = v; c = ((c >> S[0]) & B[0]) + (c & B[0]); c = ((c >> S[1]) & B[1]) + (c & B[1]); c = ((c >> S[2]) & B[2]) + (c & B[2]); c = ((c >> S[3]) & B[3]) + (c & B[3]); c = ((c >> S[4]) & B[4]) + (c & B[4]); return (c); } static int decode_wordN(unsigned int word) { const unsigned int hamming[] = { 0xBB1F3480, 0x5D8F9A40, 0xAEC7CD00, 0x5763E680, 0x6BB1F340, 0x8B7A89C0 }; unsigned int parity = 0, w; int i; if (word & 0x40000000) word ^= 0x3FFFFFC0; for (i = 0; i < 6; i++) { parity <<= 1; for (w = (word & hamming[i]) >> 6; w; w >>= 1) parity ^= w & 1; } if (parity != (word & 0x3F)) return 0; //for (i=0;i<3;i++) data[i]=(unsigned char)(word>>(22-i*8)); return 1; } static bool validate_parityN(unsigned int W) { // Parity stuff static const unsigned int PARITY_25 = 0xBB1F3480; static const unsigned int PARITY_26 = 0x5D8F9A40; static const unsigned int PARITY_27 = 0xAEC7CD00; static const unsigned int PARITY_28 = 0x5763E680; static const unsigned int PARITY_29 = 0x6BB1F340; static const unsigned int PARITY_30 = 0x8B7A89C0; // Look-up table for parity of eight bit bytes // (parity=0 if the number of 0s and 1s is equal, else parity=1) static unsigned char byteParity[] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; // Local variables unsigned int t, w, p; // The sign of the data is determined by the D30* parity bit // of the previous data word. If D30* is set, invert the data // bits D01..D24 to obtain the d01..d24 (but leave all other // bits untouched). w = W; if (w & 0x40000000) w ^= 0x3FFFFFC0; // Compute the parity of the sign corrected data bits d01..d24 // as described in the ICD-GPS-200 t = w & PARITY_25; p = (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); t = w & PARITY_26; p = (p << 1) | (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); t = w & PARITY_27; p = (p << 1) | (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); t = w & PARITY_28; p = (p << 1) | (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); t = w & PARITY_29; p = (p << 1) | (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); t = w & PARITY_30; p = (p << 1) | (byteParity[t & 0xff] ^ byteParity[(t >> 8) & 0xff] ^ byteParity[(t >> 16) & 0xff] ^ byteParity[(t >> 24)]); if ((W & 0x3f) != p) { gui_status_wprintw(RED, "%d-%u ", (W & 0x3f), p); } if (!decode_wordN(W)) { gui_status_wprintw(RED, "%d-%u ", (W & 0x3f), p); } return ((W & 0x3f) == p); }; /* Compute the Checksum for one given word of a subframe * source The input data * nib Does this word contain non-information-bearing bits? * Computed Checksum */ static unsigned long computeChecksum(unsigned long source, int nib) { /* Bits 31 to 30 = 2 LSBs of the previous transmitted word, D29* and D30* Bits 29 to 6 = Source data bits, d1, d2, ..., d24 Bits 5 to 0 = Empty parity bits */ /* Bits 31 to 30 = 2 LSBs of the previous transmitted word, D29* and D30* Bits 29 to 6 = Data bits transmitted by the SV, D1, D2, ..., D24 Bits 5 to 0 = Computed parity bits, D25, D26, ..., D30 */ /* 1 2 3 bit 12 3456 7890 1234 5678 9012 3456 7890 --- ------------------------------------- D25 11 1011 0001 1111 0011 0100 1000 0000 D26 01 1101 1000 1111 1001 1010 0100 0000 D27 10 1110 1100 0111 1100 1101 0000 0000 D28 01 0111 0110 0011 1110 0110 1000 0000 D29 10 1011 1011 0001 1111 0011 0100 0000 D30 00 1011 0111 1010 1000 1001 1100 0000 */ unsigned long bmask[6] = { 0x3B1F3480UL, 0x1D8F9A40UL, 0x2EC7CD00UL, 0x1763E680UL, 0x2BB1F340UL, 0x0B7A89C0UL }; unsigned long D; unsigned long d = source & 0x3FFFFFC0UL; unsigned long D29 = (source >> 31)&0x1UL; unsigned long D30 = (source >> 30)&0x1UL; if (nib) // Non-information bearing bits for word 2 and 10 { /* Solve bits 23 and 24 to presearve parity check with zeros in bits 29 and 30. */ if ((D30 + countBits(bmask[4] & d)) % 2) d ^= (0x1UL << 6); if ((D29 + countBits(bmask[5] & d)) % 2) d ^= (0x1UL << 7); } D = d; if (D30) D ^= 0x3FFFFFC0UL; D |= ((D29 + countBits(bmask[0] & d)) % 2) << 5; D |= ((D30 + countBits(bmask[1] & d)) % 2) << 4; D |= ((D29 + countBits(bmask[2] & d)) % 2) << 3; D |= ((D30 + countBits(bmask[3] & d)) % 2) << 2; D |= ((D30 + countBits(bmask[4] & d)) % 2) << 1; D |= ((D29 + countBits(bmask[5] & d)) % 2); D &= 0x3FFFFFFFUL; D |= (source & 0xC0000000UL); // Add D29* and D30* from source data bits unsigned int W = D; validate_parityN(W); return (D); } /* Replace all 'E' exponential designators to 'D' * str String in which all occurrences of 'E' are replaced with * 'D' * len Length of input string in bytes * Number of characters replaced */ static int replaceExpDesignator(char *str, int len) { int i, n = 0; for (i = 0; i < len; i++) { if (str[i] == 0) { break; } if (str[i] == 'D' || str[i] == 'd') { n++; str[i] = 'E'; } } return (n); } static double subGpsTime(gpstime_t g1, gpstime_t g0) { double dt; dt = g1.sec - g0.sec; dt += (double) (g1.week - g0.week) * SECONDS_IN_WEEK; return (dt); } static gpstime_t incGpsTime(gpstime_t g0, double dt) { gpstime_t g1; g1.week = g0.week; g1.sec = g0.sec + dt; g1.sec = round(g1.sec * 1000.0) / 1000.0; // Avoid rounding error while (g1.sec >= SECONDS_IN_WEEK) { g1.sec -= SECONDS_IN_WEEK; g1.week++; } while (g1.sec < 0.0) { g1.sec += SECONDS_IN_WEEK; g1.week--; } return (g1); } /* Read Ephemeris data from the RINEX v2 Navigation file * eph Array of Output SV ephemeris data * fname File name of the RINEX file * Number of sets of ephemerides in the file */ static int readRinex2(ephem_t eph[][MAX_SAT], ionoutc_t *ionoutc, const char *fname) { struct gzFile_s *fp; int ieph; int sv; char str[MAX_CHAR]; char tmp[20]; double ver = 0.0; datetime_t t; gpstime_t g; gpstime_t g0; double dt; int flags = 0x0; if (NULL == (fp = gzopen(fname, "rt"))) return (-1); // Clear valid flag for (ieph = 0; ieph < EPHEM_ARRAY_SIZE; ieph++) for (sv = 0; sv < MAX_SAT; sv++) eph[ieph][sv].vflg = false; // Read header lines while (1) { if (NULL == gzgets(fp, str, MAX_CHAR)) break; if (strncmp(str + 60, "COMMENT", 7) == 0) { continue; } else if (strncmp(str + 60, "END OF HEADER", 13) == 0) { break; } else if (strncmp(str + 60, "RINEX VERSION / TYPE", 20) == 0) { strncpy(tmp, str, 9); tmp[9] = 0; replaceExpDesignator(tmp, 9); ver = atof(tmp); if (ver > 3.0) { gzclose(fp); return -2; } if (str[20] != 'N') { gzclose(fp); return -3; } } else if (strncmp(str + 60, "PGM / RUN BY / DATE", 19) == 0) { strncpy(rinex_date, str + 40, 20); rinex_date[20] = 0; } else if (strncmp(str + 60, "ION ALPHA", 9) == 0) { strncpy(tmp, str + 2, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha0 = atof(tmp); strncpy(tmp, str + 14, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha1 = atof(tmp); strncpy(tmp, str + 26, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha2 = atof(tmp); strncpy(tmp, str + 38, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha3 = atof(tmp); flags |= 0x1; } else if (strncmp(str + 60, "ION BETA", 8) == 0) { strncpy(tmp, str + 2, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta0 = atof(tmp); strncpy(tmp, str + 14, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta1 = atof(tmp); strncpy(tmp, str + 26, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta2 = atof(tmp); strncpy(tmp, str + 38, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta3 = atof(tmp); flags |= 0x1 << 1; } else if (strncmp(str + 60, "DELTA-UTC", 9) == 0) { strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); ionoutc->A0 = atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); ionoutc->A1 = atof(tmp); strncpy(tmp, str + 41, 9); tmp[9] = 0; ionoutc->tot = atoi(tmp); strncpy(tmp, str + 50, 9); tmp[9] = 0; ionoutc->wnt = atoi(tmp); if (ionoutc->tot % 4096 == 0) flags |= 0x1 << 2; } else if (strncmp(str + 60, "LEAP SECONDS", 12) == 0) { strncpy(tmp, str, 6); tmp[6] = 0; ionoutc->dtls = atoi(tmp); flags |= 0x1 << 3; } } ionoutc->vflg = false; if (flags == 0xF) // Read all Iono/UTC lines ionoutc->vflg = true; // Read ephemeris blocks g0.week = -1; ieph = 0; while (1) { if (NULL == gzgets(fp, str, MAX_CHAR)) break; // PRN strncpy(tmp, str, 2); tmp[2] = 0; sv = atoi(tmp) - 1; // EPOCH strncpy(tmp, str + 3, 2); tmp[2] = 0; t.y = atoi(tmp) + 2000; strncpy(tmp, str + 6, 2); tmp[2] = 0; t.m = atoi(tmp); strncpy(tmp, str + 9, 2); tmp[2] = 0; t.d = atoi(tmp); strncpy(tmp, str + 12, 2); tmp[2] = 0; t.hh = atoi(tmp); strncpy(tmp, str + 15, 2); tmp[2] = 0; t.mm = atoi(tmp); strncpy(tmp, str + 18, 4); tmp[2] = 0; t.sec = atof(tmp); date2gps(&t, &g); if (g0.week == -1) g0 = g; // Check current time of clock dt = subGpsTime(g, g0); if (dt > SECONDS_IN_HOUR) { g0 = g; ieph++; // a new set of ephemerides if (ieph >= EPHEM_ARRAY_SIZE) break; } // Date and time eph[ieph][sv].t = t; // SV CLK eph[ieph][sv].toc = g; strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); // tmp[15]='E'; eph[ieph][sv].af0 = atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].af1 = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].af2 = atof(tmp); // BROADCAST ORBIT - 1 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].iode = (int) atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].crs = atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].deltan = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].m0 = atof(tmp); // BROADCAST ORBIT - 2 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cuc = atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].ecc = atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cus = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].sqrta = atof(tmp); // BROADCAST ORBIT - 3 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].toe.sec = atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cic = atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].omg0 = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cis = atof(tmp); // BROADCAST ORBIT - 4 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].inc0 = atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].crc = atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].aop = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].omgdot = atof(tmp); // BROADCAST ORBIT - 5 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].idot = atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].code = (int) atof(tmp); strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].toe.week = (int) atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].flag = (int) atof(tmp); // BROADCAST ORBIT - 6 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 3, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].sva = (int) atof(tmp); strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].svh = (int) atof(tmp); if ((eph[ieph][sv].svh > 0) && (eph[ieph][sv].svh < 32)) eph[ieph][sv].svh += 32; // Set MSB to 1 strncpy(tmp, str + 41, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].tgd = atof(tmp); strncpy(tmp, str + 60, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].iodc = (int) atof(tmp); // BROADCAST ORBIT - 7 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 22, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].fit = atof(tmp); // Set valid flag eph[ieph][sv].vflg = true; // Update the working variables eph[ieph][sv].A = eph[ieph][sv].sqrta * eph[ieph][sv].sqrta; eph[ieph][sv].n = sqrt(GM_EARTH / (eph[ieph][sv].A * eph[ieph][sv].A * eph[ieph][sv].A)) + eph[ieph][sv].deltan; eph[ieph][sv].sq1e2 = sqrt(1.0 - eph[ieph][sv].ecc * eph[ieph][sv].ecc); eph[ieph][sv].omgkdot = eph[ieph][sv].omgdot - OMEGA_EARTH; } gzclose(fp); if (g0.week >= 0) ieph += 1; // Number of sets of ephemerides return (ieph); } /* Read Ephemeris data from the RINEX v3 Navigation file * eph Array of Output SV ephemeris data * fname File name of the RINEX file * Number of sets of ephemerides in the file */ static int readRinex3(ephem_t eph[][MAX_SAT], ionoutc_t *ionoutc, const char *fname) { struct gzFile_s *fp; int ieph; int sv; char str[MAX_CHAR]; char tmp[20]; double ver = 0.0; datetime_t t; gpstime_t g; gpstime_t g0; double dt; int flags = 0x0; if (NULL == (fp = gzopen(fname, "rt"))) return (-1); // Clear valid flag for (ieph = 0; ieph < EPHEM_ARRAY_SIZE; ieph++) for (sv = 0; sv < MAX_SAT; sv++) eph[ieph][sv].vflg = false; // Read header lines while (1) { if (NULL == gzgets(fp, str, MAX_CHAR)) break; if (strncmp(str + 60, "COMMENT", 7) == 0) { continue; } else if (strncmp(str + 60, "END OF HEADER", 13) == 0) { break; } else if (strncmp(str + 60, "RINEX VERSION / TYPE", 20) == 0) { strncpy(tmp, str, 9); tmp[9] = 0; replaceExpDesignator(tmp, 9); ver = atof(tmp); if (ver < 3.0) { gzclose(fp); return -2; } if (str[20] != 'N' && str[40] != 'G') { gzclose(fp); return -3; } } else if (strncmp(str + 60, "PGM / RUN BY / DATE", 19) == 0) { strncpy(rinex_date, str + 40, 20); rinex_date[20] = 0; } else if (strncmp(str + 60, "IONOSPHERIC CORR", 16) == 0) { if (strncmp(str, "GPSA", 4) == 0) { strncpy(tmp, str + 5, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha0 = atof(tmp); strncpy(tmp, str + 17, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha1 = atof(tmp); strncpy(tmp, str + 29, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha2 = atof(tmp); strncpy(tmp, str + 41, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->alpha3 = atof(tmp); flags |= 0x1; } else if (strncmp(str, "GPSB", 4) == 0) { strncpy(tmp, str + 5, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta0 = atof(tmp); strncpy(tmp, str + 17, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta1 = atof(tmp); strncpy(tmp, str + 29, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta2 = atof(tmp); strncpy(tmp, str + 41, 12); tmp[12] = 0; replaceExpDesignator(tmp, 12); ionoutc->beta3 = atof(tmp); flags |= 0x1 << 1; } } else if (strncmp(str + 60, "TIME SYSTEM CORR", 16) == 0 && strncmp(str, "GPUT", 4) == 0) { strncpy(tmp, str + 5, 17); tmp[17] = 0; replaceExpDesignator(tmp, 17); ionoutc->A0 = atof(tmp); strncpy(tmp, str + 22, 16); tmp[16] = 0; replaceExpDesignator(tmp, 16); ionoutc->A1 = atof(tmp); strncpy(tmp, str + 38, 7); tmp[7] = 0; replaceExpDesignator(tmp, 7); ionoutc->tot = atoi(tmp); strncpy(tmp, str + 45, 6); tmp[6] = 0; ionoutc->wnt = atoi(tmp); if (ionoutc->tot % 4096 == 0) flags |= 0x1 << 2; } else if (strncmp(str + 60, "LEAP SECONDS", 12) == 0) { strncpy(tmp, str, 6); tmp[6] = 0; ionoutc->dtls = atoi(tmp); flags |= 0x1 << 3; } } ionoutc->vflg = false; if (flags == 0xF) // Read all Iono/UTC lines ionoutc->vflg = true; // Read ephemeris blocks g0.week = -1; ieph = 0; while (1) { if (NULL == gzgets(fp, str, MAX_CHAR)) break; // Check for GPS data record if (str[0] != 'G') { continue; } // PRN strncpy(tmp, str + 1, 2); tmp[2] = 0; sv = atoi(tmp) - 1; // EPOCH strncpy(tmp, str + 4, 4); tmp[4] = 0; t.y = atoi(tmp); strncpy(tmp, str + 9, 2); tmp[2] = 0; t.m = atoi(tmp); strncpy(tmp, str + 12, 2); tmp[2] = 0; t.d = atoi(tmp); strncpy(tmp, str + 15, 2); tmp[2] = 0; t.hh = atoi(tmp); strncpy(tmp, str + 18, 2); tmp[2] = 0; t.mm = atoi(tmp); strncpy(tmp, str + 21, 2); tmp[2] = 0; t.sec = (double) atoi(tmp); date2gps(&t, &g); if (g0.week == -1) g0 = g; // Check current time of clock dt = subGpsTime(g, g0); if (dt > SECONDS_IN_HOUR) { g0 = g; ieph++; // a new set of ephemerides if (ieph >= EPHEM_ARRAY_SIZE) break; } // Date and time eph[ieph][sv].t = t; // SV CLK eph[ieph][sv].toc = g; strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].af0 = atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].af1 = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].af2 = atof(tmp); // BROADCAST ORBIT - 1 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 4, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].iode = (int) atof(tmp); strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].crs = atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].deltan = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].m0 = atof(tmp); // BROADCAST ORBIT - 2 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 4, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cuc = atof(tmp); strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].ecc = atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cus = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].sqrta = atof(tmp); // BROADCAST ORBIT - 3 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 4, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].toe.sec = atof(tmp); strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cic = atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].omg0 = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].cis = atof(tmp); // BROADCAST ORBIT - 4 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 4, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].inc0 = atof(tmp); strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].crc = atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].aop = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].omgdot = atof(tmp); // BROADCAST ORBIT - 5 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 4, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].idot = atof(tmp); strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].code = (int) atof(tmp); strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].toe.week = (int) atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].flag = (int) atof(tmp); // BROADCAST ORBIT - 6 if (NULL == gzgets(fp, str, MAX_CHAR)) break; // SV accuracy not read strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].svh = (int) atof(tmp); if ((eph[ieph][sv].svh > 0) && (eph[ieph][sv].svh < 32)) eph[ieph][sv].svh += 32; // Set MSB to 1 strncpy(tmp, str + 42, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].tgd = atof(tmp); strncpy(tmp, str + 61, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].iodc = (int) atof(tmp); // BROADCAST ORBIT - 7 if (NULL == gzgets(fp, str, MAX_CHAR)) break; strncpy(tmp, str + 23, 19); tmp[19] = 0; replaceExpDesignator(tmp, 19); eph[ieph][sv].fit = atof(tmp); // Set valid flag eph[ieph][sv].vflg = true; // Update the working variables eph[ieph][sv].A = eph[ieph][sv].sqrta * eph[ieph][sv].sqrta; eph[ieph][sv].n = sqrt(GM_EARTH / (eph[ieph][sv].A * eph[ieph][sv].A * eph[ieph][sv].A)) + eph[ieph][sv].deltan; eph[ieph][sv].sq1e2 = sqrt(1.0 - eph[ieph][sv].ecc * eph[ieph][sv].ecc); eph[ieph][sv].omgkdot = eph[ieph][sv].omgdot - OMEGA_EARTH; } gzclose(fp); if (g0.week >= 0) ieph += 1; // Number of sets of ephemerides return (ieph); } static double ionosphericDelay(const ionoutc_t *ionoutc, gpstime_t g, double *llh, double *azel) { double iono_delay = 0.0; double E, phi_u, lam_u, F; if (ionoutc->enable == false) return (0.0); // No ionospheric delay E = azel[1] / PI; phi_u = llh[0] / PI; lam_u = llh[1] / PI; // Obliquity factor F = 1.0 + 16.0 * pow((0.53 - E), 3.0); if (ionoutc->vflg == false) iono_delay = F * 5.0e-9 * SPEED_OF_LIGHT; else { double t, psi, phi_i, lam_i, phi_m, phi_m2, phi_m3; double AMP, PER, X, X2, X4; // Earth's central angle between the user position and the earth projection of // ionospheric intersection point (semi-circles) psi = 0.0137 / (E + 0.11) - 0.022; // Geodetic latitude of the earth projection of the ionospheric intersection point // (semi-circles) phi_i = phi_u + psi * cos(azel[0]); if (phi_i > 0.416) phi_i = 0.416; else if (phi_i<-0.416) phi_i = -0.416; // Geodetic longitude of the earth projection of the ionospheric intersection point // (semi-circles) lam_i = lam_u + psi * sin(azel[0]) / cos(phi_i * PI); // Geomagnetic latitude of the earth projection of the ionospheric intersection // point (mean ionospheric height assumed 350 km) (semi-circles) phi_m = phi_i + 0.064 * cos((lam_i - 1.617) * PI); phi_m2 = phi_m*phi_m; phi_m3 = phi_m2*phi_m; AMP = ionoutc->alpha0 + ionoutc->alpha1 * phi_m + ionoutc->alpha2 * phi_m2 + ionoutc->alpha3*phi_m3; if (AMP < 0.0) AMP = 0.0; PER = ionoutc->beta0 + ionoutc->beta1 * phi_m + ionoutc->beta2 * phi_m2 + ionoutc->beta3*phi_m3; if (PER < 72000.0) PER = 72000.0; // Local time (sec) t = SECONDS_IN_DAY / 2.0 * lam_i + g.sec; while (t >= SECONDS_IN_DAY) t -= SECONDS_IN_DAY; while (t < 0) t += SECONDS_IN_DAY; // Phase (radians) X = 2.0 * PI * (t - 50400.0) / PER; if (fabs(X) < 1.57) { X2 = X*X; X4 = X2*X2; iono_delay = F * (5.0e-9 + AMP * (1.0 - X2 / 2.0 + X4 / 24.0)) * SPEED_OF_LIGHT; } else iono_delay = F * 5.0e-9 * SPEED_OF_LIGHT; } return (iono_delay); } /* Compute range between a satellite and the receiver * rho The computed range * eph Ephemeris data of the satellite * g GPS time at time of receiving the signal * xyz position of the receiver */ static void computeRange(range_t *rho, ephem_t eph, ionoutc_t *ionoutc, gpstime_t g, double xyz[]) { double pos[3], vel[3], clk[2]; double los[3]; double tau; double range, rate; double xrot, yrot; double llh[3], neu[3]; double tmat[3][3]; // SV position at time of the pseudorange observation. satpos(eph, g, pos, vel, clk); // Receiver to satellite vector and light-time. subVect(los, pos, xyz); tau = normVect(los) / SPEED_OF_LIGHT; // Extrapolate the satellite position backwards to the transmission time. pos[0] -= vel[0] * tau; pos[1] -= vel[1] * tau; pos[2] -= vel[2] * tau; // Earth rotation correction. The change in velocity can be neglected. xrot = pos[0] + pos[1] * OMEGA_EARTH*tau; yrot = pos[1] - pos[0] * OMEGA_EARTH*tau; pos[0] = xrot; pos[1] = yrot; // New observer to satellite vector and satellite range. subVect(los, pos, xyz); range = normVect(los); rho->d = range; // Pseudorange. rho->range = range - SPEED_OF_LIGHT * clk[0]; // Relative velocity of SV and receiver. rate = dotProd(vel, los) / range; // Pseudorange rate. rho->rate = rate; // - SPEED_OF_LIGHT*clk[1]; // Time of application. rho->g = g; // Azimuth and elevation angles. xyz2llh(xyz, llh); ltcmat(llh, tmat); ecef2neu(los, tmat, neu); neu2azel(rho->azel, neu); // Add ionospheric delay rho->iono_delay = ionosphericDelay(ionoutc, g, llh, rho->azel); rho->range += rho->iono_delay; } /* Compute the code phase for a given channel (satellite) * chan Channel on which we operate (is updated) * rho1 Current range, after \a dt has expired * dt delta-t (time difference) in seconds */ static void computeCodePhase(channel_t *chan, range_t rho1, double dt) { double ms; int ims; double rhorate; // Pseudorange rate. rhorate = (rho1.range - chan->rho0.range) / dt; // Carrier and code frequency. chan->f_carr = -rhorate / LAMBDA_L1; chan->f_code = CODE_FREQ + chan->f_carr*CARR_TO_CODE; // Initial code phase and data bit counters. ms = ((subGpsTime(chan->rho0.g, chan->g0) + 6.0) - chan->rho0.range / SPEED_OF_LIGHT)*1000.0; ims = (int) ms; chan->code_phase = (ms - (double) ims) * CA_SEQ_LEN; // in chip chan->iword = ims / 600; // 1 word = 30 bits = 600 ms ims -= chan->iword * 600; chan->ibit = ims / 20; // 1 bit = 20 code = 20 ms ims -= chan->ibit * 20; chan->icode = ims; // 1 code = 1 ms chan->codeCA = chan->ca[(int) chan->code_phase]*2 - 1; chan->dataBit = (int) ((chan->dwrd[chan->iword]>>(29 - chan->ibit)) & 0x1UL)*2 - 1; // Save current pseudorange chan->rho0 = rho1; } void generateNavMsg(gpstime_t g, channel_t *chan, int init) { int iwrd, isbf; gpstime_t g0; unsigned long wn, tow; unsigned sbfwrd; unsigned long prevwrd; int nib; g0.week = g.week; g0.sec = (double) (((unsigned long) (g.sec + 0.5)) / 30UL) * 30.0; // Align with the full frame length = 30 sec chan->g0 = g0; // Data bit reference time wn = (unsigned long) (g0.week % 1024); tow = ((unsigned long) g0.sec) / 6UL; // Initialize the subframe 5 if (init == 1) { prevwrd = 0UL; for (iwrd = 0; iwrd < N_DWRD_SBF; iwrd++) { sbfwrd = chan->sbf[4 + chan->ipage * 2][iwrd]; // Add TOW-count message into HOW if (iwrd == 1) sbfwrd |= ((tow & 0x1FFFFUL) << 13); // Compute checksum sbfwrd |= (prevwrd << 30) & 0xC0000000UL; // 2 LSBs of the previous transmitted word nib = ((iwrd == 1) || (iwrd == 9)) ? 1 : 0; // Non-information bearing bits for word 2 and 10 chan->dwrd[iwrd] = computeChecksum(sbfwrd, nib); prevwrd = chan->dwrd[iwrd]; } } else { for (iwrd = 0; iwrd < N_DWRD_SBF; iwrd++) { chan->dwrd[iwrd] = chan->dwrd[N_DWRD_SBF * N_SBF + iwrd]; prevwrd = chan->dwrd[iwrd]; } } // Generate subframe words for (isbf = 0; isbf < N_SBF; isbf++) { tow++; for (iwrd = 0; iwrd < N_DWRD_SBF; iwrd++) { if (isbf < 3) // Subframes 1-3 sbfwrd = chan->sbf[isbf][iwrd]; else if (isbf == 3) // Subframe 4 sbfwrd = chan->sbf[3 + chan->ipage * 2][iwrd]; else // Subframe 5 sbfwrd = chan->sbf[4 + chan->ipage * 2][iwrd]; // Add transmission week number to Subframe 1 if ((isbf == 0)&&(iwrd == 2)) sbfwrd |= (wn & 0x3FFUL) << 20; // Add TOW-count message into HOW if (iwrd == 1) sbfwrd |= ((tow & 0x1FFFFUL) << 13); // Compute checksum sbfwrd |= (prevwrd << 30) & 0xC0000000UL; // 2 LSBs of the previous transmitted word nib = ((iwrd == 1) || (iwrd == 9)) ? 1 : 0; // Non-information bearing bits for word 2 and 10 chan->dwrd[(isbf + 1) * N_DWRD_SBF + iwrd] = computeChecksum(sbfwrd, nib); prevwrd = chan->dwrd[(isbf + 1) * N_DWRD_SBF + iwrd]; } } // Move to the next pages chan->ipage++; if (chan->ipage >= 25) chan->ipage = 0; } static int checkSatVisibility(ephem_t eph, gpstime_t g, double *xyz, double elvMask, double *azel) { double llh[3], neu[3]; double pos[3], vel[3], clk[3], los[3]; double tmat[3][3]; if (eph.vflg == false) return (-1); // Invalid xyz2llh(xyz, llh); ltcmat(llh, tmat); satpos(eph, g, pos, vel, clk); subVect(los, pos, xyz); ecef2neu(los, tmat, neu); neu2azel(azel, neu); if (azel[1] * R2D > elvMask) return (1); // Visible // else return (0); // Invisible } static int allocateChannel(channel_t *chan, almanac_gps_t *alm, ephem_t *eph, ionoutc_t ionoutc, gpstime_t grx, double *xyz, double elvMask) { NOTUSED(elvMask); int nsat = 0; int i, sv; double azel[2]; range_t rho; double ref[3] = {0.0}; double r_ref, r_xyz; double phase_ini; for (sv = 0; sv < MAX_SAT; sv++) { if (checkSatVisibility(eph[sv], grx, xyz, 0.0, azel) == 1) { nsat++; // Number of visible satellites if (allocatedSat[sv] == -1) // Visible but not allocated { // Allocated new satellite for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn == 0) { // Initialize channel chan[i].prn = sv + 1; chan[i].azel[0] = azel[0]; chan[i].azel[1] = azel[1]; // C/A code generation codegen(chan[i].ca, chan[i].prn); // Generate subframe eph2sbf(eph[sv], ionoutc, alm, chan[i].sbf); // Generate navigation message generateNavMsg(grx, &chan[i], 1); // Initialize pseudorange computeRange(&rho, eph[sv], &ionoutc, grx, xyz); chan[i].rho0 = rho; // Initialize carrier phase r_xyz = rho.range; computeRange(&rho, eph[sv], &ionoutc, grx, ref); r_ref = rho.range; phase_ini = (2.0 * r_ref - r_xyz) / LAMBDA_L1; #ifdef FLOAT_CARR_PHASE chan[i].carr_phase = phase_ini - floor(phase_ini); #else phase_ini -= floor(phase_ini); chan[i].carr_phase = (unsigned int) (512.0 * 65536.0 * phase_ini); #endif // Done. break; } } // Set satellite allocation channel if (i < MAX_CHAN) allocatedSat[sv] = i; } } else if (allocatedSat[sv] >= 0) // Not visible but allocated { // Clear channel chan[allocatedSat[sv]].prn = 0; // Clear satellite allocation flag allocatedSat[sv] = -1; } } return (nsat); } static size_t fwrite_rinex(void *buffer, size_t size, size_t nmemb, void *stream) { struct ftp_file *out = (struct ftp_file *) stream; if (out && !out->stream) { /* open file for writing */ out->stream = fopen(out->filename, "wb"); if (!out->stream) return -1; /* failure, can't open file to write */ } return fwrite(buffer, size, nmemb, out->stream); } /* Read the list of user motions from the input file * xyz Output array of ECEF vectors for user motion * filename File name of the text input file * Returns number of user data motion records read, -1 on error */ static int readUserMotion(double xyz[USER_MOTION_SIZE][3], const char *filename) { FILE *fp; int numd; char str[MAX_CHAR]; double t, x, y, z; if (NULL == (fp = fopen(filename, "rt"))) return (-1); for (numd = 0; numd < USER_MOTION_SIZE; numd++) { if (fgets(str, MAX_CHAR, fp) == NULL) break; if (EOF == sscanf(str, "%lf,%lf,%lf,%lf", &t, &x, &y, &z)) // Read CSV line break; xyz[numd][0] = x; xyz[numd][1] = y; xyz[numd][2] = z; } fclose(fp); return (numd); } /* * */ void *gps_thread_ep(void *arg) { simulator_t *simulator = (simulator_t *) (arg); ephem_t eph[EPHEM_ARRAY_SIZE][MAX_SAT]; channel_t chan[MAX_CHAN]; datetime_t ttmp; datetime_t tmin, tmax; gpstime_t gmin, gmax; gpstime_t grx; gpstime_t g0; gpstime_t gtmp; g0.week = -1; // Invalid start time date2gps(&simulator->start, &g0); double elvmask = 0.0; // in degree const double delt = 1.0 / (double) TX_SAMPLERATE; double llh[3]; double gain[MAX_CHAN]; double path_loss; double ant_gain; double ant_pat[37]; double dt; ionoutc_t ionoutc; ionoutc.enable = simulator->ionosphere_enable; bool sat_simulated[33] = {false}; int start_y = 4; // Row to start output in LS_FIX window/panel int ibs; // boresight angle index int igrx; int sv; int neph, ieph; int i; int ip, qp; int iTable; int isamp; short *iq_buff = NULL; // Allocate user motion array double (*xyz)[3] = malloc(sizeof (double[USER_MOTION_SIZE][3])); if (xyz == NULL) { gui_status_wprintw(RED, "Failed to allocate user motion memory.\n"); goto end_gps_thread; } // Initialize user motion array // with current location/position int iumd = 0; int numd = simulator->duration; double tmat[3][3]; double neu[3]; // Set user location llh[0] = simulator->location.lat / R2D; llh[1] = simulator->location.lon / R2D; llh[2] = simulator->location.height; llh2xyz(llh, xyz[0]); ltcmat(llh, tmat); if (!simulator->target.valid) { // No target position given, use location simulator->target.lat = simulator->location.lat; simulator->target.lon = simulator->location.lon; simulator->target.height = simulator->location.height; } else { // Set target position as simulation start // Given as distance and bearing from user location in -t option neu[0] = simulator->target.distance * cos((simulator->target.bearing / 1000) / R2D); neu[1] = simulator->target.distance * sin((simulator->target.bearing / 1000) / R2D); neu[2] = simulator->target.height; xyz[0][0] += tmat[0][0] * neu[0] + tmat[1][0] * neu[1] + tmat[2][0] * neu[2]; xyz[0][1] += tmat[0][1] * neu[0] + tmat[1][1] * neu[1] + tmat[2][1] * neu[2]; xyz[0][2] += tmat[0][2] * neu[0] + tmat[1][2] * neu[1] + tmat[2][2] * neu[2]; } for (iumd = 1; iumd < numd; iumd++) { xyz[iumd][0] = xyz[0][0]; xyz[iumd][1] = xyz[0][1]; xyz[iumd][2] = xyz[0][2]; } gui_show_location(&simulator->location); CURL *curl; CURLcode curl_code = CURLE_GOT_NOTHING; struct ftp_file ftp = { RINEX2_FILE_NAME, NULL }; /* On a multi-core CPU we run the main thread and reader thread on different cores. * Try sticking the main thread to core 2 */ thread_to_core(2); set_thread_name("gps-thread"); if ((simulator->nav_file_name == NULL) && (simulator->use_ftp == false)) { gui_status_wprintw(RED, "GPS ephemeris file is not specified.\n"); goto end_gps_thread; } //////////////////////////////////////////////////////////// // Read ephemeris //////////////////////////////////////////////////////////// if (simulator->use_ftp) { time_t t = time(NULL); struct tm *tm = gmtime(&t); char* url = malloc(NAME_MAX); int station_index = 0; // Use RINEX v2 by default or v3 on request const stations_t *pstation = stations_v2; if (simulator->use_rinex3) { pstation = stations_v3; } // Get number of stations available, find index for given one for (int s = 0; pstation[s].id_v2 != NULL; s++) { // Station id given, get index if (simulator->station_id != NULL) { if (strncmp(pstation[s].id_v2, simulator->station_id, 4) == 0 || strncmp(pstation[s].id_v3, simulator->station_id, 9) == 0) { break; } } station_index += 1; } // Pick a random station if none given if (simulator->station_id == NULL) { srand((unsigned int) g0.sec); station_index = rand() % station_index; } // Check that we have a picked a valid station // Take the first one when invalid if (pstation[station_index].id_v2 == NULL) { station_index = 0; } gui_status_wprintw(GREEN, "Pulling RINEX v%u from station: %s\n", (simulator->use_rinex3) ? 3 : 2, pstation[station_index].name); // We fetch data from previous hour because the actual hour is still in progress tm->tm_hour -= 1; if (tm->tm_hour < 0) { tm->tm_hour = 23; } // Compose FTP URL snprintf(url, NAME_MAX, RINEX_FTP_URL RINEX_FTP_FILE, (simulator->use_rinex3) ? RINEX3_SUBFOLDER : RINEX2_SUBFOLDER, tm->tm_yday + 1, tm->tm_hour, pstation[station_index].id_v2, tm->tm_yday + 1, 'a' + tm->tm_hour, tm->tm_year - 100); curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_rinex); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftp); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); if (0 /*simulator->show_verbose*/) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); } else { curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); } curl_easy_setopt(curl, CURLOPT_USERPWD, "anonymous:anonymous"); curl_code = curl_easy_perform(curl); curl_easy_cleanup(curl); } if (ftp.stream) fclose(ftp.stream); free(url); curl_global_cleanup(); if (curl_code != CURLE_OK) { switch (curl_code) { case CURLE_REMOTE_FILE_NOT_FOUND: gui_status_wprintw(RED, "Curl error: Ephemeris file not found!\n"); break; default: gui_status_wprintw(RED, "Curl error: %d\n", curl_code); break; } goto end_gps_thread; } } if (simulator->use_rinex3) { neph = readRinex3(eph, &ionoutc, simulator->nav_file_name); } else { neph = readRinex2(eph, &ionoutc, simulator->nav_file_name); } if (neph == 0) { gui_status_wprintw(RED, "No ephemeris available.\n"); goto end_gps_thread; } if (simulator->show_verbose) { if (ionoutc.vflg && ionoutc.enable) { gui_mvwprintw(LS_FIX, 14, 40, "ION ALPHA %12.3e %12.3e %12.3e %12.3e", ionoutc.alpha0, ionoutc.alpha1, ionoutc.alpha2, ionoutc.alpha3); gui_mvwprintw(LS_FIX, 15, 40, "ION BETA %12.3e %12.3e %12.3e %12.3e", ionoutc.beta0, ionoutc.beta1, ionoutc.beta2, ionoutc.beta3); gui_mvwprintw(LS_FIX, 16, 40, "DELTA UTC %12.3e %12.3e %9d %9d", ionoutc.A0, ionoutc.A1, ionoutc.tot, ionoutc.wnt); gui_mvwprintw(LS_FIX, 17, 40, "LEAP SECONDS %d", ionoutc.dtls); } else { gui_mvwprintw(LS_FIX, 14, 40, "Ionospheric data invalid or disabled!"); } } // Read user motion file if any if (simulator->motion_file_name != NULL) { numd = readUserMotion(xyz, simulator->motion_file_name); if (numd <= 0) { gui_status_wprintw(RED, "Failed to read user motion file.\n"); goto end_gps_thread; } gui_status_wprintw(GREEN, "%u user motion points applied.\n", numd); if (numd > simulator->duration) { numd = simulator->duration; } } for (sv = 0; sv < MAX_SAT; sv++) { if (eph[0][sv].vflg == true) { gmin = eph[0][sv].toc; tmin = eph[0][sv].t; break; } } gmax.sec = 0; gmax.week = 0; tmax.sec = 0; tmax.mm = 0; tmax.hh = 0; tmax.d = 0; tmax.m = 0; tmax.y = 0; for (sv = 0; sv < MAX_SAT; sv++) { if (eph[neph - 1][sv].vflg == true) { gmax = eph[neph - 1][sv].toc; tmax = eph[neph - 1][sv].t; break; } } if (g0.week >= 0) // Scenario start time has been set. { if (simulator->time_overwrite == true) { double dsec; gtmp.week = g0.week; gtmp.sec = (double) (((int) (g0.sec)) / 7200)*7200.0; dsec = subGpsTime(gtmp, gmin); // Overwrite the UTC reference week number ionoutc.wnt = gtmp.week; ionoutc.tot = (int) gtmp.sec; // Iono/UTC parameters may no longer valid //ionoutc.vflg = FALSE; // Overwrite the TOC and TOE to the scenario start time for (sv = 0; sv < MAX_SAT; sv++) { for (i = 0; i < neph; i++) { if (eph[i][sv].vflg == true) { gtmp = incGpsTime(eph[i][sv].toc, dsec); gps2date(>mp, &ttmp); eph[i][sv].toc = gtmp; eph[i][sv].t = ttmp; gtmp = incGpsTime(eph[i][sv].toe, dsec); eph[i][sv].toe = gtmp; } } } } else { if (subGpsTime(g0, gmin) < 0.0 || subGpsTime(gmax, g0) < 0.0) { gui_status_wprintw(RED, "Invalid start time.\n"); gui_status_wprintw(RED, "tmin = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n", tmin.y, tmin.m, tmin.d, tmin.hh, tmin.mm, tmin.sec, gmin.week, gmin.sec); gui_status_wprintw(RED, "tmax = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n", tmax.y, tmax.m, tmax.d, tmax.hh, tmax.mm, tmax.sec, gmax.week, gmax.sec); goto end_gps_thread; } } } else { g0 = gmin; simulator->start = tmin; } gui_mvwprintw(LS_FIX, 8, 40, "RINEX date: %s", rinex_date); gui_mvwprintw(LS_FIX, 10, 40, "Start time: %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)", simulator->start.y, simulator->start.m, simulator->start.d, simulator->start.hh, simulator->start.mm, simulator->start.sec, g0.week, g0.sec); if (simulator->show_verbose) { gui_mvwprintw(LS_FIX, 11, 40, "Simulation time: "); } gui_mvwprintw(LS_FIX, 7, 40, "Duration: %.1fs", ((double) numd) / 10.0); // Select the current set of ephemerides ieph = -1; for (i = 0; i < neph; i++) { for (sv = 0; sv < MAX_SAT; sv++) { if (eph[i][sv].vflg == true) { dt = subGpsTime(g0, eph[i][sv].toc); if (dt >= -SECONDS_IN_HOUR && dt < SECONDS_IN_HOUR) { ieph = i; break; } } } if (ieph >= 0) // ieph has been set break; } if (ieph == -1) { gui_status_wprintw(RED, "No current set of ephemerides has been found.\n"); goto end_gps_thread; } //////////////////////////////////////////////////////////// // Read almanac //////////////////////////////////////////////////////////// almanac_gps_t *alm = almanac_init(); if (simulator->almanac_enable) { if (simulator->use_ftp) { curl_code = almanac_download(); } else { curl_code = almanac_read_file(); } if (curl_code != CURLE_OK) { switch (curl_code) { case CURLE_REMOTE_FILE_NOT_FOUND: gui_status_wprintw(RED, "Almanac file not found!\n"); break; case CURLE_READ_ERROR: gui_status_wprintw(RED, "Error reading almanac file!\n"); break; default: gui_status_wprintw(RED, "Almanac error, code: %d\n", curl_code); break; } } } if (simulator->almanac_enable && alm->valid) { gtmp.sec = 0.0; gtmp.week = 0; // Check TOA for (sv = 0; sv < MAX_SAT; sv++) { if (alm->sv[sv].valid != 0) // Valid almanac { gtmp = alm->sv[sv].toa; dt = subGpsTime(alm->sv[sv].toa, g0); if (dt < (-4.0 * SECONDS_IN_WEEK) || dt > (4.0 * SECONDS_IN_WEEK)) { gui_status_wprintw(RED, "Invalid time of almanac.\n"); goto end_gps_thread; } } } gps2date(>mp, &ttmp); gui_mvwprintw(LS_FIX, 9, 40, "Almanac date: %4d/%02d/%02d,%02d:%02d:%02.0f", ttmp.y, ttmp.m, ttmp.d, ttmp.hh, ttmp.mm, ttmp.sec); } else { gui_mvwprintw(LS_FIX, 9, 40, "Almanac date: Disabled or invalid."); } //////////////////////////////////////////////////////////// // Initialize channels //////////////////////////////////////////////////////////// // Clear all channels for (i = 0; i < MAX_CHAN; i++) chan[i].prn = 0; // Clear satellite allocation flag for (sv = 0; sv < MAX_SAT; sv++) allocatedSat[sv] = -1; // Initial reception time grx = incGpsTime(g0, 0.0); // Allocate visible satellites allocateChannel(chan, alm, eph[ieph], ionoutc, grx, xyz[0], elvmask); for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn > 0) { gui_mvwprintw(LS_FIX, start_y++, 1, "%02d %6.1f %5.1f %11.1f %5.1f", chan[i].prn, chan[i].azel[0] * R2D, chan[i].azel[1] * R2D, chan[i].rho0.d, chan[i].rho0.iono_delay); } sat_simulated[chan[i].prn] = true; } gui_mvwprintw(LS_FIX, 3, 40, "Nav: %02d satellites", start_y - 4); // Receiver antenna gain pattern for (i = 0; i < 37; i++) ant_pat[i] = pow(10.0, -ant_pat_db[i] / 20.0); // Update receiver time grx = incGpsTime(grx, 0.1); // Create IQ buffer. iq_buff = calloc(IQ_BUFFER_SIZE, 2); // Aquire first fifo block for transfer buffer struct iq_buf *iq = fifo_acquire(); //////////////////////////////////////////////////////////// // Generate baseband signals //////////////////////////////////////////////////////////// for (iumd = 1; iumd < numd; iumd++) { if (simulator->gps_thread_exit) { break; } // Signal GPS init done and running if (simulator->gps_thread_running == false) { simulator->gps_thread_running = true; pthread_cond_signal(&(simulator->gps_init_done)); } if (simulator->interactive_mode) { // Stay at the current location xyz[iumd][0] = xyz[iumd - 1][0]; xyz[iumd][1] = xyz[iumd - 1][1]; xyz[iumd][2] = xyz[iumd - 1][2]; // Update the target location double dir = (simulator->target.bearing / 1000) / R2D; neu[0] = (simulator->target.velocity * cos(dir)) * 0.1; neu[1] = (simulator->target.velocity * sin(dir)) * 0.1; neu[2] = simulator->target.vertical_speed * 0.1; xyz[iumd][0] += tmat[0][0] * neu[0] + tmat[1][0] * neu[1] + tmat[2][0] * neu[2]; xyz[iumd][1] += tmat[0][1] * neu[0] + tmat[1][1] * neu[1] + tmat[2][1] * neu[2]; xyz[iumd][2] += tmat[0][2] * neu[0] + tmat[1][2] * neu[1] + tmat[2][2] * neu[2]; } for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn > 0) { // Refresh code phase and data bit counters range_t rho; sv = chan[i].prn - 1; // Current pseudorange computeRange(&rho, eph[ieph][sv], &ionoutc, grx, xyz[iumd]); chan[i].azel[0] = rho.azel[0]; chan[i].azel[1] = rho.azel[1]; // Update code phase and data bit counters computeCodePhase(&chan[i], rho, 0.1); #ifndef FLOAT_CARR_PHASE chan[i].carr_phasestep = (int) round(512.0 * 65536.0 * chan[i].f_carr * delt); #endif // Path loss path_loss = 20200000.0 / rho.d; // Receiver antenna gain ibs = (int) ((90.0 - rho.azel[1] * R2D) / 5.0); // covert elevation to boresight ant_gain = ant_pat[ibs]; // Signal gain gain[i] = (double) (path_loss * ant_gain); // Pluto SDR needs more signal strength due to 12 bit DAC range. // Otherwise signal dynamic range is very low. if (simulator->sdr_type == SDR_PLUTOSDR) { // Will result in larger IQ values, hence higher signal amplitude. // Best value to be defined. gain[i] *= 2; } } } for (isamp = 0; isamp < NUM_IQ_SAMPLES; isamp++) { int i_acc = 0.0f; int q_acc = 0.0f; for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn > 0) { #ifdef FLOAT_CARR_PHASE // carr_phase 0.0 - 1.0 iTable = (int) floor(chan[i].carr_phase * 512.0); #else iTable = (chan[i].carr_phase >> 16) & 511; // 9-bit index #endif // dataBit -1 or 1 // codeCA -1 or 1 ip = chan[i].dataBit * chan[i].codeCA * cosTable512[iTable] * gain[i]; qp = chan[i].dataBit * chan[i].codeCA * sinTable512[iTable] * gain[i]; // Accumulate for all visible satellites i_acc += ip; q_acc += qp; // Update code phase chan[i].code_phase += chan[i].f_code * delt; if (chan[i].code_phase >= CA_SEQ_LEN) { chan[i].code_phase -= CA_SEQ_LEN; chan[i].icode++; if (chan[i].icode >= 20) // 20 C/A codes = 1 navigation data bit { chan[i].icode = 0; chan[i].ibit++; if (chan[i].ibit >= 30) // 30 navigation data bits = 1 word { chan[i].ibit = 0; chan[i].iword++; /* if (chan[i].iword>=N_DWRD) fprintf(stderr, "\nWARNING: Subframe word buffer overflow.\n"); */ } // Set new navigation data bit chan[i].dataBit = (int) ((chan[i].dwrd[chan[i].iword]>>(29 - chan[i].ibit)) & 0x1UL)*2 - 1; } } // Set current code chip chan[i].codeCA = chan[i].ca[(int) chan[i].code_phase]*2 - 1; // Update carrier phase #ifdef FLOAT_CARR_PHASE chan[i].carr_phase += chan[i].f_carr * delt; if (chan[i].carr_phase >= 1.0) chan[i].carr_phase -= 1.0; else if (chan[i].carr_phase < 0.0) chan[i].carr_phase += 1.0; #else chan[i].carr_phase += chan[i].carr_phasestep; #endif } } // Store I/Q samples into buffer iq_buff[isamp * 2] = (short) i_acc; iq_buff[isamp * 2 + 1] = (short) q_acc; } // Fill transfer fifo for (isamp = 0; isamp < IQ_BUFFER_SIZE; isamp++) { // validLength starts with 0 on aquire if (simulator->sample_size == SC16) { iq->data16[iq->validLength] = iq_buff[isamp]; } else { iq->data8[iq->validLength] = iq_buff[isamp] >> 4; } iq->validLength += 1; if (simulator->sdr_type == SDR_HACKRF) { // Fill one fifo block until full if (iq->validLength == HACKRF_TRANSFER_BUFFER_SIZE) { // Enqueue full fifo block fifo_enqueue(iq); // Get a new one and fill as long as we don't reach IQ_BUFFER_SIZE iq = fifo_acquire(); } // We don't enque a partly filled fifo block but keep it for the next round } } // File writer and Pluto SDR taking the entire IQ buffer at once. if (simulator->sdr_type == SDR_IQFILE || simulator->sdr_type == SDR_PLUTOSDR) { // Enqueue full fifo block fifo_enqueue(iq); // Get a new one iq = fifo_acquire(); } // // Update navigation message and channel allocation every 30 seconds // igrx = (int) (grx.sec * 10.0 + 0.5); xyz2llh(xyz[iumd], llh); simulator->target.lat = llh[0] * R2D; simulator->target.lon = llh[1] * R2D; simulator->target.height = llh[2]; gui_show_target(&simulator->target); if (igrx % 300 == 0) // Every 30 seconds { // Update navigation message for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn > 0) { generateNavMsg(grx, &chan[i], 0); } } // Refresh ephemeris and subframes // Quick and dirty fix. Need more elegant way. for (sv = 0; sv < MAX_SAT; sv++) { if (eph[ieph + 1][sv].vflg == true) { dt = subGpsTime(eph[ieph + 1][sv].toc, grx); if (dt < SECONDS_IN_HOUR) { ieph++; if (ieph >= EPHEM_ARRAY_SIZE) { ieph = 0; } for (i = 0; i < MAX_CHAN; i++) { // Generate new subframes if allocated if (chan[i].prn != 0) eph2sbf(eph[ieph][chan[i].prn - 1], ionoutc, alm, chan[i].sbf); } } break; } } // Update channel allocation allocateChannel(chan, alm, eph[ieph], ionoutc, grx, xyz[0], elvmask); if (simulator->show_verbose) { gps2date(&grx, &simulator->start); gui_mvwprintw(LS_FIX, 11, 57, "%4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)", simulator->start.y, simulator->start.m, simulator->start.d, simulator->start.hh, simulator->start.mm, simulator->start.sec, grx.week, grx.sec); gui_mvwprintw(LS_FIX, 5, 40, "xyz = %11.1f, %11.1f, %11.1f", xyz[iumd][0], xyz[iumd][1], xyz[iumd][2]); gui_mvwprintw(LS_FIX, 6, 40, "llh = %11.6f, %11.6f, %11.1f", llh[0] * R2D, llh[1] * R2D, llh[2]); start_y = 4; for (i = 0; i < 33; i++) sat_simulated[i] = false; for (i = 0; i < MAX_CHAN; i++) { if (chan[i].prn > 0) { gui_mvwprintw(LS_FIX, start_y++, 1, "%02d %6.1f %5.1f %11.1f %5.1f", chan[i].prn, chan[i].azel[0] * R2D, chan[i].azel[1] * R2D, chan[i].rho0.d, chan[i].rho0.iono_delay); } sat_simulated[chan[i].prn] = true; } gui_mvwprintw(LS_FIX, 3, 40, "Nav: %02d satellites", start_y - 4); } } // Update receiver time grx = incGpsTime(grx, 0.1); // Update time counter gui_mvwprintw(LS_FIX, 12, 40, "Elapsed: %5.1fs", subGpsTime(grx, g0)); } gui_status_wprintw(GREEN, "Simulation complete\n"); end_gps_thread: free(iq_buff); if (xyz) free(xyz); gui_status_wprintw(RED, "Exit GPS thread\n"); simulator->gps_thread_exit = true; pthread_cond_signal(&(simulator->gps_init_done)); pthread_exit(NULL); } ================================================ FILE: gps.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef GPS_H #define GPS_H /* For RKT simulation. Higher computational load, but smoother carrier phase.*/ #define FLOAT_CARR_PHASE /* Real-time signal generation */ #define REAL_TIME_GPS #define RINEX2_FILE_NAME "rinex2.gz" #define RINEX3_FILE_NAME "rinex3.gz" #define RINEX_FTP_URL "ftp://igs.bkg.bund.de/IGS/" #define RINEX2_SUBFOLDER "nrt" #define RINEX3_SUBFOLDER "nrt_v3" #define RINEX_FTP_FILE "%s/%03i/%02i/%4s%03i%c.%02in.gz" /* Maximum length of a line in a text file (RINEX, motion) */ #define MAX_CHAR (100) /* Maximum number of satellites in RINEX file */ #define MAX_SAT (32) /* Maximum number of channels we simulate */ #define MAX_CHAN (12) /* Maximum number of user motion points */ #ifndef REAL_TIME_GPS #define USER_MOTION_SIZE (3000) // max duration at 10Hz #else #define USER_MOTION_SIZE (864000) // for 24 hours at 10Hz #endif /* Number of subframes */ #define N_SBF (5) // 5 subframes per frame /* Number of words per subframe */ #define N_DWRD_SBF (10) // 10 word per subframe /* Number of words */ #define N_DWRD ((N_SBF+1)*N_DWRD_SBF) // Subframe word buffer size #define N_SBF_PAGE (3+2*25) // Subframes 1 to 3 and 25 pages of subframes 4 and 5 #define MAX_PAGE (25) /* C/A code sequence length */ #define CA_SEQ_LEN (1023) #define SECONDS_IN_WEEK 604800.0 #define SECONDS_IN_HALF_WEEK 302400.0 #define SECONDS_IN_DAY 86400.0 #define SECONDS_IN_HOUR 3600.0 #define SECONDS_IN_MINUTE 60.0 #define POW2_M5 0.03125 #define POW2_M19 1.907348632812500e-6 #define POW2_M29 1.862645149230957e-9 #define POW2_M31 4.656612873077393e-10 #define POW2_M33 1.164153218269348e-10 #define POW2_M43 1.136868377216160e-13 #define POW2_M55 2.775557561562891e-17 #define POW2_M50 8.881784197001252e-016 #define POW2_M30 9.313225746154785e-010 #define POW2_M27 7.450580596923828e-009 #define POW2_M24 5.960464477539063e-008 #define POW2_M21 4.76837158203125e-007 #define POW2_12 4096 #define POW2_M38 3.63797880709171e-012 #define POW2_M11 0.00048828125 #define POW2_M23 1.19209289550781e-007 #define POW2_M20 9.5367431640625e-007 // Conventional values employed in GPS ephemeris model (ICD-GPS-200) #define GM_EARTH 3.986005e14 #define OMEGA_EARTH 7.2921151467e-5 #ifndef PI #define PI 3.1415926535898 #endif #define WGS84_RADIUS 6378137.0 #define WGS84_ECCENTRICITY 0.0818191908426 #ifndef R2D #define R2D 57.2957795131 // *180/pi #endif #define SPEED_OF_LIGHT 2.99792458e8 #define LAMBDA_L1 0.190293672798365 /* C/A code frequency */ #define CODE_FREQ (1.023e6) #define CARR_TO_CODE (1.0/1540.0) #define EPHEM_ARRAY_SIZE (13) // for daily GPS broadcast ephemers file (brdc) /* GPS parity bit-vectors * The last 6 bits of a 30bit GPS word are parity check bits. * Each parity bit is computed from the XOR of a selection of bits from the * 1st 24 bits of the current GPS word, and the last 2 bits of the _previous_ * GPS word. * These parity bit-vectors are used to select which message bits will be used * for computing each of the 6 parity check bits. * We assume the two bits from the previous message (b29, b30) and the 24 bits * from the current message, are packed into a 32bit word in this order: * < b29, b30, b1, b2, b3, ... b23, b24, X, X, X, X, X, X > (X = don't care) * Example: if PBn = 0x40000080, * The parity check bit "n" would be computed from the expression (b30 XOR b23). */ #define PB1 0xbb1f3480 #define PB2 0x5d8f9a40 #define PB3 0xaec7cd00 #define PB4 0x5763e680 #define PB5 0x6bb1f340 #define PB6 0x8b7a89c0 /* * The almanac message for any dummy SVs shall contain alternating ones and zeros * with valid parity. (IS-GPS-200L, p.111, 20.3.3.5.1.2) */ #define EMPTY_WORD 0xaaaaaaaaUL /* Structure representing GPS time */ typedef struct { int week; /* GPS week number (since January 1980) */ double sec; /* second inside the GPS \a week */ } gpstime_t; /* Structure repreenting UTC time */ typedef struct { int y; /* Calendar year */ int m; /* Calendar month */ int d; /* Calendar day */ int hh; /* Calendar hour */ int mm; /* Calendar minutes */ double sec; /* Calendar seconds */ } datetime_t; /* Structure representing ephemeris of a single satellite */ typedef struct { int vflg; /* Valid Flag */ int sva; /* SV accuracy (URA index) */ int svh; /* SV health */ int code; /* 0 or 1 code L2 (Codes on L2 channel) */ int flag; /* L2 P data flag data indicates * whether navigation data is being modulated onto the L2 P(Y) code. */ double fit; datetime_t t; gpstime_t toc; /* Time of Clock */ gpstime_t toe; /* Time of Ephemeris */ int iodc; /* Issue of Data, Clock */ int iode; /* Isuse of Data, Ephemeris */ double deltan; /* Delta-N (radians/sec) */ double cuc; /* Cuc (radians) */ double cus; /* Cus (radians) */ double cic; /* Correction to inclination cos (radians) */ double cis; /* Correction to inclination sin (radians) */ double crc; /* Correction to radius cos (meters) */ double crs; /* Correction to radius sin (meters) */ double ecc; /* e Eccentricity */ double sqrta; /* sqrt(A) (sqrt(m)) */ double m0; /* Mean anamoly (radians) */ double omg0; /* Longitude of the ascending node (radians) */ double inc0; /* Inclination (radians) */ double aop; double omgdot; /* Omega dot (radians/s) */ double idot; /* IDOT (radians/s) */ double af0; /* a_f0 Clock offset (seconds) */ double af1; /* a_f1 rate (sec/sec) */ double af2; /* a_f2 acceleration (sec/sec^2) */ double tgd; /* Group delay L2 bias */ // Working variables follow double n; /* Mean motion (Average angular velocity) */ double sq1e2; /* sqrt(1-e^2) */ double A; /* Semi-major axis */ double omgkdot; /* OmegaDot-OmegaEdot */ } ephem_t; typedef struct { int enable; int vflg; double alpha0, alpha1, alpha2, alpha3; double beta0, beta1, beta2, beta3; double A0, A1; int dtls, tot, wnt; int dtlsf, dn, wnlsf; } ionoutc_t; typedef struct { gpstime_t g; double range; // pseudorange double rate; double d; // geometric distance double azel[2]; double iono_delay; } range_t; /* Structure representing a Channel */ typedef struct { int prn; /* PRN Number */ int ca[CA_SEQ_LEN]; /* C/A Sequence */ double f_carr; /* Carrier frequency */ double f_code; /* Code frequency */ #ifdef FLOAT_CARR_PHASE double carr_phase; #else unsigned int carr_phase; /* Carrier phase */ int carr_phasestep; /* Carrier phasestep */ #endif double code_phase; /* Code phase */ gpstime_t g0; /* GPS time at start */ unsigned long sbf[N_SBF_PAGE][N_DWRD_SBF]; /*!< current subframe */ unsigned long dwrd[N_DWRD]; /*!< Data words of sub-frame */ int ipage; int iword; /* initial word */ int ibit; /* initial bit */ int icode; /* initial code */ int dataBit; /* current data bit */ int codeCA; /* current C/A code */ double azel[2]; range_t rho0; } channel_t; /* Structure represending a single GPS monitoring station. */ typedef struct { const char *id_v2; const char *id_v3; const char *name; } stations_t; void *gps_thread_ep(void *arg); #endif /* GPS_H */ ================================================ FILE: gui.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include #include #include #include #include "gps-sim.h" #include "gps.h" #include "gui.h" static const int info_width = 50; static const int info_height = 13; static const int help_width = 50; static const int help_height = 13; static int max_x = 0; static int max_y = 0; static WINDOW *window[13] = {NULL}; static PANEL *panel[13] = {NULL}; static pthread_mutex_t gui_lock; // Mutex to lock access during GUI updates static void gui_update() { update_panels(); doupdate(); } static void show_footer(WINDOW *win) { wattron(win, COLOR_PAIR(2)); mvwprintw(win, max_y - STATUS_HEIGHT - 2, 10, "TAB or F1-F3 switch displays, 'x' Exit, 'i' Info, 'h' Help"); wattroff(win, COLOR_PAIR(2)); } /* Show the window with a border and a label */ static void show_window(WINDOW *win, char *label) { int startx, half_width, str_leng; half_width = max_x / 2; str_leng = (int) strlen(label); startx = half_width - (str_leng / 2); box(win, 0, 0); // Header separator mvwaddch(win, 2, 0, ACS_LTEE); mvwhline(win, 2, 1, ACS_HLINE, max_x - 2); mvwaddch(win, 2, max_x - 1, ACS_RTEE); // Status separator mvwaddch(win, max_y - STATUS_HEIGHT - 2, 0, ACS_LTEE); mvwhline(win, max_y - STATUS_HEIGHT - 2, 1, ACS_HLINE, max_x - 2); mvwaddch(win, max_y - STATUS_HEIGHT - 2, max_x - 1, ACS_RTEE); // Header label wattron(win, COLOR_PAIR(1)); mvwprintw(win, 1, startx, "%s", label); wattroff(win, COLOR_PAIR(1)); show_footer(win); doupdate(); } //print the header info for LS fix result display static void ls_show_header(void) { wattron(window[LS_FIX], COLOR_PAIR(2)); mvwprintw(window[LS_FIX], 3, 1, "PRN AZ ELEV PRange dIon"); wattroff(window[LS_FIX], COLOR_PAIR(2)); wrefresh(window[TOP]); } static void show_heading(float degree) { int bias; int zero_y = 0; //coordinates of zero int zero_x = 9; //draw window outline wborder(window[HEADING], '.', '.', '.', '.', '.', '.', '.', '.'); mvwaddch(window[HEADING], 0, 9, ACS_UARROW); mvwprintw(window[HEADING], 1, 9, "0"); mvwprintw(window[HEADING], 6, 0, "<270"); mvwprintw(window[HEADING], 6, 16, "90>"); mvwprintw(window[HEADING], 11, 8, "180"); mvwaddch(window[HEADING], 12, 9, ACS_DARROW); //wattron(window[HEADING],COLOR_PAIR(5)|A_BOLD); mvwaddch(window[HEADING], 6, 9, ACS_DIAMOND); //wattroff(window[HEADING],COLOR_PAIR(5)|A_BOLD); //show window title wattron(window[HEADING], COLOR_PAIR(11) | A_BOLD); mvwprintw(window[HEADING], 4, 6, "DIRECTION"); wattroff(window[HEADING], COLOR_PAIR(11) | A_BOLD); wattron(window[HEADING], COLOR_PAIR(13) | A_BOLD); mvwprintw(window[HEADING], 8, 6, "%6.1f", degree); wattroff(window[HEADING], COLOR_PAIR(13) | A_BOLD); //calculate coordinates bias = (int) (degree / 6); //data is at top line,0~60 if ((bias >= 0) && (bias <= 9)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], 0, 9 + bias, ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } //data is at right line 60~126 if ((bias >= 10) && (bias <= 20)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], zero_y + bias - 9, zero_x + 9, ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } //data is at bottom line 132~180 if ((bias >= 21) && (bias <= 30)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], zero_y + 12, 18 - (bias - 20), ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } //data is at bottom line 180~234 if ((bias >= 31) && (bias <= 39)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], zero_y + 12, 9 + (-bias + 30), ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } //data is at left line 240~-300 if ((bias >= 40) && (bias <= 50)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], 12 + (-bias + 39), 0, ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } //data is at left line 306~-360 if ((bias >= 51) && (bias <= 60)) { wattron(window[HEADING], COLOR_PAIR(12) | A_BOLD); mvwaddch(window[HEADING], 0, bias - 51, ACS_DIAMOND); wattroff(window[HEADING], COLOR_PAIR(12) | A_BOLD); } if (window[TOP] == window[KF_FIX]) { wrefresh(window[HEADING]); } } static void show_vertical_speed(float height) { wattron(window[HEIGHT], COLOR_PAIR(11) | A_BOLD); mvwprintw(window[HEIGHT], 0, 0, "VERT SPEED"); wattroff(window[HEIGHT], COLOR_PAIR(11) | A_BOLD); wattron(window[HEIGHT], COLOR_PAIR(13) | A_BOLD); mvwprintw(window[HEIGHT], 1, 0, "%6.1f m/s", height); wattroff(window[HEIGHT], COLOR_PAIR(13) | A_BOLD); if (window[TOP] == window[KF_FIX]) { wrefresh(window[HEIGHT]); } } static void show_speed(float speed) { wattron(window[SPEED], COLOR_PAIR(11) | A_BOLD); mvwprintw(window[SPEED], 0, 3, "SPEED"); wattroff(window[SPEED], COLOR_PAIR(11) | A_BOLD); wattron(window[SPEED], COLOR_PAIR(13) | A_BOLD); mvwprintw(window[SPEED], 1, 0, "%6.1f km/h", speed); wattroff(window[SPEED], COLOR_PAIR(13) | A_BOLD); if (window[TOP] == window[KF_FIX]) { wrefresh(window[SPEED]); } } static void show_target(target_t *target) { mvwprintw(window[TARGET], 0, 4, "Target:"); mvwprintw(window[TARGET], 1, 0, "Distance %9.1f m", target->distance); mvwprintw(window[TARGET], 2, 0, "Direction %9.1f deg", target->bearing / 1000); mvwprintw(window[TARGET], 3, 0, "Height %9.1f m", target->height); mvwprintw(window[TARGET], 4, 0, "Longitude %9.6f deg", target->lon); mvwprintw(window[TARGET], 5, 0, "Latitude %9.6f deg", target->lat); if (window[TOP] == window[KF_FIX]) { wnoutrefresh(window[TARGET]); } } static void show_local(location_t *loc) { mvwprintw(window[LOCATION], 0, 4, "Location:"); mvwprintw(window[LOCATION], 1, 0, "Longitude %9.6f deg", loc->lon); mvwprintw(window[LOCATION], 2, 0, "Latitude %9.6f deg", loc->lat); mvwprintw(window[LOCATION], 3, 0, "Height %9.1f m", loc->height); if (window[TOP] == window[KF_FIX]) { wnoutrefresh(window[LOCATION]); } } static void eph_show_header(void) { wattron(window[EPHEMERIS], COLOR_PAIR(2)); mvwprintw(window[EPHEMERIS], 3, 1, "PRN AZ ELEV EPH SIM "); wattroff(window[EPHEMERIS], COLOR_PAIR(2)); wrefresh(window[TOP]); } static void init_windows(void) { int info_x, info_y; window[STATUS] = newwin(STATUS_HEIGHT, max_x - 2, max_y - STATUS_HEIGHT - 1, 1); window[TRACK] = newwin(max_y, max_x, 0, 0); window[LS_FIX] = newwin(max_y, max_x, 0, 0); window[EPHEMERIS] = newwin(max_y, max_x, 0, 0); window[KF_FIX] = newwin(max_y, max_x, 0, 0); window[HEADING] = subwin(window[KF_FIX], HEAD_HEIGHT, HEAD_WIDTH, HEAD_Y, HEAD_X); show_heading(0); window[HEIGHT] = subwin(window[KF_FIX], 2, 12, HEAD_Y + 6, HEAD_X + 20); show_vertical_speed(0); window[SPEED] = subwin(window[KF_FIX], 2, 12, HEAD_Y + 6, HEAD_X - 12); show_speed(0); window[TARGET] = subwin(window[KF_FIX], 7, 26, 4, 30); target_t t = {0}; show_target(&t); window[LOCATION] = subwin(window[KF_FIX], 4, 26, 4, 2); location_t l = {0}; show_local(&l); /* setup info window */ info_x = (max_x - info_width) / 2; info_y = (max_y - info_height) / 2; window[INFO] = newwin(info_height, info_width, info_y, info_x); box(window[INFO], 0, 0); wattron(window[INFO], COLOR_PAIR(1)); mvwprintw(window[INFO], 1, 2, "Multi SDR GPS Simulator"); wattroff(window[INFO], COLOR_PAIR(1)); wattron(window[INFO], COLOR_PAIR(2)); mvwprintw(window[INFO], 3, 2, "https://github.com/Mictronics/multi-sdr-gps"); mvwprintw(window[INFO], 4, 2, "(c) Mictronics 2021"); mvwprintw(window[INFO], 5, 2, "Distributed under the MIT License"); mvwprintw(window[INFO], 7, 2, "Based on work from Takuji Ebinuma (gps-sdr-sim)"); mvwprintw(window[INFO], 8, 2, "and IvanKor."); mvwprintw(window[INFO], info_height - 2, 2, "Press any key to return."); wattroff(window[INFO], COLOR_PAIR(2)); /* setup help window */ info_x = (max_x - help_width) / 2; info_y = (max_y - help_height) / 2; window[HELP] = newwin(help_height, help_width, info_y, info_x); box(window[HELP], 0, 0); wattron(window[HELP], COLOR_PAIR(1)); mvwprintw(window[HELP], 1, 2, "Help"); wattroff(window[HELP], COLOR_PAIR(1)); mvwprintw(window[HELP], 2, 2, "w Increase altitude i Info"); mvwprintw(window[HELP], 3, 2, "s Decrease altitude h Help"); mvwprintw(window[HELP], 4, 2, "d Heading right x Exit"); mvwprintw(window[HELP], 5, 2, "a Heading left F1 Setup Window"); mvwprintw(window[HELP], 6, 2, "e Increase speed F2 Status Window"); mvwprintw(window[HELP], 7, 2, "q Decrease speed F3 Position Window"); mvwprintw(window[HELP], 8, 2, "t Increase TX gain"); mvwprintw(window[HELP], 9, 2, "g Decrease TX gain"); /* Attach a panel to each window * Order is bottom up */ panel[TRACK] = new_panel(window[TRACK]); panel[LS_FIX] = new_panel(window[LS_FIX]); panel[KF_FIX] = new_panel(window[KF_FIX]); panel[EPHEMERIS] = new_panel(window[EPHEMERIS]); panel[INFO] = new_panel(window[INFO]); panel[STATUS] = new_panel(window[STATUS]); panel[HELP] = new_panel(window[HELP]); hide_panel(panel[INFO]); hide_panel(panel[HELP]); /* Set up the user pointers to the next panel */ set_panel_userptr(panel[TRACK], panel[LS_FIX]); set_panel_userptr(panel[LS_FIX], panel[KF_FIX]); set_panel_userptr(panel[KF_FIX], panel[EPHEMERIS]); set_panel_userptr(panel[EPHEMERIS], panel[TRACK]); //Print title of each window show_window(window[TRACK], "GPS Simulator Setup"); show_window(window[LS_FIX], "GPS Simulation Status"); show_window(window[EPHEMERIS], "Test"); show_window(window[KF_FIX], "Dynamic Position"); ls_show_header(); top_panel(panel[TRACK]); panel[TOP] = panel[TRACK]; // Keep status window scrolling when adding lines scrollok(window[STATUS], TRUE); /* Update the stacking order.tracking_panel will be on the top*/ update_panels(); doupdate(); } void gui_init(void) { char ch; pthread_mutex_init(&gui_lock, NULL); pthread_mutex_lock(&gui_lock); /* Initialize curses */ initscr(); getmaxyx(stdscr, max_y, max_x); endwin(); /*check if the col and row meet the threshold */ if (max_y < ROW_THRD || max_x < COL_THRD) { printf("Your console window size is %dx%d need 26x120\n", max_y, max_x); printf("Do you still want to continue? [Y/N] "); ch = getchar(); if ((ch != 'y') && (ch != 'Y')) { exit(0); } } /* redo the initialization */ initscr(); getmaxyx(stdscr, max_y, max_x); start_color(); cbreak(); noecho(); curs_set(0); //no cursor keypad(stdscr, TRUE); timeout(100); /* Initialize all the colors */ init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_BLUE, COLOR_BLACK); init_pair(4, COLOR_CYAN, COLOR_BLACK); init_pair(5, COLOR_YELLOW, COLOR_BLACK); init_pair(11, COLOR_BLUE, COLOR_BLACK); init_pair(12, COLOR_RED, COLOR_BLACK); init_pair(13, COLOR_RED, COLOR_WHITE); init_pair(14, COLOR_BLACK, COLOR_RED); init_windows(); pthread_mutex_unlock(&gui_lock); } void gui_destroy(void) { pthread_mutex_unlock(&gui_lock); // Just in case pthread_mutex_destroy(&gui_lock); delwin(window[TRACK]); delwin(window[LS_FIX]); delwin(window[KF_FIX]); delwin(window[INFO]); delwin(window[HEADING]); delwin(window[HEIGHT]); delwin(window[SPEED]); delwin(window[TARGET]); delwin(window[LOCATION]); delwin(window[EPHEMERIS]); delwin(window[STATUS]); delwin(window[HELP]); endwin(); } void gui_mvwprintw(window_panel_t w, int y, int x, const char * fmt, ...) { pthread_mutex_lock(&gui_lock); va_list args; if (wmove(window[w], y, x) == ERR) { return; } va_start(args, fmt); vw_printw(window[w], fmt, args); va_end(args); // Refresh only what is printed on top panel, don't care about background // updates. wrefresh(window[TOP]); pthread_mutex_unlock(&gui_lock); } void gui_status_wprintw(status_color_t clr, const char * fmt, ...) { pthread_mutex_lock(&gui_lock); va_list args; va_start(args, fmt); if (clr > 0) { wattron(window[STATUS], COLOR_PAIR(clr)); } vw_printw(window[STATUS], fmt, args); if (clr > 0) { wattroff(window[STATUS], COLOR_PAIR(clr)); } va_end(args); wrefresh(window[STATUS]); pthread_mutex_unlock(&gui_lock); } void gui_colorpair(window_panel_t w, unsigned clr, attr_status_t onoff) { pthread_mutex_lock(&gui_lock); if (onoff == ON) { wattron(window[w], COLOR_PAIR(clr)); } else { wattroff(window[w], COLOR_PAIR(clr)); } pthread_mutex_unlock(&gui_lock); } int gui_getch(void) { int ch = getch(); /* if (ch != -1) { fprintf(stderr, "%i\n", ch); } */ return ch; } void gui_top_panel(window_panel_t p) { pthread_mutex_lock(&gui_lock); top_panel(panel[p]); panel[TOP] = panel[p]; window[TOP] = window[p]; // Status is alway the top most panel top_panel(panel[STATUS]); gui_update(); pthread_mutex_unlock(&gui_lock); } void gui_toggle_current_panel(void) { pthread_mutex_lock(&gui_lock); panel[TOP] = (PANEL *) panel_userptr(panel[TOP]); top_panel(panel[TOP]); window[TOP] = panel_window(panel[TOP]); // Status is alway the top most panel top_panel(panel[STATUS]); gui_update(); pthread_mutex_unlock(&gui_lock); } void gui_show_panel(window_panel_t p, attr_status_t onoff) { pthread_mutex_lock(&gui_lock); if (onoff == ON) { show_panel(panel[p]); } else { hide_panel(panel[p]); } gui_update(); pthread_mutex_unlock(&gui_lock); } void gui_show_speed(float speed) { pthread_mutex_lock(&gui_lock); show_speed(speed); pthread_mutex_unlock(&gui_lock); } void gui_show_heading(float hdg) { pthread_mutex_lock(&gui_lock); show_heading(hdg); pthread_mutex_unlock(&gui_lock); } void gui_show_vertical_speed(float vs) { pthread_mutex_lock(&gui_lock); show_vertical_speed(vs); pthread_mutex_unlock(&gui_lock); } void gui_show_location(void *l) { pthread_mutex_lock(&gui_lock); show_local((location_t *) (l)); pthread_mutex_unlock(&gui_lock); } void gui_show_target(void *t) { pthread_mutex_lock(&gui_lock); show_target((target_t *) (t)); pthread_mutex_unlock(&gui_lock); } ================================================ FILE: gui.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef GUI_H #define GUI_H #define ROW_THRD 26 #define COL_THRD 120 #define HEAD_HEIGHT 13 #define HEAD_WIDTH 19 #define HEAD_Y 12 #define HEAD_X 14 #define STATUS_HEIGHT 10 // Interactive keys #define UP_KEY 'w' #define DOWN_KEY 's' #define RIGHT_KEY 'd' #define LEFT_KEY 'a' #define UPSPEED_KEY 'e' #define DOWNSPEED_KEY 'q' #define GAIN_INC_KEY 't' #define GAIN_DEC_KEY 'g' typedef enum { TRACK = 0, LS_FIX, KF_FIX, INFO, HEADING, HEIGHT, SPEED, TARGET, LOCATION, EPHEMERIS, TOP, STATUS, HELP } window_panel_t; typedef enum { OFF = 0, ON = 1 } attr_status_t; typedef enum { DEFAULT = 0, RED = 1, GREEN = 2, BLUE = 3, CYAN = 4, YELLOW = 5 } status_color_t; void gui_init(void); int gui_getch(void); void gui_destroy(void); void gui_mvwprintw(window_panel_t w, int y, int x, const char * fmt, ...); void gui_status_wprintw(status_color_t clr, const char * fmt, ...); void gui_colorpair(window_panel_t w, unsigned clr, attr_status_t onoff); void gui_top_panel(window_panel_t p); void gui_toggle_current_panel(void); void gui_show_panel(window_panel_t p, attr_status_t onoff); void gui_show_speed(float speed); void gui_show_heading(float hdg); void gui_show_vertical_speed(float vs); void gui_show_location(void *l); void gui_show_target(void *t); #endif /* GUI_H */ ================================================ FILE: help.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef HELP_H #define HELP_H #include const char *argp_program_bug_address = "https://github.com/Mictronics/multi-sdr-gps-sim"; static error_t parse_opt(int key, char *arg, struct argp_state *state); static struct argp_option options[] = { {0, 0, 0, 0, "Options:", 1}, {"nav-file", 'e', "filename", 0, "RINEX navigation file for GPS ephemeris (required)", 1}, {"use-ftp", 'f', 0, 0, "Pull actual RINEX navigation file and almanac from online source", 1}, {"geo-loc", 'l', "location", 0, "Latitude, Longitude, Height (static mode) e.g. 35.681298,139.766247,10.0", 1}, {"start", 's', "date,time", 0, "Scenario start time YYYY/MM/DD,hh:mm:ss (use 'now' for actual time)", 1}, {"disable-iono", 'I', 0, 0, "Disable ionospheric delay for spacecraft scenario", 1}, {"verbose", 'v', 0, 0, "Show verbose output and details about simulated channels", 1}, {"interactive", 'i', 0, 0, "Use interactive mode", 1}, {"amplifier", 'a', 0, 0, "Enable TX amplifier (default OFF)", 1}, {"gain", 'g', "gain", 0, "Set initial TX gain, HackRF: 0-47dB, Pluto: -80-0dB (default 0)", 1}, {"duration", 'd', "seconds", 0, "Duration in seconds", 1}, {"target", 't', "distance,bearing,height", 0, "Target distance [m], bearing [°] and height [m]", 1}, {"ppb", 'p', "ppb", 0, "Set oscillator error in ppb (default 0)", 1}, {"rinex3", '3', 0, 0, "Use RINEX v3 navigation data format", 1}, {"radio", 'r', "name", 0, "Set the SDR device type name (default none)", 1}, {"iq16", 700, 0, 0, "Set IQ sample size to 16 bit (default 8 bit)", 1}, {"uri", 'U', "uri", 0, "ADLAM-Pluto URI", 1}, {"network", 'N', "network", 0, "ADLAM-Pluto network IP or hostname (default pluto.local)", 1}, {"motion", 'm', "name", 0, "User motion file (dynamic mode)", 1}, {"disable-almanac", 702, 0, 0, "Disable transmission of almanac information", 1}, {"station", 701, "id", 0, "Use station with given ID for RINEX FTP download (4 or 9 character ID)", 2}, {0, 0, 0, OPTION_DOC, "Station is a GPS ground station around the world which provides RINEX hourly updated data. See gps.c for station details. A random station is picked if no ID is given", 2}, {0, 0, 0, 0, "SDR device types (use with --radio or -r option):", 3}, {0, 0, 0, OPTION_DOC, " none", 3}, {0, 0, 0, OPTION_DOC, " iqfile", 3}, #ifdef ENABLE_HACKRFSDR {0, 0, 0, OPTION_DOC, " hackrf", 3}, #endif #ifdef ENABLE_PLUTOSDR {0, 0, 0, OPTION_DOC, " plutosdr", 3}, #endif { 0} }; #endif /* HELP_H */ ================================================ FILE: sdr.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include "gui.h" #include "sdr_hackrf.h" #include "sdr_iqfile.h" #include "sdr_pluto.h" #include "sdr.h" static int no_init(void); static void no_close(void); static int no_run(void); static int no_set_gain(const int); typedef struct { int (*init)(); void (*close)(); int (*run)(); int (*set_gain)(const int); const char *name; sdr_type_t sdr_type; } sdr_handler; static sdr_type_t current_type = SDR_NONE; static sdr_handler sdr_handlers[] = { { no_init, no_close, no_run, no_set_gain, "none", SDR_NONE}, { sdr_iqfile_init, sdr_iqfile_close, sdr_iqfile_run, no_set_gain, "iqfile", SDR_IQFILE}, #ifdef ENABLE_HACKRFSDR { sdr_hackrf_init, sdr_hackrf_close, sdr_hackrf_run, sdr_hackrf_set_gain, "hackrf", SDR_HACKRF}, #endif #ifdef ENABLE_PLUTOSDR { sdr_pluto_init, sdr_pluto_close, sdr_pluto_run, sdr_pluto_set_gain, "plutosdr", SDR_PLUTOSDR}, #endif { NULL, NULL, NULL, NULL, NULL, SDR_NONE} /* must come last */ }; static int no_init() { gui_status_wprintw(RED, "SDR type not recognized; supported SDR types are:\n"); for (int i = 0; sdr_handlers[i].name; ++i) { gui_status_wprintw(RED, " %s\n", sdr_handlers[i].name); } return -1; } static void no_close() { } static int no_run() { return -1; } static int no_set_gain(const int gain) { NOTUSED(gain); return -100; } static sdr_handler *current_handler(void) { for (int i = 0; sdr_handlers[i].name; ++i) { if (current_type == sdr_handlers[i].sdr_type) { return &sdr_handlers[i]; } } return &sdr_handlers[0]; } int sdr_init(simulator_t *simulator) { for (int i = 0; sdr_handlers[i].name; ++i) { if (!strcasecmp(sdr_handlers[i].name, simulator->sdr_name)) { current_type = sdr_handlers[i].sdr_type; simulator->sdr_type = sdr_handlers[i].sdr_type; } } return current_handler()->init(simulator); } void sdr_close(void) { current_handler()->close(); } int sdr_run(void) { return current_handler()->run(); } int sdr_set_gain(const int gain) { return current_handler()->set_gain(gain); } ================================================ FILE: sdr.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef SDR_H #define SDR_H #include "gps-sim.h" #define TX_FREQUENCY 1575420000 #define FREQ_ONE_MHZ (1000000ull) // 3 MHz is generated in Pluto SDR with integer dividers being multiple of 2 #define TX_SAMPLERATE 3000000 #define TX_BW (TX_SAMPLERATE * 2) #define NUM_FIFO_BUFFERS 8 // Number of samples for 0.1 second transmission #define NUM_IQ_SAMPLES (TX_SAMPLERATE / 10) // Size in number of IQ samples, one I and one Q per sample // Size in IQ elements, not bytes! #define IQ_BUFFER_SIZE (NUM_IQ_SAMPLES * 2) // HackRF transfer buffer size // Fixed to 4 * 8192 = 262144 bytes // Defined in libhackrf, hackrf.c #define HACKRF_TRANSFER_BUFFER_SIZE 262144 int sdr_init(simulator_t *simulator); void sdr_close(void); int sdr_run(void); int sdr_set_gain(int gain); #endif /* SDR_H */ ================================================ FILE: sdr_hackrf.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include /* for PRIX64 */ #include #include #include "gui.h" #include "fifo.h" #include "sdr.h" #include "sdr_hackrf.h" static hackrf_device_list_t *list; static hackrf_device* device; static const int gui_y_offset = 4; static const int gui_x_offset = 2; int sdr_hackrf_init(simulator_t *simulator) { int result = HACKRF_SUCCESS; uint8_t board_id = BOARD_ID_INVALID; char version[255 + 1]; uint16_t usb_version; read_partid_serialno_t read_partid_serialno; uint8_t operacakes[8]; double sample_rate_gps_hz; uint32_t baseband_filter_bw_hackrf_hz = 0; uint64_t freq_gps_hz; int y = gui_y_offset; // HackRF wants 8 bit signed samples if (simulator->sample_size == SC16) { gui_status_wprintw(YELLOW, "16 bit sample size requested. Reset to 8 bit with HackRF.\n"); } simulator->sample_size = SC08; result = hackrf_init(); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } list = hackrf_device_list(); if (list->devicecount < 1) { gui_status_wprintw(RED, "No HackRF boards found.\n"); return -1; } if (list->devicecount > 1) { gui_mvwprintw(TRACK, y++, gui_x_offset, "Found %d HackRF devices. Using index 0.", list->devicecount); } else { gui_mvwprintw(TRACK, y++, gui_x_offset, "Found HackRF device."); } if (list->serial_numbers[0]) { gui_mvwprintw(TRACK, y++, gui_x_offset, "Serial number: %s", list->serial_numbers[0]); } device = NULL; result = hackrf_device_list_open(list, 0, &device); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } result = hackrf_board_id_read(device, &board_id); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_board_id_read() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Board ID Number: %d (%s)", board_id, hackrf_board_id_name(board_id)); result = hackrf_version_string_read(device, &version[0], 255); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_version_string_read() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } result = hackrf_usb_api_version_read(device, &usb_version); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_usb_api_version_read() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Firmware Version: %s (API:%x.%02x)", version, (usb_version >> 8)&0xFF, usb_version & 0xFF); result = hackrf_board_partid_serialno_read(device, &read_partid_serialno); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_board_partid_serialno_read() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Part ID Number: 0x%08x 0x%08x", read_partid_serialno.part_id[0], read_partid_serialno.part_id[1]); result = hackrf_get_operacake_boards(device, &operacakes[0]); if ((result != HACKRF_SUCCESS) && (result != HACKRF_ERROR_USB_API_VERSION)) { gui_status_wprintw(RED, "hackrf_get_operacake_boards() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } if (result == HACKRF_SUCCESS) { for (int j = 0; j < 8; j++) { if (operacakes[j] == 0) break; gui_mvwprintw(TRACK, y++, gui_x_offset, "Operacake found, address: 0x%02x", operacakes[j]); } } #ifdef HACKRF_ISSUE_609_IS_FIXED uint32_t cpld_crc = 0; result = hackrf_cpld_checksum(device, &cpld_crc); if ((result != HACKRF_SUCCESS) && (result != HACKRF_ERROR_USB_API_VERSION)) { gui_status_wprintw(RED, "hackrf_cpld_checksum() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } if (result == HACKRF_SUCCESS) { gui_mvwprintw(TRACK, y++, 60, "CPLD checksum: 0x%08x\n", cpld_crc); } #endif /* HACKRF_ISSUE_609_IS_FIXED */ sample_rate_gps_hz = TX_SAMPLERATE; freq_gps_hz = TX_FREQUENCY; // Change the freq and sample rate to correct the crystal clock error. // sample_rate_gps_hz = (uint32_t) ((double) sample_rate_gps_hz * (10000000 - simulator->ppb) / 10000000 + 0.5); freq_gps_hz = freq_gps_hz * (10000000 - simulator->ppb) / 10000000; /* Compute default value depending on sample rate */ baseband_filter_bw_hackrf_hz = hackrf_compute_baseband_filter_bw(TX_BW); if (baseband_filter_bw_hackrf_hz > BASEBAND_FILTER_BW_MAX) { gui_mvwprintw(TRACK, y++, gui_x_offset, "Baseband filter BW must be less or equal to %u Hz/%.03f MHz", BASEBAND_FILTER_BW_MAX, (float) (BASEBAND_FILTER_BW_MAX / FREQ_ONE_MHZ)); return -1; } if (baseband_filter_bw_hackrf_hz < BASEBAND_FILTER_BW_MIN) { gui_mvwprintw(TRACK, y++, 60, "Baseband filter BW must be greater or equal to %u Hz/%.03f MHz", BASEBAND_FILTER_BW_MIN, (float) (BASEBAND_FILTER_BW_MIN / FREQ_ONE_MHZ)); return -1; } // Disable antenna bias tee. result = hackrf_set_antenna_enable(device, 0); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_antenna_enable() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Sample rate (%.0lf Hz/%.03f MHz)", sample_rate_gps_hz, ((float) sample_rate_gps_hz / (float) FREQ_ONE_MHZ)); result = hackrf_set_sample_rate(device, sample_rate_gps_hz); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_sample_rate_set() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Baseband filter bandwidth (%d Hz/%.03f MHz)", baseband_filter_bw_hackrf_hz, ((float) baseband_filter_bw_hackrf_hz / (float) FREQ_ONE_MHZ)); result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hackrf_hz); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_baseband_filter_bandwidth_set() failed: %s (%d)\n", hackrf_error_name(result), result); return -1; } gui_mvwprintw(TRACK, y++, gui_x_offset, "Freq (%" PRIu64 " Hz/%.03f MHz)", freq_gps_hz, ((double) freq_gps_hz / (double) FREQ_ONE_MHZ)); result = hackrf_set_freq(device, freq_gps_hz); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_freq() failed: %s (%d)", hackrf_error_name(result), result); return -1; } if (simulator->enable_tx_amp) { gui_mvwprintw(TRACK, y++, gui_x_offset, "Amplifier enabled"); result = hackrf_set_amp_enable(device, 1); } else { result = hackrf_set_amp_enable(device, 0); } if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_amp_enable() failed: %s (%d)", hackrf_error_name(result), result); return -1; } if (simulator->tx_gain < TX_IF_GAIN_MIN) { simulator->tx_gain = TX_IF_GAIN_MIN; } else if (simulator->tx_gain > TX_IF_GAIN_MAX) { simulator->tx_gain = TX_IF_GAIN_MAX; } gui_mvwprintw(TRACK, y++, gui_x_offset, "TX IF gain: %idB", simulator->tx_gain); result = hackrf_set_txvga_gain(device, simulator->tx_gain); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_txvga_gain() failed: %s (%d)", hackrf_error_name(result), result); return -1; } result = hackrf_set_hw_sync_mode(device, 0); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_hw_sync_mode() failed: %s (%d)", hackrf_error_name(result), result); return -1; } if (!fifo_create(NUM_FIFO_BUFFERS, HACKRF_TRANSFER_BUFFER_SIZE, sizeof (signed char))) { gui_status_wprintw(RED, "Error creating TX fifo!"); return -1; } return 0; } void sdr_hackrf_close(void) { fifo_halt(); fifo_destroy(); if (device != NULL) { hackrf_stop_tx(device); hackrf_set_amp_enable(device, 0); hackrf_set_txvga_gain(device, 0); hackrf_close(device); } hackrf_device_list_free(list); hackrf_exit(); } static int sdr_tx_callback(hackrf_transfer *transfer) { // Get a fifo block struct iq_buf *iq = fifo_dequeue(); if (iq != NULL && iq->data8 != NULL) { // Fifo has transfer block size memcpy(transfer->buffer, iq->data8, transfer->valid_length); // Release and free up used block fifo_release(iq); return 0; } return -1; } int sdr_hackrf_run(void) { if (device == NULL) { gui_status_wprintw(RED, "HackRF device is NULL\n"); return -1; } fifo_wait_full(); int result = hackrf_start_tx(device, sdr_tx_callback, NULL); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_start_tx() failed: %s (%d)", hackrf_error_name(result), result); return -1; } return 0; } int sdr_hackrf_set_gain(const int gain) { int g = gain; if (g < TX_IF_GAIN_MIN) { g = TX_IF_GAIN_MIN; } else if (g > TX_IF_GAIN_MAX) { g = TX_IF_GAIN_MAX; } int result = hackrf_set_txvga_gain(device, g); if (result != HACKRF_SUCCESS) { gui_status_wprintw(RED, "hackrf_set_txvga_gain() failed: %s (%d)", hackrf_error_name(result), result); } return g; } ================================================ FILE: sdr_hackrf.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef SDR_HACKRF_H #define SDR_HACKRF_H #include "gps-sim.h" #define TX_VGA1 0 #define TX_IF_GAIN_MIN 0 #define TX_IF_GAIN_MAX 47 #define BASEBAND_FILTER_BW_MIN (1750000) /* 1.75 MHz min value */ #define BASEBAND_FILTER_BW_MAX (28000000) /* 28 MHz max value */ int sdr_hackrf_init(simulator_t *simulator); void sdr_hackrf_close(void); int sdr_hackrf_run(void); int sdr_hackrf_set_gain(const int gain); #endif /* SDR_HACKRF_H */ ================================================ FILE: sdr_iqfile.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include "gui.h" #include "sdr.h" #include "sdr_iqfile.h" #include "fifo.h" static atomic_bool iqfile_thread_exit = false; static pthread_t iqfile_thread; static int sample_size = SC08; static void *iqfile_thread_ep(void *arg) { (void) arg; // Not used FILE *fp = fopen("iqdata.bin", "wb"); if (fp == NULL) { gui_status_wprintw(RED, "Error opening IQ data file.\n"); pthread_exit(NULL); } /* On a multi-core CPU we run the main thread and reader thread on different cores. * Try sticking the main thread to core 3 */ thread_to_core(3); set_thread_name("iqfile-thread"); while (!iqfile_thread_exit) { // Get a fifo block struct iq_buf *iq = fifo_dequeue(); if (iq != NULL) { if (sample_size == sizeof (signed short)) { fwrite(iq->data16, sizeof (signed short), iq->validLength, fp); } else { fwrite(iq->data8, sizeof (signed char), iq->validLength, fp); } // Release and free up used block fifo_release(iq); if (ferror(fp)) { gui_status_wprintw(RED, "Error writing IQ data file.\n"); } } } fclose(fp); pthread_exit(NULL); } int sdr_iqfile_init(simulator_t *simulator) { sample_size = simulator->sample_size; if (!fifo_create(NUM_FIFO_BUFFERS, IQ_BUFFER_SIZE, sample_size)) { gui_status_wprintw(RED, "Error creating IQ file fifo!"); return -1; } return 0; } void sdr_iqfile_close(void) { iqfile_thread_exit = true; fifo_halt(); fifo_destroy(); pthread_join(iqfile_thread, NULL); } int sdr_iqfile_run(void) { fifo_wait_full(); pthread_create(&iqfile_thread, NULL, iqfile_thread_ep, NULL); return 0; } ================================================ FILE: sdr_iqfile.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #ifndef SDR_IQFILE_H #define SDR_IQFILE_H int sdr_iqfile_init(simulator_t *simulator); void sdr_iqfile_close(void); int sdr_iqfile_run(void); #endif /* SDR_IQFILE_H */ ================================================ FILE: sdr_pluto.c ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ #include #include #include #include #include #include #include #include #include #include /* for PRIX64 */ #include #include #include #include "fifo.h" #include "gui.h" #include "sdr.h" #include "sdr_pluto.h" #include static atomic_bool pluto_tx_thread_exit = false; static struct iio_context *ctx = NULL; static struct iio_device *tx = NULL; static struct iio_device *phydev = NULL; static struct iio_channel *tx0_i = NULL; static struct iio_channel *tx0_q = NULL; static struct iio_buffer *tx_buffer = NULL; static pthread_t pluto_tx_thread; static const int gui_y_offset = 4; static const int gui_x_offset = 2; static void *pluto_tx_thread_ep(void *arg) { (void) arg; // Not used // Try sticking this thread to core 2 thread_to_core(2); set_thread_name("plutosdr-thread"); int32_t ntx = 0; char *ptx_buffer = (char *) iio_buffer_start(tx_buffer); while (!pluto_tx_thread_exit) { // Get a fifo block struct iq_buf *iq = fifo_dequeue(); if (iq != NULL && iq->data16 != NULL) { // Fifo has transfer block size memcpy(ptx_buffer, iq->data16, iq->validLength * sizeof (signed short)); // Schedule TX buffer, iio_buffer_push will block if there is no room to push to. ntx = iio_buffer_push(tx_buffer); if (ntx < 0) { gui_status_wprintw(RED, "Error pushing TX buffer: %d\n", (int) ntx); break; } // Release and free up used block fifo_release(iq); } else { break; } } if (ctx) { iio_channel_attr_write_bool( iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true) , "powerdown", true); // Turn OFF TX LO } if (tx_buffer) { iio_buffer_destroy(tx_buffer); } if (tx0_i) { iio_channel_disable(tx0_i); } if (tx0_q) { iio_channel_disable(tx0_q); } if (ctx) { iio_context_destroy(ctx); } pthread_exit(NULL); } int sdr_pluto_init(simulator_t *simulator) { char buf[1024]; struct iio_scan_context *scan_ctx; struct iio_context_info **info; int ret; int y = gui_y_offset; unsigned int irates[6]; long long lo_hz = 0; unsigned long xo_correction = 0; // ADLAM-Pluto wants 16 bit signed samples if (simulator->sample_size == SC08) { gui_status_wprintw(YELLOW, "8 bit sample size requested. Reset to 16 bit with ADLAM-Pluto.\n"); } simulator->sample_size = SC16; scan_ctx = iio_create_scan_context(NULL, 0); if (!scan_ctx) { gui_status_wprintw(RED, "Unable to create IIO scan context.\n"); } else { ret = iio_scan_context_get_info_list(scan_ctx, &info); if (ret < 0) { iio_strerror(errno, buf, sizeof (buf)); gui_status_wprintw(RED, "Scanning for IIO contexts failed: %s\n", buf); } if (ret == 0) { gui_status_wprintw(RED, "No IIO context found.\n"); } else { gui_mvwprintw(TRACK, y++, gui_x_offset, "IIO contexts:"); for (int i = 0; i < ret; i++) { gui_mvwprintw(TRACK, y++, gui_x_offset, "%u: %s", i, iio_context_info_get_description(info[i])); gui_mvwprintw(TRACK, y++, gui_x_offset, " %s", iio_context_info_get_uri(info[i])); } } iio_context_info_list_free(info); iio_scan_context_destroy(scan_ctx); } // Create IIO context to access ADALM-Pluto if (simulator->pluto_hostname != NULL) { ctx = iio_create_network_context(simulator->pluto_hostname); } else if (simulator->pluto_uri != NULL) { ctx = iio_create_context_from_uri(simulator->pluto_uri); } else { ctx = iio_create_default_context(); if (ctx == NULL) { ctx = iio_create_network_context("pluto.local"); } } if (ctx == NULL) { iio_strerror(errno, buf, sizeof (buf)); gui_status_wprintw(RED, "Failed creating IIO context: %s\n", buf); return -1; } int device_count = iio_context_get_devices_count(ctx); if (!device_count) { gui_status_wprintw(RED, "No supported PLUTOSDR devices found.\n", buf); return -1; } tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); if (tx == NULL) { iio_strerror(errno, buf, sizeof (buf)); gui_status_wprintw(RED, "Error opening PLUTOSDR TX device: %s\n", buf); return -1; } // Additional IQ kernel buffers, default is 4 iio_device_set_kernel_buffers_count(tx, 8); // Limit user gain to Pluto constrains if (simulator->tx_gain > PLUTO_TX_GAIN_MAX) simulator->tx_gain = PLUTO_TX_GAIN_MAX; if (simulator->tx_gain < PLUTO_TX_GAIN_MIN) simulator->tx_gain = PLUTO_TX_GAIN_MIN; // Change the freq and sample rate to correct the crystal clock error. uint64_t freq_gps_hz = TX_FREQUENCY; freq_gps_hz = freq_gps_hz * (10000000 - simulator->ppb) / 10000000; phydev = iio_context_find_device(ctx, "ad9361-phy"); struct iio_channel* phy_chn = iio_device_find_channel(phydev, "voltage0", true); iio_channel_attr_write(phy_chn, "rf_port_select", "A"); iio_channel_attr_write_longlong(phy_chn, "rf_bandwidth", TX_BW); iio_channel_attr_write_longlong(phy_chn, "sampling_frequency", TX_SAMPLERATE); iio_channel_attr_write_double(phy_chn, "hardwaregain", simulator->tx_gain); iio_channel_attr_write_bool( iio_device_find_channel(phydev, "altvoltage0", true) , "powerdown", true); // Turn OFF RX LO iio_channel_attr_write_longlong( iio_device_find_channel(phydev, "altvoltage1", true) , "frequency", freq_gps_hz); // Set TX LO frequency tx0_i = iio_device_find_channel(tx, "voltage0", true); if (!tx0_i) tx0_i = iio_device_find_channel(tx, "altvoltage0", true); tx0_q = iio_device_find_channel(tx, "voltage1", true); if (!tx0_q) tx0_q = iio_device_find_channel(tx, "altvoltage1", true); iio_channel_enable(tx0_i); iio_channel_enable(tx0_q); ad9361_set_bb_rate(iio_context_find_device(ctx, "ad9361-phy"), TX_SAMPLERATE); // Read back TX path oscillator chain settings ret = iio_device_attr_read(phydev, "tx_path_rates", buf, sizeof (buf)); if (ret > 0) { sscanf(buf, "BBPLL:%u DAC:%u T2:%u T1:%u TF:%u TXSAMP:%u", &irates[0], &irates[1], &irates[2], &irates[3], &irates[4], &irates[5]); } // Read external reference oscillator calibration value ret = iio_device_attr_read(phydev, "xo_correction", buf, sizeof (buf)); if (ret > 0) { sscanf(buf, "%lu", &xo_correction); } // Read back real TX frequency ret = iio_channel_attr_read_longlong(iio_device_find_channel(phydev, "altvoltage1", true), "frequency", &lo_hz); if (ret == 0) { gui_mvwprintw(TRACK, y++, gui_x_offset, "Freq (%llu Hz/%.03f MHz)", lo_hz, ((double) lo_hz / (double) FREQ_ONE_MHZ)); } gui_mvwprintw(TRACK, y++, gui_x_offset, "Baseband filter bandwidth (%d Hz/%.03f MHz)", TX_BW, ((float) TX_BW / (float) FREQ_ONE_MHZ)); gui_mvwprintw(TRACK, y++, gui_x_offset, "Sample rate (%u Hz/%.03f MHz)", irates[5], ((float) irates[5] / (float) FREQ_ONE_MHZ)); gui_mvwprintw(TRACK, y++, gui_x_offset, "TX gain: %idB", simulator->tx_gain); if (simulator->show_verbose) { gui_mvwprintw(TRACK, y++, gui_x_offset, "XO Correction: %lu Hz", xo_correction); gui_mvwprintw(TRACK, y++, gui_x_offset, "TX path rates"); gui_mvwprintw(TRACK, y++, gui_x_offset, " BBPLL: %4.6f", irates[0] / 1e6); gui_mvwprintw(TRACK, y++, gui_x_offset, " DAC: %4.6f", irates[1] / 1e6); gui_mvwprintw(TRACK, y++, gui_x_offset, " T1: %4.6f", irates[3] / 1e6); gui_mvwprintw(TRACK, y++, gui_x_offset, " T2: %4.6f", irates[2] / 1e6); gui_mvwprintw(TRACK, y++, gui_x_offset, " TF: %4.6f", irates[4] / 1e6); } tx_buffer = iio_device_create_buffer(tx, NUM_IQ_SAMPLES, false); if (!tx_buffer) { gui_status_wprintw(RED, "Could not create TX buffer.\n"); return -1; } iio_buffer_set_blocking_mode(tx_buffer, true); if (!fifo_create(NUM_FIFO_BUFFERS, IQ_BUFFER_SIZE, SC16)) { gui_status_wprintw(RED, "Error creating IQ file fifo!\n"); return -1; } return 0; } void sdr_pluto_close(void) { pluto_tx_thread_exit = true; fifo_halt(); fifo_destroy(); pthread_join(pluto_tx_thread, NULL); } int sdr_pluto_run(void) { iio_channel_attr_write_bool( iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true) , "powerdown", false); // Turn ON TX LO fifo_wait_full(); pthread_create(&pluto_tx_thread, NULL, pluto_tx_thread_ep, NULL); return 0; } int sdr_pluto_set_gain(const int gain) { char buf[1024]; double g = gain; int ret; if (g > PLUTO_TX_GAIN_MAX) g = PLUTO_TX_GAIN_MAX; if (g < PLUTO_TX_GAIN_MIN) g = PLUTO_TX_GAIN_MIN; struct iio_channel* phy_chn = iio_device_find_channel(phydev, "voltage0", true); iio_channel_attr_write_double(phy_chn, "hardwaregain", g); // Read back TX gain value ret = iio_channel_attr_read(phy_chn, "hardwaregain", buf, sizeof (buf)); if (ret > 0) { sscanf(buf, "%lf", &g); } return (int) (g); } ================================================ FILE: sdr_pluto.h ================================================ /** * multi-sdr-gps-sim generates a IQ data stream on-the-fly to simulate a * GPS L1 baseband signal using a SDR platform like HackRF or ADLAM-Pluto. * * This file is part of the Github project at * https://github.com/mictronics/multi-sdr-gps-sim.git * * Copyright © 2021 Mictronics * Distributed under the MIT License. * */ /* * TX path oscillator chain * Dividers with multiple of 2 and resulting sample rate. * BBPLL DAC T2 T1 TF TXSAMP [Hz] * 1024000000 32000000 16000000 8000000 4000000 1000000 * 768000000 48000000 24000000 12000000 6000000 1500000 * 1024000000 64000000 32000000 16000000 8000000 2000000 * 1280000000 80000000 40000000 20000000 10000000 2500000 * 768000000 96000000 48000000 24000000 12000000 3000000 * 896000000 112000000 56000000 28000000 14000000 3500000 * 1024000000 128000000 64000000 32000000 16000000 4000000 * 1152000000 144000000 72000000 36000000 18000000 4500000 * 1280000000 160000000 80000000 40000000 20000000 5000000 * 1408000000 176000000 88000000 44000000 22000000 5500000 * 768000000 192000000 96000000 48000000 24000000 6000000 * 832000000 208000000 104000000 52000000 26000000 6500000 * 896000000 224000000 112000000 56000000 28000000 7000000 * 960000000 240000000 120000000 60000000 30000000 7500000 * 1024000000 256000000 128000000 64000000 32000000 8000000 * etc. * Any sample rate being a multiple of 250kHz can be generated with integer dividers. */ #ifndef SDR_PLUTO_H #define SDR_PLUTO_H #define PLUTO_TX_GAIN_MIN -80 #define PLUTO_TX_GAIN_MAX 0 int sdr_pluto_init(simulator_t *simulator); void sdr_pluto_close(void); int sdr_pluto_run(void); int sdr_pluto_set_gain(const int gain); #endif /* SDR_PLUTO_H */