From 40238fe0231cc75b751829f4ce82d37c573dc049 Mon Sep 17 00:00:00 2001 From: ismagom Date: Fri, 14 Feb 2014 15:10:01 +0000 Subject: [PATCH 01/25] Changed dot product to vector product --- include/utils/vector.h | 6 +++--- lib/sync/src/find_sss.c | 12 ++++++------ lib/sync/src/pss.c | 2 +- lib/utils/src/convolution.c | 2 +- lib/utils/src/vector.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/utils/vector.h b/include/utils/vector.h index 475937fab..586c07554 100644 --- a/include/utils/vector.h +++ b/include/utils/vector.h @@ -43,9 +43,9 @@ void vec_sum_ccc(cf_t *z, cf_t *x, cf_t *y, int len); void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len); void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len); -/* dot product */ -void vec_dot_prod_ccc(cf_t *x, cf_t *y, cf_t *z, int len); -void vec_dot_prod_ccc_unalign(cf_t *x, cf_t *y, cf_t *z, int len); +/* vector product */ +void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, int len); +void vec_prod_ccc_unalign(cf_t *x, cf_t *y, cf_t *z, int len); /* conjugate */ void vec_conj_cc(cf_t *x, cf_t *y, int len); diff --git a/lib/sync/src/find_sss.c b/lib/sync/src/find_sss.c index 7b4a08ac3..9747a04a5 100644 --- a/lib/sync/src/find_sss.c +++ b/lib/sync/src/find_sss.c @@ -24,7 +24,7 @@ cf_t corr_sz(cf_t *z, cf_t *s) { cf_t sum; cf_t zsprod[32]; - vec_dot_prod_ccc(z, s, zsprod, N_SSS - 1); + vec_prod_ccc(z, s, zsprod, N_SSS - 1); sum = vec_acc_cc(zsprod, N_SSS - 1); return sum; @@ -66,10 +66,10 @@ void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, y[1][i] = input_fft[SSS_POS_SYMBOL + 2 * i + 1]; } - vec_dot_prod_ccc(y[0], q->fc_tables.c[0], z, N_SSS); + vec_prod_ccc(y[0], q->fc_tables.c[0], z, N_SSS); memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); vec_conj_cc(z, zconj, N_SSS - 1); - vec_dot_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); + vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); corr_all_zs(zprod, q->fc_tables.s, tmp); vec_abs_cf(tmp, tmp_real, N_SSS); @@ -78,11 +78,11 @@ void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, *m0_value = tmp_real[*m0]; } - vec_dot_prod_ccc(y[1], q->fc_tables.c[1], tmp, N_SSS); - vec_dot_prod_ccc(tmp, q->fc_tables.z1[*m0], z, N_SSS); + vec_prod_ccc(y[1], q->fc_tables.c[1], tmp, N_SSS); + vec_prod_ccc(tmp, q->fc_tables.z1[*m0], z, N_SSS); memcpy(zdelay, &z[1], (N_SSS - 1) * sizeof(cf_t)); vec_conj_cc(z, zconj, N_SSS - 1); - vec_dot_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); + vec_prod_ccc(zdelay, zconj, zprod, N_SSS - 1); corr_all_zs(zprod, q->fc_tables.s, tmp); vec_abs_cf(tmp, tmp_real, N_SSS); diff --git a/lib/sync/src/pss.c b/lib/sync/src/pss.c index 8d339f123..f9fff5bc6 100644 --- a/lib/sync/src/pss.c +++ b/lib/sync/src/pss.c @@ -227,7 +227,7 @@ float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv) { cf_t y0, y1, yr; cf_t y[PSS_LEN_FREQ-1]; - vec_dot_prod_ccc_unalign(q->pss_signal_freq, pss_recv, y, PSS_LEN_FREQ - 1); + vec_prod_ccc_unalign(q->pss_signal_freq, pss_recv, y, PSS_LEN_FREQ - 1); y0 = vec_acc_cc(y, (PSS_LEN_FREQ - 1)/2); y1 = vec_acc_cc(&y[(PSS_LEN_FREQ - 1)/2], (PSS_LEN_FREQ - 1)/2); diff --git a/lib/utils/src/convolution.c b/lib/utils/src/convolution.c index b6ff45d08..3a869b3b4 100644 --- a/lib/utils/src/convolution.c +++ b/lib/utils/src/convolution.c @@ -66,7 +66,7 @@ int conv_fft_cc_run(conv_fft_cc_t *state, _Complex float *input, _Complex float dft_run_c2c(&state->input_plan, input, state->input_fft); dft_run_c2c(&state->filter_plan, filter, state->filter_fft); - vec_dot_prod_ccc(state->input_fft,state->filter_fft,state->output_fft,state->output_len); + vec_prod_ccc(state->input_fft,state->filter_fft,state->output_fft,state->output_len); dft_run_c2c(&state->output_plan, state->output_fft, output); diff --git a/lib/utils/src/vector.c b/lib/utils/src/vector.c index a2c49c16f..cc56d06b7 100644 --- a/lib/utils/src/vector.c +++ b/lib/utils/src/vector.c @@ -157,7 +157,7 @@ void vec_conj_cc(cf_t *x, cf_t *y, int len) { #endif } -void vec_dot_prod_ccc(cf_t *x,cf_t *y, cf_t *z, int len) { +void vec_prod_ccc(cf_t *x,cf_t *y, cf_t *z, int len) { #ifndef HAVE_VOLK int i; for (i=0;i Date: Mon, 3 Mar 2014 19:09:26 +0000 Subject: [PATCH 02/25] Reorganized the directory structure. Added Graphics support. Added precoding/layer mapper. MIB detection now working with 1 or 2 tx antennas. Initial eNodeB implementation with PSS/SSS and PBCH generation --- CMakeLists.txt | 210 +- COPYRIGHT | 567 +++ cmake/FindFFTWS.cmake | 22 - cmake/modules/FindFFTW3F.cmake | 55 + cmake/modules/FindQwt.cmake | 141 + cmake/{ => modules}/FindUHD.cmake | 0 cmake/modules/libLTEPackage.cmake | 85 + cmake_uninstall.cmake.in | 26 + cuhd/CMakeLists.txt | 36 + cuhd/include/cuhd.h | 58 + cuhd/include/cuhd/cuhd_utils.h | 30 + cuhd/lib/CMakeLists.txt | 43 + cuhd/lib/cuhd_handler.hpp | 38 + cuhd/lib/cuhd_imp.cpp | 210 + cuhd/lib/cuhd_utils.c | 79 + examples/CMakeLists.txt | 99 +- examples/cmake/Modules/FindFFTWS.cmake | 22 - examples/enodeb_bch.c | 275 ++ examples/equalizer_test.c | 47 +- examples/hl_example.c | 28 + examples/ll_example.c | 28 + examples/mib_scan_usrp.c | 186 +- examples/{cell_search.c => mib_test.c} | 191 +- examples/mib_track.c | 417 ++ examples/pss_scan_usrp.c | 59 +- examples/rssi_scan_usrp.c | 47 +- examples/synch_test.c | 45 +- examples/viterbi_test.c | 315 +- graphics/CMakeLists.txt | 35 + graphics/include/plot.h | 48 + graphics/include/plot/plot_complex.h | 59 + graphics/include/plot/plot_real.h | 55 + graphics/include/plot/plot_scatter.h | 54 + graphics/include/plot/plot_waterfall.h | 61 + graphics/lib/CMakeLists.txt | 86 + graphics/lib/common/Events.cpp | 94 + graphics/lib/common/Events.h | 72 + graphics/lib/common/Lineplot.cpp | 164 + graphics/lib/common/Lineplot.h | 80 + graphics/lib/common/Pointplot.cpp | 121 + graphics/lib/common/Pointplot.h | 76 + graphics/lib/common/Spectrogramplot.cpp | 198 + graphics/lib/common/Spectrogramplot.h | 79 + graphics/lib/common/WaterfallData.h | 92 + graphics/lib/common/plot.cpp | 65 + graphics/lib/complexplot/ComplexWidget.cpp | 197 + graphics/lib/complexplot/ComplexWidget.h | 53 + graphics/lib/complexplot/Complexplot.cpp | 55 + graphics/lib/complexplot/Complexplot.h | 53 + .../lib/complexplot/ComplexplotWrapper.cpp | 141 + graphics/lib/complexplot/ComplexplotWrapper.h | 48 + graphics/lib/complexplot/plot_complex.cpp | 76 + graphics/lib/complexplot/test/CMakeLists.txt | 32 + .../lib/complexplot/test/complexplot_test.cpp | 146 + graphics/lib/realplot/RealWidget.cpp | 102 + graphics/lib/realplot/RealWidget.h | 43 + graphics/lib/realplot/Realplot.cpp | 58 + graphics/lib/realplot/Realplot.h | 46 + graphics/lib/realplot/RealplotWrapper.cpp | 147 + graphics/lib/realplot/RealplotWrapper.h | 49 + graphics/lib/realplot/plot_real.cpp | 79 + graphics/lib/realplot/test/CMakeLists.txt | 32 + graphics/lib/realplot/test/realplot_test.cpp | 130 + graphics/lib/scatterplot/ScatterWidget.cpp | 101 + graphics/lib/scatterplot/ScatterWidget.h | 46 + graphics/lib/scatterplot/Scatterplot.cpp | 53 + graphics/lib/scatterplot/Scatterplot.h | 46 + .../lib/scatterplot/ScatterplotWrapper.cpp | 142 + graphics/lib/scatterplot/ScatterplotWrapper.h | 48 + graphics/lib/scatterplot/plot_scatter.cpp | 75 + graphics/lib/scatterplot/test/CMakeLists.txt | 32 + .../lib/scatterplot/test/scatterplot_test.cpp | 133 + .../lib/waterfallplot/WaterfallWidget.cpp | 143 + graphics/lib/waterfallplot/WaterfallWidget.h | 52 + graphics/lib/waterfallplot/Waterfallplot.cpp | 80 + graphics/lib/waterfallplot/Waterfallplot.h | 49 + .../waterfallplot/WaterfallplotWrapper.cpp | 187 + .../lib/waterfallplot/WaterfallplotWrapper.h | 57 + graphics/lib/waterfallplot/plot_waterfall.cpp | 102 + .../lib/waterfallplot/test/CMakeLists.txt | 32 + .../waterfallplot/test/Waterfallplot_test.cpp | 128 + include/channel/ch_awgn.h | 44 - include/fec/crc.h | 26 - include/io/filesink.h | 55 - include/io/format.h | 7 - include/lte.h | 73 - include/lte/sequence.h | 37 - include/modem/mod.h | 49 - include/phch/pbch.h | 79 - include/resampling/interp.h | 24 - include/sync/sfo.h | 25 - include/utils/bit.h | 31 - include/utils/debug.h | 37 - include/utils/mux.h | 30 - include/utils/pack.h | 25 - lib/CMakeLists.txt | 28 - lib/channel/src/ch_awgn.c | 52 - lib/channel/src/gauss.c | 37 - lib/channel/src/gauss.h | 19 - lib/fec/src/viterbi.c | 144 - lib/fec/src/viterbi37.h | 7 - lib/lte/src/phch_sequence.c | 24 - lib/modem/src/hard_demod_lte.h | 31 - lib/modem/src/lte_tables.h | 42 - lib/modem/src/soft_algs.h | 23 - lib/phch/src/pbch.c | 370 -- lib/sync/src/cp.c | 5 - lib/utils/src/debug.c | 13 - lte/CMakeLists.txt | 35 + lte/include/lte.h | 88 + .../include/lte}/ch_estimation/chest.h | 37 +- .../include/lte}/ch_estimation/refsignal.h | 37 +- lte/include/lte/channel/ch_awgn.h | 56 + .../lte => lte/include/lte/common}/base.h | 47 +- {include/lte => lte/include/lte/common}/fft.h | 34 +- lte/include/lte/common/sequence.h | 47 + {include => lte/include/lte}/fec/convcoder.h | 31 +- lte/include/lte/fec/crc.h | 37 + {include => lte/include/lte}/fec/viterbi.h | 37 +- .../include/lte}/filter/filter2d.h | 31 +- {include => lte/include/lte}/io/binsource.h | 30 +- lte/include/lte/io/filesink.h | 65 + {include => lte/include/lte}/io/filesource.h | 39 +- lte/include/lte/io/format.h | 34 + lte/include/lte/io/udpsink.h | 70 + lte/include/lte/io/udpsource.h | 73 + lte/include/lte/mimo/layermap.h | 44 + lte/include/lte/mimo/precoding.h | 50 + .../include/lte}/modem/demod_hard.h | 36 +- .../include/lte}/modem/demod_soft.h | 34 +- lte/include/lte/modem/mod.h | 59 + .../include/lte}/modem/modem_table.h | 36 +- lte/include/lte/phch/pbch.h | 100 + .../include/lte}/ratematching/rm_conv.h | 33 +- lte/include/lte/resampling/interp.h | 34 + .../include/lte}/scrambling/scrambling.h | 38 +- {include => lte/include/lte}/sync/pss.h | 39 +- lte/include/lte/sync/sfo.h | 35 + {include => lte/include/lte}/sync/sss.h | 37 +- {include => lte/include/lte}/sync/sync.h | 32 +- lte/include/lte/utils/bit.h | 41 + .../include/lte}/utils/convolution.h | 32 +- lte/include/lte/utils/debug.h | 64 + {include => lte/include/lte}/utils/dft.h | 30 +- {include => lte/include/lte}/utils/matrix.h | 30 +- lte/include/lte/utils/mux.h | 40 + {include => lte/include/lte}/utils/nco.h | 30 +- lte/include/lte/utils/pack.h | 35 + {include => lte/include/lte}/utils/vector.h | 39 +- lte/lib/CMakeLists.txt | 56 + {lib => lte/lib}/ch_estimation/src/chest.c | 66 +- .../lib}/ch_estimation/src/refsignal.c | 57 +- lte/lib/channel/src/ch_awgn.c | 69 + lte/lib/channel/src/gauss.c | 44 + lte/lib/channel/src/gauss.h | 29 + {lib/lte => lte/lib/common}/src/fft.c | 68 +- {lib/lte => lte/lib/common}/src/lte.c | 33 +- lte/lib/common/src/phch_sequence.c | 34 + {lib/lte => lte/lib/common}/src/sequence.c | 32 +- {lib => lte/lib}/fec/src/convcoder.c | 35 +- {lib => lte/lib}/fec/src/crc.c | 32 +- {lib => lte/lib}/fec/src/parity.h | 2 +- lte/lib/fec/src/viterbi.c | 233 + lte/lib/fec/src/viterbi37.h | 34 + {lib => lte/lib}/fec/src/viterbi37_port.c | 99 +- lte/lib/fec/src/viterbi39.h | 36 + lte/lib/fec/src/viterbi39_port.c | 172 + {lib => lte/lib}/filter/src/filter2d.c | 40 +- {lib => lte/lib}/io/src/binsource.c | 34 +- {lib => lte/lib}/io/src/filesink.c | 35 +- {lib => lte/lib}/io/src/filesource.c | 39 +- lte/lib/io/src/udpsink.c | 102 + lte/lib/io/src/udpsource.c | 100 + lte/lib/mimo/src/layermap.c | 78 + lte/lib/mimo/src/precoding.c | 86 + {lib => lte/lib}/modem/src/demod_hard.c | 34 +- {lib => lte/lib}/modem/src/demod_soft.c | 36 +- {lib => lte/lib}/modem/src/hard_demod_lte.c | 40 +- lte/lib/modem/src/hard_demod_lte.h | 41 + {lib => lte/lib}/modem/src/lte_tables.c | 40 +- lte/lib/modem/src/lte_tables.h | 51 + {lib => lte/lib}/modem/src/mod.c | 38 +- {lib => lte/lib}/modem/src/modem_table.c | 38 +- {lib => lte/lib}/modem/src/soft_algs.c | 30 +- lte/lib/modem/src/soft_algs.h | 33 + lte/lib/phch/src/pbch.c | 535 +++ .../src/common.c => lte/lib/phch/src/phch.c | 34 +- .../src/common.h => lte/lib/phch/src/phch.h | 30 +- {lib => lte/lib}/ratematching/src/rm_conv.c | 86 +- {lib => lte/lib}/resampling/src/interp.c | 36 +- {lib => lte/lib}/scrambling/src/scrambling.c | 49 +- lte/lib/sync/src/cp.c | 32 + {lib => lte/lib}/sync/src/find_sss.c | 34 +- {lib => lte/lib}/sync/src/gen_sss.c | 42 +- {lib => lte/lib}/sync/src/pss.c | 105 +- {lib => lte/lib}/sync/src/sfo.c | 32 +- {lib => lte/lib}/sync/src/sss.c | 73 +- {lib => lte/lib}/sync/src/sync.c | 48 +- {lib => lte/lib}/utils/src/bit.c | 31 +- {lib => lte/lib}/utils/src/convolution.c | 36 +- lte/lib/utils/src/debug.c | 40 + {lib => lte/lib}/utils/src/dft.c | 33 +- {lib => lte/lib}/utils/src/matrix.c | 32 +- {lib => lte/lib}/utils/src/mux.c | 30 +- {lib => lte/lib}/utils/src/nco.c | 32 +- {lib => lte/lib}/utils/src/pack.c | 30 +- {lib => lte/lib}/utils/src/vector.c | 62 +- matlab/chest/get_ce.m | 230 - matlab/chest/lte_generate_crs.m | 15 - matlab/chest/lte_generate_prs_c.m | 23 - matlab/chest/samps_to_symbs.m | 26 - scripts/binsource.h | 36 - scripts/lib_binsource/CMakeLists.txt | 94 - scripts/lib_binsource/src/binsource.c | 81 - scripts/lib_binsource/src/binsource.h | 33 - scripts/lib_binsource/test/test_generate.c | 73 - scripts/mod2aloe.py | 22 + scripts/mod2xml.py | 22 + scripts/module.py | 20 + scripts/module/__init__.py | 20 + scripts/pyclibrary/CLibrary.py | 501 --- scripts/pyclibrary/CParser.py | 1274 ------ scripts/pyclibrary/README.md | 8 - scripts/pyclibrary/__init__.py | 3 - scripts/pyclibrary/license.txt | 7 - scripts/pyclibrary/pyparsing.py | 3754 ----------------- scripts/xml2aloe/__init__.py | 23 + scripts/xml2aloe/template/src/template.c | 27 + scripts/xml2aloe/template/src/template.h | 27 + .../xml2aloe/template/test/test_generate.c | 36 +- uhd/uhd.h | 25 - uhd/uhd_handler.hpp | 9 - uhd/uhd_imp.cpp | 138 - uhd/uhd_utils.c | 51 - uhd/uhd_utils.h | 3 - 235 files changed, 11964 insertions(+), 9009 deletions(-) create mode 100644 COPYRIGHT delete mode 100644 cmake/FindFFTWS.cmake create mode 100644 cmake/modules/FindFFTW3F.cmake create mode 100644 cmake/modules/FindQwt.cmake rename cmake/{ => modules}/FindUHD.cmake (100%) create mode 100644 cmake/modules/libLTEPackage.cmake create mode 100644 cmake_uninstall.cmake.in create mode 100644 cuhd/CMakeLists.txt create mode 100644 cuhd/include/cuhd.h create mode 100644 cuhd/include/cuhd/cuhd_utils.h create mode 100644 cuhd/lib/CMakeLists.txt create mode 100644 cuhd/lib/cuhd_handler.hpp create mode 100644 cuhd/lib/cuhd_imp.cpp create mode 100644 cuhd/lib/cuhd_utils.c delete mode 100644 examples/cmake/Modules/FindFFTWS.cmake create mode 100644 examples/enodeb_bch.c rename examples/{cell_search.c => mib_test.c} (51%) create mode 100644 examples/mib_track.c create mode 100644 graphics/CMakeLists.txt create mode 100644 graphics/include/plot.h create mode 100644 graphics/include/plot/plot_complex.h create mode 100644 graphics/include/plot/plot_real.h create mode 100644 graphics/include/plot/plot_scatter.h create mode 100644 graphics/include/plot/plot_waterfall.h create mode 100644 graphics/lib/CMakeLists.txt create mode 100644 graphics/lib/common/Events.cpp create mode 100644 graphics/lib/common/Events.h create mode 100644 graphics/lib/common/Lineplot.cpp create mode 100644 graphics/lib/common/Lineplot.h create mode 100644 graphics/lib/common/Pointplot.cpp create mode 100644 graphics/lib/common/Pointplot.h create mode 100644 graphics/lib/common/Spectrogramplot.cpp create mode 100644 graphics/lib/common/Spectrogramplot.h create mode 100644 graphics/lib/common/WaterfallData.h create mode 100644 graphics/lib/common/plot.cpp create mode 100644 graphics/lib/complexplot/ComplexWidget.cpp create mode 100644 graphics/lib/complexplot/ComplexWidget.h create mode 100644 graphics/lib/complexplot/Complexplot.cpp create mode 100644 graphics/lib/complexplot/Complexplot.h create mode 100644 graphics/lib/complexplot/ComplexplotWrapper.cpp create mode 100644 graphics/lib/complexplot/ComplexplotWrapper.h create mode 100644 graphics/lib/complexplot/plot_complex.cpp create mode 100644 graphics/lib/complexplot/test/CMakeLists.txt create mode 100644 graphics/lib/complexplot/test/complexplot_test.cpp create mode 100644 graphics/lib/realplot/RealWidget.cpp create mode 100644 graphics/lib/realplot/RealWidget.h create mode 100644 graphics/lib/realplot/Realplot.cpp create mode 100644 graphics/lib/realplot/Realplot.h create mode 100644 graphics/lib/realplot/RealplotWrapper.cpp create mode 100644 graphics/lib/realplot/RealplotWrapper.h create mode 100644 graphics/lib/realplot/plot_real.cpp create mode 100644 graphics/lib/realplot/test/CMakeLists.txt create mode 100644 graphics/lib/realplot/test/realplot_test.cpp create mode 100644 graphics/lib/scatterplot/ScatterWidget.cpp create mode 100644 graphics/lib/scatterplot/ScatterWidget.h create mode 100644 graphics/lib/scatterplot/Scatterplot.cpp create mode 100644 graphics/lib/scatterplot/Scatterplot.h create mode 100644 graphics/lib/scatterplot/ScatterplotWrapper.cpp create mode 100644 graphics/lib/scatterplot/ScatterplotWrapper.h create mode 100644 graphics/lib/scatterplot/plot_scatter.cpp create mode 100644 graphics/lib/scatterplot/test/CMakeLists.txt create mode 100644 graphics/lib/scatterplot/test/scatterplot_test.cpp create mode 100644 graphics/lib/waterfallplot/WaterfallWidget.cpp create mode 100644 graphics/lib/waterfallplot/WaterfallWidget.h create mode 100644 graphics/lib/waterfallplot/Waterfallplot.cpp create mode 100644 graphics/lib/waterfallplot/Waterfallplot.h create mode 100644 graphics/lib/waterfallplot/WaterfallplotWrapper.cpp create mode 100644 graphics/lib/waterfallplot/WaterfallplotWrapper.h create mode 100644 graphics/lib/waterfallplot/plot_waterfall.cpp create mode 100644 graphics/lib/waterfallplot/test/CMakeLists.txt create mode 100644 graphics/lib/waterfallplot/test/Waterfallplot_test.cpp delete mode 100644 include/channel/ch_awgn.h delete mode 100644 include/fec/crc.h delete mode 100644 include/io/filesink.h delete mode 100644 include/io/format.h delete mode 100644 include/lte.h delete mode 100644 include/lte/sequence.h delete mode 100644 include/modem/mod.h delete mode 100644 include/phch/pbch.h delete mode 100644 include/resampling/interp.h delete mode 100644 include/sync/sfo.h delete mode 100644 include/utils/bit.h delete mode 100644 include/utils/debug.h delete mode 100644 include/utils/mux.h delete mode 100644 include/utils/pack.h delete mode 100644 lib/CMakeLists.txt delete mode 100644 lib/channel/src/ch_awgn.c delete mode 100644 lib/channel/src/gauss.c delete mode 100644 lib/channel/src/gauss.h delete mode 100644 lib/fec/src/viterbi.c delete mode 100644 lib/fec/src/viterbi37.h delete mode 100644 lib/lte/src/phch_sequence.c delete mode 100644 lib/modem/src/hard_demod_lte.h delete mode 100644 lib/modem/src/lte_tables.h delete mode 100644 lib/modem/src/soft_algs.h delete mode 100644 lib/phch/src/pbch.c delete mode 100644 lib/sync/src/cp.c delete mode 100644 lib/utils/src/debug.c create mode 100644 lte/CMakeLists.txt create mode 100644 lte/include/lte.h rename {include => lte/include/lte}/ch_estimation/chest.h (76%) rename {include => lte/include/lte}/ch_estimation/refsignal.h (57%) create mode 100644 lte/include/lte/channel/ch_awgn.h rename {include/lte => lte/include/lte/common}/base.h (68%) rename {include/lte => lte/include/lte/common}/fft.h (57%) create mode 100644 lte/include/lte/common/sequence.h rename {include => lte/include/lte}/fec/convcoder.h (58%) create mode 100644 lte/include/lte/fec/crc.h rename {include => lte/include/lte}/fec/viterbi.h (54%) rename {include => lte/include/lte}/filter/filter2d.h (60%) rename {include => lte/include/lte}/io/binsource.h (67%) create mode 100644 lte/include/lte/io/filesink.h rename {include => lte/include/lte}/io/filesource.h (51%) create mode 100644 lte/include/lte/io/format.h create mode 100644 lte/include/lte/io/udpsink.h create mode 100644 lte/include/lte/io/udpsource.h create mode 100644 lte/include/lte/mimo/layermap.h create mode 100644 lte/include/lte/mimo/precoding.h rename {include => lte/include/lte}/modem/demod_hard.h (54%) rename {include => lte/include/lte}/modem/demod_soft.h (64%) create mode 100644 lte/include/lte/modem/mod.h rename {include => lte/include/lte}/modem/modem_table.h (52%) create mode 100644 lte/include/lte/phch/pbch.h rename {include => lte/include/lte}/ratematching/rm_conv.h (51%) create mode 100644 lte/include/lte/resampling/interp.h rename {include => lte/include/lte}/scrambling/scrambling.h (55%) rename {include => lte/include/lte}/sync/pss.h (75%) create mode 100644 lte/include/lte/sync/sfo.h rename {include => lte/include/lte}/sync/sss.h (73%) rename {include => lte/include/lte}/sync/sync.h (61%) create mode 100644 lte/include/lte/utils/bit.h rename {include => lte/include/lte}/utils/convolution.h (58%) create mode 100644 lte/include/lte/utils/debug.h rename {include => lte/include/lte}/utils/dft.h (77%) rename {include => lte/include/lte}/utils/matrix.h (59%) create mode 100644 lte/include/lte/utils/mux.h rename {include => lte/include/lte}/utils/nco.h (57%) create mode 100644 lte/include/lte/utils/pack.h rename {include => lte/include/lte}/utils/vector.h (58%) create mode 100644 lte/lib/CMakeLists.txt rename {lib => lte/lib}/ch_estimation/src/chest.c (83%) rename {lib => lte/lib}/ch_estimation/src/refsignal.c (72%) create mode 100644 lte/lib/channel/src/ch_awgn.c create mode 100644 lte/lib/channel/src/gauss.c create mode 100644 lte/lib/channel/src/gauss.h rename {lib/lte => lte/lib/common}/src/fft.c (57%) rename {lib/lte => lte/lib/common}/src/lte.c (86%) create mode 100644 lte/lib/common/src/phch_sequence.c rename {lib/lte => lte/lib/common}/src/sequence.c (69%) rename {lib => lte/lib}/fec/src/convcoder.c (65%) rename {lib => lte/lib}/fec/src/crc.c (66%) rename {lib => lte/lib}/fec/src/parity.h (95%) create mode 100644 lte/lib/fec/src/viterbi.c create mode 100644 lte/lib/fec/src/viterbi37.h rename {lib => lte/lib}/fec/src/viterbi37_port.c (75%) create mode 100644 lte/lib/fec/src/viterbi39.h create mode 100644 lte/lib/fec/src/viterbi39_port.c rename {lib => lte/lib}/filter/src/filter2d.c (78%) rename {lib => lte/lib}/io/src/binsource.c (81%) rename {lib => lte/lib}/io/src/filesink.c (62%) rename {lib => lte/lib}/io/src/filesource.c (62%) create mode 100644 lte/lib/io/src/udpsink.c create mode 100644 lte/lib/io/src/udpsource.c create mode 100644 lte/lib/mimo/src/layermap.c create mode 100644 lte/lib/mimo/src/precoding.c rename {lib => lte/lib}/modem/src/demod_hard.c (61%) rename {lib => lte/lib}/modem/src/demod_soft.c (67%) rename {lib => lte/lib}/modem/src/hard_demod_lte.c (80%) create mode 100644 lte/lib/modem/src/hard_demod_lte.h rename {lib => lte/lib}/modem/src/lte_tables.c (89%) create mode 100644 lte/lib/modem/src/lte_tables.h rename {lib => lte/lib}/modem/src/mod.c (51%) rename {lib => lte/lib}/modem/src/modem_table.c (67%) rename {lib => lte/lib}/modem/src/soft_algs.c (87%) create mode 100644 lte/lib/modem/src/soft_algs.h create mode 100644 lte/lib/phch/src/pbch.c rename lib/phch/src/common.c => lte/lib/phch/src/phch.c (69%) rename lib/phch/src/common.h => lte/lib/phch/src/phch.h (50%) rename {lib => lte/lib}/ratematching/src/rm_conv.c (57%) rename {lib => lte/lib}/resampling/src/interp.c (66%) rename {lib => lte/lib}/scrambling/src/scrambling.c (67%) create mode 100644 lte/lib/sync/src/cp.c rename {lib => lte/lib}/sync/src/find_sss.c (79%) rename {lib => lte/lib}/sync/src/gen_sss.c (77%) rename {lib => lte/lib}/sync/src/pss.c (80%) rename {lib => lte/lib}/sync/src/sfo.c (57%) rename {lib => lte/lib}/sync/src/sss.c (71%) rename {lib => lte/lib}/sync/src/sync.c (76%) rename {lib => lte/lib}/utils/src/bit.c (61%) rename {lib => lte/lib}/utils/src/convolution.c (74%) create mode 100644 lte/lib/utils/src/debug.c rename {lib => lte/lib}/utils/src/dft.c (89%) rename {lib => lte/lib}/utils/src/matrix.c (75%) rename {lib => lte/lib}/utils/src/mux.c (71%) rename {lib => lte/lib}/utils/src/nco.c (76%) rename {lib => lte/lib}/utils/src/pack.c (50%) rename {lib => lte/lib}/utils/src/vector.c (74%) delete mode 100644 matlab/chest/get_ce.m delete mode 100644 matlab/chest/lte_generate_crs.m delete mode 100644 matlab/chest/lte_generate_prs_c.m delete mode 100644 matlab/chest/samps_to_symbs.m delete mode 100644 scripts/binsource.h delete mode 100644 scripts/lib_binsource/CMakeLists.txt delete mode 100644 scripts/lib_binsource/src/binsource.c delete mode 100644 scripts/lib_binsource/src/binsource.h delete mode 100644 scripts/lib_binsource/test/test_generate.c delete mode 100644 scripts/pyclibrary/CLibrary.py delete mode 100644 scripts/pyclibrary/CParser.py delete mode 100644 scripts/pyclibrary/README.md delete mode 100644 scripts/pyclibrary/__init__.py delete mode 100644 scripts/pyclibrary/license.txt delete mode 100644 scripts/pyclibrary/pyparsing.py delete mode 100644 uhd/uhd.h delete mode 100644 uhd/uhd_handler.hpp delete mode 100644 uhd/uhd_imp.cpp delete mode 100644 uhd/uhd_utils.c delete mode 100644 uhd/uhd_utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c677c9f9e..411885780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,42 +1,196 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + + +######################################################################## +# Prevent in-tree builds +######################################################################## + if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "Prevented in-tree build. This is bad practice.") endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) -cmake_minimum_required (VERSION 2.6) -project (osldlib) -# The version number. -set (OSLDLIB_VERSION_MAJOR 0) -set (OSLDLIB_VERSION_MINOR 0) +######################################################################## +# Project setup +######################################################################## +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) +PROJECT (LIBLTE) + +LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") + +INCLUDE(libLTEPackage) #setup cpack + +######################################################################## +# Install Dirs +######################################################################## +SET(RUNTIME_DIR bin) +SET(LIBRARY_DIR lib) +SET(INCLUDE_DIR include) +SET(DOC_DIR "share/doc/${CPACK_PACKAGE_NAME}") +SET(DATA_DIR share/${CPACK_PACKAGE_NAME}) + + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release") + MESSAGE(STATUS "Build type not specified: defaulting to release.") +ENDIF(NOT CMAKE_BUILD_TYPE) +SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") + +######################################################################## +# Compiler specific setup +######################################################################## +IF(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-format-extra-args -Wno-reorder -Winline -Wno-unused-result -Wno-format -D_GNU_SOURCE") +ENDIF(CMAKE_COMPILER_IS_GNUCXX) + +IF(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE") +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +IF(MSVC) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/msvc) #missing headers + ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) #minimum version required is windows xp + ADD_DEFINITIONS(-DNOMINMAX) #disables stupidity and enables std::min and std::max + ADD_DEFINITIONS( #stop all kinds of compatibility warnings + -D_SCL_SECURE_NO_WARNINGS + -D_CRT_SECURE_NO_WARNINGS + -D_CRT_SECURE_NO_DEPRECATE + -D_CRT_NONSTDC_NO_DEPRECATE + ) + LIST(APPEND IRIS_CORE_COMMON_FLAGS_AND_DEFINES -DHAVE_CONFIG_H) + ADD_DEFINITIONS(/MP) #build with multiple processors +ENDIF(MSVC) + +IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # The following is needed for weak linking to work under OS X + SET(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") +ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + + +######################################################################## +# Setup Boost +######################################################################## +MESSAGE(STATUS "") +MESSAGE(STATUS "Configuring Boost C++ Libraries...") +SET(BOOST_REQUIRED_COMPONENTS + program_options + system + thread + unit_test_framework +) + +IF(UNIX AND EXISTS "/usr/lib64") + LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix +ENDIF(UNIX AND EXISTS "/usr/lib64") + +IF(MSVC) + SET(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") + IF(BOOST_ALL_DYN_LINK) + ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc + ELSE(BOOST_ALL_DYN_LINK) + UNSET(BOOST_REQUIRED_COMPONENTS) #empty components list for static link + ENDIF(BOOST_ALL_DYN_LINK) +ENDIF(MSVC) + +SET(Boost_ADDITIONAL_VERSIONS + "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" "1.40.0" "1.40" + "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" + "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" "1.45.0" "1.45" + "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" + "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53") +FIND_PACKAGE(Boost 1.37 REQUIRED ${BOOST_REQUIRED_COMPONENTS}) +MESSAGE(STATUS "Boost version: ${Boost_VERSION}") + +IF(Boost_VERSION LESS 104600) + ADD_DEFINITIONS( -DBOOST_FILESYSTEM_VERSION=2 ) #use filesystem version 2 in boost < 1.46 + MESSAGE(STATUS "Using Boost Filesystem V2") +ENDIF(Boost_VERSION LESS 104600) + +INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) +LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) + +MESSAGE(STATUS "Boost include directories: ${Boost_INCLUDE_DIRS}") +MESSAGE(STATUS "Boost library directories: ${Boost_LIBRARY_DIRS}") +MESSAGE(STATUS "Boost libraries: ${Boost_LIBRARIES}") + + +######################################################################## +# Create uninstall targets +######################################################################## +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +ADD_CUSTOM_TARGET(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + + +######################################################################## +# Macro to add -fPIC property to static libs +######################################################################## +MACRO(LIBLTE_SET_PIC) + IF( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" ) + SET_TARGET_PROPERTIES(${ARGV} PROPERTIES COMPILE_FLAGS -fPIC) + ENDIF( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" ) +ENDMACRO(LIBLTE_SET_PIC) + +######################################################################## +# A macro for passing lists between different directories +# through an internal cache variable. +######################################################################## +MACRO (APPEND_INTERNAL_LIST LIST_NAME VALUE) + # If the list in not in the cache, create it. + IF (${LIST_NAME}) + SET (${LIST_NAME} "${${LIST_NAME}};${VALUE}" CACHE INTERNAL "Internal +variable") + ELSE (${LIST_NAME}) + SET (${LIST_NAME} "${VALUE}" CACHE INTERNAL "Internal variable") + ENDIF (${LIST_NAME}) -set(CPACK_PACKAGE_VERSION_MAJOR ${OSLDLIB_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${OSLDLIB_VERSION_MINOR}) -set(CPACK_PACKAGE_VERSION_PATCH "1") -set(CPACK_SOURCE_GENERATOR "TBZ2") -set(CPACK_SOURCE_PACKAGE_FILE_NAME - "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") -set(CPACK_SOURCE_IGNORE_FILES - "${CMAKE_CURRENT_BINARY_DIR};/.bzr/;~$;${CPACK_SOURCE_IGNORE_FILES}") -include(CPack) -add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) +ENDMACRO (APPEND_INTERNAL_LIST) -option(DEBUG "Compiles with debugging symbols and no optimizations" OFF) -if(DEBUG) - message("-- Configuring debugging CFLAGS") - set(CFDEB "-O0 -g -rdynamic") -else() - set(CFDEB "-O2") -endif() +######################################################################## +# Print summary +######################################################################## +MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") +MESSAGE(STATUS "Building for version: ${VERSION}") -set(CMAKE_C_FLAGS "${CFDEB} -Wall -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE") -set(CMAKE_BINARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +######################################################################## +# Add general includes and dependencies +######################################################################## +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lte/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cuhd/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/graphics/include) -### INCLUDES -include_directories("{CMAKE_CURRENT_SOURCE_DIR}/include") +######################################################################## +# Add the subdirectories +######################################################################## -add_subdirectory(examples) -add_subdirectory(lib) +ADD_SUBDIRECTORY(lte) +ADD_SUBDIRECTORY(cuhd) +ADD_SUBDIRECTORY(graphics) +ADD_SUBDIRECTORY(examples) diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 000000000..de839a6ac --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,567 @@ +Copyright (C) 2013-2014 Ismael Gomez Miguelez, . All rights reserved. + +The following copyright notices are for libraries used within Iris_Modules: + +----------------------------------------------------------- +Boost Software License - Version 1.0 - August 17th, 2003 +----------------------------------------------------------- + +Permission is hereby granted, free of charge, to any person or +organization obtaining a copy of the software and accompanying +documentation covered by this license (the "Software") to use, reproduce, +display, distribute, execute, and transmit the Software, and to prepare +derivative works of the Software, and to permit third-parties to whom +the Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated +by a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE +BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------------------------------------------------------- +CLibrary.py +----------------------------------------------------------- + +Copyright (c) 2003-2011 Paul T. McGuire + +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. + +----------------------------------------------------------- +FEC Library - Version 3.0.1 - August 7th, 2007 +----------------------------------------------------------- + +COPYRIGHT + +This package is copyright 2006 by Phil Karn, KA9Q. It may be used +under the terms of the GNU Lesser General Public License (LGPL). + + +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/cmake/FindFFTWS.cmake b/cmake/FindFFTWS.cmake deleted file mode 100644 index ff28c85a7..000000000 --- a/cmake/FindFFTWS.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# - Find FFTW -# Find the native FFTW includes and library -# -# FFTW_INCLUDES - where to find fftw3.h -# FFTW_LIBRARIES - List of libraries when using FFTW. -# FFTW_FOUND - True if FFTW found. - -if (FFTWS_INCLUDES) - # Already in cache, be silent - set (FFTWS_FIND_QUIETLY TRUE) -endif (FFTWS_INCLUDES) - -find_path (FFTWS_INCLUDES fftw3.h) -SET(CMAKE_FIND_LIBRARY_SUFFIXES .a) -find_library (FFTWfS_LIBRARIES NAMES fftw3f) -find_library (FFTWnS_LIBRARIES NAMES fftw3) -set(FFTWS_LIBRARIES ${FFTWfS_LIBRARIES} ${FFTWnS_LIBRARIES}) - -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (FFTWS DEFAULT_MSG FFTWS_LIBRARIES FFTWS_INCLUDES) - -mark_as_advanced (FFTWS_LIBRARIES FFTWS_INCLUDES) diff --git a/cmake/modules/FindFFTW3F.cmake b/cmake/modules/FindFFTW3F.cmake new file mode 100644 index 000000000..7487eac8c --- /dev/null +++ b/cmake/modules/FindFFTW3F.cmake @@ -0,0 +1,55 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +# - Try to find fftw3f - the single-precision version of FFTW3 +# Once done this will define +# FFTW3F_FOUND - System has fftw3f +# FFTW3F_INCLUDE_DIRS - The fftw3f include directories +# FFTW3F_LIBRARIES - The libraries needed to use fftw3f +# FFTW3F_DEFINITIONS - Compiler switches required for using fftw3f + +find_package(PkgConfig) +pkg_check_modules(PC_FFTW3F "fftw3f >= 3.0") +set(FFTW3F_DEFINITIONS ${PC_FFTW3F_CFLAGS_OTHER}) + +find_path(FFTW3F_INCLUDE_DIR + NAMES fftw3.h + HINTS ${PC_FFTW3F_INCLUDEDIR} ${PC_FFTW3F_INCLUDE_DIRS} $ENV{FFTW3_DIR}/include + PATHS /usr/local/include + /usr/include ) + +find_library(FFTW3F_LIBRARY + NAMES fftw3f libfftw3f libfftw3f-3 + HINTS ${PC_FFTW3F_LIBDIR} ${PC_FFTW3F_LIBRARY_DIRS} $ENV{FFTW3_DIR}/lib + PATHS /usr/local/lib + /usr/lib) + +set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY} ) +set(FFTW3F_INCLUDE_DIRS ${FFTW3F_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set FFTW3F_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(fftw3f DEFAULT_MSG + FFTW3F_LIBRARY FFTW3F_INCLUDE_DIR) + +mark_as_advanced(FFTW3F_INCLUDE_DIR FFTW3F_LIBRARY ) diff --git a/cmake/modules/FindQwt.cmake b/cmake/modules/FindQwt.cmake new file mode 100644 index 000000000..e52243f38 --- /dev/null +++ b/cmake/modules/FindQwt.cmake @@ -0,0 +1,141 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +# - Try to find the Qwt includes and library +# - Defines the following: +# +# QWT_FOUND - system has Qwt +# QWT_INCLUDE_DIR - where to find qwt.h +# QWT_INCLUDE_DIRS - the qwt include directories +# QWT_LIBRARY - where to find the Qwt library (not for general use) +# QWT_LIBRARIES - the libraries to link against to use Qwt +# QWT_MAJOR_VERSION - major version +# QWT_MINOR_VERSION - minor version +# QWT_PATCH_VERSION - patch version +# QWT_VERSION_STRING - version (ex. 5.2.1) + +SET(QWT_FOUND "NO") + +FIND_PATH(QWT_INCLUDE_DIR qwt.h + /usr/local/qwt/include + /usr/local/include + /usr/include/qwt + /usr/include/qwt-qt4 + /usr/include/qwt5 + /usr/include + /opt/local/include/qwt #macports path + $ENV{QWT_DIR}/include + $ENV{QWT_DIR}/src + $ENV{QWTDIR}/include + $ENV{QWTDIR}/src + $ENV{QWT_ROOT}/include + $ENV{QWT_ROOT}/src + $ENV{QWTROOT}/include + $ENV{QWTROOT}/src +) + +SET(QWT_INCLUDE_DIRS ${QWT_INCLUDE_DIR}) + +# version +SET(_VERSION_FILE ${QWT_INCLUDE_DIR}/qwt_global.h) +IF(EXISTS ${_VERSION_FILE} ) + FILE( STRINGS ${_VERSION_FILE} _VERSION_LINE REGEX "define[ ]+QWT_VERSION_STR") + IF( _VERSION_LINE ) + STRING( REGEX REPLACE ".*define[ ]+QWT_VERSION_STR[ ]+\"(.*)\".*" "\\1" QWT_VERSION_STRING "${_VERSION_LINE}" ) + STRING( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" QWT_MAJOR_VERSION "${QWT_VERSION_STRING}" ) + STRING( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" QWT_MINOR_VERSION "${QWT_VERSION_STRING}" ) + STRING( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\3" QWT_PATCH_VERSION "${QWT_VERSION_STRING}" ) + ENDIF() +ENDIF() + + +# check version +SET( _QWT_VERSION_MATCH TRUE ) +IF( Qwt_FIND_VERSION AND QWT_VERSION_STRING ) + IF( Qwt_FIND_VERSION_EXACT ) + IF( NOT Qwt_FIND_VERSION VERSION_EQUAL QWT_VERSION_STRING ) + SET( _QWT_VERSION_MATCH FALSE ) + ENDIF() + ELSE() + IF( QWT_VERSION_STRING VERSION_LESS Qwt_FIND_VERSION ) + SET( _QWT_VERSION_MATCH FALSE ) + ENDIF() + ENDIF() +ENDIF() + +SET(POTENTIAL_LIBRARY_PATHS /usr/local/qwt/lib /usr/local/lib /usr/lib /opt/local/lib + $ENV{QWT_DIR}/lib $ENV{QWTDIR}/lib $ENV{QWT_ROOT}/lib $ENV{QWTROOT}/lib) + +SET(QWT_NAMES ${QWT_NAMES} qwt qwt-qt4 qwt5 ) +FIND_LIBRARY(QWT_LIBRARY + NAMES ${QWT_NAMES} + PATHS ${POTENTIAL_LIBRARY_PATHS} +) +MARK_AS_ADVANCED(QWT_LIBRARY) + +IF (QWT_LIBRARY) + + IF(WIN32 AND NOT CYGWIN) + + SET(QWT_NAMES_DEBUG qwtd qwtd-qt4 qwtd5 ) + FIND_LIBRARY(QWT_LIBRARY_DEBUG + NAMES ${QWT_NAMES_DEBUG} + PATHS ${POTENTIAL_LIBRARY_PATHS} + ) + MARK_AS_ADVANCED(QWT_LIBRARY_DEBUG) + + IF(QWT_LIBRARY_DEBUG) + SET(QWT_LIBRARIES optimized ${QWT_LIBRARY} debug ${QWT_LIBRARY_DEBUG} CACHE DOC "QWT library files") + ELSE(QWT_LIBRARY_DEBUG) + SET(QWT_LIBRARIES ${QWT_LIBRARY} CACHE DOC "QWT library files") + ENDIF(QWT_LIBRARY_DEBUG) + + ADD_DEFINITIONS(-DQWT_DLL) + + ELSE(WIN32 AND NOT CYGWIN) + + SET(QWT_LIBRARIES ${QWT_LIBRARY} CACHE DOC "QWT library files") + + ENDIF(WIN32 AND NOT CYGWIN) + + SET(QWT_FOUND "YES") + + IF (CYGWIN) + IF(BUILD_SHARED_LIBS) + # No need to define QWT_USE_DLL here, because it's default for Cygwin. + ELSE(BUILD_SHARED_LIBS) + SET (QWT_DEFINITIONS -DQWT_STATIC) + ENDIF(BUILD_SHARED_LIBS) + ENDIF (CYGWIN) + +ENDIF (QWT_LIBRARY) + +# handle the QUIETLY and REQUIRED arguments +INCLUDE( FindPackageHandleStandardArgs ) +IF( CMAKE_VERSION LESS 2.8.3 ) + FIND_PACKAGE_HANDLE_STANDARD_ARGS( Qwt DEFAULT_MSG QWT_LIBRARY QWT_INCLUDE_DIR _QWT_VERSION_MATCH ) +ELSE() + FIND_PACKAGE_HANDLE_STANDARD_ARGS( Qwt REQUIRED_VARS QWT_LIBRARY QWT_INCLUDE_DIR _QWT_VERSION_MATCH VERSION_VAR QWT_VERSION_STRING ) +ENDIF() + +MARK_AS_ADVANCED(QWT_INCLUDE_DIR QWT_LIBRARY) + diff --git a/cmake/FindUHD.cmake b/cmake/modules/FindUHD.cmake similarity index 100% rename from cmake/FindUHD.cmake rename to cmake/modules/FindUHD.cmake diff --git a/cmake/modules/libLTEPackage.cmake b/cmake/modules/libLTEPackage.cmake new file mode 100644 index 000000000..c164a469e --- /dev/null +++ b/cmake/modules/libLTEPackage.cmake @@ -0,0 +1,85 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +SET(CPACK_PACKAGE_DESCRIPTION "libLTE") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LTE library for SDR.") +SET(CPACK_PACKAGE_NAME "liblte") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1), libboost-dev (>= 1.35)") + +SET(CPACK_PACKAGE_CONTACT "Ismael Gomez ") +SET(CPACK_PACKAGE_VENDOR "University of Dublin, Trinity College") +SET(CPACK_PACKAGE_VERSION_MAJOR "0") +SET(CPACK_PACKAGE_VERSION_MINOR "1") +SET(CPACK_PACKAGE_VERSION_PATCH "0") +SET(VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + +######################################################################## +# Setup additional defines for OS types +######################################################################## +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(LINUX TRUE) +ENDIF() + +IF(LINUX AND EXISTS "/etc/debian_version") + SET(DEBIAN TRUE) +ENDIF() + +IF(LINUX AND EXISTS "/etc/redhat-release") + SET(REDHAT TRUE) +ENDIF() + +######################################################################## +# Set generator type for recognized systems +######################################################################## +IF(CPACK_GENERATOR) + #already set +ELSEIF(APPLE) + SET(CPACK_GENERATOR PackageMaker) +ELSEIF(WIN32) + SET(CPACK_GENERATOR NSIS) +ELSEIF(DEBIAN) + SET(CPACK_GENERATOR DEB) +ELSEIF(REDHAT) + SET(CPACK_GENERATOR RPM) +ELSE() + SET(CPACK_GENERATOR TGZ) +ENDIF() + +######################################################################## +# Setup CPack Debian +######################################################################## +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-dev") + +######################################################################## +# Setup CPack RPM +######################################################################## +SET(CPACK_RPM_PACKAGE_REQUIRES "boost-devel") + +######################################################################## +# Setup CPack NSIS +######################################################################## +SET(CPACK_NSIS_MODIFY_PATH ON) + + +SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}-${CMAKE_SYSTEM_PROCESSOR}") +INCLUDE(CPack) + diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 000000000..c869827cc --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,26 @@ +if(POLICY CMP0007) + cmake_policy(SET CMP0007 OLD) +endif(POLICY CMP0007) + +if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +list(REVERSE files) +foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else (EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif (EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/cuhd/CMakeLists.txt b/cuhd/CMakeLists.txt new file mode 100644 index 000000000..38d87ce55 --- /dev/null +++ b/cuhd/CMakeLists.txt @@ -0,0 +1,36 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Install headers +######################################################################## +INSTALL(DIRECTORY include/ DESTINATION "${INCLUDE_DIR}" + FILES_MATCHING PATTERN "*.h" + PATTERN ".svn" EXCLUDE +) + + +######################################################################## +# Add the subdirectories +######################################################################## + +ADD_SUBDIRECTORY(lib) + diff --git a/cuhd/include/cuhd.h b/cuhd/include/cuhd.h new file mode 100644 index 000000000..7e8b00d70 --- /dev/null +++ b/cuhd/include/cuhd.h @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include "cuhd/cuhd_utils.h" + +int cuhd_open(char *args, void **handler); +int cuhd_close(void *h); + +int cuhd_start_rx_stream(void *h); +int cuhd_start_rx_stream_nsamples(void *h, int nsamples); +int cuhd_stop_rx_stream(void *h); +bool cuhd_rx_wait_lo_locked(void *h); +double cuhd_set_rx_srate(void *h, double freq); +double cuhd_set_rx_gain(void *h, double gain); +double cuhd_set_rx_freq(void *h, double freq); +int cuhd_recv(void *h, void *data, int nsamples, int blocking); + +int cuhd_start_tx_stream(void *h); +double cuhd_set_tx_srate(void *h, double freq); +double cuhd_set_tx_gain(void *h, double gain); +double cuhd_set_tx_freq(void *h, double freq); +int cuhd_send(void *h, void *data, int nsamples, int blocking); + + +#ifdef __cplusplus +} +#endif diff --git a/cuhd/include/cuhd/cuhd_utils.h b/cuhd/include/cuhd/cuhd_utils.h new file mode 100644 index 000000000..f872f830e --- /dev/null +++ b/cuhd/include/cuhd/cuhd_utils.h @@ -0,0 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +int cuhd_rssi_scan(void *uhd, float *freqs, float *rssi, int nof_bands, double fs, int nsamp); diff --git a/cuhd/lib/CMakeLists.txt b/cuhd/lib/CMakeLists.txt new file mode 100644 index 000000000..82ad6382c --- /dev/null +++ b/cuhd/lib/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +FIND_PACKAGE(UHD) + +IF(UHD_FOUND) + + ADD_LIBRARY(cuhd cuhd_imp.cpp cuhd_utils.c) + INCLUDE_DIRECTORIES(${UHD_INCLUDE_DIRS}) + TARGET_LINK_LIBRARIES(cuhd ${UHD_LIBRARIES}) + + LIBLTE_SET_PIC(cuhd) + APPEND_INTERNAL_LIST(OPTIONAL_LIBS cuhd) + INSTALL(TARGETS cuhd DESTINATION ${LIBRARY_DIR}) + + MESSAGE(STATUS " cuhd UHD C wrapper will be installed.") + +ELSE(UHD_FOUND) + + MESSAGE(STATUS " UHD driver not found. CUHD library is not generated") + +ENDIF(UHD_FOUND) + + + \ No newline at end of file diff --git a/cuhd/lib/cuhd_handler.hpp b/cuhd/lib/cuhd_handler.hpp new file mode 100644 index 000000000..349117a88 --- /dev/null +++ b/cuhd/lib/cuhd_handler.hpp @@ -0,0 +1,38 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include + +class cuhd_handler { +public: + uhd::usrp::multi_usrp::sptr usrp; + uhd::rx_streamer::sptr rx_stream; + bool rx_stream_enable; + uhd::tx_streamer::sptr tx_stream; + +}; diff --git a/cuhd/lib/cuhd_imp.cpp b/cuhd/lib/cuhd_imp.cpp new file mode 100644 index 000000000..0010bfefd --- /dev/null +++ b/cuhd/lib/cuhd_imp.cpp @@ -0,0 +1,210 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "cuhd_handler.hpp" +#include "cuhd.h" + + +void my_handler(uhd::msg::type_t type, const std::string &msg){ + //handle the message... +} + +typedef _Complex float complex_t; + +#define SAMPLE_SZ sizeof(complex_t) + +bool isLocked(void *h) +{ + cuhd_handler* handler = static_cast(h); + return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool(); +} + +bool cuhd_rx_wait_lo_locked(void *h) +{ + + double report = 0.0; + while(isLocked(h) && report < 3.0) + { + report += 0.1; + usleep(1000); + } + return isLocked(h); +} + +int cuhd_start_rx_stream(void *h) { + cuhd_handler* handler = static_cast(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + handler->usrp->issue_stream_cmd(cmd); + return 0; +} + +int cuhd_stop_rx_stream(void *h) { + cuhd_handler* handler = static_cast(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + handler->usrp->issue_stream_cmd(cmd); + return 0; +} + +int cuhd_start_rx_stream_nsamples(void *h, int nsamples) { + cuhd_handler* handler = static_cast(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + cmd.num_samps = nsamples; + handler->usrp->issue_stream_cmd(cmd); + return 0; +} + + + +int cuhd_open(char *args, void **h) { + cuhd_handler* handler = new cuhd_handler(); + std::string _args=std::string(args); + handler->usrp = uhd::usrp::multi_usrp::make(_args); + + //uhd::msg::register_handler(&my_handler); + + std::string otw, cpu; + otw="sc16"; + cpu="fc32"; + + handler->usrp->set_clock_source("internal"); + + uhd::stream_args_t stream_args(cpu, otw); +// stream_args.channels.push_back(0); +// stream_args.args["noclear"] = "1"; + + handler->rx_stream = handler->usrp->get_rx_stream(stream_args); + *h = handler; + + handler->tx_stream = handler->usrp->get_tx_stream(stream_args); + + return 0; +} + +int cuhd_close(void *h) { + /** TODO */ + return 0; +} + + +double cuhd_set_rx_srate(void *h, double freq) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_rx_rate(freq); + double ret = handler->usrp->get_rx_rate(); + return ret; +} + +double cuhd_set_rx_gain(void *h, double gain) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_rx_gain(gain); + return handler->usrp->get_rx_gain(); +} + +double cuhd_set_rx_freq(void *h, double freq) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_rx_freq(freq); + return handler->usrp->get_rx_freq(); +} + +int cuhd_recv(void *h, void *data, int nsamples, int blocking) { + cuhd_handler* handler = static_cast(h); + uhd::rx_metadata_t md; + if (blocking) { + int n=0,p; + complex_t *data_c = (complex_t*) data; + do { + p=handler->rx_stream->recv(&data_c[n], nsamples-n, md); + if (p == -1) { + return -1; + } + n+=p; + } while(nrx_stream->recv(data, nsamples, md, 0.0); + } +} + + + +int cuhd_start_tx_stream(void *h) { + cuhd_handler* handler = static_cast(h); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.time_spec = handler->usrp->get_time_now(); + cmd.stream_now = true; + handler->usrp->issue_stream_cmd(cmd); + return 0; +} + +double cuhd_set_tx_gain(void *h, double gain) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_tx_gain(gain); + return handler->usrp->get_tx_gain(); +} + +double cuhd_set_tx_srate(void *h, double freq) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_tx_rate(freq); + return handler->usrp->get_tx_rate(); +} + +double cuhd_set_tx_freq(void *h, double freq) { + cuhd_handler* handler = static_cast(h); + handler->usrp->set_tx_freq(freq); + return handler->usrp->get_tx_freq(); +} + +int cuhd_send(void *h, void *data, int nsamples, int blocking) { + cuhd_handler* handler = static_cast(h); + uhd::tx_metadata_t md; + if (blocking) { + int n=0,p; + complex_t *data_c = (complex_t*) data; + do { + p=handler->tx_stream->send(&data_c[n], nsamples-n, md); + if (p == -1) { + return -1; + } + n+=p; + } while(ntx_stream->send(data, nsamples, md, 0.0); + } +} + diff --git a/cuhd/lib/cuhd_utils.c b/cuhd/lib/cuhd_utils.c new file mode 100644 index 000000000..f755f073f --- /dev/null +++ b/cuhd/lib/cuhd_utils.c @@ -0,0 +1,79 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + + +#include "cuhd.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" + +int cuhd_rssi_scan(void *uhd, float *freqs, float *rssi, int nof_bands, double fs, int nsamp) { + int i, j; + int ret = -1; + _Complex float *buffer; + double f; + + buffer = calloc(nsamp, sizeof(_Complex float)); + if (!buffer) { + goto free_and_exit; + } + + cuhd_set_rx_gain(uhd, 0.0); + cuhd_set_rx_srate(uhd, fs); + + for (i=0;i +#include +#include +#include +#include + +#include "lte.h" + +#define ENABLE_UHD + +#ifdef ENABLE_UHD +#include "cuhd.h" +void *uhd; +#endif + +char *output_file_name = NULL; +int nof_frames=-1; +int cell_id = 1; +int nof_prb = 6; + + +filesink_t fsink; +lte_fft_t ifft; +pbch_t pbch; + +cf_t *slot_buffer = NULL, *output_buffer = NULL; +int slot_n_re, slot_n_samples; + +#define cuhd_FREQ 2680000000 +#define cuhd_SAMP_FREQ 1920000 +#define cuhd_GAIN 10 +#define cuhd_AMP 0.25 +#define cuhd_ARGS "addr=192.168.10.3" + +void usage(char *prog) { + printf("Usage: %s [ncvp]\n", prog); + printf("\t-o output_file [Default USRP]\n"); + printf("\t-n number of frames [Default %d]\n", nof_frames); + printf("\t-c cell id [Default %d]\n", cell_id); + printf("\t-p nof_prb [Default %d]\n", nof_prb); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "oncpv")) != -1) { + switch(opt) { + case 'o': + output_file_name = argv[optind]; + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +#ifndef ENABLE_UHD + if (!output_file_name) { + usage(argv[0]); + exit(-1); + } +#endif +} + +void base_init() { + /* init memory */ + slot_buffer = malloc(sizeof(cf_t) * slot_n_re); + if (!slot_buffer) { + perror("malloc"); + exit(-1); + } + output_buffer = malloc(sizeof(cf_t) * slot_n_samples); + if (!output_buffer) { + perror("malloc"); + exit(-1); + } + /* open file or USRP */ + if (output_file_name) { + if (filesink_init(&fsink, output_file_name, COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", output_file_name); + exit(-1); + } + } else { +#ifdef ENABLE_UHD + printf("Opening UHD device...\n"); + if (cuhd_open(cuhd_ARGS,&uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } +#else + exit(-1); // not supposed to be here +#endif + } + /* create ifft object */ + if (lte_ifft_init(&ifft, CPNORM, nof_prb)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + if (pbch_init(&pbch, cell_id, CPNORM)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } +#ifdef ENABLE_MATLAB + fmatlab = fopen("output.m", "w"); + if (!fmatlab) { + perror("fopen"); + exit(-1); + } +#endif +} + +void base_free() { + + pbch_free(&pbch); + + lte_ifft_free(&ifft); + + if (slot_buffer) { + free(slot_buffer); + } + if (output_buffer) { + free(output_buffer); + } + if (output_file_name) { + filesink_free(&fsink); + } else { +#ifdef ENABLE_UHD + cuhd_close(&uhd); +#endif + } +#ifdef ENABLE_MATLAB + fclose(fmatlab); +#endif +} + +int main(int argc, char **argv) { + int nf, ns, N_id_2; + cf_t pss_signal[PSS_LEN]; + float sss_signal0[SSS_LEN]; // for subframe 0 + float sss_signal5[SSS_LEN]; // for subframe 5 + pbch_mib_t mib; + refsignal_t refs[NSLOTS_X_FRAME]; + int i; + cf_t *slot1_symbols[MAX_PORTS_CTRL]; + + +#ifndef ENABLE_UHD + if (argc < 3) { + usage(argv[0]); + exit(-1); + } +#endif + + parse_args(argc,argv); + + N_id_2 = cell_id%3; + slot_n_re = CPNORM_NSYMB * nof_prb * RE_X_RB; + slot_n_samples = SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb)); + + /* this *must* be called after setting slot_len_* */ + base_init(); + + /* Generate PSS/SSS signals */ + pss_generate(pss_signal, N_id_2); + sss_generate(sss_signal0, sss_signal5, cell_id); + + /* Generate CRS signals */ + for (i=0;i #include #include @@ -6,7 +33,7 @@ #include "lte.h" char *input_file_name; -int nof_slots=1; +int nof_frames=1; int cell_id = 0; int port_id = 0; int nof_prb = 6; @@ -29,7 +56,7 @@ int slot_length() { void usage(char *prog) { printf("Usage: %s [bncprev] -i input_file\n", prog); printf("\t-b input file is binary [Default no]\n"); - printf("\t-n number of slots [Default %d]\n", nof_slots); + printf("\t-n number of slots [Default %d]\n", nof_frames); printf("\t-c cell_id [Default %d]\n", cell_id); printf("\t-p port_id [Default %d]\n", port_id); printf("\t-r nof_prb [Default %d]\n", nof_prb); @@ -48,7 +75,7 @@ void parse_args(int argc, char **argv) { input_file_name = argv[optind]; break; case 'n': - nof_slots = atoi(argv[optind]); + nof_frames = atoi(argv[optind]); break; case 'c': cell_id = atoi(argv[optind]); @@ -124,7 +151,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: initializing FFT\n"); goto do_exit; } - if (chest_init(&eq, LINEAR, cp, nof_prb, 1)) { + if (chest_init(&eq, LINEAR, cp, nof_prb, port_id+1)) { fprintf(stderr, "Error initializing equalizer\n"); goto do_exit; } @@ -136,14 +163,14 @@ int main(int argc, char **argv) { bzero(input, sizeof(cf_t) * in_slot_length()); bzero(outfft, sizeof(cf_t) * slot_length()); - fprintf(f, "ce=zeros(%d, %d);\n", nof_slots * CP_NSYMB(cp), nof_prb * RE_X_RB); + fprintf(f, "ce=zeros(%d, %d);\n", nof_frames * CP_NSYMB(cp), nof_prb * RE_X_RB); /* read all file or nof_slots */ slot_cnt = 0; while (in_slot_length() == filesource_read(&fsrc, input, in_slot_length()) - && (slot_cnt < nof_slots || nof_slots == -1)) { + && (slot_cnt < nof_frames || nof_frames == -1)) { fprintf(f, "infft="); - vec_fprint_c(f, input, CP_NSYMB(cp) * 128); + vec_fprint_c(f, input, in_slot_length()); fprintf(f, ";\n"); lte_fft_run(&fft, input, outfft); @@ -152,9 +179,9 @@ int main(int argc, char **argv) { vec_fprint_c(f, outfft, CP_NSYMB(cp) * nof_prb * RE_X_RB); fprintf(f, ";\n"); - chest_ce_slot_port(&eq, outfft, ce, 0, 0); + chest_ce_slot_port(&eq, outfft, ce, slot_cnt%20, port_id); - chest_fprint(&eq, f, slot_cnt%20, 0); + chest_fprint(&eq, f, slot_cnt%20, port_id); for (i=0;i #include #include diff --git a/examples/ll_example.c b/examples/ll_example.c index edfc0068b..83f91b188 100644 --- a/examples/ll_example.c +++ b/examples/ll_example.c @@ -1,3 +1,31 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + #include #include diff --git a/examples/mib_scan_usrp.c b/examples/mib_scan_usrp.c index 15805a1d8..fbbfa037f 100644 --- a/examples/mib_scan_usrp.c +++ b/examples/mib_scan_usrp.c @@ -1,3 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #include #include #include @@ -9,11 +36,10 @@ #include "lte.h" -#define DISABLE_UHD +//#define DISABLE_UHD #ifndef DISABLE_UHD -#include "uhd.h" -#include "uhd_utils.h" +#include "cuhd.h" #endif #define MHZ 1000000 @@ -26,7 +52,6 @@ #define IS_SIGNAL(i) (10*log10f(rssi[i]) + 30 > rssi_threshold) - int band, earfcn=-1; float find_threshold = 40.0, track_threshold = 8.0; int earfcn_start=-1, earfcn_end = -1; @@ -34,7 +59,6 @@ float rssi_threshold = -30.0; int max_track_lost=9; int nof_frames_find=8, nof_frames_track=100, nof_samples_rssi=50000; int track_len=500; -int nof_ports; cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; pbch_t pbch; @@ -47,7 +71,7 @@ int *idx_v, *idx_valid, *t; float *p2a_v; void *uhd; int nof_bands; -float gain = 20.0; +float gain = 30.0; #define MAX_EARFCN 1000 lte_earfcn_t channels[MAX_EARFCN]; @@ -59,7 +83,6 @@ float p2a[MAX_EARFCN]; enum sync_state {INIT, FIND, TRACK, MIB, DONE}; -void print_to_matlab(); void usage(char *prog) { printf("Usage: %s [seRrFfTtgv] -b band\n", prog); @@ -135,7 +158,7 @@ int base_init(int frame_length) { return -1; } - for (i=0;i= nof_frames_track) { + mib_decoder_init(cell_id); + + cfo[freq] = mean_valid(idx_v, cfo_v, frame_cnt); + p2a[freq] = mean_valid(idx_v, p2a_v, frame_cnt); + valid_frames = preprocess_idx(idx_v, idx_valid, t, frame_cnt); + sfo = sfo_estimate_period(idx_valid, t, valid_frames, FLEN_PERIOD); + state = MIB; nslot=(nslot+10)%20; } break; case MIB: - INFO("Finding MIB at freq %.2f Mhz\n", channels[freq].fd); - cfo[freq] = mean_valid(idx_v, cfo_v, frame_cnt); - p2a[freq] = mean_valid(idx_v, p2a_v, frame_cnt); - valid_frames = preprocess_idx(idx_v, idx_valid, t, frame_cnt); - sfo = sfo_estimate_period(idx_valid, t, valid_frames, FLEN_PERIOD); + INFO("Finding MIB at freq %.2f Mhz offset=%d, cell_id=%d, slot_idx=%d\n", channels[freq].fd, find_idx, cell_id, nslot); // TODO: Correct SFO // Correct CFO INFO("Correcting CFO=%.4f\n", cfo[freq]); - nco_cexp_f_direct(&input_buffer[FLEN], -cfo[freq]/128, FLEN); + nco_cexp_f_direct(&input_buffer[FLEN], (-cfo[freq])/128, FLEN); - if (nslot == 10) { - if (mib_decoder_run(&input_buffer[FLEN+find_idx+FLEN/10], &mib)) { + if (nslot == 0) { + if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) { INFO("MIB detected attempt=%d\n", mib_attempts); state = DONE; } else { INFO("MIB not detected attempt=%d\n", mib_attempts); - if (mib_attempts >= 20) { + if (mib_attempts == 0) { freq++; state = INIT; } } mib_attempts++; - } else { - nslot = (nslot+10)%20; } + nslot = (nslot+10)%20; + break; case DONE: @@ -509,84 +537,16 @@ int main(int argc, char **argv) { } /** FIXME: This is not necessary at all */ - if (state == TRACK || (state == FIND && frame_cnt)) { + if (state == TRACK || state == FIND) { memcpy(input_buffer, &input_buffer[FLEN], FLEN * sizeof(cf_t)); } frame_cnt++; } } - print_to_matlab(); - base_free(); printf("\n\nDone\n"); exit(0); } -void print_to_matlab() { - int i; - - FILE *f = fopen("output.m", "w"); - if (!f) { - perror("fopen"); - exit(-1); - } - fprintf(f, "fd=["); - for (i=0;i #include #include @@ -7,17 +34,17 @@ #include "lte.h" char *input_file_name = NULL; -int nof_slots=100; +int nof_frames=100; float corr_peak_threshold=30; -int file_binary = 0; int force_N_id_2=-1; -int nof_ports = 1; +FILE *fmatlab; +#define NOF_PORTS 2 #define FLEN 9600 filesource_t fsrc; -cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; +cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; pbch_t pbch; lte_fft_t fft; chest_t chest; @@ -25,29 +52,25 @@ sync_t synch; void usage(char *prog) { printf("Usage: %s [onlt] -i input_file\n", prog); - printf("\t-n number of frames [Default %d]\n", nof_slots); + printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-t correlation threshold [Default %g]\n", corr_peak_threshold); printf("\t-v [set verbose to debug, default none]\n"); - printf("\t-b Input files is binary [Default %s]\n", file_binary?"yes":"no"); printf("\t-f force_N_id_2 [Default %d]\n", force_N_id_2); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "intvbf")) != -1) { + while ((opt = getopt(argc, argv, "intvf")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; break; case 'n': - nof_slots = atoi(argv[optind]); + nof_frames = atoi(argv[optind]); break; case 't': corr_peak_threshold = atof(argv[optind]); break; - case 'b': - file_binary = 1; - break; case 'v': verbose++; break; @@ -68,13 +91,18 @@ void parse_args(int argc, char **argv) { int base_init() { int i; - file_data_type_t type = file_binary?COMPLEX_FLOAT_BIN:COMPLEX_FLOAT; - if (filesource_init(&fsrc, input_file_name, type)) { + if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { fprintf(stderr, "Error opening file %s\n", input_file_name); exit(-1); } - input_buffer = malloc(4 * FLEN * sizeof(cf_t)); + fmatlab = fopen("output.m", "w"); + if (!fmatlab) { + perror("fopen"); + return -1; + } + + input_buffer = malloc(FLEN * sizeof(cf_t)); if (!input_buffer) { perror("malloc"); exit(-1); @@ -86,7 +114,7 @@ int base_init() { return -1; } - for (i=0;i= 4) { - state = SYNC; - } + INFO("MIB not detected attempt=%d\n", mib_attempts+1); } mib_attempts++; + slot_id = 10; + } else { + slot_id = 0; } break; case DONE: - INFO("State Done, Slot idx=%d\n", frame_idx); + INFO("State Done after %d frames\n", frame_cnt); pbch_mib_fprint(stdout, &mib); + frame_cnt = nof_frames; printf("Done\n"); break; } - - if (read_length) { - frame_idx++; - if (frame_idx == 2) { - frame_idx = 0; - } - } + frame_cnt++; } - sync_free(&synch); - filesource_close(&fsrc); - - free(input_buffer); + base_close(); - printf("Done\n"); + printf("Exit\n"); exit(0); } diff --git a/examples/mib_track.c b/examples/mib_track.c new file mode 100644 index 000000000..82705db57 --- /dev/null +++ b/examples/mib_track.c @@ -0,0 +1,417 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DISABLE_UHD +#include "cuhd.h" +#endif + +#include "lte.h" +#include "plot.h" + +#define MHZ 1000000 +#define SAMP_FREQ 1920000 +#define FLEN 9600 +#define FLEN_PERIOD 0.005 + +#define NOF_PORTS 2 + +float freq = 2680000000.0; +float find_threshold = 40.0, track_threshold = 8.0; +int max_track_lost = 9, nof_frames = -1; +int track_len=300; +char *input_file_name = NULL; +int disable_plots = 0; + +filesource_t fsrc; +cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; +pbch_t pbch; +lte_fft_t fft; +chest_t chest; +sync_t sfind, strack; + +plot_real_t poutfft; +plot_complex_t pce; +plot_scatter_t pscatrecv, pscatequal; + +void *uhd; +float gain = 30.0; + +enum sync_state {FIND, TRACK}; + +void usage(char *prog) { + printf("Usage: %s [ifgv]\n", prog); + printf("\t-i input_file [Default use USRP]\n"); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-d disable plots [Default enabled]\n"); + printf("\t-f freq [Default %.1f MHz]\n", freq/MHZ); + printf("\t-g gain [Default %.2f dB]\n", gain); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "ifdgv")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'f': + freq = atof(argv[optind]); + break; + case 'g': + gain = atof(argv[optind]); + break; + case 'd': + disable_plots = 1; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +void init_plots() { + plot_init(); + plot_real_init(&poutfft); + plot_real_setTitle(&poutfft, "Output FFT - Magnitude"); + plot_real_setLabels(&poutfft, "Index", "dB"); + plot_real_setYAxisScale(&poutfft, -60, 0); + plot_real_setXAxisScale(&poutfft, 1, 504); + + plot_complex_init(&pce); + plot_complex_setTitle(&pce, "Channel Estimates"); + plot_complex_setYAxisScale(&pce, Ip, -0.01, 0.01); + plot_complex_setYAxisScale(&pce, Q, -0.01, 0.01); + plot_complex_setYAxisScale(&pce, Magnitude, 0, 0.01); + plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI); + + plot_scatter_init(&pscatrecv); + plot_scatter_setTitle(&pscatrecv, "Received Symbols"); + plot_scatter_setXAxisScale(&pscatrecv, -0.01, 0.01); + plot_scatter_setYAxisScale(&pscatrecv, -0.01, 0.01); + + plot_scatter_init(&pscatequal); + plot_scatter_setTitle(&pscatequal, "Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal, -1, 1); + plot_scatter_setYAxisScale(&pscatequal, -1, 1); + +} + +int base_init(int frame_length) { + int i; + + if (!disable_plots) { + init_plots(); + } + + if (input_file_name) { + if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { + return -1; + } + } + + input_buffer = (cf_t*) malloc(frame_length * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + return -1; + } + + fft_buffer = (cf_t*) malloc(CPNORM_NSYMB * 72 * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i max_track_lost) { + INFO("%d frames lost. Going back to TRACK\n", frame_cnt - last_found); + } + + // Correct CFO + INFO("Correcting CFO=%.4f\n", cfo); + nco_cexp_f_direct(input_buffer, (-cfo)/128, FLEN); + + if (nslot == 0) { + INFO("Finding MIB at idx %d\n", find_idx); + if (mib_decoder_run(&input_buffer[find_idx], &mib)) { + INFO("MIB detected attempt=%d\n", frame_cnt); + last_found = frame_cnt; + if (verbose == VERBOSE_NONE) { + if (!nof_found_mib) { + pbch_mib_fprint(stdout, &mib); + } + } + nof_found_mib++; + } else { + INFO("MIB not found attempt %d\n",frame_cnt); + } + if (frame_cnt) { + printf("SFN: %4d\tCFO: %+.4f KHz\tTimeOffset: %4d\tErrors: %4d/%4d\tErrorRate: %.1e\r", mib.sfn, + cfo*15, find_idx, frame_cnt-2*(nof_found_mib-1), frame_cnt, + (float) (frame_cnt-2*(nof_found_mib-1))/frame_cnt); + fflush(stdout); + } + } + if (input_file_name) { + usleep(5000); + } + nslot = (nslot+10)%20; + break; + } + frame_cnt++; + } + + base_free(); + + printf("\n\nDone\n"); + exit(0); +} + diff --git a/examples/pss_scan_usrp.c b/examples/pss_scan_usrp.c index 6ebac3778..527855bf9 100644 --- a/examples/pss_scan_usrp.c +++ b/examples/pss_scan_usrp.c @@ -1,3 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #include #include #include @@ -8,9 +35,7 @@ #include #include "lte.h" - -#include "uhd.h" -#include "uhd_utils.h" +#include "cuhd.h" #define MHZ 1000000 #define SAMP_FREQ 1920000 @@ -149,7 +174,7 @@ int base_init(int frame_length) { /* open UHD device */ printf("Opening UHD device...\n"); - if (uhd_open("",&uhd)) { + if (cuhd_open("",&uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } @@ -159,7 +184,7 @@ int base_init(int frame_length) { void base_free() { - uhd_close(&uhd); + cuhd_close(&uhd); free(input_buffer); free(idx_v); free(idx_valid); @@ -208,7 +233,7 @@ int rssi_scan() { freqs[n] = channels[i].fd * MHZ; n++; } - if (uhd_rssi_scan(uhd, freqs, rssi_d, n, (double) RSSI_FS, nof_samples_rssi)) { + if (cuhd_rssi_scan(uhd, freqs, rssi_d, n, (double) RSSI_FS, nof_samples_rssi)) { fprintf(stderr, "Error while doing RSSI scan\n"); return -1; } @@ -218,7 +243,7 @@ int rssi_scan() { for (i=0;i #include #include @@ -6,9 +33,9 @@ #include #include "lte.h" -#include "uhd.h" +#include "cuhd.h" -int nof_slots=1000; +int nof_frames=1000; int band; cf_t *input_buffer, *fft_buffer; @@ -25,7 +52,7 @@ void usage(char *prog) { printf("Usage: %s [nvse] -b band\n", prog); printf("\t-s earfcn_start [Default All]\n"); printf("\t-e earfcn_end [Default All]\n"); - printf("\t-n number of frames [Default %d]\n", nof_slots); + printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-v [set verbose to debug, default none]\n"); } @@ -43,7 +70,7 @@ void parse_args(int argc, char **argv) { earfcn_end = atoi(argv[optind]); break; case 'n': - nof_slots = atoi(argv[optind]); + nof_frames = atoi(argv[optind]); break; case 'v': verbose++; @@ -65,16 +92,16 @@ int base_init() { /* open UHD device */ printf("Opening UHD device...\n"); - if (uhd_open("",&uhd)) { + if (cuhd_open("",&uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } printf("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); - uhd_set_rx_srate(uhd, SAMP_FREQ); + cuhd_set_rx_srate(uhd, SAMP_FREQ); printf("Starting receiver...\n"); - uhd_start_rx_stream(uhd); + cuhd_start_rx_stream(uhd); return 0; } @@ -101,12 +128,12 @@ int main(int argc, char **argv) { int nof_bands = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); printf("Scanning %d freqs in band %d\n", nof_bands, band); for (i=0;i #include #include @@ -8,7 +35,7 @@ char *input_file_name; char *output_file_name="abs_corr.txt"; -int nof_slots=100, frame_length=9600, symbol_sz=128; +int nof_frames=100, frame_length=9600, symbol_sz=128; float corr_peak_threshold=25.0; int file_binary = 0; int out_N_id_2 = 0, force_N_id_2=-1; @@ -20,7 +47,7 @@ void usage(char *prog) { printf("Usage: %s [onlt] -i input_file\n", prog); printf("\t-o output_file [Default %s]\n", output_file_name); printf("\t-l frame_length [Default %d]\n", frame_length); - printf("\t-n number of frames [Default %d]\n", nof_slots); + printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-t correlation threshold [Default %g]\n", corr_peak_threshold); printf("\t-s symbol_sz [Default %d]\n", symbol_sz); printf("\t-b Input files is binary [Default %s]\n", file_binary?"yes":"no"); @@ -40,7 +67,7 @@ void parse_args(int argc, char **argv) { output_file_name = argv[optind]; break; case 'n': - nof_slots = atoi(argv[optind]); + nof_frames = atoi(argv[optind]); break; case 'l': frame_length = atoi(argv[optind]); @@ -102,7 +129,7 @@ int main(int argc, char **argv) { gettimeofday(&tdata[1], NULL); printf("Initializing...");fflush(stdout); - file_data_type_t type = file_binary?COMPLEX_FLOAT_BIN:COMPLEX_FLOAT; + data_type_t type = file_binary?COMPLEX_FLOAT_BIN:COMPLEX_FLOAT; if (filesource_init(&fsrc, input_file_name, type)) { fprintf(stderr, "Error opening file %s\n", input_file_name); exit(-1); @@ -117,12 +144,12 @@ int main(int argc, char **argv) { perror("malloc"); exit(-1); } - cfo = malloc(nof_slots*sizeof(float)); + cfo = malloc(nof_frames*sizeof(float)); if (!cfo) { perror("malloc"); exit(-1); } - exec_time = malloc(nof_slots*sizeof(int)); + exec_time = malloc(nof_frames*sizeof(int)); if (!exec_time) { perror("malloc"); exit(-1); @@ -161,7 +188,7 @@ int main(int argc, char **argv) { /* read all file or nof_frames */ frame_cnt = 0; while (frame_length == filesource_read(&fsrc, input, frame_length) - && frame_cnt < nof_slots) { + && frame_cnt < nof_frames) { gettimeofday(&tdata[1], NULL); if (force_cfo != CFO_AUTO) { @@ -229,8 +256,8 @@ int main(int argc, char **argv) { sss_synch_free(&sss[N_id_2]); } - filesource_close(&fsrc); - filesink_close(&fsink); + filesource_free(&fsrc); + filesink_free(&fsink); free(input); free(cfo); diff --git a/examples/viterbi_test.c b/examples/viterbi_test.c index b1d53994c..03fdef3ae 100644 --- a/examples/viterbi_test.c +++ b/examples/viterbi_test.c @@ -1,5 +1,33 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #include #include +#include #include #include #include @@ -9,28 +37,35 @@ typedef _Complex float cf_t; -int frame_length=1000, nof_slots=128; -float ebno_db = 5.0; -unsigned int seed=0; +int frame_length = 1000, nof_frames = 128; +float ebno_db = 100.0; +unsigned int seed = 0; bool tail_biting = false; +int K = -1; + +#define SNR_POINTS 10 +#define SNR_MIN 0.0 +#define SNR_MAX 5.0 -char message[40] = {0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,0,1,0,0,0,0,1}; +#define NCODS 3 +#define NTYPES 1+NCODS void usage(char *prog) { - printf("Usage: %s [nl]\n", prog); - printf("\t-n nof_frames [Default %d]\n", nof_slots); + printf("Usage: %s [nlestk]\n", prog); + printf("\t-n nof_frames [Default %d]\n", nof_frames); printf("\t-l frame_length [Default %d]\n", frame_length); - printf("\t-e ebno in dB [Default %.2f dB]\n", ebno_db); + printf("\t-e ebno in dB [Default scan]\n"); printf("\t-s seed [Default 0=time]\n"); - printf("\t-t tail_bitting [Default %s]\n", tail_biting?"yes":"no"); + printf("\t-t tail_bitting [Default %s]\n", tail_biting ? "yes" : "no"); + printf("\t-k constraint length [Default both]\n", K); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "nlste")) != -1) { - switch(opt) { + while ((opt = getopt(argc, argv, "nlstek")) != -1) { + switch (opt) { case 'n': - nof_slots = atoi(argv[optind]); + nof_frames = atoi(argv[optind]); break; case 'l': frame_length = atoi(argv[optind]); @@ -44,6 +79,9 @@ void parse_args(int argc, char **argv) { case 't': tail_biting = true; break; + case 'k': + K = atoi(argv[optind]); + break; default: usage(argv[0]); exit(-1); @@ -52,30 +90,86 @@ void parse_args(int argc, char **argv) { } int main(int argc, char **argv) { - viterbi_t dec; - convcoder_t cod; - modem_table_t modem; - demod_soft_t demod; int frame_cnt; float *llr; - char *data_tx, *data_rx, *symbols; - cf_t *iq; - int i; - - parse_args(argc,argv); + unsigned char *llr_c; + char *data_tx, *data_rx[NTYPES], *symbols; + int i, j; + float var[SNR_POINTS], varunc[SNR_POINTS]; + int snr_points; + float ber[NTYPES][SNR_POINTS]; + unsigned int errors[NTYPES]; + viterbi_type_t viterbi_type[NCODS]; + viterbi_t dec[NCODS]; + convcoder_t cod[NCODS]; + int coded_length[NCODS]; + int n, ncods, max_coded_length; + + + parse_args(argc, argv); if (!seed) { seed = time(NULL); } srand(seed); - int coded_length = 3 * (frame_length + ((tail_biting)?0:6)); + switch (K) { + case 9: + cod[0].poly[0] = 0x1ed; + cod[0].poly[1] = 0x19b; + cod[0].poly[2] = 0x127; + cod[0].tail_biting = false; + cod[0].K = 9; + viterbi_type[0] = viterbi_39; + ncods=1; + break; + case 7: + cod[0].poly[0] = 0x6D; + cod[0].poly[1] = 0x4F; + cod[0].poly[2] = 0x57; + cod[0].K = 7; + cod[0].tail_biting = tail_biting; + viterbi_type[0] = viterbi_37; + ncods=1; + break; + default: + cod[0].poly[0] = 0x1ed; + cod[0].poly[1] = 0x19b; + cod[0].poly[2] = 0x127; + cod[0].tail_biting = false; + cod[0].K = 9; + viterbi_type[0] = viterbi_39; + cod[1].poly[0] = 0x6D; + cod[1].poly[1] = 0x4F; + cod[1].poly[2] = 0x57; + cod[1].tail_biting = false; + cod[1].K = 7; + viterbi_type[1] = viterbi_37; + cod[2].poly[0] = 0x6D; + cod[2].poly[1] = 0x4F; + cod[2].poly[2] = 0x57; + cod[2].tail_biting = true; + cod[2].K = 7; + viterbi_type[2] = viterbi_37; + ncods=3; + } + + max_coded_length = 0; + for (i=0;i max_coded_length) { + max_coded_length = coded_length[i]; + } + viterbi_init(&dec[i], viterbi_type[i], cod[i].poly, frame_length, cod[i].tail_biting); + printf("Convolutional Code 1/3 K=%d Tail bitting: %s\n", cod[i].K, cod[i].tail_biting ? "yes" : "no"); + } - printf("Convolutional Code 1/3 K=7 Test\n"); printf(" Frame length: %d\n", frame_length); - printf(" Codeword length: %d\n", coded_length); - printf(" Tail bitting: %s\n", tail_biting?"yes":"no"); - printf(" EbNo: %.2f\n", ebno_db); + if (ebno_db < 100.0) { + printf(" EbNo: %.2f\n", ebno_db); + } data_tx = malloc(frame_length * sizeof(char)); if (!data_tx) { @@ -83,85 +177,148 @@ int main(int argc, char **argv) { exit(-1); } - data_rx = malloc(frame_length * sizeof(char)); - if (!data_rx) { - perror("malloc"); - exit(-1); + for (i = 0; i < NTYPES; i++) { + data_rx[i] = malloc(frame_length * sizeof(char)); + if (!data_rx[i]) { + perror("malloc"); + exit(-1); + } } - symbols = malloc(coded_length * sizeof(char)); + symbols = malloc(max_coded_length * sizeof(char)); if (!symbols) { perror("malloc"); exit(-1); } - llr = malloc(coded_length * sizeof(float)); + llr = malloc(max_coded_length * sizeof(float)); if (!llr) { perror("malloc"); exit(-1); } - - iq = malloc(coded_length * sizeof(cf_t)); - if (!iq) { + llr_c = malloc(2 * max_coded_length * sizeof(char)); + if (!llr_c) { perror("malloc"); exit(-1); } - cod.K = 7; - cod.R = 3; - cod.tail_biting = tail_biting; - cod.framelength = frame_length; - cod.poly[0] = 0x6D; - cod.poly[1] = 0x4F; - cod.poly[2] = 0x57; - - float var = sqrt(pow(10,-ebno_db/10)); - - modem_table_init(&modem); - modem_table_std(&modem, LTE_QPSK, true); - demod_soft_init(&demod); - demod_soft_table_set(&demod, &modem); - demod_soft_alg_set(&demod, APPROX); - demod_soft_sigma_set(&demod, var); - - viterbi_init(&dec, viterbi_37, cod.poly, frame_length, tail_biting); - - /* read all file or nof_frames */ - frame_cnt = 0; - unsigned int errors=0; - while (frame_cnt < nof_slots) { - - /* generate data_tx */ - for (i=0;i 0 ? 1 : 0; + } + + /* coded BER */ + for (n=0;n 1) { + printf("\n"); - printf("BER:\t%g\t%u errors\n", (float) errors/(frame_cnt*frame_length), errors); - - viterbi_free(&dec); + FILE *f = fopen("output.m", "w"); + if (!f) { + perror("fopen"); + exit(-1); + } + fprintf(f, "ber=["); + for (j = 0; j < NTYPES; j++) { + for (i = 0; i < snr_points; i++) { + fprintf(f, "%g ", ber[j][i]); + } + fprintf(f, "; "); + } + fprintf(f, "];\n"); + fprintf(f, "snr=linspace(%g,%g-%g/%d,%d);\n", SNR_MIN, SNR_MAX, SNR_MAX, + snr_points, snr_points); + fprintf(f, "semilogy(snr,ber,snr,0.5*erfc(sqrt(10.^(snr/10))));\n"); + fprintf(f, "legend('uncoded',"); + for (n=0;n + +typedef enum { + Ip, Q, Magnitude, Phase +} plot_complex_id_t; + +typedef void* plot_complex_t; + +int plot_complex_init(plot_complex_t *h); +void plot_complex_setTitle(plot_complex_t *h, char *title); +void plot_complex_setNewData(plot_complex_t *h, _Complex float *data, + int num_points); +void plot_complex_setXAxisAutoScale(plot_complex_t *h, plot_complex_id_t id, bool on); +void plot_complex_setYAxisAutoScale(plot_complex_t *h, plot_complex_id_t id, bool on); +void plot_complex_setXAxisScale(plot_complex_t *h, plot_complex_id_t id, double xMin, double xMax); +void plot_complex_setYAxisScale(plot_complex_t *h, plot_complex_id_t id, double yMin, double yMax); +void plot_complex_setXAxisRange(plot_complex_t *h, double xMin, double xMax); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graphics/include/plot/plot_real.h b/graphics/include/plot/plot_real.h new file mode 100644 index 000000000..bd8fe58d7 --- /dev/null +++ b/graphics/include/plot/plot_real.h @@ -0,0 +1,55 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef _plot_real_h +#define _plot_real_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void* plot_real_t; + +int plot_real_init(plot_real_t *h); +void plot_real_setTitle(plot_real_t *h, char *title); +void plot_real_setNewData(plot_real_t *h, float *data, + int num_points); +void plot_real_setXAxisAutoScale(plot_real_t *h, bool on); +void plot_real_setYAxisAutoScale(plot_real_t *h, bool on); +void plot_real_setXAxisScale(plot_real_t *h, double xMin, double xMax); +void plot_real_setYAxisScale(plot_real_t *h, double yMin, double yMax); +void plot_real_setXAxisRange(plot_real_t *h, double xMin, double xMax); +void plot_real_setLabels(plot_real_t *h, char *xLabel, char *yLabel); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graphics/include/plot/plot_scatter.h b/graphics/include/plot/plot_scatter.h new file mode 100644 index 000000000..bd9536903 --- /dev/null +++ b/graphics/include/plot/plot_scatter.h @@ -0,0 +1,54 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef _plot_scatter_h +#define _plot_scatter_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void* plot_scatter_t; + +int plot_scatter_init(plot_scatter_t *h); +void plot_scatter_setTitle(plot_scatter_t *h, char *title); +void plot_scatter_setNewData(plot_scatter_t *h, _Complex float *data, + int num_points); +void plot_scatter_setXAxisAutoScale(plot_scatter_t *h, bool on); +void plot_scatter_setYAxisAutoScale(plot_scatter_t *h, bool on); +void plot_scatter_setXAxisScale(plot_scatter_t *h, double xMin, double xMax); +void plot_scatter_setYAxisScale(plot_scatter_t *h, double yMin, double yMax); +void plot_scatter_setAxisLabels(plot_scatter_t *h, char *xLabel, char *yLabel); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graphics/include/plot/plot_waterfall.h b/graphics/include/plot/plot_waterfall.h new file mode 100644 index 000000000..341b7d9d1 --- /dev/null +++ b/graphics/include/plot/plot_waterfall.h @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef _plot_waterfall_h +#define _plot_waterfall_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void* plot_waterfall_t; + +int plot_waterfall_init(plot_waterfall_t *h); +void plot_waterfall_setTitle(plot_waterfall_t *h, char *title); +void plot_waterfall_appendNewData(plot_waterfall_t *h, float *data, + int num_points); +void plot_complex_setPlotXLabel(plot_waterfall_t *h, char *xLabel); +void plot_complex_setPlotYLabel(plot_waterfall_t *h, char *yLabel); +void plot_waterfall_setPlotXAxisRange(plot_waterfall_t *h, double xMin, double xMax); +void plot_waterfall_setPlotXAxisScale(plot_waterfall_t *h, double xMin, double xMax); +void plot_waterfall_setPlotYAxisScale(plot_waterfall_t *h, double yMin, double yMax); + +void plot_waterfall_setSpectrogramXLabel(plot_waterfall_t *h, char* xLabel); +void plot_waterfall_setSpectrogramYLabel(plot_waterfall_t *h, char* yLabel); +void plot_waterfall_setSpectrogramXAxisRange(plot_waterfall_t *h, double xMin, double xMax); +void plot_waterfall_setSpectrogramYAxisRange(plot_waterfall_t *h, double yMin, double yMax); +void plot_waterfall_setSpectrogramZAxisScale(plot_waterfall_t *h, double zMin, double zMax); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/graphics/lib/CMakeLists.txt b/graphics/lib/CMakeLists.txt new file mode 100644 index 000000000..53cf7da53 --- /dev/null +++ b/graphics/lib/CMakeLists.txt @@ -0,0 +1,86 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + + +######################################################################## +# Setup Qt and Qwt +######################################################################## + +FIND_PACKAGE(Qt4) +IF(QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF(QT4_FOUND) + +FIND_PACKAGE(Qwt) +IF(QT4_FOUND AND QWT_FOUND) + INCLUDE_DIRECTORIES(${QWT_INCLUDE_DIRS}) +ENDIF(QT4_FOUND AND QWT_FOUND) + + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../../) + +######################################################################## +# Build the graphics library +######################################################################## + +file(GLOB modules *) + +SET(SOURCES_ALL "") +FOREACH (_module ${modules}) + IF(IS_DIRECTORY ${_module}) + FILE(GLOB tmp "${_module}/*.cpp") + LIST(APPEND SOURCES_ALL ${tmp}) + ENDIF(IS_DIRECTORY ${_module}) +ENDFOREACH(_module ${modules}) + +IF(QT4_FOUND AND QWT_FOUND) + QT4_WRAP_CPP(lineplotwraps common/Lineplot.h) + QT4_WRAP_CPP(pointplotwraps common/Pointplot.h) + QT4_WRAP_CPP(spectrogramplotwraps common/Spectrogramplot.h) + QT4_WRAP_CPP(complex complexplot/ComplexWidget.h complexplot/ComplexplotWrapper.h) + QT4_WRAP_CPP(real realplot/RealWidget.h realplot/RealplotWrapper.h) + QT4_WRAP_CPP(scatter scatterplot/ScatterWidget.h scatterplot/ScatterplotWrapper.h) + QT4_WRAP_CPP(waterfall waterfallplot/WaterfallWidget.h waterfallplot/WaterfallplotWrapper.h) + + INCLUDE_DIRECTORIES(common complexplot realplot scatterplot waterfallplot) + + ADD_LIBRARY(graphics ${eventwraps} ${lineplotwraps} ${pointplotwraps} ${spectrogramplotwraps} ${complex} ${real} ${scatter} ${waterfall} ${SOURCES_ALL} ) + TARGET_LINK_LIBRARIES(graphics pthread ${QT_LIBRARIES} ${QWT_LIBRARIES}) + INSTALL(TARGETS graphics DESTINATION ${LIBRARY_DIR}) + LIBLTE_SET_PIC(graphics) + + APPEND_INTERNAL_LIST(OPTIONAL_LIBS graphics) + + MESSAGE(STATUS " graphics library will be installed.") + +ELSE(QT4_FOUND AND QWT_FOUND) + + MESSAGE(STATUS " QT4/Qwt not found. GRAPHICS library is not generated") + +ENDIF(QT4_FOUND AND QWT_FOUND) + + +ADD_SUBDIRECTORY(complexplot/test) +ADD_SUBDIRECTORY(realplot/test) +ADD_SUBDIRECTORY(scatterplot/test) +ADD_SUBDIRECTORY(waterfallplot/test) + + diff --git a/graphics/lib/common/Events.cpp b/graphics/lib/common/Events.cpp new file mode 100644 index 000000000..9276e346a --- /dev/null +++ b/graphics/lib/common/Events.cpp @@ -0,0 +1,94 @@ +/** + * \file lib/generic/graphics/qt/common/Events.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Implementation of events used to pass data to Qt-based classes. + */ + +#include "Events.h" + +using namespace std; + +const QEvent::Type RealDataEvent::type = static_cast(10000); + +RealDataEvent::RealDataEvent(double* dataPoints, int numPoints) + : QEvent(QEvent::Type(type)) +{ + dataPoints_ = new double[numPoints]; + numPoints_ = numPoints; + memcpy(dataPoints_, dataPoints, numPoints*sizeof(double)); +} + +RealDataEvent::RealDataEvent(float* dataPoints, int numPoints) + : QEvent(QEvent::Type(type)) +{ + dataPoints_ = new double[numPoints]; + numPoints_ = numPoints; + + for(int i=0;i(10001); + +ComplexDataEvent::ComplexDataEvent(complex* dataPoints, + int numPoints) + : QEvent(QEvent::Type(type)) +{ + dataPoints_ = new complex[numPoints]; + numPoints_ = numPoints; + memcpy(dataPoints_, dataPoints, numPoints*sizeof(complex)); +} + +ComplexDataEvent::ComplexDataEvent(complex* dataPoints, + int numPoints) + : QEvent(QEvent::Type(type)) +{ + dataPoints_ = new complex[numPoints]; + numPoints_ = numPoints; + + for(int i=0;i(dataPoints[i].real(), + dataPoints[i].imag()); + } +} + +ComplexDataEvent::~ComplexDataEvent() +{ + delete[] dataPoints_; +} diff --git a/graphics/lib/common/Events.h b/graphics/lib/common/Events.h new file mode 100644 index 000000000..c7a8b9c6c --- /dev/null +++ b/graphics/lib/common/Events.h @@ -0,0 +1,72 @@ +/** + * \file lib/generic/graphics/qt/common/Events.h + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Events used to pass data to Qt-based classes. + */ + +#ifndef EVENTS_H +#define EVENTS_H + +#include +#include + +class RealDataEvent + : public QEvent +{ +public: + const static QEvent::Type type; + + RealDataEvent(double* dataPoints, + int numPoints); + RealDataEvent(float* dataPoints, + int numPoints); + virtual ~RealDataEvent(); + + double* dataPoints_; + int numPoints_; +}; + +class ComplexDataEvent + : public QEvent +{ +public: + const static QEvent::Type type; + + ComplexDataEvent(std::complex* dataPoints, + int numPoints); + ComplexDataEvent(std::complex* dataPoints, + int numPoints); + virtual ~ComplexDataEvent(); + + std::complex* dataPoints_; + int numPoints_; +}; + +#endif // EVENTS_H diff --git a/graphics/lib/common/Lineplot.cpp b/graphics/lib/common/Lineplot.cpp new file mode 100644 index 000000000..0f0c298ce --- /dev/null +++ b/graphics/lib/common/Lineplot.cpp @@ -0,0 +1,164 @@ +/** + * \file lib/generic/graphics/qt/common/Lineplot.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Implementation of a simple line plotted using a QwtPlot. + */ + +#include "Lineplot.h" + +#include + +class MyZoomer: public QwtPlotZoomer +{ +public: + MyZoomer(QwtPlotCanvas *canvas): + QwtPlotZoomer(canvas) + { + setTrackerMode(AlwaysOn); + } + + virtual QwtText trackerTextF(const QPointF &pos) const + { + QColor bg(Qt::white); + bg.setAlpha(200); + + QwtText text = QwtPlotZoomer::trackerTextF(pos); + text.setBackgroundBrush( QBrush( bg )); + return text; + } +}; + +Lineplot::Lineplot(QWidget *parent) + :QwtPlot(parent) + ,xMin_(0) + ,xMax_(0) +{ + counter_ = 0; + numPoints_ = 1; + indexPoints_ = new double[numPoints_]; + dataPoints_ = new double[numPoints_]; + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + curve_ = new QwtPlotCurve("Curve"); + curve_->setPen(QPen(Qt::blue, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + curve_->setStyle(QwtPlotCurve::Lines); + curve_->setRawSamples(indexPoints_, dataPoints_, numPoints_); + curve_->setYAxis(QwtPlot::yLeft); + curve_->attach(this); + + memset(dataPoints_, 0x0, numPoints_*sizeof(double)); + for(int i=0;isetAttribute(QwtScaleEngine::Floating,true); + axisScaleEngine(QwtPlot::yLeft)->setAttribute(QwtScaleEngine::Floating,true); + axisScaleEngine(QwtPlot::yRight)->setAttribute(QwtScaleEngine::Floating,true); + + zoomer_ = new MyZoomer(canvas()); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect2, Qt::LeftButton, + Qt::ControlModifier); + + panner_ = new QwtPlotPanner(canvas()); + panner_->setMouseButton(Qt::RightButton); + + magnifier_ = new QwtPlotMagnifier(canvas()); + magnifier_->setMouseButton(Qt::NoButton); + +} + +Lineplot::~Lineplot() +{ + delete[] indexPoints_; + delete[] dataPoints_; +} + +void Lineplot::setData(double* data, int n) +{ + if(numPoints_ != n) + { + numPoints_ = n; + delete[] indexPoints_; + delete[] dataPoints_; + indexPoints_ = new double[numPoints_]; + dataPoints_ = new double[numPoints_]; + if(xMin_==xMax_) + { + for(int i=0;isetRawSamples(indexPoints_, dataPoints_, numPoints_); + resetZoom(); +} + +void Lineplot::setXAxisRange(double xMin, double xMax) +{ + xMin_ = xMin; + xMax_ = xMax; + double step = (xMax_-xMin_)/numPoints_; + double val = xMin_; + for(int i=0;isetRawSamples(indexPoints_, dataPoints_, numPoints_); +} + +void Lineplot::resetZoom() +{ + zoomer_->setZoomBase(curve_->boundingRect()); +} + +void Lineplot::linkScales() +{ + setAxisScaleDiv(QwtPlot::yRight, *axisScaleDiv(QwtPlot::yLeft)); +} diff --git a/graphics/lib/common/Lineplot.h b/graphics/lib/common/Lineplot.h new file mode 100644 index 000000000..745fae126 --- /dev/null +++ b/graphics/lib/common/Lineplot.h @@ -0,0 +1,80 @@ +/** + * \file lib/generic/graphics/qt/common/Lineplot.h + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * A simple line plotted using a QwtPlot. + */ + +#ifndef LINEPLOT_H +#define LINEPLOT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Lineplot + : public QwtPlot +{ + Q_OBJECT + +public: + Lineplot(QWidget* parent = 0); + virtual ~Lineplot(); + + void setData(double* data, int n); + void setXAxisRange(double xMin, double xMax); + void resetZoom(); + +public slots: + void linkScales(); + +private: + QwtPlotCurve* curve_; + + QwtPlotPanner* panner_; + QwtPlotZoomer* zoomer_; + QwtPlotMagnifier* magnifier_; + + double* indexPoints_; + double* dataPoints_; + + int numPoints_; + int counter_; + double xMin_; + double xMax_; +}; + +#endif // LINEPLOT_H diff --git a/graphics/lib/common/Pointplot.cpp b/graphics/lib/common/Pointplot.cpp new file mode 100644 index 000000000..b706851d1 --- /dev/null +++ b/graphics/lib/common/Pointplot.cpp @@ -0,0 +1,121 @@ +/** + * \file lib/generic/graphics/qt/common/Pointplot.h + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Implementation of a plot of complex data values as points on an IQ axis. + */ + +#include "Pointplot.h" +#include + +using namespace std; + +class MyZoomer: public QwtPlotZoomer +{ +public: + MyZoomer(QwtPlotCanvas *canvas): + QwtPlotZoomer(canvas) + { + setTrackerMode(AlwaysOn); + } + + virtual QwtText trackerTextF(const QPointF &pos) const + { + QColor bg(Qt::white); + bg.setAlpha(200); + + QwtText text = QwtPlotZoomer::trackerTextF(pos); + text.setBackgroundBrush( QBrush( bg )); + return text; + } +}; + +Pointplot::Pointplot(QWidget *parent) + :QwtPlot(parent) +{ + counter_ = 0; + numPoints_ = 1; + realPoints_ = new double[numPoints_]; + imagPoints_ = new double[numPoints_]; + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisTitle(QwtPlot::xBottom, "In-phase"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisTitle(QwtPlot::yLeft, "Quadrature"); + + curve_ = new QwtPlotCurve("Constellation Points"); + curve_->attach(this); + curve_->setPen(QPen(Qt::blue, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + curve_->setStyle(QwtPlotCurve::Dots); + curve_->setRawSamples(realPoints_, imagPoints_, numPoints_); + + memset(realPoints_, 0x0, numPoints_*sizeof(double)); + memset(imagPoints_, 0x0, numPoints_*sizeof(double)); + + zoomer_ = new MyZoomer(canvas()); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect2, Qt::LeftButton, + Qt::ControlModifier); + + panner_ = new QwtPlotPanner(canvas()); + panner_->setMouseButton(Qt::RightButton); + + magnifier_ = new QwtPlotMagnifier(canvas()); + magnifier_->setMouseButton(Qt::NoButton); + +} + +Pointplot::~Pointplot() +{ + delete[] realPoints_; + delete[] imagPoints_; +} + +void Pointplot::setData(double* iData, double* qData, int n) +{ + if(numPoints_ != n) + { + numPoints_ = n; + delete[] realPoints_; + delete[] imagPoints_; + realPoints_ = new double[numPoints_]; + imagPoints_ = new double[numPoints_]; + } + + copy(iData, iData+n, realPoints_); + copy(qData, qData+n, imagPoints_); + //Need to setRawSamples again for autoscaling to work + curve_->setRawSamples(realPoints_, imagPoints_, numPoints_); + zoomer_->setZoomBase(curve_->boundingRect()); +} diff --git a/graphics/lib/common/Pointplot.h b/graphics/lib/common/Pointplot.h new file mode 100644 index 000000000..42ed25fb8 --- /dev/null +++ b/graphics/lib/common/Pointplot.h @@ -0,0 +1,76 @@ +/** + * \file lib/generic/graphics/qt/common/Pointplot.h + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * A plot of complex data values as points on an IQ axis. + */ + +#ifndef POINTPLOT_H +#define POINTPLOT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Pointplot + : public QwtPlot +{ + Q_OBJECT + +public: + Pointplot(QWidget* parent = 0); + virtual ~Pointplot(); + void setData(double* iData, double* qData, int n); + +private: + QwtPlotCurve* curve_; + + QwtPlotPanner* panner_; + QwtPlotZoomer* zoomer_; + QwtPlotMagnifier* magnifier_; + + struct opReal{double operator()(std::complex i) const{return real(i);}}; + struct opImag{double operator()(std::complex i) const{return imag(i);}}; + + double* realPoints_; + double* imagPoints_; + + int numPoints_; + int counter_; +}; + +#endif // POINTPLOT_H diff --git a/graphics/lib/common/Spectrogramplot.cpp b/graphics/lib/common/Spectrogramplot.cpp new file mode 100644 index 000000000..6a6433dba --- /dev/null +++ b/graphics/lib/common/Spectrogramplot.cpp @@ -0,0 +1,198 @@ +/** + * \file lib/generic/graphics/qt/common/Spectrogramplot.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * A spectrogram plot which acts as a waterfall. New data is plotted + * at the top row of the spectrogram and all old data is shifted + * downwards. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Spectrogramplot.h" + +class MyZoomer: public QwtPlotZoomer +{ +public: + MyZoomer(QwtPlotCanvas *canvas): + QwtPlotZoomer(canvas) + { + setTrackerMode(AlwaysOn); + } + + virtual QwtText trackerTextF(const QPointF &pos) const + { + QColor bg(Qt::white); + bg.setAlpha(200); + + QwtText text = QwtPlotZoomer::trackerTextF(pos); + text.setBackgroundBrush( QBrush( bg )); + return text; + } +}; + +//Set up a colormap to use the "jet" colormap from matlab +class ColorMap + :public QwtLinearColorMap +{ +public: + ColorMap() + :QwtLinearColorMap(QColor(0,0,189), QColor(132,0,0)) + { + double pos; + pos = 1.0/13.0*1.0; addColorStop(pos, QColor(0,0,255)); + pos = 1.0/13.0*2.0; addColorStop(pos, QColor(0,66,255)); + pos = 1.0/13.0*3.0; addColorStop(pos, QColor(0,132,255)); + pos = 1.0/13.0*4.0; addColorStop(pos, QColor(0,189,255)); + pos = 1.0/13.0*5.0; addColorStop(pos, QColor(0,255,255)); + pos = 1.0/13.0*6.0; addColorStop(pos, QColor(66,255,189)); + pos = 1.0/13.0*7.0; addColorStop(pos, QColor(132,255,132)); + pos = 1.0/13.0*8.0; addColorStop(pos, QColor(189,255,66)); + pos = 1.0/13.0*9.0; addColorStop(pos, QColor(255,255,0)); + pos = 1.0/13.0*10.0; addColorStop(pos, QColor(255,189,0)); + pos = 1.0/13.0*12.0; addColorStop(pos, QColor(255,66,0)); + pos = 1.0/13.0*13.0; addColorStop(pos, QColor(189,0,0)); + } +}; + +Spectrogramplot::Spectrogramplot(int numDataPoints, int numRows, QWidget *parent) + :QwtPlot(parent) + ,nData_(numDataPoints) + ,nRows_(numRows) +{ + spectrogram_ = new QwtPlotSpectrogram(); + spectrogram_->setRenderThreadCount(0); // set system specific thread count + data_ = new WaterfallData(nData_, nRows_); + spectrogram_->attach(this); + + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + + axisScaleEngine(QwtPlot::xBottom)->setAttribute(QwtScaleEngine::Floating,true); + axisScaleEngine(QwtPlot::yLeft)->setAttribute(QwtScaleEngine::Floating,true); + + spectrogram_->setColorMap(new ColorMap()); + spectrogram_->setData(data_); + + setXAxisRange(0, nData_); + setYAxisRange(0, nRows_); + setZAxisScale(-1,1); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + + zoomer_ = new MyZoomer(canvas()); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect1, + Qt::LeftButton); + zoomer_->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::LeftButton, Qt::ControlModifier); + + panner_ = new QwtPlotPanner(canvas()); + panner_->setAxisEnabled(QwtPlot::yRight, false); + panner_->setMouseButton(Qt::RightButton); + + magnifier_ = new QwtPlotMagnifier(canvas()); + magnifier_->setMouseButton(Qt::NoButton); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + const QColor c(Qt::darkBlue); + zoomer_->setRubberBandPen(c); + zoomer_->setTrackerPen(c); +} + +void Spectrogramplot::appendData(double* data, int n) +{ + data_->appendData(data, n); +} + +void Spectrogramplot::setXAxisRange(double xMin, double xMax) +{ + xMin_ = xMin; + xMax_ = xMax; + data_->setInterval( Qt::XAxis, QwtInterval( xMin_, xMax_ ) ); + plotLayout()->setAlignCanvasToScales(true); + replot(); +} + +void Spectrogramplot::setYAxisRange(double yMin, double yMax) +{ + yMin_ = yMin; + yMax_ = yMax; + data_->setInterval( Qt::YAxis, QwtInterval( yMin_, yMax_ ) ); + plotLayout()->setAlignCanvasToScales(true); + replot(); +} + +void Spectrogramplot::setZAxisScale(double zMin, double zMax) +{ + zMin_ = zMin; + zMax_ = zMax; + data_->setInterval( Qt::ZAxis, QwtInterval( zMin_, zMax_ ) ); + + //Set up the intensity bar on the right + const QwtInterval zInterval = spectrogram_->data()->interval( Qt::ZAxis ); + QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight); + rightAxis->setColorBarEnabled(true); + rightAxis->setColorMap( zInterval, new ColorMap()); + setAxisScale(QwtPlot::yRight, zInterval.minValue(), zInterval.maxValue() ); + enableAxis(QwtPlot::yRight); + + plotLayout()->setAlignCanvasToScales(true); + replot(); +} + +double Spectrogramplot::min() +{ + return data_->min(); +} + +double Spectrogramplot::max() +{ + return data_->max(); +} + +void Spectrogramplot::autoscale() +{ + setZAxisScale(min(),max()); +} diff --git a/graphics/lib/common/Spectrogramplot.h b/graphics/lib/common/Spectrogramplot.h new file mode 100644 index 000000000..157461070 --- /dev/null +++ b/graphics/lib/common/Spectrogramplot.h @@ -0,0 +1,79 @@ +/** + * \file lib/generic/graphics/qt/common/Spectrogramplot.h + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * A spectrogram plot which acts as a waterfall. New data is plotted + * at the top row of the spectrogram and all old data is shifted + * downwards. + */ + +#ifndef SPECTROGRAMPLOT_H +#define SPECTROGRAMPLOT_H + +#include +#include +#include +#include +#include +#include +#include +#include "WaterfallData.h" + +class Spectrogramplot + :public QwtPlot +{ + Q_OBJECT + +public: + Spectrogramplot(int numDataPoints, int numRows, QWidget * = NULL); + void appendData(double* data, int n); + void setXAxisRange(double xMin, double xMax); + void setYAxisRange(double yMin, double yMax); + void setZAxisScale(double zMin, double zMax); + double min(); + double max(); + void autoscale(); + +private: + QwtPlotZoomer* zoomer_; + QwtPlotPanner *panner_; + QwtPlotMagnifier *magnifier_; + QwtPlotSpectrogram *spectrogram_; + WaterfallData* data_; + int nData_; + int nRows_; + double xMin_; + double xMax_; + double yMin_; + double yMax_; + double zMin_; + double zMax_; +}; + +#endif // SPECTROGRAMPLOT_H diff --git a/graphics/lib/common/WaterfallData.h b/graphics/lib/common/WaterfallData.h new file mode 100644 index 000000000..136cd85da --- /dev/null +++ b/graphics/lib/common/WaterfallData.h @@ -0,0 +1,92 @@ +#ifndef WATERFALLDATA_H +#define WATERFALLDATA_H + +#include +#include +#include +#include +#include +#include +#include "irisapi/Exceptions.h" + + +class WaterfallData + :public QwtRasterData +{ +public: + typedef std::vector Vec; + typedef boost::shared_ptr< std::vector > VecPtr; + typedef boost::circular_buffer< VecPtr > VecPtrBuf; + typedef VecPtrBuf::iterator VecPtrBufIt; + + WaterfallData(int numDataPoints, int numRows) + :QwtRasterData() + ,nData_(numDataPoints) + ,nRows_(numRows) + ,data_(numRows) + { + for(int i=0;i(nData_))); + data_[0]->assign(nData_, 0.0); + } + } + + void appendData(double* data, int n) + { + if(n != nData_) + throw iris::InvalidDataException("WaterfallData: invalid data length"); + + VecPtr v = data_.front(); + v->assign(data, data+n); + data_.push_back(v); + } + + double max() + { + Vec maxVec; + for(int i=0;ibegin(),v->end()))); + } + return *(std::max_element(maxVec.begin(),maxVec.end())); + } + + double min() + { + Vec minVec; + for(int i=0;ibegin(),v->end()))); + } + return *(std::min_element(minVec.begin(),minVec.end())); + } + + double value(double x, double y) const + { + double bottom = interval(Qt::YAxis).minValue(); + double top = interval(Qt::YAxis).maxValue(); + double left = interval(Qt::XAxis).minValue(); + double right = interval(Qt::XAxis).maxValue(); + double xStep = std::abs(right-left)/nData_; + double yStep = std::abs(top-bottom)/nRows_; + + int ix = (x-left) / xStep; + int iy = (y-bottom) / yStep; + if(ix >= nData_) + ix = nData_-1; + if(iy >= nRows_) + iy = nRows_-1; + double ret = (*data_[iy])[ix]; + return ret; + } + +private: + VecPtrBuf data_; + int nData_; + int nRows_; +}; + +#endif // WATERFALLDATA_H diff --git a/graphics/lib/common/plot.cpp b/graphics/lib/common/plot.cpp new file mode 100644 index 000000000..08e2faae3 --- /dev/null +++ b/graphics/lib/common/plot.cpp @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "plot.h" +#include +#include +#include +#include + +pthread_t thread; +static int plot_initiated=0; + +void *qt_thread(void *arg) +{ + int argc = 1; + char* argv[] = { const_cast("libLTE Visualizer"), NULL }; + QApplication app(argc, argv); + app.exec(); + pthread_exit(NULL); +} + +int plot_init() { + if (!plot_initiated) { + /** FIXME: Set attributes to detachable */ + if (pthread_create(&thread, NULL, qt_thread, NULL)) { + perror("phtread_create"); + return -1; + } + usleep(100000); + plot_initiated=1; + } + return 0; +} + +void plot_exit() { + if (plot_initiated) { + pthread_cancel(thread); + } +} + diff --git a/graphics/lib/complexplot/ComplexWidget.cpp b/graphics/lib/complexplot/ComplexWidget.cpp new file mode 100644 index 000000000..da56a3aaa --- /dev/null +++ b/graphics/lib/complexplot/ComplexWidget.cpp @@ -0,0 +1,197 @@ +#include "ComplexWidget.h" +#include "Lineplot.h" +#include "Events.h" + +#include +#include +#include + +using namespace std; +namespace bl = boost::lambda; + +ComplexWidget::ComplexWidget(QWidget *parent) + :QWidget(parent) +{ + i_ = new Lineplot(); + q_ = new Lineplot(); + m_ = new Lineplot(); + p_ = new Lineplot(); + + i_->setAxisTitle(QwtPlot::xBottom, "In-phase"); + q_->setAxisTitle(QwtPlot::xBottom, "Quadrature"); + m_->setAxisTitle(QwtPlot::xBottom, "Magnitude"); + p_->setAxisTitle(QwtPlot::xBottom, "Phase"); + + QVBoxLayout* vLayout1 = new QVBoxLayout(this); + vLayout1->addWidget(i_); + vLayout1->addWidget(q_); + vLayout1->addWidget(m_); + vLayout1->addWidget(p_); + + numPoints_ = 16; + iData_ = new double[numPoints_]; + qData_ = new double[numPoints_]; + mData_ = new double[numPoints_]; + pData_ = new double[numPoints_]; + timerId_ = startTimer(10); + haveNewData_ = false; +} + +ComplexWidget::~ComplexWidget() +{ + delete i_; + delete q_; + delete m_; + delete p_; +} + +void ComplexWidget::customEvent( QEvent * e ) +{ + if(e->type() == ComplexDataEvent::type) + { + ComplexDataEvent* dataEvent = (ComplexDataEvent*)e; + setData(dataEvent); + } +} + +void ComplexWidget::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == timerId_) + { + if(haveNewData_) + { + i_->replot(); + q_->replot(); + m_->replot(); + p_->replot(); + haveNewData_ = false; + } + return; + } + QWidget::timerEvent(event); +} + +void ComplexWidget::setData(ComplexDataEvent* e) +{ + if(e->numPoints_ != numPoints_) + { + numPoints_ = e->numPoints_; + delete [] iData_; + delete [] qData_; + delete [] mData_; + delete [] pData_; + + iData_ = new double[numPoints_]; + qData_ = new double[numPoints_]; + mData_ = new double[numPoints_]; + pData_ = new double[numPoints_]; + } + + transform(e->dataPoints_, &e->dataPoints_[numPoints_], iData_, opReal()); + transform(e->dataPoints_, &e->dataPoints_[numPoints_], qData_, opImag()); + transform(e->dataPoints_, &e->dataPoints_[numPoints_], mData_, opAbs()); + transform(e->dataPoints_, &e->dataPoints_[numPoints_], pData_, opArg()); + + i_->setData(iData_, numPoints_); + q_->setData(qData_, numPoints_); + m_->setData(mData_, numPoints_); + p_->setData(pData_, numPoints_); + haveNewData_ = true; +} + +void ComplexWidget::setWidgetTitle(QString title) +{ + setWindowTitle(title); +} + +void ComplexWidget::setWidgetXAxisScale(int id, double xMin, double xMax) +{ + switch(id) + { + case 0: + i_->setAxisScale(QwtPlot::xBottom, xMin, xMax); + break; + case 1: + q_->setAxisScale(QwtPlot::xBottom, xMin, xMax); + break; + case 2: + m_->setAxisScale(QwtPlot::xBottom, xMin, xMax); + break; + case 3: + p_->setAxisScale(QwtPlot::xBottom, xMin, xMax); + break; + default: + break; + } +} + +void ComplexWidget::setWidgetYAxisScale(int id, double yMin, double yMax) +{ + switch(id) + { + case 0: + i_->setAxisScale(QwtPlot::yLeft, yMin, yMax); + break; + case 1: + q_->setAxisScale(QwtPlot::yLeft, yMin, yMax); + break; + case 2: + m_->setAxisScale(QwtPlot::yLeft, yMin, yMax); + break; + case 3: + p_->setAxisScale(QwtPlot::yLeft, yMin, yMax); + break; + default: + break; + } +} + +void ComplexWidget::setWidgetXAxisAutoScale(int id, bool on=true) +{ + switch(id) + { + case 0: + i_->setAxisAutoScale(QwtPlot::xBottom, on); + break; + case 1: + q_->setAxisAutoScale(QwtPlot::xBottom, on); + break; + case 2: + m_->setAxisAutoScale(QwtPlot::xBottom, on); + break; + case 3: + p_->setAxisAutoScale(QwtPlot::xBottom, on); + break; + default: + break; + } +} + +void ComplexWidget::setWidgetYAxisAutoScale(int id, bool on=true) +{ + switch(id) + { + case 0: + i_->setAxisAutoScale(QwtPlot::yLeft, on); + break; + case 1: + q_->setAxisAutoScale(QwtPlot::yLeft, on); + break; + case 2: + m_->setAxisAutoScale(QwtPlot::yLeft, on); + break; + case 3: + p_->setAxisAutoScale(QwtPlot::yLeft, on); + break; + default: + break; + } +} + +void ComplexWidget::setWidgetXAxisRange(double xMin, double xMax) +{ + i_->setXAxisRange(xMin, xMax); + q_->setXAxisRange(xMin, xMax); + m_->setXAxisRange(xMin, xMax); + p_->setXAxisRange(xMin, xMax); +} diff --git a/graphics/lib/complexplot/ComplexWidget.h b/graphics/lib/complexplot/ComplexWidget.h new file mode 100644 index 000000000..e50dcff76 --- /dev/null +++ b/graphics/lib/complexplot/ComplexWidget.h @@ -0,0 +1,53 @@ +#ifndef COMPLEXWIDGET_H +#define COMPLEXWIDGET_H + +#include +#include +#include + +class ComplexDataEvent; +class Lineplot; + +class ComplexWidget + : public QWidget +{ + Q_OBJECT + +public: + ComplexWidget(QWidget* parent = 0); + virtual ~ComplexWidget(); + +public slots: + void customEvent( QEvent * e ); + void setWidgetTitle(QString title); + void setWidgetXAxisScale(int id, double xMin, double xMax); + void setWidgetYAxisScale(int id, double yMin, double yMax); + void setWidgetXAxisAutoScale(int id, bool on); + void setWidgetYAxisAutoScale(int id, bool on); + void setWidgetXAxisRange(double xMin, double xMax); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + void setData(ComplexDataEvent* e); + Lineplot* i_; //In-phase plot + Lineplot* q_; //Quadrature plot + Lineplot* m_; //Magnitude plot + Lineplot* p_; //Phase plot + + struct opReal{double operator()(std::complex i) const{return real(i);}}; + struct opImag{double operator()(std::complex i) const{return imag(i);}}; + struct opAbs{double operator()(std::complex i) const{return abs(i);}}; + struct opArg{double operator()(std::complex i) const{return arg(i);}}; + + double* iData_; + double* qData_; + double* mData_; + double* pData_; + int numPoints_; + int timerId_; + bool haveNewData_; +}; + +#endif // COMPLEXWIDGET_H diff --git a/graphics/lib/complexplot/Complexplot.cpp b/graphics/lib/complexplot/Complexplot.cpp new file mode 100644 index 000000000..6e520214d --- /dev/null +++ b/graphics/lib/complexplot/Complexplot.cpp @@ -0,0 +1,55 @@ +#include "Complexplot.h" +#include "ComplexplotWrapper.h" + +using namespace std; + +Complexplot::Complexplot() +{ + plot_ = new ComplexplotWrapper; +} + +Complexplot::~Complexplot() +{ + delete plot_; +} + +void Complexplot::setNewData(complex* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Complexplot::setNewData(complex* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Complexplot::setTitle(std::string title) +{ + plot_->setTitle(title); +} + +void Complexplot::setXAxisAutoScale(PlotId id, bool on=true) +{ + plot_->setXAxisAutoScale(id, on); +} + +void Complexplot::setYAxisAutoScale(PlotId id, bool on=true) +{ + plot_->setYAxisAutoScale(id, on); +} + +void Complexplot::setXAxisScale(PlotId id, double xMin, double xMax) +{ + plot_->setXAxisScale(id, xMin, xMax); +} + +void Complexplot::setYAxisScale(PlotId id, double yMin, double yMax) +{ + plot_->setYAxisScale(id, yMin, yMax); +} + +void Complexplot::setXAxisRange(double xMin, double xMax) +{ + plot_->setXAxisRange(xMin, xMax); +} + diff --git a/graphics/lib/complexplot/Complexplot.h b/graphics/lib/complexplot/Complexplot.h new file mode 100644 index 000000000..19c1ccee0 --- /dev/null +++ b/graphics/lib/complexplot/Complexplot.h @@ -0,0 +1,53 @@ +#ifndef COMPLEXPLOT_H +#define COMPLEXPLOT_H + +#include +#include + +class ComplexplotWrapper; + +class Complexplot +{ +public: + enum PlotId + { + I, + Q, + Magnitude, + Phase + }; + Complexplot(); + ~Complexplot(); + + template + void setNewData(Iterator begin, Iterator end); + void setNewData(std::complex* data, int numPoints); + void setNewData(std::complex* data, int numPoints); + void setTitle(std::string title); + void setXAxisAutoScale(PlotId id, bool on); + void setYAxisAutoScale(PlotId id, bool on); + void setXAxisScale(PlotId id, double xMin, double xMax); + void setYAxisScale(PlotId id, double yMin, double yMax); + void setXAxisRange(double xMin, double xMax); + +private: + ComplexplotWrapper* plot_; +}; + +template +void Complexplot::setNewData(Iterator begin, Iterator end) +{ + int numPoints = end-begin; + std::complex* data = new std::complex[numPoints]; + + for(int i=0;begin!=end;begin++,i++) + { + data[i] = *begin; + } + + setNewData(data, numPoints); + + delete[] data; +} + +#endif // COMPLEXPLOT_H diff --git a/graphics/lib/complexplot/ComplexplotWrapper.cpp b/graphics/lib/complexplot/ComplexplotWrapper.cpp new file mode 100644 index 000000000..abfc819c2 --- /dev/null +++ b/graphics/lib/complexplot/ComplexplotWrapper.cpp @@ -0,0 +1,141 @@ +#include "ComplexplotWrapper.h" + +#include "ComplexWidget.h" +#include "Events.h" +#include +#include + +using namespace std; + + +ComplexplotWrapper::ComplexplotWrapper() + :widget_(NULL) + ,destroyed_(true) +{ + if(QCoreApplication::instance() == NULL) + return; //TODO: throw exception here in Iris + if(QCoreApplication::instance()->thread() == QThread::currentThread()) + { + connect( this, SIGNAL( createWidgetSignal() ), + this, SLOT(createWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()) ); + } + else + { + connect( this, SIGNAL( createWidgetSignal() ), + this, SLOT(createWidgetSlot()), + Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()), + Qt::BlockingQueuedConnection ); + moveToThread(QCoreApplication::instance()->thread()); + } + emit createWidgetSignal(); +} + +ComplexplotWrapper::~ComplexplotWrapper() +{ + if(destroyed_) + emit destroyWidgetSignal(); + else + emit destroyWidgetSignalBlocking(); +} + +void ComplexplotWrapper::createWidgetSlot() +{ + widget_ = new ComplexWidget; + destroyed_ = false; + widget_->setAttribute(Qt::WA_DeleteOnClose, true); + connect(widget_, SIGNAL( destroyed() ), + this, SLOT( widgetDestroyed() )); + connect(this, SIGNAL(setWidgetTitle(QString)), + widget_, SLOT(setWidgetTitle(QString))); + connect(this, SIGNAL(setWidgetXAxisScale(int,double,double)), + widget_, SLOT(setWidgetXAxisScale(int,double,double))); + connect(this, SIGNAL(setWidgetYAxisScale(int,double,double)), + widget_, SLOT(setWidgetYAxisScale(int,double,double))); + connect(this, SIGNAL(setWidgetXAxisAutoScale(int,bool)), + widget_, SLOT(setWidgetXAxisAutoScale(int,bool))); + connect(this, SIGNAL(setWidgetYAxisAutoScale(int,bool)), + widget_, SLOT(setWidgetYAxisAutoScale(int,bool))); + connect(this, SIGNAL(setWidgetXAxisRange(double,double)), + widget_, SLOT(setWidgetXAxisRange(double,double))); + + widget_->resize( 800, 600 ); + widget_->show(); +} + +void ComplexplotWrapper::destroyWidgetSlot() +{ + if(widget_) + delete widget_; + widget_ = NULL; +} + +void ComplexplotWrapper::widgetDestroyed() +{ + destroyed_ = true; +} + +void ComplexplotWrapper::setNewData(complex* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new ComplexDataEvent(data, numPoints)); +} + +void ComplexplotWrapper::setNewData(complex* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new ComplexDataEvent(data, numPoints)); +} + +void ComplexplotWrapper::setTitle(std::string title) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(title.c_str()); + emit setWidgetTitle(str); +} + +void ComplexplotWrapper::setXAxisAutoScale(int id, bool on=true) +{ + if(destroyed_) + return; + emit setWidgetXAxisAutoScale(id, on); +} + +void ComplexplotWrapper::setYAxisAutoScale(int id, bool on=true) +{ + if(destroyed_) + return; + emit setWidgetYAxisAutoScale(id, on); +} + +void ComplexplotWrapper::setXAxisScale(int id, double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetXAxisScale(id, xMin, xMax); +} + +void ComplexplotWrapper::setYAxisScale(int id, double yMin, double yMax) +{ + if(destroyed_) + return; + emit setWidgetYAxisScale(id, yMin, yMax); +} + +void ComplexplotWrapper::setXAxisRange(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetXAxisRange(xMin, xMax); +} + diff --git a/graphics/lib/complexplot/ComplexplotWrapper.h b/graphics/lib/complexplot/ComplexplotWrapper.h new file mode 100644 index 000000000..2d64c1b11 --- /dev/null +++ b/graphics/lib/complexplot/ComplexplotWrapper.h @@ -0,0 +1,48 @@ +#ifndef COMPLEXPLOTWRAPPER_H +#define COMPLEXPLOTWRAPPER_H + +#include +#include + +class ComplexWidget; + +class ComplexplotWrapper + : QObject +{ + Q_OBJECT + +public: + ComplexplotWrapper(); + ~ComplexplotWrapper(); + + void setNewData(std::complex* data, int numPoints); + void setNewData(std::complex* data, int numPoints); + void setTitle(std::string title); + void setXAxisAutoScale(int id, bool on); + void setYAxisAutoScale(int id, bool on); + void setXAxisScale(int id, double xMin, double xMax); + void setYAxisScale(int id, double yMin, double yMax); + void setXAxisRange(double xMin, double xMax); + +public slots: + void createWidgetSlot(); + void destroyWidgetSlot(); + void widgetDestroyed(); + +signals: + void createWidgetSignal(); + void destroyWidgetSignal(); + void destroyWidgetSignalBlocking(); + void setWidgetTitle(QString title); + void setWidgetXAxisAutoScale(int id, bool on); + void setWidgetYAxisAutoScale(int id, bool on); + void setWidgetXAxisScale(int id, double xMin, double xMax); + void setWidgetYAxisScale(int id, double yMin, double yMax); + void setWidgetXAxisRange(double xMin, double xMax); + +private: + ComplexWidget* widget_; + bool destroyed_; +}; + +#endif // COMPLEXPLOTWRAPPER_H diff --git a/graphics/lib/complexplot/plot_complex.cpp b/graphics/lib/complexplot/plot_complex.cpp new file mode 100644 index 000000000..8fca481aa --- /dev/null +++ b/graphics/lib/complexplot/plot_complex.cpp @@ -0,0 +1,76 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + + +#include "plot/plot_complex.h" +#include "Complexplot.h" +#include + + +int plot_complex_init(plot_complex_t *h) { + *h = (void*) new Complexplot(); + return (*h != NULL)?0:-1; +} + +void plot_complex_setTitle(plot_complex_t *h, char *title) { + Complexplot *plot = static_cast(*h); + plot->setTitle(title); +} + +void plot_complex_setNewData(plot_complex_t *h, _Complex float *data, + int num_points) { + Complexplot *plot = static_cast(*h); + plot->setNewData(reinterpret_cast*> (data), num_points); +} + + +void plot_complex_setXAxisAutoScale(plot_complex_t *h, plot_complex_id_t id, bool on) { + Complexplot *plot = static_cast(*h); + plot->setXAxisAutoScale(static_cast (id), on); +} + +void plot_complex_setYAxisAutoScale(plot_complex_t *h, plot_complex_id_t id, bool on) { + Complexplot *plot = static_cast(*h); + plot->setYAxisAutoScale(static_cast (id), on); +} + +void plot_complex_setXAxisScale(plot_complex_t *h, plot_complex_id_t id, double xMin, double xMax) { + Complexplot *plot = static_cast(*h); + plot->setXAxisScale(static_cast (id), xMin, xMax); +} + +void plot_complex_setYAxisScale(plot_complex_t *h, plot_complex_id_t id, double yMin, double yMax) { + Complexplot *plot = static_cast(*h); + plot->setYAxisScale(static_cast (id), yMin, yMax); +} + +void plot_complex_setXAxisRange(plot_complex_t *h, double xMin, double xMax) { + Complexplot *plot = static_cast(*h); + plot->setXAxisRange(xMin, xMax); +} diff --git a/graphics/lib/complexplot/test/CMakeLists.txt b/graphics/lib/complexplot/test/CMakeLists.txt new file mode 100644 index 000000000..d2fe42662 --- /dev/null +++ b/graphics/lib/complexplot/test/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Build tests +######################################################################## +#turn the test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +INCLUDE_DIRECTORIES(..) +ADD_EXECUTABLE(complexplot_test complexplot_test.cpp) +TARGET_LINK_LIBRARIES(complexplot_test ${Boost_LIBRARIES} graphics) +ADD_TEST(complexplot_test complexplot_test) + diff --git a/graphics/lib/complexplot/test/complexplot_test.cpp b/graphics/lib/complexplot/test/complexplot_test.cpp new file mode 100644 index 000000000..3976cafce --- /dev/null +++ b/graphics/lib/complexplot/test/complexplot_test.cpp @@ -0,0 +1,146 @@ +/** + * \file lib/generic/modulation/Crc_test.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Main test file for Complexplot class. + */ + +#define BOOST_TEST_MODULE Complexplot_Test + +#include "Complexplot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PI 3.14159265358979323846 + +using namespace std; + +typedef vector< complex > FloatVec; + +void threadMain1() +{ + Complexplot plot; + plot.setTitle("Float"); + plot.setXAxisRange(0,2); + plot.setYAxisScale(Complexplot::Magnitude, 0.9, 1.1); + + int n=1024; + float step = 2.0*PI/n; + complex* data = new complex[n]; + for(int i=0;i* data = new complex[n]; + for(int i=0;i("Compleplot_Basic_Test"), NULL }; + QApplication a(argc, argv); + + boost::scoped_ptr< boost::thread > thread1_; + boost::scoped_ptr< boost::thread > thread2_; + boost::scoped_ptr< boost::thread > thread3_; + + thread1_.reset( new boost::thread( &threadMain1 ) ); + thread2_.reset( new boost::thread( &threadMain2 ) ); + thread3_.reset( new boost::thread( &threadMain3 ) ); + + qApp->exec(); + thread1_->join(); + thread2_->join(); + thread3_->join(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/graphics/lib/realplot/RealWidget.cpp b/graphics/lib/realplot/RealWidget.cpp new file mode 100644 index 000000000..ce53fa7fa --- /dev/null +++ b/graphics/lib/realplot/RealWidget.cpp @@ -0,0 +1,102 @@ +#include "RealWidget.h" +#include "Lineplot.h" +#include "Events.h" + +#include +#include +#include + +using namespace std; +namespace bl = boost::lambda; + +RealWidget::RealWidget(QWidget *parent) + :QWidget(parent) +{ + l_ = new Lineplot(); + QVBoxLayout* vLayout1 = new QVBoxLayout(this); + vLayout1->addWidget(l_); + + numPoints_ = 16; + dataPoints_ = new double[numPoints_]; + timerId_ = startTimer(10); + haveNewData_ = false; +} + +RealWidget::~RealWidget() +{ + delete l_; +} + +void RealWidget::customEvent( QEvent * e ) +{ + if(e->type() == RealDataEvent::type) + { + RealDataEvent* dataEvent = (RealDataEvent*)e; + setData(dataEvent); + } +} + +void RealWidget::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == timerId_) + { + if(haveNewData_) + { + l_->replot(); + haveNewData_ = false; + } + return; + } + QWidget::timerEvent(event); +} + +void RealWidget::setData(RealDataEvent* e) +{ + if(e->numPoints_ != numPoints_) + { + numPoints_ = e->numPoints_; + delete [] dataPoints_; + dataPoints_ = new double[numPoints_]; + } + for(int i=0;idataPoints_[i]; + + l_->setData(dataPoints_, numPoints_); + haveNewData_ = true; +} + +void RealWidget::setWidgetTitle(QString title) +{ + l_->setTitle(title); +} + +void RealWidget::setWidgetAxisLabels(QString xLabel, QString yLabel) +{ + l_->setAxisTitle(QwtPlot::xBottom, xLabel); + l_->setAxisTitle(QwtPlot::yLeft, yLabel); +} + +void RealWidget::setWidgetXAxisScale(double xMin, double xMax) +{ + l_->setAxisScale(QwtPlot::xBottom, xMin, xMax); +} + +void RealWidget::setWidgetYAxisScale(double yMin, double yMax) +{ + l_->setAxisScale(QwtPlot::yLeft, yMin, yMax); +} + +void RealWidget::setWidgetXAxisAutoScale(bool on=true) +{ + l_->setAxisAutoScale(QwtPlot::xBottom, on); +} + +void RealWidget::setWidgetYAxisAutoScale(bool on=true) +{ + l_->setAxisAutoScale(QwtPlot::yLeft, on); +} + +void RealWidget::setWidgetXAxisRange(double xMin, double xMax) +{ + l_->setXAxisRange(xMin, xMax); +} diff --git a/graphics/lib/realplot/RealWidget.h b/graphics/lib/realplot/RealWidget.h new file mode 100644 index 000000000..b03183f20 --- /dev/null +++ b/graphics/lib/realplot/RealWidget.h @@ -0,0 +1,43 @@ +#ifndef REALWIDGET_H +#define REALWIDGET_H + +#include +#include +#include + +class RealDataEvent; +class Lineplot; + +class RealWidget + : public QWidget +{ + Q_OBJECT + +public: + RealWidget(QWidget* parent = 0); + virtual ~RealWidget(); + +public slots: + void customEvent( QEvent * e ); + void setWidgetTitle(QString title); + void setWidgetAxisLabels(QString xLabel, QString yLabel); + void setWidgetXAxisScale(double xMin, double xMax); + void setWidgetYAxisScale(double yMin, double yMax); + void setWidgetXAxisAutoScale(bool on); + void setWidgetYAxisAutoScale(bool on); + void setWidgetXAxisRange(double xMin, double xMax); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + void setData(RealDataEvent* e); + Lineplot* l_; //The line plot + + double* dataPoints_; + int numPoints_; + int timerId_; + bool haveNewData_; +}; + +#endif // REALWIDGET_H diff --git a/graphics/lib/realplot/Realplot.cpp b/graphics/lib/realplot/Realplot.cpp new file mode 100644 index 000000000..561f71278 --- /dev/null +++ b/graphics/lib/realplot/Realplot.cpp @@ -0,0 +1,58 @@ +#include "Realplot.h" +#include "RealplotWrapper.h" + +Realplot::Realplot() +{ + plot_ = new RealplotWrapper; +} + +Realplot::~Realplot() +{ + delete plot_; +} + +void Realplot::setNewData(double* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Realplot::setNewData(float* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Realplot::setTitle(std::string title) +{ + plot_->setTitle(title); +} + +void Realplot::setXAxisScale(double xMin, double xMax) +{ + plot_->setXAxisScale(xMin, xMax); +} + +void Realplot::setYAxisScale(double yMin, double yMax) +{ + plot_->setYAxisScale(yMin, yMax); +} + +void Realplot::setXAxisAutoScale(bool on=true) +{ + plot_->setXAxisAutoScale(on); +} + +void Realplot::setYAxisAutoScale(bool on=true) +{ + plot_->setYAxisAutoScale(on); +} + +void Realplot::setXAxisRange(double xMin, double xMax) +{ + plot_->setXAxisRange(xMin, xMax); +} + +void Realplot::setLabels(std::string xLabel, std::string yLabel) +{ + plot_->setAxisLabels(xLabel, yLabel); +} + diff --git a/graphics/lib/realplot/Realplot.h b/graphics/lib/realplot/Realplot.h new file mode 100644 index 000000000..f495cd3c3 --- /dev/null +++ b/graphics/lib/realplot/Realplot.h @@ -0,0 +1,46 @@ +#ifndef REALPLOT_H +#define REALPLOT_H + +#include + +class RealplotWrapper; + +class Realplot +{ +public: + Realplot(); + ~Realplot(); + + template + void setNewData(Iterator begin, Iterator end); + void setNewData(float* data, int numPoints); + void setNewData(double* data, int numPoints); + void setTitle(std::string title); + void setXAxisScale(double xMin, double xMax); + void setYAxisScale(double yMin, double yMax); + void setXAxisAutoScale(bool on); + void setYAxisAutoScale(bool on); + void setXAxisRange(double xMin, double xMax); + void setLabels(std::string xLabel, std::string yLabel); + +private: + RealplotWrapper* plot_; +}; + +template +void Realplot::setNewData(Iterator begin, Iterator end) +{ + int numPoints = end-begin; + double* data = new double[numPoints]; + + for(int i=0;begin!=end;begin++,i++) + { + data[i] = *begin; + } + + setNewData(data, numPoints); + + delete[] data; +} + +#endif // REALPLOT_H diff --git a/graphics/lib/realplot/RealplotWrapper.cpp b/graphics/lib/realplot/RealplotWrapper.cpp new file mode 100644 index 000000000..975987621 --- /dev/null +++ b/graphics/lib/realplot/RealplotWrapper.cpp @@ -0,0 +1,147 @@ +#include "RealplotWrapper.h" + +#include "RealWidget.h" +#include "Events.h" +#include +#include + + +RealplotWrapper::RealplotWrapper() + :widget_(NULL) + ,destroyed_(true) +{ + if(QCoreApplication::instance() == NULL) + return; //TODO: throw exception here in Iris + if(QCoreApplication::instance()->thread() == QThread::currentThread()) + { + connect( this, SIGNAL( createWidgetSignal() ), + this, SLOT(createWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()) ); + } + else + { + connect( this, SIGNAL( createWidgetSignal() ), + this, SLOT(createWidgetSlot()), + Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()), + Qt::BlockingQueuedConnection ); + moveToThread(QCoreApplication::instance()->thread()); + } + emit createWidgetSignal(); +} + +RealplotWrapper::~RealplotWrapper() +{ + if(destroyed_) + emit destroyWidgetSignal(); + else + emit destroyWidgetSignalBlocking(); +} + +void RealplotWrapper::createWidgetSlot() +{ + widget_ = new RealWidget; + destroyed_ = false; + widget_->setAttribute(Qt::WA_DeleteOnClose, true); + connect(widget_, SIGNAL( destroyed() ), + this, SLOT( widgetDestroyed() )); + connect(this, SIGNAL(setWidgetTitle(QString)), + widget_, SLOT(setWidgetTitle(QString))); + connect(this, SIGNAL(setWidgetAxisLabels(QString, QString)), + widget_, SLOT(setWidgetAxisLabels(QString, QString))); + connect(this, SIGNAL(setWidgetXAxisScale(double,double)), + widget_, SLOT(setWidgetXAxisScale(double,double))); + connect(this, SIGNAL(setWidgetYAxisScale(double,double)), + widget_, SLOT(setWidgetYAxisScale(double,double))); + connect(this, SIGNAL(setWidgetXAxisAutoScale(bool)), + widget_, SLOT(setWidgetXAxisAutoScale(bool))); + connect(this, SIGNAL(setWidgetYAxisAutoScale(bool)), + widget_, SLOT(setWidgetYAxisAutoScale(bool))); + connect(this, SIGNAL(setWidgetXAxisRange(double,double)), + widget_, SLOT(setWidgetXAxisRange(double,double))); + + widget_->resize( 800, 600 ); + widget_->show(); +} + +void RealplotWrapper::destroyWidgetSlot() +{ + delete widget_; +} + +void RealplotWrapper::widgetDestroyed() +{ + destroyed_ = true; +} + +void RealplotWrapper::setNewData(double* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new RealDataEvent(data, numPoints)); +} + +void RealplotWrapper::setNewData(float* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new RealDataEvent(data, numPoints)); +} + +void RealplotWrapper::setTitle(std::string title) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(title.c_str()); + emit setWidgetTitle(str); +} + +void RealplotWrapper::setAxisLabels(std::string xLabel, std::string yLabel) +{ + if(destroyed_) + return; + QString xStr = QString::fromUtf8(xLabel.c_str()); + QString yStr = QString::fromUtf8(yLabel.c_str()); + emit setWidgetAxisLabels(xStr, yStr); +} + +void RealplotWrapper::setXAxisScale(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetXAxisScale(xMin, xMax); +} + +void RealplotWrapper::setYAxisScale(double yMin, double yMax) +{ + if(destroyed_) + return; + emit setWidgetYAxisScale(yMin, yMax); +} + +void RealplotWrapper::setXAxisAutoScale(bool on=true) +{ + if(destroyed_) + return; + emit setWidgetXAxisAutoScale(on); +} + +void RealplotWrapper::setYAxisAutoScale(bool on=true) +{ + if(destroyed_) + return; + emit setWidgetYAxisAutoScale(on); +} + +void RealplotWrapper::setXAxisRange(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetXAxisRange(xMin, xMax); +} diff --git a/graphics/lib/realplot/RealplotWrapper.h b/graphics/lib/realplot/RealplotWrapper.h new file mode 100644 index 000000000..b5750c609 --- /dev/null +++ b/graphics/lib/realplot/RealplotWrapper.h @@ -0,0 +1,49 @@ +#ifndef REALPLOTWRAPPER_H +#define REALPLOTWRAPPER_H + +#include + +class RealWidget; + +class RealplotWrapper + : QObject +{ + Q_OBJECT + +public: + RealplotWrapper(); + ~RealplotWrapper(); + + void setNewData(float* data, int numPoints); + void setNewData(double* data, int numPoints); + void setTitle(std::string title); + void setAxisLabels(std::string xLabel, std::string yLabel); + void setXAxisScale(double xMin, double xMax); + void setYAxisScale(double yMin, double yMax); + void setXAxisAutoScale(bool on); + void setYAxisAutoScale(bool on); + void setXAxisRange(double xMin, double xMax); + +public slots: + void createWidgetSlot(); + void destroyWidgetSlot(); + void widgetDestroyed(); + +signals: + void createWidgetSignal(); + void destroyWidgetSignal(); + void destroyWidgetSignalBlocking(); + void setWidgetTitle(QString title); + void setWidgetAxisLabels(QString xLabel, QString yLabel); + void setWidgetXAxisScale(double xMin, double xMax); + void setWidgetYAxisScale(double yMin, double yMax); + void setWidgetXAxisAutoScale(bool on); + void setWidgetYAxisAutoScale(bool on); + void setWidgetXAxisRange(double xMin, double xMax); + +private: + RealWidget* widget_; + bool destroyed_; +}; + +#endif // REALPLOTWRAPPER_H diff --git a/graphics/lib/realplot/plot_real.cpp b/graphics/lib/realplot/plot_real.cpp new file mode 100644 index 000000000..92fd745d3 --- /dev/null +++ b/graphics/lib/realplot/plot_real.cpp @@ -0,0 +1,79 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#include "plot/plot_real.h" +#include "Realplot.h" +#include + + +int plot_real_init(plot_real_t *h) { + *h = (void*) new Realplot(); + return (*h != NULL)?0:-1; +} + +void plot_real_setTitle(plot_real_t *h, char *title) { + Realplot *plot = static_cast(*h); + plot->setTitle(title); +} +void plot_real_setNewData(plot_real_t *h, float *data, + int num_points) { + Realplot *plot = static_cast(*h); + plot->setNewData(data, num_points); + +} + +void plot_real_setXAxisAutoScale(plot_real_t *h, bool on) { + Realplot *plot = static_cast(*h); + plot->setXAxisAutoScale(on); +} + +void plot_real_setYAxisAutoScale(plot_real_t *h, bool on) { + Realplot *plot = static_cast(*h); + plot->setYAxisAutoScale(on); +} + +void plot_real_setXAxisScale(plot_real_t *h, double xMin, double xMax) { + Realplot *plot = static_cast(*h); + plot->setXAxisScale(xMin, xMax); +} + +void plot_real_setYAxisScale(plot_real_t *h, double yMin, double yMax) { + Realplot *plot = static_cast(*h); + plot->setYAxisScale(yMin, yMax); +} + +void plot_real_setXAxisRange(plot_real_t *h, double xMin, double xMax) { + Realplot *plot = static_cast(*h); + plot->setXAxisRange(xMin, xMax); +} + +void plot_real_setLabels(plot_real_t *h, char *xLabel, char *yLabel) { + Realplot *plot = static_cast(*h); + plot->setLabels(xLabel, yLabel); +} diff --git a/graphics/lib/realplot/test/CMakeLists.txt b/graphics/lib/realplot/test/CMakeLists.txt new file mode 100644 index 000000000..a5f5ba322 --- /dev/null +++ b/graphics/lib/realplot/test/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Build tests +######################################################################## +#turn the test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +INCLUDE_DIRECTORIES(..) +ADD_EXECUTABLE(realplot_test realplot_test.cpp) +TARGET_LINK_LIBRARIES(realplot_test ${Boost_LIBRARIES} graphics) +ADD_TEST(realplot_test realplot_test) + diff --git a/graphics/lib/realplot/test/realplot_test.cpp b/graphics/lib/realplot/test/realplot_test.cpp new file mode 100644 index 000000000..c2f4dd9f7 --- /dev/null +++ b/graphics/lib/realplot/test/realplot_test.cpp @@ -0,0 +1,130 @@ +/** + * \file lib/generic/modulation/Crc_test.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Main test file for Realplot class. + */ + +#define BOOST_TEST_MODULE Realplot_Test + +#include "Realplot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef std::vector FloatVec; + +template +void getPoints(T* data, int numPoints) +{ + for(int i=0;i +void getPoints(Iterator begin, Iterator end) +{ + for(;begin!=end;begin++) + { + *begin = 10*((double)rand()/RAND_MAX); + } +} + +void threadMain1() +{ + Realplot plot; + + float data[1024]; + + for(int i=0;i<10;i++) + { + getPoints(data, 504); + plot.setNewData(data, 504); + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } +} + +void threadMain2() +{ + Realplot plot; + double data[1024]; + + for(int i=0;i<10000;i++) + { + getPoints(data, 504); + plot.setNewData(data, 504); + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } +} + +void threadMain3() +{ + Realplot plot; + FloatVec v(1024); + + for(int i=0;i<10000;i++) + { + getPoints(v.begin(), v.end()); + plot.setNewData(v.begin(), v.end()); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } +} + +BOOST_AUTO_TEST_SUITE (Realplot_Test) + +BOOST_AUTO_TEST_CASE(Realplot_Basic_Test) +{ + int argc = 1; + char* argv[] = { const_cast("Realplot_Basic_Test"), NULL }; + QApplication a(argc, argv); + + boost::scoped_ptr< boost::thread > thread1_; + boost::scoped_ptr< boost::thread > thread2_; + boost::scoped_ptr< boost::thread > thread3_; + + thread1_.reset( new boost::thread( &threadMain1 ) ); + thread2_.reset( new boost::thread( &threadMain2 ) ); + thread3_.reset( new boost::thread( &threadMain3 ) ); + + qApp->exec(); + thread1_->join(); + thread2_->join(); + thread3_->join(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/graphics/lib/scatterplot/ScatterWidget.cpp b/graphics/lib/scatterplot/ScatterWidget.cpp new file mode 100644 index 000000000..268bf49a4 --- /dev/null +++ b/graphics/lib/scatterplot/ScatterWidget.cpp @@ -0,0 +1,101 @@ +#include "ScatterWidget.h" +#include "Pointplot.h" +#include "Events.h" + +#include +#include +#include + +using namespace std; + +ScatterWidget::ScatterWidget(QWidget *parent) + :QWidget(parent) +{ + plot_ = new Pointplot(); + QVBoxLayout* vLayout1 = new QVBoxLayout(this); + vLayout1->addWidget(plot_); + + numPoints_ = 16; + iData_ = new double[numPoints_]; + qData_ = new double[numPoints_]; + timerId_ = startTimer(10); + haveNewData_ = false; +} + +ScatterWidget::~ScatterWidget() +{ + delete iData_; + delete qData_; +} + +void ScatterWidget::customEvent( QEvent * e ) +{ + if(e->type() == ComplexDataEvent::type) + { + ComplexDataEvent* dataEvent = (ComplexDataEvent*)e; + setData(dataEvent); + } +} + +void ScatterWidget::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == timerId_) + { + if(haveNewData_) + { + plot_->replot(); + haveNewData_ = false; + } + return; + } + QWidget::timerEvent(event); +} + +void ScatterWidget::setData(ComplexDataEvent* e) +{ + if(e->numPoints_ != numPoints_) + { + numPoints_ = e->numPoints_; + delete [] iData_; + delete [] qData_; + iData_ = new double[numPoints_]; + qData_ = new double[numPoints_]; + } + + transform(e->dataPoints_, &e->dataPoints_[numPoints_], iData_, opReal()); + transform(e->dataPoints_, &e->dataPoints_[numPoints_], qData_, opImag()); + + plot_->setData(iData_, qData_, numPoints_); + haveNewData_ = true; +} + +void ScatterWidget::setWidgetTitle(QString title) +{ + plot_->setTitle(title); +} + +void ScatterWidget::setWidgetAxisLabels(QString xLabel, QString yLabel) +{ + plot_->setAxisTitle(QwtPlot::xBottom, xLabel); + plot_->setAxisTitle(QwtPlot::yLeft, yLabel); +} + +void ScatterWidget::setWidgetXAxisScale(double xMin, double xMax) +{ + plot_->setAxisScale(QwtPlot::xBottom, xMin, xMax); +} + +void ScatterWidget::setWidgetYAxisScale(double yMin, double yMax) +{ + plot_->setAxisScale(QwtPlot::yLeft, yMin, yMax); +} + +void ScatterWidget::setWidgetXAxisAutoScale(bool on=true) +{ + plot_->setAxisAutoScale(QwtPlot::xBottom, on); +} + +void ScatterWidget::setWidgetYAxisAutoScale(bool on) +{ + plot_->setAxisAutoScale(QwtPlot::yLeft, on); +} diff --git a/graphics/lib/scatterplot/ScatterWidget.h b/graphics/lib/scatterplot/ScatterWidget.h new file mode 100644 index 000000000..1acc7904c --- /dev/null +++ b/graphics/lib/scatterplot/ScatterWidget.h @@ -0,0 +1,46 @@ +#ifndef SCATTERWIDGET_H +#define SCATTERWIDGET_H + +#include +#include +#include + +class ComplexDataEvent; +class Pointplot; + +class ScatterWidget + : public QWidget +{ + Q_OBJECT + +public: + ScatterWidget(QWidget* parent = 0); + virtual ~ScatterWidget(); + +public slots: + void customEvent( QEvent * e ); + void setWidgetTitle(QString title); + void setWidgetAxisLabels(QString xLabel, QString yLabel); + void setWidgetXAxisScale(double xMin, double xMax); + void setWidgetYAxisScale(double yMin, double yMax); + void setWidgetXAxisAutoScale(bool on); + void setWidgetYAxisAutoScale(bool on); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + void setData(ComplexDataEvent* e); + Pointplot* plot_; + + struct opReal{double operator()(std::complex i) const{return real(i);}}; + struct opImag{double operator()(std::complex i) const{return imag(i);}}; + + double* iData_; + double* qData_; + int numPoints_; + int timerId_; + bool haveNewData_; +}; + +#endif // SCATTERWIDGET_H diff --git a/graphics/lib/scatterplot/Scatterplot.cpp b/graphics/lib/scatterplot/Scatterplot.cpp new file mode 100644 index 000000000..343435cc8 --- /dev/null +++ b/graphics/lib/scatterplot/Scatterplot.cpp @@ -0,0 +1,53 @@ +#include "Scatterplot.h" +#include "ScatterplotWrapper.h" + +Scatterplot::Scatterplot() +{ + plot_ = new ScatterplotWrapper; +} + +Scatterplot::~Scatterplot() +{ + delete plot_; +} + +void Scatterplot::setNewData(std::complex* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Scatterplot::setNewData(std::complex* data, int numPoints) +{ + plot_->setNewData(data, numPoints); +} + +void Scatterplot::setTitle(std::string title) +{ + plot_->setTitle(title); +} + +void Scatterplot::setXAxisScale(double xMin, double xMax) +{ + plot_->setXAxisScale(xMin, xMax); +} + +void Scatterplot::setYAxisScale(double yMin, double yMax) +{ + plot_->setYAxisScale(yMin, yMax); +} + +void Scatterplot::setXAxisAutoScale(bool on=true) +{ + plot_->setXAxisAutoScale(on); +} + +void Scatterplot::setYAxisAutoScale(bool on=true) +{ + plot_->setYAxisAutoScale(on); +} + +void Scatterplot::setAxisLabels(std::string xLabel, std::string yLabel) +{ + plot_->setAxisLabels(xLabel, yLabel); +} + diff --git a/graphics/lib/scatterplot/Scatterplot.h b/graphics/lib/scatterplot/Scatterplot.h new file mode 100644 index 000000000..19b340c43 --- /dev/null +++ b/graphics/lib/scatterplot/Scatterplot.h @@ -0,0 +1,46 @@ +#ifndef SCATTERPLOT_H +#define SCATTERPLOT_H + +#include +#include + +class ScatterplotWrapper; + +class Scatterplot +{ +public: + Scatterplot(); + ~Scatterplot(); + + template + void setNewData(Iterator begin, Iterator end); + void setNewData(std::complex* data, int numPoints); + void setNewData(std::complex* data, int numPoints); + void setTitle(std::string title); + void setXAxisScale(double xMin, double xMax); + void setYAxisScale(double yMin, double yMax); + void setXAxisAutoScale(bool on); + void setYAxisAutoScale(bool on); + void setAxisLabels(std::string xLabel, std::string yLabel); + +private: + ScatterplotWrapper* plot_; +}; + +template +void Scatterplot::setNewData(Iterator begin, Iterator end) +{ + int numPoints = end-begin; + std::complex* data = new std::complex[numPoints]; + + for(int i=0;begin!=end;begin++,i++) + { + data[i] = *begin; + } + + setNewData(data, numPoints); + + delete[] data; +} + +#endif // SCATTERPLOT_H diff --git a/graphics/lib/scatterplot/ScatterplotWrapper.cpp b/graphics/lib/scatterplot/ScatterplotWrapper.cpp new file mode 100644 index 000000000..35e7b8238 --- /dev/null +++ b/graphics/lib/scatterplot/ScatterplotWrapper.cpp @@ -0,0 +1,142 @@ +#include "ScatterplotWrapper.h" + +#include "ScatterWidget.h" +#include "Events.h" +#include +#include +#include + +using namespace std; + + +ScatterplotWrapper::ScatterplotWrapper() + :widget_(NULL) + ,destroyed_(true) +{ + if(QCoreApplication::instance() == NULL) + return; //TODO: throw exception here in Iris + if(QCoreApplication::instance()->thread() == QThread::currentThread()) + { + connect( this, SIGNAL(createWidgetSignal()), + this, SLOT(createWidgetSlot()) ); + connect( this, SIGNAL(destroyWidgetSignal()), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()) ); + } + else + { + connect( this, SIGNAL(createWidgetSignal()), + this, SLOT(createWidgetSlot()), + Qt::BlockingQueuedConnection ); + connect( this, SIGNAL(destroyWidgetSignal()), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()), + Qt::BlockingQueuedConnection ); + moveToThread(QCoreApplication::instance()->thread()); + } + emit createWidgetSignal(); +} + +ScatterplotWrapper::~ScatterplotWrapper() +{ + if(destroyed_) + emit destroyWidgetSignal(); + else + emit destroyWidgetSignalBlocking(); +} + +void ScatterplotWrapper::createWidgetSlot() +{ + widget_ = new ScatterWidget; + destroyed_ = false; + widget_->setAttribute(Qt::WA_DeleteOnClose, true); + connect(widget_, SIGNAL( destroyed() ), + this, SLOT( widgetDestroyed() )); + connect(this, SIGNAL(setWidgetXAxisScale(double,double)), + widget_, SLOT(setWidgetXAxisScale(double,double))); + connect(this, SIGNAL(setWidgetYAxisScale(double,double)), + widget_, SLOT(setWidgetYAxisScale(double,double))); + connect(this, SIGNAL(setWidgetXAxisAutoScale(bool)), + widget_, SLOT(setWidgetXAxisAutoScale(bool))); + connect(this, SIGNAL(setWidgetYAxisAutoScale(bool)), + widget_, SLOT(setWidgetYAxisAutoScale(bool))); + connect(this, SIGNAL(setWidgetTitle(QString)), + widget_, SLOT(setWidgetTitle(QString))); + connect(this, SIGNAL(setWidgetAxisLabels(QString, QString)), + widget_, SLOT(setWidgetAxisLabels(QString, QString))); + + widget_->resize( 800, 600 ); + widget_->show(); +} + +void ScatterplotWrapper::destroyWidgetSlot() +{ + delete widget_; +} + +void ScatterplotWrapper::widgetDestroyed() +{ + destroyed_ = true; +} + +void ScatterplotWrapper::setNewData(complex* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new ComplexDataEvent(data, numPoints)); +} + +void ScatterplotWrapper::setNewData(complex* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new ComplexDataEvent(data, numPoints)); +} + +void ScatterplotWrapper::setTitle(std::string title) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(title.c_str()); + emit setWidgetTitle(str); +} + +void ScatterplotWrapper::setXAxisScale(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetXAxisScale(xMin, xMax); +} + +void ScatterplotWrapper::setYAxisScale(double yMin, double yMax) +{ + if(destroyed_) + return; + emit setWidgetYAxisScale(yMin, yMax); +} + +void ScatterplotWrapper::setXAxisAutoScale(bool on=true) +{ + if(destroyed_) + return; + emit setWidgetXAxisAutoScale(on); +} + +void ScatterplotWrapper::setYAxisAutoScale(bool on=true) +{ + if(destroyed_) + return; + emit setWidgetYAxisAutoScale(on); +} + +void ScatterplotWrapper::setAxisLabels(std::string xLabel, std::string yLabel) +{ + if(destroyed_) + return; + QString xStr = QString::fromUtf8(xLabel.c_str()); + QString yStr = QString::fromUtf8(yLabel.c_str()); + emit setWidgetAxisLabels(xStr, yStr); +} + diff --git a/graphics/lib/scatterplot/ScatterplotWrapper.h b/graphics/lib/scatterplot/ScatterplotWrapper.h new file mode 100644 index 000000000..e8b72a936 --- /dev/null +++ b/graphics/lib/scatterplot/ScatterplotWrapper.h @@ -0,0 +1,48 @@ +#ifndef SCATTERPLOTWRAPPER_H +#define SCATTERPLOTWRAPPER_H + +#include +#include + +class ScatterWidget; + +class ScatterplotWrapper + : QObject +{ + Q_OBJECT + +public: + ScatterplotWrapper(); + ~ScatterplotWrapper(); + + void setNewData(std::complex* data, int numPoints); + void setNewData(std::complex* data, int numPoints); + void setTitle(std::string title); + void setXAxisScale(double xMin, double xMax); + void setYAxisScale(double yMin, double yMax); + void setXAxisAutoScale(bool on); + void setYAxisAutoScale(bool on); + void setAxisLabels(std::string xLabel, std::string yLabel); + +public slots: + void createWidgetSlot(); + void destroyWidgetSlot(); + void widgetDestroyed(); + +signals: + void createWidgetSignal(); + void destroyWidgetSignal(); + void destroyWidgetSignalBlocking(); + void setWidgetTitle(QString title); + void setWidgetAxisLabels(QString xLabel, QString yLabel); + void setWidgetXAxisScale(double xMin, double xMax); + void setWidgetYAxisScale(double yMin, double yMax); + void setWidgetXAxisAutoScale(bool on); + void setWidgetYAxisAutoScale(bool on); + +private: + ScatterWidget* widget_; + bool destroyed_; +}; + +#endif // SCATTERPLOTWRAPPER_H diff --git a/graphics/lib/scatterplot/plot_scatter.cpp b/graphics/lib/scatterplot/plot_scatter.cpp new file mode 100644 index 000000000..35624199f --- /dev/null +++ b/graphics/lib/scatterplot/plot_scatter.cpp @@ -0,0 +1,75 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#include "plot/plot_scatter.h" +#include "Scatterplot.h" +#include + + + +int plot_scatter_init(plot_scatter_t *h) { + *h = (void*) new Scatterplot(); + return (*h != NULL)?0:-1; +} + +void plot_scatter_setTitle(plot_scatter_t *h, char *title) { + Scatterplot *plot = static_cast(*h); + plot->setTitle(title); +} +void plot_scatter_setNewData(plot_scatter_t *h, _Complex float *data, + int num_points) { + Scatterplot *plot = static_cast(*h); + plot->setNewData(reinterpret_cast*> (data), num_points); + +} + +void plot_scatter_setXAxisAutoScale(plot_scatter_t *h, bool on) { + Scatterplot *plot = static_cast(*h); + plot->setXAxisAutoScale(on); +} + +void plot_scatter_setYAxisAutoScale(plot_scatter_t *h, bool on) { + Scatterplot *plot = static_cast(*h); + plot->setYAxisAutoScale(on); +} + +void plot_scatter_setXAxisScale(plot_scatter_t *h, double xMin, double xMax) { + Scatterplot *plot = static_cast(*h); + plot->setXAxisScale(xMin, xMax); +} + +void plot_scatter_setYAxisScale(plot_scatter_t *h, double yMin, double yMax) { + Scatterplot *plot = static_cast(*h); + plot->setYAxisScale(yMin, yMax); +} + +void plot_scatter_setAxisLabels(plot_scatter_t *h, char *xLabel, char *yLabel) { + Scatterplot *plot = static_cast(*h); + plot->setAxisLabels(xLabel, yLabel); +} diff --git a/graphics/lib/scatterplot/test/CMakeLists.txt b/graphics/lib/scatterplot/test/CMakeLists.txt new file mode 100644 index 000000000..432dd8847 --- /dev/null +++ b/graphics/lib/scatterplot/test/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Build tests +######################################################################## +#turn the test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +INCLUDE_DIRECTORIES(..) +ADD_EXECUTABLE(scatterplot_test scatterplot_test.cpp) +TARGET_LINK_LIBRARIES(scatterplot_test ${Boost_LIBRARIES} graphics) +ADD_TEST(scatterplot_test scatterplot_test) + diff --git a/graphics/lib/scatterplot/test/scatterplot_test.cpp b/graphics/lib/scatterplot/test/scatterplot_test.cpp new file mode 100644 index 000000000..4f1ccc6f1 --- /dev/null +++ b/graphics/lib/scatterplot/test/scatterplot_test.cpp @@ -0,0 +1,133 @@ +/** + * \file lib/generic/modulation/Crc_test.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Main test file for scatterplot class. + */ + +#define BOOST_TEST_MODULE Scatterplot_Test + +#include "Scatterplot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef std::complex Cplx; +typedef std::vector CplxVec; + +template +void getPoints(std::complex* data, int numPoints) +{ + for(int i=0;i(2*((T)rand()/RAND_MAX)-1, + 2*((T)rand()/RAND_MAX)-1); + } +} + +template +void getPoints(Iterator begin, Iterator end) +{ + for(;begin!=end;begin++) + { + float r = 2*((double)rand()/RAND_MAX)-1; + float i = 2*((double)rand()/RAND_MAX)-1; + *begin = Cplx(r,i); + } +} + +void threadMain1() +{ + Scatterplot plot; + std::complex data[1024]; + + for(int i=0;i<10;i++) + { + getPoints(data, 1024); + plot.setNewData(data, 1024); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } +} + +void threadMain2() +{ + Scatterplot plot; + std::complex data[1024]; + + for(int i=0;i<10;i++) + { + getPoints(data, 1024); + plot.setNewData(data, 1024); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } +} + +void threadMain3() +{ + Scatterplot plot; + CplxVec v(1024); + + for(int i=0;i<10;i++) + { + getPoints(v.begin(), v.end()); + plot.setNewData(v.begin(), v.end()); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } +} + +BOOST_AUTO_TEST_SUITE (Scatterplot_Test) + +BOOST_AUTO_TEST_CASE(Scatterplot_Basic_Test) +{ + int argc = 1; + char* argv[] = { const_cast("Scatterplot_Basic_Test"), NULL }; + QApplication a(argc, argv); + + boost::scoped_ptr< boost::thread > thread1_; + boost::scoped_ptr< boost::thread > thread2_; + boost::scoped_ptr< boost::thread > thread3_; + + thread1_.reset( new boost::thread( &threadMain1 ) ); + thread2_.reset( new boost::thread( &threadMain2 ) ); + thread3_.reset( new boost::thread( &threadMain3 ) ); + + qApp->exec(); + thread1_->join(); + thread2_->join(); + thread3_->join(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/graphics/lib/waterfallplot/WaterfallWidget.cpp b/graphics/lib/waterfallplot/WaterfallWidget.cpp new file mode 100644 index 000000000..a3d789bcd --- /dev/null +++ b/graphics/lib/waterfallplot/WaterfallWidget.cpp @@ -0,0 +1,143 @@ +#include "WaterfallWidget.h" +#include "Spectrogramplot.h" +#include "Lineplot.h" +#include "Events.h" + +#include +#include +#include +#include + +using namespace std; +namespace bl = boost::lambda; + + +WaterfallWidget::WaterfallWidget(int numDataPoints, int numRows, QWidget *parent) + :QWidget(parent) +{ + p_ = new Lineplot(); + s_ = new Spectrogramplot(numDataPoints, numRows); + b_ = new QPushButton("Autoscale"); + + connect(b_, SIGNAL(clicked()), this, SLOT(autoscale())); + + QVBoxLayout* vLayout1 = new QVBoxLayout(this); + vLayout1->addWidget(p_);vLayout1->setStretch(0,1); + vLayout1->addWidget(s_);vLayout1->setStretch(1,3); + vLayout1->addWidget(b_); + + numPoints_ = numDataPoints; + data_ = new double[numPoints_]; + timerId_ = startTimer(10); + haveNewData_ = false; +} + +WaterfallWidget::~WaterfallWidget() +{ + delete p_; + delete s_; +} + +void WaterfallWidget::customEvent( QEvent * e ) +{ + if(e->type() == RealDataEvent::type) + { + RealDataEvent* dataEvent = (RealDataEvent*)e; + appendData(dataEvent); + } +} + +void WaterfallWidget::setWidgetTitle(QString title) +{ + setWindowTitle(title); +} + +void WaterfallWidget::setPlotXLabel(QString xLabel) +{ + p_->setAxisTitle(QwtPlot::xBottom, xLabel); +} + +void WaterfallWidget::setPlotYLabel(QString yLabel) +{ + p_->setAxisTitle(QwtPlot::yLeft, yLabel); +} + +void WaterfallWidget::setPlotXAxisRange(double xMin, double xMax) +{ + p_->setXAxisRange(xMin, xMax); +} + +void WaterfallWidget::setPlotXAxisScale(double xMin, double xMax) +{ + p_->setAxisScale(QwtPlot::xBottom, xMin, xMax); +} + +void WaterfallWidget::setPlotYAxisScale(double yMin, double yMax) +{ + p_->setAxisScale(QwtPlot::yLeft, yMin, yMax); +} + +void WaterfallWidget::setSpectrogramXLabel(QString xLabel) +{ + s_->setAxisTitle(QwtPlot::xBottom, xLabel); +} + +void WaterfallWidget::setSpectrogramYLabel(QString yLabel) +{ + s_->setAxisTitle(QwtPlot::yLeft, yLabel); +} + +void WaterfallWidget::setSpectrogramXAxisRange(double xMin, double xMax) +{ + s_->setXAxisRange(xMin, xMax); +} + +void WaterfallWidget::setSpectrogramYAxisRange(double yMin, double yMax) +{ + s_->setYAxisRange(yMin, yMax); +} + +void WaterfallWidget::setSpectrogramZAxisScale(double zMin, double zMax) +{ + s_->setZAxisScale(zMin, zMax); +} + +void WaterfallWidget::autoscale() +{ + double min = s_->min(); + double max = s_->max(); + s_->setZAxisScale(min, max); + p_->setAxisAutoScale(QwtPlot::yLeft, false); + p_->setAxisScale(QwtPlot::yLeft, min, max); +} + +void WaterfallWidget::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == timerId_) + { + if(haveNewData_) + { + p_->replot(); + s_->replot(); + haveNewData_ = false; + } + return; + } + QWidget::timerEvent(event); +} + +void WaterfallWidget::appendData(RealDataEvent* e) +{ + if(e->numPoints_ != numPoints_) + { + numPoints_ = e->numPoints_; + delete [] data_; + data_ = new double[numPoints_]; + } + + memcpy(data_, e->dataPoints_, numPoints_*sizeof(double)); + + p_->setData(data_, numPoints_); + s_->appendData(data_, numPoints_); + haveNewData_ = true; +} diff --git a/graphics/lib/waterfallplot/WaterfallWidget.h b/graphics/lib/waterfallplot/WaterfallWidget.h new file mode 100644 index 000000000..63f2c9a71 --- /dev/null +++ b/graphics/lib/waterfallplot/WaterfallWidget.h @@ -0,0 +1,52 @@ +#ifndef WATERFALLWIDGET_H +#define WATERFALLWIDGET_H + +#include +#include +#include + +class RealDataEvent; +class Lineplot; +class Spectrogramplot; +class QPushButton; + +class WaterfallWidget + : public QWidget +{ + Q_OBJECT + +public: + WaterfallWidget(int numDataPoints, int numRows, QWidget* parent = 0); + virtual ~WaterfallWidget(); + +public slots: + void customEvent( QEvent * e ); + void setWidgetTitle(QString title); + void setPlotXLabel(QString xLabel); + void setPlotYLabel(QString yLabel); + void setPlotXAxisRange(double xMin, double xMax); + void setPlotXAxisScale(double xMin, double xMax); + void setPlotYAxisScale(double yMin, double yMax); + void setSpectrogramXLabel(QString xLabel); + void setSpectrogramYLabel(QString yLabel); + void setSpectrogramXAxisRange(double xMin, double xMax); + void setSpectrogramYAxisRange(double yMin, double yMax); + void setSpectrogramZAxisScale(double zMin, double zMax); + void autoscale(); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + void appendData(RealDataEvent* e); + Lineplot* p_; + Spectrogramplot* s_; + QPushButton* b_; + + double* data_; + int numPoints_; + int timerId_; + bool haveNewData_; +}; + +#endif // WATERFALLWIDGET_H diff --git a/graphics/lib/waterfallplot/Waterfallplot.cpp b/graphics/lib/waterfallplot/Waterfallplot.cpp new file mode 100644 index 000000000..64c0d96e3 --- /dev/null +++ b/graphics/lib/waterfallplot/Waterfallplot.cpp @@ -0,0 +1,80 @@ +#include "Waterfallplot.h" +#include "WaterfallplotWrapper.h" + +using namespace std; + +Waterfallplot::Waterfallplot(int numDataPoints, int numRows) +{ + plot_ = new WaterfallplotWrapper(numDataPoints, numRows); +} + +Waterfallplot::~Waterfallplot() +{ + delete plot_; +} + +void Waterfallplot::appendNewData(float* data, int numPoints) +{ + plot_->appendNewData(data, numPoints); +} + +void Waterfallplot::appendNewData(double* data, int numPoints) +{ + plot_->appendNewData(data, numPoints); +} + +void Waterfallplot::setTitle(std::string title) +{ + plot_->setTitle(title); +} + +void Waterfallplot::setPlotXLabel(std::string xLabel) +{ + plot_->setPlotXLabel(xLabel); +} + +void Waterfallplot::setPlotYLabel(std::string yLabel) +{ + plot_->setPlotYLabel(yLabel); +} + +void Waterfallplot::setPlotXAxisRange(double xMin, double xMax) +{ + plot_->setPlotXAxisRange(xMin, xMax); +} + +void Waterfallplot::setPlotXAxisScale(double xMin, double xMax) +{ + plot_->setPlotXAxisScale(xMin, xMax); +} + +void Waterfallplot::setPlotYAxisScale(double yMin, double yMax) +{ + plot_->setPlotYAxisScale(yMin, yMax); +} + +void Waterfallplot::setSpectrogramXLabel(std::string xLabel) +{ + plot_->setSpectrogramXLabel(xLabel); +} + +void Waterfallplot::setSpectrogramYLabel(std::string yLabel) +{ + plot_->setSpectrogramYLabel(yLabel); +} + +void Waterfallplot::setSpectrogramXAxisRange(double xMin, double xMax) +{ + plot_->setSpectrogramXAxisRange(xMin, xMax); +} + +void Waterfallplot::setSpectrogramYAxisRange(double yMin, double yMax) +{ + plot_->setSpectrogramYAxisRange(yMin, yMax); +} + +void Waterfallplot::setSpectrogramZAxisScale(double zMin, double zMax) +{ + plot_->setSpectrogramZAxisScale(zMin, zMax); +} + diff --git a/graphics/lib/waterfallplot/Waterfallplot.h b/graphics/lib/waterfallplot/Waterfallplot.h new file mode 100644 index 000000000..f87bb84c0 --- /dev/null +++ b/graphics/lib/waterfallplot/Waterfallplot.h @@ -0,0 +1,49 @@ +#ifndef WATERFALLPLOT_H +#define WATERFALLPLOT_H + +#include + +class WaterfallplotWrapper; + +class Waterfallplot +{ +public: + Waterfallplot(int numDataPoints, int numRows); + ~Waterfallplot(); + + template + void appendNewData(Iterator begin, Iterator end); + void appendNewData(float* data, int numPoints); + void appendNewData(double* data, int numPoints); + void setTitle(std::string title); + void setPlotXLabel(std::string xLabel); + void setPlotYLabel(std::string yLabel); + void setPlotXAxisRange(double xMin, double xMax); + void setPlotXAxisScale(double xMin, double xMax); + void setPlotYAxisScale(double yMin, double yMax); + void setSpectrogramXLabel(std::string xLabel); + void setSpectrogramYLabel(std::string yLabel); + void setSpectrogramXAxisRange(double xMin, double xMax); + void setSpectrogramYAxisRange(double yMin, double yMax); + void setSpectrogramZAxisScale(double zMin, double zMax); + +private: + WaterfallplotWrapper* plot_; +}; + +template +void Waterfallplot::appendNewData(Iterator begin, Iterator end) +{ + int numPoints = end-begin; + double* data = new double[numPoints]; + + for(int i=0;begin!=end;begin++,i++) + { + data[i] = *begin; + } + + appendNewData(data, numPoints); + delete[] data; +} + +#endif // WATERFALLPLOT_H diff --git a/graphics/lib/waterfallplot/WaterfallplotWrapper.cpp b/graphics/lib/waterfallplot/WaterfallplotWrapper.cpp new file mode 100644 index 000000000..05b7281f0 --- /dev/null +++ b/graphics/lib/waterfallplot/WaterfallplotWrapper.cpp @@ -0,0 +1,187 @@ +#include "WaterfallplotWrapper.h" + +#include "WaterfallWidget.h" +#include "Events.h" +#include +#include + +using namespace std; + +WaterfallplotWrapper::WaterfallplotWrapper(int numDataPoints, int numRows) + :widget_(NULL) + ,destroyed_(true) +{ + if(QCoreApplication::instance() == NULL) + return; //TODO: throw exception here in Iris + if(QCoreApplication::instance()->thread() == QThread::currentThread()) + { + connect( this, SIGNAL( createWidgetSignal(int, int) ), + this, SLOT(createWidgetSlot(int, int)) ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()) ); + } + else + { + connect( this, SIGNAL( createWidgetSignal(int, int) ), + this, SLOT(createWidgetSlot(int, int)), + Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( destroyWidgetSignal() ), + this, SLOT(destroyWidgetSlot()) ); + connect( this, SIGNAL( destroyWidgetSignalBlocking() ), + this, SLOT(destroyWidgetSlot()), + Qt::BlockingQueuedConnection ); + moveToThread(QCoreApplication::instance()->thread()); + } + emit createWidgetSignal(numDataPoints, numRows); +} + +WaterfallplotWrapper::~WaterfallplotWrapper() +{ + if(destroyed_) + emit destroyWidgetSignal(); + else + emit destroyWidgetSignalBlocking(); +} + +void WaterfallplotWrapper::createWidgetSlot(int numDataPoints, int numRows) +{ + widget_ = new WaterfallWidget(numDataPoints, numRows); + destroyed_ = false; + widget_->setAttribute(Qt::WA_DeleteOnClose, true); + connect(widget_, SIGNAL( destroyed() ), + this, SLOT( widgetDestroyed() )); + connect(this, SIGNAL(setWidgetTitle(QString)), + widget_, SLOT(setWidgetTitle(QString))); + connect(this, SIGNAL(setWidgetPXLabel(QString)), + widget_, SLOT(setPlotXLabel(QString))); + connect(this, SIGNAL(setWidgetPYLabel(QString)), + widget_, SLOT(setPlotYLabel(QString))); + connect(this, SIGNAL(setWidgetPXAxisRange(double, double)), + widget_, SLOT(setPlotXAxisRange(double, double))); + connect(this, SIGNAL(setWidgetPXAxisScale(double, double)), + widget_, SLOT(setPlotXAxisScale(double, double))); + connect(this, SIGNAL(setWidgetPYAxisScale(double, double)), + widget_, SLOT(setPlotYAxisScale(double, double))); + connect(this, SIGNAL(setWidgetSXLabel(QString)), + widget_, SLOT(setSpectrogramXLabel(QString))); + connect(this, SIGNAL(setWidgetSYLabel(QString)), + widget_, SLOT(setSpectrogramYLabel(QString))); + connect(this, SIGNAL(setWidgetSXAxisRange(double, double)), + widget_, SLOT(setSpectrogramXAxisRange(double, double))); + connect(this, SIGNAL(setWidgetSYAxisRange(double, double)), + widget_, SLOT(setSpectrogramYAxisRange(double, double))); + connect(this, SIGNAL(setWidgetSZAxisScale(double, double)), + widget_, SLOT(setSpectrogramZAxisScale(double, double))); + + widget_->resize( 800, 600 ); + widget_->show(); +} + +void WaterfallplotWrapper::destroyWidgetSlot() +{ + delete widget_; +} + +void WaterfallplotWrapper::widgetDestroyed() +{ + destroyed_ = true; +} + +void WaterfallplotWrapper::appendNewData(float* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new RealDataEvent(data, numPoints)); +} + +void WaterfallplotWrapper::appendNewData(double* data, int numPoints) +{ + if(destroyed_) + return; + qApp->postEvent(widget_, new RealDataEvent(data, numPoints)); +} + + +void WaterfallplotWrapper::setTitle(std::string title) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(title.c_str()); + emit setWidgetTitle(str); +} + +void WaterfallplotWrapper::setPlotXLabel(std::string xLabel) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(xLabel.c_str()); + emit setWidgetPXLabel(str); +} + +void WaterfallplotWrapper::setPlotYLabel(std::string yLabel) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(yLabel.c_str()); + emit setWidgetPYLabel(str); +} + +void WaterfallplotWrapper::setPlotXAxisRange(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetPXAxisRange(xMin, xMax); +} + +void WaterfallplotWrapper::setPlotXAxisScale(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetPXAxisScale(xMin, xMax); +} + +void WaterfallplotWrapper::setPlotYAxisScale(double yMin, double yMax) +{ + if(destroyed_) + return; + emit setWidgetPYAxisScale(yMin, yMax); +} + +void WaterfallplotWrapper::setSpectrogramXAxisRange(double xMin, double xMax) +{ + if(destroyed_) + return; + emit setWidgetSXAxisRange(xMin, xMax); +} + +void WaterfallplotWrapper::setSpectrogramXLabel(std::string xLabel) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(xLabel.c_str()); + emit setWidgetSXLabel(str); +} + +void WaterfallplotWrapper::setSpectrogramYLabel(std::string yLabel) +{ + if(destroyed_) + return; + QString str = QString::fromUtf8(yLabel.c_str()); + emit setWidgetSYLabel(str); +} + +void WaterfallplotWrapper::setSpectrogramYAxisRange(double yMin, double yMax) +{ + if(destroyed_) + return; + emit setWidgetSYAxisRange(yMin, yMax); +} + +void WaterfallplotWrapper::setSpectrogramZAxisScale(double zMin, double zMax) +{ + if(destroyed_) + return; + emit setWidgetSZAxisScale(zMin, zMax); +} diff --git a/graphics/lib/waterfallplot/WaterfallplotWrapper.h b/graphics/lib/waterfallplot/WaterfallplotWrapper.h new file mode 100644 index 000000000..119175047 --- /dev/null +++ b/graphics/lib/waterfallplot/WaterfallplotWrapper.h @@ -0,0 +1,57 @@ +#ifndef WATERFALLPLOTWRAPPER_H +#define WATERFALLPLOTWRAPPER_H + +#include + +class WaterfallWidget; + +class WaterfallplotWrapper + : QObject +{ + Q_OBJECT + +public: + WaterfallplotWrapper(int numDataPoints, int numRows); + ~WaterfallplotWrapper(); + + void appendNewData(float* data, int numPoints); + void appendNewData(double* data, int numPoints); + void setTitle(std::string title); + void setPlotXLabel(std::string xLabel); + void setPlotYLabel(std::string yLabel); + void setPlotXAxisRange(double xMin, double xMax); + void setPlotXAxisScale(double xMin, double xMax); + void setPlotYAxisScale(double yMin, double yMax); + void setSpectrogramXLabel(std::string xLabel); + void setSpectrogramYLabel(std::string yLabel); + void setSpectrogramXAxisRange(double xMin, double xMax); + void setSpectrogramYAxisRange(double yMin, double yMax); + void setSpectrogramZAxisScale(double zMin, double zMax); + +public slots: + void createWidgetSlot(int numDataPoints, int numRows); + void destroyWidgetSlot(); + void widgetDestroyed(); + +signals: + void createWidgetSignal(int numDataPoints, int numRows); + void destroyWidgetSignal(); + void destroyWidgetSignalBlocking(); + void setWidgetTitle(QString title); + void setWidgetPXLabel(QString xLabel); + void setWidgetPYLabel(QString yLabel); + void setWidgetPXAxisRange(double xMin, double xMax); + void setWidgetPXAxisScale(double xMin, double xMax); + void setWidgetPYAxisScale(double yMin, double yMax); + void setWidgetSXLabel(QString xLabel); + void setWidgetSYLabel(QString yLabel); + void setWidgetSXAxisRange(double xMin, double xMax); + void setWidgetSYAxisRange(double yMin, double yMax); + void setWidgetSZAxisScale(double zMin, double zMax); + +private: + WaterfallWidget* widget_; + bool destroyed_; +}; + +#endif // WATERFALLPLOTWRAPPER_H diff --git a/graphics/lib/waterfallplot/plot_waterfall.cpp b/graphics/lib/waterfallplot/plot_waterfall.cpp new file mode 100644 index 000000000..99b1cc568 --- /dev/null +++ b/graphics/lib/waterfallplot/plot_waterfall.cpp @@ -0,0 +1,102 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + + +#include "plot/plot_waterfall.h" +#include "Waterfallplot.h" +#include + + +int plot_waterfall_init(plot_waterfall_t *h, int numDataPoints, int numRows) { + *h = (void*) new Waterfallplot(numDataPoints, numRows); + return (*h != NULL)?0:-1; +} + +void plot_waterfall_setTitle(plot_waterfall_t *h, char *title) { + Waterfallplot *plot = static_cast(*h); + plot->setTitle(title); +} + +void plot_waterfall_appendNewData(plot_waterfall_t *h, float *data, + int num_points) { + Waterfallplot *plot = static_cast(*h); + plot->appendNewData(data, num_points); +} + +void plot_complex_setPlotXLabel(plot_waterfall_t *h, char *xLabel) { + Waterfallplot *plot = static_cast(*h); + plot->setPlotXLabel(xLabel); +} + +void plot_complex_setPlotYLabel(plot_waterfall_t *h, char *yLabel) { + Waterfallplot *plot = static_cast(*h); + plot->setPlotXLabel(yLabel); +} + +void plot_waterfall_setPlotXAxisRange(plot_waterfall_t *h, double xMin, double xMax) { + Waterfallplot *plot = static_cast(*h); + plot->setPlotXAxisRange(xMin, xMax); +} + +void plot_waterfall_setPlotXAxisScale(plot_waterfall_t *h, double xMin, double xMax) { + Waterfallplot *plot = static_cast(*h); + plot->setPlotXAxisScale(xMin, xMax); +} + +void plot_waterfall_setPlotYAxisScale(plot_waterfall_t *h, double yMin, double yMax) { + Waterfallplot *plot = static_cast(*h); + plot->setPlotYAxisScale(yMin, yMax); +} + + +void plot_waterfall_setSpectrogramXLabel(plot_waterfall_t *h, char* xLabel) { + Waterfallplot *plot = static_cast(*h); + plot->setSpectrogramXLabel(xLabel); +} + +void plot_waterfall_setSpectrogramYLabel(plot_waterfall_t *h, char* yLabel) { + Waterfallplot *plot = static_cast(*h); + plot->setSpectrogramYLabel(yLabel); +} + +void plot_waterfall_setSpectrogramXAxisRange(plot_waterfall_t *h, double xMin, double xMax) { + Waterfallplot *plot = static_cast(*h); + plot->setSpectrogramXAxisRange(xMin, xMax); +} + +void plot_waterfall_setSpectrogramYAxisRange(plot_waterfall_t *h, double yMin, double yMax) { + Waterfallplot *plot = static_cast(*h); + plot->setSpectrogramYAxisRange(yMin, yMax); +} + +void plot_waterfall_setSpectrogramZAxisScale(plot_waterfall_t *h, double zMin, double zMax) { + Waterfallplot *plot = static_cast(*h); + plot->setSpectrogramZAxisScale(zMin, zMax); +} + diff --git a/graphics/lib/waterfallplot/test/CMakeLists.txt b/graphics/lib/waterfallplot/test/CMakeLists.txt new file mode 100644 index 000000000..3f5a2dfe3 --- /dev/null +++ b/graphics/lib/waterfallplot/test/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2012-2013 The Iris Project Developers. See the +# COPYRIGHT file at the top-level directory of this distribution +# and at http://www.softwareradiosystems.com/iris/copyright.html. +# +# This file is part of the Iris Project. +# +# Iris is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Build tests +######################################################################## +#turn the test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +INCLUDE_DIRECTORIES(..) +ADD_EXECUTABLE(waterfallplot_test Waterfallplot_test.cpp) +TARGET_LINK_LIBRARIES(waterfallplot_test ${Boost_LIBRARIES} graphics) +ADD_TEST(waterfallplot_test waterfallplot_test) + diff --git a/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp new file mode 100644 index 000000000..db96b6333 --- /dev/null +++ b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp @@ -0,0 +1,128 @@ +/** + * \file lib/generic/modulation/Crc_test.cpp + * \version 1.0 + * + * \section COPYRIGHT + * + * Copyright 2012-2013 The Iris Project Developers. See the + * COPYRIGHT file at the top-level directory of this distribution + * and at http://www.softwareradiosystems.com/iris/copyright.html. + * + * \section LICENSE + * + * This file is part of the Iris Project. + * + * Iris is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Iris is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + * \section DESCRIPTION + * + * Main test file for Waterfallplot class. + */ + +#define BOOST_TEST_MODULE Waterfallplot_Test + +#include "Waterfallplot.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PI 3.14159265358979323846 + +using namespace std; + +void threadMain1() +{ + int n=2048; + Waterfallplot plot(n,n); + plot.setTitle("Float"); + + float step = 1.0*PI/n; + float* data = new float[n*2]; + for(int i=0;i data; + data.resize(n*2); + for(int i=0;i("Waterfallplot_Init_Test"), NULL }; + QApplication a(argc, argv); + + boost::scoped_ptr< boost::thread > thread1_; + boost::scoped_ptr< boost::thread > thread2_; + boost::scoped_ptr< boost::thread > thread3_; + + thread1_.reset( new boost::thread( &threadMain1 ) ); + thread2_.reset( new boost::thread( &threadMain2 ) ); + thread3_.reset( new boost::thread( &threadMain3 ) ); + + qApp->exec(); + thread1_->join(); + thread2_->join(); + thread3_->join(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/include/channel/ch_awgn.h b/include/channel/ch_awgn.h deleted file mode 100644 index c2358a637..000000000 --- a/include/channel/ch_awgn.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ -#include - -#ifndef CH_AWGN_ -#define CH_AWGN_ - -typedef _Complex float cf; - -void ch_awgn(const cf* input, cf* output, float variance, int buff_sz); - -/* High-level API */ - -typedef struct { - const cf* input; - int in_len; - struct ch_awgn_ctrl_in { - float variance; // Noise variance - } ctrl_in; - - cf* output; - int out_len; -}ch_awgn_hl; - -int ch_awgn_initialize(ch_awgn_hl* hl); -int ch_awgn_work(ch_awgn_hl* hl); -int ch_awgn_stop(ch_awgn_hl* hl); - -#endif diff --git a/include/fec/crc.h b/include/fec/crc.h deleted file mode 100644 index da6216ae2..000000000 --- a/include/fec/crc.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef CRC_ -#define CRC_ - - -unsigned int crc(unsigned int crc, char *bufptr, int len, - int long_crc,unsigned int poly, int paste_word); - -#endif diff --git a/include/io/filesink.h b/include/io/filesink.h deleted file mode 100644 index 08acd6d7a..000000000 --- a/include/io/filesink.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef FILESINK_ -#define FILESINK_ - -#include -#include - -#include "io/format.h" - -/* Low-level API */ -typedef struct { - FILE *f; - file_data_type_t type; -}filesink_t; - -int filesink_init(filesink_t *q, char *filename, file_data_type_t type); -void filesink_close(filesink_t *q); - -int filesink_write(filesink_t *q, void *buffer, int nsamples); - - -/* High-level API */ -typedef struct { - filesink_t obj; - struct filesink_init { - char *file_name; - int block_length; - int data_type; - } init; - void* input; - int in_len; -}filesink_hl; - -int filesink_initialize(filesink_hl* h); -int filesink_work( filesink_hl* hl); -int filesink_stop(filesink_hl* h); - -#endif diff --git a/include/io/format.h b/include/io/format.h deleted file mode 100644 index caa0d516a..000000000 --- a/include/io/format.h +++ /dev/null @@ -1,7 +0,0 @@ - -#ifndef FORMAT_ -#define FORMAT_ - -typedef enum { FLOAT, COMPLEX_FLOAT, COMPLEX_SHORT, FLOAT_BIN, COMPLEX_FLOAT_BIN, COMPLEX_SHORT_BIN} file_data_type_t; - -#endif diff --git a/include/lte.h b/include/lte.h deleted file mode 100644 index 5870022ab..000000000 --- a/include/lte.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#include -#include - -#ifndef _LTE_ -#define _LTE_ - -#include "utils/bit.h" -#include "utils/convolution.h" -#include "utils/debug.h" -#include "utils/dft.h" -#include "utils/matrix.h" -#include "utils/mux.h" -#include "utils/nco.h" -#include "utils/pack.h" -#include "utils/vector.h" - -#include "lte/base.h" -#include "lte/fft.h" -#include "lte/sequence.h" - -#include "ch_estimation/chest.h" -#include "ch_estimation/refsignal.h" - -#include "channel/ch_awgn.h" - -#include "fec/viterbi.h" -#include "fec/convcoder.h" -#include "fec/crc.h" - -#include "filter/filter2d.h" - -#include "io/binsource.h" -#include "io/filesink.h" -#include "io/filesource.h" - -#include "modem/demod_hard.h" -#include "modem/demod_soft.h" -#include "modem/mod.h" -#include "modem/modem_table.h" - -#include "phch/pbch.h" - -#include "ratematching/rm_conv.h" - -#include "scrambling/scrambling.h" - -#include "resampling/interp.h" - -#include "sync/pss.h" -#include "sync/sfo.h" -#include "sync/sss.h" -#include "sync/sync.h" - - -#endif diff --git a/include/lte/sequence.h b/include/lte/sequence.h deleted file mode 100644 index 0b90b4518..000000000 --- a/include/lte/sequence.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef LTESEQ_ -#define LTESEQ_ - -#include "lte/base.h" - -typedef struct { - char *c; - int len; -}sequence_t; - -int sequence_init(sequence_t *q, int len); -void sequence_free(sequence_t *q); - -int sequence_LTEPRS(sequence_t *q, int len, int seed); - -int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id); -int sequence_pbch_crc(sequence_t *seq, int nof_ports); - -#endif diff --git a/include/modem/mod.h b/include/modem/mod.h deleted file mode 100644 index 198153b50..000000000 --- a/include/modem/mod.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef MOD_ -#define MOD_ - -#include -#include - -#include "modem_table.h" - -typedef _Complex float cf; - -int mod_modulate(modem_table_t* table, const char *bits, cf* symbols, int nbits); - -/* High-level API */ -typedef struct { - modem_table_t obj; - struct mod_init { - enum modem_std std; // symbol mapping standard (see modem_table.h) - } init; - - const char* input; - int in_len; - - cf* output; - int out_len; -}mod_hl; - -int mod_initialize(mod_hl* hl); -int mod_work(mod_hl* hl); -int mod_stop(mod_hl* hl); - -#endif diff --git a/include/phch/pbch.h b/include/phch/pbch.h deleted file mode 100644 index 97b4b00d8..000000000 --- a/include/phch/pbch.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef PBCH_ -#define PBCH_ - -#include "lte/base.h" -#include "modem/mod.h" -#include "modem/demod_soft.h" -#include "scrambling/scrambling.h" -#include "ratematching/rm_conv.h" -#include "fec/convcoder.h" -#include "fec/viterbi.h" -#include "fec/crc.h" - -#define PBCH_RE_CPNORM 240 -#define PBCH_RE_CPEXT 216 - -typedef _Complex float cf_t; - -enum phich_length { NORMAL, EXTENDED}; -enum phich_resources { R_1_6, R_1_2, R_1, R_2}; - -typedef struct { - int nof_ports; - int nof_prb; - int sfn; - enum phich_length phich_length; - int phich_resources; -}pbch_mib_t; - -/* PBCH receiver */ -typedef struct { - int cell_id; - lte_cp_t cp; - - /* buffers */ - cf_t *pbch_symbols; - float *pbch_llr; - float *temp; - float *pbch_rm; - char *data; - - int frame_idx; - - /* tx & rx objects */ - modem_table_t mod; - demod_soft_t demod; - sequence_t seq_pbch; - viterbi_t decoder; - -}pbch_t; - -int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp); -void pbch_free(pbch_t *q); -int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t **ce, int nof_ports, int nof_prb, float ebno, pbch_mib_t *data); -void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib); - - -bool pbch_exists(int nframe, int nslot); -int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); -int pbch_get(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); - -#endif diff --git a/include/resampling/interp.h b/include/resampling/interp.h deleted file mode 100644 index 207a39573..000000000 --- a/include/resampling/interp.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -typedef _Complex float cf_t; - - -void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end); -void interp_linear(cf_t *input, cf_t *output, int M, int len); -void interp_linear_f(float *input, float *output, int M, int len); diff --git a/include/sync/sfo.h b/include/sync/sfo.h deleted file mode 100644 index a759184f1..000000000 --- a/include/sync/sfo.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef SFO_ -#define SFO_ - -float sfo_estimate(int *t0, int len, float period); -float sfo_estimate_period(int *t0, int *t, int len, float period); - -#endif diff --git a/include/utils/bit.h b/include/utils/bit.h deleted file mode 100644 index c6f59191b..000000000 --- a/include/utils/bit.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef BIT_ -#define BIT_ - -#include -#include - -uint32_t bit_unpack(char **bits, int nof_bits); -void bit_pack(uint32_t value, char **bits, int nof_bits); -void bit_fprint(FILE *stream, char *bits, int nof_bits); -unsigned int bit_diff(char *x, char *y, int nbits); - -#endif - diff --git a/include/utils/debug.h b/include/utils/debug.h deleted file mode 100644 index 20914eedc..000000000 --- a/include/utils/debug.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H - -#include - -#define VERBOSE_DEBUG 2 -#define VERBOSE_INFO 1 -#define VERBOSE_NONE 0 - -#include -void get_time_interval(struct timeval * tdata); - -#ifndef DEBUG_DISABLED - -extern int verbose; - -#define VERBOSE_ISINFO() (verbose==VERBOSE_INFO) -#define VERBOSE_ISDEBUG() (verbose==VERBOSE_DEBUG) - -#define PRINT_DEBUG verbose=VERBOSE_DEBUG -#define PRINT_INFO verbose=VERBOSE_INFO -#define PRINT_NONE verbose=VERBOSE_NONE - -#define DEBUG(_fmt, ...) if (verbose >= VERBOSE_DEBUG) \ - fprintf(stdout, "[DEBUG]: " _fmt, __VA_ARGS__) - -#define INFO(_fmt, ...) if (verbose >= VERBOSE_INFO) \ - fprintf(stdout, "[INFO]: " _fmt, __VA_ARGS__) - -#else - -#define DEBUG -#define INFO - -#endif - -#endif diff --git a/include/utils/mux.h b/include/utils/mux.h deleted file mode 100644 index c96e29454..000000000 --- a/include/utils/mux.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - - -#ifndef MUX_ -#define MUX_ - -void mux(void **input, void *output, int *input_lengths, int *input_padding_pre, int nof_inputs, - int sample_sz); - -void demux(void *input, void **output, int *output_lengths, - int *output_padding_pre, int *output_padding_post, int nof_outputs, - int sample_sz); - -#endif diff --git a/include/utils/pack.h b/include/utils/pack.h deleted file mode 100644 index a7df1ec9a..000000000 --- a/include/utils/pack.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#ifndef PACK_ -#define PACK_ - -unsigned int unpack_bits(char **bits, int nof_bits); -void pack_bits(unsigned int value, char **bits, int nof_bits); - -#endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt deleted file mode 100644 index fb0c9f526..000000000 --- a/lib/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ - - -file(GLOB modules *) - -SET(SOURCES_ALL "") -foreach (_module ${modules}) - if (IS_DIRECTORY ${_module}) - file(GLOB_RECURSE tmp "${_module}/src/*.c") - LIST(APPEND SOURCES_ALL ${tmp}) - endif() -endforeach() - - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) - - -add_library(osld ${SOURCES_ALL}) - - - - - - - - - - - diff --git a/lib/channel/src/ch_awgn.c b/lib/channel/src/ch_awgn.c deleted file mode 100644 index 003e6e3a7..000000000 --- a/lib/channel/src/ch_awgn.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#include -#include -#include - -#include "gauss.h" -#include "channel/ch_awgn.h" - -void ch_awgn(const cf* x, cf* y, float variance, int buff_sz) { - _Complex float tmp; - int i; - - for (i=0;iinput,hl->output,hl->ctrl_in.variance,hl->in_len); - hl->out_len = hl->in_len; - return 0; -} - -int ch_awgn_stop(ch_awgn_hl* hl) { - return 0; -} diff --git a/lib/channel/src/gauss.c b/lib/channel/src/gauss.c deleted file mode 100644 index efdd090c0..000000000 --- a/lib/channel/src/gauss.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#include -#include -#include - -float rand_gauss (void) { - float v1,v2,s; - - do { - v1 = 2.0 * ((float) rand()/RAND_MAX) - 1; - v2 = 2.0 * ((float) rand()/RAND_MAX) - 1; - - s = v1*v1 + v2*v2; - } while ( s >= 1.0 ); - - if (s == 0.0) - return 0.0; - else - return (v1*sqrt(-2.0 * log(s) / s)); -} diff --git a/lib/channel/src/gauss.h b/lib/channel/src/gauss.h deleted file mode 100644 index 2cb14b179..000000000 --- a/lib/channel/src/gauss.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -float rand_gauss (void); diff --git a/lib/fec/src/viterbi.c b/lib/fec/src/viterbi.c deleted file mode 100644 index 074d2db7e..000000000 --- a/lib/fec/src/viterbi.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -/**@TODO frontend to FEC library if installed - */ - -#include -#include -#include - -#include "fec/viterbi.h" -#include "parity.h" -#include "viterbi37.h" - -#define DEB 0 - -int decode37(void *o, float *symbols, char *data) { - viterbi_t *q = o; - int i; - int len = q->tail_biting ? q->framebits : (q->framebits + q->K - 1); - float amp = 0; - - for (i=0;i<3*len;i++) { - if (fabsf(symbols[i] > amp)) { - amp = symbols[i]; - } - } - - /* Decode it and make sure we get the right answer */ - /* Initialize Viterbi decoder */ - init_viterbi37_port(q->ptr, q->tail_biting?-1:0); - - /* Decode block */ - update_viterbi37_blk_port(q->ptr, symbols,q->framebits + q->K - 1, amp, len); - - /* Do Viterbi chainback */ - chainback_viterbi37_port(q->ptr, data, q->framebits, 0); - - return q->framebits; -} - -void free37(void *o) { - viterbi_t *q = o; - delete_viterbi37_port(q->ptr); -} - -int init37(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { - q->K = 7; - q->R = 3; - q->framebits = framebits; - q->tail_biting = tail_biting; - q->decode = decode37; - q->free = free37; - - if ((q->ptr = create_viterbi37_port(poly, framebits, tail_biting)) == NULL) { - fprintf(stderr, "create_viterbi37 failed\n"); - return -1; - } else { - return 0; - } -} - -int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], int framebits, bool tail_bitting) { - switch(type) { - case viterbi_37: - return init37(q, poly, framebits, tail_bitting); - default: - fprintf(stderr, "Decoder not implemented\n"); - return -1; - } -} - -void viterbi_free(viterbi_t *q) { - q->free(q); -} - -/* symbols are real-valued */ -int viterbi_decode(viterbi_t *q, float *symbols, char *data) { - return q->decode(q, symbols, data); -} - - -int viterbi_initialize(viterbi_hl* h) { - int poly[3]; - viterbi_type_t type; - if (h->init.rate == 2) { - if (h->init.constraint_length == 7) { - type = viterbi_27; - } else if (h->init.constraint_length == 9) { - type = viterbi_29; - } else { - fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, - h->init.constraint_length); - return -1; - } - } else if (h->init.rate == 3) { - if (h->init.constraint_length == 7) { - type = viterbi_37; - } else if (h->init.constraint_length == 9) { - type = viterbi_39; - } else { - fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, - h->init.constraint_length); - return -1; - } - } else { - fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, - h->init.constraint_length); - return -1; - } - poly[0] = h->init.generator_0; - poly[1] = h->init.generator_1; - poly[2] = h->init.generator_2; - return viterbi_init(&h->obj, type, poly, h->init.frame_length, - h->init.tail_bitting?true:false); -} - -int viterbi_work(viterbi_hl* hl) { - if (hl->in_len != hl->init.frame_length) { - fprintf(stderr, "Expected input length %d but got %d\n", hl->init.frame_length, hl->in_len); - return -1; - } - return viterbi_decode(&hl->obj, hl->input, hl->output); -} - -int viterbi_stop(viterbi_hl* h) { - viterbi_free(&h->obj); - return 0; -} diff --git a/lib/fec/src/viterbi37.h b/lib/fec/src/viterbi37.h deleted file mode 100644 index c91d30e2a..000000000 --- a/lib/fec/src/viterbi37.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -void *create_viterbi37_port(int polys[3], int len, bool tail_biting); -int init_viterbi37_port(void *p, int starting_state); -int chainback_viterbi37_port(void *p, char *data, unsigned int nbits, unsigned int endstate); -void delete_viterbi37_port(void *p); -int update_viterbi37_blk_port(void *p, float *syms, int nbits, float amp, int framebits); diff --git a/lib/lte/src/phch_sequence.c b/lib/lte/src/phch_sequence.c deleted file mode 100644 index 281e8c6fa..000000000 --- a/lib/lte/src/phch_sequence.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#include "lte/base.h" -#include "lte/sequence.h" - -int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { - return sequence_LTEPRS(seq, CP_ISNORM(cp)?1920:1728, cell_id); -} diff --git a/lib/modem/src/hard_demod_lte.h b/lib/modem/src/hard_demod_lte.h deleted file mode 100644 index 5d73eb625..000000000 --- a/lib/modem/src/hard_demod_lte.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez , Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -/* Thresholds for Demodulation */ -/* Assume perfect amplitude and phase alignment. - * Check threshold values for real case - * or implement dynamic threshold adjustent as a function of received symbol amplitudes */ -#define QAM16_THRESHOLD 2/sqrt(10) -#define QAM64_THRESHOLD_1 2/sqrt(42) -#define QAM64_THRESHOLD_2 4/sqrt(42) -#define QAM64_THRESHOLD_3 6/sqrt(42) - -void hard_bpsk_demod(const cf* in, char* out, int N); -void hard_qpsk_demod(const cf* in, char* out, int N); -void hard_qam16_demod(const cf* in, char* out, int N); -void hard_qam64_demod(const cf* in, char* out, int N); diff --git a/lib/modem/src/lte_tables.h b/lib/modem/src/lte_tables.h deleted file mode 100644 index 49f259547..000000000 --- a/lib/modem/src/lte_tables.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez , Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - - -#define BPSK_LEVEL 1/sqrt(2) - -#define QPSK_LEVEL 1/sqrt(2) - -#define QAM16_LEVEL_1 1/sqrt(10) -#define QAM16_LEVEL_2 3/sqrt(10) - -#define QAM64_LEVEL_1 1/sqrt(42) -#define QAM64_LEVEL_2 3/sqrt(42) -#define QAM64_LEVEL_3 5/sqrt(42) -#define QAM64_LEVEL_4 7/sqrt(42) - -#define QAM64_LEVEL_x 2/sqrt(42) -/* this is not an QAM64 level, but, rather, an auxiliary value that can be used for computing the - * symbol from the bit sequence */ - - - - -void set_BPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_QPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_16QAMtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod); -void set_64QAMtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod); diff --git a/lib/modem/src/soft_algs.h b/lib/modem/src/soft_algs.h deleted file mode 100644 index 963dd4cbb..000000000 --- a/lib/modem/src/soft_algs.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2013, Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -void llr_approx(const _Complex float *in, float *out, int N, int M, int B, - _Complex float *symbols, int (*S)[6][32], float sigma2); - -void llr_exact(const _Complex float *in, float *out, int N, int M, int B, - _Complex float *symbols, int (*S)[6][32], float sigma2); diff --git a/lib/phch/src/pbch.c b/lib/phch/src/pbch.c deleted file mode 100644 index 0d57f37ad..000000000 --- a/lib/phch/src/pbch.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) - * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OSLD-lib is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "phch/pbch.h" -#include "lte/base.h" -#include "utils/bit.h" -#include "utils/vector.h" -#include "utils/debug.h" - -bool pbch_exists(int nframe, int nslot) { - return (!(nframe % 4) && nslot == 1); -} - -int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, bool put) { - int i; - cf_t *ptr; - if (put) { - ptr = input; - output += nof_prb * RE_X_RB / 2 - 36; - } else { - ptr = output; - input += nof_prb * RE_X_RB / 2 - 36; - } - - /* symbol 0 & 1 */ - for (i=0;i<2;i++) { - phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); - } - /* symbols 2 & 3 */ - if (CP_ISNORM(cp)) { - for (i=0;i<2;i++) { - phch_cp_prb(&input, &output, 6); - } - } else { - phch_cp_prb(&input, &output, 6); - phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); - } - if (put) { - return input - ptr; - } else { - return output - ptr; - } -} - -/** - * Puts PBCH in slot number 1 - * - * Returns the number of symbols written to slot1_data - * - * 36.211 10.3 section 6.6.4 - */ -int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id) { - return pbch_cp(pbch, slot1_data, nof_prb, cp, cell_id, true); -} - -/** - * Extracts PBCH from slot number 1 - * - * Returns the number of symbols written to pbch - * - * 36.211 10.3 section 6.6.4 - */ -int pbch_get(cf_t *slot1_data, cf_t *pbch, int nof_prb, lte_cp_t cp, int cell_id) { - return pbch_cp(slot1_data, pbch, nof_prb, cp, cell_id, false); -} - - -/* Checks CRC and blindly obtains the number of ports, which is saved in nof_ports. - * - * The bits buffer size must be at least 40 bytes. - * - * Returns 0 if the data is correct, -1 otherwise - */ -int pbch_crc_check(char *bits, int *nof_ports) { - int i, j; - const char crc_mask[3][16] = { - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, - {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1} - }; - const int ports[3] = {1, 2, 4}; - char data[40]; - - memcpy(data, bits, 24 * sizeof(char)); - - for (i=0;i<3;i++) { - for (j=0;j<16;j++) { - data[24+j] = (bits[24+j] + crc_mask[i][j]) % 2; - } - if (!crc(0, data, 40, 16, 0x11021, 0)) { - *nof_ports = ports[i]; - return 0; - } - } - *nof_ports = 0; - return -1; -} - - -/** Initializes the PBCH channel receiver */ -int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp) { - int ret = -1; - bzero(q, sizeof(pbch_t)); - q->cell_id = cell_id; - q->cp = cp; - if (modem_table_std(&q->mod, LTE_QPSK, true)) { - goto clean; - } - demod_soft_init(&q->demod); - demod_soft_table_set(&q->demod, &q->mod); - demod_soft_alg_set(&q->demod, APPROX); - if (sequence_pbch(&q->seq_pbch, q->cp, q->cell_id)) { - goto clean; - } - - int poly[3] = {0x6D, 0x4F, 0x57}; - if (viterbi_init(&q->decoder, viterbi_37, poly, 40, true)) { - goto clean; - } - int nof_symbols = (CP_ISNORM(q->cp)) ? PBCH_RE_CPNORM: PBCH_RE_CPEXT; - - q->pbch_symbols = malloc(sizeof(cf_t) * nof_symbols); - if (!q->pbch_symbols) { - goto clean; - } - q->pbch_llr = malloc(sizeof(float) * nof_symbols * 4 * 2); - if (!q->pbch_llr) { - goto clean; - } - q->temp = malloc(sizeof(float) * nof_symbols * 4 * 2); - if (!q->temp) { - goto clean; - } - q->pbch_rm = malloc(sizeof(float) * 120); - if (!q->pbch_rm) { - goto clean; - } - q->data = malloc(sizeof(char) * 40); - if (!q->data) { - goto clean; - } - - ret = 0; -clean: - if (ret == -1) { - pbch_free(q); - } - return ret; -} - -void pbch_free(pbch_t *q) { - if (q->pbch_symbols) { - free(q->pbch_symbols); - } - if (q->pbch_llr) { - free(q->pbch_llr); - } - if (q->pbch_rm) { - free(q->pbch_rm); - } - if (q->data) { - free(q->data); - } - sequence_free(&q->seq_pbch); - modem_table_free(&q->mod); - viterbi_free(&q->decoder); -} - -/** Unpacks MIB from PBCH message. - * msg buffer must be 24 byte length at least - */ -void pbch_mib_unpack(char *msg, pbch_mib_t *mib) { - int bw, phich_res; - char *buffer; - - bw = 4*msg[0] + 2*msg[1] + msg[2]; - switch(bw) { - case 0: - mib->nof_prb = 6; - break; - case 1: - mib->nof_prb = 15; - break; - default: - mib->nof_prb = (bw-1)*25; - break; - } - if (msg[3]) { - mib->phich_length = EXTENDED; - } else { - mib->phich_length = NORMAL; - } - phich_res = 2*msg[4] + msg[5]; - switch(phich_res) { - case 0: - mib->phich_resources = R_1_6; - break; - case 1: - mib->phich_resources = R_1_2; - break; - case 2: - mib->phich_resources = R_1; - break; - case 3: - mib->phich_resources = R_2; - break; - } - buffer = &msg[6]; - mib->sfn = bit_unpack(&buffer, 8); -} - -void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib) { - printf(" - Nof ports: %d\n", mib->nof_ports); - printf(" - PRB: %d\n", mib->nof_prb); - printf(" - PHICH Length: %s\n", mib->phich_length==EXTENDED?"Extended":"Normal"); - printf(" - PHICH Resources: "); - switch(mib->phich_resources) { - case R_1_6: - printf("1/6"); - break; - case R_1_2: - printf("1/2"); - break; - case R_1: - printf("1"); - break; - case R_2: - printf("2"); - break; - } - printf("\n"); - printf(" - SFN: %d\n", mib->sfn); -} - -void pbch_decode_reset(pbch_t *q) { - q->frame_idx = 0; -} - -int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, int nof_bits) { - int j; - - memcpy(&q->temp[dst*nof_bits], &q->pbch_llr[src*nof_bits], n*nof_bits*sizeof(float)); - - /* descramble */ - scrambling_float_offset(&q->seq_pbch, &q->temp[dst*nof_bits], dst*nof_bits, n*nof_bits); - - for (j=0;jtemp[j] = RX_NULL; - } - for (j=(dst+n)*nof_bits;j<4*nof_bits;j++) { - q->temp[j] = RX_NULL; - } - - /* unrate matching */ - rm_conv_rx(q->temp, q->pbch_rm, 4 * nof_bits, 120); - - /* decode */ - viterbi_decode(&q->decoder, q->pbch_rm, q->data); - - /* check crc and get nof ports */ - if (pbch_crc_check(q->data, &mib->nof_ports)) { - - return 0; - } else { - - printf("BCH Decoded Correctly.\n"); - - /* unpack MIB */ - pbch_mib_unpack(q->data, mib); - - mib->sfn += dst-src; - - pbch_mib_fprint(stdout, mib); - - return 1; - } -} - -/* Decodes the PBCH channel - * - * The PBCH spans in 40 ms. This function is called every 10 ms. It tries to decode the MIB - * given the symbols of the slot #1 of each radio frame. Successive calls will use more frames - * to help the decoding process. - * - * Returns 1 if successfully decoded MIB, 0 if not and -1 on error - */ -int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t **ce, int nof_ports, - int nof_prb, float ebno, pbch_mib_t *mib) { - int src, dst, res, nb, nant; - - int nof_symbols = (CP_ISNORM(q->cp)) ? PBCH_RE_CPNORM: PBCH_RE_CPEXT; - int nof_bits = 2 * nof_symbols; - - /* extract symbols */ - if (nof_symbols != pbch_get(slot1_symbols, q->pbch_symbols, nof_prb, - q->cp, q->cell_id)) { - fprintf(stderr, "There was an error getting the PBCH symbols\n"); - return -1; - } - - /* Try decoding for 1 to nof_ports antennas */ - for (nant=0;nantpbch_symbols[i] /= ce[0][i]; - } - - /*@TODO: layer demapping */ - - /* demodulate symbols */ - demod_soft_sigma_set(&q->demod, ebno); - demod_soft_demodulate(&q->demod, q->pbch_symbols, - &q->pbch_llr[nof_bits * q->frame_idx], nof_symbols); - - q->frame_idx++; - - INFO("PBCH: %d frames in buffer\n", q->frame_idx); - - /* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received - * 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234. - * We know they are ordered. - */ - res = 0; - for (nb=0;nbframe_idx && !res;nb++) { - for (dst=0;(dst<4-nb) && !res;dst++) { - for (src=0;srcframe_idx && !res;src++) { - DEBUG("Trying %d blocks at offset %d as subframe mod4 number %d\n", nb+1, src, dst); - res = pbch_decode_frame(q, mib, src, dst, nb+1, nof_bits); - } - } - } - - if (res) { - q->frame_idx = 0; - return 1; - } - } - - /* If not found, make room for the next packet of radio frame symbols */ - if (q->frame_idx == 4) { - memcpy(&q->pbch_llr[nof_bits], q->pbch_llr, nof_bits * 3 * sizeof(float)); - q->frame_idx = 3; - } - return 0; -} diff --git a/lib/sync/src/cp.c b/lib/sync/src/cp.c deleted file mode 100644 index 567267290..000000000 --- a/lib/sync/src/cp.c +++ /dev/null @@ -1,5 +0,0 @@ - - -/** TODO: Cyclic-prefix based synchronization - * - */ diff --git a/lib/utils/src/debug.c b/lib/utils/src/debug.c deleted file mode 100644 index c5a25548c..000000000 --- a/lib/utils/src/debug.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "utils/debug.h" - -int verbose = 0; - -void get_time_interval(struct timeval * tdata) { - - tdata[0].tv_sec = tdata[2].tv_sec - tdata[1].tv_sec; - tdata[0].tv_usec = tdata[2].tv_usec - tdata[1].tv_usec; - if (tdata[0].tv_usec < 0) { - tdata[0].tv_sec--; - tdata[0].tv_usec += 1000000; - } -} diff --git a/lte/CMakeLists.txt b/lte/CMakeLists.txt new file mode 100644 index 000000000..3dc1dbaa9 --- /dev/null +++ b/lte/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Install headers +######################################################################## +INSTALL(DIRECTORY include/ DESTINATION "${INCLUDE_DIR}" + FILES_MATCHING PATTERN "*.h" + PATTERN ".svn" EXCLUDE +) + +######################################################################## +# Add the subdirectories +######################################################################## + +ADD_SUBDIRECTORY(lib) + diff --git a/lte/include/lte.h b/lte/include/lte.h new file mode 100644 index 000000000..7e8d2a644 --- /dev/null +++ b/lte/include/lte.h @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include + +#ifndef _LTE_ +#define _LTE_ + +#include "lte/utils/bit.h" +#include "lte/utils/convolution.h" +#include "lte/utils/debug.h" +#include "lte/utils/dft.h" +#include "lte/utils/matrix.h" +#include "lte/utils/mux.h" +#include "lte/utils/nco.h" +#include "lte/utils/pack.h" +#include "lte/utils/vector.h" + +#include "lte/common/base.h" +#include "lte/common/fft.h" +#include "lte/common/sequence.h" + +#include "lte/ch_estimation/chest.h" +#include "lte/ch_estimation/refsignal.h" + +#include "lte/channel/ch_awgn.h" + +#include "lte/fec/viterbi.h" +#include "lte/fec/convcoder.h" +#include "lte/fec/crc.h" + +#include "lte/filter/filter2d.h" + +#include "lte/io/binsource.h" +#include "lte/io/filesink.h" +#include "lte/io/filesource.h" +#include "lte/io/udpsink.h" +#include "lte/io/udpsource.h" + +#include "lte/modem/demod_hard.h" +#include "lte/modem/demod_soft.h" +#include "lte/modem/mod.h" +#include "lte/modem/modem_table.h" + +#include "lte/mimo/precoding.h" +#include "lte/mimo/layermap.h" + +#include "lte/phch/pbch.h" + +#include "lte/ratematching/rm_conv.h" + +#include "lte/scrambling/scrambling.h" + +#include "lte/resampling/interp.h" + +#include "lte/sync/pss.h" +#include "lte/sync/sfo.h" +#include "lte/sync/sss.h" +#include "lte/sync/sync.h" + + +#endif diff --git a/include/ch_estimation/chest.h b/lte/include/lte/ch_estimation/chest.h similarity index 76% rename from include/ch_estimation/chest.h rename to lte/include/lte/ch_estimation/chest.h index 1d42687e3..0f55580de 100644 --- a/include/ch_estimation/chest.h +++ b/lte/include/lte/ch_estimation/chest.h @@ -1,29 +1,40 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + + #ifndef CHEST_ #define CHEST_ #include -#include "ch_estimation/refsignal.h" -#include "filter/filter2d.h" -#include "lte/base.h" +#include "lte/ch_estimation/refsignal.h" +#include "lte/filter/filter2d.h" +#include "lte/common/base.h" typedef _Complex float cf_t; /* this is only a shortcut */ diff --git a/include/ch_estimation/refsignal.h b/lte/include/lte/ch_estimation/refsignal.h similarity index 57% rename from include/ch_estimation/refsignal.h rename to lte/include/lte/ch_estimation/refsignal.h index 7fec166af..707788361 100644 --- a/include/ch_estimation/refsignal.h +++ b/lte/include/lte/ch_estimation/refsignal.h @@ -1,21 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + + #ifndef REFSIGNAL_ #define REFSIGNAL_ @@ -26,7 +37,7 @@ * */ -#include "lte/base.h" +#include "lte/common/base.h" typedef _Complex float cf_t; @@ -42,13 +53,15 @@ typedef struct { int *symbols_ref; // symbols with at least one reference int nsymbols; // number of symbols with at least one reference int voffset; // offset of the first reference in the freq domain + int nof_prb; ref_t *refs; cf_t *ch_est; } refsignal_t; int refsignal_init_LTEDL(refsignal_t *q, int port_id, int nslot, int cell_id, lte_cp_t cp, int nof_prb); - void refsignal_free(refsignal_t *q); +void refsignal_put(refsignal_t *q, cf_t *slot_symbols); + #endif diff --git a/lte/include/lte/channel/ch_awgn.h b/lte/include/lte/channel/ch_awgn.h new file mode 100644 index 000000000..2e6ada48d --- /dev/null +++ b/lte/include/lte/channel/ch_awgn.h @@ -0,0 +1,56 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include + +#ifndef CH_AWGN_ +#define CH_AWGN_ + +typedef _Complex float cf_t; + +void ch_awgn_c(const cf_t* input, cf_t* output, float variance, int buff_sz); +void ch_awgn_f(const float* x, float* y, float variance, int buff_sz); + +/* High-level API */ + +typedef struct { + const cf_t* input; + int in_len; + struct ch_awgn_ctrl_in { + float variance; // Noise variance + } ctrl_in; + + cf_t* output; + int out_len; +}ch_awgn_hl; + +int ch_awgn_initialize(ch_awgn_hl* hl); +int ch_awgn_work(ch_awgn_hl* hl); +int ch_awgn_stop(ch_awgn_hl* hl); + +#endif diff --git a/include/lte/base.h b/lte/include/lte/common/base.h similarity index 68% rename from include/lte/base.h rename to lte/include/lte/common/base.h index b359833f6..ae41c2d78 100644 --- a/include/lte/base.h +++ b/lte/include/lte/common/base.h @@ -1,28 +1,41 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef _LTEBASE_ #define _LTEBASE_ #define NSUBFRAMES_X_FRAME 10 #define NSLOTS_X_FRAME (2*NSUBFRAMES_X_FRAME) -#define MAX_PORTS 4 +#define MAX_PORTS 4 +#define MAX_PORTS_CTRL 4 +#define MAX_LAYERS 4 +#define MAX_CODEWORDS 4 typedef enum {CPNORM, CPEXT} lte_cp_t; @@ -52,11 +65,8 @@ typedef enum {CPNORM, CPEXT} lte_cp_t; #define SF_LEN_CPNORM(symbol_sz) 2*SLOT_LEN_CPNORM(symbol_sz) #define SF_LEN_CPEXT(symbol_sz) 2*SLOT_LEN_CPEXT(symbol_sz) -#define SF_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) -#define SF_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) - -#define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?0:symbol_sz*CPNORM_NSYMB) -#define SLOT_IDX_CPEXT(idx, symbol_sz) (idx==0?0:symbol_sz*CPEXT_NSYMB) +#define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) +#define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) #define MAX_PRB 110 #define RE_X_RB 12 @@ -74,6 +84,11 @@ int lte_voffset(int symbol_id, int cell_id, int nof_ports); #define NOF_LTE_BANDS 29 +typedef enum { + TX_DIVERSITY, SPATIAL_MULTIPLEX +} mimo_type_t; + + typedef struct { int id; float fd; diff --git a/include/lte/fft.h b/lte/include/lte/common/fft.h similarity index 57% rename from include/lte/fft.h rename to lte/include/lte/common/fft.h index 9859c9df2..e84342f13 100644 --- a/include/lte/fft.h +++ b/lte/include/lte/common/fft.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef LTEFFT_ #define LTEFFT_ @@ -23,8 +33,8 @@ #include #include -#include "lte/base.h" -#include "utils/dft.h" +#include "lte/common/base.h" +#include "lte/utils/dft.h" typedef _Complex float cf_t; /* this is only a shortcut */ diff --git a/lte/include/lte/common/sequence.h b/lte/include/lte/common/sequence.h new file mode 100644 index 000000000..31f480a50 --- /dev/null +++ b/lte/include/lte/common/sequence.h @@ -0,0 +1,47 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef LTESEQ_ +#define LTESEQ_ + +#include "lte/common/base.h" + +typedef struct { + char *c; + int len; +}sequence_t; + +int sequence_init(sequence_t *q, int len); +void sequence_free(sequence_t *q); + +int sequence_LTEPRS(sequence_t *q, int len, int seed); + +int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id); +int sequence_pbch_crc(sequence_t *seq, int nof_ports); + +#endif diff --git a/include/fec/convcoder.h b/lte/include/lte/fec/convcoder.h similarity index 58% rename from include/fec/convcoder.h rename to lte/include/lte/fec/convcoder.h index afec93790..ac62a358e 100644 --- a/include/fec/convcoder.h +++ b/lte/include/lte/fec/convcoder.h @@ -1,21 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + + #ifndef CONVCODER_ #define CONVCODER_ diff --git a/lte/include/lte/fec/crc.h b/lte/include/lte/fec/crc.h new file mode 100644 index 000000000..dd29d8868 --- /dev/null +++ b/lte/include/lte/fec/crc.h @@ -0,0 +1,37 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#ifndef CRC_ +#define CRC_ + + +unsigned int crc(unsigned int crc, char *bufptr, int len, + int long_crc,unsigned int poly, int paste_word); + +#endif diff --git a/include/fec/viterbi.h b/lte/include/lte/fec/viterbi.h similarity index 54% rename from include/fec/viterbi.h rename to lte/include/lte/fec/viterbi.h index 63d192488..9cf2cbb88 100644 --- a/include/fec/viterbi.h +++ b/lte/include/lte/fec/viterbi.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef VITERBI_ #define VITERBI_ @@ -32,13 +42,16 @@ typedef struct { unsigned int framebits; bool tail_biting; int poly[3]; - int (*decode) (void*, float*, char*); + int (*decode) (void*, unsigned char*, char*); void (*free) (void*); + unsigned char *tmp; + unsigned char *symbols_uc; }viterbi_t; int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], int framebits, bool tail_bitting); void viterbi_free(viterbi_t *q); -int viterbi_decode(viterbi_t *q, float *symbols, char *data); +int viterbi_decode_f(viterbi_t *q, float *symbols, char *data); +int viterbi_decode_uc(viterbi_t *q, unsigned char *symbols, char *data); /* High-level API */ diff --git a/include/filter/filter2d.h b/lte/include/lte/filter/filter2d.h similarity index 60% rename from include/filter/filter2d.h rename to lte/include/lte/filter/filter2d.h index 73d51dbfa..44325c08d 100644 --- a/include/filter/filter2d.h +++ b/lte/include/lte/filter/filter2d.h @@ -1,21 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + + #ifndef FILTER2D_ #define FILTER2D_ diff --git a/include/io/binsource.h b/lte/include/lte/io/binsource.h similarity index 67% rename from include/io/binsource.h rename to lte/include/lte/io/binsource.h index eb926cfb1..67e525bab 100644 --- a/include/io/binsource.h +++ b/lte/include/lte/io/binsource.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef BINSOURCE_ #define BINSOURCE_ diff --git a/lte/include/lte/io/filesink.h b/lte/include/lte/io/filesink.h new file mode 100644 index 000000000..2d0f2d99b --- /dev/null +++ b/lte/include/lte/io/filesink.h @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef FILESINK_ +#define FILESINK_ + +#include +#include + +#include "lte/io/format.h" + +/* Low-level API */ +typedef struct { + FILE *f; + data_type_t type; +}filesink_t; + +int filesink_init(filesink_t *q, char *filename, data_type_t type); +void filesink_free(filesink_t *q); + +int filesink_write(filesink_t *q, void *buffer, int nsamples); + + +/* High-level API */ +typedef struct { + filesink_t obj; + struct filesink_init { + char *file_name; + int block_length; + int data_type; + } init; + void* input; + int in_len; +}filesink_hl; + +int filesink_initialize(filesink_hl* h); +int filesink_work( filesink_hl* hl); +int filesink_stop(filesink_hl* h); + +#endif diff --git a/include/io/filesource.h b/lte/include/lte/io/filesource.h similarity index 51% rename from include/io/filesource.h rename to lte/include/lte/io/filesource.h index c574da05c..d8c787cc2 100644 --- a/include/io/filesource.h +++ b/lte/include/lte/io/filesource.h @@ -1,38 +1,49 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef FILESOURCE_ #define FILESOURCE_ #include #include -#include "io/format.h" +#include "lte/io/format.h" /* Low-level API */ typedef struct { FILE *f; - file_data_type_t type; + data_type_t type; }filesource_t; -int filesource_init(filesource_t *q, char *filename, file_data_type_t type); -void filesource_close(filesource_t *q); +int filesource_init(filesource_t *q, char *filename, data_type_t type); +void filesource_free(filesource_t *q); +void filesource_seek(filesource_t *q, int pos); int filesource_read(filesource_t *q, void *buffer, int nsamples); diff --git a/lte/include/lte/io/format.h b/lte/include/lte/io/format.h new file mode 100644 index 000000000..0c313a37f --- /dev/null +++ b/lte/include/lte/io/format.h @@ -0,0 +1,34 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef FORMAT_ +#define FORMAT_ + +typedef enum { FLOAT, COMPLEX_FLOAT, COMPLEX_SHORT, FLOAT_BIN, COMPLEX_FLOAT_BIN, COMPLEX_SHORT_BIN} data_type_t; + +#endif diff --git a/lte/include/lte/io/udpsink.h b/lte/include/lte/io/udpsink.h new file mode 100644 index 000000000..e0acd2c3a --- /dev/null +++ b/lte/include/lte/io/udpsink.h @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef udpsink_ +#define udpsink_ + +#include +#include +#include +#include +#include + +#include "lte/io/format.h" + +/* Low-level API */ +typedef struct { + int sockfd; + struct sockaddr_in servaddr; + data_type_t type; +}udpsink_t; + +int udpsink_init(udpsink_t *q, char *address, int port, data_type_t type); +void udpsink_free(udpsink_t *q); + +int udpsink_write(udpsink_t *q, void *buffer, int nsamples); + + +/* High-level API */ +typedef struct { + udpsink_t obj; + struct udpsink_init { + char *address; + int port; + int block_length; + int data_type; + } init; + void* input; + int in_len; +}udpsink_hl; + +int udpsink_initialize(udpsink_hl* h); +int udpsink_work( udpsink_hl* hl); +int udpsink_stop(udpsink_hl* h); + +#endif diff --git a/lte/include/lte/io/udpsource.h b/lte/include/lte/io/udpsource.h new file mode 100644 index 000000000..42d6839ae --- /dev/null +++ b/lte/include/lte/io/udpsource.h @@ -0,0 +1,73 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef udpsource_ +#define udpsource_ + + +#include +#include +#include +#include +#include + +#include "lte/io/format.h" + +/* Low-level API */ +typedef struct { + int sockfd; + struct sockaddr_in servaddr; + data_type_t type; +}udpsource_t; + +int udpsource_init(udpsource_t *q, char *address, int port, data_type_t type); +void udpsource_free(udpsource_t *q); + +int udpsource_read(udpsource_t *q, void *buffer, int nsamples); + + +/* High-level API */ +typedef struct { + udpsource_t obj; + struct udpsource_init { + char *address; + int port; + int data_type; + } init; + struct udpsource_ctrl_in { + int nsamples; // Number of samples to read + } ctrl_in; + void* output; + int out_len; +}udpsource_hl; + +int udpsource_initialize(udpsource_hl* h); +int udpsource_work( udpsource_hl* hl); +int udpsource_stop(udpsource_hl* h); + +#endif diff --git a/lte/include/lte/mimo/layermap.h b/lte/include/lte/mimo/layermap.h new file mode 100644 index 000000000..5527d6d7e --- /dev/null +++ b/lte/include/lte/mimo/layermap.h @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef LAYERMAP_H_ +#define LAYERMAP_H_ + +typedef _Complex float cf_t; + +/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" + */ +void layermap_decode(cf_t *x[MAX_LAYERS], cf_t *d[MAX_CODEWORDS], int nof_layers, int nof_cw, + int nof_layer_symbols, mimo_type_t type); + +/* Generates the vector of layer-mapped symbols "x" based on the vector of data symbols "d" + */ +void layermap_encode(cf_t *d[MAX_CODEWORDS], cf_t *x[MAX_LAYERS], int nof_layers, int nof_cw, + int nof_symbols, mimo_type_t type); + +#endif diff --git a/lte/include/lte/mimo/precoding.h b/lte/include/lte/mimo/precoding.h new file mode 100644 index 000000000..708c61f9c --- /dev/null +++ b/lte/include/lte/mimo/precoding.h @@ -0,0 +1,50 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef PRECODING_H_ +#define PRECODING_H_ + +typedef _Complex float cf_t; + +/** The precoder takes as input nlayers vectors "x" from the + * layer mapping and generates nports vectors "y" to be mapped onto + * resources on each of the antenna ports. + */ + +/* Estimates the vector "x" based on the received signal "y" and the channel estimates "ce" + */ +void precoding_decode(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], + cf_t *x[MAX_LAYERS], int nof_ports, int nof_symbols, + mimo_type_t type); + +/* Generates the vector "y" from the input vector "x" + */ +void precoding_encode(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, + int nof_symbols, mimo_type_t type); + +#endif /* PRECODING_H_ */ diff --git a/include/modem/demod_hard.h b/lte/include/lte/modem/demod_hard.h similarity index 54% rename from include/modem/demod_hard.h rename to lte/include/lte/modem/demod_hard.h index 2d0ab7238..8675c9758 100644 --- a/include/modem/demod_hard.h +++ b/lte/include/lte/modem/demod_hard.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef DEMOD_HARD_ #define DEMOD_HARD_ @@ -24,7 +34,7 @@ #include "modem_table.h" -typedef _Complex float cf; +typedef _Complex float cf_t; typedef struct { enum modem_std table; /* In this implementation, mapping table is hard-coded */ @@ -33,7 +43,7 @@ typedef struct { void demod_hard_init(demod_hard_t* q); void demod_hard_table(demod_hard_t* q, enum modem_std table); -int demod_hard_demodulate(demod_hard_t* q, const cf* symbols, char *bits, int nsymbols); +int demod_hard_demodulate(demod_hard_t* q, const cf_t* symbols, char *bits, int nsymbols); @@ -44,7 +54,7 @@ typedef struct { enum modem_std std; // Symbol mapping standard (see modem_table.h) } init; - const cf* input; + const cf_t* input; int in_len; char* output; diff --git a/include/modem/demod_soft.h b/lte/include/lte/modem/demod_soft.h similarity index 64% rename from include/modem/demod_soft.h rename to lte/include/lte/modem/demod_soft.h index 917b432ae..39fa2c993 100644 --- a/include/modem/demod_soft.h +++ b/lte/include/lte/modem/demod_soft.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef DEMOD_SOFT_ #define DEMOD_SOFT_ @@ -36,7 +46,7 @@ void demod_soft_init(demod_soft_t *q); void demod_soft_table_set(demod_soft_t *q, modem_table_t *table); void demod_soft_alg_set(demod_soft_t *q, enum alg alg_type); void demod_soft_sigma_set(demod_soft_t *q, float sigma); -int demod_soft_demodulate(demod_soft_t *q, const cf* symbols, float* llr, int nsymbols); +int demod_soft_demodulate(demod_soft_t *q, const cf_t* symbols, float* llr, int nsymbols); /* High-level API */ @@ -48,7 +58,7 @@ typedef struct { enum modem_std std; // symbol mapping standard (see modem_table.h) } init; - const cf* input; + const cf_t* input; int in_len; struct demod_soft_ctrl_in { diff --git a/lte/include/lte/modem/mod.h b/lte/include/lte/modem/mod.h new file mode 100644 index 000000000..33df9cca1 --- /dev/null +++ b/lte/include/lte/modem/mod.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef MOD_ +#define MOD_ + +#include +#include + +#include "modem_table.h" + +typedef _Complex float cf_t; + +int mod_modulate(modem_table_t* table, const char *bits, cf_t* symbols, int nbits); + +/* High-level API */ +typedef struct { + modem_table_t obj; + struct mod_init { + enum modem_std std; // symbol mapping standard (see modem_table.h) + } init; + + const char* input; + int in_len; + + cf_t* output; + int out_len; +}mod_hl; + +int mod_initialize(mod_hl* hl); +int mod_work(mod_hl* hl); +int mod_stop(mod_hl* hl); + +#endif diff --git a/include/modem/modem_table.h b/lte/include/lte/modem/modem_table.h similarity index 52% rename from include/modem/modem_table.h rename to lte/include/lte/modem/modem_table.h index e99eea137..93a6aa230 100644 --- a/include/modem/modem_table.h +++ b/lte/include/lte/modem/modem_table.h @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef MODEM_TABLE_ #define MODEM_TABLE_ @@ -24,13 +34,13 @@ #include #include -typedef _Complex float cf; +typedef _Complex float cf_t; typedef struct { int idx[2][6][32]; }soft_table_t; typedef struct { - cf* symbol_table; // bit-to-symbol mapping + cf_t* symbol_table; // bit-to-symbol mapping soft_table_t soft_table; // symbol-to-bit mapping (used in soft demodulating) int nsymbols; // number of modulation symbols int nbits_x_symbol; // number of bits per symbol @@ -45,7 +55,7 @@ enum modem_std { void modem_table_init(modem_table_t* q); void modem_table_free(modem_table_t* q); void modem_table_reset(modem_table_t* q); -int modem_table_set(modem_table_t* q, cf* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol); +int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol); int modem_table_std(modem_table_t* q, enum modem_std table, bool compute_soft_demod); #endif diff --git a/lte/include/lte/phch/pbch.h b/lte/include/lte/phch/pbch.h new file mode 100644 index 000000000..36cbb7315 --- /dev/null +++ b/lte/include/lte/phch/pbch.h @@ -0,0 +1,100 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef PBCH_ +#define PBCH_ + +#include "lte/common/base.h" +#include "lte/mimo/precoding.h" +#include "lte/mimo/layermap.h" +#include "lte/modem/mod.h" +#include "lte/modem/demod_soft.h" +#include "lte/scrambling/scrambling.h" +#include "lte/ratematching/rm_conv.h" +#include "lte/fec/convcoder.h" +#include "lte/fec/viterbi.h" +#include "lte/fec/crc.h" + +#define PBCH_RE_CPNORM 240 +#define PBCH_RE_CPEXT 216 + +typedef _Complex float cf_t; + +enum phich_length { NORMAL, EXTENDED}; +enum phich_resources { R_1_6, R_1_2, R_1, R_2}; + +typedef struct { + int nof_ports; + int nof_prb; + int sfn; + enum phich_length phich_length; + enum phich_resources phich_resources; +}pbch_mib_t; + +/* PBCH object */ +typedef struct { + int cell_id; + lte_cp_t cp; + int nof_symbols; + + /* buffers */ + cf_t *ce[MAX_PORTS_CTRL]; + cf_t *pbch_symbols[MAX_PORTS_CTRL]; + cf_t *pbch_x[MAX_PORTS_CTRL]; + cf_t *pbch_d; + float *pbch_llr; + float *temp; + float *pbch_rm_f; + char *pbch_rm_b; + char *data; + char *data_enc; + + int frame_idx; + + /* tx & rx objects */ + modem_table_t mod; + demod_soft_t demod; + sequence_t seq_pbch; + viterbi_t decoder; + convcoder_t encoder; + +}pbch_t; + +int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp); +void pbch_free(pbch_t *q); +int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int nof_prb, float ebno, pbch_mib_t *mib); +void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL], + int nof_prb, int nof_ports); + + +void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib); +bool pbch_exists(int nframe, int nslot); +int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); +int pbch_get(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id); + +#endif diff --git a/include/ratematching/rm_conv.h b/lte/include/lte/ratematching/rm_conv.h similarity index 51% rename from include/ratematching/rm_conv.h rename to lte/include/lte/ratematching/rm_conv.h index d71f24adc..58d3e2eb5 100644 --- a/include/ratematching/rm_conv.h +++ b/lte/include/lte/ratematching/rm_conv.h @@ -1,26 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef RM_CONV_ #define RM_CONV_ #define RX_NULL 10000 +#define TX_NULL 80 + +int rm_conv_tx(char *input, char *output, int in_len, int out_len); int rm_conv_rx(float *input, float *output, int in_len, int out_len); diff --git a/lte/include/lte/resampling/interp.h b/lte/include/lte/resampling/interp.h new file mode 100644 index 000000000..39267ca36 --- /dev/null +++ b/lte/include/lte/resampling/interp.h @@ -0,0 +1,34 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +typedef _Complex float cf_t; + + +void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end); +void interp_linear(cf_t *input, cf_t *output, int M, int len); +void interp_linear_f(float *input, float *output, int M, int len); diff --git a/include/scrambling/scrambling.h b/lte/include/lte/scrambling/scrambling.h similarity index 55% rename from include/scrambling/scrambling.h rename to lte/include/lte/scrambling/scrambling.h index b5990f030..d1f9084e3 100644 --- a/include/scrambling/scrambling.h +++ b/lte/include/lte/scrambling/scrambling.h @@ -1,31 +1,43 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef SCRAMBLING_ #define SCRAMBLING_ -#include "lte/sequence.h" -#include "lte/base.h" +#include "lte/common/sequence.h" +#include "lte/common/base.h" /* Scrambling has no state */ void scrambling_bit(sequence_t *s, char *data); +void scrambling_bit_offset(sequence_t *s, char *data, int offset, int len); + void scrambling_float(sequence_t *s, float *data); -int scrambling_float_offset(sequence_t *s, float *data, int offset, int len); +void scrambling_float_offset(sequence_t *s, float *data, int offset, int len); /* High-level API */ diff --git a/include/sync/pss.h b/lte/include/lte/sync/pss.h similarity index 75% rename from include/sync/pss.h rename to lte/include/lte/sync/pss.h index f1ce7b7b5..a76e5abdc 100644 --- a/include/sync/pss.h +++ b/lte/include/lte/sync/pss.h @@ -1,27 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef PSS_ #define PSS_ #include #include -#include "utils/convolution.h" + +#include "lte/common/base.h" +#include "lte/utils/convolution.h" typedef _Complex float cf_t; /* this is only a shortcut */ @@ -67,11 +79,14 @@ typedef struct { cf_t *tmp_nco; }pss_synch_t; +typedef enum { PSS_TX, PSS_RX } pss_direction_t; + /* Basic functionality */ int pss_synch_init(pss_synch_t *q, int frame_size); void pss_synch_free(pss_synch_t *q); -int pss_generate(cf_t *signal, int direction, int N_id_2); +int pss_generate(cf_t *signal, int N_id_2); +void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp); int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2); int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, float *corr_mean_value); diff --git a/lte/include/lte/sync/sfo.h b/lte/include/lte/sync/sfo.h new file mode 100644 index 000000000..11b5ba02c --- /dev/null +++ b/lte/include/lte/sync/sfo.h @@ -0,0 +1,35 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef SFO_ +#define SFO_ + +float sfo_estimate(int *t0, int len, float period); +float sfo_estimate_period(int *t0, int *t, int len, float period); + +#endif diff --git a/include/sync/sss.h b/lte/include/lte/sync/sss.h similarity index 73% rename from include/sync/sss.h rename to lte/include/lte/sync/sss.h index 1cb1850a8..d7227a540 100644 --- a/include/sync/sss.h +++ b/lte/include/lte/sync/sss.h @@ -1,28 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef SSS_ #define SSS_ #include #include -#include "utils/dft.h" +#include "lte/common/base.h" +#include "lte/utils/dft.h" typedef _Complex float cf_t; /* this is only a shortcut */ @@ -35,6 +46,7 @@ typedef _Complex float cf_t; /* this is only a shortcut */ #define SSS_DFT_LEN 128 #define N_SSS 31 +#define SSS_LEN 2*N_SSS struct sss_tables { int z1[N_SSS][N_SSS]; @@ -71,7 +83,8 @@ typedef struct { /* Basic functionality */ int sss_synch_init(sss_synch_t *q); void sss_synch_free(sss_synch_t *q); -void sss_generate(float *signal, int cell_id); +void sss_generate(float *signal0, float *signal5, int cell_id); +void sss_put_slot(float *sss, cf_t *symbol, int nof_prb, lte_cp_t cp); int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2); diff --git a/include/sync/sync.h b/lte/include/lte/sync/sync.h similarity index 61% rename from include/sync/sync.h rename to lte/include/lte/sync/sync.h index c3fc547fb..b0c2c0d16 100644 --- a/include/sync/sync.h +++ b/lte/include/lte/sync/sync.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef SYNC_ #define SYNC_ @@ -38,7 +48,7 @@ typedef struct { float cfo; }sync_t; -int sync_run(sync_t *q, cf_t *input, int read_offset); +int sync_run(sync_t *q, cf_t *input); float sync_get_cfo(sync_t *q); void sync_pss_det_absolute(sync_t *q); void sync_pss_det_peakmean(sync_t *q); diff --git a/lte/include/lte/utils/bit.h b/lte/include/lte/utils/bit.h new file mode 100644 index 000000000..00e2523f1 --- /dev/null +++ b/lte/include/lte/utils/bit.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef BIT_ +#define BIT_ + +#include +#include + +uint32_t bit_unpack(char **bits, int nof_bits); +void bit_pack(uint32_t value, char **bits, int nof_bits); +void bit_fprint(FILE *stream, char *bits, int nof_bits); +unsigned int bit_diff(char *x, char *y, int nbits); + +#endif + diff --git a/include/utils/convolution.h b/lte/include/lte/utils/convolution.h similarity index 58% rename from include/utils/convolution.h rename to lte/include/lte/utils/convolution.h index c6f3fef35..403c2c44f 100644 --- a/include/utils/convolution.h +++ b/lte/include/lte/utils/convolution.h @@ -1,25 +1,35 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef CONVOLUTION_H_ #define CONVOLUTION_H_ -#include "utils/dft.h" +#include "lte/utils/dft.h" typedef struct { _Complex float *input_fft; diff --git a/lte/include/lte/utils/debug.h b/lte/include/lte/utils/debug.h new file mode 100644 index 000000000..f6f08d777 --- /dev/null +++ b/lte/include/lte/utils/debug.h @@ -0,0 +1,64 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include + +#define VERBOSE_DEBUG 2 +#define VERBOSE_INFO 1 +#define VERBOSE_NONE 0 + +#include +void get_time_interval(struct timeval * tdata); + +#ifndef DEBUG_DISABLED + +extern int verbose; + +#define VERBOSE_ISINFO() (verbose==VERBOSE_INFO) +#define VERBOSE_ISDEBUG() (verbose==VERBOSE_DEBUG) + +#define PRINT_DEBUG verbose=VERBOSE_DEBUG +#define PRINT_INFO verbose=VERBOSE_INFO +#define PRINT_NONE verbose=VERBOSE_NONE + +#define DEBUG(_fmt, ...) if (verbose >= VERBOSE_DEBUG) \ + fprintf(stdout, "[DEBUG]: " _fmt, __VA_ARGS__) + +#define INFO(_fmt, ...) if (verbose >= VERBOSE_INFO) \ + fprintf(stdout, "[INFO]: " _fmt, __VA_ARGS__) + +#else + +#define DEBUG +#define INFO + +#endif + +#endif diff --git a/include/utils/dft.h b/lte/include/lte/utils/dft.h similarity index 77% rename from include/utils/dft.h rename to lte/include/lte/utils/dft.h index 8f6d55d4c..6833a7ff1 100644 --- a/include/utils/dft.h +++ b/lte/include/lte/utils/dft.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef DFT_H_ #define DFT_H_ diff --git a/include/utils/matrix.h b/lte/include/lte/utils/matrix.h similarity index 59% rename from include/utils/matrix.h rename to lte/include/lte/utils/matrix.h index 863ee8b01..a74884b77 100644 --- a/include/utils/matrix.h +++ b/lte/include/lte/utils/matrix.h @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #ifndef MATRIX_ diff --git a/lte/include/lte/utils/mux.h b/lte/include/lte/utils/mux.h new file mode 100644 index 000000000..8d01e8c74 --- /dev/null +++ b/lte/include/lte/utils/mux.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +#ifndef MUX_ +#define MUX_ + +void mux(void **input, void *output, int *input_lengths, int *input_padding_pre, int nof_inputs, + int sample_sz); + +void demux(void *input, void **output, int *output_lengths, + int *output_padding_pre, int *output_padding_post, int nof_outputs, + int sample_sz); + +#endif diff --git a/include/utils/nco.h b/lte/include/lte/utils/nco.h similarity index 57% rename from include/utils/nco.h rename to lte/include/lte/utils/nco.h index ae1cb912a..4fae2ec7a 100644 --- a/include/utils/nco.h +++ b/lte/include/lte/utils/nco.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef NCO_ #define NCO_ diff --git a/lte/include/lte/utils/pack.h b/lte/include/lte/utils/pack.h new file mode 100644 index 000000000..ea64cfbef --- /dev/null +++ b/lte/include/lte/utils/pack.h @@ -0,0 +1,35 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef PACK_ +#define PACK_ + +unsigned int unpack_bits(char **bits, int nof_bits); +void pack_bits(unsigned int value, char **bits, int nof_bits); + +#endif diff --git a/include/utils/vector.h b/lte/include/lte/utils/vector.h similarity index 58% rename from include/utils/vector.h rename to lte/include/lte/utils/vector.h index 586c07554..806952896 100644 --- a/include/utils/vector.h +++ b/lte/include/lte/utils/vector.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #ifndef VECTOR_ #define VECTOR_ @@ -33,6 +43,7 @@ void *vec_malloc(int size); /* print vectors */ void vec_fprint_c(FILE *stream, cf_t *x, int len); void vec_fprint_f(FILE *stream, float *x, int len); +void vec_fprint_b(FILE *stream, char *x, int len); void vec_fprint_i(FILE *stream, int *x, int len); /* sum two vectors */ @@ -43,10 +54,13 @@ void vec_sum_ccc(cf_t *z, cf_t *x, cf_t *y, int len); void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len); void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len); -/* vector product */ +/* vector product (element-wise) */ void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, int len); void vec_prod_ccc_unalign(cf_t *x, cf_t *y, cf_t *z, int len); +/* z=x/y vector division (element-wise) */ +void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, int len); + /* conjugate */ void vec_conj_cc(cf_t *x, cf_t *y, int len); @@ -56,6 +70,9 @@ float vec_avg_power_cf(cf_t *x, int len); /* return the index of the maximum value in the vector */ int vec_max_fi(float *x, int len); +/* quantify vector of floats and convert to unsigned char */ +void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, int len); + /* magnitude of each vector element */ void vec_abs_cf(cf_t *x, float *abs, int len); diff --git a/lte/lib/CMakeLists.txt b/lte/lib/CMakeLists.txt new file mode 100644 index 000000000..5f4be9ed0 --- /dev/null +++ b/lte/lib/CMakeLists.txt @@ -0,0 +1,56 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + + +######################################################################## +# Find Dependencies +######################################################################## + +FIND_PACKAGE(FFTW3F REQUIRED) # TODO: distribute kissfft instead +include_directories(${FFTW3F_INCLUDE_DIRS}) + + +######################################################################## +# Recurse subdirectories and compile all source files into the same lib +######################################################################## + +FILE(GLOB modules *) +SET(SOURCES_ALL "") +FOREACH (_module ${modules}) + IF(IS_DIRECTORY ${_module}) + FILE(GLOB_RECURSE tmp "${_module}/src/*.c") + LIST(APPEND SOURCES_ALL ${tmp}) + ENDIF(IS_DIRECTORY ${_module}) +ENDFOREACH() + +ADD_LIBRARY(lte ${SOURCES_ALL}) +TARGET_LINK_LIBRARIES(lte m ${FFTW3F_LIBRARIES}) +INSTALL(TARGETS lte DESTINATION ${LIBRARY_DIR}) +LIBLTE_SET_PIC(lte) + + + + + + + + + diff --git a/lib/ch_estimation/src/chest.c b/lte/lib/ch_estimation/src/chest.c similarity index 83% rename from lib/ch_estimation/src/chest.c rename to lte/lib/ch_estimation/src/chest.c index f2cd2e57a..48faccdc4 100644 --- a/lib/ch_estimation/src/chest.c +++ b/lte/lib/ch_estimation/src/chest.c @@ -1,31 +1,41 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include #include -#include "ch_estimation/chest.h" -#include "resampling/interp.h" -#include "utils/vector.h" -#include "utils/debug.h" +#include "lte/ch_estimation/chest.h" +#include "lte/resampling/interp.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" #define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz) #define SF_SZ(q) (2 * SLOT_SZ(q)) @@ -38,7 +48,7 @@ void chest_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { void chest_ref_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { int i; - fprintf(stream, "refs=["); + fprintf(stream, "refs%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].refs[i].simbol, __imag__ q->refsignal[port_id][nslot].refs[i].simbol); @@ -48,7 +58,7 @@ void chest_ref_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { void chest_recvsig_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { int i; - fprintf(stream, "recvsig=["); + fprintf(stream, "recvsig%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].refs[i].recv_simbol, __imag__ q->refsignal[port_id][nslot].refs[i].recv_simbol); @@ -58,11 +68,11 @@ void chest_recvsig_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { void chest_ce_fprint(chest_t *q, FILE *stream, int nslot, int port_id) { int i; - fprintf(stream, "mag=["); + fprintf(stream, "mag%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { fprintf(stream, "%3.3f, ", cabsf(q->refsignal[port_id][nslot].ch_est[i])); } - fprintf(stream, "];\nphase=["); + fprintf(stream, "];\nphase%d=[",port_id); for (i=0;irefsignal[port_id][nslot].nof_refs;i++) { fprintf(stream, "%3.3f, ", atan2f(__imag__ q->refsignal[port_id][nslot].ch_est[i], __real__ q->refsignal[port_id][nslot].ch_est[i])); @@ -83,7 +93,8 @@ void chest_ce_ref(chest_t *q, cf_t *input, int nslot, int port_id, int nref) { DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, SAMPLE_IDX(q->nof_prb, tidx, fidx), 10*log10f(cabsf(channel_ref/known_ref)), cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,cargf(channel_ref/known_ref)/M_PI); - /* FIXME: compare with treshold */ + + /* FIXME: compare with threshold */ if (channel_ref != 0) { q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref; } else { @@ -104,7 +115,8 @@ void chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, int nslot, int port_i refsignal_t *r = &q->refsignal[port_id][nslot]; - INFO("Estimating channel using %d reference signals\n", r->nof_refs); + INFO("Estimating channel slot=%d port=%d using %d reference signals\n", + nslot, port_id, r->nof_refs); for (i=0;inof_refs;i++) { chest_ce_ref(q, input, nslot, port_id, i); @@ -120,11 +132,17 @@ void chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, int nslot, int port_i } /* now interpolate in the time domain */ for (i=0;inof_prb * RE_X_RB; i++) { - for (j=0;jnsymbols;j++) { - x[j] = ce[r->symbols_ref[j] * q->nof_prb * RE_X_RB + i]; + if (r->nsymbols > 1) { + for (j=0;jnsymbols;j++) { + x[j] = ce[r->symbols_ref[j] * q->nof_prb * RE_X_RB + i]; + } + interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0], + 2, r->symbols_ref[0], 3); + } else { + for (j=0;jsymbols_ref[0] * q->nof_prb * RE_X_RB + i]; + } } - interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0], - 2, r->symbols_ref[0], 3); for (j=0;jnof_symbols;j++) { ce[j * q->nof_prb * RE_X_RB + i] = y[j]; } diff --git a/lib/ch_estimation/src/refsignal.c b/lte/lib/ch_estimation/src/refsignal.c similarity index 72% rename from lib/ch_estimation/src/refsignal.c rename to lte/lib/ch_estimation/src/refsignal.c index 8436f627f..5e1008846 100644 --- a/lib/ch_estimation/src/refsignal.c +++ b/lte/lib/ch_estimation/src/refsignal.c @@ -1,32 +1,42 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include #include -#include "lte/base.h" -#include "ch_estimation/refsignal.h" -#include "utils/vector.h" -#include "utils/debug.h" -#include "lte/sequence.h" +#include "lte/common/base.h" +#include "lte/ch_estimation/refsignal.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" +#include "lte/common/sequence.h" #define idx(x, y) (l*nof_refs_x_symbol+i) @@ -61,6 +71,16 @@ int refsignal_k(int m, int v, int cell_id) { return 6*m+((v+(cell_id%6))%6); } +void refsignal_put(refsignal_t *q, cf_t *slot_symbols) { + int i; + int fidx, tidx; + for (i=0;inof_refs;i++) { + fidx = q->refs[i].freq_idx; // reference frequency index + tidx = q->refs[i].time_idx; // reference time index + slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].simbol; + } +} + /** Initializes refsignal_t object according to 3GPP 36.211 6.10.1 * */ @@ -105,6 +125,7 @@ int refsignal_init_LTEDL(refsignal_t *q, int port_id, int nslot, q->nsymbols = nof_ref_symbols; q->symbols_ref = malloc(sizeof(int) * nof_ref_symbols); q->voffset = cell_id%6; + q->nof_prb = nof_prb; if (!q->symbols_ref) { return -1; } @@ -141,12 +162,6 @@ int refsignal_init_LTEDL(refsignal_t *q, int port_id, int nslot, /* mapping to resource elements */ q->refs[idx(l,i)].freq_idx = refsignal_k(i, v, cell_id); q->refs[idx(l,i)].time_idx = lp[l]; - - /* print only first slot */ - if (ns == 0) { - DEBUG("(%-2d,%2d) is mapped to (%-2d,%2d) (mp=%d, v=%d)\n", - l,i,q->refs[idx(l,i)].time_idx, q->refs[idx(l,i)].freq_idx, mp, v); - } } } diff --git a/lte/lib/channel/src/ch_awgn.c b/lte/lib/channel/src/ch_awgn.c new file mode 100644 index 000000000..5729a1ca0 --- /dev/null +++ b/lte/lib/channel/src/ch_awgn.c @@ -0,0 +1,69 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include + +#include "gauss.h" +#include "lte/channel/ch_awgn.h" + +void ch_awgn_c(const cf_t* x, cf_t* y, float variance, int buff_sz) { + _Complex float tmp; + int i; + + for (i=0;iinput,hl->output,hl->ctrl_in.variance,hl->in_len); + hl->out_len = hl->in_len; + return 0; +} + +int ch_awgn_stop(ch_awgn_hl* hl) { + return 0; +} diff --git a/lte/lib/channel/src/gauss.c b/lte/lib/channel/src/gauss.c new file mode 100644 index 000000000..b2b23d96c --- /dev/null +++ b/lte/lib/channel/src/gauss.c @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include + +float rand_gauss (void) { + float v1,v2,s; + + do { + v1 = 2.0 * ((float) rand()/RAND_MAX) - 1; + v2 = 2.0 * ((float) rand()/RAND_MAX) - 1; + + s = v1*v1 + v2*v2; + } while ( s >= 1.0 || s == 0.0); + + return (v1*sqrt(-2.0 * log(s) / s)); +} diff --git a/lte/lib/channel/src/gauss.h b/lte/lib/channel/src/gauss.h new file mode 100644 index 000000000..a36fc04eb --- /dev/null +++ b/lte/lib/channel/src/gauss.h @@ -0,0 +1,29 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +float rand_gauss (void); diff --git a/lib/lte/src/fft.c b/lte/lib/common/src/fft.c similarity index 57% rename from lib/lte/src/fft.c rename to lte/lib/common/src/fft.c index 9f6f2f6b3..e5fae9226 100644 --- a/lib/lte/src/fft.c +++ b/lte/lib/common/src/fft.c @@ -1,28 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "lte/base.h" -#include "utils/dft.h" -#include "lte/fft.h" -#include "utils/debug.h" +#include "lte/common/base.h" +#include "lte/utils/dft.h" +#include "lte/common/fft.h" +#include "lte/utils/debug.h" +#include "lte/utils/vector.h" int lte_fft_init_(lte_fft_t *q, lte_cp_t cp_type, int nof_prb, dft_dir_t dir) { int symbol_sz = lte_symbol_sz(nof_prb); @@ -41,7 +52,12 @@ int lte_fft_init_(lte_fft_t *q, lte_cp_t cp_type, int nof_prb, dft_dir_t dir) { return -1; } - q->fft_plan.options = DFT_DC_OFFSET | DFT_MIRROR_POS | DFT_NORMALIZE; + q->fft_plan.options = DFT_NORMALIZE; + if (dir==FORWARD) { + q->fft_plan.options |= DFT_DC_OFFSET | DFT_MIRROR_POS; + } else { + q->fft_plan.options |= DFT_DC_OFFSET | DFT_MIRROR_PRE; + } q->symbol_sz = symbol_sz; q->nof_symbols = CP_NSYMB(cp_type); q->cp_type = cp_type; @@ -67,7 +83,16 @@ void lte_fft_free(lte_fft_t *q) { } int lte_ifft_init(lte_fft_t *q, lte_cp_t cp_type, int nof_prb) { - return lte_fft_init_(q, cp_type, nof_prb, BACKWARD); + int i; + if (lte_fft_init_(q, cp_type, nof_prb, BACKWARD)) { + return -1; + } + /* set now zeros at CP */ + for (i=0;inof_symbols;i++) { + bzero(q->tmp, q->nof_guards * sizeof(cf_t)); + bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); + } + return 0; } void lte_ifft_free(lte_fft_t *q) { @@ -92,6 +117,15 @@ void lte_fft_run(lte_fft_t *q, cf_t *input, cf_t *output) { * Performs FFT on a each symbol and adds CP. */ void lte_ifft_run(lte_fft_t *q, cf_t *input, cf_t *output) { - fprintf(stderr, "Error: Not implemented\n"); + int i, cp_len; + for (i=0;inof_symbols;i++) { + cp_len = CP_ISNORM(q->cp_type)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz); + memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); + dft_run_c2c(&q->fft_plan, q->tmp, &output[cp_len]); + input += q->nof_re; + /* add CP */ + memcpy(output, &output[q->symbol_sz], cp_len * sizeof(cf_t)); + output += q->symbol_sz + cp_len; + } } diff --git a/lib/lte/src/lte.c b/lte/lib/common/src/lte.c similarity index 86% rename from lib/lte/src/lte.c rename to lte/lib/common/src/lte.c index a80914278..74b2734fd 100644 --- a/lib/lte/src/lte.c +++ b/lte/lib/common/src/lte.c @@ -1,26 +1,37 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + + #include #include -#include "lte/base.h" +#include "lte/common/base.h" const int lte_symbol_sz(int nof_prb) { if (nof_prb<=0) { diff --git a/lte/lib/common/src/phch_sequence.c b/lte/lib/common/src/phch_sequence.c new file mode 100644 index 000000000..6b2797859 --- /dev/null +++ b/lte/lib/common/src/phch_sequence.c @@ -0,0 +1,34 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "lte/common/base.h" +#include "lte/common/sequence.h" + +int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { + return sequence_LTEPRS(seq, CP_ISNORM(cp)?1920:1728, cell_id); +} diff --git a/lib/lte/src/sequence.c b/lte/lib/common/src/sequence.c similarity index 69% rename from lib/lte/src/sequence.c rename to lte/lib/common/src/sequence.c index ec1a04a97..32e00359b 100644 --- a/lib/lte/src/sequence.c +++ b/lte/lib/common/src/sequence.c @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ -#include "lte/sequence.h" + +#include "lte/common/sequence.h" #include #include diff --git a/lib/fec/src/convcoder.c b/lte/lib/fec/src/convcoder.c similarity index 65% rename from lib/fec/src/convcoder.c rename to lte/lib/fec/src/convcoder.c index ce60e7b45..183afeee3 100644 --- a/lib/fec/src/convcoder.c +++ b/lte/lib/fec/src/convcoder.c @@ -1,26 +1,36 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "fec/convcoder.h" +#include "lte/fec/convcoder.h" #include "parity.h" int convcoder_encode(convcoder_t *q, char *input, char *output) { @@ -36,13 +46,14 @@ int convcoder_encode(convcoder_t *q, char *input, char *output) { } else { sr = 0; } - + //printf("Start state %d\n", sr); for (i = 0; i < len; i++) { int bit = (i < q->framelength) ? (input[i] & 1) : 0; sr = (sr << 1) | bit; for (j=0;jR;j++) { output[q->R * i + j] = parity(sr & q->poly[j]); } + //printf("%3d - sr=%u\n", i, sr%64); } return q->R*len; diff --git a/lib/fec/src/crc.c b/lte/lib/fec/src/crc.c similarity index 66% rename from lib/fec/src/crc.c rename to lte/lib/fec/src/crc.c index a10475a78..6360d3537 100644 --- a/lib/fec/src/crc.c +++ b/lte/lib/fec/src/crc.c @@ -1,22 +1,34 @@ -/* - * Copyright (c) 2012, Ismael Gomez-Miguelez . - * This file is part of ALOE++ (http://flexnets.upc.edu/) +/** * - * ALOE++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * ALOE++ is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with ALOE++. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ +#include +#include + unsigned int cword; unsigned int icrc1(unsigned int crc, unsigned short onech,int long_crc, diff --git a/lib/fec/src/parity.h b/lte/lib/fec/src/parity.h similarity index 95% rename from lib/fec/src/parity.h rename to lte/lib/fec/src/parity.h index 58cd67ab5..56689124b 100644 --- a/lib/fec/src/parity.h +++ b/lte/lib/fec/src/parity.h @@ -1,4 +1,4 @@ -/* User include file for libfec +/* * Copyright 2004, Phil Karn, KA9Q * May be used under the terms of the GNU Lesser General Public License (LGPL) */ diff --git a/lte/lib/fec/src/viterbi.c b/lte/lib/fec/src/viterbi.c new file mode 100644 index 000000000..614fbb48a --- /dev/null +++ b/lte/lib/fec/src/viterbi.c @@ -0,0 +1,233 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "lte/utils/vector.h" +#include "lte/fec/viterbi.h" +#include "parity.h" +#include "viterbi37.h" +#include "viterbi39.h" + +#define DEB 0 + +int decode37(void *o, unsigned char *symbols, char *data) { + viterbi_t *q = o; + int i; + + int best_state; + + /* Initialize Viterbi decoder */ + init_viterbi37_port(q->ptr, q->tail_biting?-1:0); + + /* Decode block */ + if (q->tail_biting) { + memcpy(q->tmp, symbols, 3 * q->framebits * sizeof(char)); + for (i=0;i<3*(q->K-1);i++) { + q->tmp[i+3*q->framebits] = q->tmp[i]; + } + } else { + q->tmp = symbols; + } + + update_viterbi37_blk_port(q->ptr, q->tmp, q->framebits + q->K - 1, q->tail_biting?&best_state:NULL); + + /* Do Viterbi chainback */ + chainback_viterbi37_port(q->ptr, data, q->framebits, q->tail_biting?best_state:0); + + return q->framebits; +} + +int decode39(void *o, unsigned char *symbols, char *data) { + viterbi_t *q = o; + + /* Initialize Viterbi decoder */ + init_viterbi39_port(q->ptr, 0); + + /* Decode block */ + update_viterbi39_blk_port(q->ptr, symbols,q->framebits + q->K - 1); + + /* Do Viterbi chainback */ + chainback_viterbi39_port(q->ptr, data, q->framebits, 0); + + return q->framebits; +} + + +void free37(void *o) { + viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_port(q->ptr); +} + +void free39(void *o) { + viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + delete_viterbi39_port(q->ptr); +} + +int init37(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->tail_biting = tail_biting; + q->decode = decode37; + q->free = free37; + q->symbols_uc = malloc(3 * (q->framebits + q->K -1) * sizeof(char)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = malloc(3 * (q->framebits + q->K -1) * sizeof(char)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_port(poly, framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} + +int init39(viterbi_t *q, int poly[3], int framebits, bool tail_biting) { + q->K = 9; + q->R = 3; + q->framebits = framebits; + q->tail_biting = tail_biting; + q->decode = decode39; + q->free = free39; + if (q->tail_biting) { + fprintf(stderr, "Error: Tailbitting not supported in 1/3 K=9 decoder\n"); + return -1; + } + q->symbols_uc = malloc(3 * (q->framebits + q->K -1) * sizeof(char)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if ((q->ptr = create_viterbi39_port(poly, framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free39(q); + return -1; + } else { + return 0; + } +} + +int viterbi_init(viterbi_t *q, viterbi_type_t type, int poly[3], int framebits, bool tail_bitting) { + switch(type) { + case viterbi_37: + return init37(q, poly, framebits, tail_bitting); + case viterbi_39: + return init39(q, poly, framebits, tail_bitting); + default: + fprintf(stderr, "Decoder not implemented\n"); + return -1; + } +} + +void viterbi_free(viterbi_t *q) { + q->free(q); +} + +/* symbols are real-valued */ +int viterbi_decode_f(viterbi_t *q, float *symbols, char *data) { + vec_quant_fuc(symbols, q->symbols_uc, 32, 127.5, 255, 3 * (q->framebits + q->K - 1)); + return q->decode(q, q->symbols_uc, data); +} + +int viterbi_decode_uc(viterbi_t *q, unsigned char *symbols, char *data) { + return q->decode(q, symbols, data); +} + + +int viterbi_initialize(viterbi_hl* h) { + int poly[3]; + viterbi_type_t type; + if (h->init.rate == 2) { + if (h->init.constraint_length == 7) { + type = viterbi_27; + } else if (h->init.constraint_length == 9) { + type = viterbi_29; + } else { + fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, + h->init.constraint_length); + return -1; + } + } else if (h->init.rate == 3) { + if (h->init.constraint_length == 7) { + type = viterbi_37; + } else if (h->init.constraint_length == 9) { + type = viterbi_39; + } else { + fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, + h->init.constraint_length); + return -1; + } + } else { + fprintf(stderr, "Unsupported decoder %d/%d\n", h->init.rate, + h->init.constraint_length); + return -1; + } + poly[0] = h->init.generator_0; + poly[1] = h->init.generator_1; + poly[2] = h->init.generator_2; + return viterbi_init(&h->obj, type, poly, h->init.frame_length, + h->init.tail_bitting?true:false); +} + +int viterbi_work(viterbi_hl* hl) { + if (hl->in_len != hl->init.frame_length) { + fprintf(stderr, "Expected input length %d but got %d\n", hl->init.frame_length, hl->in_len); + return -1; + } + return viterbi_decode_f(&hl->obj, hl->input, hl->output); +} + +int viterbi_stop(viterbi_hl* h) { + viterbi_free(&h->obj); + return 0; +} diff --git a/lte/lib/fec/src/viterbi37.h b/lte/lib/fec/src/viterbi37.h new file mode 100644 index 000000000..6afc563e8 --- /dev/null +++ b/lte/lib/fec/src/viterbi37.h @@ -0,0 +1,34 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +void *create_viterbi37_port(int polys[3], int len); +int init_viterbi37_port(void *p, int starting_state); +int chainback_viterbi37_port(void *p, char *data, unsigned int nbits, unsigned int endstate); +void delete_viterbi37_port(void *p); +int update_viterbi37_blk_port(void *p, unsigned char *syms, int nbits, int *best_state); diff --git a/lib/fec/src/viterbi37_port.c b/lte/lib/fec/src/viterbi37_port.c similarity index 75% rename from lib/fec/src/viterbi37_port.c rename to lte/lib/fec/src/viterbi37_port.c index e1467c3b6..56ee3645c 100644 --- a/lib/fec/src/viterbi37_port.c +++ b/lte/lib/fec/src/viterbi37_port.c @@ -5,10 +5,9 @@ #include #include #include - -#include "parity.h" #include "viterbi37.h" -#include "utils/debug.h" +#include "parity.h" +#include typedef union { unsigned int w[64]; @@ -18,11 +17,9 @@ typedef union { } decision_t; static union { - unsigned char c[32]; + unsigned char c[128]; } Branchtab37[3]; -#define DEB 0 - /* State info for instance of Viterbi decoder */ struct v37 { metric_t metrics1; /* path metric buffer 1 */ @@ -39,7 +36,6 @@ int init_viterbi37_port(void *p, int starting_state) { if (p == NULL) return -1; - for (i = 0; i < 64; i++) vp->metrics1.w[i] = 63; @@ -47,7 +43,7 @@ int init_viterbi37_port(void *p, int starting_state) { vp->new_metrics = &vp->metrics2; vp->dp = vp->decisions; if (starting_state != -1) { - vp->old_metrics->w[starting_state & 63] = 0; /* Bias known start state */ + vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */ } return 0; } @@ -66,7 +62,7 @@ void set_viterbi37_polynomial_port(int polys[3]) { } /* Create a new instance of a Viterbi decoder */ -void *create_viterbi37_port(int polys[3], int len, bool tail_biting) { +void *create_viterbi37_port(int polys[3], int len) { struct v37 *vp; set_viterbi37_polynomial_port(polys); @@ -79,16 +75,15 @@ void *create_viterbi37_port(int polys[3], int len, bool tail_biting) { free(vp); return NULL ; } - init_viterbi37_port(vp, tail_biting?-1:0); + init_viterbi37_port(vp, 0); return vp; } /* Viterbi chainback */ int chainback_viterbi37_port(void *p, char *data, /* Decoded output data */ - unsigned int nbits, /* Number of data bits */ - unsigned int endstate) { /* Terminal encoder state */ - + unsigned int nbits, /* Number of data bits */ + unsigned int endstate) { /* Terminal encoder state */ struct v37 *vp = p; decision_t *d; @@ -100,8 +95,9 @@ int chainback_viterbi37_port(void *p, char *data, /* Decoded output data */ /* Make room beyond the end of the encoder register so we can * accumulate a full byte of decoded data */ + endstate %= 64; + endstate <<= 2; - endstate=0; /* The store into data[] only needs to be done every 8 bits. * But this avoids a conditional branch, and the writes will * combine in the cache anyway @@ -144,23 +140,12 @@ unsigned int metric,m0,m1,decision;\ d->w[i/16] |= decision << ((2*i+1)&31);\ } -unsigned char tochar_clip(float sym, float amp) { - float ret = sym * (127.5 / amp) + 127.5; - if (ret > 255) { - ret = 255; - } - if (ret < 0) { - ret = 0; - } - return (unsigned char) ret; -} - /* Update decoder with a block of demodulated symbols * Note that nbits is the number of decoded data bits, not the number * of symbols! */ -int update_viterbi37_blk_port(void *p, float *syms, int nbits, float amp, int framebits) { +int update_viterbi37_blk_port(void *p, unsigned char *syms, int nbits, int *best_state) { struct v37 *vp = p; decision_t *d; @@ -168,63 +153,37 @@ int update_viterbi37_blk_port(void *p, float *syms, int nbits, float amp, int fr return -1; int k=0; d = (decision_t *) vp->dp; - while (nbits--) { void *tmp; unsigned char sym0, sym1, sym2; + int i; d->w[0] = d->w[1] = 0; - k++; - - if (k < framebits) { - sym0 = tochar_clip(*syms++, amp); - sym1 = tochar_clip(*syms++, amp); - sym2 = tochar_clip(*syms++, amp); - } else { - sym0=255; - sym1=255; - sym2=255; - } + sym0 = *syms++; + sym1 = *syms++; + sym2 = *syms++; - BFLY(0); - BFLY(1); - BFLY(2); - BFLY(3); - BFLY(4); - BFLY(5); - BFLY(6); - BFLY(7); - BFLY(8); - BFLY(9); - BFLY(10); - BFLY(11); - BFLY(12); - BFLY(13); - BFLY(14); - BFLY(15); - BFLY(16); - BFLY(17); - BFLY(18); - BFLY(19); - BFLY(20); - BFLY(21); - BFLY(22); - BFLY(23); - BFLY(24); - BFLY(25); - BFLY(26); - BFLY(27); - BFLY(28); - BFLY(29); - BFLY(30); - BFLY(31); + k++; + for (i = 0; i < 32; i++) + BFLY(i); d++; tmp = vp->old_metrics; vp->old_metrics = vp->new_metrics; vp->new_metrics = tmp; } + if (best_state) { + int i, bst=0; + unsigned int minmetric=UINT_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->w[i] < minmetric) { + bst = i; + minmetric = vp->old_metrics->w[i]; + } + } + *best_state = bst; + } vp->dp = d; return 0; } diff --git a/lte/lib/fec/src/viterbi39.h b/lte/lib/fec/src/viterbi39.h new file mode 100644 index 000000000..f9179d2bc --- /dev/null +++ b/lte/lib/fec/src/viterbi39.h @@ -0,0 +1,36 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +void *create_viterbi39_port(int polys[3], int len); +int init_viterbi39_port(void *p, int starting_state); +int chainback_viterbi39_port(void *p, char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate); +void delete_viterbi39_port(void *p); +int update_viterbi39_blk_port(void *p, unsigned char *syms, int nbits); diff --git a/lte/lib/fec/src/viterbi39_port.c b/lte/lib/fec/src/viterbi39_port.c new file mode 100644 index 000000000..0ed0e55c0 --- /dev/null +++ b/lte/lib/fec/src/viterbi39_port.c @@ -0,0 +1,172 @@ +/* K=9 r=1/3 Viterbi decoder in portable C + * Copyright Aug 2006, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include "viterbi39.h" +#include "parity.h" + +typedef union { + unsigned int w[256]; +} metric_t; +typedef union { + unsigned long w[8]; +} decision_t; + +static union { + unsigned char c[128]; +} Branchtab39[3]; + +/* State info for instance of Viterbi decoder */ +struct v39 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics, *new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi39_port(void *p, int starting_state) { + struct v39 *vp = p; + int i; + + if (p == NULL) + return -1; + for (i = 0; i < 256; i++) + vp->metrics1.w[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +void set_viterbi39_polynomial_port(int polys[3]) { + int state; + + for (state = 0; state < 128; state++) { + Branchtab39[0].c[state] = + (polys[0] < 0) ^ parity((2 * state) & abs(polys[0])) ? 255 : 0; + Branchtab39[1].c[state] = + (polys[1] < 0) ^ parity((2 * state) & abs(polys[1])) ? 255 : 0; + Branchtab39[2].c[state] = + (polys[2] < 0) ^ parity((2 * state) & abs(polys[2])) ? 255 : 0; + } +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi39_port(int polys[3], int len) { + struct v39 *vp; + + set_viterbi39_polynomial_port(polys); + + if ((vp = (struct v39 *) malloc(sizeof(struct v39))) == NULL) + return NULL ; + + if ((vp->decisions = (decision_t *) malloc((len + 8) * sizeof(decision_t))) + == NULL) { + free(vp); + return NULL ; + } + init_viterbi39_port(vp, 0); + + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi39_port(void *p, char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate) { /* Terminal encoder state */ + struct v39 *vp = p; + decision_t *d; + + if (p == NULL) + return -1; + + d = vp->decisions; + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 256; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 8; /* Look past tail */ + while (nbits-- != 0) { + int k; + + k = (d[nbits].w[(endstate) / 32] >> (endstate % 32)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi39_port(void *p) { + struct v39 *vp = p; + + if (vp != NULL) { + free(vp->decisions); + free(vp); + } +} + +/* C-language butterfly */ +#define BFLY(i) {\ +unsigned int metric,m0,m1,decision;\ + metric = (Branchtab39[0].c[i] ^ sym0) + (Branchtab39[1].c[i] ^ sym1) + \ + (Branchtab39[2].c[i] ^ sym2);\ + m0 = vp->old_metrics->w[i] + metric;\ + m1 = vp->old_metrics->w[i+128] + (765 - metric);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i)&31);\ + m0 -= (metric+metric-765);\ + m1 += (metric+metric-765);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i+1] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i+1)&31);\ +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ + +int update_viterbi39_blk_port(void *p, unsigned char *syms, int nbits) { + struct v39 *vp = p; + decision_t *d; + + if (p == NULL) + return -1; + + d = (decision_t *) vp->dp; + while (nbits--) { + void *tmp; + unsigned char sym0, sym1, sym2; + int i; + + for (i = 0; i < 8; i++) + d->w[i] = 0; + sym0 = *syms++; + sym1 = *syms++; + sym2 = *syms++; + + for (i = 0; i < 128; i++) + BFLY(i); + + d++; + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} diff --git a/lib/filter/src/filter2d.c b/lte/lib/filter/src/filter2d.c similarity index 78% rename from lib/filter/src/filter2d.c rename to lte/lib/filter/src/filter2d.c index 8da3d4264..daf11742c 100644 --- a/lib/filter/src/filter2d.c +++ b/lte/lib/filter/src/filter2d.c @@ -1,31 +1,41 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "utils/debug.h" +#include "lte/utils/debug.h" -#include "filter/filter2d.h" -#include "utils/matrix.h" -#include "utils/vector.h" -#include "utils/debug.h" +#include "lte/filter/filter2d.h" +#include "lte/utils/matrix.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" /* Useful macros */ #define intceil(X, Y) ((X-1)/Y+1) diff --git a/lib/io/src/binsource.c b/lte/lib/io/src/binsource.c similarity index 81% rename from lib/io/src/binsource.c rename to lte/lib/io/src/binsource.c index a3093b22e..cea416d5f 100644 --- a/lib/io/src/binsource.c +++ b/lte/lib/io/src/binsource.c @@ -1,27 +1,37 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "io/binsource.h" -#include "utils/bit.h" +#include "lte/io/binsource.h" +#include "lte/utils/bit.h" #define DIV(a,b) ((a-1)/b+1) diff --git a/lib/io/src/filesink.c b/lte/lib/io/src/filesink.c similarity index 62% rename from lib/io/src/filesink.c rename to lte/lib/io/src/filesink.c index ac128dfe1..c03feea3a 100644 --- a/lib/io/src/filesink.c +++ b/lte/lib/io/src/filesink.c @@ -1,3 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #include #include @@ -5,9 +32,9 @@ #include -#include "io/filesink.h" +#include "lte/io/filesink.h" -int filesink_init(filesink_t *q, char *filename, file_data_type_t type) { +int filesink_init(filesink_t *q, char *filename, data_type_t type) { bzero(q, sizeof(filesink_t)); q->f = fopen(filename, "w"); if (!q->f) { @@ -18,7 +45,7 @@ int filesink_init(filesink_t *q, char *filename, file_data_type_t type) { return 0; } -void filesink_close(filesink_t *q) { +void filesink_free(filesink_t *q) { if (q->f) { fclose(q->f); } @@ -87,6 +114,6 @@ int filesink_work(filesink_hl* h) { } int filesink_stop(filesink_hl* h) { - filesink_close(&h->obj); + filesink_free(&h->obj); return 0; } diff --git a/lib/io/src/filesource.c b/lte/lib/io/src/filesource.c similarity index 62% rename from lib/io/src/filesource.c rename to lte/lib/io/src/filesource.c index 80c28066b..d2dd1217b 100644 --- a/lib/io/src/filesource.c +++ b/lte/lib/io/src/filesource.c @@ -1,11 +1,38 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #include #include #include -#include "io/filesource.h" +#include "lte/io/filesource.h" -int filesource_init(filesource_t *q, char *filename, file_data_type_t type) { +int filesource_init(filesource_t *q, char *filename, data_type_t type) { bzero(q, sizeof(filesource_t)); q->f = fopen(filename, "r"); if (!q->f) { @@ -16,13 +43,17 @@ int filesource_init(filesource_t *q, char *filename, file_data_type_t type) { return 0; } -void filesource_close(filesource_t *q) { +void filesource_free(filesource_t *q) { if (q->f) { fclose(q->f); } bzero(q, sizeof(filesource_t)); } +void filesource_seek(filesource_t *q, int pos) { + fseek(q->f, pos, SEEK_SET); +} + int read_complex_f(FILE *f, _Complex float *y) { char in_str[64]; _Complex float x; @@ -100,6 +131,6 @@ int filesource_work(filesource_hl* h) { } int filesource_stop(filesource_hl* h) { - filesource_close(&h->obj); + filesource_free(&h->obj); return 0; } diff --git a/lte/lib/io/src/udpsink.c b/lte/lib/io/src/udpsink.c new file mode 100644 index 000000000..52884407f --- /dev/null +++ b/lte/lib/io/src/udpsink.c @@ -0,0 +1,102 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "lte/io/udpsink.h" + +int udpsink_init(udpsink_t *q, char *address, int port, data_type_t type) { + bzero(q, sizeof(udpsink_t)); + + q->sockfd=socket(AF_INET,SOCK_DGRAM,0); + + q->servaddr.sin_family = AF_INET; + q->servaddr.sin_addr.s_addr=inet_addr(address); + q->servaddr.sin_port=htons(port); + + q->type = type; + return 0; +} + +void udpsink_free(udpsink_t *q) { + if (q->sockfd) { + close(q->sockfd); + } + bzero(q, sizeof(udpsink_t)); +} + +int udpsink_write(udpsink_t *q, void *buffer, int nsamples) { + int size; + + switch(q->type) { + case FLOAT: + case COMPLEX_FLOAT: + case COMPLEX_SHORT: + fprintf(stderr, "Not implemented\n"); + return -1; + case FLOAT_BIN: + case COMPLEX_FLOAT_BIN: + case COMPLEX_SHORT_BIN: + if (q->type == FLOAT_BIN) { + size = sizeof(float); + } else if (q->type == COMPLEX_FLOAT_BIN) { + size = sizeof(_Complex float); + } else if (q->type == COMPLEX_SHORT_BIN) { + size = sizeof(_Complex short); + } + return sendto(q->sockfd, buffer, nsamples * size, 0, + &q->servaddr, sizeof(struct sockaddr_in)); + break; + } + return -1; +} + + + +int udpsink_initialize(udpsink_hl* h) { + return udpsink_init(&h->obj, h->init.address, h->init.port, h->init.data_type); +} + +int udpsink_work(udpsink_hl* h) { + if (udpsink_write(&h->obj, h->input, h->in_len)<0) { + return -1; + } + return 0; +} + +int udpsink_stop(udpsink_hl* h) { + udpsink_free(&h->obj); + return 0; +} diff --git a/lte/lib/io/src/udpsource.c b/lte/lib/io/src/udpsource.c new file mode 100644 index 000000000..846d08608 --- /dev/null +++ b/lte/lib/io/src/udpsource.c @@ -0,0 +1,100 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lte/io/udpsource.h" + +int udpsource_init(udpsource_t *q, char *address, int port, data_type_t type) { + bzero(q, sizeof(udpsource_t)); + + q->sockfd=socket(AF_INET,SOCK_DGRAM,0); + + q->servaddr.sin_family = AF_INET; + q->servaddr.sin_addr.s_addr=inet_addr(address); + q->servaddr.sin_port=htons(port); + bind(q->sockfd,(struct sockaddr *)&q->servaddr,sizeof(struct sockaddr_in)); + + q->type = type; + return 0; +} + +void udpsource_free(udpsource_t *q) { + if (q->sockfd) { + close(q->sockfd); + } + bzero(q, sizeof(udpsource_t)); +} + +int udpsource_read(udpsource_t *q, void *buffer, int nsamples) { + int size; + + switch(q->type) { + case FLOAT: + case COMPLEX_FLOAT: + case COMPLEX_SHORT: + fprintf(stderr, "Not implemented\n"); + return -1; + case FLOAT_BIN: + case COMPLEX_FLOAT_BIN: + case COMPLEX_SHORT_BIN: + if (q->type == FLOAT_BIN) { + size = sizeof(float); + } else if (q->type == COMPLEX_FLOAT_BIN) { + size = sizeof(_Complex float); + } else if (q->type == COMPLEX_SHORT_BIN) { + size = sizeof(_Complex short); + } + return recv(q->sockfd, buffer, size * nsamples, 0); + break; + } + return -1; +} + + +int udpsource_initialize(udpsource_hl* h) { + return udpsource_init(&h->obj, h->init.address, h->init.port, h->init.data_type); +} + +int udpsource_work(udpsource_hl* h) { + h->out_len = udpsource_read(&h->obj, h->output, h->ctrl_in.nsamples); + if (h->out_len < 0) { + return -1; + } + return 0; +} + +int udpsource_stop(udpsource_hl* h) { + udpsource_free(&h->obj); + return 0; +} diff --git a/lte/lib/mimo/src/layermap.c b/lte/lib/mimo/src/layermap.c new file mode 100644 index 000000000..07de82911 --- /dev/null +++ b/lte/lib/mimo/src/layermap.c @@ -0,0 +1,78 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include + +#include "lte/common/base.h" +#include "lte/mimo/layermap.h" + +/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" + */ +void layermap_decode(cf_t *x[MAX_LAYERS], cf_t *d[MAX_CODEWORDS], int nof_layers, int nof_cw, + int nof_layer_symbols, mimo_type_t type) { + + int i; + + switch(nof_layers) { + case 1: + memcpy(d[0], x[0], nof_layer_symbols * sizeof(cf_t)); + break; + case 2: + switch(type) { + case TX_DIVERSITY: + for (i=0;i +#include +#include +#include + +#include "lte/common/base.h" +#include "lte/mimo/precoding.h" +#include "lte/utils/vector.h" + +/* 36.211 v10.3.0 Section 6.3.4 */ +void precoding_decode(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], + cf_t *x[MAX_LAYERS], int nof_ports, int nof_symbols, mimo_type_t type) { + + int i; + cf_t h0, h1, r0, r1; + float hh; + + switch(nof_ports) { + case 1: + vec_div_ccc(y[0], ce[0], x[0], nof_symbols); + break; + case 2: + switch(type) { + case TX_DIVERSITY: + /* FIXME: Use VOLK here */ + // 6.3.4.3 + for (i=0;i. - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "modem/demod_hard.h" +#include "lte/modem/demod_hard.h" #include "hard_demod_lte.h" @@ -31,7 +41,7 @@ void demod_hard_table(demod_hard_t* q, enum modem_std table) { q->table = table; } -int demod_hard_demodulate(demod_hard_t* q, const cf* symbols, char *bits, int nsymbols) { +int demod_hard_demodulate(demod_hard_t* q, const cf_t* symbols, char *bits, int nsymbols) { int nbits=-1; switch(q->table) { diff --git a/lib/modem/src/demod_soft.c b/lte/lib/modem/src/demod_soft.c similarity index 67% rename from lib/modem/src/demod_soft.c rename to lte/lib/modem/src/demod_soft.c index 3426132a6..3180ff114 100644 --- a/lib/modem/src/demod_soft.c +++ b/lte/lib/modem/src/demod_soft.c @@ -1,26 +1,36 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "utils/bit.h" -#include "modem/demod_soft.h" +#include "lte/utils/bit.h" +#include "lte/modem/demod_soft.h" #include "soft_algs.h" @@ -40,7 +50,7 @@ void demod_soft_sigma_set(demod_soft_t *q, float sigma) { q->sigma = sigma; } -int demod_soft_demodulate(demod_soft_t *q, const cf* symbols, float* llr, int nsymbols) { +int demod_soft_demodulate(demod_soft_t *q, const cf_t* symbols, float* llr, int nsymbols) { switch(q->alg_type) { case EXACT: llr_exact(symbols, llr, nsymbols, q->table->nsymbols, q->table->nbits_x_symbol, diff --git a/lib/modem/src/hard_demod_lte.c b/lte/lib/modem/src/hard_demod_lte.c similarity index 80% rename from lib/modem/src/hard_demod_lte.c rename to lte/lib/modem/src/hard_demod_lte.c index 282ccf36f..b726c6217 100644 --- a/lib/modem/src/hard_demod_lte.c +++ b/lte/lib/modem/src/hard_demod_lte.c @@ -1,26 +1,36 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez , Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "modem/demod_hard.h" +#include "lte/modem/demod_hard.h" #include "hard_demod_lte.h" /** @@ -36,7 +46,7 @@ * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_bpsk_demod(const cf* in, char* out, int N) +inline void hard_bpsk_demod(const cf_t* in, char* out, int N) { int s; @@ -71,7 +81,7 @@ inline void hard_bpsk_demod(const cf* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qpsk_demod(const cf* in, char* out, int N) +inline void hard_qpsk_demod(const cf_t* in, char* out, int N) { int s; @@ -105,7 +115,7 @@ inline void hard_qpsk_demod(const cf* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qam16_demod(const cf* in, char* out, int N) +inline void hard_qam16_demod(const cf_t* in, char* out, int N) { int s; @@ -147,7 +157,7 @@ inline void hard_qam16_demod(const cf* in, char* out, int N) * \param N Number of input symbols * \param modulation Modulation type */ -inline void hard_qam64_demod(const cf* in, char* out, int N) +inline void hard_qam64_demod(const cf_t* in, char* out, int N) { int s; diff --git a/lte/lib/modem/src/hard_demod_lte.h b/lte/lib/modem/src/hard_demod_lte.h new file mode 100644 index 000000000..be79212e8 --- /dev/null +++ b/lte/lib/modem/src/hard_demod_lte.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +/* Thresholds for Demodulation */ +/* Assume perfect amplitude and phase alignment. + * Check threshold values for real case + * or implement dynamic threshold adjustent as a function of received symbol amplitudes */ +#define QAM16_THRESHOLD 2/sqrt(10) +#define QAM64_THRESHOLD_1 2/sqrt(42) +#define QAM64_THRESHOLD_2 4/sqrt(42) +#define QAM64_THRESHOLD_3 6/sqrt(42) + +void hard_bpsk_demod(const cf_t* in, char* out, int N); +void hard_qpsk_demod(const cf_t* in, char* out, int N); +void hard_qam16_demod(const cf_t* in, char* out, int N); +void hard_qam64_demod(const cf_t* in, char* out, int N); diff --git a/lib/modem/src/lte_tables.c b/lte/lib/modem/src/lte_tables.c similarity index 89% rename from lib/modem/src/lte_tables.c rename to lte/lib/modem/src/lte_tables.c index a970e87d5..cc169fbf0 100644 --- a/lib/modem/src/lte_tables.c +++ b/lte/lib/modem/src/lte_tables.c @@ -1,32 +1,42 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez , Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include -#include "modem/modem_table.h" +#include "lte/modem/modem_table.h" #include "lte_tables.h" /** * Set the BPSK modulation table */ -void set_BPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) +void set_BPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { // LTE-BPSK constellation: // Q @@ -47,7 +57,7 @@ void set_BPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) /** * Set the QPSK modulation table */ -void set_QPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) +void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { int i,j; @@ -85,7 +95,7 @@ void set_QPSKtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) /** * Set the 16QAM modulation table */ -void set_16QAMtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) +void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { int i,j; // LTE-16QAM constellation: @@ -150,7 +160,7 @@ void set_16QAMtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod /** * Set the 64QAM modulation table */ -void set_64QAMtable(cf* table, soft_table_t *soft_table, bool compute_soft_demod) +void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod) { int i,j; // LTE-64QAM constellation: diff --git a/lte/lib/modem/src/lte_tables.h b/lte/lib/modem/src/lte_tables.h new file mode 100644 index 000000000..1e5280137 --- /dev/null +++ b/lte/lib/modem/src/lte_tables.h @@ -0,0 +1,51 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#define BPSK_LEVEL 1/sqrt(2) + +#define QPSK_LEVEL 1/sqrt(2) + +#define QAM16_LEVEL_1 1/sqrt(10) +#define QAM16_LEVEL_2 3/sqrt(10) + +#define QAM64_LEVEL_1 1/sqrt(42) +#define QAM64_LEVEL_2 3/sqrt(42) +#define QAM64_LEVEL_3 5/sqrt(42) +#define QAM64_LEVEL_4 7/sqrt(42) + +#define QAM64_LEVEL_x 2/sqrt(42) +/* this is not an QAM64 level, but, rather, an auxiliary value that can be used for computing the + * symbol from the bit sequence */ + + + + +void set_BPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); +void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); +void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); +void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod); diff --git a/lib/modem/src/mod.c b/lte/lib/modem/src/mod.c similarity index 51% rename from lib/modem/src/mod.c rename to lte/lib/modem/src/mod.c index 979139615..968f202ba 100644 --- a/lib/modem/src/mod.c +++ b/lte/lib/modem/src/mod.c @@ -1,35 +1,47 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include +#include -#include "utils/bit.h" -#include "modem/mod.h" +#include "lte/utils/bit.h" +#include "lte/modem/mod.h" /** Low-level API */ -int mod_modulate(modem_table_t* q, const char *bits, cf* symbols, int nbits) { +int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, int nbits) { int i,j,idx; char *b_ptr=(char*) bits; j=0; for (i=0;inbits_x_symbol) { idx = bit_unpack(&b_ptr,q->nbits_x_symbol); + assert(idx >= 0 && idx < q->nsymbols); symbols[j] = q->symbol_table[idx]; j++; } diff --git a/lib/modem/src/modem_table.c b/lte/lib/modem/src/modem_table.c similarity index 67% rename from lib/modem/src/modem_table.c rename to lte/lib/modem/src/modem_table.c index 1877cb046..3ec2888c0 100644 --- a/lib/modem/src/modem_table.c +++ b/lte/lib/modem/src/modem_table.c @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include @@ -23,12 +33,12 @@ #include #include -#include "modem/modem_table.h" +#include "lte/modem/modem_table.h" #include "lte_tables.h" /** Internal functions */ static int table_create(modem_table_t* q) { - q->symbol_table = malloc(q->nsymbols*sizeof(cf)); + q->symbol_table = malloc(q->nsymbols*sizeof(cf_t)); return q->symbol_table==NULL; } @@ -46,7 +56,7 @@ void modem_table_reset(modem_table_t* q) { modem_table_init(q); } -int modem_table_set(modem_table_t* q, cf* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol) { +int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol) { if (q->nsymbols) { return -1; } @@ -54,7 +64,7 @@ int modem_table_set(modem_table_t* q, cf* table, soft_table_t *soft_table, int n if (table_create(q)) { return -1; } - memcpy(q->symbol_table,table,q->nsymbols*sizeof(cf)); + memcpy(q->symbol_table,table,q->nsymbols*sizeof(cf_t)); memcpy(&q->soft_table,soft_table,sizeof(soft_table_t)); q->nbits_x_symbol = nbits_x_symbol; return 0; diff --git a/lib/modem/src/soft_algs.c b/lte/lib/modem/src/soft_algs.c similarity index 87% rename from lib/modem/src/soft_algs.c rename to lte/lib/modem/src/soft_algs.c index deca250eb..3180a117a 100644 --- a/lib/modem/src/soft_algs.c +++ b/lte/lib/modem/src/soft_algs.c @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Vuk Marojevic . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include diff --git a/lte/lib/modem/src/soft_algs.h b/lte/lib/modem/src/soft_algs.h new file mode 100644 index 000000000..15b0caee5 --- /dev/null +++ b/lte/lib/modem/src/soft_algs.h @@ -0,0 +1,33 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +void llr_approx(const _Complex float *in, float *out, int N, int M, int B, + _Complex float *symbols, int (*S)[6][32], float sigma2); + +void llr_exact(const _Complex float *in, float *out, int N, int M, int B, + _Complex float *symbols, int (*S)[6][32], float sigma2); diff --git a/lte/lib/phch/src/pbch.c b/lte/lib/phch/src/pbch.c new file mode 100644 index 000000000..eaef1adbd --- /dev/null +++ b/lte/lib/phch/src/pbch.c @@ -0,0 +1,535 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "phch.h" +#include "lte/phch/pbch.h" +#include "lte/common/base.h" +#include "lte/utils/bit.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" +#include "lte/io/udpsink.h" + +const char crc_mask[4][16] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1} +}; + + +bool pbch_exists(int nframe, int nslot) { + return (!(nframe % 4) && nslot == 1); +} + +int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, bool put) { + int i; + cf_t *ptr; + if (put) { + ptr = input; + output += nof_prb * RE_X_RB / 2 - 36; + } else { + ptr = output; + input += nof_prb * RE_X_RB / 2 - 36; + } + + /* symbol 0 & 1 */ + for (i=0;i<2;i++) { + phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); + } + /* symbols 2 & 3 */ + if (CP_ISNORM(cp)) { + for (i=0;i<2;i++) { + phch_cp_prb(&input, &output, 6); + } + } else { + phch_cp_prb(&input, &output, 6); + phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); + } + if (put) { + return input - ptr; + } else { + return output - ptr; + } +} + +/** + * Puts PBCH in slot number 1 + * + * Returns the number of symbols written to slot1_data + * + * 36.211 10.3 section 6.6.4 + */ +int pbch_put(cf_t *pbch, cf_t *slot1_data, int nof_prb, lte_cp_t cp, int cell_id) { + return pbch_cp(pbch, slot1_data, nof_prb, cp, cell_id, true); +} + +/** + * Extracts PBCH from slot number 1 + * + * Returns the number of symbols written to pbch + * + * 36.211 10.3 section 6.6.4 + */ +int pbch_get(cf_t *slot1_data, cf_t *pbch, int nof_prb, lte_cp_t cp, int cell_id) { + return pbch_cp(slot1_data, pbch, nof_prb, cp, cell_id, false); +} + +/** Initializes the PBCH channel receiver */ +int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp) { + int ret = -1; + bzero(q, sizeof(pbch_t)); + q->cell_id = cell_id; + q->cp = cp; + + if (modem_table_std(&q->mod, LTE_QPSK, true)) { + goto clean; + } + demod_soft_init(&q->demod); + demod_soft_table_set(&q->demod, &q->mod); + demod_soft_alg_set(&q->demod, APPROX); + if (sequence_pbch(&q->seq_pbch, q->cp, q->cell_id)) { + goto clean; + } + + int poly[3] = {0x6D, 0x4F, 0x57}; + if (viterbi_init(&q->decoder, viterbi_37, poly, 40, true)) { + goto clean; + } + q->encoder.K = 7; + q->encoder.R = 3; + q->encoder.framelength = 40; + q->encoder.tail_biting = true; + memcpy(q->encoder.poly, poly, 3 * sizeof(int)); + + q->nof_symbols = (CP_ISNORM(q->cp)) ? PBCH_RE_CPNORM: PBCH_RE_CPEXT; + + q->pbch_d = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_d) { + goto clean; + } + int i; + for (i=0;ice[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->ce[i]) { + goto clean; + } + q->pbch_x[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_x[i]) { + goto clean; + } + q->pbch_symbols[i] = malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->pbch_symbols[i]) { + goto clean; + } + } + q->pbch_llr = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->pbch_llr) { + goto clean; + } + q->temp = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->temp) { + goto clean; + } + q->pbch_rm_f = malloc(sizeof(float) * 120); + if (!q->pbch_rm_f) { + goto clean; + } + q->pbch_rm_b = malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->pbch_rm_b) { + goto clean; + } + q->data = malloc(sizeof(char) * 40); + if (!q->data) { + goto clean; + } + q->data_enc = malloc(sizeof(char) * 120); + if (!q->data_enc) { + goto clean; + } + + ret = 0; +clean: + if (ret == -1) { + pbch_free(q); + } + return ret; +} + +void pbch_free(pbch_t *q) { + if (q->pbch_d) { + free(q->pbch_d); + } + int i; + for (i=0;ice[i]) { + free(q->ce[i]); + } + if (q->pbch_x[i]) { + free(q->pbch_x[i]); + } + if (q->pbch_symbols[i]) { + free(q->pbch_symbols[i]); + } + } + if (q->pbch_llr) { + free(q->pbch_llr); + } + if (q->pbch_rm_f) { + free(q->pbch_rm_f); + } + if (q->pbch_rm_b) { + free(q->pbch_rm_b); + } + if (q->data_enc) { + free(q->data_enc); + } + if (q->data) { + free(q->data); + } + sequence_free(&q->seq_pbch); + modem_table_free(&q->mod); + viterbi_free(&q->decoder); +} + +/** Unpacks MIB from PBCH message. + * msg buffer must be 24 byte length at least + */ +void pbch_mib_unpack(char *msg, pbch_mib_t *mib) { + int bw, phich_res; + + bw = bit_unpack(&msg, 3); + switch(bw) { + case 0: + mib->nof_prb = 6; + break; + case 1: + mib->nof_prb = 15; + break; + default: + mib->nof_prb = (bw-1)*25; + break; + } + if (*msg) { + mib->phich_length = EXTENDED; + } else { + mib->phich_length = NORMAL; + } + msg++; + + phich_res = bit_unpack(&msg, 2); + switch(phich_res) { + case 0: + mib->phich_resources = R_1_6; + break; + case 1: + mib->phich_resources = R_1_2; + break; + case 2: + mib->phich_resources = R_1; + break; + case 3: + mib->phich_resources = R_2; + break; + } + mib->sfn = bit_unpack(&msg, 8) << 2; +} + + +/** Unpacks MIB from PBCH message. + * msg buffer must be 24 byte length at least + */ +void pbch_mib_pack(pbch_mib_t *mib, char *msg) { + int bw, phich_res=0; + + bzero(msg, 24); + + if (mib->nof_prb<=6) { + bw = 0; + } else if (mib->nof_prb <= 15) { + bw = 1; + } else { + bw = 1 + mib->nof_prb/25; + } + bit_pack(bw, &msg, 3); + + *msg = mib->phich_length == EXTENDED; + msg++; + + switch(mib->phich_resources) { + case R_1_6: + phich_res = 0; + break; + case R_1_2: + phich_res = 1; + break; + case R_1: + phich_res = 2; + break; + case R_2: + phich_res = 3; + break; + } + bit_pack(phich_res, &msg, 2); + bit_pack(mib->sfn >> 2, &msg, 8); +} + +void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib) { + printf(" - Nof ports: %d\n", mib->nof_ports); + printf(" - PRB: %d\n", mib->nof_prb); + printf(" - PHICH Length: %s\n", mib->phich_length==EXTENDED?"Extended":"Normal"); + printf(" - PHICH Resources: "); + switch(mib->phich_resources) { + case R_1_6: + printf("1/6"); + break; + case R_1_2: + printf("1/2"); + break; + case R_1: + printf("1"); + break; + case R_2: + printf("2"); + break; + } + printf("\n"); + printf(" - SFN: %d\n", mib->sfn); +} + +void pbch_decode_reset(pbch_t *q) { + q->frame_idx = 0; +} + + +void crc_set_mask(char *data, int nof_ports) { + int i; + for (i=0;i<16;i++) { + data[24+i] = (data[24+i] + crc_mask[nof_ports-1][i]) % 2; + } + +} + + +/* Checks CRC after applying the mask for the given number of ports. + * + * The bits buffer size must be at least 40 bytes. + * + * Returns 0 if the data is correct, -1 otherwise + */ +int pbch_crc_check(char *bits, int nof_ports) { + char data[40]; + memcpy(data, bits, 40 * sizeof(char)); + crc_set_mask(data, nof_ports); + return crc(0, data, 40, 16, 0x11021, 0); +} + +int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, int nof_bits, int nof_ports) { + int j; + + memcpy(&q->temp[dst*nof_bits], &q->pbch_llr[src*nof_bits], n*nof_bits*sizeof(float)); + + /* descramble */ + scrambling_float_offset(&q->seq_pbch, &q->temp[dst*nof_bits], dst*nof_bits, n*nof_bits); + + for (j=0;jtemp[j] = RX_NULL; + } + for (j=(dst+n)*nof_bits;j<4*nof_bits;j++) { + q->temp[j] = RX_NULL; + } + + /* unrate matching */ + rm_conv_rx(q->temp, q->pbch_rm_f, 4 * nof_bits, 120); + + /* decode */ + viterbi_decode_f(&q->decoder, q->pbch_rm_f, q->data); + + if (!pbch_crc_check(q->data, nof_ports)) { + /* unpack MIB */ + pbch_mib_unpack(q->data, mib); + + mib->nof_ports = nof_ports; + mib->sfn += dst-src; + + return 1; + } else { + return 0; + } +} + +/* Decodes the PBCH channel + * + * The PBCH spans in 40 ms. This function is called every 10 ms. It tries to decode the MIB + * given the symbols of the slot #1 of each radio frame. Successive calls will use more frames + * to help the decoding process. + * + * Returns 1 if successfully decoded MIB, 0 if not and -1 on error + */ +int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int nof_prb, float ebno, pbch_mib_t *mib) { + int src, dst, res, nb, nant; + + /* Set pointers for layermapping & precoding */ + int i; + int nof_bits = 2 * q->nof_symbols; + + cf_t *x[MAX_LAYERS], *d[MAX_CODEWORDS]; + /* number of layers equals number of ports */ + for (i=0;ipbch_x[i]; + } + memset(&x[MAX_PORTS_CTRL], 0, sizeof(cf_t*) * (MAX_LAYERS - MAX_PORTS_CTRL)); + /* always one codeword only */ + d[0] = q->pbch_d; + memset(&d[1], 0, sizeof(cf_t*) * (MAX_CODEWORDS - 1)); + + + /* extract symbols */ + if (q->nof_symbols != pbch_get(slot1_symbols, q->pbch_symbols[0], nof_prb, + q->cp, q->cell_id)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return -1; + } + + /* extract channel estimates */ + for (i=0;inof_symbols != pbch_get(ce[i], q->ce[i], nof_prb, + q->cp, q->cell_id)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return -1; + } + } + + q->frame_idx++; + res = 0; + + /* Try decoding for 1 to 4 antennas */ + /** currently only 2 TX antennas are supported */ + for (nant=1;nant<=2 && !res;nant++) { + + INFO("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); + + precoding_decode(q->pbch_symbols, q->ce, x, nant, q->nof_symbols, TX_DIVERSITY); + layermap_decode(x, d, nant, 1, q->nof_symbols/nant, TX_DIVERSITY); + + /* demodulate symbols */ + demod_soft_sigma_set(&q->demod, ebno); + demod_soft_demodulate(&q->demod, q->pbch_d, + &q->pbch_llr[nof_bits * (q->frame_idx - 1)], q->nof_symbols); + + /* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received + * 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234. + * We know they are ordered. + * + * FIXME: There are unnecessary checks because 2,3,4 have already been processed in the previous + * calls. + */ + for (nb=0;nbframe_idx && !res;nb++) { + for (dst=0;(dst<4-nb) && !res;dst++) { + for (src=0;srcframe_idx-nb && !res;src++) { + DEBUG("Trying %d blocks at offset %d as subframe mod4 number %d\n", nb+1, src, dst); + res = pbch_decode_frame(q, mib, src, dst, nb+1, nof_bits, nant); + } + } + } + } + + /* If not found, make room for the next packet of radio frame symbols */ + if (q->frame_idx == 4) { + memmove(q->pbch_llr, &q->pbch_llr[nof_bits], nof_bits * 3 * sizeof(float)); + q->frame_idx = 3; + } + return res; +} + + +/** Converts the MIB message to symbols mapped to SLOT #1 ready for transmission + */ +void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL], + int nof_prb, int nof_ports) { + int i; + int nof_bits = 2 * q->nof_symbols; + + /* Set pointers for layermapping & precoding */ + assert(nof_ports < MAX_PORTS_CTRL); + cf_t *x[MAX_LAYERS], *d[MAX_CODEWORDS]; + + /* number of layers equals number of ports */ + for (i=0;ipbch_x[i]; + } + memset(&x[nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - nof_ports)); + + /* there's always 1 codeword only */ + d[0] = q->pbch_d; + memset(&d[1], 0, sizeof(cf_t*) * (MAX_CODEWORDS - 1)); + + + if (q->frame_idx == 0) { + /* pack MIB */ + pbch_mib_pack(mib, q->data); + + /* encode & modulate */ + crc(0, q->data, 24, 16, 0x11021, 1); + crc_set_mask(q->data, nof_ports); + + convcoder_encode(&q->encoder, q->data, q->data_enc); + + rm_conv_tx(q->data_enc, q->pbch_rm_b, 120, 4 * nof_bits); + + } + + scrambling_bit_offset(&q->seq_pbch, &q->pbch_rm_b[q->frame_idx * nof_bits], + q->frame_idx * nof_bits, nof_bits); + mod_modulate(&q->mod, &q->pbch_rm_b[q->frame_idx * nof_bits], q->pbch_d, nof_bits); + + + /* layer mapping & precoding */ + layermap_encode(d, x, nof_ports, 1, q->nof_symbols/nof_ports, TX_DIVERSITY); + precoding_encode(x, q->pbch_symbols, nof_ports, q->nof_symbols/nof_ports, TX_DIVERSITY); + + + /* mapping to resource elements */ + for (i=0;ipbch_symbols[i], slot1_symbols[i], nof_prb, q->cp, q->cell_id); + } + q->frame_idx++; + if (q->frame_idx == 4) { + q->frame_idx = 0; + } +} + + + diff --git a/lib/phch/src/common.c b/lte/lib/phch/src/phch.c similarity index 69% rename from lib/phch/src/common.c rename to lte/lib/phch/src/phch.c index 0791b46f5..bd5a81f59 100644 --- a/lib/phch/src/common.c +++ b/lte/lib/phch/src/phch.c @@ -1,26 +1,36 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "common.h" -#include "lte/base.h" +#include "phch.h" +#include "lte/common/base.h" void phch_cp_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb, bool advance_output) { diff --git a/lib/phch/src/common.h b/lte/lib/phch/src/phch.h similarity index 50% rename from lib/phch/src/common.h rename to lte/lib/phch/src/phch.h index 56774ecbb..83b69dac4 100644 --- a/lib/phch/src/common.h +++ b/lte/lib/phch/src/phch.h @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + typedef _Complex float cf_t; void phch_cp_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, diff --git a/lib/ratematching/src/rm_conv.c b/lte/lib/ratematching/src/rm_conv.c similarity index 57% rename from lib/ratematching/src/rm_conv.c rename to lte/lib/ratematching/src/rm_conv.c index 2710775d8..3694f4e02 100644 --- a/lib/ratematching/src/rm_conv.c +++ b/lte/lib/ratematching/src/rm_conv.c @@ -1,23 +1,34 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + +#include #include -#include "ratematching/rm_conv.h" +#include "lte/ratematching/rm_conv.h" #define NCOLS 32 #define NROWS_MAX NCOLS @@ -30,6 +41,52 @@ unsigned char RM_PERM_CC_INV[NCOLS] = { 16, 0, 24, 8, 20, 4, 28, 12, 18, 2, 26, 10, 22, 6, 30, 14, 17, 1, 25, 9, 21, 5, 29, 13, 19, 3, 27, 11, 23, 7, 31, 15 }; +int rm_conv_tx(char *input, char *output, int in_len, int out_len) { + + char tmp[RATE * NCOLS * NROWS_MAX]; + int nrows, ndummy, K_p; + + int i, j, k, s; + + nrows = (int) (in_len / RATE - 1) / NCOLS + 1; + if (nrows > NROWS_MAX) { + fprintf(stderr, "Input too large. Max input length is %d\n", + RATE * NCOLS * NROWS_MAX); + return -1; + } + K_p = nrows * NCOLS; + ndummy = K_p - in_len / RATE; + if (ndummy < 0) { + ndummy = 0; + } + k=0; + for (s = 0; s < 3; s++) { + for (j = 0; j < NCOLS; j++) { + for (i = 0; i < nrows; i++) { + if (i*NCOLS + RM_PERM_CC[j] < ndummy) { + tmp[k] = TX_NULL; + } else { + tmp[k] = input[(i*NCOLS + RM_PERM_CC[j]-ndummy)*3+s]; + } + k++; + } + } + } + k = 0; + j = 0; + while (k < out_len) { + if (tmp[j] != TX_NULL) { + output[k] = tmp[j]; + k++; + } + j++; + if (j == RATE * K_p) { + j = 0; + } + } + return 0; +} + /* Undoes Convolutional Code Rate Matching. * 3GPP TS 36.212 v10.1.0 section 5.1.4.2 @@ -44,7 +101,8 @@ int rm_conv_rx(float *input, float *output, int in_len, int out_len) { nrows = (int) (out_len / RATE - 1) / NCOLS + 1; if (nrows > NROWS_MAX) { - fprintf(stderr, "Output too large. Max output length is %d\n", RATE * NCOLS * NROWS_MAX); + fprintf(stderr, "Output too large. Max output length is %d\n", + RATE * NCOLS * NROWS_MAX); return -1; } K_p = nrows * NCOLS; @@ -84,8 +142,8 @@ int rm_conv_rx(float *input, float *output, int in_len, int out_len) { d_i = (i + ndummy) / NCOLS; d_j = (i + ndummy) % NCOLS; for (j = 0; j < RATE; j++) { - output[i * RATE + j] = tmp[K_p * j - + RM_PERM_CC_INV[d_j] * nrows + d_i]; + output[i * RATE + j] = tmp[K_p * j + RM_PERM_CC_INV[d_j] * nrows + + d_i]; } } return 0; diff --git a/lib/resampling/src/interp.c b/lte/lib/resampling/src/interp.c similarity index 66% rename from lib/resampling/src/interp.c rename to lte/lib/resampling/src/interp.c index 068495e3f..05a6960f9 100644 --- a/lib/resampling/src/interp.c +++ b/lte/lib/resampling/src/interp.c @@ -1,29 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "resampling/interp.h" -#include "utils/debug.h" +#include "lte/resampling/interp.h" +#include "lte/utils/debug.h" /* Performs 1st order linear interpolation with out-of-bound interpolation */ void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end) { int i, j; - float mag0, mag1, arg0, arg1, mag, arg; + float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0; for (i=0;i. - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include -#include "scrambling/scrambling.h" +#include +#include "lte/scrambling/scrambling.h" /** * @ingroup Soft-bit Scrambling @@ -31,15 +42,13 @@ void scrambling_float(sequence_t *s, float *data) { scrambling_float_offset(s, data, 0, s->len); } -int scrambling_float_offset(sequence_t *s, float *data, int offset, int len) { +void scrambling_float_offset(sequence_t *s, float *data, int offset, int len) { int i; - if (len + offset > s->len) { - return -1; - } + assert (len + offset <= s->len); + for (i = 0; i < len; i++) { data[i] = data[i]*(1-2*s->c[i+offset]); } - return 0; } /** @@ -55,6 +64,14 @@ void scrambling_bit(sequence_t *s, char *data) { } } +void scrambling_bit_offset(sequence_t *s, char *data, int offset, int len) { + int i; + assert (len + offset <= s->len); + for (i = 0; i < len; i++) { + data[i] = (data[i] + s->c[i+offset]) % 2; + } +} + /** High-level API */ int compute_sequences(scrambling_hl* h) { diff --git a/lte/lib/sync/src/cp.c b/lte/lib/sync/src/cp.c new file mode 100644 index 000000000..da62c450f --- /dev/null +++ b/lte/lib/sync/src/cp.c @@ -0,0 +1,32 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + + +/** TODO: Cyclic-prefix based synchronization + * + */ diff --git a/lib/sync/src/find_sss.c b/lte/lib/sync/src/find_sss.c similarity index 79% rename from lib/sync/src/find_sss.c rename to lte/lib/sync/src/find_sss.c index 9747a04a5..b6e6d06e8 100644 --- a/lib/sync/src/find_sss.c +++ b/lte/lib/sync/src/find_sss.c @@ -1,25 +1,35 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include -#include "utils/vector.h" -#include "sync/sss.h" +#include "lte/utils/vector.h" +#include "lte/sync/sss.h" cf_t corr_sz(cf_t *z, cf_t *s) { cf_t sum; diff --git a/lib/sync/src/gen_sss.c b/lte/lib/sync/src/gen_sss.c similarity index 77% rename from lib/sync/src/gen_sss.c rename to lte/lib/sync/src/gen_sss.c index ca9c8da3c..de75c189b 100644 --- a/lib/sync/src/gen_sss.c +++ b/lte/lib/sync/src/gen_sss.c @@ -1,24 +1,34 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include -#include "sync/sss.h" +#include "lte/sync/sss.h" /** * @brief Function documentation: initSSStables() @@ -116,7 +126,7 @@ void generate_sss_all_tables(struct sss_tables *tables, int N_id_2) { tables->N_id_2 = N_id_2; } -void generate_sss(float *signal, int cell_id) { +void sss_generate(float *signal0, float *signal5, int cell_id) { int i; int id1 = cell_id / 3; @@ -140,15 +150,15 @@ void generate_sss(float *signal, int cell_id) { for (i = 0; i < N_SSS; i++) { /** Even Resource Elements: Sub-frame 0*/ - signal[2 * i] = (float) (s0[i] * c0[i]); + signal0[2 * i] = (float) (s0[i] * c0[i]); /** Odd Resource Elements: Sub-frame 0*/ - signal[2 * i + 1] = (float) (s1[i] * c1[i] * z1_0[i]); + signal0[2 * i + 1] = (float) (s1[i] * c1[i] * z1_0[i]); } for (i = 0; i < N_SSS; i++) { /** Even Resource Elements: Sub-frame 5*/ - signal[2 * i + N_SSS * 2] = (float) (s1[i] * c0[i]); + signal5[2 * i] = (float) (s1[i] * c0[i]); /** Odd Resource Elements: Sub-frame 5*/ - signal[2 * i + 1 + N_SSS * 2] = (float) (s0[i] * c1[i] * z1_1[i]); + signal5[2 * i + 1] = (float) (s0[i] * c1[i] * z1_1[i]); } } diff --git a/lib/sync/src/pss.c b/lte/lib/sync/src/pss.c similarity index 80% rename from lib/sync/src/pss.c rename to lte/lib/sync/src/pss.c index f9fff5bc6..70cb0741e 100644 --- a/lib/sync/src/pss.c +++ b/lte/lib/sync/src/pss.c @@ -1,31 +1,41 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include #include -#include "sync/pss.h" -#include "utils/dft.h" -#include "utils/vector.h" -#include "utils/convolution.h" +#include "lte/sync/pss.h" +#include "lte/utils/dft.h" +#include "lte/utils/vector.h" +#include "lte/utils/convolution.h" #define NOT_SYNC 0xF0F0F0F0 @@ -36,27 +46,27 @@ int pss_synch_init(pss_synch_t *q, int frame_size) { int ret = -1; bzero(q, sizeof(pss_synch_t)); - q->pss_signal_freq = vec_malloc((PSS_LEN_FREQ+frame_size) * sizeof(cf_t)); + q->pss_signal_freq = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); if (!q->pss_signal_freq) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; } - q->conv_abs = vec_malloc((PSS_LEN_FREQ+frame_size) * sizeof(float)); + q->conv_abs = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(float)); if (!q->conv_abs) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; } - q->tmp_input = vec_malloc((PSS_LEN_FREQ+frame_size) * sizeof(cf_t)); + q->tmp_input = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); if (!q->tmp_input) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; } - q->frame_buffer = vec_malloc(4*frame_size * sizeof(cf_t)); + q->frame_buffer = vec_malloc(4 * frame_size * sizeof(cf_t)); if (!q->frame_buffer) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; } - q->conv_output = vec_malloc((PSS_LEN_FREQ+frame_size) * sizeof(cf_t)); + q->conv_output = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); if (!q->conv_output) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; @@ -78,8 +88,7 @@ int pss_synch_init(pss_synch_t *q, int frame_size) { q->fb_wp = 0; ret = 0; -clean_and_exit: - if (ret == -1) { + clean_and_exit: if (ret == -1) { pss_synch_free(q); } return ret; @@ -112,15 +121,14 @@ void pss_synch_free(pss_synch_t *q) { /** * This function calculates the Zadoff-Chu sequence. * @param signal Output array. - * @param direction 0 for tx, 1 for rx */ -int pss_generate(cf_t *signal, int direction, int N_id_2) { +int pss_generate(cf_t *signal, int N_id_2) { int i; float arg; - const float root_value[] = {25.0,29.0,34.0}; + const float root_value[] = { 25.0, 29.0, 34.0 }; int root_idx; - int sign = direction ? 1 : -1; + int sign = -1; if (N_id_2 < 0 || N_id_2 > 2) { fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); @@ -144,7 +152,15 @@ int pss_generate(cf_t *signal, int direction, int N_id_2) { return 0; } - +/** 36.211 10.3 section 6.11.1.2 + */ +void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp) { + int k; + k = (CP_NSYMB(cp) - 1) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; + memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); + memcpy(&slot[k], pss_signal, PSS_LEN * sizeof(cf_t)); + memset(&slot[k + PSS_LEN], 0, 5 * sizeof(cf_t)); +} /** Sets the current N_id_2 value. Initializes the object for this PSS sequence * Returns -1 on error, 0 otherwise @@ -161,7 +177,7 @@ int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) { return -1; } - pss_generate(pss_signal_time, 0, N_id_2); + pss_generate(pss_signal_time, N_id_2); memset(pss_signal_pad, 0, PSS_LEN_FREQ * sizeof(cf_t)); memset(q->pss_signal_freq, 0, PSS_LEN_FREQ * sizeof(cf_t)); @@ -192,7 +208,8 @@ int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) { * * Input buffer must be subframe_size long. */ -int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, float *corr_mean_value) { +int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, + float *corr_mean_value) { int corr_peak_pos; int conv_output_len; @@ -201,7 +218,8 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, floa memset(&q->tmp_input[q->frame_size], 0, PSS_LEN_FREQ * sizeof(cf_t)); #ifdef CONVOLUTION_FFT - conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, q->pss_signal_freq, q->conv_output); + conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, + q->pss_signal_freq, q->conv_output); #else conv_output_len = conv_cc(input, q->pss_signal_freq, q->conv_output, q->frame_size, PSS_LEN_FREQ); #endif @@ -212,7 +230,8 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, floa *corr_peak_value = q->conv_abs[corr_peak_pos]; } if (corr_mean_value) { - *corr_mean_value = vec_acc_ff(q->conv_abs, conv_output_len) / conv_output_len; + *corr_mean_value = vec_acc_ff(q->conv_abs, conv_output_len) + / conv_output_len; } return (int) corr_peak_pos; @@ -225,26 +244,17 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, floa */ float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv) { cf_t y0, y1, yr; - cf_t y[PSS_LEN_FREQ-1]; + cf_t y[PSS_LEN_FREQ - 1]; vec_prod_ccc_unalign(q->pss_signal_freq, pss_recv, y, PSS_LEN_FREQ - 1); - y0 = vec_acc_cc(y, (PSS_LEN_FREQ - 1)/2); - y1 = vec_acc_cc(&y[(PSS_LEN_FREQ - 1)/2], (PSS_LEN_FREQ - 1)/2); + y0 = vec_acc_cc(y, (PSS_LEN_FREQ - 1) / 2); + y1 = vec_acc_cc(&y[(PSS_LEN_FREQ - 1) / 2], (PSS_LEN_FREQ - 1) / 2); yr = conjf(y0) * y1; return atan2f(__imag__ yr, __real__ yr) / M_PI; } - - - - - - - - - /** This function is designed to be called periodically on a subframe basis. * The function finds the PSS correlation peak and computes (does not adjust) CFO automatically as defined by * pss_synch_set_cfo_mode(). @@ -320,13 +330,13 @@ int pss_synch_frame(pss_synch_t *q, cf_t *input, cf_t *output, int nsamples) { } if (q->frame_start_idx != NOT_SYNC && q->cfo_auto && retval) { - q->current_cfo = pss_synch_cfo_compute(q, &output[q->frame_size/2 - PSS_LEN_FREQ + 1]); + q->current_cfo = pss_synch_cfo_compute(q, + &output[q->frame_size / 2 - PSS_LEN_FREQ + 1]); } return retval; } - void pss_synch_set_timeout(pss_synch_t *q, int nof_frames) { q->nosync_timeout_frames = nof_frames; } @@ -347,15 +357,8 @@ int pss_synch_get_frame_start_idx(pss_synch_t *q) { return q->frame_start_idx; } - - - - - /** High-level API */ - - int pss_synch_initialize(pss_synch_hl* h) { int fs = h->init.frame_size; if (!fs) { diff --git a/lib/sync/src/sfo.c b/lte/lib/sync/src/sfo.c similarity index 57% rename from lib/sync/src/sfo.c rename to lte/lib/sync/src/sfo.c index ab5553370..2d13d2d2e 100644 --- a/lib/sync/src/sfo.c +++ b/lte/lib/sync/src/sfo.c @@ -1,24 +1,34 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "sync/sfo.h" +#include "lte/sync/sfo.h" /* Estimate SFO based on the array of time estimates t0 * of length len. The parameter period is the time between t0 samples diff --git a/lib/sync/src/sss.c b/lte/lib/sync/src/sss.c similarity index 71% rename from lib/sync/src/sss.c rename to lte/lib/sync/src/sss.c index fff4a4828..25c915c6b 100644 --- a/lib/sync/src/sss.c +++ b/lte/lib/sync/src/sss.c @@ -1,29 +1,39 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include -#include "sync/sss.h" -#include "utils/dft.h" -#include "utils/convolution.h" +#include "lte/sync/sss.h" +#include "lte/utils/dft.h" +#include "lte/utils/convolution.h" void generate_sss_all_tables(struct sss_tables *tables, int N_id_2); void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in); @@ -53,12 +63,25 @@ int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2) { } struct sss_tables sss_tables; - generate_sss_all_tables(&sss_tables,N_id_2); + generate_sss_all_tables(&sss_tables, N_id_2); convert_tables(&q->fc_tables, &sss_tables); return 0; } +/** 36.211 10.3 section 6.11.2.2 + */ +void sss_put_slot(float *sss, cf_t *slot, int nof_prb, lte_cp_t cp) { + int i, k; + + k = (CP_NSYMB(cp) - 2) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; + memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); + for (i = 0; i < SSS_LEN; i++) { + __real__ slot[k + i] = sss[i]; + __imag__ slot[k + i] = 0; + } + memset(&slot[k + SSS_LEN], 0, 5 * sizeof(cf_t)); +} /* In this function, input points to the beginning of the subframe. Saves result in subframe_idx and N_id_1 * Return 1 if the sequence was found, 0 if the peak is not found, -1 if the subframe_sz or symbol_sz are @@ -66,19 +89,19 @@ int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2) { * Before calling this function, the correlation threshold and symbol size duration need to be set * using sss_synch_set_threshold() and sss_synch_set_symbol_sz(). */ -int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, - int *N_id_1) { - int m0,m1; +int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, int *N_id_1) { + int m0, m1; float m0_value, m1_value; if (q->subframe_sz <= 0 || q->symbol_sz <= 0) { return -1; } - sss_synch_m0m1(q, &input[SSS_SYMBOL_ST(q->subframe_sz, q->symbol_sz)], - &m0, &m0_value, &m1, &m1_value); + sss_synch_m0m1(q, &input[SSS_SYMBOL_ST(q->subframe_sz, q->symbol_sz)], &m0, + &m0_value, &m1, &m1_value); - if (m0_value > q->corr_peak_threshold && m1_value > q->corr_peak_threshold) { + if (m0_value > q->corr_peak_threshold + && m1_value > q->corr_peak_threshold) { if (subframe_idx) { *subframe_idx = sss_synch_subframe(m0, m1); } @@ -103,7 +126,6 @@ void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz) { q->subframe_sz = subframe_sz; } - /** Sets the SSS correlation peak detection threshold */ void sss_synch_set_threshold(sss_synch_t *q, float threshold) { q->corr_peak_threshold = threshold; @@ -120,13 +142,13 @@ int sss_synch_subframe(int m0, int m1) { /** Returns the N_id_1 value based on the m0 and m1 values */ int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1) { - if (m0<0 || m0>29 || m1<0 || m1>29) { + if (m0 < 0 || m0 > 29 || m1 < 0 || m1 > 29) { return -1; } if (m1 > m0) { - return q->N_id_1_table[m0][m1-1]; + return q->N_id_1_table[m0][m1 - 1]; } else { - return q->N_id_1_table[m1][m0-1]; + return q->N_id_1_table[m1][m0 - 1]; } } @@ -153,7 +175,8 @@ int sss_synch_work(sss_synch_hl* hl) { if (hl->ctrl_in.symbol_sz) { sss_synch_set_symbol_sz(&hl->obj, hl->ctrl_in.symbol_sz); } - sss_synch_frame(&hl->obj, hl->input, &hl->ctrl_out.subframe_idx, &hl->ctrl_out.N_id_1); + sss_synch_frame(&hl->obj, hl->input, &hl->ctrl_out.subframe_idx, + &hl->ctrl_out.N_id_1); return 0; } diff --git a/lib/sync/src/sync.c b/lte/lib/sync/src/sync.c similarity index 76% rename from lib/sync/src/sync.c rename to lte/lib/sync/src/sync.c index bcb14ce9c..7d2d8f7d7 100644 --- a/lib/sync/src/sync.c +++ b/lte/lib/sync/src/sync.c @@ -1,27 +1,37 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include -#include "utils/debug.h" -#include "lte/base.h" -#include "sync/sync.h" +#include "lte/utils/debug.h" +#include "lte/common/base.h" +#include "lte/sync/sync.h" int sync_init(sync_t *q, int frame_size) { int N_id_2; @@ -106,7 +116,7 @@ float sync_get_peak_to_avg(sync_t *q) { return q->peak_to_avg; } -int sync_run(sync_t *q, cf_t *input, int read_offset) { +int sync_run(sync_t *q, cf_t *input) { int N_id_2, peak_pos[3], sss_idx; int m0, m1; float m0_value, m1_value; @@ -118,7 +128,7 @@ int sync_run(sync_t *q, cf_t *input, int read_offset) { if (q->force_N_id_2 == -1) { for (N_id_2=0;N_id_2<3;N_id_2++) { - peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], &input[read_offset], + peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], input, &peak_value[N_id_2], &mean_value[N_id_2]); } for (i=0;i<3;i++) { @@ -129,7 +139,7 @@ int sync_run(sync_t *q, cf_t *input, int read_offset) { } } else { N_id_2 = q->force_N_id_2; - peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], &input[read_offset], + peak_pos[N_id_2] = pss_synch_find_pss(&q->pss[N_id_2], input, &peak_value[N_id_2], &mean_value[N_id_2]); } @@ -140,7 +150,7 @@ int sync_run(sync_t *q, cf_t *input, int read_offset) { /* If peak detected */ peak_detected = 0; - if (peak_pos[N_id_2] + read_offset > 128) { + if (peak_pos[N_id_2] - 128 >= 0) { if (q->pss_mode == ABSOLUTE) { if (peak_value[N_id_2] > q->threshold) { peak_detected = 1; @@ -154,12 +164,12 @@ int sync_run(sync_t *q, cf_t *input, int read_offset) { if (peak_detected) { - q->cfo = pss_synch_cfo_compute(&q->pss[N_id_2], &input[read_offset + peak_pos[N_id_2]-128]); + q->cfo = pss_synch_cfo_compute(&q->pss[N_id_2], &input[peak_pos[N_id_2]-128]); INFO("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold, q->cfo); - sss_idx = read_offset + peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); + sss_idx = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); if (sss_idx>= 0) { sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx], &m0, &m0_value, &m1, &m1_value); diff --git a/lib/utils/src/bit.c b/lte/lib/utils/src/bit.c similarity index 61% rename from lib/utils/src/bit.c rename to lte/lib/utils/src/bit.c index fda92913e..51e0c6d32 100644 --- a/lib/utils/src/bit.c +++ b/lte/lib/utils/src/bit.c @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include @@ -55,6 +65,7 @@ unsigned int bit_diff(char *x, char *y, int nbits) { unsigned int errors=0; for (int i=0;i. - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include -#include "utils/dft.h" -#include "utils/vector.h" -#include "utils/convolution.h" +#include "lte/utils/dft.h" +#include "lte/utils/vector.h" +#include "lte/utils/convolution.h" int conv_fft_cc_init(conv_fft_cc_t *state, int input_len, int filter_len) { diff --git a/lte/lib/utils/src/debug.c b/lte/lib/utils/src/debug.c new file mode 100644 index 000000000..5f33ee0b0 --- /dev/null +++ b/lte/lib/utils/src/debug.c @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "lte/utils/debug.h" + +int verbose = 0; + +void get_time_interval(struct timeval * tdata) { + + tdata[0].tv_sec = tdata[2].tv_sec - tdata[1].tv_sec; + tdata[0].tv_usec = tdata[2].tv_usec - tdata[1].tv_usec; + if (tdata[0].tv_usec < 0) { + tdata[0].tv_sec--; + tdata[0].tv_usec += 1000000; + } +} diff --git a/lib/utils/src/dft.c b/lte/lib/utils/src/dft.c similarity index 89% rename from lib/utils/src/dft.c rename to lte/lib/utils/src/dft.c index 682d6cd02..4434b8abc 100644 --- a/lib/utils/src/dft.c +++ b/lte/lib/utils/src/dft.c @@ -1,27 +1,37 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include -#include "utils/dft.h" +#include "lte/utils/dft.h" #define div(a,b) ((a-1)/b+1) @@ -177,6 +187,7 @@ void dft_run_c2c(dft_plan_t *plan, dft_c_t *in, dft_c_t *out) { fftwf_execute(plan->p); if (plan->options & DFT_NORMALIZE) { + /**FIXME: Use VOLK */ norm = sqrtf(plan->size); for (i=0;isize;i++) { f_out[i] /= norm; diff --git a/lib/utils/src/matrix.c b/lte/lib/utils/src/matrix.c similarity index 75% rename from lib/utils/src/matrix.c rename to lte/lib/utils/src/matrix.c index 4c33f6725..735edc9d0 100644 --- a/lib/utils/src/matrix.c +++ b/lte/lib/utils/src/matrix.c @@ -1,28 +1,38 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include -#include "utils/matrix.h" +#include "lte/utils/matrix.h" int matrix_init(void ***q, int sz_x, int sz_y, int elem_sz) { int i; diff --git a/lib/utils/src/mux.c b/lte/lib/utils/src/mux.c similarity index 71% rename from lib/utils/src/mux.c rename to lte/lib/utils/src/mux.c index af3595e11..1d6fc83dd 100644 --- a/lib/utils/src/mux.c +++ b/lte/lib/utils/src/mux.c @@ -1,23 +1,33 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is part of the libLTE library. * - * OSLD-lib is distributed in the hope that it will be useful, + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include /** diff --git a/lib/utils/src/nco.c b/lte/lib/utils/src/nco.c similarity index 76% rename from lib/utils/src/nco.c rename to lte/lib/utils/src/nco.c index 874519a1c..0d184715a 100644 --- a/lib/utils/src/nco.c +++ b/lte/lib/utils/src/nco.c @@ -1,30 +1,40 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + #include #include #include #include #include -#include "utils/nco.h" +#include "lte/utils/nco.h" void nco_init(nco_t *nco, int size) { int i; diff --git a/lib/utils/src/pack.c b/lte/lib/utils/src/pack.c similarity index 50% rename from lib/utils/src/pack.c rename to lte/lib/utils/src/pack.c index c9456c5c1..dfa8a7e69 100644 --- a/lib/utils/src/pack.c +++ b/lte/lib/utils/src/pack.c @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + void pack_bits(unsigned int value, char **bits, int nof_bits) { int i; diff --git a/lib/utils/src/vector.c b/lte/lib/utils/src/vector.c similarity index 74% rename from lib/utils/src/vector.c rename to lte/lib/utils/src/vector.c index cc56d06b7..e45c5b477 100644 --- a/lib/utils/src/vector.c +++ b/lte/lib/utils/src/vector.c @@ -1,22 +1,32 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of OSLD-lib (http://https://github.com/ismagom/osld-lib) +/** * - * OSLD-lib is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * \section COPYRIGHT * - * OSLD-lib is distributed in the hope that it will be useful, + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with OSLD-lib. If not, see . + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ -#include "utils/vector.h" + +#include "lte/utils/vector.h" #include #include #include @@ -130,13 +140,20 @@ void vec_fprint_f(FILE *stream, float *x, int len) { fprintf(stream, "["); for (i=0;i clip) + tmp = clip; + out[i] = (unsigned char) tmp; + } + +} diff --git a/matlab/chest/get_ce.m b/matlab/chest/get_ce.m deleted file mode 100644 index 48221c75b..000000000 --- a/matlab/chest/get_ce.m +++ /dev/null @@ -1,230 +0,0 @@ -function [ symb, ce ] = get_ce( samps, N_sf, N_id_cell, N_ant ) - - N_sc_rb = 12; % Only dealing with normal cp at this time - N_rb_dl_max = 110; - v_shift = mod(N_id_cell, 6); - sf_start_idx = f_start_idx + N_sf*30720; - crs0 = lte_generate_crs(mod(N_sf*2+0, 20), 0, N_id_cell); - crs1 = lte_generate_crs(mod(N_sf*2+0, 20), 1, N_id_cell); - crs4 = lte_generate_crs(mod(N_sf*2+0, 20), 4, N_id_cell); - crs7 = lte_generate_crs(mod(N_sf*2+1, 20), 0, N_id_cell); - crs8 = lte_generate_crs(mod(N_sf*2+1, 20), 1, N_id_cell); - crs11 = lte_generate_crs(mod(N_sf*2+1, 20), 4, N_id_cell); - crs14 = lte_generate_crs(mod(N_sf*2+2, 20), 0, N_id_cell); - crs15 = lte_generate_crs(mod(N_sf*2+2, 20), 1, N_id_cell); - - N_rb_dl = 6; - FFT_pad_size = 988; % FFT_size = 2048 - - for(n=0:15) - if(n < 7) - idx = sf_start_idx; - elseif(n < 14) - idx = sf_start_idx + 15360; - else - idx = sf_start_idx + 2*15360; - end - symb(n+1,:) = samps_to_symbs(samps, idx, mod(n,7), FFT_pad_size, 0); - end - - for(p=0:N_ant-1) - % Define v, crs, sym, and N_sym - if(p == 0) - v = [0, 3, 0, 3, 0]; - crs = [crs0; crs4; crs7; crs11; crs14]; - sym = [symb(0+1,:); symb(4+1,:); symb(7+1,:); symb(11+1,:); symb(14+1,:)]; - N_sym = 5; - elseif(p == 1) - v = [3, 0, 3, 0, 3]; - crs = [crs0; crs4; crs7; crs11; crs14]; - sym = [symb(0+1,:); symb(4+1,:); symb(7+1,:); symb(11+1,:); symb(14+1,:)]; - N_sym = 5; - elseif(p == 2) - v = [0, 3, 0]; - crs = [crs1; crs8; crs15]; - sym = [symb(1+1,:); symb(8+1,:); symb(15+1,:)]; - N_sym = 3; - else % p == 3 - v = [3, 6, 3]; - crs = [crs1; crs8; crs15]; - sym = [symb(1+1,:); symb(8+1,:); symb(15+1,:)]; - N_sym = 3; - end - - for(n=1:N_sym) - for(m=0:2*N_rb_dl-1) - k = 6*m + mod((v(n) + v_shift), 6); - m_prime = m + N_rb_dl_max - N_rb_dl; - tmp = sym(n,k+1)/crs(n,m_prime+1); - mag(n,k+1) = abs(tmp); - ang(n,k+1) = angle(tmp); - - % Unwrap phase - if(m > 0) - while((ang(n,k+1) - ang(n,k-6+1)) > pi) - ang(n,k+1) = ang(n,k+1) - 2*pi; - end - while((ang(n,k+1) - ang(n,k-6+1)) < -pi) - ang(n,k+1) = ang(n,k+1) + 2*pi; - end - end - - % Interpolate between CRSs (simple linear interpolation) - if(m > 0) - frac_mag = (mag(n,k+1) - mag(n,k-6+1))/6; - frac_ang = (ang(n,k+1) - ang(n,k-6+1))/6; - for(o=1:5) - mag(n,k-o+1) = mag(n,k-(o-1)+1) - frac_mag; - ang(n,k-o+1) = ang(n,k-(o-1)+1) - frac_ang; - end - end - - % Interpolate before 1st CRS - if(m == 1) - for(o=1:mod(v(n) + v_shift, 6)) - mag(n,k-6-o+1) = mag(n,k-6-(o-1)+1) - frac_mag; - ang(n,k-6-o+1) = ang(n,k-6-(o-1)+1) - frac_ang; - end - end - end - - % Interpolate after last CRS - for(o=1:(5-mod(v(n) + v_shift, 6))) - mag(n,k+o+1) = mag(n,k+(o-1)+1) - frac_mag; - ang(n,k+o+1) = ang(n,k+(o-1)+1) - frac_ang; - end - end - - % Interpolate between symbols and construct channel estimates - if(N_sym == 3) - for(n=1:N_sc_rb*N_rb_dl) - % Construct symbol 1 and 8 channel estimates directly - ce(p+1,1+1,n) = mag(1,n)*(cos(ang(1,n)) + j*sin(ang(1,n))); - ce(p+1,8+1,n) = mag(2,n)*(cos(ang(2,n)) + j*sin(ang(2,n))); - - % Interpolate for symbol 2, 3, 4, 5, 6, and 7 channel estimates - frac_mag = (mag(2,n) - mag(1,n))/7; - frac_ang = ang(2,n) - ang(1,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - frac_ang = frac_ang/7; - ce_mag = mag(2,n); - ce_ang = ang(2,n); - for(o=7:-1:2) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - - % Interpolate for symbol 0 channel estimate - % FIXME: Use previous slot to do this correctly - ce_mag = mag(1,n) - frac_mag; - ce_ang = ang(1,n) - frac_ang; - ce(p+1,0+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - - % Interpolate for symbol 9, 10, 11, 12, and 13 channel estimates - frac_mag = (mag(3,n) - mag(2,n))/7; - frac_ang = ang(3,n) - ang(2,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - frac_ang = frac_ang/7; - ce_mag = mag(3,n) - frac_mag; - ce_ang = ang(3,n) - frac_ang; - for(o=13:-1:9) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - end - else - for(n=1:N_sc_rb*N_rb_dl) - % Construct symbol 0, 4, 7, and 11 channel estimates directly - ce(p+1,0+1,n) = mag(1,n)*(cos(ang(1,n)) + j*sin(ang(1,n))); - ce(p+1,4+1,n) = mag(2,n)*(cos(ang(2,n)) + j*sin(ang(2,n))); - ce(p+1,7+1,n) = mag(3,n)*(cos(ang(3,n)) + j*sin(ang(3,n))); - ce(p+1,11+1,n) = mag(4,n)*(cos(ang(4,n)) + j*sin(ang(4,n))); - - % Interpolate for symbol 1, 2, and 3 channel estimates - frac_mag = (mag(2,n) - mag(1,n))/4; - frac_ang = ang(2,n) - ang(1,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - frac_ang = frac_ang/4; - ce_mag = mag(2,n); - ce_ang = ang(2,n); - for(o=3:-1:1) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - - % Interpolate for symbol 5 and 6 channel estimates - frac_mag = (mag(3,n) - mag(2,n))/3; - frac_ang = ang(3,n) - ang(2,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - frac_ang = frac_ang/3; - ce_mag = mag(3,n); - ce_ang = ang(3,n); - for(o=6:-1:5) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - - % Interpolate for symbol 8, 9, and 10 channel estimates - frac_mag = (mag(4,n) - mag(3,n))/4; - frac_ang = ang(4,n) - ang(3,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - frac_ang = frac_ang/4; - ce_mag = mag(4,n); - ce_ang = ang(4,n); - for(o=10:-1:8) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - - % Interpolate for symbol 12 and 13 channel estimates - frac_mag = (mag(5,n) - mag(4,n))/3; - frac_ang = ang(5,n) - ang(4,n); - if(frac_ang >= pi) % Wrap angle - frac_ang = frac_ang - 2*pi; - elseif(frac_ang <= -pi) - frac_ang = frac_ang + 2*pi; - end - - - frac_ang = frac_ang/3; - ce_mag = mag(5,n); - ce_ang = ang(5,n); - for(o=13:-1:12) - ce_mag = ce_mag - frac_mag; - ce_ang = ce_ang - frac_ang; - ce(p+1,o+1,n) = ce_mag*(cos(ce_ang) + j*sin(ce_ang)); - end - end - end - end - subplot(1,2,1) - pcolor(transpose(abs(reshape(ce(1,:,:),14,[])))) - subplot(1,2,2) - pcolor(transpose(real(symb(:,:)))) -end - diff --git a/matlab/chest/lte_generate_crs.m b/matlab/chest/lte_generate_crs.m deleted file mode 100644 index 371fa6c6d..000000000 --- a/matlab/chest/lte_generate_crs.m +++ /dev/null @@ -1,15 +0,0 @@ -function [r] = lte_generate_crs(ns, l, cell_id) - - % Calculate c_init and sequence length - N_cp = 1; - c_init = 1024 * (7 * (ns+1) + l + 1) * (2 * cell_id + 1) + 2*cell_id + N_cp; - - % Generate the psuedo random sequence c - c = lte_generate_prs_c(c_init, 220); - - % Construct r - r = zeros(1,len); - for(m=0:len-1) - r(m+1) = (1/sqrt(2))*(1 - 2*c(2*m+1)) + j*(1/sqrt(2))*(1 - 2*c(2*m+1+1)); - end -end diff --git a/matlab/chest/lte_generate_prs_c.m b/matlab/chest/lte_generate_prs_c.m deleted file mode 100644 index e1e192117..000000000 --- a/matlab/chest/lte_generate_prs_c.m +++ /dev/null @@ -1,23 +0,0 @@ - -function [c] = lte_generate_prs_c(c_init, seq_len) - % Initialize the m-sequences - x1 = zeros(1,1600+seq_len); - x2 = zeros(1,1600+seq_len); - tmp = c_init; - for(n=0:30) - x2(30-n+1) = floor(tmp/(2^(30-n))); - tmp = tmp - (floor(tmp/(2^(30-n)))*2^(30-n)); - end - x1(0+1) = 1; - - % Advance m-sequences - for(n=0:1600+seq_len) - x1(n+31+1) = mod(x1(n+3+1) + x1(n+1), 2); - x2(n+31+1) = mod(x2(n+3+1) + x2(n+2+1) + x2(n+1+1) + x2(n+1), 2); - end - - % Generate c - for(n=0:seq_len-1) - c(n+1) = mod(x1(n+1600+1) + x2(n+1600+1), 2); - end -end diff --git a/matlab/chest/samps_to_symbs.m b/matlab/chest/samps_to_symbs.m deleted file mode 100644 index f8f1320b1..000000000 --- a/matlab/chest/samps_to_symbs.m +++ /dev/null @@ -1,26 +0,0 @@ -function [symbs] = samps_to_symbs(samps, slot_start_idx, symb_offset, FFT_pad_size, scale) - % Calculate index and CP length - if(mod(symb_offset, 7) == 0) - CP_len = 160; - else - CP_len = 144; - end - index = slot_start_idx + (2048+144)*symb_offset; - if(symb_offset > 0) - index = index + 16; - end - - % Take FFT - tmp = fftshift(fft(samps(index+CP_len:index+CP_len+2047))); - - % Remove DC subcarrier - tmp_symbs = [tmp(FFT_pad_size+1:1024); tmp(1026:2048-(FFT_pad_size-1))]; - - if(scale == 0) - symbs = tmp_symbs; - else - for(n=1:length(tmp_symbs)) - symbs(n) = cos(angle(tmp_symbs(n))) + j*sin(angle(tmp_symbs(n))); - end - end -end \ No newline at end of file diff --git a/scripts/binsource.h b/scripts/binsource.h deleted file mode 100644 index f64638cb3..000000000 --- a/scripts/binsource.h +++ /dev/null @@ -1,36 +0,0 @@ -#include - -/* Low-level API */ -typedef struct { - unsigned int seed; - uint32_t *seq_buff; - int seq_buff_nwords; - int seq_cache_nbits; - int seq_cache_rp; -}binsource_t; - -void binsource_init(binsource_t* q); -void binsource_destroy(binsource_t* q); -void binsource_seed_set(binsource_t* q, unsigned int seed); -void binsource_seed_time(binsource_t *q); -int binsource_cache_gen(binsource_t* q, int nbits); -void binsource_cache_cpy(binsource_t* q, uint8_t *bits, int nbits); -int binsource_generate(binsource_t* q, uint8_t *bits, int nbits); - - -/* High-level API */ -typedef struct { - binsource_t obj; - struct binsource_init { - int cache_seq_nbits; /* default=2 */ - int seed; - } init; - struct binsource_ctrl_in { - int nbits; - } ctrl_in; - uint8_t* output[2]; /* size=2048*14 */ - int* out_len; -}binsource_hl; - -int binsource_initialize(binsource_hl* h); -int binsource_work( binsource_hl* hl); diff --git a/scripts/lib_binsource/CMakeLists.txt b/scripts/lib_binsource/CMakeLists.txt deleted file mode 100644 index 40fd36b4a..000000000 --- a/scripts/lib_binsource/CMakeLists.txt +++ /dev/null @@ -1,94 +0,0 @@ -# This configuration is for the aloe++ skeleton - -# set-up the program libraries here -set(LIBRARIES m rt osld) - -# set-up program includes here -include_directories(/usr/local/include/) - -############## DO NOT NEED TO MODIFY BEYOND HERE - -get_filename_component(module ${CMAKE_CURRENT_SOURCE_DIR} NAME) - -if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) - cmake_minimum_required (VERSION 2.6) - project (${module}) - - # The version number. - set (OECORE_VERSION_MAJOR 1) - set (OECORE_VERSION_MINOR 0) - set(MODULE_REPOS_NAME "default") - -else() - include_directories(${OESR_INCLUDE}) -endif() - - -file(GLOB_RECURSE SOURCES "src/*.c") -file(GLOB_RECURSE TEST_SOURCES "test/*.c") - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) - -# aloe module -add_library(${module}-aloe SHARED ${SOURCES}) -set_target_properties(${module}-aloe PROPERTIES OUTPUT_NAME ${module}) -set_target_properties(${module}-aloe PROPERTIES COMPILE_FLAGS "-D_COMPILE_ALOE") -target_link_libraries(${module}-aloe oesrapi skeleton ${LIBRARIES}) -install(TARGETS ${module}-aloe DESTINATION lib/${MODULE_REPOS_NAME}/) - - -if (NOT ${TEST_SOURCES} STREQUAL "") - # standalone program for testing - add_executable(${module}-bin ${SOURCES} ${TEST_SOURCES}) - set_target_properties(${module}-bin PROPERTIES OUTPUT_NAME ${module}) - set_target_properties(${module}-bin PROPERTIES COMPILE_FLAGS "-D_COMPILE_STANDALONE") - target_link_libraries(${module}-bin standalone ${LIBRARIES}) - install(TARGETS ${module}-bin DESTINATION bin) -endif() - -# octave mex file -set(install_mex "") -if(NOT $ENV{OCTAVE_INCLUDE} STREQUAL "") - if(NOT $ENV{OCTAVE_LIBS} STREQUAL "") - - add_library(${module}-oct SHARED ${SOURCES}) - set_target_properties(${module}-oct PROPERTIES OUTPUT_NAME ${module}) - set_target_properties(${module}-oct PROPERTIES PREFIX "am_") - set_target_properties(${module}-oct PROPERTIES SUFFIX .mex) - - set_target_properties(${module}-oct PROPERTIES COMPILE_FLAGS "-I$ENV{OCTAVE_INCLUDE} -D_COMPILE_MEX -Wl,-Bsymbolic -L$ENV{OCTAVE_LIBS} -loctinterp -loctave -lcruft -Wl,-Bsymbolic-functions -Wl,-z,relro") - target_link_libraries(${module}-oct aloe_octave ${LIBRARIES}) - install(TARGETS ${module}-oct DESTINATION mex) - - endif() -endif() - -#matlab mex -if(NOT $ENV{MATLAB_ROOT} STREQUAL "") - add_library(${module}-mat SHARED ${SOURCES}) - set_target_properties(${module}-mat PROPERTIES OUTPUT_NAME ${module}) - set_target_properties(${module}-mat PROPERTIES PREFIX "am_") - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set_target_properties(${module}-mat PROPERTIES SUFFIX .mexa64) - set_target_properties(${module}-mat PROPERTIES COMPILE_FLAGS "-I$ENV{MATLAB_ROOT} -O -pthread -shared -Wl,--version-script,$ENV{MATLAB_ROOT}/extern/lib/glnxa64/mexFunction.map -Wl,--no-undefined -Wl,-rpath-link,$ENV{MATLAB_ROOT}/bin/glnxa64 -L$ENV{MATLAB_ROOT}/bin/glnxa64 -lmx -lmex -lmat -lm -lstdc++") - else() - set_target_properties(${module}-mat PROPERTIES SUFFIX .mexglx) - set_target_properties(${module}-mat PROPERTIES COMPILE_FLAGS "-I$ENV{MATLAB_ROOT} -O -pthread -shared -m32 -Wl,--version-script,$ENV{MATLAB_ROOT}/extern/lib/glnx86/mexFunction.map -Wl,--no-undefined -Wl,-rpath-link,$ENV{MATLAB_ROOT}/bin/glnx86 -L$ENV{MATLAB_ROOT}/bin/glnx86 -lmx -lmex -lmat -lm -lstdc++") - endif() - - target_link_libraries(${module}-mat aloe_matlab ${LIBRARIES}) - install(TARGETS ${module}-mat DESTINATION mex) - -endif() - - - - - - - - - - - diff --git a/scripts/lib_binsource/src/binsource.c b/scripts/lib_binsource/src/binsource.c deleted file mode 100644 index 6fa811ba9..000000000 --- a/scripts/lib_binsource/src/binsource.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file has been automatically generated from binsource - */ - -#include -#include -#include -#include - -#include "binsource.h" - -binsource_hl binsource; - -pmid_t nbits_id; - -int out_len[NOF_OUTPUT_ITF]; - -int initialize() { - - /* Initialization Parameters */ - if (param_get_int_name("cache_seq_nbits", &binsource.init.cache_seq_nbits)) { - binsource.init.cache_seq_nbits = 2; - } - if (param_get_int_name("seed", &binsource.init.seed)) { - binsource.init.seed = 0; - } - - /* Input Control Parameters */ - nbits_id = param_id("nbits"); - - /* Initialization function */ - return binsource_initialize(&binsource); -} - - -int work(void **inp, void **out) { - int i,n; -#if NOF_INPUTS>1 - for (i=0;i1 - for (i=0;i - -typedef uint8_t output_t; - -#define INPUT_MAX_SAMPLES 0 -#define OUTPUT_MAX_SAMPLES 2048*14 - -#define NOF_INPUT_ITF 0 -#define NOF_OUTPUT_ITF 2 - -#endif -/**@} */ - -#define GENERATE_COMPLEX - -#ifndef INCLUDE_DEFS_ONLY - -/* Input and output buffer sizes (in number of samples) */ -const int input_max_samples = INPUT_MAX_SAMPLES; -const int output_max_samples = OUTPUT_MAX_SAMPLES; - -/* leave these two lines unmodified */ -const int input_sample_sz = sizeof(input_t); -int output_sample_sz = sizeof(output_t); - -/* Number of I/O interfaces. All have the same maximum size */ -const int nof_input_itf = NOF_INPUT_ITF; -const int nof_output_itf = NOF_OUTPUT_ITF; - -#endif diff --git a/scripts/lib_binsource/test/test_generate.c b/scripts/lib_binsource/test/test_generate.c deleted file mode 100644 index 8a2c6185c..000000000 --- a/scripts/lib_binsource/test/test_generate.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012, Ismael Gomez-Miguelez . - * This file is part of ALOE++ (http://flexnets.upc.edu/) - * - * ALOE++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ALOE++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ALOE++. If not, see . - */ - -/* Functions that generate the test data fed into the DSP modules being developed */ -#include -#include -#include - -#include -#include - -#define INCLUDE_DEFS_ONLY -#include "binsource.h" - -int offset=0; - -/** - * Generates input signal. VERY IMPORTANT to fill length vector with the number of - * samples that have been generated. - * @param inp Input interface buffers. Data from other interfaces is stacked in the buffer. - * Use in(ptr,idx) to access the address. - * - * @param lengths Save on n-th position the number of samples generated for the n-th interface - */ -int generate_input_signal(void *in, int *lengths) -{ - int i; - input_t *input = in; - int block_length; - pmid_t blen_id; - - blen_id = param_id("block_length"); - if (!blen_id) { - moderror("Parameter block_length not found\n"); - return -1; - } - - if (!param_get_int(blen_id,&block_length)) { - moderror("Getting integer parameter block_length\n"); - return -1; - } - - modinfo_msg("Parameter block_length is %d\n",block_length); - - /** HERE INDICATE THE LENGTH OF THE SIGNAL */ - lengths[0] = block_length; - - for (i=0;i" % str(self._lib_) - - def _getFunction(self, funcName): - try: - func = getattr(self._lib_, funcName) - except: - raise Exception("Function name '%s' appears in headers but not in library!" % func) - - #print "create function %s," % (funcName), self._defs_['functions'][funcName] - return CFunction(self, func, self._defs_['functions'][funcName], funcName) - - def _ctype(self, typ, pointers=True): - """return a ctype object representing the named type. - If pointers is True, the class returned includes all pointer/array specs provided. - Otherwise, the class returned is just the base type with no pointers.""" - try: - typ = self._headers_.evalType(typ) - mods = typ[1:][:] - - ## Create the initial type - ## Some types like ['char', '*'] have a specific ctype (c_char_p) - ## (but only do this if pointers == True) - if pointers and len(typ) > 1 and typ[1] == '*' and typ[0] in CLibrary.cPtrTypes: - cls = CLibrary.cPtrTypes[typ[0]] - mods = typ[2:] - - ## If the base type is in the list of existing ctypes: - elif typ[0] in CLibrary.cTypes: - cls = CLibrary.cTypes[typ[0]] - - ## structs, unions, enums: - elif typ[0][:7] == 'struct ': - cls = self._cstruct('structs', self._defs_['types'][typ[0]][1]) - elif typ[0][:6] == 'union ': - cls = self._cstruct('unions', self._defs_['types'][typ[0]][1]) - elif typ[0][:5] == 'enum ': - cls = c_int - - ## void - elif typ[0] == 'void': - cls = None - else: - #print typ - raise Exception("Can't find base type for %s" % str(typ)) - - if not pointers: - return cls - - ## apply pointers and arrays - while len(mods) > 0: - m = mods.pop(0) - if isinstance(m, basestring): ## pointer or reference - if m[0] == '*' or m[0] == '&': - for i in m: - cls = POINTER(cls) - elif type(m) is list: ## array - for i in m: - if i == -1: ## -1 indicates an 'incomplete type' like "int variable[]" - cls = POINTER(cls) ## which we should interpret like "int *variable" - else: - cls = cls * i - elif type(m) is tuple: ## Probably a function pointer - ## Find pointer and calling convention - isPtr = False - conv = '__cdecl' - if len(mods) == 0: - raise Exception("Function signature with no pointer:", m, mods) - for i in [0,1]: - if len(mods) < 1: - break - if mods[0] == '*': - mods.pop(0) - isPtr = True - elif mods[0] in ['__stdcall', '__cdecl']: - conv = mods.pop(0) - else: - break - if not isPtr: - raise Exception("Not sure how to handle type (function without single pointer): %s" % str(typ)) - - if conv == '__stdcall': - mkfn = WINFUNCTYPE - else: - mkfn = CFUNCTYPE - #print "Create function pointer (%s)" % conv - - args = [self._ctype(arg[1]) for arg in m] - cls = mkfn(cls, *args) - - else: - raise Exception("Not sure what to do with this type modifier: '%s'" % str(p)) - return cls - except: - print "Error while processing type", typ - raise - - def _cstruct(self, strType, strName): - if strName not in self._structs_: - - ## Resolve struct name--typedef aliases allowed. - if strName not in self._defs_[strType]: - if strName not in self._defs_['types']: - raise Exception('No struct/union named "%s"' % strName) - typ = self._headers_.evalType([strName])[0] - if typ[:7] != 'struct ' and typ[:6] != 'union ': - raise Exception('No struct/union named "%s"' % strName) - strName = self._defs_['types'][typ][1] - - ## Pull struct definition - defn = self._defs_[strType][strName] - - - ## create ctypes class - defs = defn['members'][:] - if strType == 'structs': - class s(Structure): - def __repr__(self): - return "" % strName - elif strType == 'unions': - class s(Union): - def __repr__(self): - return "" % strName - - - ## must register struct here to allow recursive definitions. - self._structs_[strName] = s - - if defn['pack'] is not None: - s._pack_ = defn['pack'] - - ## assign names to anonymous members - members = [] - anon = [] - for i in range(len(defs)): - if defs[i][0] is None: - c = 0 - while True: - name = 'anon_member%d' % c - if name not in members: - defs[i][0] = name - anon.append(name) - break - members.append(defs[i][0]) - - s._anonymous_ = anon - s._fields_ = [(m[0], self._ctype(m[1])) for m in defs] - s._defaults_ = [m[2] for m in defs] - return self._structs_[strName] - - - -class CFunction: - def __init__(self, lib, func, sig, name): - self.lib = lib - self.func = func - #print sig - self.sig = list(sig) # looks like [return_type, [(argName, type, default), (argName, type, default), ...]] - self.sig[1] = [s for s in sig[1] if s[1] != ['void']] ## remove void args from list - for conv in ['__stdcall', '__cdecl']: - if conv in self.sig[0]: - self.sig[0].remove(conv) - self.name = name - self.restype = lib._ctype(self.sig[0]) - #func.restype = self.restype - self.argTypes = [lib._ctype(s[1]) for s in self.sig[1]] - func.argtypes = self.argTypes - self.reqArgs = [x[0] for x in self.sig[1] if x[2] is None] - self.argInds = dict([(self.sig[1][i][0], i) for i in range(len(self.sig[1]))]) ## mapping from argument names to indices - #print "created func", self, sig, self.argTypes - - def argCType(self, arg): - """Return the ctype required for the specified argument. - arg can be either an integer or the name of the argument. - """ - if isinstance(arg, basestring): - arg = self.argInds[arg] - return self.lib._ctype(self.sig[1][arg][1]) - - def __call__(self, *args, **kwargs): - """Invoke the SO or dll function referenced, converting all arguments to the correct type. - Keyword arguments are allowed as long as the header specifies the argument names. - Arguments which are passed byref may be omitted entirely, and will be automaticaly generated. - To pass a NULL pointer, give None as the argument. - Returns the return value of the function call as well as all of the arguments (so that objects passed by reference can be retrieved)""" - #print "CALL: %s(%s)" % (self.name, ", ".join(map(str, args) + ["%s=%s" % (k, str(kwargs[k])) for k in kwargs])) - #print " sig:", self.sig - argList = [None] * max(len(self.reqArgs), len(args)) ## We'll need at least this many arguments. - - ## First fill in args - for i in range(len(args)): - #argList[i] = self.argTypes[i](args[i]) - if args[i] is None: - argList[i] = self.lib.Null - else: - argList[i] = args[i] - - ## Next fill in kwargs - for k in kwargs: - #print " kw:", k - if k not in self.argInds: - print "Function signature:", self.prettySignature() - raise Exception("Function signature has no argument named '%s'" % k) - ind = self.argInds[k] - if ind >= len(argList): ## stretch argument list if needed - argList += [None] * (ind - len(argList) + 1) - #argList[ind] = self.coerce(kwargs[k], self.argTypes[ind]) - if kwargs[k] is None: - argList[ind] = self.lib.Null - else: - argList[ind] = kwargs[k] - - guessedArgs = [] - ## Finally, fill in remaining arguments if they are pointers to int/float/void*/struct values - ## (we assume these are to be modified by the function and their initial value is not important) - for i in range(len(argList)): - if argList[i] is None or argList[i] is self.lib.Null: - try: - sig = self.sig[1][i][1] - argType = self.lib._headers_.evalType(sig) - if argList[i] is self.lib.Null: ## request to build a null pointer - if len(argType) < 2: - raise Exception("Can not create NULL for non-pointer argument type: %s" % str(argType)) - argList[i] = self.lib._ctype(sig)() - #elif argType == ['char', '*']: ## pass null pointer if none was specified. This is a little dangerous, but some functions will expect it. - #argList[i] = c_char_p() ## On second thought: let's just require the user to explicitly ask for a NULL pointer. - else: - if argType == ['void', '**'] or argType == ['void', '*', '*']: - cls = c_void_p - else: - assert len(argType) == 2 and argType[1] == '*' ## Must be 2-part type, second part must be '*' - cls = self.lib._ctype(sig, pointers=False) - argList[i] = pointer(cls(0)) - guessedArgs.append(i) - except: - if sys.exc_info()[0] is not AssertionError: - raise - #sys.excepthook(*sys.exc_info()) - print "Function signature:", self.prettySignature() - raise Exception("Function call '%s' missing required argument %d '%s'. (See above for signature)" % (self.name, i, self.sig[1][i][0])) - #print " args:", argList - try: - res = self.func(*argList) - except: - print "Function call failed. Signature is:", self.prettySignature() - print "Arguments:", argList - print "Argtypes:", self.func.argtypes - raise - #print " result:", res - - cr = CallResult(res, argList, self.sig, guessed=guessedArgs) - return cr - - def prettySignature(self): - return "%s %s(%s)" % (''.join(self.sig[0]), self.name, ', '.join(["%s %s" % ("".join(map(str, s[1])), s[0]) for s in self.sig[1]])) - -class CallResult: - """Class for bundling results from C function calls. Allows access to the function - return value as well as all of the arguments, since the function call will often return - extra values via these arguments. - - Original ctype objects can be accessed via result.rval or result.args - - Python values carried by these objects can be accessed using () - To access values: - - The return value: () - - The nth argument passed: [n] - - The argument by name: ['name'] - - All values that were auto-generated: .auto() - - The class can also be used as an iterator, so that tuple unpacking is possible: - ret, arg1, arg2 = lib.runSomeFunction(...) - """ - def __init__(self, rval, args, sig, guessed): - self.rval = rval ## return value of function call - self.args = args ## list of arguments to function call - self.sig = sig ## function signature - self.guessed = guessed ## list of arguments that were generated automatically (usually byrefs) - - def __call__(self): - #print "Clibrary:", type(self.rval), self.mkVal(self.rval) - if self.sig[0] == ['void']: - return None - return self.mkVal(self.rval) - - def __getitem__(self, n): - if type(n) is int: - return self.mkVal(self.args[n]) - elif type(n) is str: - ind = self.findArg(n) - return self.mkVal(self.args[ind]) - else: - raise Exception("Index must be int or str.") - - def __setitem__(self, n, val): - if type(n) is int: - self.args[n] = val - elif type(n) is str: - ind = self.findArg(n) - self.args[ind] = val - else: - raise Exception("Index must be int or str.") - - - def mkVal(self, obj): - while not hasattr(obj, 'value'): - if not hasattr(obj, 'contents'): - return obj - try: - obj = obj.contents - except ValueError: - return None - - return obj.value - - - def findArg(self, arg): - for i in range(len(self.sig[1])): - if self.sig[1][i][0] == arg: - return i - raise Exception("Can't find argument '%s' in function signature. Arguments are: %s" % (arg, str([a[0] for a in self.sig[1]]))) - - def __iter__(self): - yield self() - for i in range(len(self.args)): - yield(self[i]) - - def auto(self): - return [self[n] for n in self.guessed] - - - - - diff --git a/scripts/pyclibrary/CParser.py b/scripts/pyclibrary/CParser.py deleted file mode 100644 index df2b4dea6..000000000 --- a/scripts/pyclibrary/CParser.py +++ /dev/null @@ -1,1274 +0,0 @@ -# -*- coding: utf-8 -*- -""" -CParser.py - C parsing library -Copyright 2010 Luke Campagnola -Distributed under MIT/X11 license. See license.txt for more infomation. - -Used for extracting data such as macro definitions, variables, typedefs, and function -signatures from C files (preferrably header files). -""" - -import sys, re, os - -__all__ = ['winDefs', 'CParser'] - - -def winDefs(verbose=False): - """Convenience function. Returns a parser which loads a selection of windows headers included with - CParser. These definitions can either be accessed directly or included before parsing - another file like this: - windefs = CParser.winDefs() - p = CParser.CParser("headerFile.h", copyFrom=windefs) - Definitions are pulled from a selection of header files included in Visual Studio - (possibly not legal to distribute? Who knows.), some of which have been abridged - because they take so long to parse. - """ - headerFiles = ['WinNt.h', 'WinDef.h', 'WinBase.h', 'BaseTsd.h', 'WTypes.h', 'WinUser.h'] - d = os.path.dirname(__file__) - p = CParser( - [os.path.join(d, 'headers', h) for h in headerFiles], - types={'__int64': ('long long')}, - macros={'_WIN32': '', '_MSC_VER': '800', 'CONST': 'const', 'NO_STRICT': None}, - processAll=False - ) - p.processAll(cache=os.path.join(d, 'headers', 'WinDefs.cache'), noCacheWarning=True, verbose=verbose) - return p - - -class CParser(): - """Class for parsing C code to extract variable, struct, enum, and function declarations as well as preprocessor macros. This is not a complete C parser; instead, it is meant to simplify the process - of extracting definitions from header files in the absence of a complete build system. Many files - will require some amount of manual intervention to parse properly (see 'replace' and extra arguments - to __init__) - - Usage: - ## create parser object, load two files - p = CParser(['header1.h', 'header2.h']) - - ## remove comments, preprocess, and search for declarations - p.processAll() - - ## just to see what was successfully parsed from the files - p.printAll() - - ## access parsed declarations - allValues = p.defs['values'] - functionSignatures = p.defs['functions'] - ... - - ## To see what was not successfully parsed: - unp = p.processAll(returnUnparsed=True) - for s in unp: - print s - """ - - cacheVersion = 22 ## increment every time cache structure or parsing changes to invalidate old cache files. - - def __init__(self, files=None, replace=None, copyFrom=None, processAll=True, cache=None, verbose=False, **args): - """Create a C parser object fiven a file or list of files. Files are read to memory and operated - on from there. - 'copyFrom' may be another CParser object from which definitions should be copied. - 'replace' may be specified to perform string replacements before parsing. - format is {'searchStr': 'replaceStr', ...} - Extra parameters may be used to specify the starting state of the parser. For example, - one could provide a set of missing type declarations by - types={'UINT': ('unsigned int'), 'STRING': ('char', 1)} - Similarly, preprocessor macros can be specified: - macros={'WINAPI': ''} - """ - - - self.defs = {} ## holds all definitions - self.fileDefs = {} ## holds definitions grouped by the file they came from - - self.initOpts = args.copy() - self.initOpts['files'] = [] - self.initOpts['replace'] = {} - - self.dataList = ['types', 'variables', 'fnmacros', 'macros', 'structs', 'unions', 'enums', 'functions', 'values'] - - self.verbose = False - - # placeholders for definitions that change during parsing - #if hasPyParsing: - #self.macroExpr = Forward() - #self.fnMacroExpr = Forward() - #self.definedType = Forward() - #self.definedStruct = Forward() - #self.definedEnum = Forward() - - self.fileOrder = [] - self.files = {} - self.packList = {} ## list describing struct packing rules as defined by #pragma pack - if files is not None: - if type(files) is str: - files = [files] - for f in files: - self.loadFile(f, replace) - - ## initialize empty definition lists - for k in self.dataList: - self.defs[k] = {} - #for f in files: - #self.fileDefs[f][k] = {} - - self.compiledTypes = {} ## holds translations from typedefs/structs/unions to fundamental types - - self.currentFile = None - - # Import extra arguments if specified - for t in args: - for k in args[t].keys(): - self.addDef(t, k, args[t][k]) - - # Import from other CParsers if specified - if copyFrom is not None: - if type(copyFrom) not in [list, tuple]: - copyFrom = [copyFrom] - for p in copyFrom: - self.importDict(p.fileDefs) - - if processAll: - self.processAll(cache=cache, verbose=verbose) - - def processAll(self, cache=None, returnUnparsed=False, printAfterPreprocess=False, noCacheWarning=True, verbose=False): - """Remove comments, preprocess, and parse declarations from all files. (operates in memory; does not alter the original files) - Returns a list of the results from parseDefs. - 'cache' may specify a file where cached results are be stored or retrieved. The cache - is automatically invalidated if any of the arguments to __init__ are changed, or if the - C files are newer than the cache. - 'returnUnparsed' is passed directly to parseDefs. - 'printAfterPreprocess' is for debugging; prints the result of preprocessing each file.""" - self.verbose = verbose - if cache is not None and self.loadCache(cache, checkValidity=True): - if verbose: - print "Loaded cached definitions; will skip parsing." - return ## cached values loaded successfully, nothing left to do here - #else: - #print "No cache.", cache - - - results = [] - if noCacheWarning or verbose: - print "Parsing C header files (no valid cache found). This could take several minutes..." - for f in self.fileOrder: - #fn = os.path.basename(f) - if self.files[f] is None: - ## This means the file could not be loaded and there was no cache. - raise Exception('Could not find header file "%s" or a suitable cache file.' % f) - if verbose: - print "Removing comments from file '%s'..." % f - self.removeComments(f) - if verbose: - print "Preprocessing file '%s'..." % f - self.preprocess(f) - if printAfterPreprocess: - print "===== PREPROCSSED %s =======" % f - print self.files[f] - if verbose: - print "Parsing definitions in file '%s'..." % f - results.append(self.parseDefs(f, returnUnparsed)) - - if cache is not None: - if verbose: - print "Writing cache file '%s'" % cache - self.writeCache(cache) - - return results - - - def loadCache(self, cacheFile, checkValidity=False): - """Load a cache file. Used internally if cache is specified in processAll(). - if checkValidity=True, then run several checks before loading the cache: - - cache file must not be older than any source files - - cache file must not be older than this library file - - options recorded in cache must match options used to initialize CParser""" - - ## make sure cache file exists - if type(cacheFile) is not str: - raise Exception("cache file option must be a string.") - if not os.path.isfile(cacheFile): - d = os.path.dirname(__file__) ## If file doesn't exist, search for it in this module's path - cacheFile = os.path.join(d, "headers", cacheFile) - if not os.path.isfile(cacheFile): - if self.verbose: - print "Can't find requested cache file." - return False - - ## make sure cache is newer than all input files - if checkValidity: - mtime = os.stat(cacheFile).st_mtime - for f in self.fileOrder: - ## if file does not exist, then it does not count against the validity of the cache. - if os.path.isfile(f) and os.stat(f).st_mtime > mtime: - if self.verbose: - print "Cache file is out of date." - return False - - try: - ## read cache file - import pickle - cache = pickle.load(open(cacheFile, 'rb')) - - ## make sure __init__ options match - if checkValidity: - if cache['opts'] != self.initOpts: - if self.verbose: - print "Cache file is not valid--created using different initialization options." - print cache['opts'] - print self.initOpts - return False - elif self.verbose: - print "Cache init opts are OK:" - print cache['opts'] - if cache['version'] < self.cacheVersion: - if self.verbose: - print "Cache file is not valid--cache format has changed." - return False - - ## import all parse results - self.importDict(cache['fileDefs']) - return True - except: - print "Warning--cache read failed:" - sys.excepthook(*sys.exc_info()) - return False - - def importDict(self, data): - """Import definitions from a dictionary. The dict format should be the - same as CParser.fileDefs. Used internally; does not need to be called - manually.""" - for f in data.keys(): - self.currentFile = f - for k in self.dataList: - for n in data[f][k]: - self.addDef(k, n, data[f][k][n]) - - def writeCache(self, cacheFile): - """Store all parsed declarations to cache. Used internally.""" - cache = {} - cache['opts'] = self.initOpts - cache['fileDefs'] = self.fileDefs - cache['version'] = self.cacheVersion - #for k in self.dataList: - #cache[k] = getattr(self, k) - import pickle - pickle.dump(cache, open(cacheFile, 'wb')) - - def loadFile(self, file, replace=None): - """Read a file, make replacements if requested. Called by __init__, should - not be called manually.""" - if not os.path.isfile(file): - ## Not a fatal error since we might be able to function properly if there is a cache file.. - #raise Exception("File %s not found" % file) - print "Warning: C header '%s' is missing; this may cause trouble." % file - self.files[file] = None - return False - - fd = open(file, 'rU') ## U causes all newline types to be converted to \n - self.files[file] = fd.read() - fd.close() - - if replace is not None: - for s in replace: - self.files[file] = re.sub(s, replace[s], self.files[file]) - self.fileOrder.append(file) - bn = os.path.basename(file) - self.initOpts['replace'][bn] = replace - self.initOpts['files'].append(bn) # only interested in the file names; the directory may change between systems. - return True - - - - - - #### Beginning of processing functions - - def assertPyparsing(self): - """Make sure pyparsing module is available.""" - global hasPyParsing - if not hasPyParsing: - raise Exception("CParser class requires 'pyparsing' library for actual parsing work. Without this library, CParser can only be used with previously cached parse results.") - - - def removeComments(self, file): - """Remove all comments from file. (operates in memory; does not alter the original files)""" - self.assertPyparsing() - text = self.files[file] - cplusplusLineComment = Literal("//") + restOfLine - # match quoted strings first to prevent matching comments inside quotes - self.files[file] = (quotedString | cStyleComment.suppress() | cplusplusLineComment.suppress()).transformString(text) - - - def preprocess(self, file): - """Scan named file for preprocessor directives, removing them while expanding macros. (operates in memory; does not alter the original files)""" - self.assertPyparsing() - self.buildParser() ## we need this so that evalExpr works properly - self.currentFile = file - packStack = [(None,None)] ## stack for #pragma pack push/pop - self.packList[file] = [(0,None)] - packing = None ## current packing value - - text = self.files[file] - - ## First join together lines split by \\n - text = Literal('\\\n').suppress().transformString(text) - - #self.ppDirective = Combine("#" + Word(alphas).leaveWhitespace()) + restOfLine - - # define the structure of a macro definition - name = Word(alphas+'_', alphanums+'_')('name') - self.ppDefine = name.setWhitespaceChars(' \t')("macro") + Optional(lparen + delimitedList(name) + rparen).setWhitespaceChars(' \t')('args') + SkipTo(LineEnd())('value') - self.ppDefine.setParseAction(self.processMacroDefn) - - #self.updateMacroDefns() - #self.updateFnMacroDefns() - - # define pattern for scanning through the input string - #self.macroExpander = (self.macroExpr | self.fnMacroExpr) - - ## Comb through lines, process all directives - lines = text.split('\n') - - result = [] - #macroExpander = (quotedString | self.macroExpander) - directive = re.compile(r'\s*#([a-zA-Z]+)(.*)$') - ifTrue = [True] - ifHit = [] - for i in range(len(lines)): - line = lines[i] - newLine = '' - m = directive.match(line) - if m is None: # regular code line - if ifTrue[-1]: # only include if we are inside the correct section of an IF block - #line = macroExpander.transformString(line) # expand all known macros - newLine = self.expandMacros(line) - else: # macro line - d = m.groups()[0] - rest = m.groups()[1] - - #print "PREPROCESS:", d, rest - if d == 'ifdef': - d = 'if' - rest = 'defined '+rest - elif d == 'ifndef': - d = 'if' - rest = '!defined '+rest - - ## Evaluate 'defined' operator before expanding macros - if d in ['if', 'elif']: - def pa(t): - return ['0', '1'][t['name'] in self.defs['macros'] or t['name'] in self.defs['fnmacros']] - rest = ( - Keyword('defined') + - (name | lparen + name + rparen) - ).setParseAction(pa).transformString(rest) - elif d in ['define', 'undef']: - macroName, rest = re.match(r'\s*([a-zA-Z_][a-zA-Z0-9_]*)(.*)$', rest).groups() - - ## Expand macros if needed - if rest is not None and (all(ifTrue) or d in ['if', 'elif']): - rest = self.expandMacros(rest) - - if d == 'elif': - if ifHit[-1] or not all(ifTrue[:-1]): - ev = False - else: - ev = self.evalPreprocessorExpr(rest) - if self.verbose: - print " "*(len(ifTrue)-2) + line, rest, ev - ifTrue[-1] = ev - ifHit[-1] = ifHit[-1] or ev - elif d == 'else': - if self.verbose: - print " "*(len(ifTrue)-2) + line, not ifHit[-1] - ifTrue[-1] = (not ifHit[-1]) and all(ifTrue[:-1]) - ifHit[-1] = True - elif d == 'endif': - ifTrue.pop() - ifHit.pop() - if self.verbose: - print " "*(len(ifTrue)-1) + line - elif d == 'if': - if all(ifTrue): - ev = self.evalPreprocessorExpr(rest) - else: - ev = False - if self.verbose: - print " "*(len(ifTrue)-1) + line, rest, ev - ifTrue.append(ev) - ifHit.append(ev) - elif d == 'define': - if not ifTrue[-1]: - continue - if self.verbose: - print " "*(len(ifTrue)) + "define:", macroName, rest - try: - self.ppDefine.parseString(macroName+ ' ' + rest) ## macro is registered here - except: - print "Error processing macro definition:", macroName, rest - print " ", sys.exc_info()[1] - elif d == 'undef': - if not ifTrue[-1]: - continue - try: - self.remDef('macros', macroName.strip()) - #self.macroListString = '|'.join(self.defs['macros'].keys() + self.defs['fnmacros'].keys()) - #self.updateMacroDefns() - except: - if sys.exc_info()[0] is not KeyError: - sys.excepthook(*sys.exc_info()) - print "Error removing macro definition '%s'" % macroName.strip() - elif d == 'pragma': ## Check for changes in structure packing - if not ifTrue[-1]: - continue - m = re.match(r'\s+pack\s*\(([^\)]+)\)', rest) - if m is None: - continue - opts = [s.strip() for s in m.groups()[0].split(',')] - - pushpop = id = val = None - for o in opts: - if o in ['push', 'pop']: - pushpop = o - elif o.isdigit(): - val = int(o) - else: - id = o - - if val is not None: - packing = val - - if pushpop == 'push': - packStack.append((packing, id)) - elif opts[0] == 'pop': - if id is None: - packStack.pop() - else: - ind = None - for i in range(len(packStack)): - if packStack[i][1] == id: - ind = i - break - if ind is not None: - packStack = packStack[:ind] - if val is None: - packing = packStack[-1][0] - else: - packing = int(opts[0]) - - if self.verbose: - print ">> Packing changed to %s at line %d" % (str(packing), i) - self.packList[file].append((i, packing)) - else: - pass ## Ignore any other directives - - result.append(newLine) - self.files[file] = '\n'.join(result) - - def evalPreprocessorExpr(self, expr): - ## make a few alterations so the expression can be eval'd - macroDiffs = ( - Literal('!').setParseAction(lambda: ' not ') | - Literal('&&').setParseAction(lambda: ' and ') | - Literal('||').setParseAction(lambda: ' or ') | - Word(alphas+'_',alphanums+'_').setParseAction(lambda: '0')) - expr2 = macroDiffs.transformString(expr) - - try: - ev = bool(eval(expr2)) - except: - if self.verbose: - print "Error evaluating preprocessor expression: %s [%s]" % (expr, expr2) - print " ", sys.exc_info()[1] - ev = False - return ev - - - - #def updateMacroDefns(self): - ##self.macroExpr << MatchFirst( [Keyword(m)('macro') for m in self.defs['macros']] ) - ##self.macroExpr.setParseAction(self.processMacroRef) - - ## regex is faster than pyparsing. - ## Matches quoted strings and macros - - ##names = self.defs['macros'].keys() + self.defs['fnmacros'].keys() - #if len(self.macroListString) == 0: - #self.macroRegex = None - #else: - #self.macroRegex = re.compile( - #r'("(\\"|[^"])*")|(\b(%s)\b)' % self.macroListString - #) - - #def updateFnMacroDefns(self): - #self.fnMacroExpr << MatchFirst( [(Keyword(m)('macro') + lparen + Group(delimitedList(expression))('args') + rparen) for m in self.defs['fnmacros']] ) - #self.fnMacroExpr.setParseAction(self.processFnMacroRef) - - - def processMacroDefn(self, t): - """Parse a #define macro and register the definition""" - if self.verbose: - print "MACRO:", t - #macroVal = self.macroExpander.transformString(t.value).strip() - #macroVal = Literal('\\\n').suppress().transformString(macroVal) ## remove escaped newlines - macroVal = t.value.strip() - if macroVal in self.defs['fnmacros']: - self.addDef('fnmacros', t.macro, self.defs['fnmacros'][macroVal]) - if self.verbose: - print " Copy fn macro %s => %s" % (macroVal, t.macro) - else: - if t.args == '': - val = self.evalExpr(macroVal) - self.addDef('macros', t.macro, macroVal) - self.addDef('values', t.macro, val) - if self.verbose: - print " Add macro:", t.macro, "("+str(val)+")", self.defs['macros'][t.macro] - else: - self.addDef('fnmacros', t.macro, self.compileFnMacro(macroVal, [x for x in t.args])) - if self.verbose: - print " Add fn macro:", t.macro, t.args, self.defs['fnmacros'][t.macro] - - #if self.macroListString == '': - #self.macroListString = t.macro - #else: - #self.macroListString += '|' + t.macro - #self.updateMacroDefns() - #self.macroExpr << MatchFirst( map(Keyword,self.defs['macros'].keys()) ) - return "#define " + t.macro + " " + macroVal - - - def compileFnMacro(self, text, args): - """Turn a function macro spec into a compiled description""" - ## find all instances of each arg in text - argRegex = re.compile(r'("(\\"|[^"])*")|(\b(%s)\b)' % ('|'.join(args))) - start = 0 - parts = [] - argOrder = [] - N = 3 - for m in argRegex.finditer(text): - arg = m.groups()[N] - #print m, arg - if arg is not None: - parts.append(text[start:m.start(N)] + '%s') - start = m.end(N) - argOrder.append(args.index(arg)) - parts.append(text[start:]) - return (''.join(parts), argOrder) - - - def expandMacros(self, line): - reg = re.compile(r'("(\\"|[^"])*")|(\b(\w+)\b)') - parts = [] - start = 0 - N = 3 ## the group number to check for macro names - macros = self.defs['macros'] - fnmacros = self.defs['fnmacros'] - for m in reg.finditer(line): - name = m.groups()[N] - if name in macros: - parts.append(line[start:m.start(N)]) - start = m.end(N) - parts.append(macros[name]) - elif name in fnmacros: - try: ## If function macro expansion fails, just ignore it. - exp, end = self.expandFnMacro(name, line[m.end(N):]) - parts.append(line[start:m.start(N)]) - start = end + m.end(N) - parts.append(exp) - except: - if sys.exc_info()[1][0] != 0: - print "Function macro expansion failed:", name, line[m.end(N):] - raise - parts.append(line[start:]) - return ''.join(parts) - - - - #def expandMacros(self, line): - #if self.macroRegex is None: - #return line - #parts = [] - #start = 0 - #N = 3 ## the group number to check for macro names - #for m in self.macroRegex.finditer(line): - #name = m.groups()[N] - #if name is not None: - #if name in self.defs['macros']: - #parts.append(line[start:m.start(N)]) - #start = m.end(N) - #parts.append(self.defs['macros'][name]) - #elif name in self.defs['fnmacros']: - #try: ## If function macro expansion fails, just ignore it. - #exp, end = self.expandFnMacro(name, line[m.end(N):]) - #parts.append(line[start:m.start(N)]) - #start = end + m.end(N) - #parts.append(exp) - #except: - #if sys.exc_info()[1][0] != 0: - #print "Function macro expansion failed:", name, line[m.end(N):] - #raise - - #else: - #raise Exception("Macro '%s' not found (internal error)" % name) - #parts.append(line[start:]) - #return ''.join(parts) - - def expandFnMacro(self, name, text): - #print "expandMacro:", name, text - defn = self.defs['fnmacros'][name] - ## defn looks like ('%s + %s / %s', (0, 0, 1)) - - argList = stringStart + lparen + Group(delimitedList(expression))('args') + rparen - res = [x for x in argList.scanString(text, 1)] - if len(res) == 0: - raise Exception(0, "Function macro '%s' not followed by (...)" % name) - args, start, end = res[0] - #print " ", res - #print " ", args - #print " ", defn - newStr = defn[0] % tuple([args[0][i] for i in defn[1]]) - #print " ", newStr - return (newStr, end) - - - # parse action to replace macro references with their respective definition - #def processMacroRef(self, t): - #return self.defs['macros'][t.macro] - - #def processFnMacroRef(self, t): - #m = self.defs['fnmacros'][t.macro] - ##print "=====>>" - ##print "Process FN MACRO:", t - ##print " macro defn:", t.macro, m - ##print " macro call:", t.args - ### m looks like ('a + b', ('a', 'b')) - #newStr = m[0][:] - ##print " starting str:", newStr - #try: - #for i in range(len(m[1])): - ##print " step", i - #arg = m[1][i] - ##print " arg:", arg, '=>', t.args[i] - - #newStr = Keyword(arg).copy().setParseAction(lambda: t.args[i]).transformString(newStr) - ##print " new str:", newStr - #except: - ##sys.excepthook(*sys.exc_info()) - #raise - ##print "<<=====" - #return newStr - - - - - - - - - def parseDefs(self, file, returnUnparsed=False): - """Scan through the named file for variable, struct, enum, and function declarations. - Returns the entire tree of successfully parsed tokens. - If returnUnparsed is True, return a string of all lines that failed to match (for debugging).""" - self.assertPyparsing() - self.currentFile = file - #self.definedType << kwl(self.defs['types'].keys()) - - parser = self.buildParser() - if returnUnparsed: - text = parser.suppress().transformString(self.files[file]) - return re.sub(r'\n\s*\n', '\n', text) - else: - return [x[0] for x in parser.scanString(self.files[file])] - - def buildParser(self): - """Builds the entire tree of parser elements for the C language (the bits we support, anyway). - """ - - if hasattr(self, 'parser'): - return self.parser - - - self.assertPyparsing() - - - self.structType = Forward() - self.enumType = Forward() - self.typeSpec = (typeQualifier + ( - fundType | - Optional(kwl(sizeModifiers + signModifiers)) + ident | - self.structType | - self.enumType - ) + typeQualifier + msModifier).setParseAction(recombine) - #self.argList = Forward() - - ### Abstract declarators for use in function pointer arguments - # Thus begins the extremely hairy business of parsing C declarators. - # Whomever decided this was a reasonable syntax should probably never breed. - # The following parsers combined with the processDeclarator function - # allow us to turn a nest of type modifiers into a correctly - # ordered list of modifiers. - - self.declarator = Forward() - self.abstractDeclarator = Forward() - - ## abstract declarators look like: - # - # * - # **[num] - # (*)(int, int) - # *( )(int, int)[10] - # ...etc... - self.abstractDeclarator << Group( - typeQualifier + Group(ZeroOrMore('*'))('ptrs') + typeQualifier + - ((Optional('&')('ref')) | (lparen + self.abstractDeclarator + rparen)('center')) + - Optional(lparen + Optional(delimitedList(Group( - self.typeSpec('type') + - self.abstractDeclarator('decl') + - Optional(Literal('=').suppress() + expression, default=None)('val') - )), default=None) + rparen)('args') + - Group(ZeroOrMore(lbrack + Optional(expression, default='-1') + rbrack))('arrays') - ) - - ## Argument list may consist of declarators or abstract declarators - #self.argList << delimitedList(Group( - #self.typeSpec('type') + - #(self.declarator('decl') | self.abstractDeclarator('decl')) + - #Optional(Keyword('=')) + expression - #)) - - ## declarators look like: - # varName - # *varName - # **varName[num] - # (*fnName)(int, int) - # * fnName(int arg1=0)[10] - # ...etc... - self.declarator << Group( - typeQualifier + callConv + Group(ZeroOrMore('*'))('ptrs') + typeQualifier + - ((Optional('&')('ref') + ident('name')) | (lparen + self.declarator + rparen)('center')) + - Optional(lparen + Optional(delimitedList(Group( - self.typeSpec('type') + - (self.declarator | self.abstractDeclarator)('decl') + - Optional(Literal('=').suppress() + expression, default=None)('val') - )), default=None) + rparen)('args') + - Group(ZeroOrMore(lbrack + Optional(expression, default='-1') + rbrack))('arrays') - ) - self.declaratorList = Group(delimitedList(self.declarator)) - - ## typedef - self.typeDecl = Keyword('typedef') + self.typeSpec('type') + self.declaratorList('declList') + semi - self.typeDecl.setParseAction(self.processTypedef) - - ## variable declaration - self.variableDecl = Group(self.typeSpec('type') + Optional(self.declaratorList('declList')) + Optional(Literal('=').suppress() + (expression('value') | (lbrace + Group(delimitedList(expression))('arrayValues') + rbrace)))) + semi - - self.variableDecl.setParseAction(self.processVariable) - - ## function definition - #self.paramDecl = Group(self.typeSpec + (self.declarator | self.abstractDeclarator)) + Optional(Literal('=').suppress() + expression('value')) - self.typelessFunctionDecl = self.declarator('decl') + nestedExpr('{', '}').suppress() - self.functionDecl = self.typeSpec('type') + self.declarator('decl') + nestedExpr('{', '}').suppress() - self.functionDecl.setParseAction(self.processFunction) - - - ## Struct definition - self.structDecl = Forward() - structKW = (Keyword('struct') | Keyword('union')) - #self.structType << structKW('structType') + ((Optional(ident)('name') + lbrace + Group(ZeroOrMore( Group(self.structDecl | self.variableDecl.copy().setParseAction(lambda: None)) ))('members') + rbrace) | ident('name')) - self.structMember = ( - Group(self.variableDecl.copy().setParseAction(lambda: None)) | - (self.typeSpec + self.declarator + nestedExpr('{', '}')).suppress() | - (self.declarator + nestedExpr('{', '}')).suppress() - ) - self.declList = lbrace + Group(OneOrMore(self.structMember))('members') + rbrace - self.structType << (Keyword('struct') | Keyword('union'))('structType') + ((Optional(ident)('name') + self.declList) | ident('name')) - - self.structType.setParseAction(self.processStruct) - #self.updateStructDefn() - - self.structDecl = self.structType + semi - - ## enum definition - enumVarDecl = Group(ident('name') + Optional(Literal('=').suppress() + (integer('value') | ident('valueName')))) - - self.enumType << Keyword('enum') + (Optional(ident)('name') + lbrace + Group(delimitedList(enumVarDecl))('members') + rbrace | ident('name')) - self.enumType.setParseAction(self.processEnum) - - self.enumDecl = self.enumType + semi - - - #self.parser = (self.typeDecl | self.variableDecl | self.structDecl | self.enumDecl | self.functionDecl) - self.parser = (self.typeDecl | self.variableDecl | self.functionDecl) - return self.parser - - def processDeclarator(self, decl): - """Process a declarator (without base type) and return a tuple (name, [modifiers]) - See processType(...) for more information.""" - toks = [] - name = None - #print "DECL:", decl - if 'callConv' in decl and len(decl['callConv']) > 0: - toks.append(decl['callConv']) - if 'ptrs' in decl and len(decl['ptrs']) > 0: - toks.append('*' * len(decl['ptrs'])) - if 'arrays' in decl and len(decl['arrays']) > 0: - #arrays = [] - #for x in decl['arrays']: - #n = self.evalExpr(x) - #if n == -1: ## If an array was given as '[]', interpret it as '*' instead. - #toks.append('*') - #else: - #arrays.append(n) - #if len(arrays) > 0: - #toks.append(arrays) - toks.append([self.evalExpr(x) for x in decl['arrays']]) - if 'args' in decl and len(decl['args']) > 0: - #print " process args" - if decl['args'][0] is None: - toks.append(()) - else: - toks.append(tuple([self.processType(a['type'], a['decl']) + (a['val'][0],) for a in decl['args']])) - if 'ref' in decl: - toks.append('&') - if 'center' in decl: - (n, t) = self.processDeclarator(decl['center'][0]) - if n is not None: - name = n - toks.extend(t) - if 'name' in decl: - name = decl['name'] - return (name, toks) - - def processType(self, typ, decl): - """Take a declarator + base type and return a serialized name/type description. - The description will be a list of elements (name, [basetype, modifier, modifier, ...]) - - name is the string name of the declarator or None for an abstract declarator - - basetype is the string representing the base type - - modifiers can be: - '*' - pointer (multiple pointers "***" allowed) - '&' - reference - '__X' - calling convention (windows only). X can be 'cdecl' or 'stdcall' - list - array. Value(s) indicate the length of each array, -1 for incomplete type. - tuple - function, items are the output of processType for each function argument. - - Examples: - int *x[10] => ('x', ['int', [10], '*']) - char fn(int x) => ('fn', ['char', [('x', ['int'])]]) - struct s (*)(int, int*) => (None, ["struct s", ((None, ['int']), (None, ['int', '*'])), '*']) - """ - #print "PROCESS TYPE/DECL:", typ, decl - (name, decl) = self.processDeclarator(decl) - return (name, [typ] + decl) - - - - def processEnum(self, s, l, t): - try: - if self.verbose: - print "ENUM:", t - if t.name == '': - n = 0 - while True: - name = 'anonEnum%d' % n - if name not in self.defs['enums']: - break - n += 1 - else: - name = t.name[0] - - if self.verbose: - print " name:", name - - if name not in self.defs['enums']: - i = 0 - enum = {} - for v in t.members: - if v.value != '': - i = eval(v.value) - if v.valueName != '': - i = enum[v.valueName] - enum[v.name] = i - self.addDef('values', v.name, i) - i += 1 - if self.verbose: - print " members:", enum - self.addDef('enums', name, enum) - self.addDef('types', 'enum '+name, ('enum', name)) - return ('enum ' + name) - except: - if self.verbose: - print "Error processing enum:", t - sys.excepthook(*sys.exc_info()) - - - def processFunction(self, s, l, t): - if self.verbose: - print "FUNCTION", t, t.keys() - - try: - (name, decl) = self.processType(t.type, t.decl[0]) - if len(decl) == 0 or type(decl[-1]) != tuple: - print t - raise Exception("Incorrect declarator type for function definition.") - if self.verbose: - print " name:", name - print " sig:", decl - self.addDef('functions', name, (decl[:-1], decl[-1])) - - except: - if self.verbose: - print "Error processing function:", t - sys.excepthook(*sys.exc_info()) - - - def packingAt(self, line): - """Return the structure packing value at the given line number""" - packing = None - for p in self.packList[self.currentFile]: - if p[0] <= line: - packing = p[1] - else: - break - return packing - - def processStruct(self, s, l, t): - try: - strTyp = t.structType # struct or union - - ## check for extra packing rules - packing = self.packingAt(lineno(l, s)) - - if self.verbose: - print strTyp.upper(), t.name, t - if t.name == '': - n = 0 - while True: - sname = 'anon_%s%d' % (strTyp, n) - if sname not in self.defs[strTyp+'s']: - break - n += 1 - else: - if type(t.name) is str: - sname = t.name - else: - sname = t.name[0] - if self.verbose: - print " NAME:", sname - if len(t.members) > 0 or sname not in self.defs[strTyp+'s'] or self.defs[strTyp+'s'][sname] == {}: - if self.verbose: - print " NEW " + strTyp.upper() - struct = [] - for m in t.members: - typ = m[0].type - val = self.evalExpr(m) - if self.verbose: - print " member:", m, m[0].keys(), m[0].declList - if len(m[0].declList) == 0: ## anonymous member - struct.append((None, [typ], None)) - for d in m[0].declList: - (name, decl) = self.processType(typ, d) - struct.append((name, decl, val)) - if self.verbose: - print " ", name, decl, val - self.addDef(strTyp+'s', sname, {'pack': packing, 'members': struct}) - self.addDef('types', strTyp+' '+sname, (strTyp, sname)) - #self.updateStructDefn() - return strTyp+' '+sname - except: - #print t - sys.excepthook(*sys.exc_info()) - - def processVariable(self, s, l, t): - if self.verbose: - print "VARIABLE:", t - try: - val = self.evalExpr(t[0]) - for d in t[0].declList: - (name, typ) = self.processType(t[0].type, d) - if type(typ[-1]) is tuple: ## this is a function prototype - if self.verbose: - print " Add function prototype:", name, typ, val - self.addDef('functions', name, (typ[:-1], typ[-1])) - else: - if self.verbose: - print " Add variable:", name, typ, val - self.addDef('variables', name, (val, typ)) - self.addDef('values', name, val) - except: - #print t, t[0].name, t.value - sys.excepthook(*sys.exc_info()) - - def processTypedef(self, s, l, t): - if self.verbose: - print "TYPE:", t - typ = t.type - #print t, t.type - for d in t.declList: - (name, decl) = self.processType(typ, d) - if self.verbose: - print " ", name, decl - self.addDef('types', name, decl) - #self.definedType << MatchFirst( map(Keyword,self.defs['types'].keys()) ) - - def evalExpr(self, toks): - ## Evaluates expressions. Currently only works for expressions that also - ## happen to be valid python expressions. - ## This function does not currently include previous variable - ## declarations, but that should not be too difficult to implement.. - #print "Eval:", toks - try: - if isinstance(toks, basestring): - #print " as string" - val = self.eval(toks, None, self.defs['values']) - elif toks.arrayValues != '': - #print " as list:", toks.arrayValues - val = [self.eval(x, None, self.defs['values']) for x in toks.arrayValues] - elif toks.value != '': - #print " as value" - val = self.eval(toks.value, None, self.defs['values']) - else: - #print " as None" - val = None - return val - except: - if self.verbose: - print " failed eval:", toks - print " ", sys.exc_info()[1] - return None - - def eval(self, expr, *args): - """Just eval with a little extra robustness.""" - expr = expr.strip() - cast = (lparen + self.typeSpec + self.abstractDeclarator + rparen).suppress() - expr = (quotedString | number | cast).transformString(expr) - if expr == '': - return None - return eval(expr, *args) - - def printAll(self, file=None): - """Print everything parsed from files. Useful for debugging.""" - from pprint import pprint - for k in self.dataList: - print "============== %s ==================" % k - if file is None: - pprint(self.defs[k]) - else: - pprint(self.fileDefs[file][k]) - - def addDef(self, typ, name, val): - """Add a definition of a specific type to both the definition set for the current file and the global definition set.""" - self.defs[typ][name] = val - if self.currentFile is None: - baseName = None - else: - baseName = os.path.basename(self.currentFile) - if baseName not in self.fileDefs: - self.fileDefs[baseName] = {} - for k in self.dataList: - self.fileDefs[baseName][k] = {} - self.fileDefs[baseName][typ][name] = val - - def remDef(self, typ, name): - if self.currentFile is None: - baseName = None - else: - baseName = os.path.basename(self.currentFile) - del self.defs[typ][name] - del self.fileDefs[baseName][typ][name] - - - def isFundType(self, typ): - """Return True if this type is a fundamental C type, struct, or union""" - if typ[0][:7] == 'struct ' or typ[0][:6] == 'union ' or typ[0][:5] == 'enum ': - return True - - names = baseTypes + sizeModifiers + signModifiers - for w in typ[0].split(): - if w not in names: - return False - return True - - def evalType(self, typ): - """evaluate a named type into its fundamental type""" - used = [] - while True: - if self.isFundType(typ): - ## remove 'signed' before returning evaluated type - typ[0] = re.sub(r'\bsigned\b', '', typ[0]).strip() - - - return typ - parent = typ[0] - if parent in used: - raise Exception('Recursive loop while evaluating types. (typedefs are %s)' % (' -> '.join(used+[parent]))) - used.append(parent) - if not parent in self.defs['types']: - raise Exception('Unknown type "%s" (typedefs are %s)' % (parent, ' -> '.join(used))) - pt = self.defs['types'][parent] - typ = pt + typ[1:] - - def find(self, name): - """Search all definitions for the given name""" - res = [] - for f in self.fileDefs: - fd = self.fileDefs[f] - for t in fd: - typ = fd[t] - for k in typ: - if isinstance(name, basestring): - if k == name: - res.append((f, t)) - else: - if re.match(name, k): - res.append((f, t, k)) - return res - - - - def findText(self, text): - """Search all file strings for text, return matching lines.""" - res = [] - for f in self.files: - l = self.files[f].split('\n') - for i in range(len(l)): - if text in l[i]: - res.append((f, i, l[i])) - return res - - -hasPyParsing = False -try: - from pyparsing import * - ParserElement.enablePackrat() - hasPyParsing = True -except: - pass ## no need to do anything yet as we might not be using any parsing functions.. - - -## Define some common language elements if pyparsing is available. -if hasPyParsing: - ## Some basic definitions - expression = Forward() - pexpr = '(' + expression + ')' - numTypes = ['int', 'float', 'double', '__int64'] - baseTypes = ['char', 'bool', 'void'] + numTypes - sizeModifiers = ['short', 'long'] - signModifiers = ['signed', 'unsigned'] - qualifiers = ['const', 'static', 'volatile', 'inline', 'restrict', 'near', 'far'] - msModifiers = ['__based', '__declspec', '__fastcall', '__restrict', '__sptr', '__uptr', '__w64', '__unaligned', '__nullterminated'] - keywords = ['struct', 'enum', 'union', '__stdcall', '__cdecl'] + qualifiers + baseTypes + sizeModifiers + signModifiers - - def kwl(strs): - """Generate a match-first list of keywords given a list of strings.""" - #return MatchFirst(map(Keyword,strs)) - return Regex(r'\b(%s)\b' % '|'.join(strs)) - - keyword = kwl(keywords) - wordchars = alphanums+'_$' - ident = (WordStart(wordchars) + ~keyword + Word(alphas+"_",alphanums+"_$") + WordEnd(wordchars)).setParseAction(lambda t: t[0]) - #integer = Combine(Optional("-") + (Word( nums ) | Combine("0x" + Word(hexnums)))) - semi = Literal(";").ignore(quotedString).suppress() - lbrace = Literal("{").ignore(quotedString).suppress() - rbrace = Literal("}").ignore(quotedString).suppress() - lbrack = Literal("[").ignore(quotedString).suppress() - rbrack = Literal("]").ignore(quotedString).suppress() - lparen = Literal("(").ignore(quotedString).suppress() - rparen = Literal(")").ignore(quotedString).suppress() - hexint = Regex('-?0x[%s]+[UL]*'%hexnums).setParseAction(lambda t: t[0].rstrip('UL')) - decint = Regex(r'-?\d+[UL]*').setParseAction(lambda t: t[0].rstrip('UL')) - integer = (hexint | decint) - floating = Regex(r'-?((\d+(\.\d*)?)|(\.\d+))([eE]-?\d+)?') - number = (hexint | floating | decint) - bitfieldspec = ":" + integer - biOperator = oneOf("+ - / * | & || && ! ~ ^ % == != > < >= <= -> . :: << >> = ? :") - uniRightOperator = oneOf("++ --") - uniLeftOperator = oneOf("++ -- - + * sizeof new") - name = (WordStart(wordchars) + Word(alphas+"_",alphanums+"_$") + WordEnd(wordchars)) - #number = Word(hexnums + ".-+xUL").setParseAction(lambda t: t[0].rstrip('UL')) - #stars = Optional(Word('*&'), default='')('ptrs') ## may need to separate & from * later? - callConv = Optional(Keyword('__cdecl')|Keyword('__stdcall'))('callConv') - - ## Removes '__name' from all type specs.. may cause trouble. - underscore2Ident = (WordStart(wordchars) + ~keyword + '__' + Word(alphanums,alphanums+"_$") + WordEnd(wordchars)).setParseAction(lambda t: t[0]) - typeQualifier = ZeroOrMore((underscore2Ident + Optional(nestedExpr())) | kwl(qualifiers)).suppress() - - msModifier = ZeroOrMore(kwl(msModifiers) + Optional(nestedExpr())).suppress() - pointerOperator = ( - '*' + typeQualifier | - '&' + typeQualifier | - '::' + ident + typeQualifier - ) - - - ## language elements - fundType = OneOrMore(kwl(signModifiers + sizeModifiers + baseTypes)).setParseAction(lambda t: ' '.join(t)) - - - - ## Is there a better way to process expressions with cast operators?? - castAtom = ( - ZeroOrMore(uniLeftOperator) + Optional('('+ident+')').suppress() + - (( - ident + '(' + Optional(delimitedList(expression)) + ')' | - ident + OneOrMore('[' + expression + ']') | - ident | number | quotedString - ) | - ('(' + expression + ')')) + - ZeroOrMore(uniRightOperator) - ) - uncastAtom = ( - ZeroOrMore(uniLeftOperator) + - (( - ident + '(' + Optional(delimitedList(expression)) + ')' | - ident + OneOrMore('[' + expression + ']') | - ident | number | quotedString - ) | - ('(' + expression + ')')) + - ZeroOrMore(uniRightOperator) - ) - atom = castAtom | uncastAtom - - expression << Group( - atom + ZeroOrMore(biOperator + atom) - ) - arrayOp = lbrack + expression + rbrack - - def recombine(tok): - """Flattens a tree of tokens and joins into one big string.""" - return " ".join(flatten(tok.asList())) - expression.setParseAction(recombine) - - def flatten(lst): - res = [] - for i in lst: - if type(i) in [list, tuple]: - res.extend(flatten(i)) - else: - res.append(str(i)) - return res - - def printParseResults(pr, depth=0, name=''): - """For debugging; pretty-prints parse result objects.""" - start = name + " "*(20-len(name)) + ':'+ '..'*depth - if isinstance(pr, ParseResults): - print start - for i in pr: - name = '' - for k in pr.keys(): - if pr[k] is i: - name = k - break - printParseResults(i, depth+1, name) - else: - print start + str(pr) - - - -## Just for fun.. -if __name__ == '__main__': - files = sys.argv[1:] - p = CParser(files) - p.processAll() - p.printAll() - \ No newline at end of file diff --git a/scripts/pyclibrary/README.md b/scripts/pyclibrary/README.md deleted file mode 100644 index f1af9afb7..000000000 --- a/scripts/pyclibrary/README.md +++ /dev/null @@ -1,8 +0,0 @@ -pyclibrary -========== - -C parser and ctypes automation for Python. - -Fork of . (`bzr branch lp:pyclibrary pyclibrary-bzr && mkdir pyclibrary && cd pyclibrary && bar fast-export --plain ../pyclibrary-bzr | git fast-import`) - -Pyclibrary includes 1) a pure-python C parser and 2) a ctypes automation library that uses C header file definitions to simplify the use of ctypes. The C parser currently processes all macros, typedefs, structs, unions, enums, function prototypes, and global variable declarations, and can evaluate typedefs down to their fundamental C types + pointers/arrays/function signatures. Pyclibrary can automatically build ctypes structs/unions and perform type conversions when calling functions via cdll/windll. diff --git a/scripts/pyclibrary/__init__.py b/scripts/pyclibrary/__init__.py deleted file mode 100644 index 618aaa1a8..000000000 --- a/scripts/pyclibrary/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -from CParser import * -from CLibrary import * \ No newline at end of file diff --git a/scripts/pyclibrary/license.txt b/scripts/pyclibrary/license.txt deleted file mode 100644 index 3d04b87ea..000000000 --- a/scripts/pyclibrary/license.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2010 Luke Campagnola ('luke.campagnola@%s.com' % 'gmail') - -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. \ No newline at end of file diff --git a/scripts/pyclibrary/pyparsing.py b/scripts/pyclibrary/pyparsing.py deleted file mode 100644 index dec506ed0..000000000 --- a/scripts/pyclibrary/pyparsing.py +++ /dev/null @@ -1,3754 +0,0 @@ -# module pyparsing.py -# -# Copyright (c) 2003-2011 Paul T. McGuire -# -# 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. -# -#from __future__ import generators - -__doc__ = \ -""" -pyparsing module - Classes and methods to define and execute parsing grammars - -The pyparsing module is an alternative approach to creating and executing simple grammars, -vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you -don't need to learn a new syntax for defining grammars or matching expressions - the parsing module -provides a library of classes that you use to construct the grammar directly in Python. - -Here is a program to parse "Hello, World!" (or any greeting of the form C{", !"}):: - - from pyparsing import Word, alphas - - # define grammar of a greeting - greet = Word( alphas ) + "," + Word( alphas ) + "!" - - hello = "Hello, World!" - print hello, "->", greet.parseString( hello ) - -The program outputs the following:: - - Hello, World! -> ['Hello', ',', 'World', '!'] - -The Python representation of the grammar is quite readable, owing to the self-explanatory -class names, and the use of '+', '|' and '^' operators. - -The parsed results returned from C{parseString()} can be accessed as a nested list, a dictionary, or an -object with named attributes. - -The pyparsing module handles some of the problems that are typically vexing when writing text parsers: - - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) - - quoted strings - - embedded comments -""" - -__version__ = "1.5.6" -__versionTime__ = "1 May 2011 23:41" -__author__ = "Paul McGuire " - -import string -from weakref import ref as wkref -import copy -import sys -import warnings -import re -import sre_constants -#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) - -__all__ = [ -'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', -'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', -'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', -'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', -'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', -'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', -'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', -'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', -'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', -'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', -'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', -'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', -'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', -'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', -'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', -'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', -'indentedBlock', 'originalTextFor', -] - -""" -Detect if we are running version 3.X and make appropriate changes -Robert A. Clark -""" -_PY3K = sys.version_info[0] > 2 -if _PY3K: - _MAX_INT = sys.maxsize - basestring = str - unichr = chr - _ustr = str - alphas = string.ascii_lowercase + string.ascii_uppercase -else: - _MAX_INT = sys.maxint - range = xrange - set = lambda s : dict( [(c,0) for c in s] ) - alphas = string.lowercase + string.uppercase - - def _ustr(obj): - """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries - str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It - then < returns the unicode object | encodes it with the default encoding | ... >. - """ - if isinstance(obj,unicode): - return obj - - try: - # If this works, then _ustr(obj) has the same behaviour as str(obj), so - # it won't break any existing code. - return str(obj) - - except UnicodeEncodeError: - # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) - # state that "The return value must be a string object". However, does a - # unicode object (being a subclass of basestring) count as a "string - # object"? - # If so, then return a unicode object: - return unicode(obj) - # Else encode it... but how? There are many choices... :) - # Replace unprintables with escape codes? - #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') - # Replace unprintables with question marks? - #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') - # ... - - alphas = string.lowercase + string.uppercase - -# build list of single arg builtins, tolerant of Python version, that can be used as parse actions -singleArgBuiltins = [] -import __builtin__ -for fname in "sum len enumerate sorted reversed list tuple set any all".split(): - try: - singleArgBuiltins.append(getattr(__builtin__,fname)) - except AttributeError: - continue - -def _xml_escape(data): - """Escape &, <, >, ", ', etc. in a string of data.""" - - # ampersand must be replaced first - from_symbols = '&><"\'' - to_symbols = ['&'+s+';' for s in "amp gt lt quot apos".split()] - for from_,to_ in zip(from_symbols, to_symbols): - data = data.replace(from_, to_) - return data - -class _Constants(object): - pass - -nums = string.digits -hexnums = nums + "ABCDEFabcdef" -alphanums = alphas + nums -_bslash = chr(92) -printables = "".join( [ c for c in string.printable if c not in string.whitespace ] ) - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( self, pstr, loc=0, msg=None, elem=None ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parserElement = elem - - def __getattr__( self, aname ): - """supported attributes by name are: - - lineno - returns the line number of the exception text - - col - returns the column number of the exception text - - line - returns the line containing the exception text - """ - if( aname == "lineno" ): - return lineno( self.loc, self.pstr ) - elif( aname in ("col", "column") ): - return col( self.loc, self.pstr ) - elif( aname == "line" ): - return line( self.loc, self.pstr ) - else: - raise AttributeError(aname) - - def __str__( self ): - return "%s (at char %d), (line:%d, col:%d)" % \ - ( self.msg, self.loc, self.lineno, self.column ) - def __repr__( self ): - return _ustr(self) - def markInputline( self, markerString = ">!<" ): - """Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( [line_str[:line_column], - markerString, line_str[line_column:]]) - return line_str.strip() - def __dir__(self): - return "loc msg pstr parserElement lineno col line " \ - "markInputLine __str__ __repr__".split() - -class ParseException(ParseBaseException): - """exception thrown when parse expressions don't match class; - supported attributes by name are: - - lineno - returns the line number of the exception text - - col - returns the column number of the exception text - - line - returns the line containing the exception text - """ - pass - -class ParseFatalException(ParseBaseException): - """user-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately""" - pass - -class ParseSyntaxException(ParseFatalException): - """just like C{ParseFatalException}, but thrown internally when an - C{ErrorStop} ('-' operator) indicates that parsing is to stop immediately because - an unbacktrackable syntax error has been found""" - def __init__(self, pe): - super(ParseSyntaxException, self).__init__( - pe.pstr, pe.loc, pe.msg, pe.parserElement) - -#~ class ReparseException(ParseBaseException): - #~ """Experimental class - parse actions can raise this exception to cause - #~ pyparsing to reparse the input string: - #~ - with a modified input string, and/or - #~ - with a modified start location - #~ Set the values of the ReparseException in the constructor, and raise the - #~ exception in a parse action to cause pyparsing to use the new string/location. - #~ Setting the values as None causes no change to be made. - #~ """ - #~ def __init_( self, newstring, restartLoc ): - #~ self.newParseText = newstring - #~ self.reparseLoc = restartLoc - -class RecursiveGrammarException(Exception): - """exception thrown by C{validate()} if the grammar could be improperly recursive""" - def __init__( self, parseElementList ): - self.parseElementTrace = parseElementList - - def __str__( self ): - return "RecursiveGrammarException: %s" % self.parseElementTrace - -class _ParseResultsWithOffset(object): - def __init__(self,p1,p2): - self.tup = (p1,p2) - def __getitem__(self,i): - return self.tup[i] - def __repr__(self): - return repr(self.tup) - def setOffset(self,i): - self.tup = (self.tup[0],i) - -class ParseResults(object): - """Structured parse results, to provide multiple means of access to the parsed data: - - as a list (C{len(results)}) - - by list index (C{results[0], results[1]}, etc.) - - by attribute (C{results.}) - """ - #~ __slots__ = ( "__toklist", "__tokdict", "__doinit", "__name", "__parent", "__accumNames", "__weakref__" ) - def __new__(cls, toklist, name=None, asList=True, modal=True ): - if isinstance(toklist, cls): - return toklist - retobj = object.__new__(cls) - retobj.__doinit = True - return retobj - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( self, toklist, name=None, asList=True, modal=True, isinstance=isinstance ): - if self.__doinit: - self.__doinit = False - self.__name = None - self.__parent = None - self.__accumNames = {} - if isinstance(toklist, list): - self.__toklist = toklist[:] - else: - self.__toklist = [toklist] - self.__tokdict = dict() - - if name is not None and name: - if not modal: - self.__accumNames[name] = 0 - if isinstance(name,int): - name = _ustr(name) # will always return a str, but use _ustr for consistency - self.__name = name - if not toklist in (None,'',[]): - if isinstance(toklist,basestring): - toklist = [ toklist ] - if asList: - if isinstance(toklist,ParseResults): - self[name] = _ParseResultsWithOffset(toklist.copy(),0) - else: - self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) - self[name].__name = name - else: - try: - self[name] = toklist[0] - except (KeyError,TypeError,IndexError): - self[name] = toklist - - def __getitem__( self, i ): - if isinstance( i, (int,slice) ): - return self.__toklist[i] - else: - if i not in self.__accumNames: - return self.__tokdict[i][-1][0] - else: - return ParseResults([ v[0] for v in self.__tokdict[i] ]) - - def __setitem__( self, k, v, isinstance=isinstance ): - if isinstance(v,_ParseResultsWithOffset): - self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] - sub = v[0] - elif isinstance(k,int): - self.__toklist[k] = v - sub = v - else: - self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] - sub = v - if isinstance(sub,ParseResults): - sub.__parent = wkref(self) - - def __delitem__( self, i ): - if isinstance(i,(int,slice)): - mylen = len( self.__toklist ) - del self.__toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i+1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for name in self.__tokdict: - occurrences = self.__tokdict[name] - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) - else: - del self.__tokdict[i] - - def __contains__( self, k ): - return k in self.__tokdict - - def __len__( self ): return len( self.__toklist ) - def __bool__(self): return len( self.__toklist ) > 0 - __nonzero__ = __bool__ - def __iter__( self ): return iter( self.__toklist ) - def __reversed__( self ): return iter( self.__toklist[::-1] ) - def keys( self ): - """Returns all named result keys.""" - return self.__tokdict.keys() - - def pop( self, index=-1 ): - """Removes and returns item at specified index (default=last). - Will work with either numeric indices or dict-key indicies.""" - ret = self[index] - del self[index] - return ret - - def get(self, key, defaultValue=None): - """Returns named result matching the given key, or if there is no - such name, then returns the given C{defaultValue} or C{None} if no - C{defaultValue} is specified.""" - if key in self: - return self[key] - else: - return defaultValue - - def insert( self, index, insStr ): - """Inserts new element at location index in the list of parsed tokens.""" - self.__toklist.insert(index, insStr) - # fixup indices in token dictionary - for name in self.__tokdict: - occurrences = self.__tokdict[name] - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) - - def items( self ): - """Returns all named result keys and values as a list of tuples.""" - return [(k,self[k]) for k in self.__tokdict] - - def values( self ): - """Returns all named result values.""" - return [ v[-1][0] for v in self.__tokdict.values() ] - - def __getattr__( self, name ): - if True: #name not in self.__slots__: - if name in self.__tokdict: - if name not in self.__accumNames: - return self.__tokdict[name][-1][0] - else: - return ParseResults([ v[0] for v in self.__tokdict[name] ]) - else: - return "" - return None - - def __add__( self, other ): - ret = self.copy() - ret += other - return ret - - def __iadd__( self, other ): - if other.__tokdict: - offset = len(self.__toklist) - addoffset = ( lambda a: (a<0 and offset) or (a+offset) ) - otheritems = other.__tokdict.items() - otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) - for (k,vlist) in otheritems for v in vlist] - for k,v in otherdictitems: - self[k] = v - if isinstance(v[0],ParseResults): - v[0].__parent = wkref(self) - - self.__toklist += other.__toklist - self.__accumNames.update( other.__accumNames ) - return self - - def __radd__(self, other): - if isinstance(other,int) and other == 0: - return self.copy() - - def __repr__( self ): - return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) - - def __str__( self ): - out = "[" - sep = "" - for i in self.__toklist: - if isinstance(i, ParseResults): - out += sep + _ustr(i) - else: - out += sep + repr(i) - sep = ", " - out += "]" - return out - - def _asStringList( self, sep='' ): - out = [] - for item in self.__toklist: - if out and sep: - out.append(sep) - if isinstance( item, ParseResults ): - out += item._asStringList() - else: - out.append( _ustr(item) ) - return out - - def asList( self ): - """Returns the parse results as a nested list of matching tokens, all converted to strings.""" - out = [] - for res in self.__toklist: - if isinstance(res,ParseResults): - out.append( res.asList() ) - else: - out.append( res ) - return out - - def asDict( self ): - """Returns the named parse results as dictionary.""" - return dict( self.items() ) - - def copy( self ): - """Returns a new copy of a C{ParseResults} object.""" - ret = ParseResults( self.__toklist ) - ret.__tokdict = self.__tokdict.copy() - ret.__parent = self.__parent - ret.__accumNames.update( self.__accumNames ) - ret.__name = self.__name - return ret - - def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): - """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" - nl = "\n" - out = [] - namedItems = dict( [ (v[1],k) for (k,vlist) in self.__tokdict.items() - for v in vlist ] ) - nextLevelIndent = indent + " " - - # collapse out indents if formatting is not desired - if not formatted: - indent = "" - nextLevelIndent = "" - nl = "" - - selfTag = None - if doctag is not None: - selfTag = doctag - else: - if self.__name: - selfTag = self.__name - - if not selfTag: - if namedItemsOnly: - return "" - else: - selfTag = "ITEM" - - out += [ nl, indent, "<", selfTag, ">" ] - - worklist = self.__toklist - for i,res in enumerate(worklist): - if isinstance(res,ParseResults): - if i in namedItems: - out += [ res.asXML(namedItems[i], - namedItemsOnly and doctag is None, - nextLevelIndent, - formatted)] - else: - out += [ res.asXML(None, - namedItemsOnly and doctag is None, - nextLevelIndent, - formatted)] - else: - # individual token, see if there is a name for it - resTag = None - if i in namedItems: - resTag = namedItems[i] - if not resTag: - if namedItemsOnly: - continue - else: - resTag = "ITEM" - xmlBodyText = _xml_escape(_ustr(res)) - out += [ nl, nextLevelIndent, "<", resTag, ">", - xmlBodyText, - "" ] - - out += [ nl, indent, "" ] - return "".join(out) - - def __lookup(self,sub): - for k,vlist in self.__tokdict.items(): - for v,loc in vlist: - if sub is v: - return k - return None - - def getName(self): - """Returns the results name for this token expression.""" - if self.__name: - return self.__name - elif self.__parent: - par = self.__parent() - if par: - return par.__lookup(self) - else: - return None - elif (len(self) == 1 and - len(self.__tokdict) == 1 and - self.__tokdict.values()[0][0][1] in (0,-1)): - return self.__tokdict.keys()[0] - else: - return None - - def dump(self,indent='',depth=0): - """Diagnostic method for listing out the contents of a C{ParseResults}. - Accepts an optional C{indent} argument so that this string can be embedded - in a nested display of other data.""" - out = [] - out.append( indent+_ustr(self.asList()) ) - keys = self.items() - keys.sort() - for k,v in keys: - if out: - out.append('\n') - out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) - if isinstance(v,ParseResults): - if v.keys(): - out.append( v.dump(indent,depth+1) ) - else: - out.append(_ustr(v)) - else: - out.append(_ustr(v)) - return "".join(out) - - # add support for pickle protocol - def __getstate__(self): - return ( self.__toklist, - ( self.__tokdict.copy(), - self.__parent is not None and self.__parent() or None, - self.__accumNames, - self.__name ) ) - - def __setstate__(self,state): - self.__toklist = state[0] - self.__tokdict, \ - par, \ - inAccumNames, \ - self.__name = state[1] - self.__accumNames = {} - self.__accumNames.update(inAccumNames) - if par is not None: - self.__parent = wkref(par) - else: - self.__parent = None - - def __dir__(self): - return dir(super(ParseResults,self)) + self.keys() - -def col (loc,strg): - """Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See L{I{ParserElement.parseString}} for more information - on parsing strings containing s, and suggested methods to maintain a - consistent view of the parsed string, the parse location, and line and column - positions within the parsed string. - """ - return (loc} for more information - on parsing strings containing s, and suggested methods to maintain a - consistent view of the parsed string, the parse location, and line and column - positions within the parsed string. - """ - return strg.count("\n",0,loc) + 1 - -def line( loc, strg ): - """Returns the line of text containing loc within a string, counting newlines as line separators. - """ - lastCR = strg.rfind("\n", 0, loc) - nextCR = strg.find("\n", loc) - if nextCR >= 0: - return strg[lastCR+1:nextCR] - else: - return strg[lastCR+1:] - -def _defaultStartDebugAction( instring, loc, expr ): - print ("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - -def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): - print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) - -def _defaultExceptionDebugAction( instring, loc, expr, exc ): - print ("Exception raised:" + _ustr(exc)) - -def nullDebugAction(*args): - """'Do-nothing' debug action, to suppress debugging output during parsing.""" - pass - -'decorator to trim function calls to match the arity of the target' -if not _PY3K: - def _trim_arity(func, maxargs=2): - limit = [0] - def wrapper(*args): - while 1: - try: - return func(*args[limit[0]:]) - except TypeError: - if limit[0] <= maxargs: - limit[0] += 1 - continue - raise - return wrapper -else: - def _trim_arity(func, maxargs=2): - limit = maxargs - def wrapper(*args): - #~ nonlocal limit - while 1: - try: - return func(*args[limit:]) - except TypeError: - if limit: - limit -= 1 - continue - raise - return wrapper - -class ParserElement(object): - """Abstract base level parser element class.""" - DEFAULT_WHITE_CHARS = " \n\t\r" - verbose_stacktrace = False - - def setDefaultWhitespaceChars( chars ): - """Overrides the default whitespace chars - """ - ParserElement.DEFAULT_WHITE_CHARS = chars - setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) - - def __init__( self, savelist=False ): - self.parseAction = list() - self.failAction = None - #~ self.name = "" # don't define self.name, let subclasses try/except upcall - self.strRepr = None - self.resultsName = None - self.saveAsList = savelist - self.skipWhitespace = True - self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - self.copyDefaultWhiteChars = True - self.mayReturnEmpty = False # used when checking for left-recursion - self.keepTabs = False - self.ignoreExprs = list() - self.debug = False - self.streamlined = False - self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index - self.errmsg = "" - self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) - self.debugActions = ( None, None, None ) #custom debug actions - self.re = None - self.callPreparse = True # used to avoid redundant calls to preParse - self.callDuringTry = False - - def copy( self ): - """Make a copy of this C{ParserElement}. Useful for defining different parse actions - for the same parsing pattern, using copies of the original parse element.""" - cpy = copy.copy( self ) - cpy.parseAction = self.parseAction[:] - cpy.ignoreExprs = self.ignoreExprs[:] - if self.copyDefaultWhiteChars: - cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS - return cpy - - def setName( self, name ): - """Define name for this expression, for use in debugging.""" - self.name = name - self.errmsg = "Expected " + self.name - if hasattr(self,"exception"): - self.exception.msg = self.errmsg - return self - - def setResultsName( self, name, listAllMatches=False ): - """Define name for referencing matching tokens as a nested attribute - of the returned parse results. - NOTE: this returns a *copy* of the original C{ParserElement} object; - this is so that the client can define a basic element, such as an - integer, and reference it in multiple places with different names. - - You can also set results names using the abbreviated syntax, - C{expr("name")} in place of C{expr.setResultsName("name")} - - see L{I{__call__}<__call__>}. - """ - newself = self.copy() - newself.resultsName = name - newself.modalResults = not listAllMatches - return newself - - def setBreak(self,breakFlag = True): - """Method to invoke the Python pdb debugger when this element is - about to be parsed. Set C{breakFlag} to True to enable, False to - disable. - """ - if breakFlag: - _parseMethod = self._parse - def breaker(instring, loc, doActions=True, callPreParse=True): - import pdb - pdb.set_trace() - return _parseMethod( instring, loc, doActions, callPreParse ) - breaker._originalParseMethod = _parseMethod - self._parse = breaker - else: - if hasattr(self._parse,"_originalParseMethod"): - self._parse = self._parse._originalParseMethod - return self - - def setParseAction( self, *fns, **kwargs ): - """Define action to perform when successfully matching parse element definition. - Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, - C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: - - s = the original string being parsed (see note below) - - loc = the location of the matching substring - - toks = a list of the matched tokens, packaged as a ParseResults object - If the functions in fns modify the tokens, they can return them as the return - value from fn, and the modified list of tokens will replace the original. - Otherwise, fn does not need to return any value. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See L{I{parseString}} for more information - on parsing strings containing s, and suggested methods to maintain a - consistent view of the parsed string, the parse location, and line and column - positions within the parsed string. - """ - self.parseAction = list(map(_trim_arity, list(fns))) - self.callDuringTry = ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - return self - - def addParseAction( self, *fns, **kwargs ): - """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" - self.parseAction += list(map(_trim_arity, list(fns))) - self.callDuringTry = self.callDuringTry or ("callDuringTry" in kwargs and kwargs["callDuringTry"]) - return self - - def setFailAction( self, fn ): - """Define action to perform if parsing fails at this expression. - Fail acton fn is a callable function that takes the arguments - C{fn(s,loc,expr,err)} where: - - s = string being parsed - - loc = location where expression match was attempted and failed - - expr = the parse expression that failed - - err = the exception thrown - The function returns no value. It may throw C{ParseFatalException} - if it is desired to stop parsing immediately.""" - self.failAction = fn - return self - - def _skipIgnorables( self, instring, loc ): - exprsFound = True - while exprsFound: - exprsFound = False - for e in self.ignoreExprs: - try: - while 1: - loc,dummy = e._parse( instring, loc ) - exprsFound = True - except ParseException: - pass - return loc - - def preParse( self, instring, loc ): - if self.ignoreExprs: - loc = self._skipIgnorables( instring, loc ) - - if self.skipWhitespace: - wt = self.whiteChars - instrlen = len(instring) - while loc < instrlen and instring[loc] in wt: - loc += 1 - - return loc - - def parseImpl( self, instring, loc, doActions=True ): - return loc, [] - - def postParse( self, instring, loc, tokenlist ): - return tokenlist - - #~ @profile - def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): - debugging = ( self.debug ) #and doActions ) - - if debugging or self.failAction: - #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) - if (self.debugActions[0] ): - self.debugActions[0]( instring, loc, self ) - if callPreParse and self.callPreparse: - preloc = self.preParse( instring, loc ) - else: - preloc = loc - tokensStart = preloc - try: - try: - loc,tokens = self.parseImpl( instring, preloc, doActions ) - except IndexError: - raise ParseException( instring, len(instring), self.errmsg, self ) - except ParseBaseException: - #~ print ("Exception raised:", err) - err = None - if self.debugActions[2]: - err = sys.exc_info()[1] - self.debugActions[2]( instring, tokensStart, self, err ) - if self.failAction: - if err is None: - err = sys.exc_info()[1] - self.failAction( instring, tokensStart, self, err ) - raise - else: - if callPreParse and self.callPreparse: - preloc = self.preParse( instring, loc ) - else: - preloc = loc - tokensStart = preloc - if self.mayIndexError or loc >= len(instring): - try: - loc,tokens = self.parseImpl( instring, preloc, doActions ) - except IndexError: - raise ParseException( instring, len(instring), self.errmsg, self ) - else: - loc,tokens = self.parseImpl( instring, preloc, doActions ) - - tokens = self.postParse( instring, loc, tokens ) - - retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) - if self.parseAction and (doActions or self.callDuringTry): - if debugging: - try: - for fn in self.parseAction: - tokens = fn( instring, tokensStart, retTokens ) - if tokens is not None: - retTokens = ParseResults( tokens, - self.resultsName, - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - modal=self.modalResults ) - except ParseBaseException: - #~ print "Exception raised in user parse action:", err - if (self.debugActions[2] ): - err = sys.exc_info()[1] - self.debugActions[2]( instring, tokensStart, self, err ) - raise - else: - for fn in self.parseAction: - tokens = fn( instring, tokensStart, retTokens ) - if tokens is not None: - retTokens = ParseResults( tokens, - self.resultsName, - asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), - modal=self.modalResults ) - - if debugging: - #~ print ("Matched",self,"->",retTokens.asList()) - if (self.debugActions[1] ): - self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) - - return loc, retTokens - - def tryParse( self, instring, loc ): - try: - return self._parse( instring, loc, doActions=False )[0] - except ParseFatalException: - raise ParseException( instring, loc, self.errmsg, self) - - # this method gets repeatedly called during backtracking with the same arguments - - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression - def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): - lookup = (self,instring,loc,callPreParse,doActions) - if lookup in ParserElement._exprArgCache: - value = ParserElement._exprArgCache[ lookup ] - if isinstance(value, Exception): - raise value - return (value[0],value[1].copy()) - else: - try: - value = self._parseNoCache( instring, loc, doActions, callPreParse ) - ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) - return value - except ParseBaseException: - pe = sys.exc_info()[1] - ParserElement._exprArgCache[ lookup ] = pe - raise - - _parse = _parseNoCache - - # argument cache for optimizing repeated calls when backtracking through recursive expressions - _exprArgCache = {} - def resetCache(): - ParserElement._exprArgCache.clear() - resetCache = staticmethod(resetCache) - - _packratEnabled = False - def enablePackrat(): - """Enables "packrat" parsing, which adds memoizing to the parsing logic. - Repeated parse attempts at the same string location (which happens - often in many complex grammars) can immediately return a cached value, - instead of re-executing parsing/validating code. Memoizing is done of - both valid results and parsing exceptions. - - This speedup may break existing programs that use parse actions that - have side-effects. For this reason, packrat parsing is disabled when - you first import pyparsing. To activate the packrat feature, your - program must call the class method C{ParserElement.enablePackrat()}. If - your program uses C{psyco} to "compile as you go", you must call - C{enablePackrat} before calling C{psyco.full()}. If you do not do this, - Python will crash. For best results, call C{enablePackrat()} immediately - after importing pyparsing. - """ - if not ParserElement._packratEnabled: - ParserElement._packratEnabled = True - ParserElement._parse = ParserElement._parseCache - enablePackrat = staticmethod(enablePackrat) - - def parseString( self, instring, parseAll=False ): - """Execute the parse expression with the given string. - This is the main interface to the client code, once the complete - expression has been built. - - If you want the grammar to require that the entire input string be - successfully parsed, then set C{parseAll} to True (equivalent to ending - the grammar with C{StringEnd()}). - - Note: C{parseString} implicitly calls C{expandtabs()} on the input string, - in order to report proper column numbers in parse actions. - If the input string contains tabs and - the grammar uses parse actions that use the C{loc} argument to index into the - string being parsed, you can ensure you have a consistent view of the input - string by: - - calling C{parseWithTabs} on your grammar before calling C{parseString} - (see L{I{parseWithTabs}}) - - define your parse action using the full C{(s,loc,toks)} signature, and - reference the input string using the parse action's C{s} argument - - explictly expand the tabs in your input string before calling - C{parseString} - """ - ParserElement.resetCache() - if not self.streamlined: - self.streamline() - #~ self.saveAsList = True - for e in self.ignoreExprs: - e.streamline() - if not self.keepTabs: - instring = instring.expandtabs() - try: - loc, tokens = self._parse( instring, 0 ) - if parseAll: - loc = self.preParse( instring, loc ) - se = Empty() + StringEnd() - se._parse( instring, loc ) - except ParseBaseException: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc - else: - return tokens - - def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): - """Scan the input string for expression matches. Each match will return the - matching tokens, start location, and end location. May be called with optional - C{maxMatches} argument, to clip scanning after 'n' matches are found. If - C{overlap} is specified, then overlapping matches will be reported. - - Note that the start and end locations are reported relative to the string - being parsed. See L{I{parseString}} for more information on parsing - strings with embedded tabs.""" - if not self.streamlined: - self.streamline() - for e in self.ignoreExprs: - e.streamline() - - if not self.keepTabs: - instring = _ustr(instring).expandtabs() - instrlen = len(instring) - loc = 0 - preparseFn = self.preParse - parseFn = self._parse - ParserElement.resetCache() - matches = 0 - try: - while loc <= instrlen and matches < maxMatches: - try: - preloc = preparseFn( instring, loc ) - nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) - except ParseException: - loc = preloc+1 - else: - if nextLoc > loc: - matches += 1 - yield tokens, preloc, nextLoc - if overlap: - nextloc = preparseFn( instring, loc ) - if nextloc > loc: - loc = nextLoc - else: - loc += 1 - else: - loc = nextLoc - else: - loc = preloc+1 - except ParseBaseException: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc - - def transformString( self, instring ): - """Extension to C{scanString}, to modify matching text with modified tokens that may - be returned from a parse action. To use C{transformString}, define a grammar and - attach a parse action to it that modifies the returned token list. - Invoking C{transformString()} on a target string will then scan for matches, - and replace the matched text patterns according to the logic in the parse - action. C{transformString()} returns the resulting transformed string.""" - out = [] - lastE = 0 - # force preservation of s, to minimize unwanted transformation of string, and to - # keep string locs straight between transformString and scanString - self.keepTabs = True - try: - for t,s,e in self.scanString( instring ): - out.append( instring[lastE:s] ) - if t: - if isinstance(t,ParseResults): - out += t.asList() - elif isinstance(t,list): - out += t - else: - out.append(t) - lastE = e - out.append(instring[lastE:]) - out = [o for o in out if o] - return "".join(map(_ustr,_flatten(out))) - except ParseBaseException: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc - - def searchString( self, instring, maxMatches=_MAX_INT ): - """Another extension to C{scanString}, simplifying the access to the tokens found - to match the given parse expression. May be called with optional - C{maxMatches} argument, to clip searching after 'n' matches are found. - """ - try: - return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) - except ParseBaseException: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc - - def __add__(self, other ): - """Implementation of + operator - returns And""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return And( [ self, other ] ) - - def __radd__(self, other ): - """Implementation of + operator when left operand is not a C{ParserElement}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return other + self - - def __sub__(self, other): - """Implementation of - operator, returns C{And} with error stop""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return And( [ self, And._ErrorStop(), other ] ) - - def __rsub__(self, other ): - """Implementation of - operator when left operand is not a C{ParserElement}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return other - self - - def __mul__(self,other): - """Implementation of * operator, allows use of C{expr * 3} in place of - C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer - tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples - may also include C{None} as in: - - C{expr*(n,None)} or C{expr*(n,)} is equivalent - to C{expr*n + ZeroOrMore(expr)} - (read as "at least n instances of C{expr}") - - C{expr*(None,n)} is equivalent to C{expr*(0,n)} - (read as "0 to n instances of C{expr}") - - C{expr*(None,None)} is equivalent to C{ZeroOrMore(expr)} - - C{expr*(1,None)} is equivalent to C{OneOrMore(expr)} - - Note that C{expr*(None,n)} does not raise an exception if - more than n exprs exist in the input stream; that is, - C{expr*(None,n)} does not enforce a maximum number of expr - occurrences. If this behavior is desired, then write - C{expr*(None,n) + ~expr} - - """ - if isinstance(other,int): - minElements, optElements = other,0 - elif isinstance(other,tuple): - other = (other + (None, None))[:2] - if other[0] is None: - other = (0, other[1]) - if isinstance(other[0],int) and other[1] is None: - if other[0] == 0: - return ZeroOrMore(self) - if other[0] == 1: - return OneOrMore(self) - else: - return self*other[0] + ZeroOrMore(self) - elif isinstance(other[0],int) and isinstance(other[1],int): - minElements, optElements = other - optElements -= minElements - else: - raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) - else: - raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) - - if minElements < 0: - raise ValueError("cannot multiply ParserElement by negative value") - if optElements < 0: - raise ValueError("second tuple value must be greater or equal to first tuple value") - if minElements == optElements == 0: - raise ValueError("cannot multiply ParserElement by 0 or (0,0)") - - if (optElements): - def makeOptionalList(n): - if n>1: - return Optional(self + makeOptionalList(n-1)) - else: - return Optional(self) - if minElements: - if minElements == 1: - ret = self + makeOptionalList(optElements) - else: - ret = And([self]*minElements) + makeOptionalList(optElements) - else: - ret = makeOptionalList(optElements) - else: - if minElements == 1: - ret = self - else: - ret = And([self]*minElements) - return ret - - def __rmul__(self, other): - return self.__mul__(other) - - def __or__(self, other ): - """Implementation of | operator - returns C{MatchFirst}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return MatchFirst( [ self, other ] ) - - def __ror__(self, other ): - """Implementation of | operator when left operand is not a C{ParserElement}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return other | self - - def __xor__(self, other ): - """Implementation of ^ operator - returns C{Or}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return Or( [ self, other ] ) - - def __rxor__(self, other ): - """Implementation of ^ operator when left operand is not a C{ParserElement}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return other ^ self - - def __and__(self, other ): - """Implementation of & operator - returns C{Each}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return Each( [ self, other ] ) - - def __rand__(self, other ): - """Implementation of & operator when left operand is not a C{ParserElement}""" - if isinstance( other, basestring ): - other = Literal( other ) - if not isinstance( other, ParserElement ): - warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), - SyntaxWarning, stacklevel=2) - return None - return other & self - - def __invert__( self ): - """Implementation of ~ operator - returns C{NotAny}""" - return NotAny( self ) - - def __call__(self, name): - """Shortcut for C{setResultsName}, with C{listAllMatches=default}:: - userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") - could be written as:: - userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") - - If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be - passed as C{True}. - """ - if not name.endswith("*"): - return self.setResultsName(name) - else: - return self.setResultsName(name[:-1], listAllMatches=True) - - def suppress( self ): - """Suppresses the output of this C{ParserElement}; useful to keep punctuation from - cluttering up returned output. - """ - return Suppress( self ) - - def leaveWhitespace( self ): - """Disables the skipping of whitespace before matching the characters in the - C{ParserElement}'s defined pattern. This is normally only used internally by - the pyparsing module, but may be needed in some whitespace-sensitive grammars. - """ - self.skipWhitespace = False - return self - - def setWhitespaceChars( self, chars ): - """Overrides the default whitespace chars - """ - self.skipWhitespace = True - self.whiteChars = chars - self.copyDefaultWhiteChars = False - return self - - def parseWithTabs( self ): - """Overrides default behavior to expand C{}s to spaces before parsing the input string. - Must be called before C{parseString} when the input grammar contains elements that - match C{} characters.""" - self.keepTabs = True - return self - - def ignore( self, other ): - """Define expression to be ignored (e.g., comments) while doing pattern - matching; may be called repeatedly, to define multiple comment or other - ignorable patterns. - """ - if isinstance( other, Suppress ): - if other not in self.ignoreExprs: - self.ignoreExprs.append( other.copy() ) - else: - self.ignoreExprs.append( Suppress( other.copy() ) ) - return self - - def setDebugActions( self, startAction, successAction, exceptionAction ): - """Enable display of debugging messages while doing pattern matching.""" - self.debugActions = (startAction or _defaultStartDebugAction, - successAction or _defaultSuccessDebugAction, - exceptionAction or _defaultExceptionDebugAction) - self.debug = True - return self - - def setDebug( self, flag=True ): - """Enable display of debugging messages while doing pattern matching. - Set C{flag} to True to enable, False to disable.""" - if flag: - self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) - else: - self.debug = False - return self - - def __str__( self ): - return self.name - - def __repr__( self ): - return _ustr(self) - - def streamline( self ): - self.streamlined = True - self.strRepr = None - return self - - def checkRecursion( self, parseElementList ): - pass - - def validate( self, validateTrace=[] ): - """Check defined expressions for valid structure, check for infinite recursive definitions.""" - self.checkRecursion( [] ) - - def parseFile( self, file_or_filename, parseAll=False ): - """Execute the parse expression on the given file or filename. - If a filename is specified (instead of a file object), - the entire file is opened, read, and closed before parsing. - """ - try: - file_contents = file_or_filename.read() - except AttributeError: - f = open(file_or_filename, "rb") - file_contents = f.read() - f.close() - try: - return self.parseString(file_contents, parseAll) - except ParseBaseException: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc - - def getException(self): - return ParseException("",0,self.errmsg,self) - - def __getattr__(self,aname): - if aname == "myException": - self.myException = ret = self.getException(); - return ret; - else: - raise AttributeError("no such attribute " + aname) - - def __eq__(self,other): - if isinstance(other, ParserElement): - return self is other or self.__dict__ == other.__dict__ - elif isinstance(other, basestring): - try: - self.parseString(_ustr(other), parseAll=True) - return True - except ParseBaseException: - return False - else: - return super(ParserElement,self)==other - - def __ne__(self,other): - return not (self == other) - - def __hash__(self): - return hash(id(self)) - - def __req__(self,other): - return self == other - - def __rne__(self,other): - return not (self == other) - - -class Token(ParserElement): - """Abstract C{ParserElement} subclass, for defining atomic matching patterns.""" - def __init__( self ): - super(Token,self).__init__( savelist=False ) - #self.myException = ParseException("",0,"",self) - - def setName(self, name): - s = super(Token,self).setName(name) - self.errmsg = "Expected " + self.name - #s.myException.msg = self.errmsg - return s - - -class Empty(Token): - """An empty token, will always match.""" - def __init__( self ): - super(Empty,self).__init__() - self.name = "Empty" - self.mayReturnEmpty = True - self.mayIndexError = False - - -class NoMatch(Token): - """A token that will never match.""" - def __init__( self ): - super(NoMatch,self).__init__() - self.name = "NoMatch" - self.mayReturnEmpty = True - self.mayIndexError = False - self.errmsg = "Unmatchable token" - #self.myException.msg = self.errmsg - - def parseImpl( self, instring, loc, doActions=True ): - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - -class Literal(Token): - """Token to exactly match a specified string.""" - def __init__( self, matchString ): - super(Literal,self).__init__() - self.match = matchString - self.matchLen = len(matchString) - try: - self.firstMatchChar = matchString[0] - except IndexError: - warnings.warn("null string passed to Literal; use Empty() instead", - SyntaxWarning, stacklevel=2) - self.__class__ = Empty - self.name = '"%s"' % _ustr(self.match) - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = False - #self.myException.msg = self.errmsg - self.mayIndexError = False - - # Performance tuning: this routine gets called a *lot* - # if this is a single character match string and the first character matches, - # short-circuit as quickly as possible, and avoid calling startswith - #~ @profile - def parseImpl( self, instring, loc, doActions=True ): - if (instring[loc] == self.firstMatchChar and - (self.matchLen==1 or instring.startswith(self.match,loc)) ): - return loc+self.matchLen, self.match - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc -_L = Literal - -class Keyword(Token): - """Token to exactly match a specified string as a keyword, that is, it must be - immediately followed by a non-keyword character. Compare with C{Literal}:: - Literal("if") will match the leading C{'if'} in C{'ifAndOnlyIf'}. - Keyword("if") will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} - Accepts two optional constructor arguments in addition to the keyword string: - C{identChars} is a string of characters that would be valid identifier characters, - defaulting to all alphanumerics + "_" and "$"; C{caseless} allows case-insensitive - matching, default is C{False}. - """ - DEFAULT_KEYWORD_CHARS = alphanums+"_$" - - def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): - super(Keyword,self).__init__() - self.match = matchString - self.matchLen = len(matchString) - try: - self.firstMatchChar = matchString[0] - except IndexError: - warnings.warn("null string passed to Keyword; use Empty() instead", - SyntaxWarning, stacklevel=2) - self.name = '"%s"' % self.match - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = False - #self.myException.msg = self.errmsg - self.mayIndexError = False - self.caseless = caseless - if caseless: - self.caselessmatch = matchString.upper() - identChars = identChars.upper() - self.identChars = set(identChars) - - def parseImpl( self, instring, loc, doActions=True ): - if self.caseless: - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and - (loc == 0 or instring[loc-1].upper() not in self.identChars) ): - return loc+self.matchLen, self.match - else: - if (instring[loc] == self.firstMatchChar and - (self.matchLen==1 or instring.startswith(self.match,loc)) and - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and - (loc == 0 or instring[loc-1] not in self.identChars) ): - return loc+self.matchLen, self.match - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - def copy(self): - c = super(Keyword,self).copy() - c.identChars = Keyword.DEFAULT_KEYWORD_CHARS - return c - - def setDefaultKeywordChars( chars ): - """Overrides the default Keyword chars - """ - Keyword.DEFAULT_KEYWORD_CHARS = chars - setDefaultKeywordChars = staticmethod(setDefaultKeywordChars) - -class CaselessLiteral(Literal): - """Token to match a specified string, ignoring case of letters. - Note: the matched results will always be in the case of the given - match string, NOT the case of the input text. - """ - def __init__( self, matchString ): - super(CaselessLiteral,self).__init__( matchString.upper() ) - # Preserve the defining literal. - self.returnString = matchString - self.name = "'%s'" % self.returnString - self.errmsg = "Expected " + self.name - #self.myException.msg = self.errmsg - - def parseImpl( self, instring, loc, doActions=True ): - if instring[ loc:loc+self.matchLen ].upper() == self.match: - return loc+self.matchLen, self.returnString - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - -class CaselessKeyword(Keyword): - def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): - super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) - - def parseImpl( self, instring, loc, doActions=True ): - if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and - (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): - return loc+self.matchLen, self.match - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - -class Word(Token): - """Token for matching words composed of allowed character sets. - Defined with string containing all allowed initial characters, - an optional string containing allowed body characters (if omitted, - defaults to the initial character set), and an optional minimum, - maximum, and/or exact length. The default value for C{min} is 1 (a - minimum value < 1 is not valid); the default values for C{max} and C{exact} - are 0, meaning no maximum or exact length restriction. - """ - def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False ): - super(Word,self).__init__() - self.initCharsOrig = initChars - self.initChars = set(initChars) - if bodyChars : - self.bodyCharsOrig = bodyChars - self.bodyChars = set(bodyChars) - else: - self.bodyCharsOrig = initChars - self.bodyChars = set(initChars) - - self.maxSpecified = max > 0 - - if min < 1: - raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - #self.myException.msg = self.errmsg - self.mayIndexError = False - self.asKeyword = asKeyword - - if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): - if self.bodyCharsOrig == self.initCharsOrig: - self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) - elif len(self.bodyCharsOrig) == 1: - self.reString = "%s[%s]*" % \ - (re.escape(self.initCharsOrig), - _escapeRegexRangeChars(self.bodyCharsOrig),) - else: - self.reString = "[%s][%s]*" % \ - (_escapeRegexRangeChars(self.initCharsOrig), - _escapeRegexRangeChars(self.bodyCharsOrig),) - if self.asKeyword: - self.reString = r"\b"+self.reString+r"\b" - try: - self.re = re.compile( self.reString ) - except: - self.re = None - - def parseImpl( self, instring, loc, doActions=True ): - if self.re: - result = self.re.match(instring,loc) - if not result: - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - loc = result.end() - return loc, result.group() - - if not(instring[ loc ] in self.initChars): - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - start = loc - loc += 1 - instrlen = len(instring) - bodychars = self.bodyChars - maxloc = start + self.maxLen - maxloc = min( maxloc, instrlen ) - while loc < maxloc and instring[loc] in bodychars: - loc += 1 - - throwException = False - if loc - start < self.minLen: - throwException = True - if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: - throwException = True - if self.asKeyword: - if (start>0 and instring[start-1] in bodychars) or (loc4: - return s[:4]+"..." - else: - return s - - if ( self.initCharsOrig != self.bodyCharsOrig ): - self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) - else: - self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) - - return self.strRepr - - -class Regex(Token): - """Token for matching strings that match a given regular expression. - Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. - """ - compiledREtype = type(re.compile("[A-Z]")) - def __init__( self, pattern, flags=0): - """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" - super(Regex,self).__init__() - - if isinstance(pattern, basestring): - if len(pattern) == 0: - warnings.warn("null string passed to Regex; use Empty() instead", - SyntaxWarning, stacklevel=2) - - self.pattern = pattern - self.flags = flags - - try: - self.re = re.compile(self.pattern, self.flags) - self.reString = self.pattern - except sre_constants.error: - warnings.warn("invalid pattern (%s) passed to Regex" % pattern, - SyntaxWarning, stacklevel=2) - raise - - elif isinstance(pattern, Regex.compiledREtype): - self.re = pattern - self.pattern = \ - self.reString = str(pattern) - self.flags = flags - - else: - raise ValueError("Regex may only be constructed with a string or a compiled RE object") - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - #self.myException.msg = self.errmsg - self.mayIndexError = False - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - result = self.re.match(instring,loc) - if not result: - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - loc = result.end() - d = result.groupdict() - ret = ParseResults(result.group()) - if d: - for k in d: - ret[k] = d[k] - return loc,ret - - def __str__( self ): - try: - return super(Regex,self).__str__() - except: - pass - - if self.strRepr is None: - self.strRepr = "Re:(%s)" % repr(self.pattern) - - return self.strRepr - - -class QuotedString(Token): - """Token for matching strings that are delimited by quoting characters. - """ - def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): - """ - Defined with the following parameters: - - quoteChar - string of one or more characters defining the quote delimiting string - - escChar - character to escape quotes, typically backslash (default=None) - - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) - - multiline - boolean indicating whether quotes can span multiple lines (default=False) - - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) - - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) - """ - super(QuotedString,self).__init__() - - # remove white space from quote chars - wont work anyway - quoteChar = quoteChar.strip() - if len(quoteChar) == 0: - warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - raise SyntaxError() - - if endQuoteChar is None: - endQuoteChar = quoteChar - else: - endQuoteChar = endQuoteChar.strip() - if len(endQuoteChar) == 0: - warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) - raise SyntaxError() - - self.quoteChar = quoteChar - self.quoteCharLen = len(quoteChar) - self.firstQuoteChar = quoteChar[0] - self.endQuoteChar = endQuoteChar - self.endQuoteCharLen = len(endQuoteChar) - self.escChar = escChar - self.escQuote = escQuote - self.unquoteResults = unquoteResults - - if multiline: - self.flags = re.MULTILINE | re.DOTALL - self.pattern = r'%s(?:[^%s%s]' % \ - ( re.escape(self.quoteChar), - _escapeRegexRangeChars(self.endQuoteChar[0]), - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - else: - self.flags = 0 - self.pattern = r'%s(?:[^%s\n\r%s]' % \ - ( re.escape(self.quoteChar), - _escapeRegexRangeChars(self.endQuoteChar[0]), - (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) - if len(self.endQuoteChar) > 1: - self.pattern += ( - '|(?:' + ')|(?:'.join(["%s[^%s]" % (re.escape(self.endQuoteChar[:i]), - _escapeRegexRangeChars(self.endQuoteChar[i])) - for i in range(len(self.endQuoteChar)-1,0,-1)]) + ')' - ) - if escQuote: - self.pattern += (r'|(?:%s)' % re.escape(escQuote)) - if escChar: - self.pattern += (r'|(?:%s.)' % re.escape(escChar)) - charset = ''.join(set(self.quoteChar[0]+self.endQuoteChar[0])).replace('^',r'\^').replace('-',r'\-') - self.escCharReplacePattern = re.escape(self.escChar)+("([%s])" % charset) - self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) - - try: - self.re = re.compile(self.pattern, self.flags) - self.reString = self.pattern - except sre_constants.error: - warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, - SyntaxWarning, stacklevel=2) - raise - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - #self.myException.msg = self.errmsg - self.mayIndexError = False - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None - if not result: - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - loc = result.end() - ret = result.group() - - if self.unquoteResults: - - # strip off quotes - ret = ret[self.quoteCharLen:-self.endQuoteCharLen] - - if isinstance(ret,basestring): - # replace escaped characters - if self.escChar: - ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) - - # replace escaped quotes - if self.escQuote: - ret = ret.replace(self.escQuote, self.endQuoteChar) - - return loc, ret - - def __str__( self ): - try: - return super(QuotedString,self).__str__() - except: - pass - - if self.strRepr is None: - self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) - - return self.strRepr - - -class CharsNotIn(Token): - """Token for matching words composed of characters *not* in a given set. - Defined with string containing all disallowed characters, and an optional - minimum, maximum, and/or exact length. The default value for C{min} is 1 (a - minimum value < 1 is not valid); the default values for C{max} and C{exact} - are 0, meaning no maximum or exact length restriction. - """ - def __init__( self, notChars, min=1, max=0, exact=0 ): - super(CharsNotIn,self).__init__() - self.skipWhitespace = False - self.notChars = notChars - - if min < 1: - raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = ( self.minLen == 0 ) - #self.myException.msg = self.errmsg - self.mayIndexError = False - - def parseImpl( self, instring, loc, doActions=True ): - if instring[loc] in self.notChars: - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - start = loc - loc += 1 - notchars = self.notChars - maxlen = min( start+self.maxLen, len(instring) ) - while loc < maxlen and \ - (instring[loc] not in notchars): - loc += 1 - - if loc - start < self.minLen: - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - return loc, instring[start:loc] - - def __str__( self ): - try: - return super(CharsNotIn, self).__str__() - except: - pass - - if self.strRepr is None: - if len(self.notChars) > 4: - self.strRepr = "!W:(%s...)" % self.notChars[:4] - else: - self.strRepr = "!W:(%s)" % self.notChars - - return self.strRepr - -class White(Token): - """Special matching class for matching whitespace. Normally, whitespace is ignored - by pyparsing grammars. This class is included when some whitespace structures - are significant. Define with a string containing the whitespace characters to be - matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, - as defined for the C{Word} class.""" - whiteStrs = { - " " : "", - "\t": "", - "\n": "", - "\r": "", - "\f": "", - } - def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): - super(White,self).__init__() - self.matchWhite = ws - self.setWhitespaceChars( "".join([c for c in self.whiteChars if c not in self.matchWhite]) ) - #~ self.leaveWhitespace() - self.name = ("".join([White.whiteStrs[c] for c in self.matchWhite])) - self.mayReturnEmpty = True - self.errmsg = "Expected " + self.name - #self.myException.msg = self.errmsg - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - def parseImpl( self, instring, loc, doActions=True ): - if not(instring[ loc ] in self.matchWhite): - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - start = loc - loc += 1 - maxloc = start + self.maxLen - maxloc = min( maxloc, len(instring) ) - while loc < maxloc and instring[loc] in self.matchWhite: - loc += 1 - - if loc - start < self.minLen: - #~ raise ParseException( instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - - return loc, instring[start:loc] - - -class _PositionToken(Token): - def __init__( self ): - super(_PositionToken,self).__init__() - self.name=self.__class__.__name__ - self.mayReturnEmpty = True - self.mayIndexError = False - -class GoToColumn(_PositionToken): - """Token to advance to a specific column of input text; useful for tabular report scraping.""" - def __init__( self, colno ): - super(GoToColumn,self).__init__() - self.col = colno - - def preParse( self, instring, loc ): - if col(loc,instring) != self.col: - instrlen = len(instring) - if self.ignoreExprs: - loc = self._skipIgnorables( instring, loc ) - while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : - loc += 1 - return loc - - def parseImpl( self, instring, loc, doActions=True ): - thiscol = col( loc, instring ) - if thiscol > self.col: - raise ParseException( instring, loc, "Text not in expected column", self ) - newloc = loc + self.col - thiscol - ret = instring[ loc: newloc ] - return newloc, ret - -class LineStart(_PositionToken): - """Matches if current position is at the beginning of a line within the parse string""" - def __init__( self ): - super(LineStart,self).__init__() - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - self.errmsg = "Expected start of line" - #self.myException.msg = self.errmsg - - def preParse( self, instring, loc ): - preloc = super(LineStart,self).preParse(instring,loc) - if instring[preloc] == "\n": - loc += 1 - return loc - - def parseImpl( self, instring, loc, doActions=True ): - if not( loc==0 or - (loc == self.preParse( instring, 0 )) or - (instring[loc-1] == "\n") ): #col(loc, instring) != 1: - #~ raise ParseException( instring, loc, "Expected start of line" ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - return loc, [] - -class LineEnd(_PositionToken): - """Matches if current position is at the end of a line within the parse string""" - def __init__( self ): - super(LineEnd,self).__init__() - self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) - self.errmsg = "Expected end of line" - #self.myException.msg = self.errmsg - - def parseImpl( self, instring, loc, doActions=True ): - if loc len(instring): - return loc, [] - else: - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - -class WordStart(_PositionToken): - """Matches if the current position is at the beginning of a Word, and - is not preceded by any character in a given set of C{wordChars} - (default=C{printables}). To emulate the C{\b} behavior of regular expressions, - use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of - the string being parsed, or at the beginning of a line. - """ - def __init__(self, wordChars = printables): - super(WordStart,self).__init__() - self.wordChars = set(wordChars) - self.errmsg = "Not at the start of a word" - - def parseImpl(self, instring, loc, doActions=True ): - if loc != 0: - if (instring[loc-1] in self.wordChars or - instring[loc] not in self.wordChars): - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - return loc, [] - -class WordEnd(_PositionToken): - """Matches if the current position is at the end of a Word, and - is not followed by any character in a given set of C{wordChars} - (default=C{printables}). To emulate the C{\b} behavior of regular expressions, - use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of - the string being parsed, or at the end of a line. - """ - def __init__(self, wordChars = printables): - super(WordEnd,self).__init__() - self.wordChars = set(wordChars) - self.skipWhitespace = False - self.errmsg = "Not at the end of a word" - - def parseImpl(self, instring, loc, doActions=True ): - instrlen = len(instring) - if instrlen>0 and loc maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException(instring,len(instring),e.errmsg,self) - maxExcLoc = len(instring) - else: - if loc2 > maxMatchLoc: - maxMatchLoc = loc2 - maxMatchExp = e - - if maxMatchLoc < 0: - if maxException is not None: - raise maxException - else: - raise ParseException(instring, loc, "no defined alternatives to match", self) - - return maxMatchExp._parse( instring, loc, doActions ) - - def __ixor__(self, other ): - if isinstance( other, basestring ): - other = Literal( other ) - return self.append( other ) #Or( [ self, other ] ) - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " ^ ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - return self.strRepr - - def checkRecursion( self, parseElementList ): - subRecCheckList = parseElementList[:] + [ self ] - for e in self.exprs: - e.checkRecursion( subRecCheckList ) - - -class MatchFirst(ParseExpression): - """Requires that at least one C{ParseExpression} is found. - If two expressions match, the first one listed is the one that will match. - May be constructed using the C{'|'} operator. - """ - def __init__( self, exprs, savelist = False ): - super(MatchFirst,self).__init__(exprs, savelist) - if exprs: - self.mayReturnEmpty = False - for e in self.exprs: - if e.mayReturnEmpty: - self.mayReturnEmpty = True - break - else: - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - maxExcLoc = -1 - maxException = None - for e in self.exprs: - try: - ret = e._parse( instring, loc, doActions ) - return ret - except ParseException, err: - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException(instring,len(instring),e.errmsg,self) - maxExcLoc = len(instring) - - # only got here if no expression matched, raise exception for match that made it the furthest - else: - if maxException is not None: - raise maxException - else: - raise ParseException(instring, loc, "no defined alternatives to match", self) - - def __ior__(self, other ): - if isinstance( other, basestring ): - other = Literal( other ) - return self.append( other ) #MatchFirst( [ self, other ] ) - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " | ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - return self.strRepr - - def checkRecursion( self, parseElementList ): - subRecCheckList = parseElementList[:] + [ self ] - for e in self.exprs: - e.checkRecursion( subRecCheckList ) - - -class Each(ParseExpression): - """Requires all given C{ParseExpression}s to be found, but in any order. - Expressions may be separated by whitespace. - May be constructed using the C{'&'} operator. - """ - def __init__( self, exprs, savelist = True ): - super(Each,self).__init__(exprs, savelist) - self.mayReturnEmpty = True - for e in self.exprs: - if not e.mayReturnEmpty: - self.mayReturnEmpty = False - break - self.skipWhitespace = True - self.initExprGroups = True - - def parseImpl( self, instring, loc, doActions=True ): - if self.initExprGroups: - opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] - opt2 = [ e for e in self.exprs if e.mayReturnEmpty and e not in opt1 ] - self.optionals = opt1 + opt2 - self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] - self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] - self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] - self.required += self.multirequired - self.initExprGroups = False - tmpLoc = loc - tmpReqd = self.required[:] - tmpOpt = self.optionals[:] - matchOrder = [] - - keepMatching = True - while keepMatching: - tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired - failed = [] - for e in tmpExprs: - try: - tmpLoc = e.tryParse( instring, tmpLoc ) - except ParseException: - failed.append(e) - else: - matchOrder.append(e) - if e in tmpReqd: - tmpReqd.remove(e) - elif e in tmpOpt: - tmpOpt.remove(e) - if len(failed) == len(tmpExprs): - keepMatching = False - - if tmpReqd: - missing = ", ".join( [ _ustr(e) for e in tmpReqd ] ) - raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) - - # add any unmatched Optionals, in case they have default values defined - matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] - - resultlist = [] - for e in matchOrder: - loc,results = e._parse(instring,loc,doActions) - resultlist.append(results) - - finalResults = ParseResults([]) - for r in resultlist: - dups = {} - for k in r.keys(): - if k in finalResults.keys(): - tmp = ParseResults(finalResults[k]) - tmp += ParseResults(r[k]) - dups[k] = tmp - finalResults += ParseResults(r) - for k,v in dups.items(): - finalResults[k] = v - return loc, finalResults - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " & ".join( [ _ustr(e) for e in self.exprs ] ) + "}" - - return self.strRepr - - def checkRecursion( self, parseElementList ): - subRecCheckList = parseElementList[:] + [ self ] - for e in self.exprs: - e.checkRecursion( subRecCheckList ) - - -class ParseElementEnhance(ParserElement): - """Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.""" - def __init__( self, expr, savelist=False ): - super(ParseElementEnhance,self).__init__(savelist) - if isinstance( expr, basestring ): - expr = Literal(expr) - self.expr = expr - self.strRepr = None - if expr is not None: - self.mayIndexError = expr.mayIndexError - self.mayReturnEmpty = expr.mayReturnEmpty - self.setWhitespaceChars( expr.whiteChars ) - self.skipWhitespace = expr.skipWhitespace - self.saveAsList = expr.saveAsList - self.callPreparse = expr.callPreparse - self.ignoreExprs.extend(expr.ignoreExprs) - - def parseImpl( self, instring, loc, doActions=True ): - if self.expr is not None: - return self.expr._parse( instring, loc, doActions, callPreParse=False ) - else: - raise ParseException("",loc,self.errmsg,self) - - def leaveWhitespace( self ): - self.skipWhitespace = False - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.leaveWhitespace() - return self - - def ignore( self, other ): - if isinstance( other, Suppress ): - if other not in self.ignoreExprs: - super( ParseElementEnhance, self).ignore( other ) - if self.expr is not None: - self.expr.ignore( self.ignoreExprs[-1] ) - else: - super( ParseElementEnhance, self).ignore( other ) - if self.expr is not None: - self.expr.ignore( self.ignoreExprs[-1] ) - return self - - def streamline( self ): - super(ParseElementEnhance,self).streamline() - if self.expr is not None: - self.expr.streamline() - return self - - def checkRecursion( self, parseElementList ): - if self in parseElementList: - raise RecursiveGrammarException( parseElementList+[self] ) - subRecCheckList = parseElementList[:] + [ self ] - if self.expr is not None: - self.expr.checkRecursion( subRecCheckList ) - - def validate( self, validateTrace=[] ): - tmp = validateTrace[:]+[self] - if self.expr is not None: - self.expr.validate(tmp) - self.checkRecursion( [] ) - - def __str__( self ): - try: - return super(ParseElementEnhance,self).__str__() - except: - pass - - if self.strRepr is None and self.expr is not None: - self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) - return self.strRepr - - -class FollowedBy(ParseElementEnhance): - """Lookahead matching of the given parse expression. C{FollowedBy} - does *not* advance the parsing position within the input string, it only - verifies that the specified parse expression matches at the current - position. C{FollowedBy} always returns a null token list.""" - def __init__( self, expr ): - super(FollowedBy,self).__init__(expr) - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - self.expr.tryParse( instring, loc ) - return loc, [] - - -class NotAny(ParseElementEnhance): - """Lookahead to disallow matching with the given parse expression. C{NotAny} - does *not* advance the parsing position within the input string, it only - verifies that the specified parse expression does *not* match at the current - position. Also, C{NotAny} does *not* skip over leading whitespace. C{NotAny} - always returns a null token list. May be constructed using the '~' operator.""" - def __init__( self, expr ): - super(NotAny,self).__init__(expr) - #~ self.leaveWhitespace() - self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs - self.mayReturnEmpty = True - self.errmsg = "Found unwanted token, "+_ustr(self.expr) - #self.myException = ParseException("",0,self.errmsg,self) - - def parseImpl( self, instring, loc, doActions=True ): - try: - self.expr.tryParse( instring, loc ) - except (ParseException,IndexError): - pass - else: - #~ raise ParseException(instring, loc, self.errmsg ) - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - return loc, [] - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "~{" + _ustr(self.expr) + "}" - - return self.strRepr - - -class ZeroOrMore(ParseElementEnhance): - """Optional repetition of zero or more of the given expression.""" - def __init__( self, expr ): - super(ZeroOrMore,self).__init__(expr) - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - tokens = [] - try: - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - while 1: - if hasIgnoreExprs: - preloc = self._skipIgnorables( instring, loc ) - else: - preloc = loc - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - if tmptokens or tmptokens.keys(): - tokens += tmptokens - except (ParseException,IndexError): - pass - - return loc, tokens - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "[" + _ustr(self.expr) + "]..." - - return self.strRepr - - def setResultsName( self, name, listAllMatches=False ): - ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) - ret.saveAsList = True - return ret - - -class OneOrMore(ParseElementEnhance): - """Repetition of one or more of the given expression.""" - def parseImpl( self, instring, loc, doActions=True ): - # must be at least one - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - try: - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - while 1: - if hasIgnoreExprs: - preloc = self._skipIgnorables( instring, loc ) - else: - preloc = loc - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - if tmptokens or tmptokens.keys(): - tokens += tmptokens - except (ParseException,IndexError): - pass - - return loc, tokens - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + _ustr(self.expr) + "}..." - - return self.strRepr - - def setResultsName( self, name, listAllMatches=False ): - ret = super(OneOrMore,self).setResultsName(name,listAllMatches) - ret.saveAsList = True - return ret - -class _NullToken(object): - def __bool__(self): - return False - __nonzero__ = __bool__ - def __str__(self): - return "" - -_optionalNotMatched = _NullToken() -class Optional(ParseElementEnhance): - """Optional matching of the given expression. - A default return string can also be specified, if the optional expression - is not found. - """ - def __init__( self, exprs, default=_optionalNotMatched ): - super(Optional,self).__init__( exprs, savelist=False ) - self.defaultValue = default - self.mayReturnEmpty = True - - def parseImpl( self, instring, loc, doActions=True ): - try: - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - except (ParseException,IndexError): - if self.defaultValue is not _optionalNotMatched: - if self.expr.resultsName: - tokens = ParseResults([ self.defaultValue ]) - tokens[self.expr.resultsName] = self.defaultValue - else: - tokens = [ self.defaultValue ] - else: - tokens = [] - return loc, tokens - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - if self.strRepr is None: - self.strRepr = "[" + _ustr(self.expr) + "]" - - return self.strRepr - - -class SkipTo(ParseElementEnhance): - """Token for skipping over all undefined text until the matched expression is found. - If C{include} is set to true, the matched expression is also parsed (the skipped text - and matched expression are returned as a 2-element list). The C{ignore} - argument is used to define grammars (typically quoted strings and comments) that - might contain false matches. - """ - def __init__( self, other, include=False, ignore=None, failOn=None ): - super( SkipTo, self ).__init__( other ) - self.ignoreExpr = ignore - self.mayReturnEmpty = True - self.mayIndexError = False - self.includeMatch = include - self.asList = False - if failOn is not None and isinstance(failOn, basestring): - self.failOn = Literal(failOn) - else: - self.failOn = failOn - self.errmsg = "No match found for "+_ustr(self.expr) - #self.myException = ParseException("",0,self.errmsg,self) - - def parseImpl( self, instring, loc, doActions=True ): - startLoc = loc - instrlen = len(instring) - expr = self.expr - failParse = False - while loc <= instrlen: - try: - if self.failOn: - try: - self.failOn.tryParse(instring, loc) - except ParseBaseException: - pass - else: - failParse = True - raise ParseException(instring, loc, "Found expression " + str(self.failOn)) - failParse = False - if self.ignoreExpr is not None: - while 1: - try: - loc = self.ignoreExpr.tryParse(instring,loc) - # print "found ignoreExpr, advance to", loc - except ParseBaseException: - break - expr._parse( instring, loc, doActions=False, callPreParse=False ) - skipText = instring[startLoc:loc] - if self.includeMatch: - loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) - if mat: - skipRes = ParseResults( skipText ) - skipRes += mat - return loc, [ skipRes ] - else: - return loc, [ skipText ] - else: - return loc, [ skipText ] - except (ParseException,IndexError): - if failParse: - raise - else: - loc += 1 - exc = self.myException - exc.loc = loc - exc.pstr = instring - raise exc - -class Forward(ParseElementEnhance): - """Forward declaration of an expression to be defined later - - used for recursive grammars, such as algebraic infix notation. - When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. - - Note: take care when assigning to C{Forward} not to overlook precedence of operators. - Specifically, '|' has a lower precedence than '<<', so that:: - fwdExpr << a | b | c - will actually be evaluated as:: - (fwdExpr << a) | b | c - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the C{Forward}:: - fwdExpr << (a | b | c) - """ - def __init__( self, other=None ): - super(Forward,self).__init__( other, savelist=False ) - - def __lshift__( self, other ): - if isinstance( other, basestring ): - other = Literal(other) - self.expr = other - self.mayReturnEmpty = other.mayReturnEmpty - self.strRepr = None - self.mayIndexError = self.expr.mayIndexError - self.mayReturnEmpty = self.expr.mayReturnEmpty - self.setWhitespaceChars( self.expr.whiteChars ) - self.skipWhitespace = self.expr.skipWhitespace - self.saveAsList = self.expr.saveAsList - self.ignoreExprs.extend(self.expr.ignoreExprs) - return None - - def leaveWhitespace( self ): - self.skipWhitespace = False - return self - - def streamline( self ): - if not self.streamlined: - self.streamlined = True - if self.expr is not None: - self.expr.streamline() - return self - - def validate( self, validateTrace=[] ): - if self not in validateTrace: - tmp = validateTrace[:]+[self] - if self.expr is not None: - self.expr.validate(tmp) - self.checkRecursion([]) - - def __str__( self ): - if hasattr(self,"name"): - return self.name - - self._revertClass = self.__class__ - self.__class__ = _ForwardNoRecurse - try: - if self.expr is not None: - retString = _ustr(self.expr) - else: - retString = "None" - finally: - self.__class__ = self._revertClass - return self.__class__.__name__ + ": " + retString - - def copy(self): - if self.expr is not None: - return super(Forward,self).copy() - else: - ret = Forward() - ret << self - return ret - -class _ForwardNoRecurse(Forward): - def __str__( self ): - return "..." - -class TokenConverter(ParseElementEnhance): - """Abstract subclass of C{ParseExpression}, for converting parsed results.""" - def __init__( self, expr, savelist=False ): - super(TokenConverter,self).__init__( expr )#, savelist ) - self.saveAsList = False - -class Upcase(TokenConverter): - """Converter to upper case all matching tokens.""" - def __init__(self, *args): - super(Upcase,self).__init__(*args) - warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", - DeprecationWarning,stacklevel=2) - - def postParse( self, instring, loc, tokenlist ): - return list(map( string.upper, tokenlist )) - - -class Combine(TokenConverter): - """Converter to concatenate all matching tokens to a single string. - By default, the matching patterns must also be contiguous in the input string; - this can be disabled by specifying C{'adjacent=False'} in the constructor. - """ - def __init__( self, expr, joinString="", adjacent=True ): - super(Combine,self).__init__( expr ) - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - if adjacent: - self.leaveWhitespace() - self.adjacent = adjacent - self.skipWhitespace = True - self.joinString = joinString - self.callPreparse = True - - def ignore( self, other ): - if self.adjacent: - ParserElement.ignore(self, other) - else: - super( Combine, self).ignore( other ) - return self - - def postParse( self, instring, loc, tokenlist ): - retToks = tokenlist.copy() - del retToks[:] - retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) - - if self.resultsName and len(retToks.keys())>0: - return [ retToks ] - else: - return retToks - -class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for returning tokens of C{ZeroOrMore} and C{OneOrMore} expressions.""" - def __init__( self, expr ): - super(Group,self).__init__( expr ) - self.saveAsList = True - - def postParse( self, instring, loc, tokenlist ): - return [ tokenlist ] - -class Dict(TokenConverter): - """Converter to return a repetitive expression as a list, but also as a dictionary. - Each element can also be referenced using the first token in the expression as its key. - Useful for tabular report scraping when the first column can be used as a item key. - """ - def __init__( self, exprs ): - super(Dict,self).__init__( exprs ) - self.saveAsList = True - - def postParse( self, instring, loc, tokenlist ): - for i,tok in enumerate(tokenlist): - if len(tok) == 0: - continue - ikey = tok[0] - if isinstance(ikey,int): - ikey = _ustr(tok[0]).strip() - if len(tok)==1: - tokenlist[ikey] = _ParseResultsWithOffset("",i) - elif len(tok)==2 and not isinstance(tok[1],ParseResults): - tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) - else: - dictvalue = tok.copy() #ParseResults(i) - del dictvalue[0] - if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.keys()): - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) - else: - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) - - if self.resultsName: - return [ tokenlist ] - else: - return tokenlist - - -class Suppress(TokenConverter): - """Converter for ignoring the results of a parsed expression.""" - def postParse( self, instring, loc, tokenlist ): - return [] - - def suppress( self ): - return self - - -class OnlyOnce(object): - """Wrapper for parse actions, to ensure they are only called once.""" - def __init__(self, methodCall): - self.callable = _trim_arity(methodCall) - self.called = False - def __call__(self,s,l,t): - if not self.called: - results = self.callable(s,l,t) - self.called = True - return results - raise ParseException(s,l,"") - def reset(self): - self.called = False - -def traceParseAction(f): - """Decorator for debugging parse actions.""" - f = _trim_arity(f) - def z(*paArgs): - thisFunc = f.func_name - s,l,t = paArgs[-3:] - if len(paArgs)>3: - thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc - sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) - try: - ret = f(*paArgs) - except Exception: - exc = sys.exc_info()[1] - sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) - try: - if len(symbols)==len("".join(symbols)): - return Regex( "[%s]" % "".join( [ _escapeRegexRangeChars(sym) for sym in symbols] ) ) - else: - return Regex( "|".join( [ re.escape(sym) for sym in symbols] ) ) - except: - warnings.warn("Exception creating Regex for oneOf, building MatchFirst", - SyntaxWarning, stacklevel=2) - - - # last resort, just use MatchFirst - return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) - -def dictOf( key, value ): - """Helper to easily and clearly define a dictionary by specifying the respective patterns - for the key and value. Takes care of defining the C{Dict}, C{ZeroOrMore}, and C{Group} tokens - in the proper order. The key pattern can include delimiting markers or punctuation, - as long as they are suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the C{Dict} results can include named token - fields. - """ - return Dict( ZeroOrMore( Group ( key + value ) ) ) - -def originalTextFor(expr, asString=True): - """Helper to return the original, untokenized text for a given expression. Useful to - restore the parsed fields of an HTML start tag into the raw tag text itself, or to - revert separate tokens with intervening whitespace back to the original matching - input text. Simpler to use than the parse action C{L{keepOriginalText}}, and does not - require the inspect module to chase up the call stack. By default, returns a - string containing the original parsed text. - - If the optional C{asString} argument is passed as C{False}, then the return value is a - C{ParseResults} containing any results names that were originally matched, and a - single token containing the original matched text from the input string. So if - the expression passed to C{L{originalTextFor}} contains expressions with defined - results names, you must set C{asString} to C{False} if you want to preserve those - results name values.""" - locMarker = Empty().setParseAction(lambda s,loc,t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s,l,t: s[t._original_start:t._original_end] - else: - def extractText(s,l,t): - del t[:] - t.insert(0, s[t._original_start:t._original_end]) - del t["_original_start"] - del t["_original_end"] - matchExpr.setParseAction(extractText) - return matchExpr - -# convenience constants for positional expressions -empty = Empty().setName("empty") -lineStart = LineStart().setName("lineStart") -lineEnd = LineEnd().setName("lineEnd") -stringStart = StringStart().setName("stringStart") -stringEnd = StringEnd().setName("stringEnd") - -_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) -_printables_less_backslash = "".join([ c for c in printables if c not in r"\]" ]) -_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],16))) -_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) -_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(_printables_less_backslash,exact=1) -_charRange = Group(_singleChar + Suppress("-") + _singleChar) -_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" - -_expanded = lambda p: (isinstance(p,ParseResults) and ''.join([ unichr(c) for c in range(ord(p[0]),ord(p[1])+1) ]) or p) - -def srange(s): - r"""Helper to easily define string ranges for use in Word construction. Borrows - syntax from regexp '[]' string range definitions:: - srange("[0-9]") -> "0123456789" - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - The input string must be enclosed in []'s, and the returned string is the expanded - character set joined into a single string. - The values enclosed in the []'s may be:: - a single character - an escaped character with a leading backslash (such as \- or \]) - an escaped hex character with a leading '\x' (\x21, which is a '!' character) - (\0x## is also supported for backwards compatibility) - an escaped octal character with a leading '\0' (\041, which is a '!' character) - a range of any of the above, separated by a dash ('a-z', etc.) - any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) - """ - try: - return "".join([_expanded(part) for part in _reBracketExpr.parseString(s).body]) - except: - return "" - -def matchOnlyAtCol(n): - """Helper method for defining parse actions that require matching at a specific - column in the input text. - """ - def verifyCol(strg,locn,toks): - if col(locn,strg) != n: - raise ParseException(strg,locn,"matched token not at column %d" % n) - return verifyCol - -def replaceWith(replStr): - """Helper method for common parse actions that simply return a literal value. Especially - useful when used with C{transformString()}. - """ - def _replFunc(*args): - return [replStr] - return _replFunc - -def removeQuotes(s,l,t): - """Helper parse action for removing quotation marks from parsed quoted strings. - To use, add this parse action to quoted string using:: - quotedString.setParseAction( removeQuotes ) - """ - return t[0][1:-1] - -def upcaseTokens(s,l,t): - """Helper parse action to convert tokens to upper case.""" - return [ tt.upper() for tt in map(_ustr,t) ] - -def downcaseTokens(s,l,t): - """Helper parse action to convert tokens to lower case.""" - return [ tt.lower() for tt in map(_ustr,t) ] - -def keepOriginalText(s,startLoc,t): - """DEPRECATED - use new helper method C{originalTextFor}. - Helper parse action to preserve original parsed text, - overriding any nested parse actions.""" - try: - endloc = getTokensEndLoc() - except ParseException: - raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") - del t[:] - t += ParseResults(s[startLoc:endloc]) - return t - -def getTokensEndLoc(): - """Method to be called from within a parse action to determine the end - location of the parsed tokens.""" - import inspect - fstack = inspect.stack() - try: - # search up the stack (through intervening argument normalizers) for correct calling routine - for f in fstack[2:]: - if f[3] == "_parseNoCache": - endloc = f[0].f_locals["loc"] - return endloc - else: - raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") - finally: - del fstack - -def _makeTags(tagStr, xml): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr,basestring): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas,alphanums+"_-:") - if (xml): - tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) - openTag = Suppress("<") + tagStr("tag") + \ - Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - else: - printablesLessRAbrack = "".join( [ c for c in printables if c not in ">" ] ) - tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) - openTag = Suppress("<") + tagStr("tag") + \ - Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ - Optional( Suppress("=") + tagAttrValue ) ))) + \ - Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") - closeTag = Combine(_L("") - - openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) - closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) - openTag.tag = resname - closeTag.tag = resname - return openTag, closeTag - -def makeHTMLTags(tagStr): - """Helper to construct opening and closing tag expressions for HTML, given a tag name""" - return _makeTags( tagStr, False ) - -def makeXMLTags(tagStr): - """Helper to construct opening and closing tag expressions for XML, given a tag name""" - return _makeTags( tagStr, True ) - -def withAttribute(*args,**attrDict): - """Helper to create a validating parse action to be used with start tags created - with C{makeXMLTags} or C{makeHTMLTags}. Use C{withAttribute} to qualify a starting tag - with a required attribute value, to avoid false matches on common tags such as - C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python - reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k,v) for k,v in attrs] - def pa(s,l,tokens): - for attrName,attrValue in attrs: - if attrName not in tokens: - raise ParseException(s,l,"no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -opAssoc = _Constants() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def operatorPrecedence( baseExpr, opList ): - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants opAssoc.RIGHT and opAssoc.LEFT. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted) - """ - ret = Forward() - lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) - for i,operDef in enumerate(opList): - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward()#.setName("expr%d" % i) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - else: - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - else: - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - matchExpr.setParseAction( pa ) - thisExpr << ( matchExpr | lastExpr ) - lastExpr = thisExpr - ret << lastExpr - return ret - -dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") -sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") -quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()) - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default="("); can also be a pyparsing expression - - closer - closing character for a nested list (default=")"); can also be a pyparsing expression - - content - expression for items within the nested lists (default=None) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener,basestring) and isinstance(closer,basestring): - if len(opener) == 1 and len(closer)==1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t:t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - ~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: - ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. - - Parameters: - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single grammar - should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the - the current level; set to False for block of left-most statements - (default=True) - - A valid block must contain at least one C{blockStatement}. - """ - def checkPeerIndent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseFatalException(s,l,"illegal nesting") - raise ParseException(s,l,"not a peer entry") - - def checkSubIndent(s,l,t): - curCol = col(l,s) - if curCol > indentStack[-1]: - indentStack.append( curCol ) - else: - raise ParseException(s,l,"not a subentry") - - def checkUnindent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - raise ParseException(s,l,"not an unindent") - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = Empty() + Empty().setParseAction(checkSubIndent) - PEER = Empty().setParseAction(checkPeerIndent) - UNDENT = Empty().setParseAction(checkUnindent) - if indent: - smExpr = Group( Optional(NL) + - #~ FollowedBy(blockStatementExpr) + - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - else: - smExpr = Group( Optional(NL) + - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) -commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() -_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) -replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") - -htmlComment = Regex(r"") -restOfLine = Regex(r".*").leaveWhitespace() -dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") -cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?" + str(tokenlist)) - print ("tokens = " + str(tokens)) - print ("tokens.columns = " + str(tokens.columns)) - print ("tokens.tables = " + str(tokens.tables)) - print (tokens.asXML("SQL",True)) - except ParseBaseException: - err = sys.exc_info()[1] - print (teststring + "->") - print (err.line) - print (" "*(err.column-1) + "^") - print (err) - print() - - selectToken = CaselessLiteral( "select" ) - fromToken = CaselessLiteral( "from" ) - - ident = Word( alphas, alphanums + "_$" ) - columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - columnNameList = Group( delimitedList( columnName ) )#.setName("columns") - tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - tableNameList = Group( delimitedList( tableName ) )#.setName("tables") - simpleSQL = ( selectToken + \ - ( '*' | columnNameList ).setResultsName( "columns" ) + \ - fromToken + \ - tableNameList.setResultsName( "tables" ) ) - - test( "SELECT * from XYZZY, ABC" ) - test( "select * from SYS.XYZZY" ) - test( "Select A from Sys.dual" ) - test( "Select AA,BB,CC from Sys.dual" ) - test( "Select A, B, C from Sys.dual" ) - test( "Select A, B, C from Sys.dual" ) - test( "Xelect A, B, C from Sys.dual" ) - test( "Select A, B, C frox Sys.dual" ) - test( "Select" ) - test( "Select ^^^ frox Sys.dual" ) - test( "Select A, B, C from Sys.dual, Table2 " ) diff --git a/scripts/xml2aloe/__init__.py b/scripts/xml2aloe/__init__.py index 78baf7aaf..1a26a8b67 100644 --- a/scripts/xml2aloe/__init__.py +++ b/scripts/xml2aloe/__init__.py @@ -1,3 +1,26 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + + + import shutil import os diff --git a/scripts/xml2aloe/template/src/template.c b/scripts/xml2aloe/template/src/template.c index a96676b01..cdf1cdf14 100644 --- a/scripts/xml2aloe/template/src/template.c +++ b/scripts/xml2aloe/template/src/template.c @@ -1,3 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + /* * This file has been automatically generated from -name- */ diff --git a/scripts/xml2aloe/template/src/template.h b/scripts/xml2aloe/template/src/template.h index c09551d7e..c9283193a 100644 --- a/scripts/xml2aloe/template/src/template.h +++ b/scripts/xml2aloe/template/src/template.h @@ -1,3 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + #ifndef DEFINE_H #define DEFINE_H diff --git a/scripts/xml2aloe/template/test/test_generate.c b/scripts/xml2aloe/template/test/test_generate.c index c21002790..1f31903c0 100644 --- a/scripts/xml2aloe/template/test/test_generate.c +++ b/scripts/xml2aloe/template/test/test_generate.c @@ -1,21 +1,31 @@ -/* - * Copyright (c) 2013, Ismael Gomez-Miguelez . - * This file is part of ALOE++ (http://flexnets.upc.edu/) - * - * ALOE++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ALOE++ is distributed in the hope that it will be useful, +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ALOE++. If not, see . + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * */ + /* Functions that generate the test data fed into the DSP modules being developed */ #include #include diff --git a/uhd/uhd.h b/uhd/uhd.h deleted file mode 100644 index bcc41b11b..000000000 --- a/uhd/uhd.h +++ /dev/null @@ -1,25 +0,0 @@ - - -#ifdef __cplusplus -extern "C" { -#endif -#include - -int uhd_open(char *args, void **handler); -int uhd_close(void *h); -int uhd_start_rx_stream(void *h); -int uhd_start_rx_stream_nsamples(void *h, int nsamples); -int uhd_stop_rx_stream(void *h); - -bool uhd_rx_wait_lo_locked(void *h); -double uhd_set_rx_srate(void *h, double freq); -double uhd_set_rx_gain(void *h, double gain); - -double uhd_set_rx_freq(void *h, double freq); - -int uhd_recv(void *h, void *data, int nsamples, int blocking); - - -#ifdef __cplusplus -} -#endif diff --git a/uhd/uhd_handler.hpp b/uhd/uhd_handler.hpp deleted file mode 100644 index 633ef00d0..000000000 --- a/uhd/uhd_handler.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -class uhd_handler { -public: - uhd::usrp::multi_usrp::sptr usrp; - uhd::rx_streamer::sptr rx_stream; - bool rx_stream_enable; - -}; diff --git a/uhd/uhd_imp.cpp b/uhd/uhd_imp.cpp deleted file mode 100644 index 91a04d983..000000000 --- a/uhd/uhd_imp.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include -#include - -void my_handler(uhd::msg::type_t type, const std::string &msg){ - //handle the message... -} - -#include "uhd_handler.hpp" -#include "uhd.h" - -typedef _Complex float complex_t; - -#define SAMPLE_SZ sizeof(complex_t) - -bool isLocked(void *h) -{ - uhd_handler* handler = static_cast(h); - return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool(); -} - -bool uhd_rx_wait_lo_locked(void *h) -{ - - double report = 0.0; - while(isLocked(h) && report < 3.0) - { - report += 0.1; - usleep(1000); - } - return isLocked(h); -} - -int uhd_start_rx_stream(void *h) { - uhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - handler->usrp->issue_stream_cmd(cmd); - return 0; -} - -int uhd_stop_rx_stream(void *h) { - uhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - handler->usrp->issue_stream_cmd(cmd); - return 0; -} - -int uhd_start_rx_stream_nsamples(void *h, int nsamples) { - uhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - cmd.num_samps = nsamples; - handler->usrp->issue_stream_cmd(cmd); - return 0; -} - - - -int uhd_open(char *args, void **h) { - uhd_handler* handler = new uhd_handler(); - std::string _args=std::string(args); - handler->usrp = uhd::usrp::multi_usrp::make(_args); - - //uhd::msg::register_handler(&my_handler); - - std::string otw, cpu; - otw="sc16"; - cpu="fc32"; - - handler->usrp->set_clock_source("internal"); - - uhd::stream_args_t stream_args(cpu, otw); -// stream_args.channels.push_back(0); -// stream_args.args["noclear"] = "1"; - - handler->rx_stream = handler->usrp->get_rx_stream(stream_args); - *h = handler; - - int size = 10000*handler->rx_stream->get_max_num_samps(); - - return 0; -} - -int uhd_close(void *h) { - uhd_handler* handler = static_cast(h); - return 0; -} - - -double uhd_set_rx_srate(void *h, double freq) { - uhd_handler* handler = static_cast(h); - handler->usrp->set_rx_rate(freq); - double ret = handler->usrp->get_rx_rate(); - return ret; -} - -double uhd_set_rx_gain(void *h, double gain) { - uhd_handler* handler = static_cast(h); - handler->usrp->set_rx_gain(gain); - return handler->usrp->get_rx_gain(); -} - -float uhd_get_rx_srate(void *h) { - uhd_handler* handler = static_cast(h); - return handler->usrp->get_tx_rate(); -} - -double uhd_set_rx_freq(void *h, double freq) { - uhd_handler* handler = static_cast(h); - handler->usrp->set_rx_freq(freq); - return handler->usrp->get_rx_freq(); -} - -int uhd_recv(void *h, void *data, int nsamples, int blocking) { - uhd_handler* handler = static_cast(h); - uhd::rx_metadata_t md; - if (blocking) { - int n=0,p; - complex_t *data_c = (complex_t*) data; - do { - p=handler->rx_stream->recv(&data_c[n], nsamples-n, md); - if (p == -1) { - return -1; - } - n+=p; - } while(nrx_stream->recv(data, nsamples, md, 0.0); - } -} diff --git a/uhd/uhd_utils.c b/uhd/uhd_utils.c deleted file mode 100644 index 89548991e..000000000 --- a/uhd/uhd_utils.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include - -#include "uhd.h" -#include "utils/vector.h" -#include "utils/debug.h" - -int uhd_rssi_scan(void *uhd, float *freqs, float *rssi, int nof_bands, double fs, int nsamp) { - int i, j; - int ret = -1; - _Complex float *buffer; - double f; - - buffer = calloc(nsamp, sizeof(_Complex float)); - if (!buffer) { - goto free_and_exit; - } - - uhd_set_rx_gain(uhd, 0.0); - uhd_set_rx_srate(uhd, fs); - - for (i=0;i Date: Mon, 3 Mar 2014 20:25:56 +0000 Subject: [PATCH 03/25] Added USRP arg options --- examples/enodeb_bch.c | 48 ++++++++++++++++++++++------------------ examples/mib_scan_usrp.c | 8 +++---- examples/mib_track.c | 36 ++++++++++++++++++------------ examples/pss_scan_usrp.c | 8 +++---- 4 files changed, 56 insertions(+), 44 deletions(-) diff --git a/examples/enodeb_bch.c b/examples/enodeb_bch.c index cbe540119..e92fe1e77 100644 --- a/examples/enodeb_bch.c +++ b/examples/enodeb_bch.c @@ -44,7 +44,9 @@ char *output_file_name = NULL; int nof_frames=-1; int cell_id = 1; int nof_prb = 6; +char *uhd_args = ""; +float uhd_amp=0.25, uhd_gain=10.0, uhd_freq=2400000000; filesink_t fsink; lte_fft_t ifft; @@ -53,14 +55,14 @@ pbch_t pbch; cf_t *slot_buffer = NULL, *output_buffer = NULL; int slot_n_re, slot_n_samples; -#define cuhd_FREQ 2680000000 -#define cuhd_SAMP_FREQ 1920000 -#define cuhd_GAIN 10 -#define cuhd_AMP 0.25 -#define cuhd_ARGS "addr=192.168.10.3" +#define UHD_SAMP_FREQ 1920000 void usage(char *prog) { - printf("Usage: %s [ncvp]\n", prog); + printf("Usage: %s [agmfoncvp]\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-m UHD signal amplitude [Default %.2f]\n", uhd_amp); + printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq/1000000); printf("\t-o output_file [Default USRP]\n"); printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-c cell id [Default %d]\n", cell_id); @@ -70,8 +72,20 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "oncpv")) != -1) { + while ((opt = getopt(argc, argv, "agfmoncpv")) != -1) { switch(opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'm': + uhd_amp = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; case 'o': output_file_name = argv[optind]; break; @@ -121,7 +135,7 @@ void base_init() { } else { #ifdef ENABLE_UHD printf("Opening UHD device...\n"); - if (cuhd_open(cuhd_ARGS,&uhd)) { + if (cuhd_open(uhd_args,&uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } @@ -138,13 +152,6 @@ void base_init() { fprintf(stderr, "Error creating PBCH object\n"); exit(-1); } -#ifdef ENABLE_MATLAB - fmatlab = fopen("output.m", "w"); - if (!fmatlab) { - perror("fopen"); - exit(-1); - } -#endif } void base_free() { @@ -166,9 +173,6 @@ void base_free() { cuhd_close(&uhd); #endif } -#ifdef ENABLE_MATLAB - fclose(fmatlab); -#endif } int main(int argc, char **argv) { @@ -222,9 +226,9 @@ int main(int argc, char **argv) { #ifdef ENABLE_UHD if (!output_file_name) { - printf("Set TX rate: %.2f MHz\n", cuhd_set_tx_srate(uhd, cuhd_SAMP_FREQ)/1000000); - printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, cuhd_GAIN)); - printf("Set TX freq: %.2f MHz\n", cuhd_set_tx_freq(uhd, cuhd_FREQ)/1000000); + printf("Set TX rate: %.2f MHz\n", cuhd_set_tx_srate(uhd, UHD_SAMP_FREQ)/1000000); + printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, uhd_gain)); + printf("Set TX freq: %.2f MHz\n", cuhd_set_tx_freq(uhd, uhd_freq)/1000000); cuhd_start_tx_stream(uhd); } #endif @@ -258,7 +262,7 @@ int main(int argc, char **argv) { filesink_write(&fsink, output_buffer, slot_n_samples); } else { #ifdef ENABLE_UHD - vec_sc_prod_cfc(output_buffer, cuhd_AMP, output_buffer, slot_n_samples); + vec_sc_prod_cfc(output_buffer, uhd_amp, output_buffer, slot_n_samples); cuhd_send(uhd, output_buffer, slot_n_samples, 1); #endif } diff --git a/examples/mib_scan_usrp.c b/examples/mib_scan_usrp.c index fbbfa037f..3ee312193 100644 --- a/examples/mib_scan_usrp.c +++ b/examples/mib_scan_usrp.c @@ -71,7 +71,7 @@ int *idx_v, *idx_valid, *t; float *p2a_v; void *uhd; int nof_bands; -float gain = 30.0; +float uhd_gain = 30.0; #define MAX_EARFCN 1000 lte_earfcn_t channels[MAX_EARFCN]; @@ -95,7 +95,7 @@ void usage(char *prog) { printf("\t-T pss_track_nof_frames [Default %d]\n", nof_frames_track); printf("\t-t pss_track_threshold [Default %.2f]\n", track_threshold); printf("\t-l pss_track_len [Default %d]\n", track_len); - printf("\t-g gain [Default %.2f dB]\n", gain); + printf("\t-g gain [Default %.2f dB]\n", uhd_gain); printf("\t-v [set verbose to debug, default none]\n"); } @@ -131,7 +131,7 @@ void parse_args(int argc, char **argv) { track_threshold = atof(argv[optind]); break; case 'g': - gain = atof(argv[optind]); + uhd_gain = atof(argv[optind]); break; case 'v': verbose++; @@ -381,7 +381,7 @@ int main(int argc, char **argv) { #ifndef DISABLE_UHD cuhd_set_rx_srate(uhd, SAMP_FREQ); - cuhd_set_rx_gain(uhd, gain); + cuhd_set_rx_gain(uhd, uhd_gain); #endif freq=0; diff --git a/examples/mib_track.c b/examples/mib_track.c index 82705db57..f33a7cdf7 100644 --- a/examples/mib_track.c +++ b/examples/mib_track.c @@ -49,13 +49,15 @@ #define NOF_PORTS 2 -float freq = 2680000000.0; float find_threshold = 40.0, track_threshold = 8.0; int max_track_lost = 9, nof_frames = -1; int track_len=300; char *input_file_name = NULL; int disable_plots = 0; +float uhd_freq = 2400000000.0, uhd_gain = 20.0; +char *uhd_args = ""; + filesource_t fsrc; cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; pbch_t pbch; @@ -68,32 +70,38 @@ plot_complex_t pce; plot_scatter_t pscatrecv, pscatequal; void *uhd; -float gain = 30.0; enum sync_state {FIND, TRACK}; void usage(char *prog) { - printf("Usage: %s [ifgv]\n", prog); + printf("Usage: %s [iagfndv]\n", prog); printf("\t-i input_file [Default use USRP]\n"); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-f UHD RX frequency [Default %.1f MHz]\n", uhd_freq/1000000); printf("\t-n nof_frames [Default %d]\n", nof_frames); printf("\t-d disable plots [Default enabled]\n"); - printf("\t-f freq [Default %.1f MHz]\n", freq/MHZ); - printf("\t-g gain [Default %.2f dB]\n", gain); printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "ifdgv")) != -1) { + while ((opt = getopt(argc, argv, "iagfndv")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; break; - case 'f': - freq = atof(argv[optind]); + case 'a': + uhd_args = argv[optind]; break; case 'g': - gain = atof(argv[optind]); + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); break; case 'd': disable_plots = 1; @@ -188,7 +196,7 @@ int base_init(int frame_length) { /* open UHD device */ #ifndef DISABLE_UHD printf("Opening UHD device...\n"); - if (cuhd_open("addr=192.168.10.2",&uhd)) { + if (cuhd_open(uhd_args,&uhd)) { fprintf(stderr, "Error opening uhd\n"); return -1; } @@ -287,7 +295,7 @@ int main(int argc, char **argv) { #ifndef DISABLE_UHD INFO("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); cuhd_set_rx_srate(uhd, SAMP_FREQ); - cuhd_set_rx_gain(uhd, gain); + cuhd_set_rx_gain(uhd, uhd_gain); #endif state = FIND; @@ -301,10 +309,10 @@ int main(int argc, char **argv) { sync_force_N_id_2(&sfind, -1); #ifndef DISABLE_UHD - /* set freq */ - cuhd_set_rx_freq(uhd, (double) freq); + /* set uhd_freq */ + cuhd_set_rx_freq(uhd, (double) uhd_freq); cuhd_rx_wait_lo_locked(uhd); - DEBUG("Set freq to %.3f MHz\n", (double) freq); + DEBUG("Set uhd_freq to %.3f MHz\n", (double) uhd_freq); DEBUG("Starting receiver...\n",0); cuhd_start_rx_stream(uhd); diff --git a/examples/pss_scan_usrp.c b/examples/pss_scan_usrp.c index 527855bf9..599a6cb06 100644 --- a/examples/pss_scan_usrp.c +++ b/examples/pss_scan_usrp.c @@ -62,7 +62,7 @@ int *idx_v, *idx_valid, *t; float *p2a_v; void *uhd; int nof_bands; -float gain = 20.0; +float uhd_gain = 20.0; #define MAX_EARFCN 1000 lte_earfcn_t channels[MAX_EARFCN]; @@ -87,7 +87,7 @@ void usage(char *prog) { printf("\t-T pss_track_nof_frames [Default %d]\n", nof_frames_track); printf("\t-t pss_track_threshold [Default %.2f]\n", track_threshold); printf("\t-l pss_track_len [Default %d]\n", track_len); - printf("\t-g gain [Default %.2f dB]\n", gain); + printf("\t-g gain [Default %.2f dB]\n", uhd_gain); printf("\t-v [set verbose to debug, default none]\n"); } @@ -123,7 +123,7 @@ void parse_args(int argc, char **argv) { track_threshold = atof(argv[optind]); break; case 'g': - gain = atof(argv[optind]); + uhd_gain = atof(argv[optind]); break; case 'v': verbose++; @@ -303,7 +303,7 @@ int main(int argc, char **argv) { INFO("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); cuhd_set_rx_srate(uhd, SAMP_FREQ); - cuhd_set_rx_gain(uhd, gain); + cuhd_set_rx_gain(uhd, uhd_gain); print_to_matlab(); From f5f1ee186eed3de5516ed568a89d2bd8446a8642 Mon Sep 17 00:00:00 2001 From: ismagom Date: Mon, 3 Mar 2014 21:01:04 +0000 Subject: [PATCH 04/25] Added VOLK support --- CMakeLists.txt | 4 +-- cmake/modules/FindVolk.cmake | 29 +++++++++++++++ examples/mib_track.c | 68 ++++++++++++++++++------------------ lte/lib/CMakeLists.txt | 18 +++++++++- lte/lib/utils/src/vector.c | 16 ++++----- 5 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 cmake/modules/FindVolk.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 411885780..d1389040e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,8 +51,8 @@ SET(DATA_DIR share/${CPACK_PACKAGE_NAME}) IF(NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Release") - MESSAGE(STATUS "Build type not specified: defaulting to release.") + SET(CMAKE_BUILD_TYPE Release) + MESSAGE(STATUS "Build type not specified: defaulting to Release.") ENDIF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") diff --git a/cmake/modules/FindVolk.cmake b/cmake/modules/FindVolk.cmake new file mode 100644 index 000000000..3da43592d --- /dev/null +++ b/cmake/modules/FindVolk.cmake @@ -0,0 +1,29 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_VOLK volk QUIET) + +FIND_PATH( + VOLK_INCLUDE_DIRS + NAMES volk.h + HINTS $ENV{VOLK_DIR}/include/volk + ${CMAKE_INSTALL_PREFIX}/include/volk + ${PC_VOLK_INCLUDE_DIR} + PATHS /usr/local/include/volk + /usr/include/volk +) + +FIND_LIBRARY( + VOLK_LIBRARIES + NAMES volk + HINTS $ENV{VOLK_DIR}/lib + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + ${PC_VOLK_LIBDIR} + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(VOLK DEFAULT_MSG VOLK_LIBRARIES VOLK_INCLUDE_DIRS) +MARK_AS_ADVANCED(VOLK_LIBRARIES VOLK_INCLUDE_DIRS) diff --git a/examples/mib_track.c b/examples/mib_track.c index f33a7cdf7..d41c7df83 100644 --- a/examples/mib_track.c +++ b/examples/mib_track.c @@ -154,6 +154,18 @@ int base_init(int frame_length) { if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { return -1; } + } else { + /* open UHD device */ + #ifndef DISABLE_UHD + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args,&uhd)) { + fprintf(stderr, "Error opening uhd\n"); + return -1; + } + #else + printf("Error UHD not configured. Select an input file\n"); + return -1; + #endif } input_buffer = (cf_t*) malloc(frame_length * sizeof(cf_t)); @@ -193,26 +205,18 @@ int base_init(int frame_length) { return -1; } - /* open UHD device */ -#ifndef DISABLE_UHD - printf("Opening UHD device...\n"); - if (cuhd_open(uhd_args,&uhd)) { - fprintf(stderr, "Error opening uhd\n"); - return -1; - } -#endif return 0; } void base_free() { int i; -#ifndef DISABLE_UHD - cuhd_close(&uhd); -#endif - if (input_file_name) { filesource_free(&fsrc); + } else { + #ifndef DISABLE_UHD + cuhd_close(&uhd); + #endif } sync_free(&sfind); @@ -280,6 +284,7 @@ int main(int argc, char **argv) { int nslot; pbch_mib_t mib; float cfo; + int n; int nof_found_mib = 0; parse_args(argc,argv); @@ -292,11 +297,20 @@ int main(int argc, char **argv) { sync_pss_det_peakmean(&sfind); sync_pss_det_peakmean(&strack); -#ifndef DISABLE_UHD - INFO("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); - cuhd_set_rx_srate(uhd, SAMP_FREQ); - cuhd_set_rx_gain(uhd, uhd_gain); -#endif + if (!input_file_name) { + #ifndef DISABLE_UHD + INFO("Setting sampling frequency %.2f MHz\n", (float) SAMP_FREQ/MHZ); + cuhd_set_rx_srate(uhd, SAMP_FREQ); + cuhd_set_rx_gain(uhd, uhd_gain); + /* set uhd_freq */ + cuhd_set_rx_freq(uhd, (double) uhd_freq); + cuhd_rx_wait_lo_locked(uhd); + DEBUG("Set uhd_freq to %.3f MHz\n", (double) uhd_freq); + + DEBUG("Starting receiver...\n",0); + cuhd_start_rx_stream(uhd); + #endif + } state = FIND; nslot = 0; @@ -308,21 +322,8 @@ int main(int argc, char **argv) { sync_set_threshold(&sfind, find_threshold); sync_force_N_id_2(&sfind, -1); -#ifndef DISABLE_UHD - /* set uhd_freq */ - cuhd_set_rx_freq(uhd, (double) uhd_freq); - cuhd_rx_wait_lo_locked(uhd); - DEBUG("Set uhd_freq to %.3f MHz\n", (double) uhd_freq); - - DEBUG("Starting receiver...\n",0); - cuhd_start_rx_stream(uhd); -#endif - while(frame_cnt < nof_frames || nof_frames==-1) { INFO(" ----- RECEIVING %d SAMPLES ---- \n", FLEN); -#ifndef DISABLE_UHD - cuhd_recv(uhd, input_buffer, FLEN, 1); -#else if (input_file_name) { n = filesource_read(&fsrc, input_buffer, FLEN); if (n == -1) { @@ -333,11 +334,10 @@ int main(int argc, char **argv) { filesource_read(&fsrc, input_buffer, FLEN); } } else { - fprintf(stderr, "UHD is DISABLED but input file name not specified\n"); - usage(argv[0]); - exit(-1); + #ifndef DISABLE_UHD + cuhd_recv(uhd, input_buffer, FLEN, 1); + #endif } -#endif switch(state) { case FIND: diff --git a/lte/lib/CMakeLists.txt b/lte/lib/CMakeLists.txt index 5f4be9ed0..6d3888e69 100644 --- a/lte/lib/CMakeLists.txt +++ b/lte/lib/CMakeLists.txt @@ -25,8 +25,15 @@ ######################################################################## FIND_PACKAGE(FFTW3F REQUIRED) # TODO: distribute kissfft instead -include_directories(${FFTW3F_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +IF(${DISABLE_VOLK}) + IF(${DISABLE_VOLK} EQUAL 0) + FIND_PACKAGE(Volk) + ENDIF(${DISABLE_VOLK} EQUAL 0) +ELSE(${DISABLE_VOLK}) + FIND_PACKAGE(Volk) +ENDIF(${DISABLE_VOLK}) ######################################################################## # Recurse subdirectories and compile all source files into the same lib @@ -46,6 +53,15 @@ TARGET_LINK_LIBRARIES(lte m ${FFTW3F_LIBRARIES}) INSTALL(TARGETS lte DESTINATION ${LIBRARY_DIR}) LIBLTE_SET_PIC(lte) +IF(VOLK_FOUND) + INCLUDE_DIRECTORIES(${VOLK_INCLUDE_DIRS}) + SET_TARGET_PROPERTIES(lte PROPERTIES COMPILE_DEFINITIONS "HAVE_VOLK") + TARGET_LINK_LIBRARIES(lte ${VOLK_LIBRARIES}) + MESSAGE(STATUS " Compiling with VOLK SIMD library.") +ELSE(VOLK_FOUND) + MESSAGE(STATUS " VOLK SIMD library NOT found. Using generic implementation.") +ENDIF(VOLK_FOUND) + diff --git a/lte/lib/utils/src/vector.c b/lte/lib/utils/src/vector.c index e45c5b477..1fb0c55fe 100644 --- a/lte/lib/utils/src/vector.c +++ b/lte/lib/utils/src/vector.c @@ -31,8 +31,6 @@ #include #include -//#define HAVE_VOLK - #ifdef HAVE_VOLK #include "volk/volk.h" #endif @@ -56,7 +54,7 @@ float vec_acc_ff(float *x, int len) { return z; #else float result; - volk_32f_accumulator_s32f_a(&result,x,(unsigned int) len); + volk_32f_accumulator_s32f_u(&result,x,(unsigned int) len); return result; #endif } @@ -94,7 +92,7 @@ void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len) { cf_t hh; __real__ hh = h; __imag__ hh = 0; - volk_32fc_s32fc_multiply_32fc_a(z,x,hh,(unsigned int) len); + volk_32fc_s32fc_multiply_32fc_u(z,x,hh,(unsigned int) len); #endif } @@ -105,7 +103,7 @@ void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len) { z[i] = x[i]*h; } #else - volk_32fc_s32fc_multiply_32fc_a(z,x,h,(unsigned int) len); + volk_32fc_s32fc_multiply_32fc_u(z,x,h,(unsigned int) len); #endif } @@ -170,7 +168,7 @@ void vec_conj_cc(cf_t *x, cf_t *y, int len) { y[i] = conjf(x[i]); } #else - volk_32fc_conjugate_32fc_a(y,x,(unsigned int) len); + volk_32fc_conjugate_32fc_u(y,x,(unsigned int) len); #endif } @@ -181,7 +179,7 @@ void vec_prod_ccc(cf_t *x,cf_t *y, cf_t *z, int len) { z[i] = x[i]*y[i]; } #else - volk_32fc_x2_multiply_32fc_a(z,x,y,(unsigned int) len); + volk_32fc_x2_multiply_32fc_u(z,x,y,(unsigned int) len); #endif } @@ -220,7 +218,7 @@ void vec_abs_cf(cf_t *x, float *abs, int len) { abs[i] = cabsf(x[i]); } #else - volk_32fc_magnitude_32f_a(abs,x,(unsigned int) len); + volk_32fc_magnitude_32f_u(abs,x,(unsigned int) len); #endif @@ -240,7 +238,7 @@ int vec_max_fi(float *x, int len) { return p; #else unsigned int target=0; - volk_32f_index_max_16u_a(&target,x,(unsigned int) len); + volk_32f_index_max_16u_u(&target,x,(unsigned int) len); return (int) target; #endif } From adf5260e29b12d21edcea50ee9d2cbe26842ecfc Mon Sep 17 00:00:00 2001 From: ismagom Date: Mon, 3 Mar 2014 23:54:02 +0000 Subject: [PATCH 05/25] CFO correction using LUT --- examples/mib_scan_usrp.c | 9 +- examples/mib_test.c | 10 +- examples/mib_track.c | 10 +- examples/synch_test.c | 8 +- lte/include/lte.h | 4 +- lte/include/lte/sync/cfo.h | 55 +++++++++ lte/include/lte/utils/{nco.h => cexptab.h} | 30 ++--- lte/lib/sync/src/cfo.c | 80 +++++++++++++ lte/lib/utils/src/cexptab.c | 80 +++++++++++++ lte/lib/utils/src/nco.c | 131 --------------------- 10 files changed, 262 insertions(+), 155 deletions(-) create mode 100644 lte/include/lte/sync/cfo.h rename lte/include/lte/utils/{nco.h => cexptab.h} (63%) create mode 100644 lte/lib/sync/src/cfo.c create mode 100644 lte/lib/utils/src/cexptab.c delete mode 100644 lte/lib/utils/src/nco.c diff --git a/examples/mib_scan_usrp.c b/examples/mib_scan_usrp.c index 3ee312193..16654ff53 100644 --- a/examples/mib_scan_usrp.c +++ b/examples/mib_scan_usrp.c @@ -65,6 +65,7 @@ pbch_t pbch; lte_fft_t fft; chest_t chest; sync_t sfind, strack; +cfo_t cfocorr; float *cfo_v; int *idx_v, *idx_valid, *t; @@ -182,6 +183,10 @@ int base_init(int frame_length) { fprintf(stderr, "Error initializing FFT\n"); return -1; } + if (cfo_init(&cfocorr, FLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } idx_v = malloc(nof_frames_track * sizeof(int)); if (!idx_v) { @@ -229,10 +234,12 @@ void base_free() { #ifndef DISABLE_UHD cuhd_close(&uhd); #endif + sync_free(&sfind); sync_free(&strack); lte_fft_free(&fft); chest_free(&chest); + cfo_free(&cfocorr); free(input_buffer); free(fft_buffer); @@ -506,7 +513,7 @@ int main(int argc, char **argv) { // Correct CFO INFO("Correcting CFO=%.4f\n", cfo[freq]); - nco_cexp_f_direct(&input_buffer[FLEN], (-cfo[freq])/128, FLEN); + cfo_correct(&cfocorr, &input_buffer[FLEN], (-cfo[freq])/128); if (nslot == 0) { if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) { diff --git a/examples/mib_test.c b/examples/mib_test.c index dffce8274..8992952ce 100644 --- a/examples/mib_test.c +++ b/examples/mib_test.c @@ -49,6 +49,7 @@ pbch_t pbch; lte_fft_t fft; chest_t chest; sync_t synch; +cfo_t cfocorr; void usage(char *prog) { printf("Usage: %s [onlt] -i input_file\n", prog); @@ -122,6 +123,11 @@ int base_init() { } } + if (cfo_init(&cfocorr, FLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } + if (chest_init(&chest, LINEAR, CPNORM, 6, NOF_PORTS)) { fprintf(stderr, "Error initializing equalizer\n"); return -1; @@ -265,8 +271,10 @@ int main(int argc, char **argv) { fprintf(stderr, "Error reading %d samples\n", FLEN); break; } + INFO("Correcting CFO=%.4f\n", cfo); - nco_cexp_f_direct(input_buffer, -cfo/128, FLEN); + cfo_correct(&cfocorr, input_buffer, -cfo/128); + switch(state) { case SYNC: INFO("State Sync, Slot idx=%d\n", frame_cnt); diff --git a/examples/mib_track.c b/examples/mib_track.c index d41c7df83..6273f7032 100644 --- a/examples/mib_track.c +++ b/examples/mib_track.c @@ -64,6 +64,7 @@ pbch_t pbch; lte_fft_t fft; chest_t chest; sync_t sfind, strack; +cfo_t cfocorr; plot_real_t poutfft; plot_complex_t pce; @@ -200,6 +201,11 @@ int base_init(int frame_length) { return -1; } + if (cfo_init(&cfocorr, FLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } + if (lte_fft_init(&fft, CPNORM, 6)) { fprintf(stderr, "Error initializing FFT\n"); return -1; @@ -223,6 +229,7 @@ void base_free() { sync_free(&strack); lte_fft_free(&fft); chest_free(&chest); + cfo_free(&cfocorr); free(input_buffer); free(fft_buffer); @@ -385,7 +392,8 @@ int main(int argc, char **argv) { // Correct CFO INFO("Correcting CFO=%.4f\n", cfo); - nco_cexp_f_direct(input_buffer, (-cfo)/128, FLEN); + + cfo_correct(&cfocorr, input_buffer, -cfo/128); if (nslot == 0) { INFO("Finding MIB at idx %d\n", find_idx); diff --git a/examples/synch_test.c b/examples/synch_test.c index 216ab74a9..babf3cdd4 100644 --- a/examples/synch_test.c +++ b/examples/synch_test.c @@ -106,6 +106,7 @@ int main(int argc, char **argv) { filesink_t fsink; pss_synch_t pss[3]; // One for each N_id_2 sss_synch_t sss[3]; // One for each N_id_2 + cfo_t cfocorr; int peak_pos[3]; float *cfo; float peak_value[3]; @@ -155,6 +156,11 @@ int main(int argc, char **argv) { exit(-1); } + if (cfo_init(&cfocorr, frame_length)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } + /* We have 2 options here: * a) We create 3 pss objects, each initialized with a different N_id_2 * b) We create 1 pss object which scans for each N_id_2 one after another. @@ -192,7 +198,7 @@ int main(int argc, char **argv) { gettimeofday(&tdata[1], NULL); if (force_cfo != CFO_AUTO) { - nco_cexp_f_direct(input, -force_cfo/128, frame_length); + cfo_correct(&cfocorr, input, -force_cfo/128); } if (force_N_id_2 != -1) { diff --git a/lte/include/lte.h b/lte/include/lte.h index 7e8d2a644..520003cac 100644 --- a/lte/include/lte.h +++ b/lte/include/lte.h @@ -38,7 +38,7 @@ #include "lte/utils/dft.h" #include "lte/utils/matrix.h" #include "lte/utils/mux.h" -#include "lte/utils/nco.h" +#include "lte/utils/cexptab.h" #include "lte/utils/pack.h" #include "lte/utils/vector.h" @@ -83,6 +83,6 @@ #include "lte/sync/sfo.h" #include "lte/sync/sss.h" #include "lte/sync/sync.h" - +#include "lte/sync/cfo.h" #endif diff --git a/lte/include/lte/sync/cfo.h b/lte/include/lte/sync/cfo.h new file mode 100644 index 000000000..76bc9aafa --- /dev/null +++ b/lte/include/lte/sync/cfo.h @@ -0,0 +1,55 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef _cfo_ +#define _cfo_ + +#include + +typedef _Complex float cf_t; + +/** If the frequency is changed more than the tolerance, a new table is generated */ +#define CFO_TOLERANCE 0.00001 + +#define CFO_CEXPTAB_SIZE 4096 + +typedef struct { + float last_freq; + float tol; + int nsamples; + cexptab_t tab; + cf_t *cur_cexp; +}cfo_t; + +int cfo_init(cfo_t *h, int nsamples); +void cfo_free(cfo_t *h); + +void cfo_set_tol(cfo_t *h, float tol); +void cfo_correct(cfo_t *h, cf_t *x, float freq); + +#endif diff --git a/lte/include/lte/utils/nco.h b/lte/include/lte/utils/cexptab.h similarity index 63% rename from lte/include/lte/utils/nco.h rename to lte/include/lte/utils/cexptab.h index 4fae2ec7a..2063eb0cd 100644 --- a/lte/include/lte/utils/nco.h +++ b/lte/include/lte/utils/cexptab.h @@ -26,28 +26,22 @@ */ -#ifndef NCO_ -#define NCO_ +#ifndef _cexptab_ +#define _cexptab_ #include +typedef _Complex float cf_t; + typedef struct { int size; - float *cost; - float *sint; -}nco_t; - -void nco_init(nco_t *nco, int size); -void nco_destroy(nco_t *nco); - -float nco_sin(nco_t *nco, float phase); -float nco_cos(nco_t *nco, float phase); -void nco_sincos(nco_t *nco, float phase, float *sin, float *cos); -_Complex float nco_cexp(nco_t *nco, float arg); - -void nco_sin_f(nco_t *nco, float *x, float freq, int len); -void nco_cos_f(nco_t *nco, float *x, float freq, int len); -void nco_cexp_f(nco_t *nco, _Complex float *x, float freq, int len); -void nco_cexp_f_direct(_Complex float *x, float freq, int len); + cf_t *tab; +}cexptab_t; + +int cexptab_init(cexptab_t *nco, int size); +void cexptab_free(cexptab_t *nco); + +void cexptab_gen(cexptab_t *nco, cf_t *x, float freq, int len); +void cexptab_gen_direct(cf_t *x, float freq, int len); #endif diff --git a/lte/lib/sync/src/cfo.c b/lte/lib/sync/src/cfo.c new file mode 100644 index 000000000..74f058151 --- /dev/null +++ b/lte/lib/sync/src/cfo.c @@ -0,0 +1,80 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include + +#include "lte/utils/cexptab.h" +#include "lte/sync/cfo.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" + +int cfo_init(cfo_t *h, int nsamples) { + int ret = -1; + bzero(h, sizeof(cfo_t)); + + if (cexptab_init(&h->tab, CFO_CEXPTAB_SIZE)) { + goto clean; + } + h->cur_cexp = malloc(sizeof(cf_t) * nsamples); + if (!h->cur_cexp) { + goto clean; + } + h->tol = CFO_TOLERANCE; + h->last_freq = 0; + h->nsamples = nsamples; + cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); + + ret = 0; +clean: + if (ret == -1) { + cfo_free(h); + } + return ret; +} + +void cfo_free(cfo_t *h) { + cexptab_free(&h->tab); + if (h->cur_cexp) { + free(h->cur_cexp); + } + bzero(h, sizeof(cf_t)); +} + +void cfo_set_tol(cfo_t *h, float tol) { + h->tol = tol; +} + +void cfo_correct(cfo_t *h, cf_t *x, float freq) { + if (fabs(h->last_freq - freq) > h->tol) { + h->last_freq = freq; + cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); + INFO("CFO generating new table for frequency %.4f\n", freq); + } + vec_prod_ccc(h->cur_cexp, x, x, h->nsamples); +} diff --git a/lte/lib/utils/src/cexptab.c b/lte/lib/utils/src/cexptab.c new file mode 100644 index 000000000..235b69389 --- /dev/null +++ b/lte/lib/utils/src/cexptab.c @@ -0,0 +1,80 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "lte/utils/cexptab.h" + +int cexptab_init(cexptab_t *h, int size) { + int i; + + h->size = size; + h->tab = malloc(sizeof(cf_t) * size); + if (h->tab) { + for (i = 0; i < size; i++) { + h->tab[i] = cexpf(_Complex_I * 2 * M_PI * (float) i / size); + } + return 0; + } else { + return -1; + } +} + +void cexptab_free(cexptab_t *h) { + if (h->tab) { + free(h->tab); + } + bzero(h, sizeof(cexptab_t)); +} + +void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { + int i; + unsigned int idx; + float phase_inc = freq * h->size; + float phase=0; + + for (i = 0; i < len; i++) { + idx = (unsigned int) phase; + x[i] = h->tab[idx]; + phase += phase_inc; + if (phase >= (float) h->size) { + phase -= (float) h->size; + } + } +} + +void cexptab_gen_direct(cf_t *x, float freq, int len) { + int i; + for (i = 0; i < len; i++) { + x[i] = cexpf(_Complex_I * 2 * M_PI * freq * i); + } +} + diff --git a/lte/lib/utils/src/nco.c b/lte/lib/utils/src/nco.c deleted file mode 100644 index 0d184715a..000000000 --- a/lte/lib/utils/src/nco.c +++ /dev/null @@ -1,131 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2014 The libLTE Developers. See the - * COPYRIGHT file at the top-level directory of this distribution. - * - * \section LICENSE - * - * This file is part of the libLTE library. - * - * libLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * libLTE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * A copy of the GNU Lesser General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - - - - -#include -#include -#include -#include -#include - -#include "lte/utils/nco.h" - -void nco_init(nco_t *nco, int size) { - int i; - - nco->size=size; - nco->cost=malloc(size*sizeof(float)); - nco->sint=malloc(size*sizeof(float)); - assert(nco->cost && nco->sint); - - for (i=0;icost[i] = cosf(2*M_PI*i/size); - nco->sint[i] = sinf(2*M_PI*i/size); - } -} - -void nco_destroy(nco_t *nco) { - if (nco->cost) { - free(nco->cost); - } - if (nco->sint) { - free(nco->sint); - } - nco->size=0; - bzero(nco, sizeof(nco_t)); -} - -unsigned int nco_idx(float phase, int size) { - while(phase>=2*M_PI) { - phase-=2*M_PI; - } - unsigned int idx = (unsigned int) (phase*size/(2*M_PI)); - return idx; -} - -inline float nco_sin(nco_t *nco, float phase) { - return nco->sint[nco_idx(phase,nco->size)]; -} -inline float nco_cos(nco_t *nco, float phase) { - return nco->cost[nco_idx(phase,nco->size)]; -} -inline void nco_sincos(nco_t *nco, float phase, float *sin, float *cos) { - unsigned int idx = nco_idx(phase,nco->size); - *sin = nco->sint[idx]; - *cos = nco->cost[idx]; -} - -inline _Complex float nco_cexp(nco_t *nco, float arg) { - float s,c; - nco_sincos(nco,arg,&s,&c); - return c+I*s; -} - -void nco_sin_f(nco_t *nco, float *x, float freq, int len) { - int i; - unsigned int idx; - - idx=0; - for (i=0;isize/len))%nco->size; - x[i] = nco->sint[idx]; - } -} - - -void nco_cos_f(nco_t *nco, float *x, float freq, int len) { - int i; - unsigned int idx; - - idx=0; - for (i=0;isize/len))%nco->size; - x[i] = nco->cost[idx]; - } -} - - -void nco_cexp_f(nco_t *nco, _Complex float *x, float freq, int len) { - int i; - unsigned int idx; - - idx=0; - for (i=0;isize/len))%nco->size; - x[i] = nco->cost[idx] + I*nco->sint[idx]; - } -} - -void nco_cexp_f_direct(_Complex float *x, float freq, int len) { - int i; - for (i=0;i Date: Tue, 4 Mar 2014 11:55:55 +0000 Subject: [PATCH 06/25] Graphics/UHD optional to compile enodeb_bch/mib_track examples --- CMakeLists.txt | 47 ------------ examples/CMakeLists.txt | 47 +++++++++--- examples/enodeb_bch.c | 29 ++++--- examples/mib_track.c | 49 +++++++++--- graphics/lib/CMakeLists.txt | 75 ++++++++++++++++--- graphics/lib/common/WaterfallData.h | 6 +- graphics/lib/complexplot/ComplexWidget.cpp | 2 - graphics/lib/realplot/RealWidget.cpp | 2 - graphics/lib/scatterplot/ScatterWidget.cpp | 1 - .../lib/waterfallplot/WaterfallWidget.cpp | 2 - 10 files changed, 159 insertions(+), 101 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1389040e..ad7f6b782 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ IF(MSVC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE ) - LIST(APPEND IRIS_CORE_COMMON_FLAGS_AND_DEFINES -DHAVE_CONFIG_H) ADD_DEFINITIONS(/MP) #build with multiple processors ENDIF(MSVC) @@ -87,52 +86,6 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") -######################################################################## -# Setup Boost -######################################################################## -MESSAGE(STATUS "") -MESSAGE(STATUS "Configuring Boost C++ Libraries...") -SET(BOOST_REQUIRED_COMPONENTS - program_options - system - thread - unit_test_framework -) - -IF(UNIX AND EXISTS "/usr/lib64") - LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix -ENDIF(UNIX AND EXISTS "/usr/lib64") - -IF(MSVC) - SET(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") - IF(BOOST_ALL_DYN_LINK) - ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc - ELSE(BOOST_ALL_DYN_LINK) - UNSET(BOOST_REQUIRED_COMPONENTS) #empty components list for static link - ENDIF(BOOST_ALL_DYN_LINK) -ENDIF(MSVC) - -SET(Boost_ADDITIONAL_VERSIONS - "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" "1.40.0" "1.40" - "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" - "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" "1.45.0" "1.45" - "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" - "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53") -FIND_PACKAGE(Boost 1.37 REQUIRED ${BOOST_REQUIRED_COMPONENTS}) -MESSAGE(STATUS "Boost version: ${Boost_VERSION}") - -IF(Boost_VERSION LESS 104600) - ADD_DEFINITIONS( -DBOOST_FILESYSTEM_VERSION=2 ) #use filesystem version 2 in boost < 1.46 - MESSAGE(STATUS "Using Boost Filesystem V2") -ENDIF(Boost_VERSION LESS 104600) - -INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) -LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) - -MESSAGE(STATUS "Boost include directories: ${Boost_INCLUDE_DIRS}") -MESSAGE(STATUS "Boost library directories: ${Boost_LIBRARY_DIRS}") -MESSAGE(STATUS "Boost libraries: ${Boost_LIBRARIES}") - ######################################################################## # Create uninstall targets diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6d535f4ed..a93eda319 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,7 +19,9 @@ # and at http://www.gnu.org/licenses/. # +################################################################# # TO BE MOVED TO UNIT TESTS +################################################################# add_executable(hl_example hl_example.c) target_link_libraries(hl_example lte) @@ -39,20 +41,47 @@ target_link_libraries(viterbi_test lte) add_executable(mib_test mib_test.c) target_link_libraries(mib_test lte) + +################################################################# +# Check if UHD C-API and Graphics library are available +################################################################# + LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND) LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND) +################################################################# +# These two can be compiled without UHD or graphics support +################################################################# + +add_executable(mib_track mib_track.c) +target_link_libraries(mib_track lte) + +add_executable(enodeb_bch enodeb_bch.c) +target_link_libraries(enodeb_bch lte) + +IF(${CUHD_FIND} EQUAL -1) + SET_TARGET_PROPERTIES(mib_track PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") + SET_TARGET_PROPERTIES(enodeb_bch PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") +ELSE(${CUHD_FIND} EQUAL -1) + target_link_libraries(mib_track cuhd) + target_link_libraries(enodeb_bch cuhd) +ENDIF(${CUHD_FIND} EQUAL -1) + +IF(${GRAPHICS_FIND} EQUAL -1) + SET_TARGET_PROPERTIES(mib_track PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") + SET_TARGET_PROPERTIES(enodeb_bch PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") +ELSE(${GRAPHICS_FIND} EQUAL -1) + target_link_libraries(mib_track graphics) + target_link_libraries(enodeb_bch graphics) +ENDIF(${GRAPHICS_FIND} EQUAL -1) + + + +################################################################# +# These examples need the UHD driver +################################################################# IF(${CUHD_FIND} GREATER -1) - IF(${GRAPHICS_FIND} GREATER -1) - add_executable(mib_track mib_track.c) - target_link_libraries(mib_track lte cuhd graphics) - ELSE(${GRAPHICS_FIND} GREATER -1) - MESSAGE(STATUS " mib_track is ignored: GRAPHICS library not compiled.") - ENDIF(${GRAPHICS_FIND} GREATER -1) - - add_executable(enodeb_bch enodeb_bch.c) - target_link_libraries(enodeb_bch lte cuhd ) add_executable(rssi_scan_usrp rssi_scan_usrp.c) target_link_libraries(rssi_scan_usrp lte cuhd ) diff --git a/examples/enodeb_bch.c b/examples/enodeb_bch.c index e92fe1e77..133bfe568 100644 --- a/examples/enodeb_bch.c +++ b/examples/enodeb_bch.c @@ -33,11 +33,9 @@ #include "lte.h" -#define ENABLE_UHD - -#ifdef ENABLE_UHD -#include "cuhd.h" -void *uhd; +#ifndef DISABLE_UHD + #include "cuhd.h" + void *uhd; #endif char *output_file_name = NULL; @@ -59,10 +57,14 @@ int slot_n_re, slot_n_samples; void usage(char *prog) { printf("Usage: %s [agmfoncvp]\n", prog); +#ifndef DISABLE_UHD printf("\t-a UHD args [Default %s]\n", uhd_args); printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain); printf("\t-m UHD signal amplitude [Default %.2f]\n", uhd_amp); printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq/1000000); +#else + printf("\t UHD is disabled. CUHD library not available\n"); +#endif printf("\t-o output_file [Default USRP]\n"); printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-c cell id [Default %d]\n", cell_id); @@ -106,7 +108,7 @@ void parse_args(int argc, char **argv) { exit(-1); } } -#ifndef ENABLE_UHD +#ifdef DISABLE_UHD if (!output_file_name) { usage(argv[0]); exit(-1); @@ -133,16 +135,18 @@ void base_init() { exit(-1); } } else { -#ifdef ENABLE_UHD +#ifndef DISABLE_UHD printf("Opening UHD device...\n"); if (cuhd_open(uhd_args,&uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } #else - exit(-1); // not supposed to be here + printf("Error UHD not available. Select an output file\n"); + exit(-1); #endif } + /* create ifft object */ if (lte_ifft_init(&ifft, CPNORM, nof_prb)) { fprintf(stderr, "Error creating iFFT object\n"); @@ -169,7 +173,7 @@ void base_free() { if (output_file_name) { filesink_free(&fsink); } else { -#ifdef ENABLE_UHD +#ifndef DISABLE_UHD cuhd_close(&uhd); #endif } @@ -186,7 +190,7 @@ int main(int argc, char **argv) { cf_t *slot1_symbols[MAX_PORTS_CTRL]; -#ifndef ENABLE_UHD +#ifdef DISABLE_UHD if (argc < 3) { usage(argv[0]); exit(-1); @@ -224,7 +228,7 @@ int main(int argc, char **argv) { slot1_symbols[i] = slot_buffer; } -#ifdef ENABLE_UHD +#ifndef DISABLE_UHD if (!output_file_name) { printf("Set TX rate: %.2f MHz\n", cuhd_set_tx_srate(uhd, UHD_SAMP_FREQ)/1000000); printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, uhd_gain)); @@ -260,8 +264,9 @@ int main(int argc, char **argv) { /* send to file or usrp */ if (output_file_name) { filesink_write(&fsink, output_buffer, slot_n_samples); + usleep(5000); } else { -#ifdef ENABLE_UHD +#ifndef DISABLE_UHD vec_sc_prod_cfc(output_buffer, uhd_amp, output_buffer, slot_n_samples); cuhd_send(uhd, output_buffer, slot_n_samples, 1); #endif diff --git a/examples/mib_track.c b/examples/mib_track.c index 6273f7032..29d31d422 100644 --- a/examples/mib_track.c +++ b/examples/mib_track.c @@ -35,12 +35,19 @@ #include #include +#include "lte.h" + #ifndef DISABLE_UHD -#include "cuhd.h" + #include "cuhd.h" + void *uhd; #endif -#include "lte.h" -#include "plot.h" +#ifndef DISABLE_GRAPHICS + #include "plot.h" + plot_real_t poutfft; + plot_complex_t pce; + plot_scatter_t pscatrecv, pscatequal; +#endif #define MHZ 1000000 #define SAMP_FREQ 1920000 @@ -66,22 +73,25 @@ chest_t chest; sync_t sfind, strack; cfo_t cfocorr; -plot_real_t poutfft; -plot_complex_t pce; -plot_scatter_t pscatrecv, pscatequal; - -void *uhd; enum sync_state {FIND, TRACK}; void usage(char *prog) { printf("Usage: %s [iagfndv]\n", prog); printf("\t-i input_file [Default use USRP]\n"); +#ifndef DISABLE_UHD printf("\t-a UHD args [Default %s]\n", uhd_args); printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); printf("\t-f UHD RX frequency [Default %.1f MHz]\n", uhd_freq/1000000); +#else + printf("\t UHD is disabled. CUHD library not available\n"); +#endif printf("\t-n nof_frames [Default %d]\n", nof_frames); +#ifndef DISABLE_GRAPHICS printf("\t-d disable plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif printf("\t-v [set verbose to debug, default none]\n"); } @@ -117,6 +127,8 @@ void parse_args(int argc, char **argv) { } } +#ifndef DISABLE_GRAPHICS + void init_plots() { plot_init(); plot_real_init(&poutfft); @@ -141,15 +153,20 @@ void init_plots() { plot_scatter_setTitle(&pscatequal, "Equalized Symbols"); plot_scatter_setXAxisScale(&pscatequal, -1, 1); plot_scatter_setYAxisScale(&pscatequal, -1, 1); - } +#endif + int base_init(int frame_length) { int i; +#ifndef DISABLE_GRAPHICS if (!disable_plots) { init_plots(); } +#else + printf("-- PLOTS are disabled. Graphics library not available --\n\n"); +#endif if (input_file_name) { if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { @@ -164,7 +181,7 @@ int base_init(int frame_length) { return -1; } #else - printf("Error UHD not configured. Select an input file\n"); + printf("Error UHD not available. Select an input file\n"); return -1; #endif } @@ -257,7 +274,6 @@ int mib_decoder_init(int cell_id) { int mib_decoder_run(cf_t *input, pbch_mib_t *mib) { int i, n; lte_fft_run(&fft, input, fft_buffer); - float tmp[72*7]; /* Get channel estimates for each port */ for (i=0;i #include #include -#include "irisapi/Exceptions.h" - +#include class WaterfallData :public QwtRasterData @@ -34,8 +33,7 @@ public: void appendData(double* data, int n) { - if(n != nData_) - throw iris::InvalidDataException("WaterfallData: invalid data length"); + assert(n == nData_); VecPtr v = data_.front(); v->assign(data, data+n); diff --git a/graphics/lib/complexplot/ComplexWidget.cpp b/graphics/lib/complexplot/ComplexWidget.cpp index da56a3aaa..39646fc37 100644 --- a/graphics/lib/complexplot/ComplexWidget.cpp +++ b/graphics/lib/complexplot/ComplexWidget.cpp @@ -4,10 +4,8 @@ #include #include -#include using namespace std; -namespace bl = boost::lambda; ComplexWidget::ComplexWidget(QWidget *parent) :QWidget(parent) diff --git a/graphics/lib/realplot/RealWidget.cpp b/graphics/lib/realplot/RealWidget.cpp index ce53fa7fa..85b10844d 100644 --- a/graphics/lib/realplot/RealWidget.cpp +++ b/graphics/lib/realplot/RealWidget.cpp @@ -4,10 +4,8 @@ #include #include -#include using namespace std; -namespace bl = boost::lambda; RealWidget::RealWidget(QWidget *parent) :QWidget(parent) diff --git a/graphics/lib/scatterplot/ScatterWidget.cpp b/graphics/lib/scatterplot/ScatterWidget.cpp index 268bf49a4..f3f3542a6 100644 --- a/graphics/lib/scatterplot/ScatterWidget.cpp +++ b/graphics/lib/scatterplot/ScatterWidget.cpp @@ -4,7 +4,6 @@ #include #include -#include using namespace std; diff --git a/graphics/lib/waterfallplot/WaterfallWidget.cpp b/graphics/lib/waterfallplot/WaterfallWidget.cpp index a3d789bcd..89846f04b 100644 --- a/graphics/lib/waterfallplot/WaterfallWidget.cpp +++ b/graphics/lib/waterfallplot/WaterfallWidget.cpp @@ -6,10 +6,8 @@ #include #include #include -#include using namespace std; -namespace bl = boost::lambda; WaterfallWidget::WaterfallWidget(int numDataPoints, int numRows, QWidget *parent) From e66fe3102cbdf363358b4cffd3ed927bda933b33 Mon Sep 17 00:00:00 2001 From: ismagom Date: Tue, 4 Mar 2014 19:33:06 +0000 Subject: [PATCH 07/25] Fixed compilation problem for old GCXX --- CMakeLists.txt | 2 +- graphics/lib/complexplot/plot_complex.cpp | 2 +- graphics/lib/scatterplot/plot_scatter.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad7f6b782..89c5bb254 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") # Compiler specific setup ######################################################################## IF(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-format-extra-args -Wno-reorder -Winline -Wno-unused-result -Wno-format -D_GNU_SOURCE") +# do something ENDIF(CMAKE_COMPILER_IS_GNUCXX) IF(CMAKE_COMPILER_IS_GNUCC) diff --git a/graphics/lib/complexplot/plot_complex.cpp b/graphics/lib/complexplot/plot_complex.cpp index 8fca481aa..0f6b07c86 100644 --- a/graphics/lib/complexplot/plot_complex.cpp +++ b/graphics/lib/complexplot/plot_complex.cpp @@ -30,7 +30,7 @@ #include "plot/plot_complex.h" #include "Complexplot.h" -#include +#include int plot_complex_init(plot_complex_t *h) { diff --git a/graphics/lib/scatterplot/plot_scatter.cpp b/graphics/lib/scatterplot/plot_scatter.cpp index 35624199f..b3d36986f 100644 --- a/graphics/lib/scatterplot/plot_scatter.cpp +++ b/graphics/lib/scatterplot/plot_scatter.cpp @@ -29,7 +29,7 @@ #include "plot/plot_scatter.h" #include "Scatterplot.h" -#include +#include From 034b003a85c0996ae0f9c512e21a71437bdd978e Mon Sep 17 00:00:00 2001 From: ismagom Date: Tue, 4 Mar 2014 22:08:52 +0000 Subject: [PATCH 08/25] CMake chekcs for Qwt6. Fixed UHD close RX stream at exit --- cuhd/include/cuhd.h | 1 - cuhd/lib/cuhd_imp.cpp | 27 +++++---------------------- examples/enodeb_bch.c | 1 - examples/mib_track.c | 32 +++++++++++++++++++++++++------- graphics/lib/CMakeLists.txt | 11 +++++++---- 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/cuhd/include/cuhd.h b/cuhd/include/cuhd.h index 7e8b00d70..188f127c3 100644 --- a/cuhd/include/cuhd.h +++ b/cuhd/include/cuhd.h @@ -46,7 +46,6 @@ double cuhd_set_rx_gain(void *h, double gain); double cuhd_set_rx_freq(void *h, double freq); int cuhd_recv(void *h, void *data, int nsamples, int blocking); -int cuhd_start_tx_stream(void *h); double cuhd_set_tx_srate(void *h, double freq); double cuhd_set_tx_gain(void *h, double gain); double cuhd_set_tx_freq(void *h, double freq); diff --git a/cuhd/lib/cuhd_imp.cpp b/cuhd/lib/cuhd_imp.cpp index 0010bfefd..88e1ce27f 100644 --- a/cuhd/lib/cuhd_imp.cpp +++ b/cuhd/lib/cuhd_imp.cpp @@ -95,29 +95,23 @@ int cuhd_open(char *args, void **h) { cuhd_handler* handler = new cuhd_handler(); std::string _args=std::string(args); handler->usrp = uhd::usrp::multi_usrp::make(_args); - - //uhd::msg::register_handler(&my_handler); + handler->usrp->set_clock_source("internal"); std::string otw, cpu; otw="sc16"; cpu="fc32"; - - handler->usrp->set_clock_source("internal"); - uhd::stream_args_t stream_args(cpu, otw); -// stream_args.channels.push_back(0); -// stream_args.args["noclear"] = "1"; - handler->rx_stream = handler->usrp->get_rx_stream(stream_args); - *h = handler; - handler->tx_stream = handler->usrp->get_tx_stream(stream_args); + *h = handler; + return 0; } int cuhd_close(void *h) { - /** TODO */ + cuhd_stop_rx_stream(h); + /** Something else to close the USRP?? */ return 0; } @@ -160,17 +154,6 @@ int cuhd_recv(void *h, void *data, int nsamples, int blocking) { } } - - -int cuhd_start_tx_stream(void *h) { - cuhd_handler* handler = static_cast(h); - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - cmd.time_spec = handler->usrp->get_time_now(); - cmd.stream_now = true; - handler->usrp->issue_stream_cmd(cmd); - return 0; -} - double cuhd_set_tx_gain(void *h, double gain) { cuhd_handler* handler = static_cast(h); handler->usrp->set_tx_gain(gain); diff --git a/examples/enodeb_bch.c b/examples/enodeb_bch.c index 133bfe568..4250ae8a5 100644 --- a/examples/enodeb_bch.c +++ b/examples/enodeb_bch.c @@ -233,7 +233,6 @@ int main(int argc, char **argv) { printf("Set TX rate: %.2f MHz\n", cuhd_set_tx_srate(uhd, UHD_SAMP_FREQ)/1000000); printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, uhd_gain)); printf("Set TX freq: %.2f MHz\n", cuhd_set_tx_freq(uhd, uhd_freq)/1000000); - cuhd_start_tx_stream(uhd); } #endif diff --git a/examples/mib_track.c b/examples/mib_track.c index 29d31d422..4506ec893 100644 --- a/examples/mib_track.c +++ b/examples/mib_track.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "lte.h" @@ -62,6 +63,8 @@ int track_len=300; char *input_file_name = NULL; int disable_plots = 0; +int go_exit=0; + float uhd_freq = 2400000000.0, uhd_gain = 20.0; char *uhd_args = ""; @@ -238,10 +241,14 @@ void base_free() { filesource_free(&fsrc); } else { #ifndef DISABLE_UHD - cuhd_close(&uhd); + cuhd_close(uhd); #endif } +#ifndef DISABLE_GRAPHICS + plot_exit(); +#endif + sync_free(&sfind); sync_free(&strack); lte_fft_free(&fft); @@ -302,6 +309,10 @@ int mib_decoder_run(cf_t *input, pbch_mib_t *mib) { return n; } +void sigintHandler(int sig_num) +{ + go_exit=1; +} int main(int argc, char **argv) { int frame_cnt; @@ -346,6 +357,9 @@ int main(int argc, char **argv) { #endif } + printf("\n --- Press Ctrl+C to exit --- \n"); + signal(SIGINT, sigintHandler); + state = FIND; nslot = 0; find_idx = 0; @@ -356,7 +370,7 @@ int main(int argc, char **argv) { sync_set_threshold(&sfind, find_threshold); sync_force_N_id_2(&sfind, -1); - while(frame_cnt < nof_frames || nof_frames==-1) { + while(!go_exit && (frame_cnt < nof_frames || nof_frames==-1)) { INFO(" ----- RECEIVING %d SAMPLES ---- \n", FLEN); if (input_file_name) { n = filesource_read(&fsrc, input_buffer, FLEN); @@ -386,6 +400,7 @@ int main(int argc, char **argv) { sync_force_N_id_2(&strack, sync_get_N_id_2(&sfind)); cell_id = sync_get_cell_id(&sfind); mib_decoder_init(cell_id); + nof_found_mib = 0; nslot = sync_get_slot_id(&sfind); nslot=(nslot+10)%20; cfo = 0; @@ -393,7 +408,7 @@ int main(int argc, char **argv) { state = TRACK; } if (verbose == VERBOSE_NONE) { - printf("Tracking... PAR=%.2f\r", sync_get_peak_to_avg(&sfind)); + printf("Finding PSS... PAR=%.2f\r", sync_get_peak_to_avg(&sfind)); } break; case TRACK: @@ -408,13 +423,16 @@ int main(int argc, char **argv) { find_idx += track_idx - track_len; if (nslot != sync_get_slot_id(&strack)) { INFO("Expected slot %d but got %d\n", nslot, sync_get_slot_id(&strack)); - state = TRACK; + printf("\r\n");fflush(stdout); + state = FIND; } } - /* if we missed too many PSS go back to track */ + /* if we missed too many PSS go back to FIND */ if (frame_cnt - last_found > max_track_lost) { - INFO("%d frames lost. Going back to TRACK\n", frame_cnt - last_found); + INFO("%d frames lost. Going back to FIND", frame_cnt - last_found); + printf("\r\n");fflush(stdout); + state = FIND; } // Correct CFO @@ -454,7 +472,7 @@ int main(int argc, char **argv) { base_free(); - printf("\n\nDone\n"); + printf("\nBye\n"); exit(0); } diff --git a/graphics/lib/CMakeLists.txt b/graphics/lib/CMakeLists.txt index fc9b5b51d..65457ee23 100644 --- a/graphics/lib/CMakeLists.txt +++ b/graphics/lib/CMakeLists.txt @@ -100,8 +100,11 @@ ENDFOREACH(_module ${modules}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../../) +IF(QWT_MAJOR_VERSION LESS 6) + MESSAGE(STATUS "QWT6 is required.") +ENDIF(QWT_MAJOR_VERSION LESS 6) -IF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND) +IF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) QT4_WRAP_CPP(lineplotwraps common/Lineplot.h) QT4_WRAP_CPP(pointplotwraps common/Pointplot.h) QT4_WRAP_CPP(spectrogramplotwraps common/Spectrogramplot.h) @@ -128,11 +131,11 @@ IF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND) MESSAGE(STATUS " GRAPHICS library will be installed.") -ELSE(QT4_FOUND AND QWT_FOUND AND Boost_FOUND) +ELSE(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) - MESSAGE(STATUS " QT4/Qwt or Boost not found. GRAPHICS library is not generated") + MESSAGE(STATUS " QT4/Qwt6 or Boost not found. GRAPHICS library is not generated") -ENDIF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND) +ENDIF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) From c4e0e403ce56c19edaca190edd330dcfd6cd8d4a Mon Sep 17 00:00:00 2001 From: ismagom Date: Sat, 8 Mar 2014 11:46:19 +0000 Subject: [PATCH 09/25] Using CTest for testing --- CMakeLists.txt | 3 + CTestConfig.cmake | 13 + examples/CMakeLists.txt | 6 - examples/enodeb_bch.c | 8 +- examples/equalizer_test.c | 213 --------------- examples/mib_test.c | 10 +- examples/mib_track.c | 8 +- examples/rssi_scan_usrp.c | 8 +- examples/synch_test.c | 12 +- graphics/lib/complexplot/test/CMakeLists.txt | 3 +- .../lib/complexplot/test/complexplot_test.cpp | 174 ++++++------ graphics/lib/realplot/test/CMakeLists.txt | 3 +- graphics/lib/realplot/test/realplot_test.cpp | 137 +++++----- graphics/lib/scatterplot/test/CMakeLists.txt | 4 +- .../lib/scatterplot/test/scatterplot_test.cpp | 145 +++++----- .../waterfallplot/test/Waterfallplot_test.cpp | 149 ++++++----- lte/lib/CMakeLists.txt | 12 + lte/lib/ch_estimation/test/CMakeLists.txt | 31 +++ lte/lib/ch_estimation/test/chest_test.c | 251 ++++++++++++++++++ lte/lib/fec/test/CMakeLists.txt | 51 ++++ lte/lib/fec/test/crc_test.c | 119 +++++++++ lte/lib/fec/test/crc_test.h | 68 +++++ {examples => lte/lib/fec/test}/viterbi_test.c | 94 ++++--- lte/lib/fec/test/viterbi_test.h | 71 +++++ 24 files changed, 998 insertions(+), 595 deletions(-) create mode 100644 CTestConfig.cmake delete mode 100644 examples/equalizer_test.c create mode 100644 lte/lib/ch_estimation/test/CMakeLists.txt create mode 100644 lte/lib/ch_estimation/test/chest_test.c create mode 100644 lte/lib/fec/test/CMakeLists.txt create mode 100644 lte/lib/fec/test/crc_test.c create mode 100644 lte/lib/fec/test/crc_test.h rename {examples => lte/lib/fec/test}/viterbi_test.c (80%) create mode 100644 lte/lib/fec/test/viterbi_test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 89c5bb254..e3d4d66ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,9 @@ LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") INCLUDE(libLTEPackage) #setup cpack +include(CTest) +set( CTEST_MEMORYCHECK_COMMAND valgrind ) + ######################################################################## # Install Dirs ######################################################################## diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 000000000..32743e0ba --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "libLTE") +set(CTEST_NIGHTLY_START_TIME "00:00:00 GMT") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=libLTE") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a93eda319..b82f3fadd 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,12 +32,6 @@ target_link_libraries(ll_example lte) add_executable(synch_test synch_test.c) target_link_libraries(synch_test lte) -add_executable(equalizer_test equalizer_test.c) -target_link_libraries(equalizer_test lte) - -add_executable(viterbi_test viterbi_test.c) -target_link_libraries(viterbi_test lte) - add_executable(mib_test mib_test.c) target_link_libraries(mib_test lte) diff --git a/examples/enodeb_bch.c b/examples/enodeb_bch.c index 4250ae8a5..8a01c51d2 100644 --- a/examples/enodeb_bch.c +++ b/examples/enodeb_bch.c @@ -39,7 +39,7 @@ #endif char *output_file_name = NULL; -int nof_frames=-1; +int nof_slots=-1; int cell_id = 1; int nof_prb = 6; char *uhd_args = ""; @@ -66,7 +66,7 @@ void usage(char *prog) { printf("\t UHD is disabled. CUHD library not available\n"); #endif printf("\t-o output_file [Default USRP]\n"); - printf("\t-n number of frames [Default %d]\n", nof_frames); + printf("\t-n number of frames [Default %d]\n", nof_slots); printf("\t-c cell id [Default %d]\n", cell_id); printf("\t-p nof_prb [Default %d]\n", nof_prb); printf("\t-v [set verbose to debug, default none]\n"); @@ -92,7 +92,7 @@ void parse_args(int argc, char **argv) { output_file_name = argv[optind]; break; case 'n': - nof_frames = atoi(argv[optind]); + nof_slots = atoi(argv[optind]); break; case 'p': nof_prb = atoi(argv[optind]); @@ -238,7 +238,7 @@ int main(int argc, char **argv) { nf = 0; - while(nf -#include -#include -#include - -#include "lte.h" - -char *input_file_name; -int nof_frames=1; -int cell_id = 0; -int port_id = 0; -int nof_prb = 6; -lte_cp_t cp = CPNORM; -int file_binary = 0; - -int in_slot_length() { - if (CP_ISNORM(cp)) { - return SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb)); - } else { - return SLOT_LEN_CPEXT(lte_symbol_sz(nof_prb)); - } -} - -int slot_length() { - return CP_NSYMB(cp)*lte_symbol_sz(nof_prb); -} - - -void usage(char *prog) { - printf("Usage: %s [bncprev] -i input_file\n", prog); - printf("\t-b input file is binary [Default no]\n"); - printf("\t-n number of slots [Default %d]\n", nof_frames); - printf("\t-c cell_id [Default %d]\n", cell_id); - printf("\t-p port_id [Default %d]\n", port_id); - printf("\t-r nof_prb [Default %d]\n", nof_prb); - printf("\t-e [extended cyclic prefix, Default normal]\n"); - printf("\t-v [set verbose to debug, default none]\n"); -} - -void parse_args(int argc, char **argv) { - int opt; - while ((opt = getopt(argc, argv, "bincprev")) != -1) { - switch(opt) { - case 'b': - file_binary = 1; - break; - case 'i': - input_file_name = argv[optind]; - break; - case 'n': - nof_frames = atoi(argv[optind]); - break; - case 'c': - cell_id = atoi(argv[optind]); - break; - case 'p': - port_id = atoi(argv[optind]); - break; - case 'r': - nof_prb = atoi(argv[optind]); - break; - case 'e': - cp = CPEXT; - break; - case 'v': - PRINT_DEBUG; - break; - default: - usage(argv[0]); - exit(-1); - } - } - if (!input_file_name) { - usage(argv[0]); - exit(-1); - } -} - -int main(int argc, char **argv) { - filesource_t fsrc; - lte_fft_t fft; - FILE *f = NULL; - chest_t eq; - int slot_cnt; - cf_t *input = NULL; - cf_t *outfft = NULL; - cf_t *ce = NULL; - int i; - - if (argc < 3) { - usage(argv[0]); - exit(-1); - } - - parse_args(argc,argv); - - if (filesource_init(&fsrc, input_file_name, file_binary?COMPLEX_FLOAT_BIN:COMPLEX_FLOAT)) { - fprintf(stderr, "Error opening file %s\n", input_file_name); - goto do_exit; - } - f = fopen("output.m", "w"); - if (!f) { - perror("fopen"); - goto do_exit; - } - - input = malloc(in_slot_length()*sizeof(cf_t)); - if (!input) { - perror("malloc"); - goto do_exit; - } - outfft = malloc(slot_length()*sizeof(cf_t)); - if (!outfft) { - perror("malloc"); - goto do_exit; - } - ce = malloc(nof_prb * RE_X_RB * CP_NSYMB(cp) * sizeof(cf_t)); - if (!ce) { - perror("malloc"); - goto do_exit; - } - - if (lte_fft_init(&fft, cp, nof_prb)) { - fprintf(stderr, "Error: initializing FFT\n"); - goto do_exit; - } - if (chest_init(&eq, LINEAR, cp, nof_prb, port_id+1)) { - fprintf(stderr, "Error initializing equalizer\n"); - goto do_exit; - } - if (chest_ref_LTEDL(&eq, cell_id)) { - fprintf(stderr, "Error initializing reference signal\n"); - goto do_exit; - } - - bzero(input, sizeof(cf_t) * in_slot_length()); - bzero(outfft, sizeof(cf_t) * slot_length()); - - fprintf(f, "ce=zeros(%d, %d);\n", nof_frames * CP_NSYMB(cp), nof_prb * RE_X_RB); - /* read all file or nof_slots */ - slot_cnt = 0; - while (in_slot_length() == filesource_read(&fsrc, input, in_slot_length()) - && (slot_cnt < nof_frames || nof_frames == -1)) { - - fprintf(f, "infft="); - vec_fprint_c(f, input, in_slot_length()); - fprintf(f, ";\n"); - - lte_fft_run(&fft, input, outfft); - - fprintf(f, "outfft="); - vec_fprint_c(f, outfft, CP_NSYMB(cp) * nof_prb * RE_X_RB); - fprintf(f, ";\n"); - - chest_ce_slot_port(&eq, outfft, ce, slot_cnt%20, port_id); - - chest_fprint(&eq, f, slot_cnt%20, port_id); - - for (i=0;i -#include -#include -#include +#include +#include +#include #include #include #include #include -#include #define PI 3.14159265358979323846 using namespace std; -typedef vector< complex > FloatVec; - -void threadMain1() -{ - Complexplot plot; - plot.setTitle("Float"); - plot.setXAxisRange(0,2); - plot.setYAxisScale(Complexplot::Magnitude, 0.9, 1.1); - - int n=1024; - float step = 2.0*PI/n; - complex* data = new complex[n]; - for(int i=0;i* data = new complex[n]; - for(int i=0;i > FloatVec; -void threadMain3() -{ +void *threadMain1(void *arg) { + Complexplot plot; + plot.setTitle("Float"); + plot.setXAxisRange(0, 2); + plot.setYAxisScale(Complexplot::Magnitude, 0.9, 1.1); - Complexplot plot; - plot.setTitle("FloatVec"); - plot.setXAxisRange(0,2); - plot.setYAxisScale(Complexplot::Magnitude, 0.9, 1.1); + int n = 1024; + float step = 2.0 * PI / n; + complex* data = new complex [n]; + for (int i = 0; i < n; i++) + data[i] = polar(1.0f, step * i); - FloatVec data(1024); - int n=data.size(); - float step = 2.0*PI/n; - for(int i=0;i* data = new complex [n]; + for (int i = 0; i < n; i++) + data[i] = polar(1.0, step * i); + + plot.setNewData(data, n); + + for (int i = 0; i < n; i++) { + rotate(data, data + 1, data + n); + plot.setNewData(data, n); + usleep(1000); + } + return NULL; } -BOOST_AUTO_TEST_SUITE (Complexplot_Test) +void *threadMain3(void *arg) { + + Complexplot plot; + plot.setTitle("FloatVec"); + plot.setXAxisRange(0, 2); + plot.setYAxisScale(Complexplot::Magnitude, 0.9, 1.1); -BOOST_AUTO_TEST_CASE(Complexplot_Basic_Test) -{ - int argc = 1; - char* argv[] = { const_cast("Compleplot_Basic_Test"), NULL }; - QApplication a(argc, argv); + FloatVec data(1024); + int n = data.size(); + float step = 2.0 * PI / n; + for (int i = 0; i < n; i++) + data[i] = polar(1.0f, step * i); - boost::scoped_ptr< boost::thread > thread1_; - boost::scoped_ptr< boost::thread > thread2_; - boost::scoped_ptr< boost::thread > thread3_; + plot.setNewData(data.begin(), data.end()); - thread1_.reset( new boost::thread( &threadMain1 ) ); - thread2_.reset( new boost::thread( &threadMain2 ) ); - thread3_.reset( new boost::thread( &threadMain3 ) ); + for (int i = 0; i < n; i++) { + rotate(data.begin(), data.begin() + 1, data.end()); + plot.setNewData(data.begin(), data.end()); + usleep(1000); + } + return NULL; +} - qApp->exec(); - thread1_->join(); - thread2_->join(); - thread3_->join(); +int main(int argc, char *argv[]) { + int argc2 = 1; + char* argv2[] = { const_cast("Compleplot_Basic_Test"), NULL }; + QApplication a(argc2, argv2); + pthread_t threads[3]; + int i; + + if (pthread_create(&threads[0], NULL, threadMain1, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[1], NULL, threadMain2, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[2], NULL, threadMain3, NULL)) { + perror("pthread_create"); + exit(-1); + } + + qApp->exec(); + + for (i=0;i<3;i++) { + pthread_join(threads[i], NULL); + } + exit(0); } -BOOST_AUTO_TEST_SUITE_END() diff --git a/graphics/lib/realplot/test/CMakeLists.txt b/graphics/lib/realplot/test/CMakeLists.txt index a5f5ba322..3168de705 100644 --- a/graphics/lib/realplot/test/CMakeLists.txt +++ b/graphics/lib/realplot/test/CMakeLists.txt @@ -24,9 +24,8 @@ # Build tests ######################################################################## #turn the test cpp file into an executable with an int main() function -ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) INCLUDE_DIRECTORIES(..) ADD_EXECUTABLE(realplot_test realplot_test.cpp) -TARGET_LINK_LIBRARIES(realplot_test ${Boost_LIBRARIES} graphics) +TARGET_LINK_LIBRARIES(realplot_test pthread graphics) ADD_TEST(realplot_test realplot_test) diff --git a/graphics/lib/realplot/test/realplot_test.cpp b/graphics/lib/realplot/test/realplot_test.cpp index c2f4dd9f7..e6a00219d 100644 --- a/graphics/lib/realplot/test/realplot_test.cpp +++ b/graphics/lib/realplot/test/realplot_test.cpp @@ -35,96 +35,91 @@ #include "Realplot.h" -#include -#include -#include -#include +#include +#include +#include #include #include #include #include -#include -typedef std::vector FloatVec; +typedef std::vector FloatVec; template -void getPoints(T* data, int numPoints) -{ - for(int i=0;i -void getPoints(Iterator begin, Iterator end) -{ - for(;begin!=end;begin++) - { - *begin = 10*((double)rand()/RAND_MAX); - } +void getPoints(Iterator begin, Iterator end) { + for (; begin != end; begin++) { + *begin = 10 * ((double) rand() / RAND_MAX); + } } -void threadMain1() -{ - Realplot plot; +void *threadMain1(void *arg) { + Realplot plot; - float data[1024]; + float data[1024]; - for(int i=0;i<10;i++) - { - getPoints(data, 504); - plot.setNewData(data, 504); - boost::this_thread::sleep(boost::posix_time::milliseconds(5)); - } + for (int i = 0; i < 100; i++) { + getPoints(data, 504); + plot.setNewData(data, 504); + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } + return NULL; } -void threadMain2() -{ - Realplot plot; - double data[1024]; - - for(int i=0;i<10000;i++) - { - getPoints(data, 504); - plot.setNewData(data, 504); - boost::this_thread::sleep(boost::posix_time::milliseconds(5)); - } -} +void *threadMain2(void *arg) { + Realplot plot; + double data[1024]; -void threadMain3() -{ - Realplot plot; - FloatVec v(1024); - - for(int i=0;i<10000;i++) - { - getPoints(v.begin(), v.end()); - plot.setNewData(v.begin(), v.end()); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } + for (int i = 0; i < 100; i++) { + getPoints(data, 504); + plot.setNewData(data, 504); + usleep(5000); + } + return NULL; } -BOOST_AUTO_TEST_SUITE (Realplot_Test) - -BOOST_AUTO_TEST_CASE(Realplot_Basic_Test) -{ - int argc = 1; - char* argv[] = { const_cast("Realplot_Basic_Test"), NULL }; - QApplication a(argc, argv); - - boost::scoped_ptr< boost::thread > thread1_; - boost::scoped_ptr< boost::thread > thread2_; - boost::scoped_ptr< boost::thread > thread3_; +void *threadMain3(void *arg) { + Realplot plot; + FloatVec v(1024); - thread1_.reset( new boost::thread( &threadMain1 ) ); - thread2_.reset( new boost::thread( &threadMain2 ) ); - thread3_.reset( new boost::thread( &threadMain3 ) ); - - qApp->exec(); - thread1_->join(); - thread2_->join(); - thread3_->join(); + for (int i = 0; i < 100; i++) { + getPoints(v.begin(), v.end()); + plot.setNewData(v.begin(), v.end()); + usleep(5000); + } + return NULL; } -BOOST_AUTO_TEST_SUITE_END() +int main(int argc, char *argv[]) { + int argc2 = 1; + char* argv2[] = { const_cast("Realplot_Basic_Test"), NULL }; + QApplication a(argc2, argv2); + pthread_t threads[3]; + int i; + + if (pthread_create(&threads[0], NULL, threadMain1, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[1], NULL, threadMain2, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[2], NULL, threadMain3, NULL)) { + perror("pthread_create"); + exit(-1); + } + + qApp->exec(); + + for (i=0;i<3;i++) { + pthread_join(threads[i], NULL); + } + exit(0); +} diff --git a/graphics/lib/scatterplot/test/CMakeLists.txt b/graphics/lib/scatterplot/test/CMakeLists.txt index 432dd8847..9b373bca8 100644 --- a/graphics/lib/scatterplot/test/CMakeLists.txt +++ b/graphics/lib/scatterplot/test/CMakeLists.txt @@ -23,10 +23,8 @@ ######################################################################## # Build tests ######################################################################## -#turn the test cpp file into an executable with an int main() function -ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) INCLUDE_DIRECTORIES(..) ADD_EXECUTABLE(scatterplot_test scatterplot_test.cpp) -TARGET_LINK_LIBRARIES(scatterplot_test ${Boost_LIBRARIES} graphics) +TARGET_LINK_LIBRARIES(scatterplot_test pthread graphics) ADD_TEST(scatterplot_test scatterplot_test) diff --git a/graphics/lib/scatterplot/test/scatterplot_test.cpp b/graphics/lib/scatterplot/test/scatterplot_test.cpp index 4f1ccc6f1..a40eabc58 100644 --- a/graphics/lib/scatterplot/test/scatterplot_test.cpp +++ b/graphics/lib/scatterplot/test/scatterplot_test.cpp @@ -35,99 +35,94 @@ #include "Scatterplot.h" -#include -#include -#include -#include +#include +#include +#include #include #include #include #include -#include typedef std::complex Cplx; -typedef std::vector CplxVec; +typedef std::vector CplxVec; template -void getPoints(std::complex* data, int numPoints) -{ - for(int i=0;i(2*((T)rand()/RAND_MAX)-1, - 2*((T)rand()/RAND_MAX)-1); - } +void getPoints(std::complex* data, int numPoints) { + for (int i = 0; i < numPoints; i++) { + data[i] = std::complex(2 * ((T) rand() / RAND_MAX) - 1, + 2 * ((T) rand() / RAND_MAX) - 1); + } } template -void getPoints(Iterator begin, Iterator end) -{ - for(;begin!=end;begin++) - { - float r = 2*((double)rand()/RAND_MAX)-1; - float i = 2*((double)rand()/RAND_MAX)-1; - *begin = Cplx(r,i); - } +void getPoints(Iterator begin, Iterator end) { + for (; begin != end; begin++) { + float r = 2 * ((double) rand() / RAND_MAX) - 1; + float i = 2 * ((double) rand() / RAND_MAX) - 1; + *begin = Cplx(r, i); + } } -void threadMain1() -{ - Scatterplot plot; - std::complex data[1024]; - - for(int i=0;i<10;i++) - { - getPoints(data, 1024); - plot.setNewData(data, 1024); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } -} - -void threadMain2() -{ - Scatterplot plot; - std::complex data[1024]; - - for(int i=0;i<10;i++) - { - getPoints(data, 1024); - plot.setNewData(data, 1024); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } -} +void *threadMain1(void *arg) { + Scatterplot plot; + std::complex data[1024]; -void threadMain3() -{ - Scatterplot plot; - CplxVec v(1024); - - for(int i=0;i<10;i++) - { - getPoints(v.begin(), v.end()); - plot.setNewData(v.begin(), v.end()); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } + for (int i = 0; i < 10; i++) { + getPoints(data, 1024); + plot.setNewData(data, 1024); + usleep(100000); + } + return NULL; } -BOOST_AUTO_TEST_SUITE (Scatterplot_Test) +void *threadMain2(void *arg) { + Scatterplot plot; + std::complex data[1024]; -BOOST_AUTO_TEST_CASE(Scatterplot_Basic_Test) -{ - int argc = 1; - char* argv[] = { const_cast("Scatterplot_Basic_Test"), NULL }; - QApplication a(argc, argv); - - boost::scoped_ptr< boost::thread > thread1_; - boost::scoped_ptr< boost::thread > thread2_; - boost::scoped_ptr< boost::thread > thread3_; + for (int i = 0; i < 10; i++) { + getPoints(data, 1024); + plot.setNewData(data, 1024); + usleep(100000); + } + return NULL; +} - thread1_.reset( new boost::thread( &threadMain1 ) ); - thread2_.reset( new boost::thread( &threadMain2 ) ); - thread3_.reset( new boost::thread( &threadMain3 ) ); +void *threadMain3(void *arg) { + Scatterplot plot; + CplxVec v(1024); - qApp->exec(); - thread1_->join(); - thread2_->join(); - thread3_->join(); + for (int i = 0; i < 10; i++) { + getPoints(v.begin(), v.end()); + plot.setNewData(v.begin(), v.end()); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + return NULL; } -BOOST_AUTO_TEST_SUITE_END() +int main(int argc, char *argv[]) { + int argc2 = 1; + char* argv2[] = { const_cast("Scatterplot_Basic_Test"), NULL }; + QApplication a(argc2, argv2); + pthread_t threads[3]; + int i; + + if (pthread_create(&threads[0], NULL, threadMain1, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[1], NULL, threadMain2, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[2], NULL, threadMain3, NULL)) { + perror("pthread_create"); + exit(-1); + } + + qApp->exec(); + + for (i=0;i<3;i++) { + pthread_join(threads[i], NULL); + } + exit(0); +} diff --git a/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp index db96b6333..84dd958c8 100644 --- a/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp +++ b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp @@ -35,94 +35,93 @@ #include "Waterfallplot.h" -#include -#include -#include -#include + +#include +#include +#include #include #include #include -#include #define PI 3.14159265358979323846 using namespace std; -void threadMain1() -{ - int n=2048; - Waterfallplot plot(n,n); - plot.setTitle("Float"); - - float step = 1.0*PI/n; - float* data = new float[n*2]; - for(int i=0;i data; - data.resize(n*2); - for(int i=0;i("Waterfallplot_Init_Test"), NULL }; - QApplication a(argc, argv); + for (int i = 0; i < n; i++) { + plot.appendNewData(data + i, n); + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } +} - boost::scoped_ptr< boost::thread > thread1_; - boost::scoped_ptr< boost::thread > thread2_; - boost::scoped_ptr< boost::thread > thread3_; +void *threadMain3(void *arg) { + int n = 2048; + Waterfallplot plot(n, n); + plot.setTitle("FloatVec"); + + double step = 2.0 * PI / n; + std::vector data; + data.resize(n * 2); + for (int i = 0; i < n * 2; i++) + data[i] = sin(step * i); + + for (int i = 0; i < n; i++) { + plot.appendNewData(data.begin() + i, data.begin() + i + n); + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } +} - thread1_.reset( new boost::thread( &threadMain1 ) ); - thread2_.reset( new boost::thread( &threadMain2 ) ); - thread3_.reset( new boost::thread( &threadMain3 ) ); +int main(int argc, char *argv[]) { + int argc2 = 1; + char* argv2[] = { const_cast("Waterfallplot_Init_Test"), NULL }; + QApplication a(argc2, argv2); + pthread_t threads[3]; + int i; + + if (pthread_create(&threads[0], NULL, threadMain1, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[1], NULL, threadMain2, NULL)) { + perror("pthread_create"); + exit(-1); + } + if (pthread_create(&threads[2], NULL, threadMain3, NULL)) { + perror("pthread_create"); + exit(-1); + } + + qApp->exec(); + + for (i = 0; i < 3; i++) { + pthread_join(threads[i], NULL); + } + exit(0); - qApp->exec(); - thread1_->join(); - thread2_->join(); - thread3_->join(); } - -BOOST_AUTO_TEST_SUITE_END() diff --git a/lte/lib/CMakeLists.txt b/lte/lib/CMakeLists.txt index 6d3888e69..f4d09f9a2 100644 --- a/lte/lib/CMakeLists.txt +++ b/lte/lib/CMakeLists.txt @@ -30,6 +30,8 @@ INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) IF(${DISABLE_VOLK}) IF(${DISABLE_VOLK} EQUAL 0) FIND_PACKAGE(Volk) + ELSE(${DISABLE_VOLK} EQUAL 0) + MESSAGE(STATUS "VOLK library disabled (DISABLE_VOLK=1)") ENDIF(${DISABLE_VOLK} EQUAL 0) ELSE(${DISABLE_VOLK}) FIND_PACKAGE(Volk) @@ -63,7 +65,17 @@ ELSE(VOLK_FOUND) ENDIF(VOLK_FOUND) +######################################################################## +# Recurse subdirectories and find all directories with a CMakeLists.txt file in it +######################################################################## +FILE(GLOB_RECURSE cmakefiles CMakeLists.txt) +FOREACH (_file ${cmakefiles}) + GET_FILENAME_COMPONENT(dir ${_file} PATH) + IF (NOT ${dir} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + ADD_SUBDIRECTORY(${dir}) + ENDIF () +ENDFOREACH() diff --git a/lte/lib/ch_estimation/test/CMakeLists.txt b/lte/lib/ch_estimation/test/CMakeLists.txt new file mode 100644 index 000000000..04b6d85b8 --- /dev/null +++ b/lte/lib/ch_estimation/test/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Channel Estimation TEST +######################################################################## + +ADD_EXECUTABLE(chest_test chest_test.c) +TARGET_LINK_LIBRARIES(chest_test lte) + +ADD_TEST(chest_test_all_cellids chest_test) + + diff --git a/lte/lib/ch_estimation/test/chest_test.c b/lte/lib/ch_estimation/test/chest_test.c new file mode 100644 index 000000000..0162f3222 --- /dev/null +++ b/lte/lib/ch_estimation/test/chest_test.c @@ -0,0 +1,251 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "lte.h" + +int cell_id = -1; +int nof_prb = 6; +lte_cp_t cp = CPNORM; + +char *output_matlab = NULL; + +void usage(char *prog) { + printf("Usage: %s [recov]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c cell_id (-1 tests all). [Default %d]\n", cell_id); + + printf("\t-o output matlab file [Default %s]\n",output_matlab?output_matlab:"None"); + printf("\t-v increase verbosity\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "recov")) != -1) { + switch(opt) { + case 'r': + nof_prb = atoi(argv[optind]); + break; + case 'e': + cp = CPEXT; + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'o': + output_matlab = argv[optind]; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int check_mse(float mod, float arg, int n_port) { + INFO("mod=%.4f, arg=%.4f, n_port=%d\n", mod, arg, n_port); + switch(n_port) { + case 0: + if (mod > 0.029) { + return -1; + } + if (arg > 0.029) { + return -1; + } + break; + case 1: + if (mod > 0.012) { + return -1; + } + if (arg > 0.012) { + return -1; + } + break; + case 2: + case 3: + if (mod > 3.33) { + return -1; + } + if (arg > 0.63) { + return -1; + } + break; + default: + return -1; + } + return 0; +} + +int main(int argc, char **argv) { + chest_t eq; + cf_t *input = NULL, *ce = NULL, *h = NULL; + refsignal_t refs; + int i, j, n_port, n_slot, cid, num_re; + int ret = -1; + int max_cid; + FILE *fmatlab = NULL; + float mse_mag, mse_phase; + + parse_args(argc,argv); + + if (output_matlab) { + fmatlab=fopen(output_matlab, "w"); + if (!fmatlab) { + perror("fopen"); + goto do_exit; + } + } + + num_re = nof_prb * RE_X_RB * CP_NSYMB(cp); + + input = malloc(num_re * sizeof(cf_t)); + if (!input) { + perror("malloc"); + goto do_exit; + } + h = malloc(num_re * sizeof(cf_t)); + if (!h) { + perror("malloc"); + goto do_exit; + } + ce = malloc(num_re * sizeof(cf_t)); + if (!ce) { + perror("malloc"); + goto do_exit; + } + + if (cell_id == -1) { + cid = 0; + max_cid = 149; + } else { + cid = cell_id; + max_cid = cell_id; + } + while(cid <= max_cid) { + if (chest_init(&eq, LINEAR, cp, nof_prb, MAX_PORTS)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + + if (chest_ref_LTEDL(&eq, cid)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto do_exit; + } + + for (n_slot=0;n_slot +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +#include "crc_test.h" + +int num_bits = 1000, crc_length = 16; +unsigned int crc_poly = 0x11021; +unsigned int seed = 0; + + +void usage(char *prog) { + printf("Usage: %s [nlps]\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-l crc_length [Default %d]\n", crc_length); + printf("\t-p crc_poly (Hex) [Default 0x%x]\n", crc_poly); + printf("\t-s seed [Default 0=time]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nlps")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 'l': + crc_length = atoi(argv[optind]); + break; + case 'p': + crc_poly = (unsigned int) strtoul(argv[optind], NULL, 16); + break; + case 's': + seed = (unsigned int) strtoul(argv[optind], NULL, 0); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int i; + char *data; + unsigned int crc_word, expected_word; + + parse_args(argc, argv); + + data = malloc(sizeof(char) * (num_bits+crc_length)); + if (!data) { + perror("malloc"); + exit(-1); + } + + if (!seed) { + seed = time(NULL); + } + srand(seed); + + // Generate data + for (i=0;i + +typedef struct { + int n; + int l; + unsigned int p; + unsigned int s; + unsigned int word; +}expected_word_t; + + +static expected_word_t expected_words[] = { + {5000, 24, 0x1864CFB, 1, 0x4D0836}, // LTE CRC24A (36.212 Sec 5.1.1) + {5000, 24, 0X1800063, 1, 0x9B68F8}, // LTE CRC24B + {5000, 16, 0x11021, 1, 0xBFFA}, // LTE CRC16 + {5000, 8, 0x19B, 1, 0xF8}, // LTE CRC8 + + {-1, -1, 0, 0, 0} +}; + +int get_expected_word(int n, int l, unsigned int p, unsigned int s, unsigned int *word) { + int i; + i=0; + while(expected_words[i].n != -1) { + if (expected_words[i].l == l + && expected_words[i].p == p + && expected_words[i].s == s) { + break; + } else { + i++; + } + } + if (expected_words[i].n == -1) { + return -1; + } else { + if (word) { + *word = expected_words[i].word; + } + return 0; + } +} diff --git a/examples/viterbi_test.c b/lte/lib/fec/test/viterbi_test.c similarity index 80% rename from examples/viterbi_test.c rename to lte/lib/fec/test/viterbi_test.c index 03fdef3ae..43180f9e5 100644 --- a/examples/viterbi_test.c +++ b/lte/lib/fec/test/viterbi_test.c @@ -35,9 +35,11 @@ #include "lte.h" +#include "viterbi_test.h" + typedef _Complex float cf_t; -int frame_length = 1000, nof_frames = 128; +int frame_length = 1000, nof_slots = 128; float ebno_db = 100.0; unsigned int seed = 0; bool tail_biting = false; @@ -52,7 +54,7 @@ int K = -1; void usage(char *prog) { printf("Usage: %s [nlestk]\n", prog); - printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-n nof_frames [Default %d]\n", nof_slots); printf("\t-l frame_length [Default %d]\n", frame_length); printf("\t-e ebno in dB [Default scan]\n"); printf("\t-s seed [Default 0=time]\n"); @@ -65,7 +67,7 @@ void parse_args(int argc, char **argv) { while ((opt = getopt(argc, argv, "nlstek")) != -1) { switch (opt) { case 'n': - nof_frames = atoi(argv[optind]); + nof_slots = atoi(argv[optind]); break; case 'l': frame_length = atoi(argv[optind]); @@ -74,7 +76,7 @@ void parse_args(int argc, char **argv) { ebno_db = atof(argv[optind]); break; case 's': - seed = atoi(argv[optind]); + seed = (unsigned int) strtoul(argv[optind], NULL, 0); break; case 't': tail_biting = true; @@ -89,6 +91,34 @@ void parse_args(int argc, char **argv) { } } +void output_matlab(float ber[NTYPES][SNR_POINTS], int snr_points, + convcoder_t cod[NCODS], int ncods) { + int i, j, n; + FILE *f = fopen("viterbi_snr.m", "w"); + if (!f) { + perror("fopen"); + exit(-1); + } + fprintf(f, "ber=["); + for (j = 0; j < NTYPES; j++) { + for (i = 0; i < snr_points; i++) { + fprintf(f, "%g ", ber[j][i]); + } + fprintf(f, "; "); + } + fprintf(f, "];\n"); + fprintf(f, "snr=linspace(%g,%g-%g/%d,%d);\n", SNR_MIN, SNR_MAX, SNR_MAX, + snr_points, snr_points); + fprintf(f, "semilogy(snr,ber,snr,0.5*erfc(sqrt(10.^(snr/10))));\n"); + fprintf(f, "legend('uncoded',"); + for (n=0;n 1) { - printf("\n"); - - FILE *f = fopen("output.m", "w"); - if (!f) { - perror("fopen"); - exit(-1); - } - fprintf(f, "ber=["); - for (j = 0; j < NTYPES; j++) { - for (i = 0; i < snr_points; i++) { - fprintf(f, "%g ", ber[j][i]); - } - fprintf(f, "; "); - } - fprintf(f, "];\n"); - fprintf(f, "snr=linspace(%g,%g-%g/%d,%d);\n", SNR_MIN, SNR_MAX, SNR_MAX, - snr_points, snr_points); - fprintf(f, "semilogy(snr,ber,snr,0.5*erfc(sqrt(10.^(snr/10))));\n"); - fprintf(f, "legend('uncoded',"); - for (n=0;n expected_errors); + } + } else { + printf("\n"); + output_matlab(ber, snr_points, cod, ncods); + printf("Done\n"); + exit(0); + } } diff --git a/lte/lib/fec/test/viterbi_test.h b/lte/lib/fec/test/viterbi_test.h new file mode 100644 index 000000000..c0080b6ff --- /dev/null +++ b/lte/lib/fec/test/viterbi_test.h @@ -0,0 +1,71 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +typedef struct { + int n; + unsigned int s; + int len; + int k; + bool tail; + float ebno; + int errors; +}expected_errors_t; + + +static expected_errors_t expected_errors[] = { + {1000, 1, 40, 7, true, 0.0, 5363}, + {1000, 1, 40, 7, true, 2.0, 356}, + {1000, 1, 40, 7, true, 3.0, 48}, + {1000, 1, 40, 7, true, 4.5, 0}, + + {100, 1, 1000, 7, true, 0.0, 8753}, + {100, 1, 1000, 7, true, 2.0, 350}, + {100, 1, 1000, 7, true, 3.0, 33}, + {100, 1, 1000, 7, true, 4.5, 0}, + + {-1, -1, -1, -1, true, -1.0, -1} +}; + +int get_expected_errors(int n, unsigned int s, int len, int k, bool tail, float ebno) { + int i; + i=0; + while(expected_errors[i].n != -1) { + if (expected_errors[i].n == n + && expected_errors[i].s == s + && expected_errors[i].len == len + && expected_errors[i].k == k + && expected_errors[i].tail == tail + && expected_errors[i].ebno == ebno) { + break; + } else { + i++; + } + } + return expected_errors[i].errors; +} From c946562787902cc3ca2a667027951b93baa99de0 Mon Sep 17 00:00:00 2001 From: ismagom Date: Sat, 8 Mar 2014 11:53:42 +0000 Subject: [PATCH 10/25] Removed Boost dependencies in graphics library --- graphics/lib/CMakeLists.txt | 56 ++----------------- .../lib/complexplot/test/complexplot_test.cpp | 2 - graphics/lib/realplot/test/realplot_test.cpp | 4 +- .../lib/scatterplot/test/scatterplot_test.cpp | 4 +- .../waterfallplot/test/Waterfallplot_test.cpp | 12 ++-- 5 files changed, 12 insertions(+), 66 deletions(-) diff --git a/graphics/lib/CMakeLists.txt b/graphics/lib/CMakeLists.txt index 65457ee23..a7ba627de 100644 --- a/graphics/lib/CMakeLists.txt +++ b/graphics/lib/CMakeLists.txt @@ -35,54 +35,6 @@ IF(QT4_FOUND AND QWT_FOUND) ENDIF(QT4_FOUND AND QWT_FOUND) -######################################################################## -# Setup Boost -######################################################################## -MESSAGE(STATUS "") -MESSAGE(STATUS "Configuring Boost C++ Libraries...") -SET(BOOST_REQUIRED_COMPONENTS - program_options - system - thread - unit_test_framework -) - -IF(UNIX AND EXISTS "/usr/lib64") - LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix -ENDIF(UNIX AND EXISTS "/usr/lib64") - -IF(MSVC) - SET(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") - IF(BOOST_ALL_DYN_LINK) - ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc - ELSE(BOOST_ALL_DYN_LINK) - UNSET(BOOST_REQUIRED_COMPONENTS) #empty components list for static link - ENDIF(BOOST_ALL_DYN_LINK) -ENDIF(MSVC) - -SET(Boost_ADDITIONAL_VERSIONS - "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" "1.40.0" "1.40" - "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" - "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" "1.45.0" "1.45" - "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" - "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53") -FIND_PACKAGE(Boost 1.37 REQUIRED ${BOOST_REQUIRED_COMPONENTS}) -MESSAGE(STATUS "Boost version: ${Boost_VERSION}") - -IF(Boost_VERSION LESS 104600) - ADD_DEFINITIONS( -DBOOST_FILESYSTEM_VERSION=2 ) #use filesystem version 2 in boost < 1.46 - MESSAGE(STATUS "Using Boost Filesystem V2") -ENDIF(Boost_VERSION LESS 104600) - -INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) -LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) - -MESSAGE(STATUS "Boost include directories: ${Boost_INCLUDE_DIRS}") -MESSAGE(STATUS "Boost library directories: ${Boost_LIBRARY_DIRS}") -MESSAGE(STATUS "Boost libraries: ${Boost_LIBRARIES}") - - - ######################################################################## # Build the graphics library @@ -104,7 +56,7 @@ IF(QWT_MAJOR_VERSION LESS 6) MESSAGE(STATUS "QWT6 is required.") ENDIF(QWT_MAJOR_VERSION LESS 6) -IF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) +IF(QT4_FOUND AND QWT_FOUND AND QWT_MAJOR_VERSION EQUAL 6) QT4_WRAP_CPP(lineplotwraps common/Lineplot.h) QT4_WRAP_CPP(pointplotwraps common/Pointplot.h) QT4_WRAP_CPP(spectrogramplotwraps common/Spectrogramplot.h) @@ -131,11 +83,11 @@ IF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) MESSAGE(STATUS " GRAPHICS library will be installed.") -ELSE(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) +ELSE(QT4_FOUND AND QWT_FOUND AND AND QWT_MAJOR_VERSION EQUAL 6) - MESSAGE(STATUS " QT4/Qwt6 or Boost not found. GRAPHICS library is not generated") + MESSAGE(STATUS " QT4 or Qwt6 not found. GRAPHICS library is not generated") -ENDIF(QT4_FOUND AND QWT_FOUND AND Boost_FOUND AND QWT_MAJOR_VERSION EQUAL 6) +ENDIF(QT4_FOUND AND QWT_FOUND AND QWT_MAJOR_VERSION EQUAL 6) diff --git a/graphics/lib/complexplot/test/complexplot_test.cpp b/graphics/lib/complexplot/test/complexplot_test.cpp index 1a01ad386..e3f7f93de 100644 --- a/graphics/lib/complexplot/test/complexplot_test.cpp +++ b/graphics/lib/complexplot/test/complexplot_test.cpp @@ -31,8 +31,6 @@ * Main test file for Complexplot class. */ -#define BOOST_TEST_MODULE Complexplot_Test - #include "Complexplot.h" #include diff --git a/graphics/lib/realplot/test/realplot_test.cpp b/graphics/lib/realplot/test/realplot_test.cpp index e6a00219d..111d07de6 100644 --- a/graphics/lib/realplot/test/realplot_test.cpp +++ b/graphics/lib/realplot/test/realplot_test.cpp @@ -31,8 +31,6 @@ * Main test file for Realplot class. */ -#define BOOST_TEST_MODULE Realplot_Test - #include "Realplot.h" #include @@ -67,7 +65,7 @@ void *threadMain1(void *arg) { for (int i = 0; i < 100; i++) { getPoints(data, 504); plot.setNewData(data, 504); - boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + usleep(5000); } return NULL; } diff --git a/graphics/lib/scatterplot/test/scatterplot_test.cpp b/graphics/lib/scatterplot/test/scatterplot_test.cpp index a40eabc58..fcc104b7c 100644 --- a/graphics/lib/scatterplot/test/scatterplot_test.cpp +++ b/graphics/lib/scatterplot/test/scatterplot_test.cpp @@ -31,8 +31,6 @@ * Main test file for scatterplot class. */ -#define BOOST_TEST_MODULE Scatterplot_Test - #include "Scatterplot.h" #include @@ -94,7 +92,7 @@ void *threadMain3(void *arg) { for (int i = 0; i < 10; i++) { getPoints(v.begin(), v.end()); plot.setNewData(v.begin(), v.end()); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + usleep(100000); } return NULL; } diff --git a/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp index 84dd958c8..6be65f77a 100644 --- a/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp +++ b/graphics/lib/waterfallplot/test/Waterfallplot_test.cpp @@ -31,11 +31,9 @@ * Main test file for Waterfallplot class. */ -#define BOOST_TEST_MODULE Waterfallplot_Test - #include "Waterfallplot.h" - +#include #include #include #include @@ -64,7 +62,7 @@ void *threadMain1(void *arg) { return NULL; } -void threadMain2() { +void *threadMain2(void *arg) { int n = 2048; Waterfallplot plot(n, n); plot.setTitle("Double"); @@ -76,8 +74,9 @@ void threadMain2() { for (int i = 0; i < n; i++) { plot.appendNewData(data + i, n); - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + usleep(1000); } + return NULL; } void *threadMain3(void *arg) { @@ -93,8 +92,9 @@ void *threadMain3(void *arg) { for (int i = 0; i < n; i++) { plot.appendNewData(data.begin() + i, data.begin() + i + n); - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + usleep(1000); } + return NULL; } int main(int argc, char *argv[]) { From 3566b6d8e5562255815493f82373f67fa78b72f8 Mon Sep 17 00:00:00 2001 From: ismagom Date: Sat, 8 Mar 2014 12:25:07 +0000 Subject: [PATCH 11/25] PBCH example capture --- pbch_capture.png | Bin 0 -> 445705 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pbch_capture.png diff --git a/pbch_capture.png b/pbch_capture.png new file mode 100644 index 0000000000000000000000000000000000000000..b99e011ca7fa17b4d15904dd8bef0887233fd547 GIT binary patch literal 445705 zcmXuKbx>Pf_diN;cXtU+ad(OacPs7=Ew05ixE7b<4h4!saW59!3lt6Ra`U|3`95x29@bXW-u51L)bc1#IdIB@<8=d^DAlgz#t% z9(5xg*#(Kn2v3Tfx4n!pP;uaM^UA4Jk1HfSs=K$osw3`{b`d`(*Dws{S@YHS z&a?L2onVHG&EQL(C66JKge01N6F6R;Z zd0x_n9Yuk%iwNM}62SrF< zQ_5S(k&yE5`jRs+HA;CE?Fm3qxhcXI;D%7k_Vfm?iXp=R3vGq2BtbiX5G4~@B97D| zom|qt)QETAzM|r2hYK1V;5@#bur>isKe3WQ$M%VR%LP)e%22}O7m`cbY8~5u6tOG{ z7h>^#U0IDSz=c8E%8ufz2r=Z@JznDRe`}b5%Kb}kOtVP#nCZFH=WV8sDV}%3?vPD4#70_`v^;k`H<@OTJt|cX%y9&1mwY2Y(llj5P zJ|%wsoz4XFv%S&pAEVAHFCl}jIi0NuT2)L^X~{vvw_F?31d{MLB;GRB{Br7$6JIXz zH3F_!Vfp-0axuTO$4B2>cf5n%VpRL=?c9JpBh%$gaEaxgQ_`pyobfrD z;1)tl^yw&nC)e+nIOTL2HhgOs3NaN_B$Zx&Q_ZJ+P8FOmP2`IICO`X`i*9BTH?t13 zu%U0RZt0SkGX5Ul-RmMtFxH7rZv9YCY@Vq5ajS^gislm<5xBAc<1Lht*@M(!w6# zZxT|qOJ`;gkD;V_dI$qATc|jhK=RT@Jsbm`b=!;nSLve-n=fi)+aed?y`d`0SPXhD zEAn)O^GE3NMIJE6N_?|+I1IGud?Mpn%XDvl+Vm&7(8H!geu@-rNwEpxHC zmz$Gi{(%OscL|craKuTyh_oe9?QiG^q5L_H%q-=ph$SLPc7B5Gl`X!q@5jAv)}`vr z!edzXWrn7r=)I;fI$4-Zn$m}F%{1u5uE6hb7mOdbE@Q#GgS~uF^7>XtxwI0I`r+e* zW&)MOa3PyrE9r+{NvQFpy6sFj6g3WA7REG8KC+86#RDBT`t|g5$lE?0X}u#dxx9rW z&ub2vevk(KN)7g8PRT)JuzoI@r~Ghs414O*QFi@tIBG`>{U=L4DG^DM4?MSHQV+ST z1NLEDi1n0oWvX9DWn0S-xU3K@0_Z9n5G_un1vI>$c6m`hhxmf8I^pR9i5~ZYD?xL}N@icrFm8h1k9N$9wPW-fddwD_aFet74UVDG} zT7UG-sddSJ*(0!+`)@6Y1fXZaPY9Q%@`=K`VU)9n^t%$CWkK?QX>zBv{JLo$w3B!z zYI*t3JX6eXP1X|!P9M8%PVh6Np*Z7#WhFdwPTzX4EIA$tVL-=LG+y6_{Sq9gDtE1U za679KE!BEHFb=nuPpfCpGZu&eGfD+-iCNbq(J{#8jH8XKVsIoikdEopi9>xXm8aTp z9LQVb4hKguVUx}b@M*ab-9RpO3U<-LVzyRcY!lqd2icPJ*stc3s@S(dO-XWd?SPd2 zEa#;PMj0JEd^P-}jdL<=^~0zZt;J`M%Yion7elU_G7IC%urqHnNz&!A;n|D>jHWF7 ztzz$*?a?^yW1ocr>}~cs+I5ho@B?tCWBG*p*K>P)?@YY8%~k;HmOs9aoP#4=O7FFs zBxTs#n3z~UMJW1wOS9@NSIm#NkS#G-Ia;3&jMDe*#~tG<1@IQwSQ=g<6qJh{7fc~l z&;R!9KP-!NZ&H<(-zz-nm!GA|{~V4Huj0D?_kx;tb>VA>y*K;lmuSa7COGvK!J=^6 zqB1o`hhQvh^T*(q?NO-%EX0G=!C);S-xRncbBm-pT1%;bbOQBK=2&b^R64jLCPx>7 z@Hm_W^cby3g<`PhX98-C*E28P#!u93nc8XA24S?r#7-6?2Ga1FIpFC(8|uF*h2>A4 z2qwdE)2j~Vqe*~`vy}>C2s8+w#)nbt)9P(y8lclF`usv9BE$L0-Hi%3X65_o+h=lC zv&!*M+{oaviWGrtbYXjxB0u2-nR}85F?MPR{lNagtVm(2lO_c~89I!W=@7#;@i`yBzMCZ0ihiqPzSQRX+4bP~KivPud_|~cX$C@gqARF1`*17*F zQn|Udu_S}7g)+oT9K9}%G2-2RoQv-C3)%VE;fuiN=`s#oV_TGq6hN+QH^|3A62u&% z?1)fE63x|E%4#iHvS!!jOsp@S!6(WY&2@nwpD#YyhXqR{C%W@zH1_kg#Y6B9GUX&T zZL06?mMmJJnF&Ghnnq$WI3JxZtnWAk1rb@)SlXATq-}bo1 zukaU95&aC}_4RsQ^7hZPXF2dzxm~|)XOU9SsH7LKb~57Fp8Vt9n9OLYoXcA`p-Zwe zvOLlE^yPRjxjcUm&8$qNIEWm3_Z8h@eJ1~!=_!mE&W7Rn1ur+d8(%*uS#Wnv0z1%S za@VAcMmlIAYVcVU&+?{_iv=-5R;q2y+C7jiEjUh8FgnC;6cp{5G%duLZgG2I)jT_D zFOY2o#QlI=x^~jove=(a$`=tPZQdOIcqbY!7Y4v^ntKf@aA{L&TN_Hp>{^&?fv&c< z3tfp~ftJHUP3q;_)&_)nOVCUF7?1%$QJ56Frz_1yzuZDhjs*Swa(aW$FK&1kfgXKS zbssgJBpvqh&`Oy|1sMYt3cMT|HB@c0z@;KE=q$Y5HrTqkD?-r;lwyi)Yo+r?3n>;w za+9(!@Src$%*V!p5A%Qu5E;|Nhj^RmN=W|B@RHuY3AallUK>W{?yhIPLO};<*yecV(fFLW|3&`mIM-RsZHb@wMqY_YsV-ltf12vT z3)($XjylF0a84mhA}BbZPAdO!U~93-nNAH56ktwlU&j)5olo4KKsAxDu0lf3)4P-} z9MfksAZ!I7$Bf_`R}*|BqlLkp5RP>CLYGV6a1I*^YK^LVX2;HnCfu zel3-&2Kx6*ovpPUIXRoxn$=kE^ueNCBXNQ?@e#npgrpcvqVhF6(|IBxEnp!+Ov=Iy zm!+qnP5(JII#+&$Yj%N5mlVYgORWY5&YW02E8pSdfvAgPyPt;-msml4pIv(gb}UEC z192>IwUq{$O7(&+<9f%BYaNZcJiZT>y&Pxuh6g14IE7L7+v&y<-yXU?v~N4)Oa|Pf zs)_F$Oy%f1+VN(0-K{zr{klNWt2c!UNo7>~`u5@*^qjNx_54N1g?;ny+`dvs1V65z z(8sa4T&XQ{O(ryX147;$A?uxsGhVrTSUIt;hSsN4>#jJ9i!a0S1NZSy_ zos=S@tlze5Z}BqPC`QJoQ|Yr3uG0rQ@JOv zfOQ~vi5}34cpN2@7;@S?y^CMV!yp}8>q%(8f1`IMs+(c>fkL=L4n&EWRpd_wsl8po zTmAGy{5mO?O6k*EB^PeRj0|nGd8^PMuDlC%IreRGa2GAQ08Sb)%aKCYaF#AcZRQZau3bHO`788}1nyG(?pm|dH=|P9t9r4!866s$_sB6IOdyxk zDk>im7#%EJ#V=w10@y$fKv%TD%bp@c#ka;$;2!#OA|TpTMhR*LL{Nga1n%U`+F1^&_^my*+bAC4AXj7!uZ8enE+ZphEflcaSKq z=BQ3J9-?(g^U&(CGh#O-@xV^lb?)mq`NlZ~p{r5JO>86Kp^|8RG4aLD2vstbPj;p% zmG$M5C!uS7J466SzX^AjL{-DvD=ONU?P^u{w=;G(zZZMHU(hR=@oBMuT-+~ zce&B>XZA;zjgFzKD{ftY)vAv~b@K8vy@$=#*K()&5wPNMRmvA`PEINFF-3LO-{XU% zbMl`u_~NMux3uonziLp0t%mb;+Ka8meqBVp(JL9F( zvveBJqBTkp$EW9155s7QbjJ)aoan4{g)VO$pNSX9$hal*yj#+!L7bN(6P?7IOFwpt zMUaQ$`7AN@)rG3LDWtl|V3`~&GAh)4B+O|Y)6GH}2`eypEE?GPuiS+uvldQA3+tLV zb~|*V9(8OCDE>LBa7y@8^%Tv&N3r@t8eE{Vv~B%aO!+hZei3DVnxSslr(&^*nqZPwg6t*cAPV z^>y;vcEUW>s5_%L(U~us$%o8HnB+g$(4Jm{Jd_J(M;Swjy+dfnt`V=#`0ca9zosuF z9iq(Sippn@h(BLp2|^TVFoKmW^n1FMxv@=z1JlwMbqHPAX@(IGf3WFyx+CG3a}vXs z5Bn?XUENZ>^|{Z8$)^B**!#SBZ~7t~Dq>Oy4;~-0KmqswN#cb@z{RMXZ>g9~bVl?V zowYjdSJM~|V`5`h7s$-M&Aq8>EM{-!qBLlDcW!ExnH6;;nNID?#|apk8TSNo|7<9# zWD;>)&lmI<7I3z$Id2Bu8|V8SGtG$Ile4l`1MFjq)5J&fbe>Lla#m7LdYM=hueZsO zR*~#xTI8(;8H}VTF#;WRETJCdjJj@7XNK~6qOD%F`-De9?g1UQr|<*RbpYwPscF<3 zHz9i{ya_+uwh59h=Uq!uqD1370fLD;P99Q?SMCrjVt)USo`d;BC>sLAh>s9zMclZ2 z9xm+mz>ME7vd>QX!E9}?OSg@!D1dMnQ0rh*Td6aVB#C;SS4}Wg#fDalm$94%0REPW zyVrP~EQ060Vkxk^@k9t~p^+Z+PrR->xPTtAIfPHm0IQnu1ax>jGc=0#c5s)81}dM8 zui|U(EG5|iwGhOf@AEII5dE-5(vU*82J=RIjbdSy6lBiuuVysCGWIEv8ipN7_9Db) z>o8-V(Gdf`k`b&84@bM81&3ZA=P}MS{mECwarCxf9mOLJm~lcJK4k>i+VJ=*z^S)Q z%@u-=xcC8r0Q>RZA#na^7Vz(8Oefy~87^%zJ9JSb_;BLyFFez^Leg|IQUs0zG2yWG z86hm6KU3%~f!(H)WyPbVqp*0kTrBs>@(<%3PO$#w9s^T;dNHL8ata8HoUO5Hwdj^o zxupD1H8n6X*xSJ3xEyDd*#5Ia7j0C^grp#f(x<>QRPn-aYh^!Ph+Zx{qL#aTQ6$YY z-U;cE@7=0~D`eHHb30yORxJ=$N%`R+oa5x|$vG_7M<-}*ZIJXMLrvlpKN;SV&3B-w zJtMiSzX+H@&Xo@sgMs3G|0Od8zshV72{{3sQMVt#o*&y}i_iVWy&Zqf&oyT-_&lkM z`RtC|fN-^O>Fo0D83GsEf+EW$IhX<%Ggd9Je7Rm|yjWZZD+$i6T06gS?W%swsxkTXXY#n`Q&J z^l4x45~QK0CnV9qewa9W3USIN_3^Qtbkuu#r|S?rhZ|N5^V?iD!{+DOPDi{PoFksL z*yqp2`~m_;_x{OoBb+fm)_f!M&v(1a*<#hjOUxzS-mtzYe5)R?H8mLoe>3@Fv|z{< zP1^Nk8I>Ft+w%)%RE-|(h(fv)x6|KIouYgqQ_-2l`nSHhV`ozPNUhQ>$R0*op18%w zsdffgjX|#?OXpA73^&hWr16M3Ib_QKX&v94N1|WuCx}bB{0hzZ5L9*9#T}4*S3QZW z2mFuZ!MUx~;_tRjo^1;AY)q>|pjAJO*^!EHD;sD@PujFrxBp_)B_IH2i6L_F;;M3K zzi|%3^Uua(B#6KAwz+H+uQ=`M3_xm!M2G}==E5_GNv9a(VEbm{$jl6cW1DK~4Kk!y z`72`6vUp3`j4NCTDh1{{+kqw{*D>9!(q-X^%-Q^OmD)-H)T~q2x$GazE_|-o;@?UJ zZ?xkGcVrHZ(^&&9`k#N;3v!Ll@rsk-4+XEaxn7?(m%G*dU2QpOv50Gl1OW9-Ob*W4 zwsuZev|d`ioz!3D^ahAnWk>gC6$%!kMqyFy{3!|xxS2i7{q`2H%|tWWhNxQS_Vq&p zXK&y4X4{9q^J**W>xE*y!T0%#iv-{$0hoXH&oqnkhI70{Y!ktL^gxe`^}vboX(yY+ zROW5BBof6voX@+;1Wx~2VfYpf)bP&F1)1>My??S=%3k6Hc@bf3bp-cn+Wj$XbE`<< zeG;mTiK^~+>#Wb!h)|5OuV``d)GjAYjkL+#c1x2kJiv;sQWN%~7Mpfrq8-rF8LpFf zi7N`Y!^W=sP9i@V)7G+Y0uK%@ko@~_hOx@LPre|Uf`Z=6jVt0VL5K)?*6fcYu!MvwY6jCVi15QpX;9R7=fTF zevPh1Ar=c{W1Ub6c-WDLUg_snj6xZj_*67}b*y{<;Fgy%KzW8hQb`*x(g6~ij9U*( zOS}8x^*t+j*FZn3UMhdQO(ftir$o>#<6pQTK*m(9%BJ6Q?LT?=ueVKXf^OT?60b+q zU6;dT9)1YBqV7`+-E0G_X|f-B28)S3)A$iQi75`RaPajA|N2hPe0y`>^u4Fx=jR84 zC~#X6ij7JNhew6Eo4-#v+3>6#sy@JfEArZ-ZA;M$bi*DX7Wt%0An==bQd*&Tef4DCFt4TXMZ^Kb|p(s9~g63x!&HTiA6i$u~_dT`U?Mllps zm(IQ(PE)vWa75w70(Kr6Eu)e1roqMm-7x$Q{lUdd3fH;Kwlf#EQ#sLG#9V2z1$ZL~ z*GumkwwJq)H*qtcHfpYO1%q1h^L7zc z-|lm1arU#^e)#I@BeeNX<%x(95)nC?4Y#`$@YqgY{6?G6!Vd&B>a((Oa9qzRN&I*@ zC*Q2MkkL0aE!pr(Aj+4J&#+Bnuc##6eo^9ReCfOg*2u~V`XA%h+Qc22`s72z@5u|k zy&+UrJ9fQX-7hce+d4bPmzL)A18?%1)>UnoELFEBZSDqZzOSV-!YeA)0I@Hl5ihir zD|Rl99T`{jq{5%FXfcNhsV8{4uF+%FRc)NPixGKiWT|eDpge(Vz*iR^PCH$6BCJYF z*;Nf!$-u7$S&r@Di*cxfiY~^AUg>=_hJzv{EwdSVM_yvf3wQ2@oWq}aVy&=@#iwIb zd51Ze4nK}JeT^)g=PQjqs-2$&9%9%BgA5JfM5+&Za`grf0U9ca{HD;B zR#laHeqOH7jve4nzOrH4PozOHe1z$LBPi)FSM82x2>g>T4ImZA?^m=wv zx4wm|a0fj#7c_E8%;?&tCW=2L^%o`*?;J`z3z3oKbiGcCFuA=&918hf0W#T*|2EHv z4-pS|7x*U}=0CINO*6Q5--4G(n<*Bup~%DE{QV3M-ST(7U6tlEOUAn#ZgmHbj=nr# zqU+wHRk#qKpYZ=?0rXjUbK(tmidWEc-0GJs-Q9WegMC_sKToz8TKOj2$6nJj{q>Dx z@!)rDUOd{?Ir0$Zy+XXcc2+GA*1`K4gJOW5lS2UpgS{5&mnN63W4IzkPENWW56IH$ z3b#(K#v;^6&=HLupICJX@m^qr!`Qv~1tilsdR}skP?b}(eEt)EJ-m+(dxs zYr9NcNR^SA3=LD3w8gfLY^3Q-$PzsYTVZ&-~5qt z9bH}Bf43cJ)?-k~Hg#aG&Bs)G>kox7P$TH7G!BLz#F_(`Y+G}3aZ2IG#mz|qpg^$@ znut;IaGU*SHmouvn<_GL-&eDodF8+%VO<@S7kqubLp8`~XzYo!U{)MiJzFy}Jioqj zFW2wxo_yLx)vJFAf%epZ!yA<80*M)U*KePy#YS4H^%Smrr}O;tZ$Ik+=Rk6Fhr5|X zLSJ?TXL{Wd5;uEC6N7Fnvp)_%8TiBm$6Ia)XHOF@M;p@GGnWJNzT)q}Q9J@)^;Hn%WZZ%TnGOz2O$pQC(LlMTp>~( zC86g$yqe99v8jDNcH=IE>c!ogBU>T&19T$x0QA00)1a}ZCq!9|s}j8+F9_Svyamql=!*L<;%l``N6@eMgpJUbG4(Rlao_;Gp8j}Y-su- zL61b1xlHn{e4(WV#|8~aD|!G_k3-=s;E z=7pH43_ms-Iyzv(FtgG$X&NInm7strMPR8Z3B9*$H=T-;TlRc73r06Aca4g^VFv*$ zmgC9S6fyx9Y*l*N}sLD#_80KqTBOoc$F-*5tIxSnjY0&$@P@}E~{3;YHYH0AAad}R3C1ft=o`~CBU z{nGAU0u-3rLb`~c-=5yDTl^F9;5={c`1I6Nvh@mb^el99;^N~AfBnsq*Z%I6u9 zW=V#xIyE)+sBYS+-ykiEmV@~2CH!w~18x`fYU*kcbwa*Y!0&WnmG9#=Ns@ocFU9d- zl7w~RkytvAmw3T(?YTq*rd-U$aa>(SpzX-4p@mssha{?Zsi(2mL~SCS!#jQ&&b=ni zfp6N~2qj&)91`OnEQ%d#Yjrp`6W%xzy2XJZ(Udswz)mbUZn1`S)A|4`V}D-tl_zWV zz7mDb)j$QJ?R4$|)I~No-+_S=`-FAvGVxn`-t1eS?yt8F`gov9;K<>svtccv)Tc%8 z-+~Vn4VKmo>4T1*-N0Y**ZGsg%J3A;qDUXdn$k>gxa(T^m)d=fgl&7>({!tW*#bUy zE2-q-FTBRRQsuo793SA6;^NRfA`0G*iGHrBEJW1QI4;#EmoY~r{Qc{z>R+d_8J=n4 zheLw`DeN(s!KP_MNidfifOZw%W)#AnCqK#Z=CPfDC?T&?Vj_+p;L-1F-|GXFTvQ;g zVsw8=O0XZhY0o5-e>ZMmX0m_$a`#{k)o%9LU(X*88;8x}=_i`}VAwVci?_&<4uzn; z4y_Uy?^Q0_j2$njN2l>qH|2l(F;}W&NeA^2{?lxC4{Kpz#2SW9=!44{ga&B{j*f60 zpL3-4X|Jr&5oNn8qtXVq@$a7kUDkg|ZQ6xvZ~q&_LJa(=ITS6~S11APZZRz_e5COV zRrydTP?HWcj_Y{+UhRK}1P2I3F&SWR9NNy6*X;KVW!D##_*gB1BD}e+Ew<&j_Ec$nz$cc~X+$b5j zxJQ=g)aB+PJrm5eyYqsOuHF(;9Bsq@nmu2VpsSQPqT@in3eK*n0_0+9+0_Sq!zq{%a!2tBaT2M zp2XVpTv88de0-o)^Z4(+_wZnsR#j|E%sauWb1KKRtN-d4mFzP`ZX`F~Vd!q?@K=Hy zh6+8Sod)n9$lY-vvkI<4N<|QvHhrs~7#O^CbmxaYjthArb|ks!Ft5iclK2NTAYpMD z`6{#&iMT&!Puaky-MRIFJR*ZmN<3G$m8pRIk+BO%6#iEM!a~(0p*LNdWOEuih+)_; z_kLnfrtrIYCVv;2{;YR+#=#+B%2}s0)Zab7W)mF`UheHk%6@9q9BN#I+|z_&n)iDB zus5U{#U=oTe$|gr$O@}lXC`n26>Xi&uB`61A5LFxB2oQ0xy^F?tDb?wbN$0U(mjOA zscxfe!{z$K^c|smrr_^rf4N1wlDbaOj{{UZ%S%JK2m<*O8+1g3Xme-h^hAz;=!~cf zujacFsDDt_ETuu{lQ#L2X(C09J>d85oJtB?+C<>ZECd=NJ#60h%%E{}{fAA!{6k_K z{!X!!hed)~n+qC@Cp5PdWdul-`!5K-o0BPxR?w1|LWnj!WqN zoZJ^}B~lOmNGVM#hCx0k^wfk4M}b@Cs*75Ll4J4r4?LFR%Z={arw7=#EG$- z=u<;#{Reh0(C34i0vQ&1g$9cX{Gqg%;4aV!?n~^o)ZBDBLIg+Xo|AK$b9G;EEVlcC zFRH6)sU8yA5^DI5u&0$~whXY$9M=UgoLxB({W+tY z{=%6Rt#YBjC*FxRSNF>}j!&(*D%b>Uk&EdBZ;SQcrW(r=FRvyzuEC#sE!{1f*^S!+ zE^!NjE`?OZ{!6@p|86>+?ppPGn#Xy{^Y@?TXzlZx-TqN$a(q)Mayu27X=rVXnc_C_ zd$i7;Vu*{23wV4adbuL1zdY}RB$bnJbid$Iy}wyPm7+w@{z!;CGYh>hK5iTyr@`+= z+n#~+p_7-37vI%0uc`K%wXK&7Eod6~bYrg#rTGt`LqoFpV!kO*F`sfMR1kD$FzQFf z9MaXMQa<{#ZLaP<#B`zanBDsuzN7aLl8BDTu{H*icOXG+stSwmPM;R{c*yqU=IohQ zW8>-G!zCUssV~!ab+zosu!tZDE32CNEflM4uF^&Au8%m_%yHZq4UwQi#Y;B$r3o54 zfec;8Iz)9S)ddLaMt*)|)_DY%DrNdy4iP=>b9{@4F7Ip{KXL6Q8($ohqa+IhY6|DH zG3D)Le!JC65<0J0@3#t#|M82T4=QGu6mjGGu0CQ1dSPU1OZoL2-EpuN-6O6D-)Tn5tA$F zIzf-srcE82JGE_F{S3?z_l5M+>4H=WD+FZ0L^-=qA5FIre6{LWpR7NyyNdzc{Rvjq zuB+qfn+~y{>zu8tBgO(~=&SUyo7vl!jsWSoXNQq^UYUVWGsvi^uTM<;Kl>(UWpAfs z>Y4;e%2*cFk!C+xSaS;G z;U7i(Ycd#KMCU}u{B6#LfBM*^+`Jwr%x=Q*N6~= zTkC}e4Es&%nx%XnSo%{$J81CqBT>RA4EG}Hlit70Z zsRa+DPs0fOp?D0roW6-P*x?3v%^ZrRTh*E*%Um|_GPXxqJpN6SzEl%Eg=-tA+a-2M z#GrTDPbJ^OBsDPYIDS5!HqQup{4--B0OEGBTq6A8Nt-`5AJXyDVd zYz3>JryviLKFJnLO*7Q@J_=9Cg%!N~)kz>1`_#KtAEtF1;yn<%bG!AHK^K*H0X-OB zZpR5QDFpkV-N6?E4Faa7E`PVXuXhE7!J(nubQr%)nZ8+%E>y##-?35b%~}b;V|QVm zdCQ+r#yt0@UDav9aKXdsbfa|@9bfY4TX2Iu^!i%Ytm7J(E`YxwPWyLKa_Y{yII z(ONv7nf4@{WeT#n_PhzFQEr*;aRj~AGb`<{wp6d{vte868yM_N3FzTyEF21MNq1jB z#bYY5QZExHj-7$2>7nWSkf~YV%^~fFe{)xgfe#vl2vyJorkhF<6=C7>>BKdd<49?` z_ibVeY9Fy1J%le{K_^O{PU`oJff~Xl-(Cgy3ceSceQK4rNYyU`ux<1VjptOi^y*~L z!*;z5lhMRYM-}K;i~a^JyZ;aA?pUq3X0@{1wr~2Vxd?Cl%O~Q z&GuB+2YP3EKoc?A8d{1wE^1&iIh{J@u+)mfW~sq;Wn+waP|uWZ+r$o?{Kc{KMUm7r zr%D)$6fFuF85ufa7-^Qu9Ds?K+R9h8Z4F~?Y!iV1n@I6f72n7p%&7J;OpNTtUs-wu zaU}Z0KB1ik111TQ8^4?9K)*0?XxQt-d&vO9W{}Utgz98*d_{6dQOY{tN8Qlmms46= zs(?zW*(2Th{XaT46L`;Nt6H!-noPI;%L~I19>NPlBByWECFNf?R3CK9+VgUyRALx1~)mn(UB?D^Fj&^rVfSz8)0)ZQ8{E~%y=l~go&&5i?3>s;s?PuT2lI+&gdlG9$@^x{ zvQ1vx7BWGT8^?;Cr?sw#ih-jmkjwg7)u%7go^x ze8TA3+VRPlmXhtm#JAS5`ON9IgFT#1PuV}-a9X;HL$Z&LhqsV{Xw7w?kX}c=>wD=N zaQj{3n-ikZB-FsqS$Xp$VCge^&hO3qPJEm`54F9q-gYEpoE7wVW8xXn8tb$eFB1Oq z`g2yqn~Dsj=rY{~DN7-qs@~REmu~UUOK07BGD@ z(lj#uxLs@)JuQHdXOedeb)J9Pxv(`NgiVc zcXN?=0vgVbBv9D8)D0m~1?b~>XJ{0Efj*;qiHBhK!|5;32`F9NP~5rxuqdcKg`b@3 z4$U5+PT_yUdj)Sdww{&ITv9s@och(kHxt2kSc811(%wo1M7p@59a5MEz7i-Y2>|EW z@_VF@(5`Gs81w`1(FwoRNJi>P4wnQaQ~?G_W2O4X&j|Rz0-Rt(mpT)Us@3pIb0;Qd zGJLr8j*|H!^g~5a&&y;hf9<#0=Ea!5`j?UUbb2u=2Qhs_U?P-h1ls2gefr>B4 zUO%sU2YQp8VN$ejJw!ZwJ$~O~2`gHE_D!bs9CurEkxp+nmpefZ&H6t}H;~Rk4Nu^r zFmeZY8vPQ3S#4@tc>kZ-`|3a&2|y`znf5iT)m3Te44h((P2pCI6@R^%zlG#Ovk7gn zVL(l7tt@tlnPc_9&VYRAxZHm0NSa%4P_KObk}RNtw8hZf%0SgX80x8jv-RU3UsP9? z^{IsNc-Ocm(&U=Q=4wCZ2HY8_<_h-fmcvQr+c>$nd_6uT$>%Qr74#}BKuBcYY6uwq z`Zh%5d-M|LlP2)(o&m-nV=1KucYbIFTD?%o5Qj6MXZmdlsy;N@;@uPKd*8D6MiQHU zdc>*P##yNUapDCQ8xE}S*!rBW3mF+)MM=DbK>2behv~uO_jy2!wWVchG1Gtd+j@Vy ze_X5&BG9SwgG%|?lw=TQm;3%|j>Zavz<$Qa(zUMh)^{mb-a!RupzyZ<907#fu-U?A>pq&zZ@XFjAceN#+9@VX z3Yc#rg~6<{7pMZy!~OL7rmg`jfw#(tNcldRXIWcH)~pvgX@#pfn^zLecRWno+Oa`> zmR+VnQB0;J{b?AeZgz!k(NID9qokRwbc<@iL1pn;@X+B;B^ztZvP&&X@?l8uPc|j( zz+XYeB7$p0`MjBBr0>{1^sP%3y8ASnNG){^m?w5#L!(BE$&XWBujrzq!RvsX4AX=TmbIVe<{b5(>oj<9&Wrhxj^M{ zn61U+)YOg*PR-qkAMMWp3^XDrdOFp@koB-0 z4WAyV4@aeq&vJBlk^5cy1-CBfu1_y{;iu@dP*?S6XvCml?mhGw3;(Jf6O(ncM%GEx zqn~Lm^edJ6I4(pbfd;fVA@yBUR28{!7u2(G#$BwMdwL8lPLF?O3pbK7CtMPxx&D=c zK#6|b=rEPU6z~uIh}ehZ;AwhGYvOmpOBZcMEedHQAlYQVbCTbMJzgTS)9U5aJ1A2A zHUMj{hld^XzsT30od4$1)fqZT?!`5nWJ3Zb?&#hW*crU|f`z5?ohRJ?Kcv`m&`jPN zzwoEp&y|I@{Dgg)H$bdZzf``|8NtS}H2npEUHij*MeYxGdT9A#uHey?|03vjp@*RU zIrBR5YIDHWx9Fp%8|>Y2oiyIGTnA;5f^{of(rKw0sC7EJ5+D>7UM5>vUv$4!;#U<; zuM@vJrSDtN6*Z8om9S{eWV3VjXnCfHz40`k8IQbEob_JZuQ{HKthwsSUHzTf36dDi z$*>0I+rG?yyPFB+g}enGuQ1R2FB^j*9bNv)oiaKX;vqTzRSiAxckjKx+c4Dm`(Gj7 z0djO5=ys!4NVxP-%b&RDyD<|=!dZk76&F76<7)GJAcKA>Apa5*6c`?M3R7nDuv^EGBcYaiUlju-1+f699st}TGqv4`EUh1^&AY5Tz!nI>z zTMnymHM`n@Bd=;>eVUnKIipU+8K(o-0PA-ajQ+V^182EHsSoadY1Sthxn zi4B20_7cv$BOB+HSc0v09nKzDBM+~eT9n)O^P=_Q+bxho9-cL&r7!+U{t%LzXDb6J z1LnOVe^(KN{*I9_-K+{$$Pjq-HkMYfhIDs@rxJ4ke;Y0|R&nIGL&t7sBfiWcYgayeb7_y!kZbW+IyT`Fyza$1hY#>rN1`qn_ZC zM-zjG;%NEi#VYL3LDGLoHaTJ-*lh+VQjWKXJpOJX?sw1uQ&z>8Y@EJNCrvmMG*{I< zy*YTgmq3Ly-Lx3oxT3^?*WZwCZ_oCLUn)Rkrtylb3ef4u;=RuIu}upU9&AgSt#ccC z#Gg3?FIKscZK{qkJ*|8mYG znnl2>D?P#0D3FkI%_fh`OWKkjUZnRW`rwL|C4sB7y;m-y*S6lU1kv^d|Ah-73mcPD zo_<-%Y=5!dm@|XGQO$iK-D?i)f4W;o(FH<=bMzu3u=kn`RZnaRBxwAb)tgf!4&)P~ z0V&CE;7eb^XO}r2qR_1>I^3~nv_1dl+jAk)^ULR$8S~co_h&SPk~N9lYtWsNXG(Fu zE!cbuV{0~%4Rvj8O7?v?m|>@R(RO#sUUXE0kIr%^{CQ!CpKec*aWZ07Xnc_CRpz|X z^O480=VV}0&>N&{IZB3MWROzI+-;Irt7?mAF?s3|rf_0r2#RQmoA~@Nmr9~k&xXCH z5Wn43?9iCKV~xi8;lj;?v-klZH4Zx~IeDn~K`|Vk1|=u$BQ^&$W|;KQ94m&(eBlMX zFXAd>HwDpjW6@Q6Go5UaIt!{N^fVKkS|$BVzj^*Df-wX(^i zz3zthr`|_bp|5|Dk+3KMGtJqBSNY;U6T;c&$x;7pyfzj2S9?n{mfg^Lkd5-~EMe^@ zortpwvod~P5uk7}!J$i&1-32iL*DDZ6%>2mwOjUJpsfp9>sRMh^VHQ~)gpY-3lAAGZ$CQ!6a73eIA}$6@7PSddDq0p` zU|_FUodWHYvpZrtKz4j;k+duvu*ROrerLQjWSAPCiXe1V26xbxeTERJ?#GpRN zCpPj1#lYbQC25vW6a`p=SWDpwQr+bB-~N`@KfB3)__zN{o^55|9f^3fzeNhDO(~sV zn};ZHD1Af=j26M+6pMF)F^YOcO-%RqC0-0BNdmCrOVmS}WOQ!d;$QxYe@^Gb3;fN0 z=QSD$7!TeFzAR9&*u>)1g;Oi_B;shelK=gG`^Q}CDu3s{_;c*m7Kv?AYKh4VMGsQl z&2gRC1K`g?_t1omvv#WPwMWSmo0m+t&*^R-ntC6Rd*USzZN$h!`|!+Wb~l$Ro`Y7z zypL{N1+w1&W>ghpA#+PdO32K%fNpPEVfuL{Q_e$~V*bYcM|CVfsN{?VTd$9D&wEUp z!8PTx>h>?>p17+pBsm^k97Sc9XTDdvj4I&BFd*9&kn7lJkX_nFx4mY}0Z=A@3Dfo= z+n$B7aXP%~2$zfQHh$%pvJNy zCcjHX1RgRks)L7cIv_j##@uDsH9N=1_NY!rTCU3OP&4`@Q}lz}yN~p4Yl80nw-Z;S zxFV~JL0AFgsj?qzAV$C2m{GH19M)rDBs)U3jeo5(!A6M1l?nY30Svvzm{pl>-5g^` zf1LdqAYj`13#gGyd#P{}cY!-~3bl`~TtZQdVhf>dEqup`j)Lgv1!ESFBnvhOCiN6a&0^ z)MgkXWEPS{C?z9JbG&*MuU_F_{fl37;qo$)l>Fm=^uO>w|GmG*Tc2IUmcm>kL8L%^ zL5k2(i%A4y9D`zjbB0U-?+{}H{%nLS7p(8`&;RfLi+}R}{!5ky79|Bdsdc1&!2k14 z|4;t&zx{Xk)4%l}^I!dU|0C~TypAb_vQ#V%@s9tWz4MNfq$>CR_c^Dkx_i>*%;o^> zE+Ww-NDu>ZMUadFzG4pCs~8cyMpRHhFTcUTvk<7Z{Jr>E+K=bZCAzvp>=kCIlDR>TghGX$Q(^F2uU6H3J* z&N{TR_!^4+B{p@g=b}r#$6p_P2rE8e7~#-_k)}=Bh)WR0Ax=5MD8?y^Zz6JGiEI9N z1=rnr7sZrV!A22L7^c-N{7Qw3F=KG74QpeJ;Rj=FtZkbumwE)t>0y0+i85Hts!T(5 zM@=8hhRA0(y)-xgToVUGeF{ahhfHm>n$-M{0ffi3zcW-H>-#m>XN@8}QsY8ggK^b* zUCV^9_5VYLn$Jdk9PSVv1hkPxbM6KiV>Q}D8vai;(N~+uO&v(k_<7}I*?M2M8)Ter z1i+N5lbLEIEe4FE{vB=*Fi%jTGW~X$XEu_*I++;YeQ*4IhppuCXWuapcp2^@mLc|{ zWZseamTN{9U6X4dLyU8ny*^U)q7s>vtxP6O?i~hMmpX7MBSTV?{FIp!r_(<%E z_b!A*O_jLU!C~XU4UUqo{ml?z9-s z&!M9tCN5#LAYP7ABxo;(xDd~Vs8~?G5St>_844;-f63r!i&Y_3Jgf=Ojaf@}q(F&S99AqY0wo$#XeBl;;Az3);Z#7LB1M~c6FgsGOagAW z*x__Q$p}U~VjGiFV8aqWx#-9Ih{k zY$`eOzQ#p8#Ll7pJkG@AgQTiNY%M59E|;TMIbs*#l!GYc5)qBn8e&I#kf)Dcj0JRo zejF{aLYWfS5M5})Sn#w$qd_a8G!@1QC~;#E`D1OYjkU419knDBH{rDD>e|dxt5)F$ z3A1|Q)zUaSicuI+&Lvl)lq05wFccdZ6a&eJRbA+@b*5^>4Ei`X{C7sAkZ1bu2KU{$ zgxDatV{7{C>b&-i(pnhCxEY?0!%E}wg2S)8ON{jabM1zYHmKo-P?c_1q7{bgv$Ig0CHa=RHF3_>dH0)_g@&(D1J}fkYV{>G zg1$A!2O+~o2-`~aw9z}as1UCYfODMP!ZpdJ)NI{3^HOQX#`t~$U`(4fjcL=SCDH9; zAjVkR?v*bmC>5l{mN9WuM0pw2p=EFSjbdGO-`-{A=>R~?h$nN#t!MR~&CZ@G#OeBnG^eZ+~J{=Rn;J^whTeDK4x&soN2 zKJp%Z{(}p+`=OO=>bLB(*KW+7K8@?Hy$<{wN1t*E$G_z$ViS?`^W1gQoxJPnKZEV( zgm-;_H@x{6T)3X~YoFtSA79L^x8KXONwYZqz3=9r75kA_8_<3TrHEFmu(q4ae)W5P ze%Y_-v;FM1&%bfzr~W;Y#ULZRh{oDjdwH~NLO3-_{o58KS)=jn?R`$SZ+UbTGGMJW zQ4}+0?i^;$+5u;6W?n~0+0F123}D3^%8sbz;2b>N-3?*G490C)PrD(mHs#I@@w|#* z-Cztu6^n3nW2%XQAA|rX^F8Nk$&Uw6P&B(1ZiL^F1_Apc1qYfbpdt;{B-??JzF(S< zh2iF={xdfSme%Y$>Yq6jWr++ShlBmx4fFir=6)n{lKrk724~z-{HW2GhAYl9)YuvY z$;fbkkqj>v(!^Z5VfI5a;~H!%P0#I%MKrbzTlt7C-&K-C+=pQ_s6O*gu~o-Zjj`7F zOi@UzaG}E2c|5H#N^`+?zssl2I)hJt`jg!8f438OF&F>%NBr!UzhQ?3i#hV3z3AQW z0*^oOWWvD{_YpcDYXvO{-$Ti?&=N&Vn=+AC9ef~fIpH{@do$;M{ReE=)C;0%Z=FQw zLbODn!X#xP&a+_Aj=byCcXQM+N8q;x;OE(Gmz~+T`Wept{Fhk2VKYY`cO*})Ud35w zpUb*+8@T(noA~*EUq;)wN$h{XLHJrhv4@;1p_L^z2JHvz_v(EJiktY{=RVGNfAmA1 zS>H{l3e11SGIp5kbN@YeQ4B*Kdf-mFONM`Y(_!@YbaKx4hxtHF=IxWlDh`_3mho_n6T5B@>b(KEXzk3wT=q-L` zm66=mln@?k&4WKl)go0>Lb<=cKbcDYX*bdd23^=CNHquC6Bkwovah3g{2 z0ANFnXDCVtHAtU_0}qFr6W7FgG&}C5H;ZkGczkO`tM6Ml?s1!A}{Qo)xMh@p*#XMLPWs&|CO(ifKKE%njg z-;0S2HtOZC_dY<|`dIbkDz5(16_jY>jI++-^!LA;WeescJRHuUj6r)s zjOD%W{Q#$&d@@reKcr{Vmc#eM4L2Q2hY3{w}uUvQYO$6hnvfp02 zlcPjr9Se3@!RhZkh1t_4lIxhlyFc`CjyQN<%;tzS&-Y?Q5f!`HWsen{`oRzI@BjW@ zViR-4HP`d_19!3d*>xQJx+D3>M?cKDUpk%ExSwDD>JRkC8tW833hR4}AJ>XxD2aSJ(bEQ z8UCVd)riql!enrYfZ@Q0EX6{N5iw$48vTpZiqP^?mLa%U2jMWgU_EE!Fp%cxY@BWge(l!uR%TKt~O$4I2sf; z%>0bhm>iCJq>uO#;j5@glk(dveavuC7(;!R zA>?DQvw$W9cMgZIFE{DA8DJ(7wXP zC4A50BOg7TMYE<8=p0?D15KI9B8WnKe+Qy4WZn7=SZ_QXv`<&4|C=hF5pK$`~j~%=)1h}&_ns}Ki8d6|ZON zgjSw;x`^@v`n`5Kyoj8{gyb+@j>&&SJ^^E5L=>LqAtIDYr3z3G$H~vO)IPK}t(4?>7-Ldh+tz>% z%^LPDE$t=(Q52C2a)eQcq#@i|>kMdO@qLdl3<-h&YwU>LHE2|GWKm7A{%{NKMJ`F%V;{ZAa&{!-yr;7Db3JF=7m>pL`m>w-0AE#f~}bHYd-t zsS|m+b1iq@b~Dp@UZ98~G?v&2Egc=W$JTP+J@*il)=@Hsg0+O5n|W&W(=2}FEBTL) zewe>paxuTWeluPWQ1l!+u&i74FumbEC}}6`FJX0&XI4MLXFm6z>Ab@dozXV>b>z?g(;WM3#u59x3WEa^V&f2k zJl4i|N+?D#y+wydM356oVINj2j2NPxHLQH}39kFoHT0K?9Jpe47VJ14-0!*N#%tMW z#}1x*_$D@Xl~{4$j(FnmwW7OwJ^xs>hVc_RnKpG2U-|M^`1<$$i^m>&oG^;eDyhVx zoQD!!WquxG`y6X8o69pX#LHzXwN`{_l^(5i%Ajo$vc#>3ph$inj7f4otA;!^Kt?mI z{JTjXJ&L6w>({TRrKOej_O{G3%TXXnbV&kixUHNohjS6W@6*}YO|jU|v}x0t0E-|9 z0z`zhYuD1**~y|si^c$q5z6Z2mtPLRo&oWO#+Q?4*BDjmoWl zoCVR_)@Uiaf8C2P=xwbohlf?U_@-(OeW+Gjo;Y$#GjKr!va2 zJCb_z|6(e()|y8id6>&CyNum;-<`#a7ynaJO1#lpZzm(K@;%nJ<1?1^LIy~*kS%oSSyyp?tcg2{v$XRE645J)-9JniAKK(tcc=f*Ac<0^R_SZY;(3BGiAh+Li3*Lko9I)@+L@L0FBCiaDJs7{82#-Jut-cWB(`EW7_z{RE*FKRa z?!JltyyZ4Z7+$saejIV={kS%-CIEChV4z3iH&HN8E(Z0_&H*B&O0 z2>gJSwhnyl6NV)`rLYE^7PNJMNh6aKu}W&yS4!=<oqKo%J7kq>E zwsuS$gE*8E;;=|pe}D2@O4HijPM{UWCGP}59H@kDnWU(NLOGWf^-FGJ!Qn)swBTIK zv(G)vM?d;8cHC(>XP$WmO6j!RTs8qTofWYtn?!$zgD-sU^K97M#dp5_9VSi~k9ASP zzk^0OLCVUDZ1)#hRbj5AGbE+Joh0*Ft_qRpGRc})G)_!9uVrA^r1PaR(f##hI)&qp zKb|Ox=HIa*^u^O04o{DYdX?x(=Z#S5vRn6i4a}7{{|N^sROj? z?fT(-H0Ay~2Z&8e&f4^kSG8a2?Utby(hi*8`k@ctqpWoW$T}La zF1X?5MqUQQo`W=F42u^p=BT5NN(rmObO}{j9@*gVQ$57hoawdekO+wrNpDwI`L8s^ z2D_CYhPb2*k}I#INcC8EwR`a;7%(;lVo=??A~{}n{0CWfs33R{M~LzX^BuIcP2i1h zI))`X@68Jvdswq}4L`l~SNP#3RBtc4>~%2bd}A5UZR)~=CGuXt z>S;=GNIs4U+UGK7)?^C71kOJ9Jf3=P4NG_4ky#xbEZuVj3ubmOE4Lrxr_4r?Lpcv4 zmT}X^;dDFyar!4Y^oeKL(A`bW!Nkea(ApzEehOdu;%9k$)mp;-Zj|6paK@hyNs zs{kB)`77tL`nl&Q_C=s`jGHo**>mU8*R`3ao?gY@?tcuc^PF(Z5sV8I+Qv+sw~SAm zbvA_^mJ+KL-t(c4(CZ4csD760|2ocCu!8vuW-_VW@a6%K6v^^nZ3g#lyzv) zgj#UMV8x*&P7F}nPN=#GoS;SFFgOd^Ih4*55n@~k?OKSeV0(I4v+7x*TsPJh$*Xo^ z7l8(G25}xnd^}Hm{I4qv&!RS2JMZ~3zl*~8?T2KPk#2A%a z>#d{|lYLZ`}zK@=EkQ~Dr5#9^#KiHBIhNsNd~fb7^Jexlzx2U?|Mr)Wg$ zFv&sA<{MW^F9Cch$5?4SMYm*YD1MD!DjMo^O;o=#nfLCUJ2qMWS zj`4hN;Ce`%F)W!iahdsUU=h1i0e2ZIqRjc7ojdDl)cC5sZWEb@x(Wmh?9r^>q?X?& z^O@8R%$4_*b}2NQRM!!7&AZ9i|5X{T18{}~f}{eZMJB@6$(fKHl69aYSqtCy8jmMa z*`!(fz7iBG0}XRmH@-xDvj{Hfhvy|}-AWIGkkuKRHjzo^lDSeD8?Dg^>C&;=?EZqG zxi9LF50OM>%FjV8iUf$)qOlJ8`-`*`@>MLe@&suJLNuy;u_gFG`MJhew5NuN^s-5b z)Xey@shyH6QJSXtujjVE|Ni@V{`u!Q?6AXFx^$=7``>{zHj!Iwvi2y9jd3^v&j;tQ zC|q1Zc^;xX5QVh{r3A4K*CcYV#UO@qB%2}&0=sHtP$OQZvGx*bQa)MvNj&by~w^3r$&6ZN4WGi*K*p2jwNpmUF%-p;WewtsR9#b%w+PE4z!K%q?e7| z&-21l>*()?_VJTgG=B#QuEdj1tz_-yb?6|^?3ue#n2@8r;PKIa{|`!S)9LSdo>i;X zP?$W4S#!q|m@fWy?@Hp<87x~cog5MWc;a!oy8D^jI*w@z<`J|8i0P$ob0>d)d>sYV zOXs?Eh$>L>TQH(2cnU?#^;i9lzyIw)_IUl9+5MHTpy!1(%$UEJu(y*3AAJgc{0_`n zv=CEV%|j2Z#hCqsJ-1q{kSFOg&6_`171`}sY#Fbv4;5kBRCln1XX7l9p!W8Dtnw=gw+v|Db1{kNtCLT9u9kC7Zd_~}UiJ|T1@T5o-g%~FZ*N`(9 ztI^6w#DH~pfk!d6SSJL&4{?||avcuM>8F1bQNqPPz8L3{6rpW1O_C_+sm)`eguFVj zFp_B~r)CXl2T5b&u|oA89@B@|1m6u+DUUG$rsy9{? z<+vH^N_ffxbZV3(?=sfnY1K3&R5n2*i#NpiK|JqXmFN@!AEN%`8Ct+N&;?g{USwjl zO89TG=2+EGbd3#7r4gtxnuo2(ArO`zmrL-Z%1dnBFPA|Joh+GC)jg4&Yc&Wkk<4+* z-=hSZ64VadVUihBV}n~7gb|1mN`0VoYQkET7||*d6{V8^Bjr`+(3luc`vd1{ka-iS z?h6O_I+p@!wUb*;>o2cG84i>|z^4%vov8tNY^8#n!=@1k8IVDeJ?5n%k<4dfOUh!A z1`|;R)kHvN2T=D{sn_|X*>Tv~KBezaDg{EcBQ_DDJ#=~)DS_6O{NP|u-A+m7-lZ`T z)!0F4Fb_kmh146bY1Bs)M=0f?MA9goT8lNY6QVRaKvltT$}+0>SIZ|+O0mnRC38;z z03ZNKL_t(8yKw5MrxM38*18m2j&arz_yrU(v57zw&REFjaG@dR2PknwVT7nW;sq!% zh(Iy4s34hp>k>CfGaGAE(@DyX;FobE&A%X)kv;Kjb*(cBf#>^4)c|LiJ9{?YzVKUk zo+g*eB^3k^Ol*=v+X<4Q?G^RXrNO$c_Goofqk59FC*xsCLzT*BlFJm!R<4$A*rCDc z8o!9tv7;xR9$nGcq;fI9^5x6;*-tLU_XFD6TFK>dnef;uXyEF0Tx}RFxU%M?Y|S^M zCXy-Jx3vG|%V9vM-ynzQ7=SU>UWUyRg+w8~?qiod_T`QLwl~Y>PT-z?4nH{ujQ&%FYc$MMrM(EIx6@ne4J;8XI1?m5-fu4&9xW$c|&$?1g;u{Qu;^J8$H? zZ-0XgJ$*PC$AU!%a@KjDq}4Ng>zn7%Uzo+`&p3j!KJ_E!?sWj4{oH$S&p*sLXMcs+ zuX+n#{^Wc4uk*jgpKiE`*eSez;q}L!$U8sy4s_JRSHE-yH$V6k9YGG)7vW?w+E|n* zEEbKWcVj0Sjo&t&{SJ8@6J0+AJT|O*lnc)LI_*2`!WYgxm(G><^6^hyz-x{^nS!6= z>z_D{$73O1+=QR&pwKaep016w$G!ATUdV;tJco|3gp(3~e|ROI`|Ld^Q)J5Q1$^== z=d*lTKM&q{CFgzPJ3PBCB&Q1$X6(sFKJk9`TG)u)|Y3FydzAPQ`x@EmB#RgJbdy*UFC0K+tCNjA6^pL-gPfAe=!&zq_^J`!|A z%2q!*=;iubKgz0=@Q#zditlwGUN`SMeNT=%;Sj_se5EKAAqWNDx*UF3=JQ~^g1er_Daz9LH8;5N=^gBWA&U%Qow^_&CW_n9$cdQEA+1Oc~$BB)z_ ziZ)78Z2Zlp&zQ2Zn`wnYfkL5xa}dW-N-j7C6pXd8wk?~n4y80Lrob zf2GTK9JpjUlgCpKV8G_OHbqb-}yf4dnWSEcbve^ zix=Re2=)ms{IBnzay#a@Ug&v#xVJYwy1g)jF5uyX}eZHlj+;vvjXhdHSEsjDyH z7k50z{r5f0tCvhfE63c~3pnll$8+a(zu`|;-OBYhKf>aJr}O>ueoVhiYSl_sKfjvY_dSf0PJ9Ppe@tsjI}p7j;P}MaO~chC z57J91+{_DWJNeNME+O98M$9{Wp&>LUFPriQ< z&#m7`u6;ZQAM!euEMC;`jlcZmFAxzPee5wrBqjHBFYXYDQb;}k1930>;*w5EJ?(UN zhbcctk``(!;nc&`<8%mqVj#N!B*w}nbbcK_y7;}cPX@e9Dow^%v2hZ$aPjvaVB*9n zT=AC=;pa(HFw*D^3tu?vPKc+08J{>4BnC;U4Yd~9X>Cmmq}V_{535&i!q_MY=N6Tu zrVOcDB#w+qNP_AE922JYaMRyD(0~-7R;`d2zWB-KnKq|~TmJSbj7ca9uDJ}(eBbi~ zen4zuS_%cCC>}7UGiFQ}IIA{^q9jb234ab zF-Wb2HETAe)tF>Jidj7iPx6kKsk2Jlc-Ma*UTWN?2DtWy4=%cJEw+F9Kq=9Q!4L44YegiykXWc8xDJ~Y) zt?R-VgVI~Be=8B??RVOW^xF|ICy8 z?EBxwYi+^KI*<>(?G3EF`$jJR?e$EYIguTAJBSsp+LIh%bIz?X)p)FJwQ*sp6#=x? zl|v#a$>&w_#>!zwqXlWZG@))(0}tu|3`q|?&+~xl__XrIA|A%tSlh;zr-=;O3iscB zBM;nBq!h#c|Mpf+KJiG}ix2R^>Qxk#$GPX8$2h-}I4tnY+I4K`?4k3y^~|4lAg_D< z8<@}*5EVOF{oI}O^z^e?Ht?~}e419%h3}^G^vXw3W+}!AMR7!4JIB5G5bpi{@3`aE zJGkS{zmi|L4+reAGk^H6-{WG%RX1MG|6O%8{rQm1s+Gr{dW`2DeiE_MIO)XWSv)D^ zv6+*(vCv80Dpc4*E(azqFn+=AeEoX|bHklCa_KKF;+o(8iaYLH&5ytPIo|NvgZaPn ze!w07cL)8CKa43%VbT1#1REbjoJY&}c6NK^BIeGS&T}h^%wF;eX1=hTU*7sG-Cg}S z(KxY8nKYI83zsrADDvkkZ{VpXRC_7OxQ6L-rqbV2bh&z_&g zxVr&LwR4WQp7>UjQe1M$CD^n&&5Jr!4v@0tmjs#n9l|N^pOARgjfbNJKu-lVHStP{!MZ*?DMtbLS^5D`@Qx2Guj{H!B2{u8r6T<2*Y-|4qM3je zmwI%?C8-mhR}EEH-gx3L@iG(F^>-+{tIX+0km4B=l7DNcA@-|hnGE2JbEe@1(<;U; z8eV@Ky#BapiJ|EwpiCof2x;I%cO>&)Dt3^|S3kFPUiIvA%70f1#GgbQ&c*YSIjN^0 zloY(N7G^Jkv(8zJxWvE}(I749pW1mZzrz~v(UV6@2ZTKzFYvQ?73Ge!OcGX<(-L2sfQo%{A#7y0drhlGuxg#)+YbF^pdA} z*PL$>*=`hB@nSC1A zJXJCYVdd(t4Um0n(`PDxNyG*`sg-@sej6tr ze=T7t1?t2k8(17?e(n_B{qbc3bPk+gRf(tm@d$GkEaKQB{*7OL z>r32q?<$Ua_0FiiXSn;W+d1Tf53_V>j(5HPLe}lOkg!zbwMQJq-miQmp$@Pv#)%pj z@jluhMq=0{d2&g3R-;pDl<#SmAec!`nPd+51T`*~zsp7fRC3SKJha+N#*I~S7zN|P zxkQnRqNoPYC@Xj(l7wH4vgfG=%pK$~ajuTB%4KfU4E=RA->&Y^Q`M4c__s5EiqD1LI-iw~KFG!%%N*aK zJ@A4Y?|#p_nBCHcxOUXU85CL_R(v}9yHSA$M$wjU$9V?d2s5Wl zi>A_Zw;~eF-yeOL9S>ZJBEUs10Y;TWq9RyJ>4EcEu5=r4um*Spp;?1e7b?=7tbHf`F(oH_GQ%46n?X>|8> zGhI!?^L$Lnkt?)N2)fW3`ubtsyt!O+%{6rOg`DuVlbJDn8p;^7z;)MM&%=+dL{v(8 zsuho~d^`p5&5KVC=F&XwGH02qYIzc*Sso6&R_YLnK~>f|tv>T{g_}Ynv^TsvbvZv? zG9;fVDZK9|3kKpsX8_W+SgSbK~Te+msOI= zy1{sX!Rjz(pPBzW-Tk86hF<1v1 zl_Dj~Osfj{^_oky3P#P^rN3dFnnESZ6YpudaeYv*)^QmMg4bHmNq%dh!YpBVHl332W%TPldVc-t2;F+DT{7! zP)b~CxYnOI4J$s`hLIg*1Gn}=TN8RY)?O+ovc>Z}X3m_&gb5Rp@I*x=Jhd!8z8{QD z)^^sEQpCm)iX-1Kor4d0C9592oB#Rqom~FESF-T9o!M!ZUAg=68@TcMtJ!nm4Ep;6 zmhaz=A6S;`w2b?1e}vDR^$C_QSk9(RoxJzMhqGkSZ0>pRac;Tc2By!QPUrew-nZfu z#J)fn776{BoXS&}G?`s@p3M!n-cKa$yzaogai*U=_S~C}KVHX;f4PB4p^s}X5&1s< zw(kn|J?KC#z4iev{KnT<`{2GjaqmL}qVbeqoWi0gXvI%1zL;xnxQCr~Ury)dXX)(j zWy+*Ew6~7Oscw#W^J_WpqF=Jso5N>LJb_lv&|^aIbGX=GY!V);Q<7t=0-Os`VsWA{ z&S%4#XZXcWF6P!7{sev-`>fc5j)^fl@3fE`@4ubzocl%GnkU%ME4=pb*VEqK!K4{8 z2%lfWPcFKcL-$$AZ8!Xx9Tx3OTSpF_?IMn%?S{BpGH7;rA17FKty?{@?M-5o8E9!~ zp{uKtDN}v=OA&zzaFI#8pS}STV{Hqf0z4IQ$RV#~Kd1T4AFkw&fBYk-{=ff+;4sE; zzySwvz(KEpvNEq!O6-~TV;iU$qGf<*JyScjrl7#^`P_QQt6XI(*V3Sxfg?jYs8fKz zrlg8e@#eaeG|y$sqjIsmq{S6L#bp48deg6wo;s+lQ4x!nGC3(jPKl7^t1M$gBC66S zSAijFC!#*FqPc&ohW)Qrut@bEl_d_9A=fcu3J$x*m0%i`0x3!jVa7|{T+|x70}VSF z1~gMO!*Lm*Q!?B$6cA7i(MhT(N>C2<@ls%7VD*?}oL*%t*>`6tCAVf9OU?f`%KDc& zg3BtAWCgf!wFun+J~pYYcamzAeOEKxZF=ePz)BL_RaB9)V% z0dJxks!x)c{jHpT(T#$S(&{m3UWrxJeNloc=`9i#`*1;lMT>W4(atMa zd;j%(<=fxo+;hK*-yqUGTb;_KyggeUiQ!x&UrfrZ#oIuY5fM5&H*xXBKjx;JZsLbO z{2^1PPEA=-ExsS1b&|*Dd4452!>$8Sy%Ae-~5)94?n@|1`8NLgw-)BieFa@TC8l<0{Jf&W zi^;o~&}-xH!{5lQcRaw7UG`(o1yixEk6DX$;`3kp5?{UKhg@^rZ?JyEo(CL3(M)3A zEB5E?&kNuD@&9neAFd>?d=~BaN+z{voDK+GJIWN9KYJ>ZTP!zSeFd)IvtY?`PI~Ky znKo%6rc`9<(j|DVk3}mEXYus$c#~gJuIURE1 zdq2P<7yf{I|9%hqzG5Nw-}3<1U3&}Oj* zuThd?;ev%+cG;yI@s`)|_%qLP;^b-Ebj`KAdcQqso6*J%SN@6d9h1;O2dzOjPyXX+ z+9ywCm~IR0&TW!DAhmwGN;r-rb0tasQkkIDz1V$rgKW795Skwv7pcf=IX_2b z^yf^h(LcislaxC~Btz~y0*vQh%yjg9pD+yZ{QzrCntEo?fgoZrk;lRvm-G9Jf5nn{ z^SI%bdpY5Z6S?V@o0vai5_9G+;;t($qJ7`pcxwG-ChahrRZskb853skx5po1=Byb_ z5qgS9QjT>?mtfw6DI9s!TlnJFzKZn}v9)LslufEASgBSmaieVRZQ8VnC!TnM^UuG4 z3obaH$&;txQiUOo;|gM!g#O!#;bN`h@yA!P*Is+2@U`7GY~f=qqcx2FqlTrXXhzDV zT*KZC8+1C>Ug9!{!&ysDPfw-PptZIFc8Lh@dCz+|?X=UFG-)yuCr%(pDd&|MJ1_nz ztL7Y9S!^7kB%s&j3A92?Kb~*t5d{$xJdI#5#^Q^j*9ulNzJnmPM1?#q>`hWRv_`B3 z@$usRgt*ts5sRYa_0duid3>6SUoc1ez|B$LMyjP@o4tP7H15Ta0oLjiYnpN-q25<*Zuy7>_^w z1aJS+4>)?4>4e7M`E5vK+3bb1#37z4&`&@JOfuc{h$7NL4#<}bJt}})gmMmLb3mSg zaTwcAxY{@LHyz*)eKKM{1>f#Sq z{En_3IO52onKHGV|NZqZcy{f2c9_4I*B^Qqf+MFamjb5Ie)yvw0`S;lD>?Vv zb9m~hr)Xjo{Cv`a(M!LNW4u2N~G zO!AhLSPG6;4va867H?a)=@DUDF6HP|DtejVu153VpIkkIUO%2q6W%{$yM#=Ieu z2^6N|_Xo`3KMP=#wCricNT}&kxg<)neJrnWhM+X9H0K7SQEWYiwB>hMbEF zxefj@DAC!VByt=@^!E00#u;a@V8H?oJm5fLelz8otN3r9q0ykZKC3il!mI-sGaOCUXz?5+v zIAgHcsQQdET2eBn!w)}vE7D1vLzg2l%K7Bm=rru;=wR8hI+)8UJMv7G87u`0JfJRXGa97!ISDXx@0o9+3L)>t}5U9Hy*^yUMbG3fF zgD{6}K{>SgpNk9;fmRRZi(^1>?}jJ6jPAANWY7a)@W~huD_Glq8fY=nNOq)dO;v;U z50Rrf=y#f2IM)oK?FQ+?)~I>I_fM222m+!gCW@jothw4rtw*2pI9q{SYM9ie=He4< z3IaSSq4CP2ST)rCv-+ zgvFtiMk$X{Ux`-HLuIdZ;7U?W@{ffpe`9#gVtG}c+1 z5;m+`&&{{qOpM|~pZqktEuTlJ*p2fw#W*5Rc?!-VN{F3BT#-O|^gAEzSz;T3PJkI- z3DzW8(VkY6;v$h(NYYJgOrQg-)i|RFC=rWKNwv`Kmxzo(6ll*!TMrW!Y0G=WrHJj` z6(u%xt*1q=ok8iOLWhz9qC8&z#zPS_o&;Fi$B{?82@?q>7|#hOokA2jJTJgRAx9r` zBE=}g&$kkrgnPn9Avo|v@U>4ETTVUoRJ7Jya`7cenC**A?3q1xM-r7FBRRe5&*)6* zis-s$*GEsN+Mf^boO@KFG?c9^d!8|LQa(dcDz+x0AIi@!D+G1rNCvEn*TC~u*pf*6 zhDKyEWsRbjM~Ey8_cfh;ZZ@jKq~35Dq2W5ZBJ9eFTt*gMHU+81b{x(W&#ZC9DlPw` zR{wDoo|bKVnY!lJ*VPb}rZQWOc&Lc(*04h~yOuSfAeoj+K35GST&ja`1OuwbB)hv7 z*x1$@MVVi*qG{KFXepa-#n>1j8tGoXN}5uNU3cA;V~;%+tu;!OQv-wKOm-otR#xn_ zFMIE~f|k}6dZL(v4tOpVb;_&20ez9FKNQX(BK^zrnJghWecE>xF1^&B{*j(l|qt=Guf1~#50u3 zjh=U)np2zPTRN8>z!`aWmA_Yo!bwVn=UkG(>&ld0f@=_dlqERMrcXJ?%YcM70iR=Q zP=0`C5)P&qFjnx<#9@f1bHqj=O7Q(0k&PgZ@wJa}4lPNfl{F6E&n2OdE~XSaoKnOl z!o$*@YaxoFv$0*NXL=hT`_B6&>j0GfSr=`pJ*`=3abwbV=h#|fdY2y;FbZASU45AgKVjsQ;&Voau zL?aGLk;Az-!2ztoiN#j|CXR5<40CR7OSQDDoR;+jT_FL_mb?&^d}d9YlsN#jO1`h0 zMLeG{iV_|-KTjAJQ3_04!t(;6$l`k*))WymaU^KbDDkn*Vr{b4m;U@xthGG$=%e`B z!^SZ(lw~`bz7wJDQWSfQhYZAuw!f1zs46P^`xf&KtaF0wQe#QOa-Z=kSHodnYHR^ z(+0d`(oDJaV06f)#4z3O-8o5^0c+6Fv42Q)ac(uMzsnc&N zqq6Dgkvf?(I={$Z&Mg_`b5k0~uB{TxxcW6~0MfX$g3P~I;I=V_haP%}U;p~o?7jEi zEL^xCxlhw_xPnXXOhw@5h~k9LNO>9?S&AVPO$^8sdND z!W{rX5NrjA0eF7GIblqM^7J-{Zm0qoQ5=!Wt4{+czdUysUH8%hip=HJCs*ntL>juvLNPe9VhJ70Tg z)hrKXHDIdIcq9Svl9X~t0@+0;Z1Gl-s0$JURUA0K!a|61jzE=?^be}QqxRzT8t^K{ zvBOIYXOc(;oFj(bxN00Wk)JXoW(dM4_x95^$pMbaIC!X%Q)de)-Cf zgwo#`(sIyc;7lq2vrY-$911LGt&(}t3P*r*f-epcAFq73sU(s?1J1hCc_G_HTBcG- z^rbZ=d_N!va#$-FgOmz@FX&`VtjXkcCp;Fe3}`E`E=uxloduQ5n<#}b2B!=-hwtT6 zL%WtN@T8MY0tXjgd@)fJVU4ZDonJg8rwSa`>))Bur>d#UYp)sUwKQ!rIt#Pb|_dC^Divs|t2AvQZTbPYwVjF`8jUz6b@Ldh6&Svk` za(=fARJadheJuWk^4 zZyZ__{4PXj}u0WGFA7G&weDgoSVA{K(eMy$t_u*JS_P2-aydWnd&fciV^jC*@a1 zFP)^C-din3p8PGFD{d>kO_x?Ss0k;nQBN9k(N{}OS3wMG%cW#r`j@9<6gsDB%>b9q zk*aFgwG96iFvqJoJ1V~0O6qlz1DnkPlguqz{ku+plLktt$^y`U;ME;bwstH7!gM9$ z@zyo#Zbj7s#Nsl)F9WI$WCOoy!+|9;zG_IXt0)i|uV|(Cpj5!48u{H$u3l>_CN{~e zDUI*@Dd!D!QDSu^HL^;&RHOP_xh$x&l@)dpBc4W?0An1Ur!aAdC|4~Hnnpx@`Rua+ zVeQ(ryyMh&Bvl@^3-3f}V!VS5 zTAI|;M=)o`x(aWY8$u?B^dG-iGPtFCt|kiX|Fiez@sd>K{r~$pr>gtjnPCPPmKkQ) z5o9xpASz%8XrfX1#)x9XFD_9NjSIwGj08=TMExbi#3YJ<`Zc?|-^4`y#wBQs#sx8K z$|}P)FbvFcyQ|K5et(>*>gw+5zSZ~kU1p}w>vdsn-|DJU=RD^*&+|FY^Lcc)P8F%s zk|g2V-~KkY-F6$#de*Zz{j)Yp!)*7NFIw<|815otB%G}cmgcc~GY zIfz_i+|&%CY=}~GgPG4WmJ9f`jGC$v$MN_Lj&l8R0koNI<};y!faYZeO+d4?!;O&x zATo;=HsmF00|284@0PH1;+L7_= z!DVEh)t*nC$Gg@?jK*)qEr~6#yki*Wb$na*FiGkQS6$+Bk=@sR+@<}t8qGP!lb-Y> z&O7hCEOj{pt-LRyyjCwEwC=XG z1kp*$*chp!AdTtdNyc$B_Ms_-1Z0P`*-5$hP~t1AU7tr{NR}kv#p1+-6XCr6ubMs% z5)mPeE$w!~-FI)I*=*vyCrv$jBpc|Y?O7m(25zo)yN!1~gtDd%Yh`S!STOwe_kPHS z|N3JbdBm~2_I1BQbISCW<|7=omy?QmawE&!rjhyBUUd)XbhKOQd|yVFvCPyGn(Shc-SY4#L^s;5YJTC3 zg(z;2B#u?FaOK~BhA;i|zwmMlwqjk4)$PvY1B(4frIG=S4Wv-|ah z$qgj8nun8L?oXAX8pMxP^*5c5%$Rzq3d1V4%-0H>OH_pnTFE2GjEn#<%{C`>4FjFy znyCA?F>OXcANWFFbQH89jvHfrUNDAy2*QXAX-PNXQ(~(`BBV*1#O#F?#gKw`U@cCv zoPVDo-cb}zfy?O(su`KFq0wk?)KN#VYOmGH+c224NgMM%+n#B&eaCjL{qeO}Yv~XN z&ewQTyi>mX<*)O5zjra$eCrxM^{Kz*g)e*|B2$^25z04<2%r7zXHZpcz4f+I>g{59 zG$jLizI!Lb>sHMf6p@+_9l;@;EjqpLRDBU0RIRzMNuBZ1dHR}_nFjUjioz7F5)5DZ z+*H_qy6jZNAg=iSbceCN!~7A%a^=;rM`FxlO%5Et`HsuD#=mm?4XVCUW8ep--pFoi zb45|i4L4lRXFvN{9{bqGa@e7VVys0GT8XC-Mci=h5Bd4MTX^3GKEPl6!CQ!rIg`_l z-k*kX0VbG(&~{N*Wa#3&IWZcIP&!Aw5?PB+(}h<&A_YNb$ocjI(V~xk{NsG*JO7D@ zaO$b2E=Y*@$O=P8R1Cf0mvsxE47paM22qZV;eBTVBI-V0=dmH^9Oh_TV^DRkLA)U2 z4z4g2YAu2!{a~|f+=MM5yg4Kwnw((MVX#j zNfa53SX|P=Co>3b9&yv`vsVLgJ9u!*X0*8qXs}nKLDPg<-GSgzD%Z9#h8;V01Wo|w zFk+Sk@#3US+Jbu}rF9^)RH_)o=#{K$OPWG6-it#I+eqSnfh#|Hbs%I-EygOU35r9o z{Q7gBi#3Kv9(e?prXwi&F*->)|2x%E?epe;n#aCd^HpU&oLpnXmu3SZWVSI`j3`&; zU^_~Mqqq#gi$POS#V0;+A;{d8<4|B|&eOFBQMV(`;n>ZMaYw9|v-#E6w{f*5sIpu0 zW++D;brk2Gdv4}zJDhq_2hHXb&b8Qd$E_TF?1>zB;5vT!%qQ{vTkqz~V-G}~>n_DT zB8Xu!Qti9~-@MaF!i^}vgyp2GM1)to;^n;f#TO7o5m#S*HA#{#QD^4kB#*Ed47Jfx zsnfstyxT%5q2jr{m0-qkzXeu6gR1T79KJuwb)zV0naA8MH;&y=fQHf$QxwHxmQ@ex z`O3e2jq7jx2~&GD*#E%8c-F5zlSTwqVs5Gm!Y~J|VSI|Vc_^of|8P9tW ztJdt#DJLAyR5PX#N2J+}z4u+5?H~Hkhj`6vUPGhNpw$Xh8YTe7r0o$cKatN)Yityi z%6!jr{bu(y5-P`}&Jo83BLsp^+W=|tr7wMn8*aEB^@7bvQt!Cq4x%Uu#GVUjB^4)t zvTMxMq>biqEr)3vsjTWUHW=pfNHdQ)sp8k1Y|mqul)3Aj+MJG3D*3!8Z?mfW;0Hh8 z{`>C_pn^|nG@88T)vu%7YT=AyYHEr`)S%UF1^IS?b^^m<22X$bGcY2k3w`+LN3Sf|APfECyMRZu2>QJC>GpExahmAv1@e{W37*uI8yRBv zjq`~p|6zVn^ynK}75 zzVW>u^SE=KM&gv%2nG*xKnrs4q70(Ojrm4mSPNO*bf1NIESF|XApJ;^B!p9=i^qvk zmxmxfq0P&(RWaI{0gGH59R~710$9|?rXRe>e=kP^ip=7)jx#c$t^;0YqZp-H)@XI) z#%t8bh}X@^nISc=o5Qo~7=U#^kv`z8Coxust%d&k7pdK|Px74QG>;@Jmnh8%`NpOa zR7rgTYS?S9eXwx~-m_!pHX0Fl6@K|wp2oMo`;WMrAxRw5Hsb8F&*oE~`ZV`$euPWk z{uZKU1m4q}TERsZUX&Hp>`cbaojY(Y@Ol(H9AYLL^rY501K9LFR#Iv3+?Jaaa3MIf2` z`_Jc$ZqdeLpgc_4D9zf;$MznVl}-=aWFqx8xc-gFm6WO- za7{0pOb?o+ray18OhQxo*mp6?(oc$2M6Ha7m<2H zj~bVZkHp5L{fNz=@h+ELo@bwB5JIGMk5571rA29_4#`*UJm+gJ8UV%(_?>RxP3Z^X z#&KQh+A&{8BciZiA2`0t(EZp_x&+wD5;u9UtDm(1(~xXrSzCdo3uTkm2`=< z78}{(0AqGdYXF1A8I-N9UZ!8*JNGGt4vNXwMc{rYXE(oN^# zqz$}IhC6nGmJIM&hNXhXeKhi{;p5n4Th_eo$bO!Tu*Z+kf9Vmn{9W>;r6U|x&U!9V z6-6-@(piRxpLVZD?SfD1KB~f|F1rf7Chi>2g(@(uJodfvRK8Z`n=w=-jvYmQst!8` z$fD=~oiqW0D}jXn`Je0gOwXCj20WSwkw&tcrZ>=%mcS z*pYhLX|9TKD2vtt%F28=C#t?zWem=jV`Jcbq>t?1CkZBQgemq8G13tqi89$9U>MZw z&(KYHL?&p}&RJ`3bq|x(wjL{pA&N(7POawDC!WMtzWUGXv+r6CIrtEEY}?64{?8|P z)o;HNBSJe_#Y(#q<7aqq%Qo)(`JI?J;^ymq!nx0S7O7KGml8)Yg3HQHNvZ#K1WRqw zCT;fvG4d#k5dC2A>S}zkqd)}^4IqXJsOITe2Cx|M*=dq1bVkr6oybT;dBF>RgX!rN zeE7p34poC9TNr1Hvo>X@5w1O+N%s~~9XXfZ%B$~yq%w~ka~2R_P{A}AW&bEup;GA{ z`1u4eb`6gXFdeerO~^?aPVBD3-c9;^);f--$}FZ>sSji2G1t<$+!zScyN<1N0A?p6 z)FD!xeV(=EXCuDI;7Ya9Crk4xa#58=J{NL!sZQxFpGj6%=ok|aFc180ARnt#+*TRv zx5$9eQ^S8gYscmlui8^1xCrU+m&eoR$}5Ih!>0g6Uq#HToKvI7BwsG`P!hG_guS(J1asft{v8XKSe zXy)kU*Vvix2Z@0QWIM_Vz?QhyL)cBBu?=8mKpXT$PMDgTNLA{B&Wkd6uqK&{lRiF_ zX_h{l`tP;ih*(|D4;GHSxlfb#Sga0AqX7vJd6WdDfmnk>@tR;IE}fLQ?5lQsWTrYl z?5U#^r7*xkX~yvyb=EYw;3Y5NKmYT;@D6_Qj59%mlOA^x#u}`JM?Lx&R;*r&SC5Tj zwmh_zBuRMdo8HKgM;s2Sq-jEI0;6_bsVU_CPY{bqn=~;YJSHkN0@SD)-~7-Z-`Au0q>~t9GNx|E2)T`SSN!BB zKgspiUr!W89CFAZH4U0E?ZzV2rwm&9v6&Yr+2m>r>2&8-HUV4~LYD+XNiBE*nKs5dz0iK3V%KJkfwr`2k+cI|$wTYm^?3e(fmoO$M% z82}r;Z;a4rG)P?=Yb_$ejvYI=_uhLm{WghxoU}c-`QTuC^|g6zq&QI!98*NCq20P}n zpASokT_&Yi7vizws2V&G1_LAkcvMT0VG1EiDh#&v z*wA?ziTA-ypBJ&GkVj#J)uyT$Ne9bjMZMq!F9;_x*7C8BeXKv!dZ84&R4kJ6=aMNY zPpFwI4x}AE&}M0I#z2!UJ^{(N2E554Fxudola;lnwz`Uc6a-rb7{) zLrTEYnwH>T>lASUX-4;A1ZbLqI-_=}wneQqFQH~QjG5RoL!_-CNm7gfYb+uLRTlsg z5mv3*i&iV4)oLLU5k(d;xzQ2;3hzChKs=hJDIx}Iqb_37Nt?7iwdFbS`M$3;6`o`% zAWm1Jw15;fO6(n;m=3dnP_rN(m;C-Ec&|M8-~+t$r7vBiLzKBY9m+Bxl3XaqdI6je zneUf%0sFXwPM0o4ih0HP;qdEaiNsawMU3ofMMrcIx{3ew zzy22|o%Fb@Ol>C-F$4sLeKqpXvS2lwdgh!{*2Q=`gYiwgHgGn@XoShqH&uq|l^|46 zG>=FOCFG5(`l1Bca$0fWG&Q2#ZWqebxpeU?gFz&WSp=t7a0!?uC=Ey({N2afs0#vM zBKVbO!QKZ)#$1aVVX4{vzYSBa;#a`xFz=imPI-+9@qp2B_ge!5ELKL)GFv z*x2AT#dt^Cc_hj*@@_5P@*XJ1-$_#9Vphl?5lpk}S^=_HiTiVrsRd z*XEqXHm<61%VM3Ch%{v`>%9)4Zk_we2zHpM`J5_7+d+2eLZna6l29Nr zafKip@ST48={*1W&nJqatY}6!trTm(E3q-`^lf4cHU~5uXqJX(tb{}ZWAG^;29Lo? zg!-^IyAlURnRhB2*abR^Mx!wg5W{<>rl#;dtgbxNV>uboc{LL6eds5PRN}06(U<=3 z+)f0hQ^TQkhIQ z1rQ^*mh##`-uvQ|ID<7ZM)2^vF(JBR%oSlLZPIp49he*WCY;iQUwZ8V#affuETJs% zUw-I={P5cU=9RzwE>3^)NqFN>r)=A@g*W~F@6m2Q%$qO1m;*LGihugMzu|MA`4sI| zi{Jg-H*nTjzXaq&;(2v3Puz%$iWE^41&~pfXk4nypNh}w`rPG`p$o~au+aDM3lVuz zcM!GaJdN>v2WDbQwHZ)(@LkK9FIAQsRN7{oKvoqHH0p58mYzCK?yb%}N3hi^=0|$Y zNu`R^eNZ3g!&A{KAcomfnnrn+NIiRYp>(J&@cnm_wqwUm09LJ9l@-lskR%CevIDjx zK@{wCjHI61fBG}t`2Kfu_&)1+%o2bf zYo}2MPkzGn+7JjlQO z+n34H`X+7C_T-j>e8$*PgjH!3X8bZakodK)eT@wpHt@pp&*MXX`HwvMxFgtmzm-@I zfA@(`@PhMS#QuB3-~P=fdDWZVgKN+5XO~~jx^?S`B3qE_#&Lvm9#xMqhA)2c|6)YA z@_tF?UWhsNOsQfXCL_gc-!0GM*FS@`ImqFD$hRmJob*2WZUgrdg&Eh@~(Gs!wo;? z-@f!s&ij?)NRxz5{>|SJPc@mEYGe_{y#)Ljv1b%vcGrP6#%2gnn*DATM_Q{&oC2^D zF`=T!fvaD~Pky)_hsTR0aVrTFo^6uwDULRdF$;e$001BWNklepYYT5y?kChfT^GnZw6>-2NjE$&sBC*@-F9lfBv;M?)zhzg|gK7XHzm2K` zJO>`Qj`Pnyp9?OyV8V+rX?wWh>y%Bl)>LkFssZe~@4oxE|Ni^A_4eC1;}@U8VTT>X zVH?+R|AP zr9B*jT6g^G_IqAsPb*$b#vn)IEJUC+FV)>huPj1qw-b&({&=4L^ry37!v-3SCW^iK`Z{X zec*`9c@aXHN;BF5o2@YVFkXd8+eL(bq3$bLgs2iD{Qf0}^QN~P&NF}cS{mk95+7j% zyeddifNMSx;^+WA^hXcz+~@Ai$_ScO&URHTav30>eeWx~*2G92k*TGc*nSAGqx~Cn zrp%oSj#Qea6WL#A19>R7jF#rGiru*+%vE{GgHD;cLb7|fkCtJ1^QB&5fiO4Z(FPjM z?g6sak|YV={qA>JyLK(s7#eXCmpVjKY$GO7h>XSHNkvH9P6UTnc;0!>=aGjW;Zy(g zPi(q-6VH3z^Ki~#jNx;i`yAI@cOAwAo*Qc|cinXtFL~)pG8Xp5kktWE33DhSC&g*- zFx|8Z01gYCO)Pbur`^iG_r$!IpQ_{f904#(YnRojf-)#wM8@>|VVn+HN@JPJyHu@@ z`RLeTayLxhm%b={_vsk`fuVeOgCb+*yLSxAL8>!6I&S=}zvx`)=;oavv+aMN>y)4aOQ~T0W$Dnv@lh zVdu`2xVbW9O!yXityshR{^)(&xp@m8|M-D8HONC%)o&Am353$CD ziI&DV=oycQ?LB)M+SNUwH5v^b|M*K8>*4Ku z;qO1qJ$Kzh`oO~+dH6xJw?E9!Hr>wpeGg~bwy&}Io}Y8ePk+Xv9(6F=+76fCigs za5oLPhd6qG6^+$|B!~*}s@y0`TjMS)lah5_O?dmfcD80^8#OCiTLhIzmBm;3$;wrU zSfa>gB_+*7)E5+ps!PS*Bd;1970Fyc>Gf+ySb|+1hs@D}+Kr7aK{9spykeSGI&nGZ zpsGCcna|{hKl~9FUU(sAob}7>+jRWX$3Dt~TXyoS^UmY1KJ@4O;d}pzbDsS?R>eDc z?|U!f<-hrIZoKhE&N=6tf;m0s;K=zlthM1;MliL@y^cdxO}~ufUPZ~VFjl(6D+evQ z#L{$RS_&=J?>CK#p=zC<5^yf1|5x)pmHH;7dz94WC_nH)9sUzzTD zpq!OVSt(UW5$T(bS;kJf``Xj@s&}r5WmltQQVlM2y>r)hP0yaxk&R;BbE*oH{XQRJ z%vmbib&X5U81*fvTG41ScrlK$f9VK6(k-iD@J;t;y=K7f4qjANXbJmUzUO(b*vOKm z4x<8T7)-Gdk!{diwHGgb{cCvtd*9EMpZEm7a@MIly!kdhaQR=7tT~ETzv_8hdhtbk zXYvUG=Br{xm@j5>BkN0u=&mQ3=zjY*k@Mmz@pPtMrW5I*h z9l$iuCZv36Si8eQ9AMlR)L!5o3nF)+d|>r|MLt! z{rQLavp@PVkvj>Cu#z?}f9?Lnt09#gcr(SDE?tX4eZ?a(I6S&<-_vDG>=}r7!*pm- zV%Ccw8bBQ{g4Nk7uyqWxPzgcVwCQfHx#m9#r)<(jaOCG&#b?-Bnag);UnBDt!$}j; zG$o2c1XHuw7jCT_$eiJ{qWy!CBwBXtqc+GF|fpZ_W9d??#)gm=ID-FTM-_n~A| z^&YJL!$1B5B8H#+>=sxoV$TuMMXD|vOmrzVuN$F{^OaW}4PP8P6-3n;fS-F`OyvQ! zOqOt^-O3nJgk+)BOuhFRb#Kt_{a_A!wu?7yG6F~2Ql$mg)OjOaBD%~;=I1e7BHVP- z&HTeZ`~xSSd@=_gd@zyej#%-4c%|8BaN>z4@>hTU7l<)<4N(3E{^WA3wVd|kC-bBy zJ~8M&W7vDmFS+NjgHXdrzH~4T?tHbt9PRfU&e326}NPR zBC4g_e}TuI3dfzgny9fqpZv%_@i(71ms3uKM8Qsh*cHsU6*MU5Xsb)7Y6URCfKb|9 zN?t?cgk}Jf!DCc$Vrh7XBBUn5W>FeiBR)F^EY__%kYD??=U|NC^Pm61ME>`5uw^mt zw+psBYAH?APq~ms95xUt^bT&N+@g`e@EQ_gq8_#t1G+`v3;-vvfI=aa3iWhrKsc4~?RTG)=Q> zr6^IJrYTcXOt6AZD_1ufwy0BFmJ#3 zV46`tD;l-~Ll~=gBmfy+5i?6c_af|4b`x3cWpeYVL_SQ}1!M52y^ljp@{CoiJi^a0D#ikP%r-x<O6nXovLOpaAsQtfxsH)IQf>N9Sa&7&?ZUZFfM(e~J}~~P zqu}o$a3?tLu(82qA?&%H%LxSYeEu?-rz(?FlKHNW*$8m+zZ+1eh#f$vT>jm3%W$!8 zNr7W^P87?}FMm$O-{`rwR@`R-U<_~|!$5iOix5@s9I76*O1qs9HDc7pL=Ltmjuq1} zqDta&=TnP{Kvi~#2wZsKg#cW6JdF@V1D${H%RMDTY%R*Th`2Ia~DS)7^%WI|V?Y;<)h;ZVG zCvyJz=L4bqX__Q`N9aCNR!O@}6h#;j(lk#IF*ujfZnv45n#ycfDfZ(iig7LlB#>k? z*oTjnR3du58;tfq9FinS@pA2Un`X0_)nT{U=}AnhHA5W71zTxvTeD_OVK{;=juA18 zUAIt7V7 zG_i3^lC)?x8zf0mI_WiCea}PjZoTzZuDId~k|Ze@?Hw!{0Sx0kLqU=Ohn=cN0UQh|p3XTi&`wes%_iPCocCB0Djzyv zs~M%Lz@W*Lg(|gY~*L;f~{pd$L|M}17q>~;`Ba3S6GKY-88WVyO(lqFfUOxmE+ zM^$C@>eZ}Vxspbs$=ARBjm2?R>X5r>mOFxiI1~#SbrwhwaiIkA&eCLRR9Z>BG}$SL z9Zu|7Tf=L=g)nKmkb^ehbDiV;s+gg>s(a;iHiAkq1;}AQ8y&{*J(ifzP%2J`iov5a z(w%I2AmzK?{T>JGyBELkxL+hTU?W@T!|#9p2drPej&4G^AZjiG}s<9 zGdqhzm3{|Fc>nv~55UzQyBcGpIGI{-Gh1izQYUl2+luinIn7$*?~}8+%5f3)fOE@* z6zsXk*5tr|GtM}j^UpuOh%Bx0!_|9QNfP*@jKO&y4g&hhrBaa6=cyy@cDwexESs8q zX&ORDj4?DCG2Z(Ol?h$U2|v90B8(G4)JCJxsFpP`rJAyyCDVih1>O@FFv5gMc6uG0 zK@hb?uti^;=e|m*QC6vBOjD}DWaroefQ|Xwqg?eZ(}kb{Lvr%F_&EOPBw4e zT<9^8fNmROFvbRACFd3~EoL_{jJj(5&7!_*$d@jJ9FEmslkR$Plh)aXHAFkjv@J$b z?)>>3yz;kS$3?IEE$+DMF08Q_QBs%S5x(-(ukg9geQscis%l7ADIbR$Ba4u`CS$#9 z(sqsE>P?-QO!1^m+N3Q+%Oil3B*8gHQbFpO`}B=QgIjL7g_pkcrCfB;MQq)=HLI)_ z6_FR8{`9B#(1$)$`0CrYZ^zpD@fCB9#294%4XRHac(a11t{MDgRPf>v$U-x#R!EYaTzTckY0b1aXv0CAd+xa?im1&_n?f#m z>e>i~Bxz^i*voC_GyRtpD4*%BJKpuTVx+V9u?2duiLtwTIc-uyR;$7~Yldq|SG0z9 z%ko^jJE`cu&gUhU+6v{5NXK?Z%`||OB{U5w@2rxapf^*M( zE_?62rg$%7LM~y*6ND%-ML1s7iSQodVbrN1KS~J$8S(o?UcPqchR!v)S_ZtE4pPiud2mDx3u3F(FA4i1s3qhxp?E{UX2n=1bs_Tlk9)U%?-~^Y@6tg?#0! zU*YB(uIIpY>qycxR5u5_bhwWZ;orah?^t8lv}sc*bwwBFg(#<*R?nX<)|!NoPA~f| z9rWErJQ3BozV{+-OR^CB*NeHn(TikN@~7e*1O5#Xnr}VGcj$MAq)R21}c*k8EYz)<^iA z*S>}*ZsJu?QLIF$wlO9!^~X_!bAg3C4_8@MUZrYC|0pl4ocVLm0_<8Gr81XwoldE# ztR?`)9tLuh*wMO_eI{e0GOb}sQ9%MuxlW_W z@&JeKld^T|Rt|gAqgefj@ZiH=;Nh)X*>CUFIAb~MsZZsIjR$k>^*;&Z#%&j(p#+Qu z5ldD1#WT+&wwBv&y)Ce`o5hNP(Jo{yL~rGjEki`gXCh+;&h@NB$YsLhlB^B;-azid z@^`8nsgej5Vw^p=3?$~fN5e=lhsm>MFO(bAl~>9vPE%i%@SMo5Lz+5xTnFeV?WsB; z3#@ax#(LNTwbVZuO>V2|<;sBJr~(Q~XM6N9@t8RykiO?R!$1siDsw1dLaJpQ;|o?7>-ef%e#P<@eH3E=+k}ERpQN!c99fOXY*TE6brTft9S0x0k+aV}3sp#xggA-{@Ls)V)7^Ko zbH@%I*|v=+ia6wuLpX5VI==e<{vB&0RKa_A<};to{`(!k_rCYtz(-{*?RGoMuBbPl z(Y!QV20oZ9^*~GZQt_j~e{n{Mo#3Tsjfh880xQ1@PQCXjKl$->JnrOE*m%_8oP6}r zeE+(eIP)n_WD2FV?Gf(&`6j;iz3=k)Q=ZIJG=&&4f;FrKCls>0QWx!G^-own^|6Wjixv#%EOH_mhA9#Sf?tO&)qWwq`hu0RyM2H0P zF=GuXq1tLxYO;TPL43Ef>>MkI&$?6~e&&bi#~=Yt$-Kw^#Q`HWxB@eyggFQcMa%3pdM+(Gcp=-85Ut=s-TNhR*n{|bFCG8{3ifhsDwe$dXp z>qeYNFH?2TmrCMQ)*)K)`ckn(y7o2GujVSdWJ+40N-tGyl(X??)$#pynN6|!V`B{0 zTyqUS{pnBf-s7C35yf2c&Uezvif}yi&_g`1`2nmo*o^d3Js62d6VKGtbTRfK7Lh31 zYs*zi>UDF@Ach|@pPEgkDeajG7*$+xGL~N)40ErZM${lhkVeebhaRG_*BX*`fzEA$q)JO?_bBOUi}85MvRc+{0!gt<~69Z1ri)ZQNeh+m`3!_ zO+uyLpsc)~=`iB=4YE{k=6yma_0YmeWs}{%$%uc*!=|e6h1uk22*K#kl6*Sg_)Ukb zr(MFC%1C@g0Eh9g@7^2PckLQJ_VKH^?T#OC{tI5m%{Sh}kFNazFL=RuMf61+M;WGL zP<36-SE%e1PJ<_{$H1HQ8kzmOQtw$K=}@Wv4s(9j3b12V!=U!_WEWU(^##SN=g1=; z#i2*7VNLuXYxh~pZ9m<_rtLpq)zm)N$a4LSKjDZ+pM(*ED3~ZhB8zXipwq13x#ynC zdCz$+No;Tk;+2nG{js8g$>IfKbnFA^i}oC1YgF1UQeRedfM28|%4+5N%qHKxGAYA> zg<*Q2&mpM-PNcsT!==9b z43yI%u)I`qY5?bm42*6X{66*e5UDq>6>F{!D62aUL*}RNo>F)14w7x@v)yDC-$`oV zE;49w=`8a<$I%^iL98mR>e zF4+)f3R!acg}=VJRhPk zawca8%+8HQyhtUamw_niJOpkodHM?xs$J)v;|7%%B-}h(jJq@dE*KMoI*jZMW~Eyv?eK8b6uy_UcD z;9sy(BcA!_Be?pbAK}qQ9z)vR$&au7A9l1`9CGNPM6tuGgVYkoO^mczxiYU1U zX3osa?1}#3q)pn=8NtKuCpFeW)w9hmyx|puKptxZbc!fyBN8z+wVpJc0fO>Z)vy^j z$LiI46(B`GzeDvVYamB@RToh% zC#F-a=A7e}TW;Z>|M_2d>|=j{jT;X}GP_euAex_PwGg1uXmIq=M|0&>S0Q3(Cyv7p zKb*@hy9{d$t=0^`^h;;*;EdDHM#YelVxpAPImE=cGz*^; zk9WQxbpnW^C5ptT${QPnaC1^8d(xJ>aobC@HIR&UM1!U-Q~9XtBa37ZS>*L4zDN=C z+D2WBScesZFL-=%paNR0Ru)m_y8sO6R4Unj|NXh(e_ue7Cj864d};RwB7|=8B}y`9 zhR8^j9GMEE$;_g+mD%|!0SCr{__osq|N01@PT=Aw50Mtt^sox(w~ zG2i)D##_d?4&WtokoBds?Pp6r*CG4kV)uJok(aZ{*#y9--Msf4bkISZa>~iW3 zj+L~%9kr|Rsi)b95fV@nm`$f66o=q5@ByOCK(`X%tUh$h0z;|Zj8-x2bI*h-Ct1%{mkjZoT#}Nsk0GtYE6)2 zSV;P8Wl@K{i_l%=LI>Qxa{Ly+^4u*>DZ`){V^AkVaj+NE2^u9>H;oftFw|@2C>c}K zn8^Mg{^n6c44XG^#)$BQC!9jF*$7va$ndm^Y;rjc&A9I~ip=ODzenA^&BjVPpN@(w zUfIdw-?BJkJs+pEuAbL1j!~WWnd`HcV(E{vG%_KuE92J+XZ7z^JtQHBwxVA2?~7wI^+zZf8)NbQOl zdSQeH7j=aOumqC?W1Iynw1o1(Z5U-!AWjw`Mg6QaF6P<48ahAd0rd%9@+>;lnV2)L z2trHBnu6ek>Z3lMT~^+`j6cn`9xKQu?p47!CxZq}YK+gRT^f}A@)GP$oYNvy9-VRp z96SwS1 zU9o?YHu@S^pEm1!pF|1{sKbbz=6?HR=0=IWiiMU(g&EG+`TtK_ zkRwq@5%J=nD)QJB02T;tX<+jm;cC4EUH{xmD}Y?)_SX@j#$TV3d5#bD7gk#lQ4Hg; zfFy#ARfi+=bQoVehlU$@jxq>i(T4fZWN?gq6@+L|eVDmrm^vMDNf?>e+H1ex8xFk$ zKpUukymk3xX&+hkc_X@l4kV(OK-QonJA&)$vlP_ld^2TpfP!_V)!%j02{rrXCd!z5 ztu)jZ;#}3Rd2nUwLlo@Y#uNhvn)^3f{j{p8iant84Pa~Zu1mewR*C8(dpeA*%&KHl zF}OC*JAtHp|kW9rAG{f9_#39<$Zi-aCxussXAHwbWzs9 zzEU^LjN8X4f3erY;@Bmz1sw#DC+1oAj4nI z{O5#pqFFbR(!Gvr%mb}aW`uKg3~OGKj9$Fru?coGxPyS)MChzQR09eHhf$5st&BVA za`jr%YF8M4GZSCtBN7=gYGGEIMv+3lu+AaDMip;VVSbo%lRA*Yz%#nuZ0ogspQn^n z@*l@*Gb~J=FVC2uU?OOtJk9*}_?Pqa)AUuZhpG=NPilpN4sBnv|4qsL1Y=4uwg&V3 zq?vBZzTDqjT$`Y@uJbFW=}$MKGutm6L{>wF?&Eo0#e)XZBEX7b)XNQSh(Pfjo%eO4 zvr}~RzW&7AD82%^RFkEyxKnHre=hlgj++lnaZXyrwG2}`Z=Re z@%hO%>z{P+76Pw}iIkf9zv$OSgf2^OyoM876FpoI+ziVN$6D{dZYqDsPpQ6puVgJF zw5QE{6`sjG(~+5HNicdm{V@@%s5F%`PZDD{Qa~q8_((yRQbS~2DAsD?4=j`Y=5{lX z0}u-Mvx9hHA|6hNQl+86MpRUAjBKYK zbH}`?=EwbfRSx;`qwl1o|L(ujD69SG9y2Q`iKR9kO7#r%cE*hX&&s=7FK$l04%*te zUb}2)$jRlYVe>c7oL+8to5`!RcwsbJ=3+JWdpd-2XRNG*r@KhV*=#@d=u{1Tf1?s; zA#OaGvsq&@GjzsVb`>ClpmfLI+l+`Wpe9TAAlVR^iuAdB$%$_d!QN*xEl*jW8nHW%wdgQ)ShMSQ_+`% zsZJ*d;Gj^_N~E8tU^SNQXtk!{^TEj|+WuKpXFAF$4kqSPDWE2<%EQH%q{FC28r+Ox zoweI-Qhmys18dfY!p2rDOj*&sm&q5@C_vWL%0lAZJZA-jx+pCN->rrYmVgC%aAvN| zh#=8kOuGj$A=zG?j7n8~(*{FTob0>Y8t1)$AA1>8;Fy;b`Y`wJWoO3Ns2ikM4sF}j zb{9T1HMO2}F0h9TArb8Dh_p zCZW*5c4noLBGaNoic8)1?l+QH%PiiK(o*SU^tvIZjF7cYI+A#?;voHam$d~#fY5J1 z;u-c8k%Y=Hzcuvk@7+LMw1X~iFq+;Zm}>&VEf7=Ee25qhC>GhurT_dJa+pBA{k@pO za`bHFY|a0HN*()|U8k4CbI+(PGvv|`s)x&>s1#6}ytfd@E7gj#F)J)Ru2O)5VAZbi z$*PpgH@|=0waX+cf0CeUManE>wFls6FIsV0x2{J@_cN>_u4oo+Zu{WIaW~5OYDqMI z@Y*W$t6{rlQ7HJeAax#zb@S@cjs;b#wx4=SJq4l#AM*%DhKXv8M(ZH%B z5cQg`C`;pg=8qSntKns!C;`H*`|H7E*%0knWbC|qJkanJ&hVi@>wYXfLOWnrgt_~N z{BdU9R1-K=d`U0F&rW{XimLIdu>n0<+ z(P>w{`jkILehk#S7RB^W&p$dHvhR5WQv!JYp;OlMM{5ZJOZCTYonS@^E9!EfFy$GF zVB`8mBD|$F6|z}SeYXPFUP@u$+^cQ&tsC3`E6(g8C;W+JoelG*ir{C)NiEGTE#SyM zCIpGM%-@3z+-ovDf_pdclMNV5qs3Qf1a z?Q>;*>HofjTIWmD@wmLfeK$rXDO=cil&;79aWja*B9Y~Ub&|0qq<{w8w?M3>PG#sn z7l=72A_3Gs-|Oz@I$$ITR{XE-YusNwuNH)#C#F`d8#-m~Ue`9S2~k&;ykXoOgg0Eh%D1JP_Au<>i!YY-|!n z6Q6Y8SzMcr;8H|>@(j@YAjIiFZ0Pq=r52h5C-=Ug?P>3SUm8T>i)B}cIsi^qGUZ#m zNgukIK!O?cTIwvOvj#Hr44@&HFM$RyW}k15;I3AWP`yAgL)J9qa+aWRlSlkLM@hw| z$Bti>L1zo+wKlqh-9`)Iby^yoR&46A&#b_6ga4;@mj`>c8crXzpRfjW6AZN751|j{ zO0C+O_I&Ivk4?7~N~G;Jmc`MsNOZ43yzQ}QjD<8y&JdCK*QU82`((SYuT5;?-u^I{Ts)U~H)sDT7{ZG^UOEEn)ZzQ- z+H?DUrkWvwbf82bd$q%j?!TRM2}w)pd!M~?7^UQ^dW@i}Tb((^$F_D~+GkdI=E&LdgkJkUcY2NTZL1_V zgY-SbDj6u(~5NGjV`t8@Qv(AGd(W3+wjRQ>&%F7P#ra zhn4^zK69z>=ef~A(}E)1VfPh&z(XO?QCh99d42qO*A7q@I-A8|yV~Yt`qXvfl||`C zEJ=7|Tz#~V|HTfZW-DGUa+rn!vEAR$gsW4xqeS^Z-ZjhEUq?n?7`1+vwvIEv^L1{t z4y`Vs65Ct?()j&o^c@F;jckj$PZ}n-@t#ObX<>OiARAsFAzAKn=phJwt>Ez^AvC26IF%XDpzR(= zKnoc^@D*lOEqGkcA~S!Ss;0=e?dRCx`28HS7?WH$AD}>mC;~CVdtRmcIZ5_YeNQF9 zTYZmsrIHW>=3Y_g(^Wmq{XZg*xXNbpKi)n`DuaVDvV<~$QJG}aQ5CBE?vw200hAQF#1B0c`q0>p^_x)a^QgaG{$vn2~v^q>acDe#?!P|CualJS+xaU-0g zW1ed)zJb#HMh+ODn>~HS;@dBzGycbNrOD$q4W7}E^>M0veGs-~-DL9NjIa9foh{u- zp!31<5%P)-nHQWoNV#rvWxl}IEtOlf3xM~2hijD2l6e{U%6prNk1A&ye)T1Ak&o1z z_)&fy`wi@NtU0s8YUoNG5E^R1c4h1nDS9nxyEW%*rd>~$crQcI;+eReoC((GzCrsG zWl$L&9{Zsk@Dm$=ek@=xCSFsfgld688ZKRQ$iqx_Zm9C%!p+*fS0J1__lVoC@vU>{ zVyp|IrO(F|bl+Lc@71u7nxovt+jK-(6bB> zY~U1r5~(Q>lv^aTveq`vL-DGjJ@M`YWZ~NyswJ8hD#T+z0;?xT;dy&d&ghMG-F`H{ z4*;$2|9XU77&)$}vxE-JwzKpgPD*s3#SuybiW*6^xVUJ&lU|U$*H{wGxwMpGP+_%( z({#qGRl#u1)B$7Hl^DXK#Q&=4E>}aQ0-OJ^oQWAWc_Z6)^pgEG7PSaB=W5|Gau-0I z`FWsXsp$&ls?NjwdTNC~Dt7-^i68Tw-+hmiQdM>L4d}Y=ZmsCB^aex2!8RTfVU~OcJ=i5OLN_ENmWDb6Q1Hj{(g^OKkWp3r*E?y_Ca^Q%>V9T-`ug4 zQyHt^5as2Ib#b{@yI6m0R(@IOI_6-w`-R>0!=CSH^&mLq(04LgZpiUC)3TrfNFK{! z?xZ}$6?;EO6hpFpMdvHBuzAzfhW`|q!$xYQTjqxdfLUU_r8fWjLf zbHZeb<=1ix(BiHciIKhJ9O1e*t7iN@r*YOF$2zNJ^O;0;0_Y`O268;|^(8}IZHg{cWId7RbER=J-{cOX2JV=^e^=(suBzY=M-hX?gp(XS_Y%63!MB4jj{EpJjo` z@?m_PDt2|B`S~nh@sn&;ank)zO3PqW*hsN`0Ywj}SkFZ?$A$k_)G{7%;~ah{$d!k> z%vrxaUd__tKTr%gm)VSY{qaElgfG<;%TK_&$`0BmS={PSPk=@vMhmz6!`bEqE`wiv zsxEZkKog3NpIsgER#D}06t_?O(S}cbvM99(z1GZ=)e^1AZCI-q(-w0-=xifL_X&%WAkU6_?eI&VET zp$KKnDs7=85c6Ck9BQ;0Q)3B^X~sBJ`@o0YM>S5(Oc|?X{W~Okb(jC);2)@IZKm#! zJXz!hT;LoxVYBC<&?7 z+rMG+;9ci~YPoBoIZgiCVrgDFhrp^J&PoUVY+C|)`$9C2S+v!kpdf!Bdy|yF zZt_S+&sxcpDApqudL{JN+#9Z3gpTd`P;+UXw~YX)yUD!>m0xUjTrRUAfzYSYaVuja z3Ndx8r~m!N&TVdGKzIf#vVji8E%nnlbGqoe0u4|usxxFFXSdx z^@|!WzOqIgWtWs48M}uzOODeUj{bwgceo7y^QI5e79ORP_a^)l!j`cE*Nrw0*2>v+(qyn~M{#-t>qaG4bDlj-}R!~eGV@)_fF zpry%Vi&05QB6ij-`J&M$$d?t>L)1uTI|LmS`@Q{Jo%@_gWhBZRkgDTN1=^!q zdjUq{ji6oE;`=aezwUk_5d1Qi6%+Bm!O2Oa+_67zZD}%>-Nh}e*%-eY5kLR3iW^&~ z9L!Qqo|Hs^zWB!f`nvv(&;HdN6~u17Hh%=P$g3q+v?=Zsm5t;OP1kz;05BZ|g8wjH zYuRuf#?ZQHY5gSd%4@V%J?Qx&q~92SpmaV&$tvEwCDumH-Uws4qQ3@ z`;VKqcw+br19Ku-jAF(4UIre<`$8^LPJH)VOdAp0cR4Qce^Wuw|6WxW*(qYyTbd&M zGyZ%y{#+F#&uF43rPj$p9i_;bWN9Jx!^oWzVfh=i&b$Ogm4;CDnk^Ko}mw`E>($qz*p6K3M7fz*vS! zs-{6SxyHy=WYn)`TbP z;n@>m+p$T+D>Td<($4W73S5Zwx#~`V+@Rmzy8faxmPR_57_aX?7 z2m-#5?27bXKZ)iy7UYWO`_D@ANM%Uf*SxTJEz{ceeZB|hqzDE_uG}_n?$5dRUKTP1 zd@xCoSO`Og`G&Pao?jQq%~7Fr)Xrf~_grdw42Os5GBg#rTG(@gx~LU8ymT(Jh}0&f zFnp72OtSE7r(pza?Xj5xS#+I-ulR67=ZrT-iunuqvj4GCg%7si=bKkS~6 zH1Na<1pvzj=M*uBw~5yd;T5u}7;ts@C#oJM5oZi~;KDDvG)1{AXxC3sfKr6}lRomt z9E~ADe55pfLC=^`KB#bba1yeAu}I+_$G{6Mr6Mv~64FDm>l7K$Y9KZ^?oah`gL`9|MtH6ORv zPAWo}lOH^M&dZfX;}>2A*yQw-=8gOh44{M#WZ&Lr|KkF*A88T-&6AKZ4{_Ru;^I+N zH;p&Ty|TuRfEQ;=)2{Hia_Kxm9_vbLLbzJ>Rk zzvoD|HZ~icr4^6^`oX59A??b;%ZKw{~S=#^hbOCI} zHfc$tsp)1*k+8ShAv08>IJuL#8b_Wty3F{lx+N%nJ&Y=(o{}zUr0Prvt3dRlM~HtS zq4uJAZ1n`eGp>$><)7gC-i}NN@V6|Z6KA#5%HWU?z!v2QlgsHrE&b6-%^52C z_=*>DTH@#}pu74xch}8xUd#mhVOmi3@@KY-HFpwMrXTNbAjV{%3$|O8?+s&*->}D+ zyC;xtyX2(axpFtm|b;6<^rZ@4)4y9dXJW2s=%>&;kfAa8?5l|s%8^KqsU$H7u*2HCSsiK&o?&* zbY7!E-4Qwh2~MX+JK~nt(pnvXTwRaDA5X`?T*Bpze|Pjqo6jKeJa5?Iq>ETd@}y(z zSwy74o`}L}aZX<07)n*)5kws^@Iv<#GrohP+&<5Qg1)D%krr+ZHhx@80bB2DVw!hJ zOnx!1F_;bh)4Yc7H8E=~h0zS2dak;qTA_o#{!WC){kT~Ucp;@Ca$~)>OyfBVqq+z9 z_>X^YJEFy}T{M^wDSFLWZ+=YLwx7?upIqPLyDh5Vdk<}s5Xi~>VSK4uhhnlohztsx zRGSg{j_dlE?hLd8TMCBO)jKUBTi`)g~*vVyCXxjoAYm;y#2b!0^ zaON-_{%aC20uikpf7$9oG`u zu)HvM%sR7i6aVk?(Y3mt;G}Xu7r~%K7oeax&@*T9jN_oT=WSHYr7DZ({XnoRaha)- z=+39FK+yA zIu5-_r|FNoifQqWFx8*iU~Nxaoz5FfyWQ6>99v3PBNfdW$8NCMZ$2lp$7CqR{AY-j zoq)R|xvRZfp&vI!JA#R;oH|6TsLI?r_kUmmS@5f@VlAvELpyZp4+YIJHGXw`@hS!^ z@vU~cU@&jeAZmiF5{TOaGwMRYUk8b-<1{JvHNoL4Fv0gm^mvBxzFm&-2xwLMLjtCXVs_pdfCe$(FkG3ep|(F}%!2Un z=yD14+Zm_I2^8_VxJ|2C|D*qK)#?biY;`=Z?)}sHfrZ5LMXt#IM%)7|Kks%00xUKF z_6he}3xd@{@SdTnszKYauepsp_fu*o(&!5+``JR>F)oJhgtEn;+n-M>E{k&!H!*nr zquDb7;7d)v;Td0uq494pc=^zrckt%zbNWqU)LvWc>{;~mlA@1OO(xT{fdAhAj;$z@ zf<}ih;Db=`E(pH-39`knJ~(R}&lTmT@gF#=eqs9-yZUDPN?_R-9gGXDW*3MMZr_{H)u}he#mdpV%8X~-Zu{YkMRVjB@Hc?4_Kfn{zOFk%f8a> zUw9@kXZJNX$ch1DH^~Sam_no@u;~Z<-gLS6cr_#VKb*#3m|Y<~3gKN?GlHLlGlHgw zRD|!uwO-S*6|X;i-#749J!*yOzw=t}dY-?MK!4B#{ZPw~`WM*Is|@MQVunrBP&NUJ z@5jICzT)ya_vrH4uO$1i?*U8napFrmC#!#@vZHt1_|*h|ad%wJJbSoqzt`B(#D9uW zry{?+G8^u89?8plLaVjveoDIRuo~!rjDtuuLCRLpDIqU1aRQeI&;61JXmH#!Aw59Z ztyK~nkEe4FY73z-U{MmdwOoEgs=JI9xbd1u=4m|HT8tAL>;9#9!H)@t`JWprDGe3cX{>HGUdp2I7 zK0YGZIJk$WhdS>iqoCC7AVWfw@=b;~T`n%@sMyRP31&)1ed1Sev^y#O4_{@9fHaCfE!6 zVAURQq|W7*ault`2CfZs!cIBnQDIH^d|u0DGJKO^{67A@_7AvB{$h4~8El;?syNHF zKGAV_ZF>8%<4^Q^Se|FGl=Ss9Iv72O+Igd>CRH_SzuzZAJ>V`4U%#%7_*2<}D=P4S zd(Dpy;q^DZ4Vb7IzxyY|DDbrI0qFrv1}klzG}aDbm!3puz7jlc&9>Iy$5g516MA~j&mao0_iwK0e$mfvp(&Y>Di9Ij7Zr& zN~Qz0I^5IIQ7C96_k(c?;kcbi-M6WJuK2yzx!$DrMsO%G94}yfLl(z4auNi#ej+h& z)GF?mW=t<_m$V8a!kzI`#?z$HN#`wa{Tm%o*m6ys99m8;p`nb1BP0;o(u?(`pj|hY zgWcv+y}qGM)RL*BNsEW0%hy***IZl7UuZUF#OTc0jypHL(&seiAuwoE2Gk*xl*^m$ z&_Be{e(Esow0$_|d5pnxf7Ahz9=9{9{o#pX>@!SE0VjoK8+@ zc%}BaC2zD%Gq@ZGflN12k4+!0|2p+z@v+Ko&+9ey$Ej>KqH#8;H}A-aXa!jbS?^Vc z%y0~Qh<>H z(hI4GTLM!%%cv3qOn)J@6|VvyuzLLOp<7?aPu{OGLTf zL=r1GYcKr6*l;DA<0z)0%WhYG8TuS%emQO)r8z+>z_QrfB>v`)JGJzN6rTfL9uje+ zwy@*&o_JJYMvUMs$=5^Qfz|jf`l4IIU<1H$j%qs&B?OxYyGJ7^a+5}r*bQ8p4<0}^ z-zAGL@dwigzA>@NQP!kNoH%btoFu}aUlO%IR2(>2dT&2WmV+yFi@wgQP8Q;X%pZB@ zpjh!&r{jO+{k#@2OBJGmcu@SgOP5kHNjJK z%CyD`CBXL>!TIrLPde>|$4&p{$3f*<8>!`Z%SMCMwtm;-n0>HHf~OFfJRVBF7e(~J zKY!C(8yOc0)_8K6)qrpC*EtC+rzxjV=g;e_1A%K_c)#umjh#1ITP6PVh!r=r%q%hVf1oNt4 zekg!nnW`B==41w!2Vs8qgR1`ev=P?>+U&K-UW)<$7vS0%u;p=6SXsM&gJv}Si$Ll2 zdZ#1U>vren0PokHUuky)^~Bs_&_q*d05bYxeDnEAil&dw`;gJe)l4Q=XN)v7sc@9VU^-Ri$-7AYbnb4>=fN_0Ec>}IaMQWg4}{wQB3MJX!F z94Dv^CL=>8g+c_{B;3vbAo8s5IYao~@a*Dl{2I?!|NiUUaW^Q!>CLP5=hX!^9V4@i z(sH`4csd`TetEz9pr-T6r1$g5{4%SVUazH!L(Ii9@5Wf9ucfJ&L2XGUz1}SZL?;VF zJfnGiCxq~c?1zx3CBTk0H`g^)ZfF^bYF*M8LM7rhuP2WXn7BOgM3>$;7|*8?ZF%4K znQ&XMkT5EVX-b2=Emlk^*8XSnwk-Tck*oxSI6B$D1R1RlPDSrTL(5Y0NBd6L-)dk+ zr>G*&k-Fi_K+0?WcYE~ju0#3d8iUbbNO);kiaZu{&8!0{1_Lf%?P5+vQ(qCdStAGl z!t&6jFGG&wp+HebVos=Tjm*hDEnRx`aa=%WplQPOUO??uoX6*Ri>sSRsEIoAN*&iY z_BH>Sfq@)BvM#p=go)TNY!tE)R6dAP89i~3-&^}A$dl9j6|3zxZy%7}n#X2{smNg0 zxRUx8>EE)oJiH%;-j}vUah9f+&5|0uPQ&4v3(Q~HB0}3R^hx)P%f69P!UHBe)|r4w zyfSi>ZCG5d*eny8nMCITNVLYeb?bH!dGSr22#iTg)&O@ZzN_E78>oct?UbO|@+ zUeIRjf|BlM&ubbASYQVNSl;j-3`yXhh%xf!q;p@-yu_kBOuj$oy8=`-ZV%1Zz(EtW zVI}ZC!Ps&>{1;!sQrU=Ri(qKbpTgdqs7s*vq^6)4TdYpAu%<&ola|@*sFzNkGc59c zJyHvFs5E%u4K>a4A$GxHG&WQ{zVabFl&JM3;Ja=^zB7zn{OCSay0Bhqbb3V7cNtWu z(iQ5PoaIR%z;drQiCFu19bbBQpaFEBA-bybu37&c-t>F!CcmXOd}{cOLh%=sKo@T4 zE*S%*ZK2ztWY;PRL8i>}kD?BU4sOby!Cj0s#;tiT>7pYl?{L zMtU@_qu|6HocE&eZO&iCzVT01?%qx1F6G8T-{b=3djDs?LfH20kK}blLe|)rU?bgH zneyN1Kx?=9%jf}a9!~x6@UYwcyUB{>9+Sa1yWOR12zCTpRl`|@ z6u*9!WOm#yjNiEohU?>klkt5b&G+(7Nrf@3E@__w(hZw*NJ}&|Ouhd%JQXQZ9-gQt zxK)x`Z7fN1vVjDnu!ts(x&?^MJwFHqF#d$_x6v2kRqr?G)2gY5#dO|u#!#|SYvW(E z55t%aEj-$jCre;Rx25lXil684E~-?rc15?b8(IuNsQ)wJTEw#Wgc91s;N)DAlrN|> zm*0fjC=%g)3b}xdI`Wu0L^ntIVsw~-uz{Yv9G_!tu%$&nP_@FU5%9}e#s#r`dn>!6 z{XW6tKNL~G2WFWaU74L!Jr~}b+(5Az9R&bOi$g1W!6!T~S}Tu|;5uMmlM4a;6~4;n zs}eQFhb_%pZUwg_ScQ@^)Mmyw^K4?EXhJ$9nw5Uj^P9TiaQm}ogIbj30nrSGC3hSov$;rJ`YyELVJ_jvk%mE;jdryKxr}I* zM%00B8Y-7P2tt}}06NB+ykxQzH`#85IA@7$SoGznbu<^nJpF);{P@$)5BeM!4R5DI zY<^7zClJ$;N8WmB!CBDBz(;Z!=$cs;K1MQs@NPxsar%Z$GD(ya675hJ#IPKAz3JtC zF|{4P4dM|q+1+A1_F&+={ma2tiz-QZnf>yhK7wdJ_`5B(Ilzw<`B%Ed++2I)Y`~F; zEadHsSxJ^xE%4%6x!nc-VN&&1wH$eLZV2K0iQi66ucU4)QMJOyNp1DpVlat6xQ4(VaLIi?PSZDmD zTl0BD=;{t42u+UNPb<9l$c0NfKftH7>nGK1)QDOpRv=>IgmJXbMnG>=iA0yqK1=hv z;F!GP&Q&pTFWc3}A=j0oj3jJ13pvPUk|jy{AC79GamBs_oI)9!2@I6nPoFLrMUp7G zht42JRw4>+#Ew-90qLqk+!T}gB|}msX;d{go>k=il7)7Mrd1~Lf>IAOCz?b=3u_|I zGhsMq{9B}JHfQ`Z=OJC%BZ%ua@PrhEEsj!5i&AV;AO(NaiMx&+3gFf_J=m$-KN7jC zk7N>Ah&+83F6 zM{7wl7uHhG`nD;RXcN->g=-SV0-3m5@dD43P!|lL=>ulbT{@FLXPwM;#Pf#oW;iz( zvFTA`K&+-fm8R%WdUxxVS6Tc}{IAD(gi84Fp_=^~Zo`sX)v=DG41VjjGr^B9x4^;Y z)r7}$qNA}C#iIzLuJEs*)cFfmZU)g#DJgX5XIlx|=r)of(eq3n;IJV#R%p)DerToV zi^CZp8UJAQlcMFa`oBj0YDUccl)+$_cYvz@h6^V1zRc5NHgEc3Eid`BUv2{Qop3;h z$&Ufpojbwww_(e3x0gG(s&K<<2q9E4XZuv5N{f_-amPRt1+G)KJUl+$^?@f~wDcXiT9suBx z1muGmu-$6Y6~w!_`(3C~uvvf%X_bS2Ht|EE_+jW->>hu*4@V$^MW@f-^J4YTkidw= z3VMrB+-=mcbz6@xVEYN@tzK8=K?d`z<0z(Chjj1wp-~#Ztp0!otJZ3hVQ23e#FrJE zAym2!%|c#ipn6CZlTb5OoeS5)U`%FC#gmvI-$W&wsM#VfJ#GqD!6 z?aUl&qL1O6LOqs1P{Fd`XF`)lEGFuUVta*hyjk;2?P4Vymjs906W{S!M8_$IsF&!6 z3(IuvAeMD=o*|975xCW?e{1_?nN^O0QX~!Uuhm&I5ZwQ6JdfvFSVD2SxknOK3z>Bm zzJ0-TDIAO{Rxv^j%2J;B7vYsIbvS7Ca&50vFd$kGKeudLPKTv`?xyou$5kvL4CXz{ZA>|1XJh#}^&m0`)@I?Y6qlM<> ztL z?6Qfxcsfs4RG(xj*|4c-)RJzw6KMHuT&x_*qMel*J_BZc<*1G34XQ*LHa`z3WLYid z9Ilai|2KKG6m!^M-1AUNY)0t&RcDHlzAwvG_M2E}AQW;T(k6-7_FsrE0lk>*rXaNb zGBVcSA=q}dA?&I^aTF7=HA4*}hdBAp@qaKb!B*N-%Miv&LzqRom}E|MKH_uSB+Cf( z(b0o&o-||`CURNZ=i(B#BY%>SZG|*3>M;^w=xDU1D0jP(BS~5)(aY`$0X8=|4$Q(a z++&=9ENsFEg@pRygAJagpR3khSnn6zfe}}o5iW+0nhl>Vmi`na1$nFMn2!xqn6sC? zuL3Jyhq!sV%}Cc|V;I-`J!8-#)TE$4n@s9ZWu0&ca*Ue4&qKY!(yU<8RI^@%?X7_~ z*@>V4KdEV(8vBsQ9b)zmv2F>Dy-=j+P2J%b%pyZsifERK!zEgvHDAC@GhMG(x<Sty_!pnkfdiCgjFus}ZC;dCg&DKb#VV2(k^kPhfb0t`SA@rBD~5bC7SVHvq385HRa z=_`WwqoN;;%^a7wHJCZYYpnY@$oCTEq8p0XM0^E;2YyDE3E8T49rs(LqtfpEL#p>A z%JIK;u0kC%S~6J-{nLcoCrAjbKoN+Zal?7WV#1uYkSR32(|z3B_3f8W{LEw6p!`af zz}y2~=oFbZ@-td>U!xY(Hrc<9#}Awb2PHw!8I5bl7w-&Ul4GKvT4WJEU!W+^4qnTt zjzYJX^c=00uuGzZQp!y_v2wE&Yw7cy(6f>VI&k-hqrke4KE>D^P?o0AO02o`Hf%<^VF~pbp_+y zTJ6Q4r4W&`5u2P-ma2FSb|cS?DxmC_vGN>C$(&}LZSgoEcS8ncvl<>KvAJwg7iTV8 z3qe$U@k}L{1@n@otv>ItZ+e0Lu{^ug2Qq0UX}Biz%8Rji**a+{39s;Z7b%;i5&Q7h z?z#A|4U`yWH}a+{4!?#6?$d9QO_c*E%os;zYnX&ZFmh55d61>46k`$Mkt7>&lh7_+ z;rTUKMv7?CSZ#048q4f<3ZP=szZyBsB4w(jNRa9j=*Gu=DalSc*%3(x=0#+^91(?P zLWK6)GUcgyR5A!=a~=CUbqu0RLO-0c@pRT#S}e&ik%W~yU%cdYpggV;X+$}6zDWXQ zYhAKbwWyDlwk6V=sSke>K~5b0()p#oOtO3qRe2>>Of$5pyWbKr5@mu7XR%oS{owoP zEux|e@UhZh)|so*wK?yA2FxPOOCe{;0+{YB9Bujz&T=1Dc{I!t#+*>6X*aiI=VEz5 zu|}j}C!kX7-sNz|-%4O__Vx8y2~LdVTMri5<7xl*ytkrsHRRERM91!OR^iTrb~WvH&lC6qEo4`MDY`B;U1(%<1Ivt=J;@%y@o{d8^(Zc2d!Y`$>x-i(-d97)?8}s|Tf61{` z`i?=fuIF2-Z)E@DluIEpN3W)1`^6gjqP76n$*RK-4XKp7cv}FK^*1pV`8Qoz^P$c} z#LhdR|Mv}V!Bo9#He?qtD-h7v4G4KQn_;t5#M*iJ7=4{lawzUJ0(PD_RNXh|h97q> z)OdY>t}nM*V6ATGc+BT{GqY4G(?Q&VXg`aM5RG|35B( zzkk3=h$n)>XT-Df2MV;y@N@6r^Vc))n-6Eh7UTO(jmL)opF=Oi;u0B~~|Ds|+QjM;%a43j_v=a>zZgNLdqa48iVL9(w2|Xb8QD4v z&i(9%COV`udZnE?k}y1P#4evtR5~%(J)61%Mb(p>Qby**mptZ2++0?Z&MQfF&_fMk z8*H}*Qqn9V5NLyNHA9QdVQ=tnVRF+gpT|^Q*a$$x29q=s1hYl$78DO3mw4z}><5ul-mp z{6RsVDXR^pHF|a|DPsVZ>KBg!0>|> zY25{q=w*lk$iu-~JH@aJE9-s=Y$;}7BN{@64T@Hc8Ak&(bu`rOFvGF+_eZ>bAAJ@U z6h`&B8Ibs`Khf3h5-A>?Sj@5*)=aP6m3mdG+20~p-jNE_|Mh#2QIb>JZ!X5os+`0XkH*$a-st@6^m!~yxaU)Em3`Ukgq>0;ISPviOA3=Bspa1CFk+c~XJvW~jn2 zeHFEPDK^(*J1qo>JMjFvGiB=eCWXjobV=v~7EgMdtD5X;GYt|x-SNaFVHb$}M&miO z$31^6ynF4>MG#*e0EE6A{GH-N$Sp3z;|@`ue5~n8{#%+5>^(E(8$s$yewS2PmUiB} z_bxJ?*K$Hq2DU`LBGnNa(PDi#`N4HChIU#T`q6xE-ktV4+KOlTKek`|$8M652`Y%O zY@Jsv@JU*WJo4y&D?X07dWR#e4KSMBG=Vj^pIHu7TZ7Jc@^aT&R=k?V;*sxlG~Mg| z^Di^&_8+x4w^d=AF0-E>(=kRvIn)Dk@rAO7@=2!aXN6#nd)<}bI2F4&m0FZ662BT< zYF@nHybceXy!E>CIU52KZV*7p!JYrfCZ!PEH7cOPd<@iDNGD}j#xc#!XPmPA7F-k5 zwgMJs-O~N8RT4<%#F7Xs*d)AsB5M8rN7FfY$K8EhJT@CmoW^Wy+qT)*Hrv>C8Z@@; zOd8v^ZNKyU)_VVg?^-kW+*{`&L0a$y)6Esn0GC^xm}ps-@!C`o_;MiLJtO{- z{H$#cD^lBOa`40m6Z~Y#`x-BKneOpRb(!LWC-!g#dR=Xw@8u4xcJ`mg{z`l-#KXb@ z{wGr!_e2}Wgj?WaDn<0Y^4S_jy+=p2EdcF}!qiH+6!YrnwHkT}|xt3}cmhY%FX3Q+sqi^Igfi8^shIGJ_^oLlt zZREGf%>w}BrqAbf*xJh1AJLT`1_RAwI1NYLVR!k60#Rwy?_`pW;b|~o-mZkFe%oyz zG90zc^(HG5lTe!;kedm(trrf34uyd=G#PrB(?$kS3hgej<7yMto{Z61GAL*3V< z&An;Saa=2G1H}^AgZgDzF_HG)P@YwxFC3o0%Y@$RGF}DN$u^MI`g;`e=^J`bxl3{p z@q++nw>$T)$~P>I%C@Mbo(sF_JXVX(T!O|Wf=h!ols!JeZwU`}gG2geGvBa(EQcC` zw!EK@ooVr?gRYmZT&RMsjKl$7StISK^<`8?e_k56?tA@K{Ei4)Z|U;(0~i&L|Av47 zb9r31ZLog?93)LZ-!uh-HlTx#biHdaLD2Jd;h_C8?U!89#b?ixVMCitPR1x{>)PV7 zj)-F7ZxNk9I_`r>M|o|7=bOt~VdmJ3VTxHnQ!V5tulxvuL?qb8ZYDl#6!xFo=2q^- zhyjL6POI}t4958=+>T_CK1JpkE|p;|RX5;)?$XDV>>GR?Jl26t(`8dQ1pXL2Ci*TH zN`h@$p;|rHQFsAh;d-2Hf;vsslb93(v`HA7;M~&7Hj*bawVBP?bP``>dof_p5^ua< zk6ZDYHM3WMV9HNwiU-yGX1R%pR$LanqQp{0l}WOHDXddm$buf=l=a!(kFwN^t0~kR zox2N1%r-9qT1aBSb@{u0Oft4sMot*L;0X^3j$s;f{uU zSjLh)H}Iz{w*2x$J#;9{v7_8q(C3$UEQ6Y6C_nn!k?$biT4h-*85a# zmJ-GddVkPq{OQh-0eu;tjS!OhkXFVh5q|9@WKE6P!TdA` zt_9{x!By54n08Je@BFBItdA3Fg}L&+Qu!h&GGT(&ERq7x4%F)yg_sZ5<-98TS_Id@ zkVTGsIm`o%b+4=fcf?6rxL3oW%VM_F8B*&*&8wy0FOZ3cXa_S8Xtq!R=aA*OP1q|#lrh$N`Ar@eacT&;!#-plT| zE*xsX_noB(_0nWs(a8GKTD}L$a^*e}E&RC}?g%X3)DW#s@CqZ5NqE}GQd9~9$bOa^ zmR}F6BNyAJ&qUl!TQ%bY*tK_dAk^U$ODY*dz>+c6N@&IjbDhKwpjihB|$ zO2+iA0wyqb5k?zRhjScn=@{HvPP#F?|F#CBEi%3%p%IFHboy!yLL&6Khfl(*0NS;>2N*)KNwy7OP=+BA|o3Dlorg&Co4^6gO z&r3%H@s562k3*H;z@~a=fQBp`{-OdQqExEz&p#yMe2svR871e17g}j6Yz3jt0V$DK zw4W%<=ouke$>j)}zc9z-QZNi2InUQD(sj$wc*XCMomI?}xJ;-CNw>8%)iy0yfbG(e#@Fbwqo%g zc>;8tn8z+LCMsL;{ehJCM21pQ6Tqm4^-saAfyP$Cz+=`uxA<=c z1IsTtgC=teg`At{W=r1qz89LNBw+9oDd2*rb64E+5;4|v=pt`VmDpdiwOQ@(cS%n| zTLa5z!cd6{JvyN}CvB)+pUp4K$YnrORel%PuFx+K&@Hs%vJ)dvJPI2<13PmKsk72c z_(HV=^y-j#?GUx?r0o0^Q$`TEIKte;*y_n~w~RT+iRKilv`j4%X~`I;e6Y6FnjJLz zE!U+_ki{d`qFD#Vd7yYO(I^~^L^RrlD>{i8zvR^Gv`qcH=j(GkgEcG!RUjSY3O9|8 z|7qptl2IRIEP<5yor$*-w>DqYFWsrc zXcEo0tXaT@N5U=L<<3^fK6f$ZPsRoH0OfqGG~evUJZkD5QDQtF0XMPhd=~FbObt~C zkJI20YSf&n)hy6>%3jaW8jx|3E_~}txQUJHK4pwp_qfU9n-ELDQ}RIj8>Z~z94juE zD;mHdEd>&ybmGYM;U2hMt3yXI%!ilO`Yv^8hYl5#qPqK6)6^kI6kO{^)M_YW&GY*1 zcyc4pY)1aUYvt}lq&W`XW@;@!*5KzFS}yJLTQdG&>-_Lv&Q3C3LdnCK0P2K(*bvm< zWvH@}h=y*chQ`>i_S!eb7;$m!g!KYQ+&*fe)lTF?!CfxoJ=&#He-1iP$0`>_#Y3!R zKav2i+XTcu7Qmt70C6#-8t2W#c^%K+K+ac+Gmc7&wVYH|hqaAbFVsn|=$J^$Xl!pu zQEO>Wk&7q1TkVFN-${m^ETBAM(gWuV5ajsG^8v#p);!g30rbw{;%#P*F$9Kt+D1R) zLGihcPw=TxI2j>rax;-E*1j?Z?D&8n=E~&z0>TpnAQH7yv|G5PF+e*s`!Z&s2;u{q zdEZL`Q~W`GXcf)|$-!@Jjbo2yTElgH?f|nz$+4V9DW6P_6&<<~2~uSh&v)D@zK*kJ z!jjb$O36C=gJ1P=8Be%|xmasBB-=Ra$;U^*eq}HPo&Dp`WjY$!WF9;yf$~L5NG*h2 zZ%g<6F!sDOa7@dNc>vS4;mZwbI2|4POe%V~P|PFxfJExTQaLIwDjnu;=`(t^t18!y zn^HH?FRD^T6iIeW-YQXLAV;)yxciI_c}y@G3bk5rpn0sy(Sh(xf?623jc65C zqj>aTAQl}g4JxFXoHWJ4M1kw0-dV*|M-Jb0#iF6(lFJ3 z;xnI%CLBn}4>ek>0_x0J?neaGJjg$jZu5(0WDr7C%gsC^7#78@g=`WM@L3#gsy{R{ z^;W!p{nuo{tv@@_Q?K;f0QZuc%P}e6+G60&%&tXajy^(G9qhTY^H8lF;hcx&&xU@hL*a=fe(P>;H4zqeQ_NwPW2c)b z+0K`1&-a4l`0@U$#XPw(%3; zTefyDqJu>@H^Pu3wM@AsQpuYBC{Ms;W+SB(bHGAuEGY>Cl_V3)l17H9_w||tpI2!E zMe*DzY>ugt84kxl?9UL6^<(+{c0QN#XvFpv5g@vI+3R$oF z$+v#<>c|*+=`yj+G<0p=3`H1a%&MgLW#HSG)}+D?0b5aX)07OT5v1Au?ogJ*Yj5_e zeK(iihl5U|(rDt8#be6{bpzsA?`z(OojqLvaW~nuA?yMz8(5bhA*FVgsa3T=_po9U6Yc_qNwVq zxXa!yUC1kS0lq?%LZ!?_a6$@=jKn*19e#>?Tj_PwhWq~b2qZhJITRGU%Oj}2LeysR zpb@wdCk;cfu-GJpqG3YEM<=y8qu-IoxzhsL6UJv`mgIsCV63FE*rTnRKoI!`YdEdyQrger4m z5V{VtIodfII&I1*rreh0f@7L6nPkhY-s}!gU027MOJ|)d3ty1I*CP~JJO-9*3X(I8 zc~%L&j3KGq1bFwt3SHhvy0;Tzbr{d#W8biqRd%3}w`P5)DyvMf=en&6F5C=-IRoEp zs<*d6?&s7=Q5tq1)Cgn5v~mHMT6cg1uu4ZWt5v8MP}8D3!c;g-aG{Y|@+l@JFJFPW z^rz_`^_6J>U;BS6^|~U&$DV)|G=EjZ$_4hL6${Xv_ND^*K={|-x*aI?$Y%Q4Lc0La zu~Y4ZUBu>mPTDP4~OAp8FT zOvXw==ydhSj01aEsosxbDLb|qcTkz_tC-8^}liffA0&> z$U5bK_p)zo>qS8ZT}(hc5)+jw7@gHB%X|CzMhl;GoTI{<37Vrlzeq$8qn9aoM=P!% zt4!!#3Rn`mgLVuTu!#NUB^&*Oe$>aR1zkB(NOjAhQbd~~L-Ak8?&GQrV>I)HRCE>h z75?EyxN<{1R_?~AH$lvu6BpWyiuhb`fUp^6iK^}z=1)w8-w|TkF=;X)I@8J%uMXrT z+7jtFh2W?xvqbq5VfmWws3=Ux1y#ZAHq*aN0I;6IA=t8P}P zt#l5W*X&JOHr#78NAa@*bZWHcxHvPXl1DNGb#Vu4i%z~QuDbRZDn{SFo*EW6-_}`T zSde}KYM}s7MVvt-mTKqW$<*?gYz08EUB*{=+m}p}L(y)yAnBN;F39m7)N$5~FQ#$u9yrtX=v(nL8mZ5@VG$!e zW2)|@Ew@KP)vV$DE>sqVysOihIx6K}Vi8z}*PDY*`>ELVy zLm0;pubr+6UI5OkgIQrYh(xqSBCP@vyZJ{mPt33NX^AlvHl45KEImiLyI514L)Hj@ zO>79ko!EP}pcL8}T8HknJE2LnT#3mQsJ5gmm0(@zjWLv0$zo=_Zcr{8uQ^B-4(N9i zejOifDN5qcmznKn4w?k#OWfW&Mt<2tQEA>e*toShIKuzMuEC4P5MY9b%44rTm8UQs=2%UoWfb{ z?}f<-B`*+~fA|kTZM5BiSiEwVj=phGCq6kX=ZnYS_IBwHOSIFO=XUj)*PM9Xkw995 zq#b}i8N!n_h*4JLt94G<>Eb?w7%YMHfM zyHs&2nwa0y=$LamnIxNa-qATiqAKGsoyH}2=cpB0Yth$ADz?iJayO7{YN+6#s#q-JQ+GYqdD5d?6ckqiC(d#0hc+ zppW}L5KmbD`LaatgQna40h!PK#W`}k);5j(eK%Ft5)*oW5}Mf)a*@tcUkR7g#Dunt zq<~>}x9Z~M{th>X;u-uR!ija^)f!b)i7vD)NTYF*-k1M{bi!%Q$bmIl9*hB@RvOX5 z5mW9pI1`t2qR%HpdhBZDk^2JX+e5|sQ9X7u<+WXC%ilQIJ#{HER81ELlu>EyfpYs* zyV)~v8deK9*Vj>fe(w{a@?5Jf#o?sytn@?vwzJiQ!BFVDUC|d{{zVorGMuo@Py9Zp zs(9FPH_m_QNanpz?+zm^H=|Ba(x1G=M(6LFP8Kx;2@GZrv{q@REWZCSgCY5SvIy&U zWAcy9rwn=U%&QGDqlFOtLoauA_ifMf{nt8JeRKh{1W)$uJx)(*3RxlfX3%pwe;rywW>{F+Ny&AKuF|l^Yp=OuwnAh=+1=ep|M|eZr6`d-2>e=X--2auytk~nqadDWx z=p7cH!9r86Wf!R*9A-Iy_yfd;gBW?hO`9k94ApXZ{W83iHUMAhyy7l7JXjcJ`2L@! z$ENQoEcR91&CtTpfqh&EX?KZt1|xTd-U5kLCVZSl17wyg(QHIOALyh%=6$Vi%IX5Q z331K?$bz8zBbIMBT%*fWQPy1o&RR)<1rTy$Np$BZAqosJvJ0l897+6@!dt!neLIj! zFeBij*C)Mm_l#b<8$Y8Cz`XxWM(SJyfTN!*4Q~J!+~j@+GS|7(d^E#kPdr%|2Ori6N(9c_q3A#@))T{LHkl>HLQmaqKl~8`OM15 z2BC`LlnTU7B%IfTCkW3sr+ACK)=}*B@F?Nla+fyDoQ{jFsCARNBN;8%X+M%7qP4w1 zNR;hy)+sUd5k$q*OpaJ0tUk+HKYo}BXayMo9@NAT;=hEyUFwP!#G=}IMqiW?eg9tz z5G@YP3>Eh?daK4KTvosZx2OHZ$JWvesb)Fp1C=Y9_3M5pdIZw<=}mCkYRXdpq@H0I z&*(aO*X#^V1Hmic`Xxa>LGSLCJi`6hGCSCR1&Bz0dl1u|){a%f0t3Jx%{u1FV~PY? z5a8h_V(}S=U3hNtdpJLlnerWEFq)B%^y1BPN5fFQB&JtL<1adO+7n8QE|p)Y_aPn; z1UwHAw|>5!IZfK>GY}ljl{@Ambxa-^`c1)hNz=k51-Yw(0!Tzt>Th7;?YdR~2dR?-LnQ{VekC7tTz%3sC{v`KW$P#pxW%Gye3DM3}y z*HOPys9?J>hq}q{DpY*4($Z4J8b0sk+@f~z_G~R6295PEOa=h4)7+QBrhG~zj{Et! z;q()vA@F8X%Q*1!7~xC6B{;FCIwu#dik+;aKA9;i#6cs!i-&Z9;wX0HJbpKO%x;I5 zhb5n&%2Ij-ljM>1!IsXRr0pqyXF>TBXJ-sB{V!z!1Yi-I0%;k>iQ;@9>>3-tjg5@n zeuGc0`CF|X6Pl)z%tx=TiaIB3}YDdL~UX#5b%d-J^E zvH!WXG0blG#pbUXl9XGQiRyRDg2KN8UD=2z=35dRQAYP8(ObucjLadI{G;7%7*eT1iCPpSN-oJdObY2)OA@QO?GRe28Gm_jTV)VL?+r|?Q3%->#4+9fFT1^b^dK$3 z_{_N)XP0$+mfGy}?Nr#7aV0lcwl5fdJS}KWa0Go849WJPK0y1vAM&OWrxpUWCgkSf zx-1HLRA8P?b&llcJm9hemZ*o*lZJ^rYA&S0!DMQP(^Z$fiwOVBE6cYB&`AdY1`oi( zgNc6H#?l>g+zgx7hA;9Cx)d=*ushTE4>AY_D| zt6|UQU%WI)6@9oKLgT&8Y0ls2vH-DSLAe$*`H><}>cn<1eNvY9`7^B?1zg{h!kE8} zV>%3+Db#T^!*h@%?vmwu_qe|ln43`de|?@G*{(7HGXib`Ja<%4$z*%w`Fg1sKhZRO zFKF#MkaKIFg2-2x`kx`-v(PRom(J$v@w^g3ps0%I>dRR?25kQK49E_l-vHegNG)J6 zWh4G(c4*+z( zFE_%B|WgR{V>SnJ~TVaF8ZQeH2>#v7Q(dJL6I z+94nD?E)0ta<)OhR>&8CZSLJe*?ThA>h%e4Aa$4-A0zB61g~i+DgsUYGN4gHjuw@Ah0R1RKJ!xe&xU>cCdqI)rp6PV+&ON6{+JJ zR8?FKn}m^RNaud>xT)rrs(LOC&XL7 z3;sh2gW@yCcGn>B&Bl17@3iLPLhZ#Hy8OJ5c=J}T^d4sFsZ*mLTfz^yrXb&4n3`c! z2fXY%ZzL|;jySVjXQlsJZjnUZ$s`tU^H($Xy?yPOuDdJ|o_BduDfkcJYo&S(#?|?M za9sslm8>wi3rZ4xvgJPYV?4hw+8;t*y+MK4$eE(3{K-iQ-sW3N4MrrPG9DsD7cIIH zsnnbso{(=B&h#3;B=lSFXKR$`<)6=5IztFGk&s1xwH3W)O=zh^s4Oix zMl!=<de} zVzfy5@snElRwC!hz=QS5_i1QZi*%QK_6OSRX(#Pu3NCJb5xJSsFZ!tbd}A4tU7>jz zwp$nuY3j&e`ms#sDk#?f5{Ct@gHV+)#Va}2tfM>1M$-&9vySy1ogr}B zSMI#Z1KG9Xc!KpGmkDY3zU~&j4%anvr}i2Se2=@XEb_=uivJ;QJ_9-H=SEY|WHrEN z*&rLe-g{mvdaTiXfR$xIWcBPcOM8(d`D9zJ8tTX}#y*Cn+M@t2o59S03v-7}r{cS(7 zchwvLtDy1C*>|!7YNwgc8|a96q_mVX$u%rFb9NS+c6aC(P8Tx7pwO4}9ZOm@9d%j% z%=Z{Or(J7(QgbcXSTbO*()89vskf9!c%)gv#M+{Si7e>ljbkKOgd_f1mV6oN!bjT@ z!pqgYY)#YuI*y=UfMVY6Bqa~$>GZHA$lHHCt0BwF-cL7GGE7g{vJ@-0|lo7nhopt6l}l*gqA@&?dPRz0SLo>2cF>gKU&#YV1hv@=+^bIO9ri>wyyj*9}~`Ucs3)5mX9+D zW_?cvhGvdgGnUx!7ZFQUhEq<%#8bz|)E$@Y_(>1mwKUll&c4|>?i;@k9-t5ubC1p& zovFGVUk=Q)fry6BA|MT8&{O0S2$RFWJDDfKnq2(PcYav@ikM|i2MA*jWuJ!X>k*@e`X71BE=BH6H;o25L4l998z z9#umt)rKv&H&e=CkhV!8*V1*FSEF}#g2LAqVz{{7=A~Nqc>SjT9wQ(X*EDRM$k977 z{KBFC%->kp^i%L$YL(by6bx;}T%v1p`*F?TsOR&nrxsYSX@PACdRct@^&`{OH9h_LnhI7wCS_#aFv9suTpFN#P4OcK}H^Nka_hHpXGl~XW%;8 zz%+uk_(+AE43pH^ekwLWC2vDdkjGj%h? z8-c!;3Ee3Hxs%%hFVTwy9jlaNi}%OGaaT$neCXS4diQRp=-?ixzq1sAee{|2C-B_< zETtv>dJYJs{`(w>N%J@C1VTh@I-}eOtC%Bp@r0vJ?nCdOL@I3xzAGc_1m&}ZZc1k7 zNyrfi$j21JG23W$-DTBPeG)5}(>EV8+GQvySzPyxY}HyW_?48aNtHwExr>PzQWVQn z6kmZ3hL=xn)6#Kjqh}}9aDBkjAkd8OWIKfWX`#l5oVgUQ(z6oNvjd*M_i?s{h0koo z9jbu*`LE15axh-)w=^r^smYA6QSm#E!piv)bVl`YlT=#^p1RPN4SlD@0h5r)QqbrKwY5F~tm4g6I zalh-zD?Cb&tA9f9m56im9m##2<{5rW14;(|`>+PQASWbv<2|VZ#+I!-wgE!)7G)Q4 z(ib{A#G1#(&-3=Rj<$~_oE-wQ6=#u!aMW_9h}ih7zd1Fo(8oRk8Y}hi@Dt60-_D`a zZ~)MTm~w}15Xod*K_i*QW}UrDEWj$N26#rE7c;yEo-_k*+%$c6-2dVu`Enhng<+jQ zM_e)%i9-4tk?(dNWx#}yt@d;yR3Hu>Ym&jFdl*5ob|IErp{FcT2t$B?lYvgz`%!Si%m?^U8{@K{sd@{pCbM!2o z3K3{kh-S&oIeoUq7FEj14SZSE9~9X+soPhx0{GB;;@m4+)>m`B6oTqzA&F4rUaaJfLdx1D=c#lH!h03xE;JX$m?Q28T=-}DMq z&%mU33e9%~oZ++iDu}9fj^PrqSnk|ZlT7q_3E5DiBoy?AuAR)w(kELK#aa1>Kf z5-Mz(!jh|!uPmB-=Ykmz$~iY*`Nw*7La3=h*` z>|Sndj+N)Q@3z}i?`(&l8o#O2wO#UVnXc6TOsA)iRq=Ou+K7>+(GGtJG^_Y1X!L zNI3WVH%}ugmHFlpkJ(0zZ}Ow{vEOzO09R2gt)4S$KY0mptcZo7XsXOYe)#)05f?Wp zjT9MI0WBh#WimEoJ+hIN=S=f%P$Dd>I4!E+3WSqI7&BapUU3sb5;cPeKh#ZcY@?>Oj5>233o6TwvrB%paJ0x!F=tJ7zs@YEnm`u;xof6Vosw~edk_Ibng8%ecC|4wbXxaL->7>Qyv?rBU9Yl(LsS>HCUlcA$ z2E2iimZBhFqarm3w3aj;7q?D)pqPr5Y#!j^iwJ$Z3C3q59jTEw5nAzO*?b_NX|dWi zN;s=PLJi$4CEo1gW(zfQ%U|5K%@(H%$2L4~t6-RLC=`Vm6lBF?eS8<%+r>kI^P1ED zB)|&7A$slxnnwK&Z;s5MXl!9i|6H7Tw8zq^hMv^}OHSU47w= zgXc^NW_A|M>m~>P97g{;S_S?~Zi1U}vO3X1z&GIV(j7?ckOAM4JIrq{XmGb0g*Hd0 zX*2e0G+UMMAeX0$6yne0#}38=V=9Z6s7vm<_(jQKP-}fd>Uk6HDVO=(KU&wmGj+;$ zcaFfh|Bm~^i>lss$bJ2_hx2CBBOo2M>q>CN?#Yd>(-@LNzkh793yeYK_8q<3cS6_e zIUiQLdB9l?MwDIn15Gxydb>~3KZ?=l11NvqX-)ObhU+?hVEHcHwAE}^<+A(ihr6~H znhuoYxx?q7gHV%QnmAoQ}zUt=WO8|*57-Gu6M{qFC zUJp2%K)Bpmh9b@|SlJlO9&73dVEsn0O>ST|=M)HC)AZvk%>v*0F*rTgm8-1CPcz3e zlh9eD-HD`9UrnfC3xzF9m@^{X{3%a|q3Cb6*I@<#gNvoI3A6!RkuTWb1TpsI3-r|w z`VkE`=yzyG+;r{Kb&vj8<1T7dGJ78B?&pTo_5PX1p2bjyZOe4a*Uvr@XJ)DBc|%Y_ zli{5=OF$FM8%^T)IrlrU0hjDLG~O-<++tPUo(9gyo=6x4l^)!Ix4!fIo@4}5$P`+dG}JCGs|xSr4~y6z9=6^Z&Y|02cp zJ`Vy7UZk>!2jQCXq08i%fbmDrq^su(KS%-aRS~5ay*O^#hJ{@bs)(++$;an}n=)wnLP>1ZMt(91>GG<|H_>efJKY#zk#n zgMriciZmaI`io^Zs|y^-HI-N~Y{Yk7)#@6xw!9?tzyQ`>#8{==F*H-?q0{W%`*p&-qqq7v#u zC%ASfAS5ETG2B)kMTN1AOVj*b8@z3m<=bCuEJnbd6id_+L_+pXzOpl85zz7C33X3g z@q7UiUOZ!49p@c-INOt8n>o=q z5k`T4ir|Ya*Lyu8oc4$GzjewV?Z<8a-J{?;!!Qnav2pSkGq^K;Mu9yFIZBv9Q4;7# zlf-y|b3bYac$6|*{lDah;^W-JV1H&mN8&lYI9S#(gDH8)67jk{7X(@%XnBq6nP6!xD13?&X`*GM%S#PVKeEOcu>T)?exyU zggWs**LRjVEnR<*ycvr2gMiz~DIM-*X$daneZK6Gp&xX7X zev%i&{muRt$E?(e*p1WaVBv9o&7L*a-Mj7i&q${D(u;V?3P-u8iMxTw*ose+#mz;U zSK*HgM!3xYviZ7;0FhwXKO_t@85D8wky;DV8des9bH5`ACKKAh#F>{Wgz9>lJjA61 z=VhPk2-k*`tR*rotV-%&)B5y3sq`nkaS+G*RYvPdlFgX~_T-d~^UV9t8NK^l*>MM9 zI?%oxLG+^RHt2uTq#Y+LBrCW@SU>r&dP|q6GG`pn*P!x#J9W-y`pWH^)XVw`L|TzK zFgWAak3wZ{{aWsgB4;5`Y-0|l856;dDXS)odkQ7bSC^i(T559Y8)QWBWNyyb(dz?| zVbwvlA%q{T+bIMd>vhKbDS%W)M{6n7D3AR8MX7i~11!_vX{X$-yd*{ZLm`yjtKHz_e%=%3*;$p(cGM(LVishb+9IQYdp+|I2s_K z&Gao3pjZkHqZh{kPceBdSQUhlRsSZS+iX@b@3%Qm8%tjQbugIf>3PcYx+Er^c;t<} zjmp~`#OZN_h9z{Sp7K1r!L$ctIGZs>#6x*G8NsKc#tBYM~@Mu2-QB z>jxVHbFCV-iiYrKvd0oA1LMuM{rZ&bxT+tN#!ZkhSHo9qD2_2Uv6wO22JqH)+vc_Z zEE8(yeW&S{Nc<_8TI7j9Qsz64;f=ePsh0;LlfrY7qQ9#rkA@nPD>11`CX)VTB<}pW~|;e#>4}3Vn(>mTj`ErKL89V| zN{NA41uJR#_a2JgG|>KN z$k7}JS21Jm6LSbZj2eI(nu`s+S#%z7Jd^Ev>hQ9k);-ztdF^AVGRzse{PSqosV}b! zNtEzL+|@>OqOSLy1@-^60N&RTq9$V+-zD8}txx;lZrOcxhNDc_0-FtxBtjO@d0M3_ zqOt&;E}(uIAov|Zu8aWh{^t)hbf`|# z+vR*U5#kB?#I1tvTG@GW0X00S`d-(f#h@E@?0WFlb#p;_GAO(vja(X+m>4xS2&o`p z4KFYlzB|*7gO1hmbN2%uw_h=kTx92aRgh^Sd1{Mg82CkyL;@>|neGC{2PM7RKNjf4FK`Y% zq$KXj%>fftS%#2hZtmcA_CKLojyEf~NotfvptLpY&;kU72}cHCzk~g6RmA|vG~f5U zI_yED7NJWb zV88;_dF!iz6cF-we{41no-gaUH|!H8Ni4RMFOb^A=s;$m{=i5pbcu;^F*Ux`3W6RS z%H47#4Lw3@El@v(j}B7M6d%(^rf_B(ge^n{<^7nR%>Km`pCPZ-NPshJwzk2VBfYzJtZQC3t0e0GwF+>RWQYE!|kAmVe#~J;d5FNb6)|TIH6nEh}u6l>keAE4& zc;kV~19~qpHjd_r7z&;!r13c1otTK|NCtfgRT9K`0*HB+BX%hQQ6!LwWJ&wY2>*q0AKcGzy)K)!o?ZGZGbz{J zmHTORnR}#GtJrS*v1Vq0FW?fyE8HXMlN1smD-oB|#Oafi9WIru^+eJTt<;BnpXs!_ zinQdCk#pHvrH9Eu=>FkOqucb{rRx$%V?!BrI}JO8*S3rx7{M$$9|c3HtWw{6ekh)n z!wj+H;MZCPMi(iX5gjb>R}Os19Lg0DO!>o8dM#aGwbh9%LcWyq6da6@dVMH?MVFRZ zmB-a;Ul}dR10Fb^$@~hJ!vSs!_4<`H+ahMlOob284`6XiL=X;p-rn3D$&dk zLh#!=7GiXfXq9bHJyf+EkrfZSe!+|W;far?;w~}K?&%fQ`(s~LukBzPL?d>q@Zfj0 zKqm6h*K9l zWd8b~PP)LC)dQ3GP=ez2g??T}A_4l^@}^9)uGoa=biSAUM8>;$xLey7d3-6mQkZyWI)GR;#ua_ElL?Dkn<8Cgo?74fSSLdlmE zcU^U?Y9k`&SlKm5UEmM35UPo~!LI!Kc4`m742o~N8qeGynkoXsqi56Zo>Frc1Y3s= z?<5 zMw%3A5?Io_9hIj(5;kotjYb+&GZ=s)L81^1vW&4F6Io)Z{`*r;HYr2DxR@Bp)sh!u$L-;$XS7tc#*<4+;ss4450{2Q5#CwZ@+!md6(eSPddl5Pq9eWT3bSC=0pNciujvF$Vf7S`@)#F*a6`nAipv)09!JfFn+rswbU;qn`OBkS9^!0B?BhfAf_ujgrU?3(kcSqCz+E+*6hy8DJYAjSw8D63Pw$jBRE(=h<<`9a+0}ZEV)z zVit}gU|a^#n0}@PqmYXs5gSTO!cpSdOQSuNN8iBGyLM}VK@6kayEd5)!QbCygbOxe z@vasKK{Svi)r^u;ASSS&Hu4761gTWS4XZvUGbu&kNvy$Z5lcq|5sh^_uE`|6Ief*I z@my6xr63Mk&{xyu)F9$fsUu{lH!VdMwTnm>iO{VzQojb}z@U2Uff(&n(z5_CrfWG? zuJOo26OFMZ+|hK@q`M>5DYL51TNQVi&NtG9o+rh6i90pqPk@4VG@M7Q#gpP}hI&hy z1OmVE{xLqvLKF9SwHJF3?tz6okex~FDiLZ2TN5Jq*s)KV5RyboS7ZC?5~VH4k+x=M zqo=H#i(IPm3iSfHq|0K73rf1SMD@X1G`3vQ6(7RUq_Xd*%aE8QBPugr*OVKNe>Zz> zSGB2jfZ;m5Vr>%LnWn{D1!1Zz{e9_&US0I)<65SB{?bK%>6Vs_?H5pj5lucCANJTS zxBQI%_y7KlefC+)UtM%jBn(%)j{t`R-F^P~7m!#>y>bI!(h31ke$yiuhv=N=m4 zaQrc^WPJT7`Q#{Bx{N%HK#K+xbvWU; zi%9G;5XY{ocjPNqTo{~d2o8J#B!Z3e5cq){jtq~6IZ!1XHmGE%1gHCegW-K2IjR}I z(+*uN!1zQct#Em8z)yO{d-=O}7<`dXt7Tk!{a;ZV3J0v$38fEKz@+3JOd9;2CXc1F z)Dj5EFplBW;+rzeM_n77{5QUR|7M`K#A3(CAR6h%b=o5tD!Ghe{j zXT6WVeaj=bB07nt#s@$6W)40Ck{W1oKbr3xYI#gWuZh7VRZSOJoFwvH#8F!Y@BQer z+JU-?*cu;)Mm@~EFJOG#5{@|F|1vo_j;TGu)1S3J@A}tQwVRq-T)q#+oNTr(V7)QI zRaafbx4->u&OP^BjyU3|z-{3@ss&;c<0kmTWnbl+-}ojkI_X)Qcx*t_25l3I2IxrBcL7;MZIFh zHlvb=P~=4<81bGN)z%px~;%JFfN!D!ZI((UXTdjHP(WAT>E@@x0 zGHtYd;gzIBr&N)^ML{h8h+P?sNhqSl8`?p6tO)8g6pU0;R(zGxD98kZ#+S7f^U~0Y zv6|H4wVPfMj0qi{9(iOP-~H})qv5TOzI8g^?@sCFBpm>T^l+YgARnf}bx~c1RUxd} z<S3Vq*)-95Ehi5;m-#AWer6@no6hb+0=lINz}v%d~aO zh#Jp8?h8_r;(=@k&?PM05yWideIGsr8Wo-cX%`=5Cd9SZD)0aBjX@6>%SS%^3C=j} zOooR?c7aP2c zZGwOQ@J%RnthF>6O5RYiv_|eG5tGo!>m)YeX(t@X0SCtFk2FN)UA{d&VEv?)Hd=%( z`AZCEIl~yx1W~x*CvQjdmYjOeyKlmigVj)eX~6jcRH)Z;hO!azJOmF;Ir%A=yp_2C zb<~fvtYZm0A*G|DiJg+5T(dA7GbpTq>wok~OwnPADE!-df7p(dD>Uj2HvWD$PI&eR z%~}o*Sl>E9ER_k5%e2;n1Td}5IjbovMq4OCnYsOyE8hq%08mMEFD}3Q9)9$(-{XDG z5y$U~B%K|7%@ngrDwZ3aADxDhU?eMhTs@31%y!!ibHhz<4$mwh5Pg{7yDV@+puyPk zs4*a%c;bn?h!MM@mUj0P4%#_4&ybEJHYww}w8MH+YGtG?HN-nlQ51}gjm3zH zCr#72U6jmfPU8m)+AQg|D4UP(XYEjqfW&6B><106^RNM-IkPyhPMOS`M@WvIJi9#)C1`QEC(oumxxyjz^5X z`y38_^2H2|PNM2*6yrQ_|ASm`?z`~LU~7HsYYCYoRkI!wfTN-a*ZtbRKDcQyn+U<= zcrt9W45OF`_xb8S`vyP#=@0qhm){&8@o+1++w{)Mp3+k&da)IK>okK`Tv%ux#BdGKIGj=0Qb`NVb1uu$7nK@_| z2}u|MENUXfK^99}P0`YkCLKt;ZEQ3eb)0joTeprzqk**s?+R9|*dCD-V;cPKxA(Bu z+WlC$az~Ci;y`Y{=V6Z8XEj49IA0~b=|KQAi$#-c%gXJ(`|ji6haZmVonZfgj)Y4$zDnu7;bw z8+4iX0lX-pNBz0aUx&&VK3_tXEN5clD8IkI9=R!lvz(-+$z>!JkiU$?m+uhG`Xqt6 zjA4dDefZnny*F>uzpiY>h;H~HZn_3;zBUHCEEJM)=2@pf7C0Sx0fjz-AeaQF9-Ab1 z7x3u8GRR#pMGGm&Qi~rFZo2ao2%$x_FW`pnZ(#ic308Wz?42?$21oKD zINrHykXjSEnIuW``9PH$Q%MjC3X&vYG7sesjU4{t(|>}xKZ)_pkew~I>_J+z?3`%` zv;-)eq9*VYNi&O0dSb4TmK(d`5!CH~NIaL1;DN8TzHVlzV@eQ00-sS=Lo?oson<$SSs#Iip?>leIuJ7n9HjW1AV)COq`eL)?D*ZRB}Q zmRK4^&Jjl*3kc3{#O1>9(h!gVZT}&2uJtW)mWbe7>o~&83h9pV2`R1FW zV4S{+dxf+UfaQw65ld2L*`6O1V zL`ERe+271yY@^<-EWD%C{BFgZkVfpdK8nMk*jCYr1jYeN5skDgrqtG<31mor=k0V5 zyOBlu$KGQ$vFu@de4JnZ`q$e=y82Az68%K76qN1i$t zyG_^xo^kq7xJY$SZv;o&$V>6$7*c%9IyB@l^R7zTZla4>U}B`+smEYQtw9*aS`iHJ1+$ThaLy69$SDmi7=Oi<2n#K8OuAfHgGk}LyFQsB}Yj}Yz<`s#bFo@6*?NL zAWe?3iqVXeBxIThF(+aJmKBf~j{4(Kv=Q2^di5BYS;v>aP^r%xIhH_5?7a<@eGVE9 zOyW>yNem<=WRavv9YM*`4DU^|5)jQ1lU1g=8vQk0gyEiO{g~r?!!iB$gd^*ntBNJdiA_F)=YomZcPKoLViTKE4CH ztXai%-}(uM@Ao8r_|se1=lM^^W){;3bw&#rU)p9t3}Yk+@EENWF%(5UFJo0^Lz-Dl z<7&&5E73_qnM=`tpd3mg3ApjwaQf4Ki1#bl`+#ft&JAY=-_J~dAFiBE0$M!_QoKQp z+GEqu>umwf)?q6NvWD3-2BX7p=)Pa4QQv{l;T?Ix+JcKO+lOHbh6LAuv|Myq7BC0x z(}C3%#leh=xz9yiIvCKq+rXm&tBTj2Wf?o|v{Mt*c+`UzRsNy*v}UWO2k9I^k!g;t zRMt*s`P8)KET*sH%jP0r)-Lnk*$P)Ww!h5q>{#IW-a`AUbFW!zIOdpRc=fAa&DynV zx!{5eT6(x7@HG)&EdP#)L0dKP?xT8m#Ki7ylC)SFDI?QGh^pI?;c`jaTFlaxIc$wq z-YUEMC=&S-Y^Hc^#pX>04o8{=0!hUqXr*DC07G3#@Pj01kw#c49ou1GIOf} zU|1f&1_2QZ8948-M*0GMs})uzl0rCj7_l*fCa`v9vCKn~b{S(LKqDdn>+ytghBEV@ zt@UTsP>`OW3KD>V_R@UQo}3nq67|yV7ieqlOtr-+F{Pz#9$WfDBRCOMR*TfT&6g;z zOTbcQ%rvNF2~G<{l1c}&_IvCleyit|Y5aq(RfUm{5cP;L%>aT)%f%CbMQhDKL_I1E z%&7~Z?1E>EiJeIr3=I$CU4a!#(I^m`u;=3sVD$Sx=P&=_^&ENdK^(sCPBiM9NX5i8 zzi@`YHVa~S?_>YkKz^s{k$I$4_3h_$BLI~ksms(VvC!~15b2 z2IGDW89Q;;J=f!+fKF`8)9+#rdDVb^&xI|1Fb1QKwGs4G=&0cw)C?T|l<)EDTMwt8 zhGhxAy!9@=dnNqx7Dyaid*%In|64!d#it#~dp^0GHr|LnuB9y!tv#-_-~FlG?&neZ zm?EKQlBDds^Uf57d-TDR-CBjt*YWMACAQB0x}~#AR6p0NbG(nuvEOI1Vxf=lw+~6gN^!7C?`$ss-S~E^{G!GM!44(P0jbw8UdJD@RTL9R_rY zyh#Fp5ofr04lJU!Wemh}L1{}_Q^e%)fJukUvMumf=~`YWXr=S%%NP82q55}Wc z+^X`cU;T>DeC9J8cKG4!`M4*9OrZGB8&EP{e$Kg^ebzZhk|KC=?{QIDE+nkA4TBg; zQPfG2B-G)|cPIHi{R5o}WT=Akb&_NVrv)m38M^L54{z6iC+_tXes}lN*{)XOEpNG! zpWX5_RzH3fzrJIF%R(;K`~vRd)=CVfr0-WOm=HQ zCA3tIebS`^)KXPWJN-1Qu}$*N-~bMyl8a;TO(!et%==z^#o14K*n6gO&=OckIxfI; zbrk2k$24tkTTvl-*QRssfRTz9TqUlw6=Sq$*@w-~n)X)H(hYPe;(u3hvdjN2a_C6= z6!aqmrPcLk>VuQ+Kv1{$r270y2ikV513Qg?T?qN_BFNLA*``BcJ&y4m&!E=F`OA)Q zJu+Iv0z=`CYQlz0r%M*WNYajd7W&-M?1ZO_Gu?x+C4FnpUVH7u8E2dkGY>7U$Vrgs zdIXR+k(5FT966cKu*S6n*85uM#bVt7VHQ{%6KL?NUNF99rY3%z1sC!7Lv0>fu?74aqYj(Mh zuYG$jp0Jmt@Rf%bnX5(3%ofMb7#%?=_7$RAPO)#P+IF4YLn@=s!r) zgxh{~8)5`&1^~w5t=XAuvJUwn#Fm>S;p1>@u~2K@fYwLnG>uB808ME24y}LzEpIaI zaS=L$(lt{BK$C1hK_|~%mH;)ct?IZNN+LL!uFOz!TBP{~G^4E@HFTF@eK^-`ybsz>DOlNG zRN{5{2v}5}ktBWU92ftW#GFa{bE}rAoAQOc1yo6`3lKF8FfvyKDxJXH|Igl+$H`Sy z`G3#7@4c$(C3`}`9s(!jw3juqvMJWi~K}U z@Hfi1usJh|fFKA80dYW-ENltsbXV1T_uljSG<6^b5#jplug8NAJ_rC%z|^47k1YxaP{A|ntJwGqp!W?CWP<`M zn-wKx9xN|?9%CEO+hYxLZ)6UqZ$aDyWN;%(V-zloJlw#hZ90NZ0Gfb3mdu8r^%#o# z;W;vd1XSGc4s8QFfdC^o`lPvtB0y;aG~{n$!j5lZke#1@2Uf$Ceale;RE!C3a-l z1Y!b{b{!H-dhVoya-|fmMi`m1%4u8Xi!Z(i0C4ryS3?L1!|qJ?#ce> z9pKlA>59}H;$}WeZZV3cYXAP}pZ+O6@{y0?!yo=IgpiYJvD)zaIA3Ttd4G*Q{6hin?DLZux!dXJ}$Ftv9BlC6}{_$57^;_mZ-{A~$1X0>C>zP{F6Zv=}8M=pzeC zNCW2%BsK`-PK>~8{NwwdLJ|X^6sa-a+_WLTfBS@5Sr$6W79DO7_+<`27wMDPpE)wOu&sV5Oj>E_=rgyqE8 zd+)vQ!4F=B%P#xS_Lr{SDI(S?n{sR|PNP&A`OEp=&Gj9b^X1=Y0051C#w)v%Gz&J8)kZqO8 zY#_*h*}yOmYeu3R=N4OItjWM40b>|itDYsl6@gHXBSAF(H#d5Gw}A5^^I>X{BS<-K zEMz6!ju7s5uw;ht@|+-Cd{Mww4+xY&2p!JK&DZZF<0$8Exq)*Dc6LAy6w2dJ;%Ba+L$pm53|IT$>Mlq1vbc8EuApod2-j5VQoB0}uuQ7ESNh^xk95N6Tl%(11Nrw}Z&lJd?@)>p3TlF_tWtYyg0e zz>t9?4MB`pB`7IIBnZ$Ff(RNz_KZ^rf^7DZ(FTHSrUrwcwB@nXO4}CN++v@*pNWGQ zEdedTjrQ_P&Av5H;VRf(&(yW{gK1}jTq3$G(M`}KK*L>NTYsLn8rj&?^9gwP4<2D?PKB=$dT=5;A9_tem`XR#cW4 z%Q9Tk*Z@Y>Xqb>-w&|hFrht*sCCAA*L4XE9qp#A3L}}DsT#qk&>1(*~qDwGqu!0}n zekYW&&sDG2aqZPtr8O^0}!sf@=9EJ<&{{oW(|bvl-AR$ zTL-}K4u5(yQ}#H&BEojh$kTzH&I^l{^WFeH$YmN(=ElTXyrK+ZnRd45c#b&-l%!Fd zc7GgMIx@~iN;EYAG-1AM%LXITOf=C<_kqs9vNby;fIhJ6pW_?U;5IQ@YrLIy7uDPqpsXGF(4RdWl#2i`xP;eT67F%`gCieGCQU*y|S{DrfKl<5x z?6u&BFv%hWViAm406;_Yizq{)Qi`E)KYsADlTj`KlJ>T)qZ)yfmL0m?u$9~Xy?bN( z+OI|Saa-DXrk0j`UlD@{1}}YSh{FyY!Nb4)C1Mp~b36!%-9bDH3C9S9EkCKCj?aF1 z8G-;XI);=H3b%J1&7th%+)cOTaFIPJ2s()omHJ?6F-D$Wk3IL; zmvzorV_;waXRSUHKPKZMRn0GFjA>?P-%(){rx=YA~n zxtpaX+jwZLK|}}wiF!ST)^*IBITOR1Hz6#~gw!M0yk!XeG6ES1!F3Roz$6ib5&|(| zl>k8*3?PgmkdB)-3Ny|>|NIy&dGE$>ehF+iB6-gKOc{lDLCHsoD9g*sRei* z(9|0{)X-YtfCHA|+;h)KvzWC`0J7!K#Fp5zp2P@ALTX0B$^!W?&`cmzL74?&+6+Nu zkf>lJt|CGJDU1!$u|-;yQV4?(TI<}plAyOF+d!C+t6}u$H|i+ODq(GW3K9cca{jaU z`Zdp?0>;eQFXCPA-xnMX;f;TOGNSSbBpi_x6WEl1Kq9S$MA+a*%5;%R)h$gzcJ%;^ z?Q_dbmQ$${D|@Ev2_XcLLjM5cw!2>jg*s|@5%=CX7eD%r#OMEc7$pSICPsi512dn- zE011)DDq@sIX9YwakV`tOd>*DOAv)2{{8B!P)`(A?6WIkQw0kRVFVIg1Bfj^AqeG4 z1vM3?nQqx}-F4Tsl^k%d=6-y@*UqSqcJd*mM3N*h#%5{?0&cwF>v(pxBR^@rG87dY8aOb8+ZCqNLD zFn|R(d^vE~p}>1T+7D52yFZB#$yWdbXw)E4Mp91@mP?b1tZYw?lMi7DKxvJ0&-n{f zYYNGR`|-^8?!w5>FrIw$ek_{1047nWR<~fr>={V3Mj`-dY>hs_XTcFxpS{{;Xf(zx zx7^y>{CRt6tWG3F&c}`bg!BwbXl+tsJ`6+r&wu?l?)}9*_>AHkt#y&lyk z*5SuLxf82i`x*@DO&C#6ARn&Sp+f^V-+VJZ`q7W#V;}n%m>E$NZaY2MBrhDhIhjnopM(^E5P(uZsl*7& zK*=Xsux&jh2`H5U+qMRY13%DczvYomk>nE><;oQ9$)26jW}^1uIfn+c?V za%d2W1pO7e>&pPb5Qqd2k%2@X0D-XFVEo@oG!)Zo7y}p$DNB$tLR=q#q!|74UxE`> zF2{#1dq4j3wtH~$F^AxhM}CbPZ@STDRR{qSO3di*2P*>-A`KQ2i8Q)PuI9d{VKF`q zicUrv6*yZon#|u$K#QH>?B#5dD2i-rRAxN9W(`)K``1|ZvcqxkK7)8>%L~x51dJqj zcHJhN__|YZ%uxqI*M~4P5@6G&jd2_BM~(Qz1-?) z;Q#R$Vt@*seQ!OF=j@N^EOPNw{5-yh373%Zgv82dQ^ixgAtrLvXnJp88pr&1bpa+z zlGhV^I~<4+hM{eg!M3bfIQ=JtXc9@!KmR=9S{;WSaX3ys=_EY**zX{MG6XbUc=`bd zIR^*5>Tn!>)FF8I{$HY2tAQE#<^A^~Q3;|bve%cwm1O4lAv>)9MugK&dp%zJ+Sft| zforb025}rujQ~yib!!J?WLrlW1k#J65$EKgLC%<_HwNj&skL7=(8PW`mK+ng$**~H z*h4b5YXD<1wen+K&-Mm&WlUkCoJl8cKgSTR9cu~f*wS12iH|1|z}xpyG$OFlwvKXL zO5&g&uQ~YyoO1F>VA+R&6YMd67M3krX4|C-;OxIVADf2zQ3^tU6$~3NIY-Ii(3T-u z_V)cm_Do*TaLhXQoc})^_b89?UIn9gJKCt%>-f-zK8z(xmSALL1eMAFgzQHtWYpu$ zAS^^*X%LAPh{_>K6j)Hg0H>XH8Xo@LQ~30!KaD?o^BGuv$g2Qm7^899ZMS31V~@ia zN9iafo_OMk-g%rZUMqF}?6}G~n!Z3|1BH3*jay_C`9JbFIy~^`XEyh3$CK&7WBbVY zmzzS*l?uq@Z6V`m95Q#DSO*ue(dC1QybT=MP>j8*@U)R z1(PL-$~Yk`a->#EkESMpEeq=o}ZEhQ)W+Y7^saWbl^h(lvId>4U|X_Y9I;%C}yw_Fho!S z7_9U`7!A#AQE6di8yVp7#~;TPS6t!Ftm*Cj-sr+{;QZY29bsD7mXUnuLzjV>@r~>L zFV?Sn8Vlym!BZO(96wk_$;8mIf~Y^l&*D=U(}a8Oxd*Q} zcp0R$QK(g`R^g;ouLe87R!WJleeG*7Y#@7_7+Y;Ur-U!HaHU434sg^HXIwUj><(#V z+Ac+GRi|>h6cM})USvlP0QLf$JLL$&2rqy6%W>wJXCep!D5YVHg3=mc6u>BjH=l6^ zUdIZXo_QAk_>-T4Y6-Rs4`Y`FbI@OwNJb0>=P$zx8~+C;8N%?;X6&)+t|02eK`&d1 zWy=r6^BbSX`t|EEf8Kl$0kT7oZinW?&{{)E38fT-6djSAt)twu{UME%$gl~BaDPYC zC=Edg5RT=XOAgD>AovUti6-{WtYW0`JYKO6_cAed{84C)Nx2V6$BiMZB_tRY5>tRE z`9_}rl>h}m4m*n}visQ(fU+xpa!u-4jI9(~e=oLQ%gKY53}5m@7;QiSLBl|40|@~#j3rB!;EP}UBA6NHoO5n(Q%{wFHQnSTO=9kL z;4T_M5R$~Esw$;$)X_)bs;_nix_u?@Au>Lumx%wQ)AOKiFN(rqEv|*6zW3G+1t&oMVnN_{96?7O+ za>M8C|AOE|S7k+)?Ni^Sgtoo@oHAf(T5Zgp{oKuC*&EYiD3!P*0pSR;4WuNLMC4Ef z32h7l2<)=ULaaRbM11x0Uqrkm#%tg720Z=5Be?DhU&LG9{}G&e$}za=U;hQeb&Wqe z^AbG!$91^wKmHT32(e{&1dI1x3?VG{~U&U|$B3pLCEI=#)IEF;>ZAfCI4Y5f! zwN|A9_I%f6YfB&RhDnk6g;)A*_6FtGXi`=YcQ7ED(lRsm8M6+&KniH#_im}{VHzi4 z(gdD`#<07(9#IHd?rl==fg!=!{pSsJ5&F14Hs|L-^~sUmIXwhL`Z@L@1JZ-h#q zp|~IQdJUz3ASi$~1|%em)<8gD2rz5gb_%XVq@Wa(Whd8qlo;c1LPpzPdHanDoL%0A zhHMcTGoE|5UT+89OO_uiLakOq7>3wu@nU@F1Z*@Q0Btyxb0h@*=5H=W5Cjo z`uh4%uh((f8~zl62xS?d&IVjA;hmrOB%lob;{4T!tHaPt5J5kTjBvqQ-wI+0!;Byd zAV@%I)iL-!DG(!d;i!3E3vT?ryY>+0oFK{w5q6Hgo#-G4p|u4sd~f(|XGDw6)5}?3 zTG$kD+j|uvMdK~^1ve=};T)L|#N^U_ec4LdzqL)Mwb|rFQ1WpYzs$`totl7%ERf-y zIA3018Y#=7)rh7sSfi+UBcj!dFT&(E2|>@@x-K=IxQMWCd9_0RJdg2_@hESCZ495Y z1cqUe(!9u69=OERKBuGg9^YOSVR}Xgy!B7Y(C5ROa$;LTn1}g?c048k# zPi6>`5JEsXVx>SzC);|KLhfD~TP>q03d-Cpe<CZjX$9KzEP^1!*S`AI!erXLF?nCU$cI{K6Q~t$ z;Bj29QxiM_Mr+cMwZk0SSs}-nVReHbfw6)hxi&B57yw1eE8M-Q&A6CE-P(9Nv9#F& z++^tH+O|~Ow#~i`6?#5KZnldzh&mbCEjAprEPa;uuCT36aQ=X3l!A|ww3;z?7viQ3 zoPBqL(e1t7*kQ);#~+W?t5*X6;y8v3B%}zSk_15zKtuuRwHQJMfV7xEU2B_B5o-Vf zk`3ax1`&i1OaRA7v_M5hFq&bLE*MHD4f~eZ?(xiyoiK$G;Du%JmvS2t-=#nXM(^~>wzEL9FcPCUnzC8GWRZ@%Cf<26w#$Nras?%I zo|D%1Y*qH-LcgfXAVmR%%&ppIf@ob<|I#=P9(R~#9^Y$B$oBBuE;%#ZsMhie8Z;#& zHGSA1pI&=MReFAlWB^xk3rJ}_nSfzVqlH8S0SOoZ1MQqBK#%|%hJb*Ogle^l#~**Z z2V_jxfX=acM-wY)1s`<)->6ysj|I@Rp6^Z$5VcylqZzYX%@y3ChB2lQG-u;LMtAsuY_iUP% zQC@f)=_sR4T~6FK_iq`SnrJj2YGPZ4IvO>j*m6CSWXCg`i16HV&*Rs>{xx>pbyv)p zGso&S0;CLFe8uWX&ICAF+UyEqgAfg2XROi$fe|7|uf+~1BWjh<89_-r`Ert`ObmA5 z##RR}{7Z23%jjA znQcKiy}Z&BJL($Gn?DZ(LZz=_jWJNKK72<6Q40L7+x9SfekI<bg#=e8uzpNzRQ ziYYi|Hy49^0Gubcb-0MFI1$<3?X8}Z1ieklw|iq+3@Ihb6ucywLbI%63NVnS~bLF^PIiaXO#N?~wt5DOPBw8ue+Da|t6zIuK!HM$vy47nv3DYC=X zkS8J}af~nu0bs{Z=VTf`iCfo{42@ngv!e~_;^$}_A0~`1!b~~FUh4wBCxqx_V)RCDOd}m}?s@0p{PWMp9k<^OayCiNnBR`|`&48dbbJ)<)`jw4xRb3h zMS(!>!t!m^hcV-LI4VX_K}GLP#qT@q7q2&_{SZRnmRoMcIp>^%pZ@fxND{T}_$G74 zw9dpZh9RX$iMV{Eky&AQcq6`X?YHpfZ$1+D1WpGTUq@(q|-rU0(|XjUxkzsYx65$IyDCVv<5Mx6lp(Nb_5@S1lB$C zEUx_b%W?0W>+$;6zX3B)f{G2oNFt6C$Uq{FM^LF$P^($CR3d^>Y6st4qX<;aak9A! z!iC3hUMHIRMxC_T81KfT063EYPsVzNPT=wP( zsNG3;;umeLu*mQ5of8$J;(MfR|5E!IpmFR|qJn#rFs(9{dSh$Yp1|hKoAJaGPhj4> z`4|}Jw;=r#Wob?OauVi7+O*aRLP*S?w-A5z*XLt!a25m!Xsuw_H~}kQ45M5wBT)&$ zFi1fR?j5Flg+Ey(yHyOcf;F1drRT{OK+*)~H0O6?rhR%iyugC+RHg*>#_{c{-(&%*58@vQW1i46@+dx6k>B;k&N+-RIO(L5z-%ie zf*^pI!YspSor%HDxnpKL_0&_i{`zlX#^7w6bIv)KJ!jChb0LrbLLt-d{LlaVH?*lD zkO2(4c7#G8a{L%O6}yq zjG;UR{IL~#SN4p*cuz@fe@C7OLI_uD&$d-nZ}diQOo!8n#rrIFvX!BYh7h6$LQg{= zBiCW0;_Xhi{ku`x(`wA`(j@LP8qXPzx&61UJ=o!%yp^V(w(;J~oxaJu92MIPgnS;( z9tV4G_#vgl;>G*G7@cymF`Fq1piTSkBw-k$TCHKhf(5wr(szMbKuU?twJM~PP)Z|^ z66^>+rIa}4=wncix1dytpcJE2Dx+4bVTZOLIHxRtFgD>PkKwG3IcK6h^{%xe$5`oR zMI?9!ZDP=&9d~OzAkpakwPAZ1(^y^b@`$jC)V|W_*Bg@&Bd^A`6Bk9{M&C4XP0eE% zHly_n-^pqu)2U)|t)yI&RAvYljbaEeM8k4s-@=ihNv4Jfv2nlmgq|*DBW%0X-sp|q zm>#E|4G@(=%UuIP5+~qt4`}TAn1nv*5L0N^V`C92=9O(wmwfsb!UXGNx{hWRQ=|kzDQUr-lHSmg#93E1Ck;`=E)gNoL5t407HNvuu&ip7y@ttXAOewB0#>cTX>kAmAOJ~3K~%$1 zww{EW6%fOugjj}+?dXt&V{(;qV?EgzyC#0FJJ=7}yQ%E)oD7=cvsS=O#gRlg_idT$ zCS9&oqot-taws#mxg=lSvhe#~v}mkmE68Z&qK9O^DzZeF8Jg{;`)00Z6hJb=V#Rh( z_Z-I1jg*ZlNs@PcC6A=;w z$q=LflnJ1N0D==p7zhGb5VSH7MA*1-BYyt#pF2g;bCPQKJN5Ybgvnwj>>#tz; zoLLR$#mlbAik~K6xJ#|8{WT4Dh!S+?`fM0rwl>T}N0!LEReyS2C|ai`BllZWKx%`v z_<}^EXph`rf;Rvx=uBgni@Ay(F3Jhf?wn@rJ$5fSCb2iS5i+!KBeZ}9%neg6-|%a; zqtm&f(SQdxH!M27_m~TkJ6A31(TVUgo%d#AE;W-xdvuxGzT0As=OQUBOqw~b*eLGciQ7+k+MyCy(#V&n1bDJRB1i6^<7%&nBO+TtgSG zHD1{~ncKAbOn6fEZE+ZM()8jY_4Qf*C@J!h8hY`63~plLV6N!52cdeP1if-QkTGTlQkc z?mm%rOaN38Ba9+wtssM-&Dryv2!xQ(DuE0_d)V6)G_`1Zwg3SGNs=In1QKNshC$0~ z*o~=Gj35ZWMuUWeF$$thI8w8OX9k1>G()q25Dcv}f*@?sj>4^Hj4&*v+Cdu)=^awe z!sgs0xrqSd{Bom>CA$&?0qNem@!3Qx1R=Q{#86rxps=ZoBNs1XEBNC$MpP<;jcHOw z+_EN82GB|&3PLbBVPKjd@y0rU0AruPXa#`NwPpusi58UI6WnT}5rlySGh|?mO{c*v zuHX%eMr1#ZV?>cNrE7Z9Pq(8%C+%sN@)aPnC=%=+U{%4 zyt{+@O@@S$nIVJF-D3=!)i8vVP)Up+jKD@aoTM4NX#jyORv1}}Hp`QgEM9{jm70(|V{XWD>2)>1$=B|Jqp55Z1eGWd>j}>T zk%Uoz3>)&_cWz398m85Q^-f+3GbmYC&xRpP(|Sr0P|CnnF1z_M&L7WSM`W(&{%tC0 z^;VjO5ZQUQyl%Eyl^gaOahjQz{XLOsx+bM&s_n=pgAhh*cW)1Z==}HgUO3w;vAZ>U z=U&@)w6P`occl}AK?v>iZ`O*UtPO%zuz+1gqOmtI0y2~tr2!ZzG7a0;5J(7+u+OHocApjY#Q--aA|=#hV*p|pm^5=T-Mb{9jX@w?5RBRSAdZu- zd2=d&W%GPf)`p_A6r8gjKLJ7twRM64>ZTcJHv?^MPb$D|6Bk#jeR@3#cbaf-rKj0}Xj%|K+AKlI;6V)tL6>B<+gU zPM~qEQ?m|jlhc+17ziP8zySvUE($*$6wac7_PR2(&Q_Y8(2{>B1g8ft2%}M8)pjew zJ6>T_0vp|M>{_1F-`n`lz-NcFW?!`C`BCa%Mx_dj)(E4(-lH|cf&|EIUY~g)WPpx& zeCd6qy@!;LkaixTQu}*Q1{o%HnGVvE{Yf#fHqCSIIp=*|Y~#A~ z9s&;hW3%!`9Jlf4FI=`x^=2?4#0limK}%-n{t>n!_BCVc)NEN`YNPz7`fHA}&3Roi zA?tarKvj(HFc8$0QZ)0z$EYdqZRx2oFo(E;Cyxn)(lr_LFfDeuh8F- zK|6eJup<8d$S(lQNaYs|#fGF?d*>GaG*!tDg3lAd4}l>9aQ84Eu6^rhMeRH{p zvBEC52uSWU`|;^+X~J2IinVbOp?^A%MAbE*=3JY6h&fPL^kn3WYtlbr98LseVnWr{ z=6j!QA*P%@8Wkbf0JTS)CBtWH+TyYZ;p%m@045YR&S z6%JCSM5PTf$_8Z2{%e62_>SKB!4ip)SR3OR4}s`J@QwZYy1TNcmc}wBI>zXs7HL9{ zFOqZ#xA}?Cw_lnDZTKq=g~lo2IIx3*hU?D$CX(C6;y3u5GovU~EEq&P9#LR*gb8>{ z1D;dHuhu#JPBx#t5~=5{I6%FKOS|IXKYy)$tp?~yj%Xq~HkIJo6)Ms2 zXl0R*$@Hx(we5PgS*;7m-)?)@(5Xn35dmtr$}~=J9abvSD^nglY>t20ISir&*mDOqM2MXCO`)@pKd}iyz{l_F2MXpAT3i?6sxO$?#SN8AFd)oitry*O# zO9o_l)Svh-h38LL0)-Vu10IHKFvShb{wmbqVsmM&EBvMAgR zgq5g_Y4Cl6qt{^Www}@I-pchl=b_hWnck2r-k-wAfj8!Mxjk>c5bU^4sX6`UONRcq zP;=^gv)ykZ!saJI2(Yx=NBPj-Bx|%MYgwWJN4MkY?=9cs&W<-8oHATu{?|);Kz3h$ z>zTA-PZ%6HrdGZ1E1k#L?29dp^)M2Owo?&uw_Rf#NgaET)3)R5B{-d>ci+<#u{`I z7JZL&>Ke0~TWzG$u19N4L?^r<)ML>a78C)8HnWFhUMSZrePf@LceL+U(lJ{V9VUd5?<<9Lnh z2d6QvK(p3v4{9^Ef&DFH+A=JDlyd=-a&z0=u1U}W-i61M7qG-BPfZf(gx+quy>Y+j zxs#0UUl-$J8{Xsh??nGKbs=|uuz2?SE;cw*%X|1e^= zIRmA8fPQ;C9q+5bBv+NSGn}9`pu>nN&%2%?i>REGR=k{nBcXHK<@KT4k4>$L7stoP zCw$Lc<&$Zro+32ke^CSfAtYTDd>RN5d9Qz|xKC^Qflmr?urNB+Q$~D956RhLcc@Bv zFfwBcM4FD1w0aJC_txfBtlNKVI5adgz>g<64S7Qx>=Au%Z{9cmmLIJ49V+@Pu{Y_?Pgeq?U$ZH1Hbx$=^E>qVd zDUgAH`ibYSYIq^KRg;w#5^I7pekfY9On!3Tl%MYxwQ8N2g-W=d)Vo~2caNWH6#&d| z+?spj8&!zV-{1eAJJR7_c^U6^dpo`bkfg>5n7`4*l!LXJQlD3R<1~DNsxNMGJ`G$d zBWLNUhJos9(Y9e@R3f0sF$J$86b%dv@-N+{cn$%>(^-?Hr^67^?|qBier5d&GE=FEY zJMiB0Jurt}?KAly0%R}uWY@ti%_$1SFlaY#16dDjBr*5LZ{-r9e2sh4*3mWx1j*i`8gVoEeVs<>OvlP*oGU_*3;x~k-$}zj`kedvm8u&4JNG?kQcocg6>*) z=Yvi-ysrn>0aIpNhVugi#S|bT$QdB0}*D1mjjOuM40L z$Bv8jcvI8Fe@6#rdqk4mU)wuKgQblg2E}`n&@>cq8odox*JSM@-X%E%jJ!tH^ z3@BLSUz+LN7saoek7ukH{dCn=i$6SIZNDXR$8dhx(9yCU&*EaTJ^MEj`Ll{DP5K+T z5AL-4ISV&;dg+_CNPy1py5w%tA{h7@Cb5jacHd8M`_u8wEDoC zO(iO2NQwrHS1`h}^ZcA-EnB%JILM#bc*uZuJrdD@PU(#eDwx=!X5Yr&87f5vwn>X% z->LkvZ7NrQ?yXCew_g4+LQYnR2AW(ty6`xCya%vl;EGGo|^YT_{U#P+{Z{OZ2pN`Mnf_&s>;@Ta{l=mgP&bo%!%A2Kl zag1c<^A|zk*%$;Iv;w`FGEIV@pdf8iLIvLA;!=YR@LH+9<|_-t01tcwW|8$*pRO~Rc9bRRcW@DX-{P{P+a>eaJh48<+az;ds)xfhI+g*}xU zNB8k&t>HVa%nYgNmJ2!JZ?Iom@heq2E8aVUuQy1N{m>hF%1FxHDU`pNo>i#tLIhUF3U4bto)_Nr9SkFyvX{XnL zbURU&vG-8uE2jZFiBl>GZxnY1UTQB`)0iOq`GO@NHFmRR&^&(Pbb5{|#UaTRI%=Ngq zLFSy9yXQ@fO%lBwNrL#+$la;tz`%5=5K$XR?Rqio0ly~sSG(pj#9bRh4sfjQ{o1>A<2gS15| zlG^+8RQXPZm(SE0e}@81`}w|;rH-|zm?iBOw4mUTMy@f!S_ztjLsxKIeng?lYM%wW z$ucqrQ4@}0d6=U9eF;krKcpi5UpBGGMHI8VGLDmiJ3?j5k_^FI3}b$CZWOWyf=LR? zyP*~pBxTGtb~`qTzC0WyEnyg#s$cQdDFlU(!9LAih{s4)tYYBrkn_T%{;V*ZND`=D zBf%=<2GmOmyxqkkiOZvnB`jHbmeT*-H!z!=qmwcc#;&tAC-uN^@hZe9wwkuihi%7v z9Sk@yhT7PJKVbFJaj0_L3&~PG>)>48x{>{H(fn5ZcKHLIIU305=i6F=XpWxx0h&`# zSVp%_q9Mrb>QB4Y?5c+Vy1sD-&C~T*SDcUB{mQtqx0*1@&9gEU7ez2cjeuem)O|y` z3Eizjn;Z}5yQ_Th)Fe{HV|IMGnS;tNj!;$kyK=mmUGcJOQ7WZp>O)PF&D7@1FxDU& zuf_gYZv0gX^*C!;LfkEmt)G6G#Ofi`O~V%qSaeplG;s=RXD!->6oW3{~o?GI$TW_T;F{+i}Q+ z!@QO^w_=&_24IYY%VBB+_c38a{ly?i5skMGh}RMt6Y}>18h2sDgh^z)F?9tdMP zD~Da;;FQQ`>C^Z>h0})@Hn}2M8*!8*CGx}6633H+DIhSu{=o(NOo!UinHpLbNuwwK zg0@*=LXjn=G0+e>=4Ge4cLosz$dcH!*_Ws^pOComR<`{&NmCvbdwy*#ur75Qm;$S3 z7NCRe{+6x?2VwQhoiTKfosC?tYSk#_q@13T6qnE66QM#1iw1H46c++qXjPL%_8^G{ zUsjS9?zG~YxA{X;lWg3Ax{Br$3nvV5wPJ z8?Eug!_cJuA(i}uQ7#v5XwK51meml03=Yp`{ce$nNzO{+CzZ433hLwi@~#xe)k&*O zmI;flmE%U#WIp#ZhD*T+c~9q?p{#`{4M|KW8U%9wx_T&tNIFP@CS16u)>f600-7=M z?1Zp^0$sc}-G(Mwk!S3yM9dX|7`M-FQiF(YPV-z2`|5BTBD4~8gkE3@a3B^#aWE)a zmER3mJKtHUgcKz%i3#LWqUR`WJRJKYIJT_7226|zsp2N6Bi_T=pis0%HRez!MtcSx zPX8G=6)Er{vC()IA2eVK%5qxO4G~saO--dP0ZBn7nekeHtCZQfmTGbqM-*pvj40MN|+bTp(}*!O{W}#865Q zwaqKC(Q>~i5k$bEY|x=13h#~ROdO-}2(oF6@^ig0X6mbA(~J~NdnfJ9RrjmG5{fPg z56-Iv!$~EGE6m=I2u`MAt(w}5QGd=b_GKawU~F+I^aD`x<&70V5wUkYU3`kzf6&SE z8v}`VSeMq{4VtG0{SEj0g=e6Go76R*U1#3C2vLlUiHl>B5tr^4g{auR#yfbiC$Vsh z-rv9&YpUodikYRwF z_ipBg!smxWeZJ~n#P!z-=#bM87%0X4ZB5Q*7Fzrb`$#wB8LMxbHZSy$r4j?kC|}_M z5oe|RA><*Xpd=u}^OuQ#B|}aaR6(E#2N7o%S;#>k%s?cRy1Q$)qGel){r%mfFAviL zmO=odPfLg(g&H7I`1_;gvr{9XDM83!B0y=;fChg?AW%%iPaBYuj}zCO(H*>EHwhwe zrF1EN{+;(t2ndjXX&%CYMpsjWcY=^uf{+o$EM;0KCof&#T6V4PD>lXCBrco9{wYx1 zK!81vj}lKp4+Re`;%^r8K8r#wjY1m43<_6;5Eg-31TPHvQ%mjLPX4fGmEdFPH-jzu z7#9oPKPrE;@Pi}DnQ~<@40VFDSE(RH5T(Wng3d}+t65}ju!qpXvzXEm^_L*1M1*9v z?5m>JV8J#>dgv^CusB0cf^9$uCInGiVgSy&6GfMCl-ackDV@}b z8|pkTF8TA!KA<+JF^&&@A=EVJcQ$^!Te-m4#Rk8As4LPT*WLuryh1;V?rS zT$L~kH&}isw%__9NN4Tt-eP&-8B5UJES>n5*2-ST3*zT@+5)YrQrmPq27GZ(#jQv# zWGL#CzkvzwZuf#Dzf{*oXE4^D;xOWQXS?SA1ld*zryRDAltx%pDc1}iom3D@qYpSH zrfzu53Vmi>OFoAnBO|l4K3xQ|z*IVm4*BQ?9J42j;K3;}Dn?=_m{zxUcZPtyyjnij zQWkz9*K{p`*70WH2tLoPng4OgGqqkgno0ULZ$Ya7Sg^WFiw)YeDe>gexP~_!93Cb{ z?`^J}FbZ}LkIHLf{EA!fUOpW}2l=avXbCr{{+8bW$~;SS#epB|W4 zSRiOoq%RI2%wd9-t5J;AQzga1n?h$@g6oYu3ZtRY4e{fTqDk?VGo5<=jA4$ZK(1}- zx$=ggps>Th1SkBYav`4;4h;gwBi#+WOBwyi50)urF66M{i0LARNc&2h5Fb14$qN&s zy?-#@6wzo<3(+G@fSD@73IqcO*Rz6CA6z?O|-kS5cwQ3 zqS_96S{d|ZHg1!!AncU8`SdD; zNI>hj>_h&q)^wE@sY8376|u9Tpl|Yq!CB+Ax*K&Nqm0s|SFa+-!e?@pEg>5DM{x6CLplv#!_Kxym-0M2-< zvFUs4t2_3>ekp#HB>dBUnCo>JsosGuW)TTzU3CRuB76Bkp8{E;xvTl?D1+?%Ue?_E z@ix=j&HXNeh;|oh1Yt!HzE^PE-l-A?_n>mE`vbsCCk1p6@KWKwL9hSQ0(1`oICF$k z>**g4+2h~)q*K> zL@W+%XC0Y^8(dwl^OWs?di6Wp{iZYMgcD_NH;Pe2+%spK_|eX(lO=Uy9Ap62xofIW z#zDDkc)95P*RUC;e#&$eG3A5U&ajTx-TJY_{W04;%s2z*ef-PbXy>)F_I;%6a*4>D z$V^ta0<5BR@we&NufF$NuT*pZ8cFDhs`vHVMStR+;xL|~c0iVW3%1`fCMo`E;67F5 zfb6Grth~2*A)^cNnm(fU7D#jb4qRmHafm^rK+%)_+xkf@R3eb{cgbdAR-_M)IeGVH zT;m!a{ZKMJUus{+Ew`(R7eD9&9*vF*l}cV0*y{Fk-eAMrtUZYmPi#iMP#5FoJOaR;SBx2 z)$FF8gdG>M1dF`0{+^^+*K&L-AJ3;r)qO0(_&N30ULeb})h3OmeHh)LHyPi%ob%an|IyAAg4PW8W%*8dK< z1*Ur(e}HHCb*B5O6CM5C@%3uBeHJ~WCq;1m$6gHg^&c{la21d-fPq^Bc=c*h^WBii z1fi%uuU+fYnnwM{i2GhSmiObe{cePi-Oy}jVDhS5%MT}sI3f#r5HtcrtmEdBc>q*U zSF7{o`18c0&oS({PA#8WjW5Renh)jq`-0Xf?p91u95>uBp(FeIj;>$Si?90N=pedJ z0!7Jr41KU6vEdl1{IdDe*YBwM-3M#j+a*y~aysHZEkNV-qguy{vp)R$Bk283Td2p{ zf%acNR<_5z)%rsc-euIwMSnzDc3|(c*|HUp!r=If5W$5l6pxh5(Qp<^G(HOQ>A}P|K1Sa{Zdflw;jQM9q9jcBoE7T`FsIr0mRuv`MkH)tVPSC zi__XZ?ib@6ZyFo(P~v1zpyLO$TajNrOc8W#op4ADxL0D8=4)Snqb@x~U4X+76Lffr z1SZ1k_RB{=ke&-U<4e!W)!WA-WUikb+xJ3Z@**{&|768cQ@;0G4$sGap~9&mTDBcEKoYzam9 zJ^vLlp6P&noLV~%MuP;y8Isqn5qj-L`nBN$wcW$a(_3-O(I6P7c8e=D_cL9tRKN#^ z{Py++Fi%{3SH1lJ%`#enj|`}71$#feUOL|@<#KQOH7o87%h2l}O zhlZRq)Q$jo!P(?*nVyrM=gJc?ZhV}Ss_ts{hbo@qLkT9OUvsH52JH6#Aza6BsKhO6^fzIlk- zqA>zfbi1-)Qm!Uve+(fIqDf0&;`y2(Nwx%CfqQ45`QcJiD2%|>qHQS{1Ug?~1P=9N z@bV2AcrQH03t}kt!qYlNNbm{f;G8#n`nF(pE39eC-hW3BjNp$BF&a|nX(!cQXj6FS z8XyFT&H}h3rPrVL@^-e>J7Dg(hoX6qN%|qYP6hF%_<@cxeXhW#hC?#q7;)r-oz{Xc z1vA@3|6<&n7Q6=B|Z-<-wVT;#&`t5pZh`l-teU3 zU1_#g}v?rM$carHABc#_KoCH)i;CpHF-FJsJq-l&xf2wn}NEu zV;w8tBf7nn3007^)GToozGCC@d5#Wc-wj^w;CP1fw2(2JUYm*f>jlxfzm*M}JUMyu zJZ5$KLB%8R$BW6co9}8f?#1l(crco@uz<0zZ%nV5K4$(x4yij~lnJ0LAtoUyJG zDP(n!+axARKv2N2W=Xykq$B9mWieC8ZcMzUwFHi7bv`^0R0RD=E*|Au9FN{`+aeHi z*r%0h5Ts2(f`f;5%4_ZJ-ZHfTOU5q|59+o!B%dK)UQQ|?(Dd4Z$1E?Aw}SO@s|x6Fmaj>?R-|z zLnQ6%@VHqWwSm|M1OY$t&@#He&2_w_E@ERMGFva&tc{EYP&Z{x3@n@H4e~tHPBl*n zz7QkOQ-$cunhgQb!iPQaV9`IxaRaGqFd651F`R2r*bJvFX@`>;))8SFo-fhAN@UZZ za`Bh1P}tpNJSkxKI?!aH*x=#oEXQu*!jK=w#Kv3O7}Cwl*jh&TDc#ao0kW9{4g;R{MAFQaM6uEK1_wjw7~DhJgc0 zOK_1ktNHTuI7bcqRAa1d?d=&t_JVVzQ^aBv_OZW5R-&;yJ&{$WeC{{H_Tq$c&ClwQ z0V>={WO45_HHbfx)o}#(k-wjkV+aqYT#Ox#6b|cKWS}k1&*lc2guwKQ16ENRQqJC8 zDfzzk%@0F8uhR(lx)4K=yjlX7t^8dWmTF^^s){qix0`fL=lXUgOUtFCjUe%^K>n95(D{u3HRR1$CzOI z>A<1RjmFpuUtGYX)!OsVaZ(=JVQ!Mb|r!rJs z-@&4W9LzvI0^ez)KsWCT{JPf^sa@-tpxG4IWHGy3=f{r6>&|CCqmwE(gDl%-%CXFK zAg2rX)#1(Vm+qGJTh$6Q|Q_Rl8GPI;+El)!^`wuyZoHobNj9mz3==n`V3#aXR4w z6{n+uFhRTBm&VXBmg~f{j$+-xOEKxk(2n=i)2k@GACD4A%hnWgn_iW4lRNQWKY0ud zZSf3G@?Fdpd_pJs#$)xhW+bFO0^oyDfCZc9G?>Oeqb1TIR zuZyMU%l6m3_K@_R+SFN1Wn$QCg52l-?%O!R+H08RK!;1w_XxU(!P-dN_AhtDg?wh* zO>MW!pQ&t^a}7GJKVe*V8q@9A4q-C-1Lb*h{ zb1{~UGOR`vhm(-PtU8rcUn6^gn*;W@vWJE(-xr6eji_{fOUfyqO_AF>txp1%!glR0 zkX{7d;P%rFGRjz^7^-y4uPQNjTG;St;V@=XnbLk!x<$js@&=TNWrNeb*W*P(5oI$kW|$Ps)LS7;}1q@b*75&6i?QOYEPk!PO@T-Ki)GZr^VaB`N?JsK>RADKu{AyuSEo=z??+NyjBP8 zl;2Fk`j1u3*N3#p1KW#dVoUVaBgHRuH?HjtJK}zS6UV9zR->z)0CP_4G73Y<#iE*4 zXX^wsS}#RtTGwA{1^)f-sZ}h&of_GaU~^xv*6VLxn>>lPVZgX!$j2?(ztr-E0U$wb zjrQ?2s@bah`0DEF`kcQRbPe`R&~o1){z%|JUufgwH*et9IUWQymdI!idzb#op_J0J zZ5J)J2$_j-+CnZ@dqEk&S8t}!!VIc-sC{63NgO{B#fU;AHfv0_QV91C4-^xt_?NBv z4cM(5g%N*xdV0YF6stbsB3p9JKx?a*?jJR_-)84@hh&tA(_}QTbb2Yr zE|PlJ^|qFgN)GjjrKJ2)sZ!2LfY+`$GHs`sd%Sm>H2FBhm-!)%)&(IERpXjbCU@jz zvreO<7G^j3A4RKDO_xz5(DVazfWWl_f>nQnsd#N-92~;;I0L1)j z(l~?_ZW;v&X*5z`&>;jq;B=Og#3cxeMvMN#9heq3LlQ@yqfN0NJ4qe3F!rf4;ZH@U zotC$Yu{-k%zq0sFPeTHKh$*LVkOj`#9zc_hs^b{^19#!rd}O)+wnQwBQ23!~tFS1e zq%EyQXL++(ldxT#tD)!63p<|95>);T9tWJR;v`zR!dYk8av5Q6y09dgrC&vVteDfE zwhl?EqBf2=+vd7bE7$`23#$hCSl^r>nIFYspmN2#O(Zt}yVo(fUV!ppxi~bA=O;Ev z3C*wD)xCvOljySs`!uX-Ni03cLd4B4`3|XEb(9{snEs<-jg|ZjxNFw-FF4rH!HLjc z(c}t-u%dh)sZ}UFFx>3E!d-^PTYs8K#Er5N6ORxb(ImFrdJZ;z16MDBzc#Fx0I~gm zvMUDwn*ionLj1agh_HXPFT3FSn4_pTqYY)H@s?n%Ful_>XG|qE~ z$P-h>(q9w~p%2_9gdkA%Pm4nvCM5+np2!eL; zjF+{Dlq!oBD$h0SzoRM3?Se-P(F0HvmA;}A1W+!@_rWWMq!8;lB`7cZ=m$%u#f+7I}(nN=0z_*mJS~bo#94*=<7(D7k7WS(*%+UsN1;eLD zW^6&hyRTHx1-7@9lvyqj#u&9r98@Msn37Kzqe59uNC(FvdQKK-P>j%`?Xx#UOgJY< zB&x@LKR`N#I0Q1TSknAcM3T>qi6LMif<%p(q&s%XCykyd7zAdiJ9_(MC0t_QhD|XH z8X&fkWo8tP{)zkDHUUD4dQvjvCr11FnTdRpL;n5}8;dh0La!0CKNYVy$Jg)7rN{}G zM&u9j$^KJpiqWAgc@x!ixjm?B1+)IbT??~ijB zG);3ptU^ZIG)i*(4ntyo)&JQE(0|l9%oG-qb6GoW?58=8n5sMORUyj=6`rw7yZBr; zV&PaLu7Qnifj-wx=6e!oKN3z0a@MlY=te<622Z}l|)z55ex+4W@p*8#`!Zrthx+ms6WmA+{Z0~ zxoABO#cO$vqyuYp;p;@Uz!U|lPW|~KbKY`d_#M-Tp5)MF@O+WqJsQ$FnBSU}wWHp~ zu-s;BLT!}Q2Sp79f!lg)z;)qJP0K*b)H=F=gJQ-mT!W^CrQ^T=WkMoW3Qpy_Ely8H z@XraSN`lGKaIr$p+9C7>&;n)_1xX^1636{*K*vgFaVP_l1fNsX;1;^Ad#a+G^{atJvdyftxd(MoNP!Kg)k<7-O+Zs z5Q>S@(O|DU5Tb`ti_b_I_#iYaT#V+w4!eEV)wQ^Uj$B#yu z#6~V&t|u7&vcSj5qLG!{lPWHTSRhRbm$qEbI=rnjE1|D-Gx-yH^%gG!U3WCYE<;K* zD$?gG^;%5hdIq&SB+{j5p^;I6O+)D1rcM@QNTQTOppE00WrSKbn+9~#Jes$NLiRG| z<+|I&#_KE9hh%Ep%~hOVWL=#oI{OqqGLgogGNETe1%W+nlpSU(4fiGaN{!}V0=MOi z3^ubbBTaR%U#58}ml%!}R4vRjfqa~3BA*vbyOt}7^Oj>zU!TbfF!CpkRI^Wh?|ZGU zOJzcnc}&kI!Hq(2=gQq& z9RDpT9_(Q44P3#cyrPDhIdOU{{{5SzKsm-ANCCs(_GR%{#pe>y2jJEt=qiex7M@0*D;_hVu88lfZ?~MLW;SsnG0rCOM;~3;f>*Vg8Iq(PS{P zY8?pL+~?zE-Q|$LRNBi_yZSg-F%H%}pxMe#K4W~~e$orpkbkIQp7OrZa30gKuqpT_ z_X+c(5m_0&7;4CDXhdB!Tk$e6Z3XWD0xp?HgY<6<6rNsl_6GlDyG)Pb84FT`m;-lZ z0;+iPkjz9ZNzy^HY;f3;RL@uJTD3agOaMz*{CDgpac!5|Agmo|Fet) zNks%hEMuOa8(l6JUv@`mWYa2(L<2BG%PPt?yH<^^uCB6N`}*hG$Po`9C{c1oMkE zw5cm$RWU`#htvJ1vcv_Ii7)eEcRv0ROTe1g1P~9c_P5B}9I7oQOWUSkH?N@;LswEu z3n#Q_nmOc{uT_sT5a?oRgZ!~wKJEJreJU;wkq}4<&`&_nFjkTW8575%UBhWsCV+bn zCkky=%{96cGZ8#gj5P>ae-sSu8cU>M*dS~>S5p6_YkhCaYWZ7Vkw893Dd0|YDHxV+ zY#fUyGjWG5moJh;OtY~_tcg(p)FoRE!L{0BQg{#j_(PXD>^|@pC*y%OFtRsa%rby6 zge}%+R8xtzzVSfBG;5f&D)1oz3>`BBZnlBG_Ly1ozH3r<7>J5Jm_9sL{P-&k@R?AR zEaB2eXXiVe7rsnvr4#gKX29J(hK-X~p&?;LHIZ=$wCEw(Rl+~Bu%L==6%Z3(Q`u|# znSLKjlBho`)`P}v)8mW$A#>q;A2plV)J}+GXXEmMm3~(BjidH#k8Okj<_zT+VOQD? zcNX*~cg8Rwd;Q6m4JON@?@~})dY%MpwgPb*_nRS|uFH`^`i^X$tH&*evX8=!&ZI}O zVyjKiSwyRGt`L(vb1w6==<<3imrJ5t1~^;pGp*gv>g*5Vzdo2>CyxLePCbNQc7JAB zDV(V;ni(=r(b&as*70f6gJ8#BSooWltv4nzOCgu-dRa!}C4mQ%-y5z5Q%`e`V<~Q| ztdu4(l!8R16_Rz$zftm+F1#_p1bF?t!xO*P#~p{?FqqtTNLs`+nj000r5WyS@8oui zh@KSNJW&}P#ZdXN#(TBGnnHJWQeMw?yX*Eo@!5&U$fgL`MN`!UpEntw(XH)^QAw@R zc}25Co7yO$@5oxj6-d#UrGuxS^f|?0zaqO5xf-Z|MaG@&P}dLZ6)sD^8nS{_VRMP(`M#RvyG7Vl%5Feb&bQw*f&IIoCD3-XHP z=Sd(u9!0Pe>0xBoX{dz9UP(Z^pki;jVCX-tr?u`pYyZjgsH1^nKiM!5k^VZ68NQV5 zsTlZe@W3BTj(t5N2s5h{s2(Jbam^1MBd$8wwp7#9u3G%M1d?HHrifNjEB(R#?Kf|k z)};~A(SkG5q^I=tK27MrrN)EfH-m<# zyufChaWu`UuTdz4Av<+-1$;#0Uz}g^^no0R)oN71(Fp~E-e)Ie4l{#kbKfb(Fw2*O zp@$C13%T(uLk&BAi*1G2n}O$gC_;ZiTBA%Zf++v^iAB18Ig>O0D{VuornZs>>2tIN!CTTx2B(jwaurr@ z#viq{Gk?<8DfO;#(=qCI7bk45I zm=xR$*Ks+;Y5mRl?Y+EcWi!SkRg(KqJruHmjZHMrdhxXbFk$ScX4yln$%pfNgZI>I z{(&g+?K@P|d3#>QoEfp0ncTVY44LdPPWI6G%jrl)nP1RFjt3?6ID!NNX)(DqTDWN`gSe%Sr!})Z+al`xgyqQd(7^8Tk3KayOV(dHgN_L2{2Vl_= zk}7?rFa^LAlo}lc_CT?n8Pl=yyC2^wN^Y0XQ!8~uriw-Gmu=m;Tt=l|x7)V{wIr1m zv!G>E%S&h^f&s?X)5}ZRI+a`=tc_C(mzc?HUcqy#m?`)DvzAm*Xnm&9TP)s)vtq87 z91X)dsY6vI@W#i&_JmOiRQGmr>5Qf+JW)(Mb9TJEUF>Q{91(QZv^p#$p<x0*iTqAN%UI5K&R{vQEvY_Bfrh~*#eg>Kjtw$)m@fyC2S{N9&i>l0GE@}C*Ez3aP6Tdzn1tEx-^p2u|9F+W1-Whd21SD5|A{1K&etz<-NDUV@ zigN8;r`za+Cf`wWy6`Z0V)l9SnePL)9vd3061y?!N#)YTecB&i?V3y;@xk|fRPH#s zOuL#<^oyC9F>JGHHqI3o$?wC)@*|NbmCNXxzTPKq&bpFF@>GY8pfR3cVGfk-v6^uw zMBvsQ<*%l4CncS?62EvqTBepx+^(>S9;ICir%N(mFW_lQWSe^aw6cva_!h#%yqt5B z{r2#8=B2x*X=y9cp7C&Pi#LMKt}Trz=Su$ld5`Gw%(?ool1*`Ms{0+%Jllm+I?i~g zQQ&DBUg%FXpW@z${yYES>zRrUTUjq=bL1Y1kUKol^X-y{fd$S`X|~hu$m{WI!{Z8* zUl`k)P4`KF_v6+F!5n^nYjDl$RNB*h21nPk5Y_eBl(&CaQV!3)bKVXa(K|#8&zi%v z*H+Kr$IF20uEr08N#Fo=-UXj3U_eZ* zQfI`^_Z3;k?M2k82^XHooq4$P*mm40uqhVUU&#)0K8!zY-9W?&>i zD1HH4uwiMPxfq+5PJ~^DoC@!&zgzC#+tYc??G>JH!_rr=cy8dg_CI{x1e4(4+{e1R zLi#^>`AXMwljzdeysK2D*sdzdTvZ)a#IVFuA|~<%x;88kzFY&lV}G9{{PFp@Syt5{ zkm^h2_*kJo{Ms7yQ<|98m!E7YdW^9>k{)Sk!+{KN`cg+Y2ZLpyHe8G)nVVHJOnxr3 zhbv2?4>!0j=jy+|nseth0Is=Rb^4v#Jny*QuQ!OrTD1n`&3|)1y)XDwt^^qFBvTEX z5K0wp0gLI23uEgv4!uN^`}JcN+f6lo0>5=UXt*}7K=zNrX-<g4vNwHo#1zIS!AAq%_b8&; z8^(4eS?k-ohc5$TGut{M{tth3C1!#Mn}~y*t0$vZ_*kZ2%-^rekXv8>WpN*<*6g-L zee?x};oenA@=_IOOd8c!FDVz1MH4!*lJBn_EW zPOJqu&dY|p_iRARlfiE4A7Cl3@G|7)F^m^SR)uT?1y@T-L9+eaE)V#gF8W@w$>Mku z#iOtj4taKeF_!@wJ!P~ZdCANrJsytPX!FbT+>$s#K$oY`P1)3TBJAg+n9qhWl9eQ1 z!Yre=OG2Ac(+HkEpVm$l09XRH0^k1Ub%zZXl-sGj8o$=`L&Cc_e7DD&$7w3TFz3)^ zjiIlyggf%~e(*0B?L=(waPBM0lFpX}Ogo`j)d!74IsJhzsr6rLVVGXms5Ykx!cIjd zfX3;bh3D{1;}U6Xlo{&JjD$u@0-v`TE_}Ik84o-1d{e|f3l6kz-3Z!s+_U=LWI226 z&q&U0WEJ9q990|DHBGU?l{HPp!@flmnzM;!h-uI-_Wh|3bH6Ou?>L^Y-%SvAy9y$D zjemL(3fr6sE6<1~o?a6QS+j2sE7J3zpL_Y|eb{JU;N$%C+iyLWe`?w1x4GjyTFs%k zK5s(ahc`CS+csjyy@~4%AcE)ypcJVnV>&%m?`ou%b2kWD z#7UlO{$b7Ev`RN(gkLHD-hGFy<+gTT6Y7pN{cL{P^brM4=S394Om9CEjQ`>I+P?XJ zJe^~7+-V>M}oXe;4Iugs5PFegb{#o%ZSZ?MSWy5N@9y}gT(5|dql{iTb#XNSS z6ALy`Ca<}cCUc`ibniNK{_3>m$|BwM({^0b#8$t)8$>RaVqf&yxN$VvqnKDT-LYhM zT}Qqx5N%!Oo!wfQ^4WHWijI)t`O^^rETSp!Sjo}uDIDXv`;`v#!HJw|kl6R;8XVG= zksU$<-}(Ek`k#AoWZ|)7Ek`U%dmS-+SIWFEOj_9389D#%!%@M`!LFRNJdsvR6JTe` z2$oNS5bC^-7&_h`H&%X0f}aI?Jwt{=rsTo>LDoh$Ytd?Ev?X>9E~H_!kotulDtt1$ab-@~n?)P9w+Bq&YbhqMMKP$@9nLDZ&~}EUk+c(&L5wtBItSr~7+1=2YO*Yvy3SNy&7q5j!S6))_2GbsB1E8%WARwBVDMwz1<3P-YX%`ux}L z$aAs8LUT;_&Cnf%>vC`M>Jv1)N=rduii{f1<~3jZ*wNNJ!LVO4p8H)-9SOWJSmOiI zLx;FiE9S(3g9N(zt94wh*0me!1}-udtizoJ(t?6c74RS$(l$@RDxE~j0vm?XEyN-BHU_Rm-00uCEZR=U(EBmYU+K}wr%rPr(xO67HZ z1<6vfDRlerslPJCg$9kK8Bt`&HcU}(86?ZRmI8CNF@&WOU}9Tt@xJEwsQBoX`ItbW zN^q2#tpUoxXLbg=WeBwdr1K=5k#J7+Ib$*XqY=U%vd5DdQ;l6&_9tJ_2*fE{@OcZ2VygdN*V1bVQ8bGT z&otgHKW(W=dgr!sb_g_zDJjz`Iu7MHJtim%i1;8Lk#WsF5*OFT1(DI%6eq@-+D0J; zmChh=NhUeiZ~`X4<+?YuDZm@&%J-9I&0^fO<5+m6|0GB}*N124b1ygBbHCSh{+p8V z_~$HXqlrNXMJ?+HjY73_pj474CYy{-l91l6?#b0#V(CSBY`v_kSj3G8gQ z1O1!tN@PG=ljH`0SyS6*3rtI<)?Xw<6LN^g@lV7*c{pH_ILK<@c?+!|0|CisPG)2S z`{?M62-My-N>&orqCmK=r=dbKl;KP@dS|~tB1vHRrBraCF<~k~D~TzA)v9r zp0l$W-iqY@ohU3Mator^u@6|mF81`Q)GWQTx>c797W=ymoFW3`Ut3{ zar>hR)(C`9NnD2x#G&D2RCE#kTC0@i)rm2564e45WJA{Qi&!}XYm=6VwFt#dlerz- z7i8kW+G4nD%%G2SCCWQ?Mi^$_@J_#4Kz}=@DTeO)w^~$RVZ8G0vHuhIJqoq^ZVTD0 z=>B2G(rBDANoYdZ-wmVpWejjI#rwQE)r_}M|Bv|Yc}lrw>}7{P4BdT_pS52eh3IKj zVl>#&=87~KuJd{MN%=U2gkIH~wV{9a2mkCso$sBk;elT1&*Yw4sz<(SUVYyIueUqB zZH42`Rp)RucEk!2n&Sfw7v;*Lu4nVQ<3d}Z5gMTnVEBmZ5B|fKw-ssO(Pmjej?AJV z_h>bAQ}B3b0cBFI(I9HBj2cr=^GBTsL?c%)tfC(6Xi*MJ0*!PiB%3N#haj+dm_@G=@@;B<%A>Nb{z6DL@&uJ}1Pru=Z8`&R>n3+=--d?_w zvQ6Iud$%<{(@PF-a_))-_5|ln$Ofx^e(t>W6AwsMLZ0se+2} zkr@!IT4!ux;~OAXaf^#s9~(4KOE$9rybbaB@df>9LOBy_N|Fsu*_#N-<;CW?X2ue*SRHe{t!qgC6FQ!fh+1F;yD$u3$$ z%dRTGucs#G`ED0z%=GNc}SsNx!;i1Up-nU#F2|hi^Iy7{7B0g}`S5 z#IMTiGX^J6b3#Qm2$(PS^GcigW}{J%4ZBz8SNyk-h(T1WThu37uzwi^o1jxyz7Idm zyds(^PWT92qd%^?3oZBi!xIfg8orWyFnXqBE~j(&F}qsM9NU)p$DccNMGQZ)RcDP& z&#Ysc+IBtd2=%)La4zBoBxsF$HOK16>c(y@a#Nd?AIi;b6Wg3ik>n*`euY+g9( zK8*7m7p+ep;8OIngbB`(S(K9Pw@po|M0ogG*G z?myW;_+Ck29kVm}cy|nRbz+l3d1pUq7)a@``*kU>{l5Kl7wyVSxteWx)l1M+rfA{Y zVLms2nsgi<@cwh9X+{WM?TMd)?C57eS9(E-d`8=4>$3pEzvq?%o$|hDMF5T7!2>-( zYp2&Un*o(O^LKbd7d()O9aL!$dnp85XmHyQ4*yZD9Z^FosR8>m7F9Er+Rx$4MtYlf zsI~GabSiIUm1uS-D4?g!G{zDck_k*~!_*hmdz_I6A!YWZ_R~a5aEOt zNvkj>l-y|YEuPW&CJn>8cR|OEpS$EqCauaw7s!gS3NjmpioLRn1d1ThXW5Lj{7_ zz+Lg=W=p;ZLIKb~B5d?JJ{OngUgAceCWyEJQP2Jt6Z?RDV2j;-wn?W^79UGig6#u^z(EE;r)fUDupDvqqKXtie0Fvrm^PQtGQJ$B$O_O zxfHsBT`Gbtf6y1T3kCdGN-6F8-4Mcnx0|l_P!H$9MCo1ml+DW4mM+Pb?(ODV4p@^8 zTgw(kd0ey>+6HlW{fQLIeX|m(8v2?QY*54LbQNbZvn{e}%Ox7i^8u_v-v7x+#4=04 z>!;kTq?V-jG&U&_y7A{|n^+Ywn^v9xfyCe|k!A>fC`7Fm!KM;UB!a+&5>49uSR+0j z0VE{(jl|$I)OdtS^M?_2fDIF`wL(>kgyKsR*1LPjBp$aE3u7YN1j_G&pZ3F?Y_vfoLgD^Cyys zUMAyYGB11Ry&)G%GmnXn1N(A3oWLkVGzLR`9diONO*U{sq&8ZVmRMLxJhS=BEtz+j zg0Y$->O-w}#oq!LS>W7H+dxqb5iuWhI)N0?Ye3YFs@GCuDGRX9cpNuCs?5mrnF@+au@q-OOWvKH!jd)`a+FQe*E@0&Y&mmp%l~1 zmP&U$QL@5qytMU0pq7)rHmzZtt5NUD)57GXdiCpx9shA{ny8U&j~t?1D5A^ckG{4b z)*?0nE_RDf~nnzhfAgg<5UZ4LDK?xfE><_mnd_seU|B{TxPu zljEvdqamxkkk)N;+}2!P5IYuWI;1g3&#^l?g4iI)#_ShfQ%2KUVHVB~sJnwV&4yNQf6esPi-qYxtr@rUt#-NjH zTSnU;+Fw?OTV5pAY)Y7?+HY$CziYNiX0-l*(74>$|urxR}+{(lJkM5no+;3wA3{ zu=yoO4ddGrK*EZMJ(;cb`8vWa{rbq-9|1}0Mi|wT0a1XxyIGlf0o9QT3Q){i% zmT4#-4Qd!)13OkT1AL#L`H1y4t}csX%IGtyH&qLMjjR{bAn`;otZjm9U(w67)a?D3 zJ7^@RCBy(oG^wqAebf!8wUdWp+J4vzlT=snF?LCqk9+pPzS9kSO$m5Fkwy`!d zC%G`aEyg}Hj=?S(AbkH#@w|ZjbUo1IKRGkq4Vv-4xvSQ1O_-Es?zoWiwljLH2D|zb zaLPt@{v8;+{svG~63lSRo$~8;@m40B=y)lmewJTcU5e@xh#>dFS2w|si~|(85T4Zj zJ2G!p)`A%v_T3qtY@ChkW4dg+MPt(N#StX0bS1u2bfxvq+sxI1GZtmXVdr#L{L5SK z3;WM_eO&0Os*eOopnx!g0E`n>HH|B`$5ULvF3r9c^SbGwea*mo(&a+*+j5!vutWM_ zKnM(#a~k6W??=wnUfyXTj_rK}JP1;1WW@8jAHMzdOz%&H`@+VGVxPE17Yl+^5f|V) z@1Wn4;AkStJ87NYmVeiAsK+iLM^@rfz;4dwOCPzA`+kMK%MG>y*xa5jJX!Vq2LJ22 zrDb^9_6#^?h6PcuGE_v5(yx*#Jzv>}bcyeKQZYvsN{n+k{nMr^s%%oyiI=TO>WR9b zMQY8jp{Yh)V}pvIZ!0|ijm=y~`UASrAfs6G?Kdwl8L@@`sE(?F0$08*M$*pfqH&M~&uxduBZ8u1rJwf)VRXj1~nW_d;*WLdgpJUrbgwZZLC1 zkS(<2Y2=Qqp|r&<4*613QfTm8{Z>*b4hoZKrm6?5XTZpj_^+50o1X7A+1JactYutb z9(mXPU`ei(Ro@uW{5p@dUts>isk7fj*Ok7%``;DbleV-;WY!X))H`sf`Oe43(4nl+ z?LFQ_`#|eO&p(Khts97)e;;Gn&XsJgPSkn)?x+d@H6d2DoozV~0}H6xSUWUOZE3+c zlX=4Nx!c>3uJbbPKk4-%Dt_7I_DbrH9zf##_rzG-X#Zrpl>E2?eD5eEqS3P)yF<3M zC*$lzU*386sZEsK@6r2Y@Kxwu#RWxz^hW}X7Dm;7@1wTte7vR~2DPL~6^VZPhKsB3 z1i4^2ZFaU;p^&+#oLQ=ETXnNhSSm(#XKgBfj7Kpv6OYTqNZ%3oVwgzdy>mrm{!AH^o3dlT9-*xcs#eRxsAU7b9Rtp$e z7fNa-SV&C<3>h!)9)9|8TtN>EFm!+|yK7*PdETRlocu1kteQmroRv|{5!kv3UV=XM zcZ8Nzxg5>^Ut)T7!J;Q$=2Y`J5N<*dz|NJe)MJ=)ZAbjToNk$ z6OxfnQ~PG{GkRrRi^Xat;#SEm?&HkhDLnF^<;3KKQ0K^-@A%M$z0ptF#COut=5l5< z9B1Cw=Bx7*yUx4dTE2AF^BKSTxo&?t9GLwe8_%Y5E+ASs6#q$`yS>y%9S&iOBf7Ik#81^IWnCHT1_MLMAvn@NMx3>LdmQ9Ei!dg<{)K5`3(C~B z%8eM#PP9WlVp`OHaE8)YC1^j0C2x*aS8s^{3XJ!^LejYs$gG?N+?mSDO@APgM`5hj z2bE`}dszh6&sYHo3I`jXwo(%moCAWf0&FQjo`sS5bmb|el8E~VD)H@vl(=FAJm2+u zU=l!)Q0b5EbYk^Eg9L?R6L-!MkwXHsK~zi252uyrL4WX5VdSC*nUw~2pel)MVC_k* zp(+zwOatQaxCJxXeR2+xmf7hWP(ra+w5My*0B>+`Kg)<26;q+eHxJy5nRH$s z9Q-ddM8PBY^4jv8p2*4y<=Q;&n#r#?gOpWUvuLfzH44B+DHUdWtAAs?Wk|qK6T&<- zF#`>OW*n2|qa|n?Bs6jHxRE5E_$Ii#cnafeh30EzHYo}uG7hUykt9k9``r+-pP_aB z?^Q8#vB03o_+0?igl%~-%-HB+(eQ3|3GcQ9rO7C$ARME-BSnfGTGf|UD--P-hNNr= z8b^XWgejhFjJiCd=)(=kqRWT-Hs-1JCGk~vOWTf&&z8?`A6%Lr=G3j|%U9bi$7$$I z<4v?OWEDhE_|aEDPc2+y%)}&66GEfHiz`9_44YZ%j_rH)3EuQtoR^?;Y_IT>zW;lc zVUcPE&=n4z$dh#Y#B9o9Y^LG#=2QSr#MXdonfpGxvo@IVJ!cnCnqIZM{sY4(#tWI$ zR9+NECmB5kz&Qz4{3%eM<1=zvo(n7SBtiGzd-0# z*8s#aplhRTwZQ|@98vq9rH|Ke9YQe3yXocA?Wg3K4LbbO`uqJaztu8vY**7^7x@3 zR_ZhdER@7aEYbJ}$Qn8z(r}psbg4yj9~(?kemiAJ;?YI><+4Dt6M8I~Xy_szeynp$ zQ#oBNDzJDMUDNt4;BlLOHm*|t^Rt?b{a1FHl#CckD84st?Z+$&0>TOud8@5xT`;aF zAH5{3s)|Ua8_Y#tOK^TUNKGO%bT$bhC^R8AyLDo+x@CKZq-kbMmZvXqA6vljLuOhi zd5zt+PRFNv=3m!L5fplGB=R;dIZ+~i0ijN2?bl9NA)eUyf<+G6yu?g6`U*9dgKGy^ z`X4;0SO+*T3}IGTwDB0Rv6yG?L6<+aUV_4@sdj zW3ndLsz1ANXGvJcHK22G-A81j4Vj6`#E7oIgpI75h|^Xoq`|ldP+RRA-SkLdwah8F zYEVj)5aQ!7gkw!K*Ki*&@hcrkb8mtqH1Ruj#YiKWRCA3eq;sFp31Vdw4B%lT$&DJZ zzA+b&N82DcLzJkw7(kGLAh9~`X!Q!{6!&S-_Uc6u=<-pf>Dee`%v7v%TB~8U_Zh;a zHkETzS#_kc=8JWtwwL1EgNm0aG$S zZwka_dC8{HFF~&T-trU}*%B%A*z^aSRmWv!Zt846Z1!-6ooF>NLMS=pSVlBSDifmF z?V{#>=zRk1k}v);42cUEQK9{SE*MIfU@e(hn9zKv?-RR)z5;6Dt!zfISl0 z>KI>keh8C8+J+&&atJc0OK|X-(K`amsMU&6j#T}^vN%A$pd^e|3iFx=N)*b{|6QTf zQJ5&2d`1nQQ!;m5bS0>TA~dl>?9Ej3ntS=!uiaU7K<&?wrh{(8%O*C3=Vi&s?(ALo zGH0n;{V*09rDXF+Bz_k+dYBrqGUiX58I$}m@)C5m(3o7Q{6MP`o`^G+WhO+X>y`)D zS}Y6~Qs^Kl$hNk?DM%I4KqLU2h@2|ypK65m{$gDz)*Y$aj4t9j6mCniP*-^=Hxg(O ziFLdA@|E(3r)~%VKz@g=t?q8<2tfWCw(56!VI4ON9Y?S3E{F@ih*R`!*PG9&r|qJF z-*L^q!Jg#FVnNGpnQ0%Q`6 z`3>T^8R>1F4H9waqfb{c^OzRanK02(vSLMhi(V+k?lR!$FC~3`h)N{GpeTFkdgKGn z0Vhs~mXTY-`)!A`bZfabeb`(SsW^i$SOZ92f0K>o%vLN_8>$F^XT!{IcMyw<1lX3Ou8>{wt{d9a#OVB@bQ^?S z!ph@FMPw_v{(~0s2E=V%U;31y&|Rnw_j1|-Mygk9qheHCTUPUZxIc}*VIzp`iqK#} zT!R{tmyc`>g`*i*KJo8x9}*tQC@VvwP_{h|Slv!BlB`{b_7p;7$n^y&*w8zRBkMU_ zK(RrSs=pKDBg+pXV|H)xov{k&5;HWIoJdAyC?-;Tzo*PiBa>x9YoW0YlAJVp(!hDq~W~+ z565Arh)fZVDMNXSu6g*{HBM-L;)SPlJ=}Qa1p`G|K8J45@NTLoYU^vc@sGLNsH7ms zK&oaHL%cs;HnE1rX6lr2I%$djvN@E&!S$MC1U;FrHW-Lrn0{>)#>qn~IE;xuVdWM- zGd}v{Avq`@9hVnVlD+{S9b-DE>a5j-XO|(xa!>A`o|?eaAl$$hUECTIGMH=zfMMI# zhaPe63+s7JFC^I5;A8FRIAS$*!Ry)%mSLQpo*Iu3{V)R?XhpL)Pg>Mw7ZxVwOXM4t zlS=6rw5p4>&cQYc^O}=<2$U$flrD#v&1gx4#^VchXrny*xoToMO!3&~PD4D_*W+mv zELFAN0PzY9az%Ce)H70cN3>BDZ;ls|*f~IKKt?^+{>=!nG~Li~mSea_4{doAnq->5 zlfdmLW6x*N6{hs1?`!f%eZSN>#t84jupAhDLXRVy2eh8oj^le($2v~(mN2>mo7idf91?6{+i9D|; z)33b<&QzJ+KT~|zc`6CL)ZNmSkEOR^%TWtjl1x~epZ2RHJpW?kDD0AJX`OLzuMXif^{1V%5Em@&^RA^gk za+a3mI_ykyw;D`E_*rpKK7uYy{N%OWan6zCUV8iMJYeu>r#r5ePQCrPlD(niX4C7S zMc?;9S$26R`<(g1QLFk)VoY0WIw0FI*PlcF4QBfx((idOLU_E8SNXPOLCIq5H!&Z> zrI}D5d=)mN;ugarzY|Fu->uUy&E@9J0g3AeitzDnykDreurI_!+B7Ab`FMfCly#iI z<6YM$G<;AnCF4QyLC+*KDvRmeR@=cj4T)$u9P_Vx{!P~j$Hekj%G|!ApSi>4xVa-S zy;C%Qun?DUa#oXlBK~02RA#&F|I}`Mqbp1T>*oOSB2C*V&V)Aw(4KD@LTaa!NDMsx zQj{02GU6pvFn&Li?-)#g&a{Ms34V+P~R;`ueKIS81 z_PibiuY0}r_j;WsWy=z&sCryp!Pz30wQn!ehAwknCv$osTRX0_WyAq6RF}^SOuYy5 zt}If8*@0M(aW!Yv-)bry=7u!ULJ(+aU|@RFLS($%{slDM}mzMZf;Yin5Y`7@8R?(_`}@lYr3 ziTH#_P;iS;yB>^OzYl&G@sh?G^1SkVJ@wl3*y;8?yH;ig!wHC=H{QW_pshS7Ac~2g zE51MuMKFLbVEW#^_4hiiLp6RZob`IKmh1iFBN%Unq*bjw*5x&0RAb!&-+XcUftug4 zr;Q8E1p2f?6R${YL#Pa(5Y{chqK=9z1lhF6NlVk{q^UChj66t(VdcV~N|!R_kUB7= z@A7bS`>~tD5``$lq8+QdU{`wRH7$v2@EgyNH(7IQ>+P_#isz%91*2=E{HxH-&CKj_ zuG5-FU13>Syz{{@n_BTBb_hUB3L&o5<(nu?(Efxj<1(Om`B@-L<(^e~S&jK;BAOFR z{dZMQ^Kx!NQ);EX_p9|})b1xWZ=W*`1L2+&Qd8=Buz1>vQ(K*7 zGevi!@&s%?%Z8pkj;2~Q)%6RdrfW{?op!wg(w-~+daiHIBlRoOziyVmqVO-$*%E2+ z80zG%bP~aAQIs|Zqn|H?CpiJs(H6oZ;Zn)m><+J|if>1CZFdijB+9ubl(MM;v^^dJ zC~wcp1TS^sUyVkXxQvz$W-v5YPC}D~$2v~(#CvbDZ%Pg_d_Lb9wM%8!yKmHXpIEPtS{K;kwcbyT8cFgm~zxwyvnKG44 zO3l|Jr(GaC-uIBy-)ZZCF}D|hOFwCNjWunv-SWW2XwV5tAw0kw#Nxw;0xGITl!%x2*yYIzmO(#r;Dr{HgGJ^UG$tB`+g?j_jX;zyP0}EP3QO?YzPm@ zV9H%)dmK>-+^ys}PjU($Rv8KfEBhXz)jn_6y(~u3+&wfYo4mNXR;WQiF4&OKtvBcf zhHAOR63Mqz<=9vf4C7~e#u#p~6+jSaHPOKhy0#$0vM%Z(R=kePMWPdPYS23N5&b?7 z#0W!=W&esfxeKRT18aiJ;{WI}&L3i}E`MN-04gAD{y_Mh_qQjR|MPZ4v~_hh)RR&n z@Tc6Z;c0shq}nyEteW8`Wq|haECJ(-*TV(6*OqAFis|&I?WVs`3|X_AUG`nd#E}-2 z53Q>hX0Y_Rm`DC~J2#kyqzN#+*}fYQ{^(JOnICF^t^)%?u?N1(BM{fz=a=?};rhd0 zL6@k_&oi2Ujy8q=g?IrWewfu5)VcG^HiVCZ;uy+DKACBy=9C)mrumzewDf@PXVfu7 z{1#4(F?4GbWxCYAY%2H(-hLzMpS`dy&ATH=BpbVkZH+v~;*j_6{gZz%Rh=KtFO?{n45 zC)AM4m|}e7D)6$4{5PE^4BWwmQ}+DSj3(fC&APAAmD;)0Y@hApi=IQb zE|L`^AX`)=lSVe%23|GLXt%2!sL7vDXElC8^553_wH~H-A>(j)nW3A0f+e;`$>2YP zbFea$Vqgg9@qUam-E}H4Zt6ek^VJuZ`@1{2B6bKq8ZY9X{5iQ~@0r_N&0-y;nY)QU zitI7W2Pwm}19$8eH~MudQ^+WW<~gw|2e)68jRvN+p-QL}(ghN+zp&jm`saFE5SNb$ ze5+V!od&deZESd+f7iQP-nmrw6_Gztk29SwB^52F)Pz!e&7|wB_PYLn2N%8xo~H{( z=YOf-w6iLBNEN>05E7x7 z4Syd3Wx*D-BDI)`5SQm!CFzwk!p0ppA-8L8Yd(42NN0(Q_q>01+wVL5G(z{H-fW7$ z;20pw^pe~vJI z!}m9R^?_NrC~0*gn-VB>tvc;`Ao}I9yFqg&TviJ&WO+0%+5SfT_x&zM-(!u__n%&w z>jA2N;K<$(R%b0)e)UjQ-)iJRM*B9U3{>%Tm$ ze5`H$W-dSx-OtN}gGJKO+jYS!3Pa)VQ%%phA%FpJ$V~w8;ZAiKWxgjEy7XV#6z|{m z1J+WNe=EAll!)6tF+}z>c>h}!a#)A2AKQ9!Xro3O3GMru*ZIT50xI?CD0pHT4l{-U z(Jf-vaYqy1oQ}WE95Gn>hPYHLzWRISWB}yM!HCynRR66HYML1zA zXxAPPHu$6}mB=gUAg`TD7KBK!y?Pbr$$>>FNcm74bzXWXJ;nXw+<5U zxWDf^jp)wOas3tyT(sdOGUdbLcYvZ&|4m?3`t=-{;beP)=t*b*tnhPa)$a|7Ce{(z za!a7?E-mz_N-%Db{6fj~+blPqvx27?n9^0akBwoi;FDSv;0Z07WLA-YeV@es6mq(QIg?K-1V z7LZH;PV#o#a5SuaKZW(ZJ}@LzguTi{8#$a8U9-w08zGpIrO6$wD9ag-!k$R1|0<8m zVtj1)_b7cPN6`>dj+*M~d$3wE0`Y&*VQf!xI`_?;R%zA;N=@SKArM z!VkQ~(kj4WS(Hn@Yrb9gJ)?Znc+c20m=NOu*@IJ^2do|+yx!*rlILR&7O?XVZ_{f+ zStdsu-RkivxkXWoT-**LGZwBPC>d2C- z-1Dxqmyos{J(`OIB42}>5+I~P;_&b}jj{j-;rl3z497c}I72*Vr{-LP70sXo8Vm{H zcPOr?>wS?R+cil1nz5lh{X}GK)6s4{==Rx4r;{5Dr{B9X>sIrOL)v%24>AO?Gen84 z9KOj>QDeSJvuLo2tkBaU#wuHewqf&`n@2JTR`$A)G7V1ijcWI`RM`7);n5S5mNkzlmxd7WH?na9G-8oN8oqQs>Cp8jsy0vmPvE%>F z#t#E$4|KKKS>}gt>u^}mLQrnk*?>@8vN3v69R77&w*qf^v5E!e$zP#Rz5eR$}=kA)S{IMSkhc zI(JOcQL@7R0>;IjJfvHRR4O5bQJv8596=^ zwtp}PL3w>+; ziA#f)YHQNZ8&aCk6>j6g4W9jPKw$5u7nc`Y;+$tifqPsM#xPwqDo1JpRl*_483V5C z>CnnehC0jfO565tK3S69_8aPBX5GGlFDNU1pYU(Das1_J6b_w_NNJotFUwTi&KpxZ zN1d)c(^1t#w)zFOr1(rx`A7|z(Zd}2&R4pazNBph_3~6I+=!xVHe^K=_>;c-WItBT zQPl*49@d9N;dFe$%olZ@H`VVC1@oUmrFL0_I}#^Y1ksm(aVn0i2iyE!e|n@5VaThT z`%Uc>(m4NArtzEw2o2oHpQu{59d0K)|2Uh9`70!ySvR9f^Um!zBRG{R{02+%Hdtm| zB*o@H{_paMi@5Vs_E->@RpOU-a#5Evb!G8}sGqD?_W{)Pp;lVWo~Z50o|yiG`)26P zP!1QWL`q68Nhe9DX=Rp4^kY5Iz+)#$_FTc}({E?oG2jWJ_t()6e;@fM4CCL6C@*6V z!o_vJ@*0UI;(po9kofz3<-j*0&!%(TO^k4UdJs0w+Ilnrcl$ek-@i<}qL`?STfAF|=ooESm1L2B8wRFrRtO=4p z2RxSd`!oa1bht+QW_6PXfy`4;kC-qeNGX~Ont@1a8ve0d8WX8v;384Y1%taR_W}Ep zm;Q)8Rw{f6k^~9+hiaSvwB+}8HW~R@1v<79@Tw4KWeUgb>(vygg+vrpX+x(mSW9#*vdZ5PA)&>c4JH(EZ%tc}+LfZnxrk+P;Ix za_qqaQy@>ezD)m?0Eccq4_tJd=tWnidcTNGGW@OH=(P7vWR-`e%50i`WwXrwffEXl zRJz8gf@;;oo`Gl7@^aqmW9`%3j(vesoIA%XSOJ zmmM<}qsJS+{@AF0?*H}Y2C0N`dj!1snA_gd?(zg*XBro>wWySCawaly6GSiNt~WXy z3wI-Mdr`D1dZ%+bqTf8r#+=eNxJ)NkCxt%7eJyqDL67_~>Ei&D$6*pL0InAf?iV6s zLowIZA8F||8ZE!Ol_h|NvoHqDxpth8><>DfPT6uZ1tYxGG3AKty7#c;mM~(Q`!7Z; zAmcbPCco+_n0;iAoYoD&p2e3 z_wFd>vFsIz#7CT^-`F+Hd15Stxx7z$z48WEuvN^@Y`$>1U^nf(9#puq7|R-FJgnH7 zc8{G0%X>L&C*QlD9LnKpWfhb(pz&ss8J|zJ=DRBJcXOGpb-`CKO^m9;9f`#PKRmnXVsi%+0MW+sL1uG>lj_brJCKQP+$Kxn>?%uW*C+nQC`BX6K<$G*;&jacfO{PF1OtX+pXLUv* zP|gT7tB#cq9pO%d%z3VjGO_5kx zYI(lHDhMEE44YxDaYwztM}N`22dD7o%tW(kY$wk@oF=Wdk46(;H(8w(x$7?9XuULx z&0*71#PfK~Qm4xkNag{Xv*cCC^P5*1%%?4jIlt6?K2*@+#KIsPf53Ei7%e?_XL}keQ{fuMj2bV0(zaEV+skIq z)fA)sv+ecW3Xfq+SE3(2z6G5WU@y~@%@a4)MFTR@6C7riZzwgAl?&sEgO4YHaE8CYg-_dGVxkU$q&Ztz zpEKeBFAT$(reatXKcB`)4N*Ok<^aU2=p{EoXp)j__23MMTawQk5`}D@L_|#L+3jtm z3Jozjc0Qy3tqTD`&9&c~g24dq=XpBA!)o@K-yK~LY=gL5$2E^*!As>pE_#kCT_4vH z4xLO>3b2Y2sKL;ri)zZ`Z8-P0M_2t6tQZS8A|1YuWBM}QyhIvQm*-AjmhG!M@j72X zDT+CZmF2zCnI3fZH`6c)sktQ4oRUg$;Y1@vDsotoN?%txm0>l3e2Q8w6&_7~8 zUXBXe=sQJ|;&ca+ksU{+>U4qd>LsOXyZXb$T!cS_^H4nofp>Rds@@(@6deIS;fix63B z$__2MrwWP!&3Wb&Av`Z)(p;7+sFcR7;HjqUHi?apvSl*bn-ryU%;!^R;DaO!u6Yg+ ztlU6)d}Mg#NTPTubTTMZif$k@IZ&k2zA6Hr8Ao9#EhS|J%_^J>A`o$3y}8(|H5SeC zxhr0VGzl=6FCN2ACkj6(?MWb09IC2#J1e3Psm5CU>!Y;mpLj&W0<+%rL$c7+8;v(B zb-@h$z76cF7MhMUGq4a-?L^hNBp-B!zYk5uf?h@6P>JqFiWxf()IEDnIA|%1w0xi9 zDzwjG&UVQc+mlmM`HDba*7W@#hd}x0)4m5t6#4r^F?*q1jV2&hI4@7&p4;1<|7TV2jpT9lD9=MQ&2?6uzO+}e0k73l4bOX(VG$h? zO)i>%dYRHtM|RnXV3=x@b(SJU+!uMiE~6IX(|ySlImDctoG@yT*8S5x7?Vhz&BFMV zd(95NMK|XJmsz5yUaIMInBeZh_0H?FA6{JvgSq>H&5aQEm&Xzmj>^!TAeM&8&}&KY zEFn5N)b*6>^d3C7KA6G64o(rOa{=Euf^8MOEP>RTtA}*OXqqTt%Q0GEPp$b#>#q=f z#&%UZzL&4pR^zPTBRR9=jG{H+>5PxPNCY?d=OiUr$s*)| zyYF9LVjh1mZKLFQF@h>#2u!AR6c9LWB>}Z9iWTA!cCL1OrY*;lhe?HVwe{sk`#=z& zt}AEo1W*DN1CxP4H0nx~aFbF;$Z{uoeavt6mmr$VpBoDqfRfLWH?WIf& zNgn-!j$Xu${yBfNduW)KenwU!1I~y_@})4|(CzDYgxb&qV-8PefPmKebpfh5)IMy; zEQm2)iS+>VS4AO8Jm+h0*NTe+NMwruB$md;!dVfQY#Xv zYS;uB#5Mo5ik{eQ(yI8)It#1$hg_TOq~9}T8dUSU46X(<8`mw`I|H6fcHN4L02ZwX zS{KjdDpEltMQj6xAfKC~K{jSaut6kAtdxkJ5`buBEkduYKFkoW6^w+ggrzLl7COQF z0;gk&7!CyekU5)6GX#^PS-xKYy@(!n%-E{4rP=?7rgMyr?D^XEAjZVjq+{EfBpusk z$C%ih*!IM>ZQGuh(-Yfz`}cp=yH?z8{<__G z4Y7ep4QAv(H3VDn#p>;iL0Ca_S@y`n8uYN~x<4(nC8bJ}6zH~LMAgpQQMz)>P{+5V z{M2&VxiI~DW`{DnzRpH1&8eEEAZ0XGSdd2V-uSS%K-u&0cH!=M53&)oMS;sUBP;5d z%k3&a@)-0Ix-{}k5g6gv&JsNjdLX$Lq^uN*MvnnPsiuE)JyCLQI;s@gH9<@EVz}Qz z4o&ykPf%?ov&=7iP)uYixxobHdvf7=uG^8Z-R43uyjPJMA3I32%ejHG+d0OnFmS|? z|I*3}+2x>RFc?Hsq}fHq(fQ+PmpNsXEv6r%*r%S}NX{XtomS;>4Jgpu-yO{|bdYGs z_(IuOtyWrDML2`QSrW}OUYW0MDbTt&1^Yz&2)mUPAF^!L_%_gz@m+4$;)v+qe3jUT zW|P6%juej2zc!BjH|7-Tls9yb$Rsdo{g`@#a{JIZSjP(0l9>ACGgc5bqHfRV6HMY) z^~8*WWL?mSoJr71Nk}Jn?Ey>QEYlcS0~zGu*CdOB!219z4#RJ;W@yULs zdkuXL!9&eZji~$sJh6y-*Y*DUwV$wEa$dAz+b%7<8i?hz04US!V)Utu81}`|d_vY^3i=R3qECyaWaMV-6X*;S>ZygHLFbQXHoGE%~4X# zNKOQYh#e?$@v={!(G4=m&`H%Y=s5@5sG8N3FJ{F>QKL%t2+M18COSpLud=+Oz= zxs&|+ErQj*^a)GGDfdF>RU)TQid$Y&10MaIK3n%n3-wX4LzBn+aKuDY7Yn`*Xn*NIKf9ryJiqUMI~X#U9GhN)XU_tUl!+--H!2^0E`Cc5{e;@eep6;4f* zi^!pp){uU6``&uy2*7X+Tg=v$AgwLd8t?kQvy0s=uL}?)X%2&vLf_9aPb^bOuiqk9 zpVrp5FrwBPcwYp;X#(*ArhoY-Qo?qb`(+@4<6wtY9n=^*|0y3==NNqKS^0XM@VAqR zR-QN?2((_eXZkE_@iXkVozvwMyj1$@pml z6+#whYGFv_fIQEqV0EM9VLk;LviS|AXbj!#g>2lUmcsC%R^D;2J3!Zd6*a8WP%b|1 z1aq%d@&~+IQI&B(h>pAReD&J;*|#_`QAss zoS0VrXhN~&E(-~}@E5IYC28r$6vxK7h%Nqs)wF~N`@5u7ktFx$6B?y9=~2E~=>i{M zG4#q$IAmON`X+6|)T|azBEwqS;om=n)g;clym{LFexH9S1C^uG);Qs-)(o`*&pnYr z?0PQD%c!H_g|p5UX;2va9Aioryy5Ys!eli@`M5X`M2tltJlYr10Shq`L@PyOI(8=m zZf!|@e{Svg_9zjrIgFt!_J(AjL%P4&rc?w4qDn?I<#0wn51Gbo5U#aj8zQfXpVXVt}t-&*1}m|AA21i!ExSugRl>12P# ze@Y)8Q z`Ngt}4~ix0li9}{FNI$Kh&&i2!Un)+jNGDdK}{+7#-+`frC zDtDeUL*C+}RLBMvD1H6z#-mMBUl~4+Z;fEJvRVhyYIDTW)6-kD;@IQP*JWh0oG0&m z04fdk2hYqIR47?m58xpG4ErJ!6rMNq8Gf3k^Cx60G1>*l4L6qAgx~B!BZbO-;s z*qap<>NU7%$E={&mM}>tEBVDhH$IH+{T)iIOtr6#axeI3p=IwysP&gB?9ZO(k=MU9 z4{jOmPpwQG zN{eT#CaU^HneoRT^8E<9nnRQ`ifyX{e=F^)k3ToxBIgL29tPQ+y>j}8!x@7%) z?-m24WYdJ7TY>vw;DHl1KrBAY9&@7?p#&4VA>J)$-5El7dIrJw71)!Y0mY8Gc-a3_5YVA$Q2{7tLq zwP6wt)xT8RR8Y6v+g48jaLot};x@ZRFT_H0L-Y|d6)S!rHeoBXCbi{ZmYSV}E(>}` zTkBZoAK}bkg+d2vo(L}Wf{jC9$5oZm#P*D)DVQ>IBCs0{hl+0$BO-{+boH#KSC_t$ z6^IoyL4c}Gv%g?TjhTnzO9L?wsPfM#ts1nG$6tc$02>RuBGfN}uJh7}V|s?MWaKg-?(!@z=|F#5S*U zWk(R=p_v`2)Eol3)MzGZlwukA;)|DJvvg7vsVyg(+Q6#6d{D^AFwi!v5LXY{fZSgD zD1W$`fPEpVt{oUHSjZd0l;z0X^SX;QnQ1tbH%1r0|W zwd26vBw~Z7@Lho|fjWQJF@OQHR8C2TqGm#Q#SjG}w`qBt9K|?Z>Q$F;dyg z4}N{H`GV_Jc!yGDdlM_L+-tMv^Xq#g(Ofl)q8N+0 zaVZEfht`KXeCcX4tQy*3_7KMw-Gi#5;LPskcsXZIxzKYUTN^1%7tx7Ch^beC3d+o^ zoI`ftW7Kp!o1M=UrFm9_MzkhjS9#YZc29MTPn(|OvRE^$bq6V?cgzp!+ zocp6U6<_x>vf5Fs4$fOF)+z;r0o+5QK|uj;b?#TnQQ-()1N=a`E7UDgD@iKn)HA&N?g6q@vPqJs~IW zVQajF$+O6>{ECY8qKyS26+5@Qh&$gt;jJ#gUn`3LN+6wvu|A{@-071JhmNE*g725NfrEnwtog|a@fYM8@4*bQR@NGT$^dD%l`duK$Ig>8UDg> zqnx(~)5s|<@EQRlI9zOV((@Jmn%nHgw%Itajwo#&H)X~nX@umroX08eX)CyzoYr`* zp&^~`MT++9?6eD<@*(5}CNh^&s{XjpS=t0=!v7vK8mm2^{~iEH`_XBpI$QDHNiD6c zlo0m)yFMzg23Jva(o+cy3Nwz-EgX@a=d|9#&6O&1c2f-^B|2tvGIRA8W7d1Qaxwu= zrY|flZK~Y~-gVAQB)r2o6os1k#C2R`OR2Q48t%KK*F6;@Rcns>xNXL${i}A)V~hhV zBW%HKk<02iv93?AX8yEJ6MFC^&glwr;oOX(2#jpo3Ec)z1*eE`7(Fllt{o_5o8_lW zFmF4nPQSGTvKNK=v}Aq8CH~F!`!F(lpba5;98H>IdM6Cy5fF2RcqBp`nOz(vdgUiUuwy@FK!t^NZenH?2LXyQE}S$ zpeeke2j>R&A?am8tG%0Ka+%+qmf$Qc_6zuVQ<>4i-$TQ!X?#fi{lSRoye_l|r!fOc~d6_%Er%lDa!rzWF44#8gHTVL$OA z!O={bu z_*GAD6LM47be-physybF4EN=VZ#FuEPaB6}Y`T_)o!vAX^dVS-yq1dbqL!_3{ka&M zx}m=Z9`Y5*^+W1xtw+B~E(Og^yT(%1?Pjc9w|^1TbVrmea2gN&AfZ!V)Ka&DxM*^* z&f#bKn4$TuWrWq4SdC`pYs=;vN9rLP=z3XNCyZ+~0>jZM-h<~{4^Ti}UZxv>!dXvM z<-x)o>k4YH0jK^XjxpxKoMq6g{0Xz)%TBclDKqD)ci-lG4Cje~$A;49d-pbGrX{|- zs^WO?!^Z5A?fOmTc`FHh6y?DaCNyYLRaGCq??&V4ca#(BwYLHFzWG6v93uX-gjUwK zB(y#8qCmoUfL@W@x#BI6splB&3XuD}bp~L}tUjiP;QTp&B|G^ovUT_QH_yo_nPhx8D_%UKt}4F^QE7k+G&i0kL{+g3}=jFVW3CQRI2gY-x*bYK3R z8JzPX_Ot6={N4Qz$Hsjx#v{D-<TeG~;hO>86hU zgEAG#uKJ~KY~1Hbu?^F_iJI2)w)m?{4Tt>A$Otl zIa8zO>g@F(;iBQG_@n`>@0D-}Qwmaz-`hnGwkj@ElSwgO)jhICQohQD6P;OXdU{vz z?K@w$5!a9Wm!&x18To*=G={dYte={rd#%U0>Fw0Kb+knIdaiNP{o%&_LUbt~^E`-V z)81#|x_Tq``VR+(%|RQ7gR`N8b@=lCABtmgWc46Yj(8M=OFM7nN=O^x$Ium4V&*iM@H zTKA6EXRWv>Ex(}4DppZ(MLZZbN-4}waIT;Bw(I=tUtnzOPh@fOXyY^VU`_<6u2#T2u zcMP^xZ&I!0DYTqdD9Ol%X~EEqCi^K~ou4d$1u+6KpqMwLm#*_WAmb390?g{_j*tA7 zO%;uedQ>iQOObx3cd(f^2G;qfb8b}Mc)!be@Jb&qR2(HeY=*D%*~0IJAgkc^Mr zrxbCpI>6rw{C;x>Ev@}vQf5x7#lFD`!Na0xys{(hM`idj#nK<-3e#7+YgzJ~FG-`s z=9&6FH0^e3om_56bTD-(U6~b=Td+>;3sjp^hf8Y)nX*6MWZ|*wqan?#bk~7RhhyIY z5y*+F-YqK3XZLv3BX%0_1?iZS6j`!EN8HainCwwv090$`@iD4-(-34!8*IPC+IYmj zp@)B=H8rsFJ6$-khrBT)p}x3g$TIiFoiUg!gkGm5JjbPEJkS3?+|U0C+)w95!V73E z*NjWM2?>T||7D3(Dd!0`Mv0q^b&W_)Rv0uq<5C5mas?XjwWv+pY8nU03sD z)41bLYPw>y*vv_Bn;}a&Y|ouxJ^wz$AOj+!VK{G95YQ*8J2H~%V8kdI+u3+pEDRZz z&c>%@EY9HBqaV-!1pQKsY<5Ezm2W#w6#`Em(v|(e&t^I)$u~uRH<+lQ?cY1_&vb{;cK*mW?Ie%IJg9cF;FIp?Y7XfeRo*S zyYB=mkNR-~o3QVQt?Bjom$gHVUFO+~N?KBXSHBRwP5f2KYR4iGxPQ}q7;w_`J0|~o zHwCKsdcPa8&PZv?u)8AkB-qn-jPLt2@IiVs{pYIc;-GUuet$)%BP4i6U?QeQ3s#Ds zTjn6J#UE!BDXkB?Wm_Qey>qOfW%?lgmvH4pzv579+KJyY=%*HG<*rWYQNPq=2BqoQ zmSy3Sbn}`S-LuB+Hj3L>&{nyXlnMeRe30vtQFul*KB~1GC z(FjrR?#C0vu<2**(%6GKvXAh#) z7TeZdzsg3rm>?0lQGwv?h40-5K5x97X51EvLf~dm-ALKpcfzjnluj2@Y?ga-E}VO5 ziLpE97jIky)h&#%Ogf|1D87hX`Y+VJL#QXtatCv5H|Z`jq9#p0E9(xlLYUyZ$tVlm zM=#%z#f4c4m2glFL}fKO2e+E=F*ZMCm`QHQ__2on*1>DxVx6yMv{x)s)vR}C5fJDi zPlmEa3fsAcsZx(jrh}DChzqrdGTQ2Flg;K&w`vkkIShldTm#6lJ-=wo+raRov9Ol2 z!rK!4f-#*a>Urhyo5XK4o?($7iJl@tenHCVJXfHWtHl$ikJp}l8c$!m#ty$Jr2Iya zxlc=#<;;rZ!o3i=_cZ(jCSydQP>5E%3&2Ml-t%VBv(Bo?^hBuR3`@k9Vl1`1uBypV z7~%tf9~b9x3NzGLI7cMn8vJLn9C-DOX}4f*nnrIDNqkcc^qkQE9!9#DP7^L%%r|Qj zqh(i5GrlS1_q(siy3c_!qqZSjkUTdAKJ%@O$-gY|50W?2&mGGc?!81@{w?*n%3#F< zSnLx@CUnVYbPDMt>MOq=;d$;ZnuuE-vPnB#!Z(bUdbpsz#jE*1QknpPkCvZHrSk*F z$T3KhjsQ=^c86(?i6b)>K>Zk8Ro;0XT`Fc1Jib%a-B~L6JbT2F%@F+ zz6^3B(;U=mk0-{lGBSgE$vr|_kwWRw z_civqS#h85b%8fZ&^FynCY*Jzz8(@CDu z@`ok+4l&Uq4_$}1f8V|H_f&YK5%WU%1F4HeP3P&K`Shc`YO6vlD^wy>Gh8+KH#+xT z*7Ru)>#dhox4pXG?`}k&xw8uqJSuoAi%Zoj8u4kO^GGwQGNo6bJgwj2s15}C(uAYv zsg@A*MK`|t-qBu&)m+-cQa7A;|0b7D>oF8m!X$%}No9m|6WEc?K+IHNM{3=b`HhsVT3`0h43)v6q1TU8QPr1q2vQpr7hr&lon_rrvMCc zr`~f{}+SMEc#rlN7VslFQLpb9JRM{@No=Y=Q zFZ5W6h6SIk^u{C9gDjxOo{y@XZwB)z2K6=%1j)krEhjK3u=p_L0k^8<*?ZJhnspnh-7eX zjS?AElVQ*O;1aFlrCC*zAYvnv6f^FMRq9jS9f7(sot4V^5B(m$-aOOlK7lhS5OW5Wc3MApgIYT8o4oJ(Nv?Z0Tb$-g1my%`?WI|I zUv}98=Ejlix$#Bud^R!KJWWZQp4plWI#Qb`-5MN6owhd)`EBWtWMdL8}$c;hrx*k2D0lg)U#7e4BT<9Wry*Z zbLkZJKuq<>Q-2O}u~7C?ZVCsP5u)ygqaqji^-H4fMh7(EGLdr+YGUfh{uBwyH6Gae z0mboU*dMnOA5-*H$ApvKHmF}{xTN%RNdTz`YNf-#HrBqyAAGDF;>?=MrpMLDPssYOvO_ z0z9nL&WCJ9KjsyKM=9Cy<=WdJybK*9`EY!0^}Xvd3qdS{$V($^&7prUcH$<9X@1V*J|+FuoKgARqWn^Y^GJoflqHxt4-N$wR9CY{>> z_pqzg@12smy8+AkBree)INE~hk|^UEq;i&caZV~Y#^_&t zeZrj@o}L{RP1-vQfC!clcmeDB6Zv(g1vx*X_dSZ!n~;{;+p&(PF{Yp31qnZVb1dDc zGWS7`!GBgIQa$-ctPpE|Wijm-D_vn_nXbbKOnMm;+6}(}4nmg=DVwGfd7IWT7)kt} zpC%<;;}8nP^LnI)LIrXBrGbB)n2DqL+cXKY)pf<%Js~nxejYc*uU}Eb5 z-=DUxWUG1wZ%L`mvkyGMSj(?x8hkt}Z#nL~8?j%hUFro7(idr?87J;6gwZ4x*Z;*= zDFb3H>lfhMF~|!X%AB^X3!XM_LaYkLg|5m5Vh`HD*!kT<*)QjVwi=s~oI3;lT2+_f zDnb()JrT%zkd$>?64h0yI7Bl>V=~W;N2iiY`8Eye+SDJx_Bx+{9DIpvwfNKdxgplP?Tmy` z$r&z4oy?wCHv%T~VNWLtoO1pSCMp<(R=Juaob@%$D-yS=$RD|I>lI|l2Z)%UKkJPD zxWJ#RmfJg~!0Ykwgid83-l?k&XYZg2-TX{HteGG!f(d)?X<;AN-7T2HWFTLlPgRd? zJyn}t*yP&!YE`jhyxSlApVvWO9w{W4xiK=A0fuf2BuyhVn7=Zl7_IGI?1;7*UkJz( zO~3SOYNPJg9gBEIge!uWjl-5eKzqQWh?zx%`9d08;7}xDKCL~#x9uIp7W{)s|Dh$PT zta5MA&+WUFU-aifn zSO(5dl^xWGhZckC@wX7qKHj=^{rRCaLdm1AqAxFOK3NU+Eo?P@{ii{@bAt4b!PcV( zO9Gw821@;h4vcL8$rmB+>+w*LTon=o+MrSGde`u298x>puUj}B0R?3Efu6zyVqPIo z2C4DQ$S$23QK(-B$os6ynImR8SF^t7lh+l+b4l3*>ylO0?D4)>OI}7p;qQd>B8zNU zkx-boioml}6i!us+h#3asz~v^YK<%17k08ozI2(*i_s8hoM1=F2yi=_%9+MY4~WB4 zS9cyP1s#7#dzs_*!Ul}3{Tl*uQ4H(Kfr%bA-GmxQ+~KUD$*|QzS)@%sLHgf>_UwzR z>R|ZJ>d9WZe=-%{Rt^x{ufu`}Owb$7e!|wRFcP3jXyfA7WM9qDl@HnrS~O)n03CoP zHq56C=VkmeEL(=B^Z0y}{~Tfvb(=2;N@)Ex^Y zIY;%!%|)qt`{*_0!?p+C8|}d~#>n?FDgwo9uS)r$9b{3@SN~hP7nS)1#5311a z{kILDfvm~&Hvx4<_lQ3V_p_JTWecEwoJGFI(8UV!G~}%o~twzhijS=)<%0q^UT|A z`ZJxr0ym+U75sQR$A`@Z`zy@%%gBqmVh;=nVlVdL=EtKU@Gb{z6K;GwsSMN!7Lm>6 zj=;lwPD!J#{3)nWuvfrsYuJ&6onye11$%_*+Y73JzrI@<2sShaJ6?@toNh6ah*3<2Z#BiO)QNYaFa)AZxURYjIKd<+Pp6B zeY;O$Ean6M;j_gxhW{-UG1_xl!S~WShWk2kvi(V6!~3G(v~4BZW_5kw4>&8<0eSlA zn5%WSS%PPG(23{ipQ!r?-*!L4%64yN2gxA55yYO~!wHAwqiu-ntHHXSwo~WKRXXY) zh~tbs+)Ixy3Si-&ktu830n+|S;q&z%T{IIs1`B4k>vPmL4wsEM`aOcowIOQKo(Cu1 zF`tRzw*xx3z=L?X1u=P_H&oiNEAI8C$V7}_{0^J&^M(F!*zqO2T+?v4YAdn7QDd_- zixDO|(E0Eo9wDq}Xduz~q=c1Ib8t0sarn36ul>eY3CqheUF-gN2lt`M$aOeKX!yV6 z)BA{3{%O99-k>c&E7d(i?y6G{}%yq3SUKH<69U|R9eay;E1M8 zoT++6nmIBgB16E_sBehYkksH^k8{A=&mCv;A@ z`9>+iJ44%kgLg+Zzo#2f+00tfWoHmz1m1w3(Avt~<@HU?JtUMkZDPqUGu$m;g?AWN z_nUE&fPNRlOQ-8dOzuOnvSDLIL?c~Zp`mgdAsLI0jKKECR?U@GsGl(DhnwJ#cnp#2 zM#9^6E!R>8#e}v|){SvPNzk~}HTtvps_Og-H<%75)G>Eni=lwX5XX|Dc*s2V15Pq> zcbs&<)KEi$qM(A>GosinGsNHhdPAuKzkc!ZGEl+s$#eyqLZ-l|Uc>>|{Dr#gWdAWy zFeDA>4$Xq1ab^)u>P9Q;&G&b#KR^@fKUg#GkX)aN9asB|Ujf4g~!3L4q39rW-p zxe{s9y|qrO2N$KOQ4XX2TiaE6fn>>1AEj^yj`_KFtDL2? zc24*rL5j3lpTo)q?yEtJI;9}&z`)#6m8exCj$dxXAQM?dvIA^K$MyzPi(D3lU+fO5 z^-xM;m2odfWZ6Ih+-)&EI!TjoJrXMrRN#zf=|3Q?R${k1MR!8NIw8hVoM&{#{XPff zgD~L2Tizz)fU{UECOJwv`-jO`)(D`~Y3RrIuz1CkCT6P9E{JR3IgwK4&P`A`9{C+l zTg@zmh+h&qIA+u}n4Wz*bna)Eh;~F=oM&himy{KYzH$&!jT$}6@t5Cg-$EgyBXicV zoARiar!D%2J8+J$eDVk$)VL~^mX_CG2Obl)MoHTu+@+AR(2ZvHi6{2*45AoySg{k( zD4DPb!sdV~i;j@3=;PtkiC{#*>ymZ)9XEcsb#m}Ke#t*>x zcVAhxQQb(%zP~Vg#Q2T1t+gsdT8?C;a0x@2c4&!vXQ=ue%z_ANI>vn!q&Gaqj+F9?zQ|uCzVm8dKjtRSUz#pfC5P3gE1MyTcHO3yfQEzeBm`LwW+ExAmBM0;^Ab$5$ z45}1er^>51bKEnd=x`M+rvki8_j&N+5OfZ>oPY%IHR>@xK{5z8lEnoH%@UuCGe4w6 zXx#EIjexQ+;o~SOya}U(k|z=S;9PxnNO5JymFFNvg*#iuVC{558zC)P1bPVqXMdzb zCDjAXHzP+nKy=VzE8xesabgDA>_!T|UlYs&9UsF`LNI7F*-PIRrj@3T&w7Yv3TY9K zh<+l(e}g>cK9JfddEZ}nD_F&SW^!DY$L7z>lu7We5nMF-BGlW}*Vt%b2n>`}ajs~X z_~Jk0q7tEsf@z}W>30Tm3&ZeL3q&kQaqTYi4>$+HjBoHuMI#r{m^DexH7?1w<{IJa zI1w^PiK}rb5!zVX(f0D$tCzkbxt^i8Gb~bI529Nmf(5D-J%AaBGR$(w(CRYbAV^_R z`ju_~XMBy{4o!k1s;mye1+8DHFqq1)Rxs&u;mT`i=S0j7S*<-D?%n=neg#up{ngr5 zGYAJiSd+Lp#sfLZ1fxmhHfJ`e;JNLJbZCbp-43j-Hm*>+>cED(*FMm0c zeyZVXn6y7`^M>Fhc9va@<0>I>TWowpQ>XX4gvrvo#&@aja-k4>gAbk|^L1x`i zl(zN>`xjXvoT^@3xcRWRrMbtNpT=;0K}8oU`{GPERc0^Mw-0iW!S)k{0my9VqhgA& zeyj{AW4CNWazKlZl`5dzjE^gcnbfQwWiI+7radO%)>xQ7oB4}40GEoIw3kIb467FV!ipQs7$ZQ`t~?kV5m1#V z6{(JC;u$ZVCdvxy7*b8_3w_M2FpdRJfiCBiFpJbMohM|_p(jEM%zltVHn)IQmvZF{v1%LiTKR8-Cj+XW7 zytF(GW?7SDS+k}<3PhXesxC%E`7>w^k``IlMxoPIqbovx2~+wFot0DOD%yldStIb* zYnX2CDxu7%&8_FQtPy-}me?CJCqA|Li1>>;nrgR3Sd# z((*+!BYwtHTw1Hz5%*Us%yS(*-_qA#@|yexPh{M&znPw8Y05d=v|gC_)aeS{fUeG{3a}nV_0T zbyDy#Da=2WS6xo>xh^7&5QIFX9yzEUrdlgsCK0V@DY=Hy7g^&}pz*rlUH3@rJ6aT% z9Q(?@<-bm>nh6ADr@1O(2TV2(>qHab8M!R&_TjKs3HMK%83D=mBE572>+>EnnKr!T zuJn8tRmcVuiZ0x*`$c(-OEG+1$^K%A$!B30c-xWsJ>;Fp@m-25k~qFBF&o>kS^Zd6 zTQ$NmKyv2v4m~QIgjRWE#o>KJ{f>t9RjiJ#3ZGcBkopZMX^?kT$IOZ^2|L8Yk6lJ! zR1wP5j%PNuF2zh4bjpoZ%WF1RKSZ&z>zw{0C1^inb}BaQ1PQi}*G!OYfEXd`A=~Ic zyHQ51JZ+E1K}-m1N>RhRJL~#Dq_>m|Jl}nxYWnDVZ_HfI&v&6E&wX{noeBbqV4;x} z$W}Mr@JH?NUEc$Fuc6f>h>?)r_7FcJCl5`urqLt_g&74)pkhHdD<@TLMrHUYR!xxW{$yC{#=W~cVX52ytZ_TWDV1g9^pp?ptpTs8O0d}W z>=7hO5XF!3xvxBEk@g_E^ARASzVGuQ@(_&Ijsk|qZd||kjSC{p!4)vy-*?Z#!~Ur6M5GjRq?Ti@c-}V!3#X8#>rM$sT3sZ~V#6jGDwV%g_0Ljv4 z3II1)YC)Yjegv~yhV^{y*i=5SW^(&gfU>IRzV(^woa~M7_DSYxI(32SnTs3c4fa0z zJ<(_8Lo4X^ntujDqtja_nE?#Pb!4h%Tt^zbA+Yp&ZQbZYni#;0gOW_R4IdNnxR5pf zq%RD9sVYmf(l9D2VY!Pnl8`ileyUSUS{ySP*G9P%vM@*?8N_t_H${~42YV#H`{P6W z5uy2Do>!F4 zw=+AE`<;bUqg<5)$k3f%F2#}&bx;v)LzIB}!kpZNB!8#@*da1$_#kmSMQBUowokPG z4M`dB_rqqz6Y6D)%l8(+d6N-1aT;V02HIMrL39#D8auJz2nUk_>Q`qG^ zz}-~4~yKbi+R#Xvfa zxZE#1wqLeF(fWiRYn+^2T&`l=x&vhxpWwB!9Uz8Z#XUVKep`fImG1ZpQ*7n8W*z;u{_1OtoT-fu}{DAsp&zq=5DWttA5do>OcJ) zUz=0tJ^%YhovXU6{28v_WI3rK1?|br)+bHip?vH0qTm59 zUVhjXjQPTUA9HsFN%9L>YBloaLSy!NJy*}}i*C_Y%`tJf%Z74`SJ?z$?nBJ~ZI~ew zzJT=0h3`s7|Kh1YTgabnZAA3t?{*#&zdZ&Bw*56DnS7bHP|T+wq&-*&2ddrJbOax` zeu6@z^ZA*Ao}Yp~7#Jy*KpkOMLm26j8bzN!7^_WAN~{!!Bh0K81TV4qAyr| zGIg!C8a)Zq!^e@G|9jtn;6khrg;?B6qf4UfN|8IFKx0MZhIBN0T5gwI;go+AlTi8J zA_sDRKnW%hW3#c+gk*if5IF_7^i`0g10(mTi9j9rYid!E%emD0YZw(F9*BzcF>piQ zd0A&%t}(><@ZtkLDSlm5T=ccssX24S$$9uzL0l0~DmJtewdY{F1%hkw(Yd|OgMAe-Wg#lg;J%u!=MDg-tb0|3=V^VSIau6L^2Qoe9rmf6f;$iRt zVvrf-(z*OTPpBgW4aIm4@&TPt16S_(4Ok%&;C~TC2;DNpVe1Ke({wXEkhWvKq`&>x ze~KZ3;C)W0DRb;pz65n;B(O8I?^DN0!}uSwsrs};>i7q zSDoga(Ol^n5gUqCB(!!&geZHT%y(oYe7J8|m9XZK!KLWFoX*M^1LT3Zz+(huXy7_| zh$zSKpW1-LW)UFa@(4HR3ZRLB9jMRNepBmTF*0Y{$Gq@@|atOcJDRvhT6MQEJX9?h~PH;vsc8==SG|^qI_Z; zc(ee4%peB^dK$CkH><#gx0pBtI@iWRJEbmk{Z+4I8Jiz8VonR_Ma2X$cx02|n{mbl zp&AHx2FNK&k=ij|q(7;aV?Y=cO=C_JtOyywO52B%89W#uT6b9WAazxvsyi+d>D0rq z(2IfJxTxC4!pQ3od01QQj8U&gr2{uhX&Gu!e<6J0s`qd)w+nDN2oo`1F@BVBlX*F=FkAcF zlHBO$jo@U-Fg`#l!U9E0Uu+)@Ktivz?Ud>D9KMliH}ka39WM=vqj17ua|x_ElE=gf z#iBr;f>53#7%GS?CFgmxnwnJ(Z>4CN>Exc*^Rar9S5i$IuQ{%ROdy{yQ+HlEA9r(Py)< zv1C`Fb%2gCbP`*{;Rg8S^i&acDGc8$sYi+`9DY-lgVbI zTMu)rY>ozRzki+*Co14ccdKj~YbJDhEwiaVdcI}H$txQm$HMG`Ypm?kIgHWyx!dpL z*Bctkm-zMDYPc>v*4^SDb19_edu+dzh?@tXDO0ydN7!7BHxXTIN{!0Iy2;rMn(@xi zICEl_AoRghQsC0^2`j$}GlB5${2Szjg4<8}#07v;$?W^IA6M@KtJdpxJU5H`N3q3< z!ec`PgPGnN@ZK-44O*P+L1uEgx}xorpc4kyEnhErfj7)W?(2OM$j6N#&U z-skf$d2qWhoyTEh+kLxf*Rb@A)$S9>_p|F4lffludDWGv+vP8~OfyzHt`K3`a3O3} z0@rr7_Kkc=f8H~a`y4PudWW`Dh?oIGFtm7sP08BW83$yUrli+wMnAJM!vDJAes}nK zD$(=zawe+}B9db(gHziEpcj>)89NS&U_0ZHC*)gQZJ|mbHpX&S2MmV+OPv?CC&Qzu zelD>nL4*0l^+S^WC!qdMUQ@r@2~GpWh^e*h1k_;sNn@9*TXuHWhl`I^s&F5ZVinDX z(8!IPhCKjR2bM4nQ4xJFFm-72>IlIB>~MGpbphRk=mTHH?T7axpi3Hhqx`QnZy@yDB}P3 zHdF*RGbLn*KPb)30spiY?fSGAa~p)7B2 z^4TBW94JV$Q!8ZriSD=@)mEjRKlVzOtLco)j%wL^@Uj_Bc$(UuqQO6J-nv}y{=Qj$ zH~ZN1i-6pN6a1XXq-TW^8Ge6 z(=pKYuh2t<*SD{#Q3f-79}bgEHonIhmZ-?LcO;(x5di)@vYBk5*t*l20{_$cugt7o zPl5@9P}^oRqnO-p2Ror8M|rCamfuu8iz~GlM(CXL)%K$QJ;yUxAWDuSX<#(?ij9L7%6FafUbBSzJV z>Uf55(C{|p{vbCNJVpgs3>Q~y+IEs*Ss84xkdB z4QUCEGgK;$c4!z!I|`D%uJ12>GXjr?rasa>rgY3mlT&n`T!{LJJcoQO0LET6&HBFr zko(`YH@VfQg3|RtTYQ1IZx})KGZTXbs>4WL&VChKR~jAXPVc*0k^PC~y5JE;w(lVw zwGdy1{`)OG>bYLd!#C|5p`RZ;>)oZ1Ts<$Gr2!3NlMR3N?~eV3$d~e+xlUxmO$*fV#&31;iy@|eWk=NgBfR+$0)7OS4UpR7C zo{~&wX0$1N;U8*JZ{Tw_1W1SzN9lgwxO>^-Y{6PZj5oDuoM-HQE>bO9`4!Fde@Qrw zb!g6lgLO!yY$S|DQtAK$rxXAdJu>y)$s9091sVbuF5)>#<1kP zC;pvns`Op)B##Ba?&-sZ&o0rf{Ba+TjQtBxR*aFeOs7vnfHjD76aV|= zWJoT+A|ftv;(lfvvonA_i4+SX6gO}&clY{&5Fcq#msK#;cFFko7Ye$Zq`XOf>_8;> z&>o^OcY*%|yw);;9#fhnDRKUv750==Jig06XKw3Za@|iv+RW6;h-pDis@V4Y+ZZ_p z-m!h22^d<6vKH1VsWG_$<--6xb+&5VUrGO~J7^@;yz~9R94b&$m_pF#tNUfRRCxJM zn+7mdJ*Y!pBpfDoJ;*XRb+qJI9R@Z{ILijSDief=}9?k;)0LLW|Ub~x0hYB z6BI)XitLa9ZBvStcexYHlbRi@({TUmg2OnXn+!j5>|35Dr6HFwGTVHXLT$VJb=x_5 zU*)=+Y41&-UXWW zg_Ul%B6K}|dmgRnnXX#i8i`uo8ZLBcF`3tEHMbM0vsMc4;Q2^&DqFJRIGjFYH`g7= zY9QIqbt4|hVSW?9@?6?(-HR!dC86=&iF_`MT-gUqrn1^HMZf;BM4tM9GS~Zs^0f7d z&Sz5(8~4ppM!p<1Vry*drte-7|Hrdx z=gm9GK2gnOU!WIINWJ5QbmUo7% zYj%X5Hlt}$JU`AFq~gV~+V&KeZ<{|?g0On7DYRd(egAc_bDxb%UBC8sPXO(`a3Yf# z4_VwcCOvSyvz!lP*FVj#CiV_W$L>FUrg_vie6A)ZT^m@?Wmdif2}_5aw?dr4)3XN_ z6hsH)l4n4%pMnZTT3Juga9o@v2Vo=t&bkm7;-*+8NI{(E2xMeu3YSYiU8X$z{M1~w zGGLc-l91)huHfG>t<0SQuxEoNS8G?3H4>3YDDRs1H96-OeOe*V`F#G6C zx&C=E%$lAV~DpM?JiX$7TkO$P&8SIx8HLzvV zB@D4kXH7!iu86Q1ew{Co-!A+?G8yChXzlAX?L+7`0(4jC^^&*9C6fYyHAvMOI8`bZ z0YY|!f)|XNetpI_&Xz0sr$Z&rOtJdy0YxoZaB?tb4wQtc_9a244-38 zdE%!#;BCv5XX|-xuyT6%uEpL{AcN$E&P^aKE;O+l<*fBEH03rypQVVcHu_@)GRimp zgV)zoXteNTB9$-2`@ko)vjuyF7~*uGap*bC$z4Ve#heW`BoV-9d0e6#Zjw&n5y-*Y z-eJz>xXb;mglbKa7eZ_SIgU4#j9iSr>z@q0B~2)!FJ6LMmbA+p_O_+?_9~4=ydA#D zXwwhJJr>`PQP{k>$@C+Q{E1aE$cz8|1h&>XYkwby?-r@$Q^SCN3StChL2+s9=5wp7 z2)#p51X|tRJoB06F9|X1pU)JjtaQdPbR7+52Z+TAl`3*p6Y1^{UOi4fh}*X@#e36( zBnxsPWWvjfV#Ur%IdGWWrpB6WCHt&qF{e5b8R-{~fg*XkBZH)xtSGm~eJi(cdRG15;IY1)U6@k!<1Muk4ZO)8EHBht z>-X$}?=OmM#p964K!N#}`x2qPn3`9}W&KZ8JZorEBdP~w3Dp|;El z`)8P|&)!w%@Vx#)XVB$gWXjJ!f^X-l@YUg#Qx0p3R^V@MsrjN)I^DKXJ>nOCpb-k7 zU|-q&WVVJB_(xr+EnU!Fn(Fp^Ua@X}(+f|)411m$MCL+QhE%!L83&u(R~bDNBCxo-$m9gQD}sxzW2Mf7Jk@R97|nDh>^U}l~@X)qGkX+T+u&FxZe zfgU0K=$Y2NHxyP>+*ovJ1ij9^rqD9@ZdxdZ#rH-|*Zby(=i|AD^a%xoF=rDdDPTV0 zy2D`1^H^Fi^7@U<*M_S`@KSTgMSb)Z^zo|g>-nIr`#G=ka@He|rs#cZeDCaRT!zVz z<@}v++-aJBN`Xsn`WP?o}`O2?fsQkV7g|F%{_ z&hces!9Xk!p!zF{TN%U&*0a+6&U1n7d4`oLXw0jD$p%!qDzbMX`JwLyh5PwN^Zb`a z2ucJ&(0~Ca#3(mkRNoKmh?u%eI6QByL7J{wBL#b!eaFI0l2Ub4vw73f`EKhsY0ms1 z$dbmHfq>P)^S0g~gQ3A+mrRNCsL7qt@LeG59o3VHA!~sDI^qXWADZtksu)He(6sMP!~KR2)N*zCwc_WkNcVFZl2#XjpkH4`8LwgK zj-!lhQAMETeDUqJ&l5%`jk{uzHdW9}_xyj+*_HtvD&i?jwpSuD7kUeVA*>AOwnKK_ z0){AmOfqFYKK2-^q!>>|gpL9R9czTLWw2nqba#EM=hy=;X4{{M>?A`<6h^cc{;$Cr-}U>f_*+rcXXo&0fV=e(GzGEQ%#(y#yTfWUp9 z;B)c^m>OBW1sVz}hjCP2&+Fj#PSIjVXCL9}_QgP3?;>G; z5)Au6#1@KxHRQB5?KcvfL1KFHu2|r_Ly#>Y)s;-5+mk5Q;|q-0ymQhJ>>|v8(?w>~ z%9sYGUDP;Vx?b+Tzm}lql*>;rVEA_vq^I!OWYw&O+m(SwGx$=SJwqS%2gd!Jp zbK?pn`~`2hKEQtV>T#UCcHGGB@#|n-KASapQBgqRsF0_%8vmz}GD<0&%&$z&XU1)t zh+QNiIXdlnxEsFdfm&^NEy)7t0_-Y97?NI|Uc63NU8B)1^Y_=u^@e@BWWm}Xg^XAD zwn4ras#r~_t!)S*W63xpnJh678B#W_frMs@9TCw+f%{XM`OT7BCTRTC5^UB+JlL`2 zMgL>tmO<~6>&hS{EkTCJL`19Y7MQ%*lNd_u4U+5qsuG8cqz#8#2vMFdii?p zs0||<=Mvwim+AADp%PnLFJK^Ga!QRJqhRp0^t2B4)*!Ch(R-hQIiO^KxEJDkcH-%4nt@{VDv)jUo>5|RI4b&A)Ma|Jt zhVNkULq?cpfOx#p(Nags46e%5%bU}x*CPj}&K`;2v*LNnkKw3l`lJGYE4S+UQ;lLMXB>zgA*i!(DUZkQB`4oO(lDku;9+bZC9F;Z| zD)j2OdENd))p5IhDDKF5wb3{BN6#U2wBH7h-vjJ%_c?Y}N-?qN!>a+e;@2no@ea;M z7igau;5`>R$8ljle3Vk3T6>x1Am_K)8X`c#oFNWHfMR>u4Cy%gqn-on75^S12g!bM zhP`2~8`jSatml~*662P#z2}F?77VI`+?Tgm_VS^DG_PMD!z>0*s~rw&S#p})uD=71 zuBIol!@*MT*WpOhN2Z~d(_(J#Aq0LtT!l%0;w)L5{$I9u*J3Ka1#c|V=D+hrq4Oun z!hkLK!hyS)6hpr+j*WKm;2QQX^&O_(KH}J?ywMV1CRRS^Gz>7$Gg&&{Sxt$1npFrVSbytBGE-gY+C z0Wg9k_-6A5f*GZ>#)$m9?uhYHt37DQfQZvn4jm{_Yl#fLZL$*}vF^Cs<*Z0FoGDYF z)#6A*ljVw^l)l&M0arI%u{n@&$i$#%~xu6b@7py1AX+C40+b}mMT!|r3iuPc9 z2dkC5LnUE@Tjrc^7{-CJ&nwJ~o}j!EQSV_Jm>tc`dy1#hv8?XuNNOJ{kkmQ zue!_B*43E4TF&=8md^ahK>_3Gn}2m|8AUKVlut7qS;E@YNB)0 zUPz_wyNBrKCsx5??q2*uN$Dms;WHL$b$!vs9^#G^&aE(*YtLbVxz+hFwY(W}X5g zbsb1TB$FG!Iab2_@r4Wzt=(UQXmF>-nZbKWq(X2U5-e4Fp3XqCK~R2`8ZeyrW`dX~|PY>Y!aIaKRz;r52IbXan2# z>Y)B)x-ss*?Djc6$ur#2^KL6ji?8i|bt9OII0Wc?DqK<`A7lXm3uyHJHHL=Q6s6Z;E>7~XX9s3DG?aO5J!$+kRcdS=gkz=rsTzGyWMB5^KnBd;;D&uYWA5H zL@Y+m@NJ^>6d&swXYS#JK138OnY&ENnmK97gna}pKNb(|oh}mY$fIn)cD?Y2IyIO%XM={O!DN5RSKp4Ln@rDYX$4RT2^SYAno z7q0t-wy`=FfJ-NW%oO;}c8$xl_LzJP!ggXsCWg?KPzVV(D4+;KS6~#zmt4Z>9TqohsJ`KoRzoyrSeK>Nuc-@^GcFNKX=xkl5S9b+$IVhXaWe= z{LyDkX#^}^sH7XyZ0z!Fyof<~Jw^T9rE^E7aFPQZp(f3)oX3Z3M%#`vvIQ&hzgn{E zd3gfOdmAbUz3uvs;@65i3=p2)xF`safWCmuq$=T%C#wci^4yvy{g}Z_Nt?bb5}X-^ zJ_cAekacvS+Z)f)qrxrpBF9wD0FvSzeXjM~PJccFt?ZD!F8DI6ssb0sNsJcftxxbd zlj~HSh<#c~3ieivuj=?87cS(9?(mDx)ANA0=c?1SJ8ehsv)lP_KL)Ka9uJ0Uwde_5 zww;MP?GfHjm$#Dr>V0x5rHO(l?IDr50u7|^X!{91IE@$SQzFA z`R8rZ^2EBYvSBl|K4^Ti;;+~vE0ck9%43*?F))g>*shv!`^ z4^o!~w#Oz5_w)(H-wk(51zuX72>&pA_ryJeQ)21HKXh zGOrjbEI}{_G^5M)UKvgHx+#3bqVOq$X|H}F@=4gXw+Vz*<8R1(T9F{p^y*UU7NF5$ zd?eF27kS8UGMX9xat5pNBIV9|V{KlDJm*;~)~H>pMGyq9H^!lOV5F#DeC`}+I0iwm z0dhHx_ng#eUiuFNBY-TqhS=E<*Xhkrz6$rCWRb&lkpaGp$B;7ew{Hmylg)qTzG$cW z$}+sLeUVqOTzbWITCM$xtHLTam^u(dE*_x+iK-Kb3lp)+Nr9^vn2G0P=J>!;-JcL> z`9&|$2A}9=0~am~qX5&GwnHYBNZTV~yXifC<)tqaz5lC>HQlfa65P)f$dY$O@XJYC z@P0{O!~iwjnwYjO$g9aEYY{4!moParjY`~+VcS$!-sfU{5N6M4Mc-e1frs zQ#9Dh@OyDL#~WEw)fuWmG7OtBb%4smgVbt(hc6RGBvi|Sp5o9NkZSc4YtCri5($D> zZ!SdS=8(%}oKq=dSsx_DC_-e(f@u3jp#_4B+d(#hq_;ss-)yg)$M%ijI2)O`_N_O5 zWh(Et%=7n$6ntBqu6NiGHIvWjTPRb-6Y%5!n?V4~|2|9puW1tsOn{}ZY=DA-^4<=- zI-($XaX`@1tZdr%hz3$rAbinC1i;Af;#okqgUAL%IKTr<>0Jm9YRwe#C2Zdida`Mm zxx$gRAthe4v8H9cjyri!SYKE&@)t{{g@;#Cw=EA`K;H>OnZof#WA-LwAUj5m{cfF< zmxyG8)k8t0W*4S8gGBQfKIJGv8{O7dSZ_*T^!Fnz&tabx{@CJ-*1JBv_+uHDuwjzU zl;Oq*x42WKtO%KrZ}w)nEZ6}S$a(M{urd7C$fV*M1A%DFbYs+&R2BAzt0iZZdUcrq zCY+4hkuK{xwYv1%=eZ7<&Ru$c)y-$KFSSpXaCdvwe!}{G5J2fekLSmZx>@5zGT_(z z>{*IaM6!B~Yn|LXOqW(-e#+8Gohb{(IX5axu@={>X7?mXOV~pUf+T32dde@#$i!r` zN{LDX)MELqNJv9cqtzILiN8he+@$E$MjrLH^WndS<1BE-TIlqUv=~^yN03R+?`&q* zi`6Oa7=}_Sn2L|egJh9Xb4?ntnLT%GN}+9>-Q^HWG{qxt zX_NlQgi*;2m7~l4E;z9OWd_A<$Dj0E_`k}jB!*e?S`b}S`4q=^<+Kj3jLS^&Srl@?)};lFUHZRa&V^q_Fna4n^O}-6 zD$l->5wZ>hVDgwT(m$eZrSiLx^@6%|rtpZg4ct;;VV1;!XqarWsal0PNU4@(jQdAZ zlgbr;7{&l3B#lSp(v{eEsX0h)R@e&l{AmoJ#WATYi{&8su2R*`-cY{X55gfHeiSeP zq#27G02GHQ2^fV9=W$N`jyb7yL@}iyO&5^w2@ykJ24Q$L)8Z}0Sa!gf#U8zj60q7SBe z&~GD0?O_5rYSbtLK=TRUk)yRJ@N$fG|CzYPO%ndk0-F=pq=th#Dck zFwZ2X6w>8D6P0XEcriKAgK12}0#>M=$pMmrFc+7;{q+cV1vRx=pWVZA#0Oy%)=ZIu zdmM*UkJ4|HA>S;#&>D;iPQB75NDx&9_#7SLgQ+R`sq^-8lyfA!K62YY!2jCkkEgVTWRJT;Tjg(hwKbvdnPHGA@m%<=l_Kf*A@m#j zlwfD&LzhhDZ)gt*OS~A?r97ce9vBVfc3fsFmgxO*D#vu&$`KHgyi%K`s*WN2*lO{m z!B{~Y(l)Z!!MB|au*Pg4SIoP7#z6oTtdx?{RT;MqbTL^;Jf%2=$rlUBAG{HsOK5_^ zLE5{=1^(0gm@t)8<#cS_>6l`)68+dz5+gT)vBm27fPCxN1ON1jIl%40{O=|rz~tC> zMs0HVEFFI5Pqn5Oc)d_s8zI=Lxt$Aj-ARXk0ybS^$SY*CrC-N)b)*w*ITf6j( zU2re_hkxU=CG80=-xgm76ROC+#7@IInQm6n78<50{B!@&=BwfHE z2eevIr3pMOLc}oLpa2cbTM-0M9|Xas^B_hMoX&YG6m}5q`$M=Llqd&F zg{E90dV0iagybNv&Hx21@Ra0UZ{VqBW(V}$Z%uNktajMe6O`YD-me^n z0_+{`s#+TM{X@07Tm>4;r*}|jy0a-A2j!kT^uF_<811J!OaO;}ZFg^YpU>;lHTZ02 ziroE{OE*RO3dEQ|2QEbXHAVHKA^n`I;B{pg~NTZbnah2%U;S$%P4fL&6&mR~ws3b$mTo%IQAf zd-q3?J;oj_zH;d)#GHi^Cbg3Oum%&+mmxk(-d=H70R7)H38{P8ctG)_A6?{;+RNs_ z4|FL-L#M77W+5e$R*)EIaKKYb4;@j7;zy@ANKLt3bXZIma(4jYi%fdHhXu^d9A+=$ z2Zcykp}G;cTBY54T+c9mO6yZ(?jk(Hy0!Z9ZH~5yt`a}9hBejpkBiGrQLa+42CGV^ zcNe`;p-G%{MKn*ca0sxuvTfwrl7vitAgn)LtShH_ie8Y~ge@plnnN955^#lR6(>{v zEHCmN=6R+G{dE`dS`@LCI^=g-nPq6BhLx+lijrRe#U)Uql;G8roaE%uuTF4-NP%9(Ii z3B!qjs>ZOyir1_jBlmfc?(JuHpfn4lqLDC1zZ|Z3?RV{SB4Y(*ef=-?L+~EixF|Z} zVL&#|`{Vp*fhOn&zx!bNX@MWQjxfvmenfXiB)w%N-B>zvcNq&n+lW4^25)i4sd~=` zE2}2_a@q7^carCXT$VK``%n3`hzE1?7TdMSgJ7Grot?G5ySIj@ySwl0c2C;P*ymVA zh1fY?qUvbjRyQ@@`=%X_hF^m>kQ_UDTBxMUCE9e-qBhxj6Fv=}M7@5x@2t6g{QD@` zL?RD1uEsw(Imy#L%bm5r{^@Vjrx}SD?gZ`WkDss3arp1tTxfSK#WxV@Q3&MLbLLFn zB)sZ$;+9>Cp_<%uplOZd(DdKNS8w2}IuDq*(FCYpl;cH=p$?8T1B%$_Ay_WKkdqZTk@?)`#Lnv_q=)syd zhlx*FP)V@VUdiPo<3b=sQiY>$gHLXg` zN|E!d&{guwYKQ$JHCubHU9d(QDko-m0$`EjtkQ*5_eXgK=%On9!*S-s5l(^4?qw*F z)2){CtL8Pt)vSNo{mCj_C=pEXXW`ZBkYfYj1(zJdV|kGZrIiK%`G?Zj`PvS!XUIN* z?`i`|buuUJ!zzje2J+mT`)R|}qMHDtrE`H5uPI93uTVV6j9+t=`aHjtYQid85z&cY z*CmS+@obAD04d6M6qL%qbuUaLGW3T5%3YhJlL${yc;am>)f&8~&*mOLzsm-XsXx)hx!FPdEjk;!= zGb^lL{7nP9cf3p5AWn``6y05PCfV|cTO_%A;vbIa?B4JL0HniN554w5s@|V(TSw7+m;1H-sJ63LV(^+e$&aO&0nM}qLjbd>_2A1(J5UqL`i;$h3v}?Z z7R?5K98*g*`F2PcxnFCLdd$!t-ARp619O1(jH~h>yMuUlT>k@c>wPLo*XKdeeS<*n z-OGsn?`=j@<9N$|BaD${d3EOYV{&2m18om5JcG@8sha!9ecKeqU|s}=k^E92hJ1~lkm&StphmVo6v6)(swH{;m%um z87(BIy^5;lh9oq3MAQ8pvjTmipXd^ zI2vE6HG|saJ;`Q`G6si7njD=`u#LIAKrwV#-Ez0;s_7wiw(l-kwz~-xwQDYrD*Z#$ zKov!qFvIA3l{sRhz9TYJ7thnCFBTL?YDgPZyB1eH<#uBJ%>6OWYt$HSuO!-dDU(NS zgZuoDrtKs<)G{@Q=dC+PMs=umt=nsR-K>2$+IWZLUqpOCl`P)q zz$b2qc>EZlM$l%`bbK4|ym{G#TJh8$UF+8{CO$AWj4BdCeMD?vkrxkuzrP*)Hp^Kq zs?+vk2@v95V5yUM9() z$=OBxjZR%o<#VG~E&iUT{Iu@(sp7eOMCg322NvKBaLJ%}BWy*3iuni$T}EILun^p9 zC+eELKHhkm?Qz61rha1ey3_;7@VT39@MgE0Bec;%P1PUwZs0AlCk>N$g(Wfkz?1<9 z*>XMRtjqA0XY{?2%Gvh>->|!y?u_e?iZqne9hR9^US8}?d0k2kehcks4aLz-ci7$y zTz%?&=Q+LZcXyt*x_qBx`{fAATItUVWid+9@9*#FhDu80FLT`f#{;?o47MLF-5%Eu zM_^)`Bbv(!8!I;_v?78r0g+RVSr+u}G@90-Ykxj46vS6r9VwiSriw~RLIcqRQ-?|X zI-COFcHT(4o)P(i~Zf*1tJ$&xAo<^g%zP;G2 zeGV24W9Q#8nI+eSNs3+%zBSw2lnm7Cwjo*}Xq)#BE#8CHf~TVG4yU(5&#K?31cgvj zus0H1p?ig4fJH*Kf2IBYH`l;z;W^@dJ8y{m#Rsf%sYsjg9+lJkb5hb1%1rPwl_W%s zd{6!}3Q?pCZl#|E7fm3`vn=SmY>Pz-}^i15kznju(8#T zbjgsV!i~82=%nYhoP(8&v3db%5$Ym~`G5t~r2O#U+`=c>?1P}n7#xP|BKte^jO7}W zmxv_jU{3+_f6sLHpqvRfp*Im!9&df!rau{qd|W z`rQl?dLEOl$(togmpVH;bBLm(eD=!a3 ziW)_Hb++%ZlpdL^S^D*UJQ$96(9ic_97M#IKAJ$ITBRnPpxpThb>z_d&%>S3{BvB$ z@%i7zY-41M@9rKLwh+zrZ&Ii2c#Gu^8R2^b*vRn6FE%^-Cey8N*kf2{GmJLV`|*wY z=)I)wa}(~@XiN8K_TA$jV~@3t>Fvap?kt48VK!40G*nXu+Ja?VTS5ioh0s@4Gib{$ zY>9iY%P1B;8BIhi8fVxk-Zjr`5m<+j-FQiS>g{;geQ-9^5VYfB&y%M5-G?B7CT7nm z=Zzy0mqZ-iqvBC};A@szexAz7Hs4kk22-+&&)SwrT8XqE&xU5T zFgFKmrTRbaYo_B6UZ&F&&wp3&XH9GEpQN-pT6VLS#qXAW%^w_r$5kDG%VRe;=i$%D zz4l4TjSqvCtNzEM&)pJ6rhHBs$?()Ms;qB5d6~}rBXgj24*Ljn||FSP@_cr)&-agKV%5y8-Ne}#L<7A%G zQ)yR{254#w?DHp!KRy1E5N0qwai5%3DjO#jWojPYxT3|6PO9Cg<`~Vr@R;`(Mtg)Z z-(S2JC{8YF1J5jOLnwNVU5?YghqgIUwAGdPR*}3+?8)jTbjwJSbLZ;&v7yLnJ4d)w zb9FTGgRsxGW!=9hKs+>pA|D#-WVrs&zex8nxQ6X|T{7DHz);t3+_aP{mEUk;K*s&_ zoZSo8PQN{5mQMFA@N@s@hfqj3=aP&3w&^hQ{&>!dIrHa~;_B5&klO`c_F_eLp4XUj zH2)#=l;5;t=0}`jy`@1IV~dS;Ph4=v)!H z`t4Dl9}iaG-ca?q=JSE7m!9p7E1tB3n2hnjCk;cDKh}z`*2$YsJf$v<4`|(!Nz3m+7Jg0y zhyM=?kjL)+t#WJR=abM#wdMX&hP_FGCx@${WTQF`Cm439;FHmMwCPpKb-I3YwpD9W ziffPci)eq8hWLOlMQ^N%fPK^JTzTDR3-jZn-`wGWt4Cz|+0o{2qyHXwKd)oo6$R-1 zTWCNFSst?Bf-X9x{^K)jSc88sp8~Z{o9x`-@FjS)P$r@Ew7f#A(E_9Ad5}f3)7_Cr zKlsV54+a-3L9JaxH3R@p0GvCYswApLoLA>~Z1~J=-)!k-AJH4(+HGy$=7mtn`->M`VeTJHjNJ?VzYAgY1KllkE(73u|QYJ+4?@XdDbAKzhVTU$v9 z+?Bvo+Y)ZZtImUG*9>NTgq-TviJal>c9iv-ZPm=rS+x0il*I_)VaoR5?}s;`+1njk z@6<$DS)Ct2Skm#0=$gLSbM0BMK#)f|4e{0l2FVBF6S2_m1ndGBQ|9#gerOffI13-^ z)HHwOmRwOpB{A|i+0iiwG154QsoASxOj}%LdQMe3?UwRba_J@E_@K?akbs9fX3 z^}2%OGeW%ruJy8A?&NF(1mw&QsdtuO#YF!SuHf%m9$wUAA2z%9Sjd;vH@NkWA8o58 zh~e|ChvzWJSek(Vqr!x;v@A|C&yGM1UrfH##i3u7AvV=0i!8r#jWy`zI(ZLuJH(54 z&vck*__ic@uU+!)MD8<5Zt9bA`X1%lsskY9H(N&;)w;gtu#k|5FiOi&b9ZN5V>bV) zp!;5M#lF%JU5Z`_gP_()_qR-TKm$s#+LusHls;frnjvd}o^@qSih{jTyD7fwaV4;B z!{0oGe{4~x4OK?f?%&Om=EFI=jjp3W&1d8q_rrO@%ZG>g4VwMC99YX0Z-B=LnX;@u zNSK-7q{-=~I3XG$kbpg4yjCW-x-fyWLStTMv{XV&kvAeDM$OwG#S@n+d>18E6G9uG zK^&o!7N{l@Pw&R^X0P{BZ_RzNJ`esAf|LE))=*Z<{rr|dNU9ns7cv`ciW_NtIH{P4 zU`aJf@{=to<<|rbRv95OSR+AcHUxRD1?ah>kX~4gN}qVB!^@aOBSPEu5bg7l9qJjH zFKsHEcuS87aS3u54Mq#I3bYMK&Db8VAvvemr;cyoKCn%$jBid5e&OT zf+J+jVV~wt*ozU$hC<_a&~(wvRVhn+qi+`fZVUn#kN3!FshIsep!`2n7H$!z91r48 z&`z`=c?ds?IG6GuKuWKM+z2={Hw%mqL(>`H+w#DdB6>lL=DC~^4Ydt@U_x{?4kHF8 ziXKY5$bzx~?_sWYJ$o?a*i{$uC&t5?A9(=l=9QXOJ+W)ZGhHoN<7NVdK>+JgKK@wq zT)hBg@q!Y=qb;PU**I`Ula0q%yppKy^U4*K!|LqmmY+f2VKyZD^clz;MMT;tN3PKS)R>k-Fs2O~-tstn z-0&bBq*vVy_x~Qtbq(zjR@DUoYL|qOgFA!dcurf5Z{pDhVy(gQtTD{@@Ls5fJ%KT# zViD2C7u<7!5+IvzrJk+_;rqd0S|bR?780TVqQ{puiogn$B@GF4QfEt4Lzbksad$=G(sLrH@m{Uw2fai+rq3m7xNs3KBJ z56vPBamFxv{SMK7 z^;)hs8dSAeuf@0;|K=C(-@qNGvM^ugxPfoib|4u28JQprGJUM13%jBsGiqE7?6mN~ zHzMije$x9ca3#hFUc|Gc@xas|Ojm97ve;`4h1>g~g%UT1wPZiT+I1dkU;1r}P*1E! z!Gscl(m~eE5!Etp+6T(s4lxZHWDKG2$$qwho`Mf67hn93D3x)ECvXmNRh&lMgR z?E_OIumgxr<<9z0e_wdFpuTEnf$O>_-Spj^UH9Cm;(uC@^nJL4<9Td+0)CoG-7f9v z_h&8Z1}n$lkfCi47#N4tpT!_i9!dFiO2j9E66sk zs@=X8<8k;P)TN?f7qggDYLOk}8KAkZCfWaMo$R=Nwka=lWML-1mE=jmqKSMUMqr#r zVJv^65<&IXQe66H{bD+I09J+H%DEL*CHKcO)-%{sa+qN2c}Ubzv$@j88qs42Fy0*1 zZ>#y=F7&@bIRk`@-Gn2J!natwFfcP$=W2=|JL`BBMuEC_d-aZc<+)s@S)rL>5cQrl zTaoPhi_GL`Tm_qHT%rltVsB*SrNvM72dTTh4Q-&L@#MF+aNxiUa=zrhy{|oZvuv&5 zeJORFs~>Fuvgb~IjRb$o=2gY<-crET-1O>U>)I#B5iP8~^JQcmOsY+nK63Z9R3+Uz z29ezw_w8#VdMgX`q3PSQMgTML`Rlf6)1IKoUBnTUS6gQ>99v!A~}1N}HPU zI)vh+C*KM}kBT#b=%-M~c-t$v`Sg~#>_ zantnm!fg`=o9W*PnL?RAwp^QorP&DbWt8iVZ$JN?hv?Tk+{KMjvH0WcS8x^!Na_+a0Tn-y9$!` zu#y~WR<|oH{fweLrqkiLj*mu4>}13s-f=?J)oS%CA~~sjKd|j4SO? zL8s!Iz;Tv>7W=TiKHn+N<&(~Tsm2}-qZ{kL`cW2|h63IX9WEA^feY6Lm=?4`?*~zY ztoFnu-Fvp3CWp?Kod;&GUSuky@ftYty~PesP%#F&aOU0b_9tmP31@!yV#L1tGSzJ# zp3zTc8Nqmr1Iqp~X^ z%FsATgD6_i)t)Ry=;DJ!ETkFvjO6*`?vOATN^N80xdDh@z{ZS@B1(z1(22D$^$KR~ z9j7)|Bqi)oujHNfLOIjkqSA^k>RGBKQ+Hmz(^sL38=c-6TK{f`bYA*5&|=kT z9A+Tj>TOPpz@q9E&#_8?VW=k-(<>jhok@FNcC_^;RyksA{50hJFsx;d#>D%n^l`&b zCZis(Yusw@a`!*=+ns=|jTAYP1%tHb$ zAg*S*2UBgV7d$sFf6F|Z#`uFk9CyU+y7gfWGSmhrC?IxscUx4G?%Au(7T;~9Hez{YtzJ|oMvwL&J$>TV@cAk;2~TMZu=vu)I&vcp zlFU|6j_?wFBHN5@o>YL&YAzO!up~TNshN69!=uSIjKBl4zG%F;Y<47|-t&rrB;!8R zZ>6w=vxt29{r?ezAlhjUugQN3AiqF+F0yH?2pxS(FW-m z0;g)9ZK0^OT~2EL;V%i~&}y+V6KfnaVl47(dK_gUSue_=Rmc7-nc3Qtk;5%!Urc+v z8TFOd_4H}cLuhCny8>UKwd{W9g9 z1Hz%V$fW~S_yXJ(t)!8(kOTZAwu|ZXPl{gwCL5)u0)~sNnkJ!fn)P z+hFMG8YIB&`4@*L{7bsfKx&hQ6N+~uR=xB;yJR)Qf z=z6G3RjFuFL`BRD>1sIUwt$FvG%V4QnU2gu#enHbVm2if(zc)B-)2N1f`v@7giBDe z26zIM!La#O&9T%*Vf>TOuj5{LV$I>6Uk*yIXTH1c5rQVMIL|SUl@GmI^IX^NaFM4s zCV=`_ATcu72AyP5K^%%|1Um*fIDm{wYc%NR6*~{stEgdm>ai%26>2$Z5~iwEABuNG(R`Hxc#N|hZ8~vanPKe|amRcJ z+gmWMS;2d%Itf8n<%<-GgV!s6<`Qq~YE$=%vs-}-?0t8F^!jR3w|R4&ykuoqlYeRU z%XJ0u7T;;AgIJ<|?_u#9B}Z%-ph0R=l3tJigB{~1;>e`ld}^<}n&hQ-KHT7eTeXWX zLvM_Ai=VY4H4jhCg^Lx^q43F3uP85Cz9o6fO}f9*9c z+}Sq8TQ)BHp#$5UzeX};kg=S8@rM6qB@v%8Af{rcOnB669dYP-o6aaKOrM?dvNlGO zJRu!3vF1i(D}gX+paV{&Hy;(afkh7ohR{8GI3vMfo*&5fa%igE_}; zv>6r_YSua-2;3k=GAVRqa2hPpx-nH_FAUE>D`6bvBn3rikE~&G7=q9y3>Oc0zK@->Z6ofCGXB6@N(W zqrC$!>{s5lQwr}_iz^NkYyJ7$6$6Xz5!B}q{s^X3YeIumwi8HEO@m^2tbc(etKt;5 zFBw6n$WCzfeZW0PQb+=>UR1J=q-yv@sG_rPv5={9GH!9ti&1!~oFZeoN%A+=RiSkS z9G62J0ssTuJdg+}HZp9i*Eyw1J6~T&k_P%fDgct!vIv&4vn&i;i_N}ez)rnbV*mXx zB(uC$UA5dF3t%pnBJj-AYVh>~0;fuX63jtHY{4QZOTV@Z=${%f1Q+G7&k!K78IXYP zpcFSwK`dD`uBdw5Kdl&(Hp!bYE}W9!5jh=$Cvk}WEF=c>niEsyh9NV87yo4e(>+k& z9dJWbOc7LOs-IS{0U$9IIti;;C83n`fAVLWAQ{(I{x;fV7H<9nF;|AFcv?Hk5?d?R zSShm@i-$^#lIOoFx?4;@>h$sehD9nRP;_{cS9E^F}PpC<2(2k0oRRcGkaS*r)zK1to0Ab<58 z#3e&C`y|gCj&-vdE}G`n zk$SauuyRICjdmwd^J6y-Ji~_jQMOg@$K%=gk@g=`T46#0f)XlNC@3h7<5!=LGlRxK z1l}mAri0ZCYVzsZ)ZX{t>ekH;i1PO|jF6eV%Pip5p1RE7s-JonqP>FW(}u6vU;0*y z@3B*8BUm7BnMrY6Yt;bn8aza9Gx|8dZL87=tkZB;`1woT;;zV6HMcFJZuB@ zTVUJ|A_@*=aXX4P85piJ+k8xlAo34&x!-Y?6~AHjK0|h1eNt3K`vos{Kx~t&0f56* zT8+!zPaA7~|19bb4bZ1>ha{bqDQNw-`6jiy?o9k(++qr<4tNA-OHfz6L$!ghnWzy$ zDHIS#Na1kK%t4xXGR72d?{(14xlksKEsi+lcHK`r#z< z9qq8Y`O$CJ<5XE>nSc7ERh$8q@ydU;CqnQ^gKN)RLuHd0!f%S^ljO6lO2Nh#PD6mp zm+Ko?9R)<9`Q5 z+i{(rCjl&iECd(U3Q{5}+>MxLhfPtTSHG$L>OcZ^%&ai=P=FTt?>DW!+c}+I5w1CO zf2bm$(r3}y`_IPiZp4W9hu2Yv;iUuK)#ilp81#woLzg1|-Ytr?S}?xWAQt5WT17EtA%Wz###@-F`>khYHd zkM}ATiBX-b89RO{y%8@#=$)iisa`*fp<^|T>iqTb+Vs&uzKe|QQb%k zdJl05ap3u7_mlqS+ol*5`4X6=HppwDz<$zkMhx=?w2FDcj$Mn$&nE_b&D43@UgeSz zPZd8ys;&dfmHU(@RfO?RPb0oQDmfKUP4(D(9^s6FqlrG==c?&`)PCIbw7un{luEE4 znT*vct?4+Wd0a8ih4)DT{o40G4OQ?3EkYwVhqJ3ziN0!lwnkIxj9H>l8P}v$5zN~| zjmGQsXJlcEXMm&&f?u|Y&}?$^VMu_T=wXR;DR9HAQCk00i~#cZh%vGVr_v$_-Jm2* zTPYeC9+|O73I;B^7$W5+lx=uvHRy7~wi6`q2xEAv&;$%E*c~oH$tJRR@GF*|M3vx2 z#M@Fu0NC(U#USw&*zb$`sw&Z~V8m!N=ny;g9EwQkSO|7lMRip8RHA{p2qMO$`T5jw z48n=vu!RuqbV3p-FpDZ}cozQulu_b`Iif|s#cbmRM_2PH!)5(syUofY7m$o% zhen?+2RE-@Y!RlW?sE&At&g4NQc#2ucDI@%%@UyrbD;dKcW z*LnO2e)u&KuE<{8n2?J)*9eTnt&o6Ii((_vni&`{Qqh!UOw6>@WNT={`7%TwLp*EmqBeDqnj_NK0IEv;zEA9 zfnzHU)P{nxzvxq73(~>!QTGJLT0yhT+~|Tw7Kyg2X@3BuYGt#1?80v_3$+sy7P2L{ z^&tq>$93D*jTT%l@Bai6h=VvZ8+z;}Z%#FOagMxrPY7*wwdrr)&hHSCl*yfaFY49E zk>*!pUz}0K)hXs^`m}LJFY&LIT?0{jKoU6#zqD4MSbHw}!Z^gX*=yb9aG|lTXtndk zi^3C+&(2B{)JCjrr+HYQng}e3wchl@l?H$UL)|BRecwQdW^KEE4OrVf!nG3p#V1s( zywhCky`f!I;#^x@%-?vFKSE{q(15&`F&iGvXxj@b(=eZA}7ZL#LYyl?(=bDPF~-TvCN6kREq&e z0Z2He&6Zt8OG~W4MTn$trl?J$wBd?4=5Pr}lB$@CFo3$U2Im10(~>d2YS=DWJC;0^ ziR%m~0JW|kAKMKJ!)7poCgUILm~p;EPRYs)T$}dzPK>W~MxO(xrm<-fayhce1z{Xr za0YZNLUv3ROhqD0}KsM$;=qDrkYaaNaqNw(e%7G{;hZsH)2lan+wHjMq;9D zEKMY}H>Ncj5_n4yCF0E5I9+`MJvO=h=YwKyk8%4u`cuxV?nYf_dab&gI`Af@2)%p8ug z{BA0pUrh8IKqnN(16!DYa!jJBKq8D}Jzr!IPW8g9prIQ_!cPyOSaENP0Tc=N+O1Gq zc}2r?dtofjaQ?p`ve%{#29{y_?J9-uhDVnk1s}xCB?)cDK#IcAiulzqO zfZ0?|xA4mlqpDVLW>&45vSd&%;zj^S8|I&X9xXEI06NymKU+>jd z|5~a9i@(hG=XHaX9Bu@%$GJip8X7uuj$J|Z$x*yVtJP$SF@hBIP*Pt(J{_H%(nn!8 zqw+vG@3zU~f|X^HXg_Cqr7mo)5p1b%_%aK-OJ$nv;Y-IiXZ*t3% z$SUt1di%+hM)Ov&hz?N$roN@#DmO zdp)U|3`H?)JD#iRuaZ`BQ)#MP>qdBM?=SOzb4MR?bq-^a@%=jG1Ls5>3WO^75F?{v3enL1nkSL5H>mLm9aQQbjB`}BN zct{0xIi`H61PrzeNY5^7TZE5gk`hGO0?VJokhJQB%GdhSc8YVb#e5BMl6*Y-Zc4+R zVs;_gIR5D!_(|EAt`R3F_g@!E=+qU`XsgVW}d zr~n$nr!f-Yyi$Z=Egi8korE*C^^mWWsF!h&+!&9q2)9{5VnCOk|IEL-1;-NJ2uT%1 zF~k-9zH4mH{Byt1c|OIKxJ~@qJ5^J=ngiGC1J&F8A}ru>9!_)y(V2nYvXv5h=}RP| z?jhiM8-9Op*&zyeLVW_6#`Owcjofix&BC=`tp0Tyt~|28Ihxw;(7F0msrR^_C_&H{ z=(;|Y*_tz+|AP=<@@t?pRxtjLil>xnpz+FHme)K?m2d;a2?fkx5pn4*L<)hx-36tovdWrWe?httYkt{)ylt1F?<$u=#kd3B|ad7GjN; z$d-a6bfc?TRS8PAY?51nN) z27Z06lH{G4a15y{ub5$SVS>drqkdrc0ooO?<(3Gm5m2Tooe+^xiaEL(aD(wo1~#oY zq~@IXpUi@)ryYFj7wcVq2-Duc7o$p?y=wxK#9qE4yQp+*XCHqIlw~Ndy%bkv)f!kh zbYm{tNQ48jnslBeVkO}EurIRedpxm@{05Q-Qk5&1#hAdJk*+ybok}cGE=-pE?ZibD zniq*6PPv=VqoC_zzDevw>p885(W5AfclSRqGdB^WpG`Rt#OEW`^2;SEaUW~S-yPo& zcyA)(O=F=}|2AQ^@4A~o6BAA#+l!F`<}?SH>Fhnr-nBZ%7JP>_K@)-my8^EK`1wX$mJauPZT~eAfW0=oaPRfewD)qruV6QS zl56zFX+7E3G_~`CT_f8ON8|lHR<#2PTG;Ptf6*9q_}=~QW%q=VnY$|Ak~ZskrBKu1 z3&l*<6MNL_h3t3Y;WpvR_tDQIYT1l>{4KsWlb?tsRSkn&jai6<4peKsJBE{dm-u|$ zRsE;^2U(xP3ieZknL#^#<(!Ht{H6&MU|J`8dEXJXt!>>=8#>2peP6zRo<-e_c_R|5 z*0IEeK>^h3On+LjBr;~ZLzHKW6FSQayfm&~hjy;q)ji!^a$b!O2~0$5=nn>)>-EuY z)#F|Xp1K{HrEprYMs9sw)OtP0U^Wc9=r}Cz%@H`#3kmpOCp`!m?O-PKH%%dL6)?T{E9?y1Qm#oOH}NZz#J z>MQG_<$Tvf!V6ySY-e31wz$`z-s{@_mI)FUEqnnQQ?f#>M2^6Zn{~yZvRcbwYDD+TSyeHvB<}0>ICXrYiB-EryOOP~QywHS2O7&0+#Y`cWF=RiAADWba==(f~ zk5u@-JK%(9#TKG}8SIf$K97x9CfA$%M=tU3U@Y3(Vzdo(h1${cz9w{97#wwodQFLs z$Usp+n@q_w-sVLUkF(LY81+a=+2i-J`7$&CdiD;}g7_iuW%ncR!fdLLS^fG2sM(5^ z)`|&b8!TZj(M(aL$^~h-xf?xh;{+;cq`p+ThzH8oi=dfQ7_$0mE{;ggrs?`p?Y_ra zuU}2`5U^wke$WKukbqLEVw3y@990zTZWg{y^TMR+#~)%!X}35dlhHF#*=2L_NwVn= zWM0KFq3W=t+6Q~_i7>=w2Fer`b8=_2(cPLujR z15qGQBq!f#Z62^2EF>vKYTK|xU@HdUPbs<~7#93%C%pPC02z2xmPlnvG%*Vu`Hbny z+xB@x*I8TwNqQbtm%=c@%GlZf0a(Ba}-XSyr zsgv<btet^bb})lyz!!f5XlEjMY*3pR;=JPT!HQLC%(+IPLas{ za9=YbG&{Mn*-ui)9;0b= z7NLX*1~z=8z^%9$qFinIA_GGfr!UX~qCyG0qY%5U_$^jOCp2=W7T2t&<_5iW?-FHR z<|=Uh7>>API@RA$B#u!i9L)%BO@<2Ga+~$~Xb9XN)Ly5z68=+c6p#O{PODL{;6F|Q zw|u^kX+K@iN>stxzTJg-a{564REG*k4aL4gE_jU@F%KZCBkcJE4rBHFP?7}@h} z`wcQ}w+V1*e7|rsa-P(BTn}&7-y<_M6MWM@S~CBL8SwcNbA!$CCd2i98Q?V?BVD$i z8O3nl+9B}lM}unhlTtKs&F7U&{h-1wUXd{tCM*&QYzfyADN8pH2li>uBLECNU{UhF) z9L+fYiYIOVNFltJTM7Yj+hR+KlQRc z7Fd@}YCo3Ex4yPpGHZ?kSbc;lXp~&Z!cem5tGlp&C8CMX$5l34kfZOH{kY22#`Yla zD?$3@Oc7PlYijn$truzGZ^8dk2l;5 zsu;yZoqqv0-=gZbdTVG0mhb5>=yj~&E?^x`o{ zURdd{?^JLz=C7AGBSm7(wlmkc>BT5H&7cUG4x2TWi#SFy*ApU-_bw<7i?Nqx*0qf@ zr5;(epdum@PC<7eR579_pM*on9)fWEu8pN_!fEVSF3!DPRT(_(FkZKqbO8-yb`$xt zOP|dT3slq|@gxNAXaaK~_LGJl*{KqKKS0Tcz+>t7_MSX)K5Pa9MG-S*e-GKf|s2oR5imL(FR z3BQXmgo^`9H`T;Vel4;i6EH-=qLz%$lhH9^9BFQ_{%9VCw&YHW;yfx!LKB0a82Qcw zfMJE9t{W)~%46at!-N-}h&M8(eFpb1pNa;7|k}&pW{iSDxASNPvzI}d%wt~8v z(M}10ueQf?zx>*QVjkiuPc%Fy^LZqv_E8U7tR8k(WaziLmt&6HLEGWLwsM!wGor?~I+|UACJu2Qx#&6WNs!AyGkq zX}B>FSjGHAU?iahK#ZQ0<^XC@s<0YN7jXEyVQ6tEdZ>!~JniIXc$uq zKPYk$dl`|v^0ix$gR%VSD|3`{?{B2b9VXGTT-#euCb^tfBR9G4Oz>)PZ16P6UOP=H z*a#n4uN@jBs1imFDB0IBuIExq0qxIER)N?YXx!^=YqeZ^ZyI*T^`ZHyD9#E^yKt1b zV~WZrFY0ikD4DOveoc-vQ4@ zU(_Q#Cj>&C{l=njV>dT=uKt7{sk4IhnN_DR<0l6DVX^0b+mCXlzDHi|ffm2{`n9BD ze{G!gJZlvJ6ViWB=+XEv$F75yucPlDUA{&A4=&_*NQP7V7Lj?xe?Rxr1CII6wZJWU zLlp}Cj+anM__QOK{K#)BzSODOSSNvmyyj}YzxbilunBC#S`y^d$bdXM5r|?%0SaY+ zP>SgcF^Sxsk6;vk|B<6N{WnwZUf$glj2wFfmc!S-5zc-2s>$2m48SQ1{GDRx10tWM?lA$R5C@e9?BB2s~5~EE*CR7E`>SPTn#Ri~Sg_EA^Qu4m=>1 zp3J@{7`}nqr^Ow6*kq8E&ykqLR1~I4QtTR|TE!t^?3Gv%D-scW&LYl?njlyaJDAzR zx*t19R!G&3SegdH9Loql35e4`uMPKw2sGw_jfoKtru;r=NWhLB5!xV~!K-V@KOa<73I%|( znq9jvBc!DMu+T5)UHg;}H(!ua+PfRu+WO(Uyh z_wp;%er6c+rsn(O5h~#e(P-AIf!F&rDfg#{?RLu+FUQ6{D-rm^D^3!o?$?G4RRjpI zHYUb)3b_$pn!XUnuZCg6650PoSh7hLk+4YLy#PbD@biE?cB3<`lu&0f-8@@?^&aV5 zI;(yx&tKW9th;aYjW*s*#qcMWZMuLbRAY(!0qGSll3wNz$drzzgXbH(utg2A6pbl= zH&-dPes@GaWv1>^BJT4;buXz5FlgsTyay>u_DxD_cRD)|s{LjJcSc*xo|sqy8UDMr zg~J(gdg=A_*4nVc1L%UAZ^ZxBWnz2V|Lgr!gEQVmd{V-*C-nCpN zN_(on2Z6dguWteoiu4($2dZ@FEIDI{bb3snk~-J_ zR_km~3}e$-a>FgVkZe>OzhYYIl+0$3OetwMTCK1&GyybR1+`!&5gM%I0JU*W4^D}h zc($i$1Az&uoh%RppQ(59mL!rHt5<>Eh-q3{TlI zLv{vc=axc$LNT)GQ-pn69kqX$Nw|dUic8CCR-2R7QKjCE{gYWl)6WK{t|{k>Bk9$j zSeY@OR9fnbLyVO%sKA`X+h7W=$NvF=2Y~H@JF_rbf>vf%uIs zRl%=NtsQ4i%Xo5COQH(PR!;&3zPjd6OK&i31Z1uMyRNw{r8`-N z941YKYS?Z5vQ6W(DyKj}jWbxK%`cmAub9b)O%FvKqQT0QHm8;uae6tDVu;~Z<$CO* zl;r#Y{^<+OoFn)R|9U`zh%{){LT$G?I)J&LFOoPTnmFr1Sla(7h0wqoozi;;!sq(q zYs7hC$5!x#LAo@^aLoJsO&P2MlCOOq>{9a`I#u5tGPb(j$s8mhmWd8OKyx}~Uts|w~{ttTd+&8(JmlO8*;CyuY#jeJbrOPH%37^+F zEOV*4tdC$+D%nZuQQr`g7~XWVq$0fQae~W1r{;`Po=2N0G)tvfH;4N? z$*!ST<&6>T=Uxf z?)B7A`Mjs+YPWwhwfhk%FkCM|IH{B?$sDbQ_Yof3@SHE4AUy(--JiHE&Dkxt+#%9U5A&aIu~^FTz8(z%#7bkMpTdojz}T8xE`;{dC1jPFl`?AYy96AR1Sg? z1^;$&J0j2Cl=3o|+YSHjDsR|m;k7?O-!f{O-7YE=RG@n0Iu8Wt6t`N5qmWhp&~SXa z=%uDEH1@K_Mz#)(FBA*mArk+8FJ7Q-dm}78&{k<466AQY_YZXEi4qx(r0-@L*%sMe zCHzVoz{>GBrG_e*`qkU*aug9cwI{`i&dfEIQoHIu(Yh9OH_4Muf5@=ExV8RQLg16x ztlNsT2$~VFSTgh?Q~(4izeNdd*i%Nci@d*Ma@r4tn=6$Ij&U-!r$FAd9&C1gjpc*b zQ(Hc7xR>2;1P#)hsGJ!CI$O6M0b#N*2j6eSn1ZY+rM6}Z;hcV{sq=SdP5B-|m|e6` z5-xwy>%LyVxhu<(;CjUc*KUMXV@@7bCH9OR*1D1Vb{>IY;J*r&Ol4?kJBZWAmF+zm zyCv}Ta|hVX+h^IYu5@{XUX;tF4RF*h#}^}hggEv*IJT_*#!aE~P7)FH!Sn6DaPjGM zV+KffbiA!x9X2L|SZ)n&STFwt7uoK3tK!Fm*^~peiunz@3qm`dT~=7%rUy$dE!4k5gxBpDLHl z6i+Q@1y-gyo`)hHs9de)l8a$V%Fru;lx+0Ei}F1zIlwE-?0V!e*{{KI`TT{{t7ra2 zqWFbt!^|*p6Lky2#X1IPUqLLUsga?DL18V(@k3CsMseaOWdnq>7mUzrU#7$HUNZDI zJ^S8ZR!p6coEG2*rFWg-BPGi4wDzB;an)j36^x+DPuLpdyF!?>o5n=SL_4%@saj^_ z$S(%D6bOE*dvcdcSqUnBmiWH_8HW!tiSkTC%o&z}eMzGZ% zsX82mHJF07|A+8?`LLQLiaTDEjFxmGMAYRtODo6$jY$)=EbH zP0uz4S|qAN3Aq15?z$o+?1Nph8H||9ilxiE zbGKPvzW>K!750AHUd&#Sio7M}>gP+XX1Jr08#AMWfb(;y+Xt@x6h-Dgv(qH%pR#y) z+T{hPFN{X?VN9EE0>35Nlx2)hNMr5(uyZ<&fnDv>=K2th<~kEMv&TA2`hErAZ+#Nj zbib5(?f13XztOignoTfu_CC;;k%^R-ZQcCDfMvWEr^pFPNboCee`UUGd%&pq7`FDh zO|rF{a^51%BZ0GJrz0kUPksh(%ITuj`avhu9OE zI*|(+EN&XFeA2_$&ih?5>Gy#k?H5miYvh{FYdQgs{ZgusRL6bh+=H-21|yFprsQpy zNzrQYe0O4y`nsc9lGx|i&@{p9w5jBS3e4a3+u6#x{LGh3IivCSrT%GBue+{B?%gsI zx6>BFo9ZnNx2K%W+d|r&(fCo{oK6tk=PNI?_43VD@@m@6^qLHr+Mh~J=eOP~CVCw5 zMxwYbR&D2H2c`oX2NI!GYm#D%_0VFNxHojQ;lpXN68W+xGy)Ko#uCz;Ur!2P11>MV zwlMF)dJJC#Ywk#+eN)}YxIbd>nV=H$&St>-0pzo!)5iYIB8)qhK;s?7OV)W5pKMtwd6wc@V1(TdjhUKbDY`Kf=Tn&O|UvBQ5O#4G~Y$fTrsI0Od7+ z_G2Q1QicU!Q!hV@1Xc*yuc?fRG$Ncq6i{5jVoH3Xj1xQ1QPzhi-YoFV&Gu$<*D63A z=Sq8gH4avcR&5(swNfeuSeWP)_^RyHePMn8*~*p8-w=~SvFx|v7Ac1*L#rF@1Z$;N{A zTp*+d1zjOd@qHXyH4XwZm^T})xOo0Kwlr#&6t`;kBS2u-SoGYYsafa$E51^?B2g9y zO+Mf|xeVtk1M;HfoJwin=J?Vv4dUMM-SRpd1=>KI2Ofj3+D7*`9oOO&Jc^49a7)64 zp;;`iERZzJG`}T>z=_G^NMd=d*5;xTmSv`UJQoQxVq07719f;6i{YY7Oy#wyYP9N2{*`Igy>6@iqTlG1yON*D zt~)q;QgD4e*eUT`fBwbwf31L;qp*oW%CW>%Fn+l`(8Pok1}@NO14D4#(Pm;1DbE0z zjA${)dc5C>0wuPoCHDl@=G(veL({l@X)`Kw4?!!7e~Eu{#4W{|6IkqL(bNS#(Ef9p zky0ty=`QYV&3q$l6Ffwb-qkx9LmAM-dm=}99d_m^PD8NIGe&oh3zYglEWqksV4m_B z$y1DIsT(f8L$|YJ`OPG$X+Q&15{+6MxqvCzIBL-#ROkf#kIN5+EKOmG0z{YTWDD*+ zym9};09{~HG7#S%;{20kDGvWv8b(L+SVQ-b@Dv*==x`Lp4ah<-s5Lcpr6 zq6Itfqn`QoRsSNwDkFz!8AFyv4Uz~&k|LF)(&M2C7l)D=L^~Eml@%so!w7YTrAL?g zO&vw5M!nBXB9!p3%kG9!q;b4$ufL50mJ1OD4Q*b&U8lbtG^82&55;PejJQ91S%I>+ zP*SMF%?1F06yi774l(CMb#Q|o*YLxVxQ&7aY>%A+x`U-Zma#XgvgVZ_{6d_(*hI!p zlqM+lj+mw}1QfRXwUgHUANR{Qtu@W788WnEd!Fy?y|28{v`Y$ zC|pdg`)xDGE)VcmSDEi2OnVtt3q+ zx`a%rQUq#oh?>oIQgmj_Stom6UC9RW`7~2m+8p9egQR4$*9W~_`TeDp)tA0En)O6& z^8;Xl60DbUn7gqVk;(SNz>LGxQ)I|aA&?Wzz@f?aENQ}(6S`)|l;R}yDr!%U$B`kl z(-N?Wge7$|;twn9XZ=zvO5@roFDi`hNPKj{QY2?PpuM2OVe+H}O+)jI=m9RIaZOkV zdo!on-J-}`kx9(xeB&alN~RcnCghToO4{PK`sU?SVyB@>PJ1GvEx{1^sCIFoMl|7l zoczK-QRpD$AKc2mWO=JJjZyvZ8`^^xYfi-2WH}JT==pZ4jX%#qka>i0Lhlg*iDtlU zGYm6L`Oev69QYe$f=)W2wAmX>~YVc{|-{wB2CbM2^{?93{prQI<*p& z$_bhZmKReIiv-sORH*4S5x=m@9<0AK1X+Ucl@bQctdZ{bN}*!*q*l$$z?&jdA$Vk1 zB!oKTCOFve!Bz|u{f>XZDIOj&)I34c$OIQ4e$0Q^U(>?jfUuV9nO}S97ks%_`dq6) z9_BE8O(%1Oj|%#yMH=rBY`0kOrEu$d{3T&PuQ;B`gbL5Niy)l?4;`Mv1Z{qC6Tht6KbqiQul5Ev_}Tt9F+@MxP6d7B zh|p05o{^Nc#Ja3n1rKET=YBOzmuQJ_j+q9$p~y-Cmr19`6#70r|~j zF?;i+cKqccVhziwaNjYea{qP}SF1uW8~AWXU%0$dvu7ggrwG1->{GZxxEnDYOk1^~ z;Lj)8H1r2x#9{@!%W3@vcDn%5*~_QCgt%$)6x)ykrLauG4cCGKdc@SoCt4v)JD6-tXIFt&b^w!L zA}6Vsc+`YGsjLK{sF6L=ALArel7Gne#lplT|H5-_Oe!F}FgrK9n|TXFCZxFKl6{jw z0naM}FG{_TBHE%rT9y0>2EAi9y9WV{CQp%2EM%EQO)5sKx-iq~NEz`^8E#5I4?9!< zQ!cmbxJyF~jKebJl_FW6b<+~>@XJyaqE#Q>zN0Fg*)-h6@LGn+ zr{`!_aG^#kV2AzMKWKG(z9q!GYPB}=Vip=<(1g^eO zR2ij=k0T1^c6$#C7dFB^s^-`)S_Vxtz3)J{g<^EcQqo((lnA*cYBmA`S@D9-Us_dA z|997&#Y|8mHyW`twDbU}9>E2lauV_YiIsD~CDLW*akp*vz9gU*Z_xDxS;wjo-6s2G z{BiG#-`%(g-P77c3}h0z*Y_RKaG^qNe$k~waqJ7I*UoA>7Dp@+@+H;!bOAfgo7MP! z!02l<#s4IUayKvN3SVc<-gNPTJ<1HC&`>}Fcotj=6qSU3iIHocx#*NX7%q|7S6VU} zR{#1efSj*om~GXtZP_qG#lcCLDc50a*|6v2Q3CCNE&hgx*j}=jDiBe`XO>lYrWEqoANR_39(N`d1p-k#;j+3p(*A%mg-@mz^w?)dwfkfgS z@t377vkwNoH||2uLxfXsp>ZkGK<(;Zg~H$K=z&Zuqe`Lp()k4u6PznS#g684o@d@G zHgh?W7g!w&>U3G`$=0qLp*tU0FXC=CU!v;&|c)x<-qs$?~odgx0Ys5>IJ25RV3Ote%Js-k_uWu995JZRTYq^PEj%2QkCEUk0U9( z-4gls1;fX6?gG#L@$-Jm1J&)_8DKK4ZroH{1@Z!EN-T3h7hlUIvX0X8KqiA?9-w6S z^GCOoNIfr0f>(yBABolKIp%!c^P6wi9mjabPy2>={e+@1s=#FX+Z=tLvjH2Gsv>*Q z0TO;UOuwnS!fe;W*UTx?p&N5%;J9wmBx)p*Fae`+R3tPFuj27+AZ<-&3~{E-P{oQ$ z+vU4s8m~b;$Xw_yQbi$O#<_3p>MMMWp)0!JWC&8@GvmKB)K=F1b=ZjgwEHHy(A~Bj zLCbA8O8igzhCMag85=GIz5Qwh^LUB=+v`ka>P~U!@(Zh=CmbOcL@tDc1X z*NuP|U!|-VcGSJ55%S`0)8XOh$nA?x?e&;Q?Cmm;@b1#N^!9Cu!r}f;Da&pXsvY3w zrE<#QMXfd8l;Dg^9L|A)+H!N87ydWV6zSsHQHp(@*(rr>6=J+GTSvMbg9EXDC&DqCI zg#I+5t-jer;#N54FE9~wA=pfeh~Sf(p!X{=n->3x+eSs1l=_}tW^fLrDCYZV5r%+!xaPoRYb;IUGpi~(JQ>SnYGboKvHYd^4S+VHI$Pm>zC zoM%}FPI;!@gr4{37F;oz_K@U}r6v4|S0f<^`VwB>(NG-*ARlbmXwk#9R&oQ*jPKel zC!OyYcu@s^nX#|9EJ~9z*9%ie+sDZ3otDkG*QK;68XhwY#2EYo7)%0CZ|?j}NhmWKi?jK;*%p;ZU=pENX8hFs zm{;GHLb)8KOfO< zV6NR0F~s!*ya5HdndmOr>7<9a)A=kAi91RTCAdg-Qj8pZWEJ%9#$9>9Cm21$UxYIk zO694gu(r)GCgxLi@3Ogdlk2yAiF{v6 zoX->Z8Z*TWLCzuEtcTNzy-=il@v_U})S|}Q>4&uc4^QvlSBW2Oe^16_yH2jjnrxkH zW71^Xwq28L+qP|Ms>$|qzW4s_^E&@Qr_Vlnuf6tqQ)PzshD0K!c8`-W@=k{Y zzM>xAs<>WGE*#{XjP-Q)(+CIP8~iCZC~TY}ljC;x9XDaXQ zY6vT1+3tC7@pG^iM;{>ScAQBg4-&cJnnEx*=e zwONWu=WTSP!eo=kyY}J|1|0>xd~XOpc|LJY5<(<^q()&`63_6rq$DS2@p7{S*!FmZ zKa`ndgVab=k*F|+HNgo<=z{b}P;oHCu~3K%X_)F|EzS~DeSz!r)R`@AIVhO@?rdm* za%h6a>(6Z}p(J#|OO4{vA?0BjFvw$M4C152rhwOg zM+}7tT68v;PwMTP?9U%itv}Fx)kpg^i}|1lAnj40MJps^BG?^LmrFt7nbHqyL^vYz`MGv)9L zffO~;^Vs&L_YB`XVJABbar=*)t#^bsf<^)L_g8qE_FJ#xDP5sFdoB}Y<3$Q=yM-** zyU#1%A?zG^BXjSl@0i{{%b`}JbV`d%zxbUdxl&U1UUxQyE6nzyatH@!i*|??Vsr~Y_1X#b2O1!Tf8WhhjKlkSTKs&emgp4 zl*Z=GJXQXuO#d_5y_{z%($I&kl*)O_;&0ajgXG#EMS;(de5^oLgO4+kt3GdthNR)a ztZ-iC5ENJGze%W6-Qg}*-S7U#`O@O{{iY>KR`;L|`$9Zp`{?hAEmPOe%DsTOg1+Wj z57qAmX(v^&+eZBojP;<`WPfvnZTIf;#_IF#Nmp)VSR?y-^Vin)cXUf!#X^%ejQXj$229Al)|k-_oIJ8CW|9*RO;21|JzR8WN=Swf_DsW%EYG;TLB}^K-)=_fyn`Z z9RCM-`?6D!iUsgdS6h!a0pWk1G+^Kf2GE_waPw7=b#rlbbWKfx%A)w2WIf@8*E)Oq z>ox0I_l<(^*CP=?`6w`zQ<-!TI*%Z}1tnyVlG2u!AKNe=%`AEz{sdwD2U|eGLzv)h zS6?KP><=LCe$-QtQc;43|IhF>KE3@;00wkEj{Z_`YbQpKU<7y?njCK9II4E|EMS?Z z@fw2-185;)Lew*dJ)r`Z=a!$YUB>a*j}bcMNIV*b+ku?u)N z^cuSo+SgpHOQZgrk*{&aa$ z<|^-?N85RK{>~q7Q}Z>P=9;ghC6Bb{>;F(Ha(hm$QOFU?g6j)A;P>Yb9`ybB$l%k? zfxJB5w4Xr+`a<%@(`r`eWp|9ao^PtAzKrSkW-up>xo#7|0ble4c7xTPa6XpA;yzQo z3bY6bEkV2~{Z;zT9jm!83A(t~s5b+#ROrwld>mY`{lnlmWC6K1B=N2~ zC;QN#(EvR@gq6ae*?*f1NG9xPm?6?;eI1|U<+)UC-gbw9M#7cCXxiUL!_E}=b6(R? zw^inGVSsso9YCLYs-Y>iHi83SR+Iq~+l8vT{ws_`fw}Eroa?q%HnL_tBl2wgLPzAW z@3WtxnxrFB@mJD%#U|?Gz_#tCd~Q-xwZ9dKGspCC(tNhnPxo;(`L_`Y?aUdbCFj4< zg0%OwRBDLiHu&X6ag}?5!El(%{)_|tk#Q-*^J}D#t472`D%sz+g{OmxQBRcY`$@a1 zhULm@iR~o6_dRVJc7!BA;Vn8UCET=9Q#uaYL%ckn5H{WCYSmpwv`Ld>q#}1`FO!-4 z;R4%F>MmR75-N)t*BCU zV-TisgPVm@}!gp+ol!+YfVSORAm>w3VLImgWLuN#5ybqI@_lZ#E<2gdBa4$zXhr zxd{`MO2Atyv2X+=Tu7^5_3WJjc+Wo)x+H=4cJT^5lJ}g(GAyoebVnH4T{7C)xTVKNQ~y&0lk}&;^2!M61=Y6Tgn5XK^JM5N?(g zbXA3Ng~g)@#>z(CF*Zz?bkDdKY7Ln`-tNUlz)I63L)@qok6^W->B+-jZ6kw(3mmfQ z_(4Gm46}`mNt~yL%4KCK|GM6`b?(kH?EJ~?@Ms8%lw|lTg-b$c4RA;b2Q@Nq@POg2 zG078ZwIi`lyY2YC1E2#ANAWB#aH1K)Sl)5z*!TEAm-QU;9G{@Xm`*q|`X;7>#BiBU zD>Q>{G%7;+@+gsm@qlSnLZ+hMGKJ{of6xyHEb>dEhUextM|o_$`Y%OVe$7}no2+7O z%r4F*jK`qqBg+T5W_xv`6=4KNpq%hYsga1r=#+BqwTf^))(&vp*OHmjNE|{?|0Pwd zkT}CDga=skr)u??#Ry!JxDaqBSwmk9BCc!(D1wtA1VZ1J$mbv~gsDEcVn<$Q3({7G z-{g^|z#BkuQNi*)D=c^qY~P=*5nd1s9Ydd`${D|1JQ{}jzg_pEGv1sf8r>`>GTxuW zXxkjlf0sL1kpCU0P7#MHh1>o9Y{bCji%?#U?(EFYR;4+G#DhfD%)G^|rrhI%IFyo> zTi+Y=Mm*XXZoAOxK|xV+eTdTi=RkfvLm>8xb$&TlHB7}a0Y7YIOm@pN=nR3A-`$*U z*C|^J-#(MLK1&F+BCwQw zs^Zvjaxe6@qGDLV1q(M#6rTYj`9&(JDffq+{f~Lfu~0l>xPaOOLE6HA{OCBy!vPc{ z`l+Jdol%;q`uXlJ@YUV>ER;Shs3&|=O1Z{hr|e2_Qi`HcgKn_3klj%zwRVqA#WESm zl{V61{Q4xR6M0GCW?!#UDU2c^2s<##iS`N2OCHFri;Cnxsy@%kNUnb+q1UhY2QLR7 zJtd^-18{^jrlY@AJD)dM-!HdIO54i=#zt6r0v;vCuq>Qw;l$y&Pr~k& zhU)HxDu?+daRyBUBYtxfyox4y93*2&xNLun$Yk(+wD~-% zN6Mnn_^!2U9FvS*Qp@sJDloc_tMS&V@q&ZE^-}X+*F<;J+Z10VU2>TqlhHaI67PFS zrb{2xWZE;ctv44TJ==-CCxpHai@(mH``KJc?Kah&H)%SyC@|lF6*HKCf1IqScNMK4 zs8d1Z(D3T@^(Z0Cv`Hf$Q^fv{}b?2$Ma{ZzVr7Q-Zx0h z@?QLz&e6?z2dA`?M;_mae3Klze)|&?$QR>cnaT%hEIks6!*Y0pEq70D=f9Je9T$CX zMaVB6dmxX}F6hKKx6>Gj>hs;qDXpN~g7uNdj^~kkS0YJUo%wPoJgLcW5woP$>_y7L z0;cwDo6qe17M+s*eAM1FbGUps*sAM-Y4E)UPJL?kNL}xsx`g;72`a|>aXkyhwl&6q zOH@VdU+ss2p64sa_z|oVl8$7_AH-FM39!iwR$^O65-p)2N&UKI1u02*Rrpb(e6*rl zcswylF_58O2NPS5En-dCl)3hbDabOfsLj7L(`Yt#4>X{XyKFw73~xVCX{D&xJ-+nh zwsm6cYzi_==BdO@Hn_*f9R9Bci-6nxB*lKN{w3`Ort~Ao4#(6EJ^~c)cC0#?&Yh(x zjlWu^9lt^Bo%VXnQfZSn1m|$HM5n(VfbJfIbUN=0p|Tl;!91ZxpNGK(@6@llIM;=g zQmrASTp2$Vd8)7#F1K~|cdmr^%YN%2$)dTq-kBwl|075!!&bHiu|45xC3=3}9hOjG zqkwDzdpf_+W1+$_j6=3kuRTer(46g$BrvE}fuXKkqp+?l3?ss3tDOoi%=#hS^}Ln% z_k&gl!WR~~xOP;6Evqzege^!TV`kTzJA;By3KRl($5^S+23rN6v>zsM)fKsJMkD>7 zkq!{bH9@Z4$H|zI5n6U;msw!?R={hMUzl()C;Q_1Fm@1%XetxGhlbFMuQc_ za=3Ai0ZRXF*05*fatJPKvE9zrLj3%mPLu**sQ%#J!nBdXPPW@=oAIJp;~8y)3H5aP zBo~={o`ki3@(*l97qL&br>mFiiqX_;ad(#Nu1a2Z`X zne)M(1jSW@E8s$>ON-uvP>~CyFDGpY2q4|`-*^}^c}L`9nxIj-17@ut+mzX zceCXEE|3FPj&?M$={wT&7gzb3RPt7(v!KYCt0c>TXd(~8L+JZsi0fQcf_k<`aJyWVCij(fw&%UF)DU}6n@0-WpKgeO|gSa@(yx|I6(5!^i#9 zebf8=VNzN9dN#V`ZhWHV?)rdnUSmflR2Vhyr(jyxaR`MD@A2>B8EwICg}&~$e zWXzNyk9F}f%o%`z9?WonQXnbv#Wosgmhx>`dW2bqssk2)SGh!1?zc}I=c!qmKwtte zO72XZem^kT5_U)`iSdAjq%P@P{pMz5;-)?dod_TjA+64AFeaRz8zzxgCb)`4DXSG@ zinVT@1Xe*xj^Z(-PV5_#B6f|GK3vOvbuCP~kTxH(^x(x}Ak3YE0niP*CtB$Yk>|Jh zCV+lHpXV&8FYg`MHx?%a(BO4tkcASFKpsk__j>5jB1Popka>G%g-kL0 z6<;5*90J{))`~$zw{B6We)1mDIL!eVtO7e*z{6gi2L?siAx<)&4m~9138I!#U5^&+RTBC)pSdH*$%5-^d z)})zXaNhC7CXvt2AM(`hrEKtp#y0_6bb3taKr}*1UP-`>&4msqQt<|&G#QN*a1)(> zg-=BD|K@w-GG_S^o&svE(6kx6)vHZ)QsCaTWM_*cLDfUFic6_UtoM`Qd#l9hk!$Y> z53|+Z6wu*Vs2VDet z)W}m)VlLZ80x?_GB&qsN=)~UdZ~{c#xXDVpmEu8QYDZj0N&0rr)f@X25s=N82T*E3 zW~S7~;5yQLSKS|A-T6OSqS|7f^xJbrw)MVE+s|Ct7Ubo{--&-DAoD-tUv<9`f%saa zZCwK<8gaC5rf}g15nQCC^^gs8)v?#E^PLpf2?02+gM*mO2Wou-biMQl%b8X z%pb>d$mj-9C~?|>M^nkA8=L}c5s9MhFx~d-ScFM$7z86YZ;s-i3L~ze3!e_1{G1bP z>7#2XOe9G|g5m)T$6A}|zK&0+dVn*YIF)v8fH($8hq{G-$IV0j#zno~;B~+N1>`=eoG8#o zgwNlO$mOUjuOV@gq#xU)#~_%@P89U^;>Nnsx!s9#{!Bf`iPG;TJX!X%mE=elj^kg= zs`*pJAH(H{>K5Io$Vpayql$D>4}Rjxw9`0EoUuYToET^8Tv8(+&YLG6;e-!U75xRT zaa2bu?4}@RlmHRzkPzTHs8o<8>;}Wae1*Z!bpuJu4jzG`S5g=vomm@9G0(wVS5I_7 zV1kK46Y4}{LLr4G`i(38UY|GjZ8Cj@!DNOu$ez*3at_I_<6=CnazYJf63hjGxK>bwpax1g zY&{e*Bu6YZ{15VBPk6c9KzOZ4W~l%tv`{IsUtdJ?H59Di6*R|){cuT3qAd1_h*d*U z)X)jShtwx*LJWf=s7l<8`a*&6)IVSgWyk4whP<3;LX`bS@bjtQ(;Tcf+IA} z%Og|W6y-3WM%jd^q(E%c#CmlzVzR6O+Q4}5Qz-w~T1d`35_VpxPoppjMGwg315y4P z!xW&N$Nb;al?0qH@|8FImjMRvL0~t4{u(U^sLtyknM!trA3qIX;zZpG65$DN#v{X2 zqb$#yZFdn*e3!BMXBA8+*<1MNf>;-;@ooXv)yptzk&4f{6B=(IRv9WYSwA!u_gW4Dv;A`}aJfHitu=GN9l`3mub zhX}aK=NU*|ch4;On3^J)2XYqz{F!LnUE_4k; zWu`rnq_>xVfsHXrmZByj@UXmE=Hv!aY8st(=ht-kd|fLLbwR>bbJGebB-T(74*OvH z6#c*`gi%ez@~~72VQeK91sCS{us9(k;u8f*;Wr#cUN!O!EdvB44CZ-?1B7Nj40~a4 z*z3~5B`o`VFFUY)^FnG$Q3QA|SpLvkXnH&v7%{^mxM=Z`_&TDhZI>9g;8ku|* z!J-hLK* z<`o2CfVxe=-po{K{*4i%?GLX+rWgN7wmU7~IY(|TEvj1F3Uh|dcJxfQS1RaoEG9%1 zHMu(>qFpGe``WAg2l-Xk8`6;>E`Cb@&_d&YUYNF%MY1UTfIx5Mmndr!A-vUQLyCH~ zXHbD0Z!mK4@IS}HIXX?wXh(_n%$nbm#nlS36_o}%T`qZSU&_*xb2F-ypCgON`pc!G=cmuSF_FufUfTrJ6;vavOk*D_VPzxnfCPY20eDz z2(;V>j}D(W6#cfnmn9hH=kPB#H-0{^a=<-$u1SZ@15%#fWTWZ;|2K^_c|@%je3MuFw(Nn97_ubi#$Wt{Bx4-d*2%?Q*NV>;3F zAaZamr_nLfoc{jUoRbTYRE>pP&R-={?z{0N)MZGSKI6<@*OMZ2dJ4nX zkkcb(fOx&R-*dYQ_BrI^OB8Ek+0DN1$B2ADvJY{WYDwV|#hu(%Ny(rj9YMEDyHhtA zVXV3A#rRT2omU>Qai8aA1T#oj5i{^iNryy}SJum`Y*uThbnO$@7pKB9`YY{y@3>=T zsvN!-nXwZ`SOTOU^tpDz0h0HW)N#Tn+A#A4h4L~6-e)iS$|kW)u#8{lFnjH1g%3Eo z9WF;8twWS%Xf&Rz0mkcp^s?tYj^pivt^)a-?kHSV=qtkI^)E(Ias1OS5z4P%n6+jc z8iO697}E|7)L{w_#MR904`B@E0hd?QtXBl%ftX~3m!YG7ttV*vIE6@mH(gQUknx5OY&vh%@2^q@RD&`dA0-2(w`#i+?xuU9%Bw$4=bB;`81ZC}{JK|3MoJ@V zel2$n4U!hRerdIsz-*1_*rc_!Cwx^Z1^Q)+bIN>+q z{@iv0)nU3m4T&e4t@!>uFRiDD!BoaO@iEX^THMeLqu&z)E0QE7I{~)sy}km~hrxBz z3qSDZhTpOqBB{RYY+i~i$3KzKKYr%u0%Tg3e`h_VL;l^8g*aoeF{Y0t{C<9KsJYqB z4H`^?Jt61DzTF$iVR9X+%CwslVq^4)yt(ojjxhQ0Vll4b<2W&udAAn+@9r+=`nVMv z4O#T`Mvpw2j)gM&D4U7pud{zkIo|Qgb+Srf0CU+Uo9r z54Zh8jZU{7-FYjO$#>c7n88NS`QKo126Da#|IGXhWv#IZ0%`A&FvptD4>ce5=#Zrf zR3#M2VEPtH#L^a68U@`Fgl6PAW5(`cpEjhX#=OrinbSW|~%_j;|BNQQHQ?&};0#Go3`a#x4c; z!iKj^il^VccU;u5E||_3&13(-VQOVH0Z=PX4QJSlFXD+<)*(ZLAd3D7WeRa`IAJ!u zB!F+@9WIXezPH?5e7@bY5FhEXKz36_EQ#;dCa2&1SYh_%3?~6V7Z-5PFZrFaBXlJrrcO^(ulRhpNtW=*=?DM9q*gxYE0cON$RUmc8?X*&7&hr8MFS^L{>AzUbx?%OYhZo?VLu~x?T1_7+Y~|4#WIw z=!)<6c&#*DFqGeOtU=M^(jHG|z$-nzmF1t)UJZ{^a)BU;?&tf!0eL@Fap+iilWZ~$ zSa5_z3Vr%O(paMV@hP^Ml}(@dkgd-f$;M?TAw-DHG@);6pbo=us}!gcK6J#P50gcW zCE*CS^;e1@fvufJ6JvHfo;DpCU#iq;ig;-#K9HXBdhb&q!r{qouTuwqujDj!qi)8p;S<`&ok*sY&-19_+W9ABRl}Wu^R<}ZR8E7TWT0O^>#fx!FW9##OUta zPabl`kFmiSn^D~w6Mi*gviai*l_nL1z0BNn8ZtaIIEwm^BCDcfzOGqd!={bvs*@+a zy&58HKg~-buqX^x$RhR{n#?h-hD2YnkFaHDIOOAvv`Da=qevvfc(y3I`+b?vd#k7B ztyf=~6!z=s-8nx<5{Q%&h1`UQQ{6P8@;k4A0YILGy@jDd(VP6nL2U_V6DVw5FJ|P;FA~?UPaB)I7q!=?4HRHPnuK;R z^}2;bciy|~nQ9#adN6i)co#NH)$M${Y}n&W!=)wcm+x0%854`~^IklTe}i8Ej%1XI zifBZ6X?jIzN}jQW1F6gT|GD)UJ*U145jhLi`M>S!@I!hQ;U`0ddz8Lm(2~bm7WQ zZ<0E?XRIA&Biwr9PsyuVv;H%NC{Gu$MV-d5zNRemv<5%jmW_}A z-O;F)l7-G+xcXktNHsn03=LQeCFgN{_m?`rk>nqv5`cDX58ZQolD|0d$T z4I06w*FH{b=9gyrM58DvFr=2%)RgE%h0Ef`i>8u0dbddlXOWeBBc9W+IG&^Wnxrv} z+5Puj&hRZO#cZj&a}brf0dR0rrl3ClwVO`kjva>+$$hQiM+@CKOqwnifOL#BbMb=D}9sEYugxK zx6DE6!0SI=7)p=KMZL561i{NJPr8ougnzF&;kT7~O24-Y8DFlCTxShpX7t@- zxF-Hkt5aVyFFEA~fJc)K52C`#0`zlrtopPfm(k7-2Wi^2Pvp8YLXJ=3PE@x%k-qQ*udTurat=i7l;g>8yI zkTl9L=f;gfu;GqKgl2}2<02?3zcQ&fC=9_^N-0bDii-f@j<*3BHEr3`&QPK!NdH`R z!|6WkxL#OP5G(g@>RKE>>qc0;Kgl~Qg}Gw~4GO7hBUa%ZlDTf7+|KgqKy14^6=a;= z<)ti|pyc<5z|^+GeosxJ`$PYoPduXu9ObFzftG@qkS@-Xpoy?(S*P#NNlCs}-tqq< zLHuV`0}OmYtnXVkE#nwbOQ0H{kctUgz`_atZZiGvTO&@w0Ya8Q(nW;z!aI=8WP4r= zP--I;YhMv9syPE{Lx9QxMZbF=tvWWUpLL=bsN>v&W(-nhj7FQa>u(-;>K|&Y<5|p~ zBa6_uVbK!hPvW=HU_%{ZdZADbXF=Mcaywgv1(k4N9fii+{upkuRx%f9K zawWgJ?1%_(DPRB-xw>v&qr0Q#y2i{r`ww%5ru}?gleymhVs?a`gKy0^^~_yA|MgGK z6wmt3qHt_F;E-Z0I@2lSix%cO&TMbqy(fY)hPrq8wu{OrmG(2`b)%uC-MO9q3Z}*o z&bBAm@V%iPQ*h5NI$!h^#PSaUV2J(L!9$Dcm0dlMHiPuDxdJeWKR__5ucQz!_SD$6 zkPaVL)(Wlj)A^u~~~^IrA_%#P{EuVR^U9 zu{7_a+rTPR@~!s0B^TOgSl|l9VENcS$dsQH+vxselz&GPHjL`?l+Sg3Bh9ta#VTM<1^bKV->JA#K{`#sf`+X?;5QB;ZdAUNBup?k)j;SOF_ffxLS6WbXRqw0eK zSApF~#!o?GgyB~5F|uZAdX#z8(lmF+-y*EP=q>X-nu*%se(Kn>QzRZNnQrg8s%MU^ z{-H8AlYzyeT5i(Bp!@SP67wON^NJ=_dQ>OIl(-|+UpHnig~w*7v2tHto;OIO;FiLs z=g3vZ`E=;rIAg2rq4_-h`J_z8XRik5V*-`HS4&@y_zOqhn}ZM)i+U(T)pD81_w)TR za8-#eVFGjg%SMT2vy@f5_9FJF7^P~r+|u?i>W-nXsZP>SnYBW##(1sXgoG_SY#^31 zD9d##W+mGK&!3D?DHBIi--LKwdK-Q9u2yo$x}_gJ*>wNey!(KXh) ziWxJ8p9bR!78bu>_ScdkEz<-#@GL1-S)`8{@`|W&+t&p1i`g@Mx7$?WPu5Jck3Qds zd$mFzY^>o&f5JvtF))d2AdZzlNhtlT>H?blug_edai5oFcH#)ul3r;XG@O04k$Z34 zDM<>IsgNRwRI-aIik9gJn@2Cd?wDQGXj@9HM=kX?YRF{dM0r*@85b877j@?Dec#mMcMoPn z2NzQ+wJ6CHqC@}?50MFvJ3xfYLD^DU?ALA8PzXY(g35=kPeWDF#2>-=orco~42PCS zwOmw|hNUpUgB@1B3mEheODl)c{2eA1^Fv9T9tT@8Kmw;1UmlpH5(JjWdhBR8C4@V> zsa&&kV-jw-jw|2+hI2z{|lef+RAEmd`IW|j?)_*Q&;`Flq;0pl3Y)5dA^+H zAwPyjS*zNCs!w$ro_&P`QX4UV4K5Fe%ZDQ(jZlX>O1{6eAVv6)A~G)td8=VE8)g_T ziV+weX1zU>R;I`rhk|0HNSc6Sh0|Lq0<9$O%*U>zL|jW*RE8_sMa*QYnHNg1(F`sU?vxN&a5nd;3H9|p)Ni0rqGLT03a1uP~Wgduw*{%hs%)MJM{J-zb zTdi0uQ+nMCG2kih0ewJjNjPFtuWEDUD`|5{EYVbU~4txm1(?FlBh8=!2g~k>L>N?`Ov(9&ksm1i|ly zW`DRHsI|Blu2+gH?bKh&So)n@ubcxPiV)KDde7w+XAF;Bn5a)fT&6dfD76@cOiXiE zMtIu-6x<73h*H!1#iXIS2)7zV3O(2|S`VpQ%$7l43Z`d{GbAdqt|8S>%cezCVX2_) zKM$N7OK2qJRT5i_)0QR+Ko?WYtk@II+N>g53Q(aFqroZDaf6JN4vObZ2r@9#h>aym zrMQ4In=KtgVZK;Y+X`?c>TSJBmK$HER&~&bydrNbIw(}-nmBX2eDX=E-LU-%$-0;M zKQ91`1o1}q8w`SjNwkhGT9tn))FZGj8H372|Qb-V!Dgq^|NX**eYhwx#1q>L(Q|&4Vv?5<2Qu zzEJ&EytMT3ElU}AT0SA?(`=Q?gag`eOHTXoT+NyDEhPhGBk%IU$? z#h?1Xk@ZmzX5lzs^7V7%q-&f2URbpy#@DuZRjFR+fcd5)-2_oa71GHNL6mBH%uOKe zwr1?0Soffy{D6QR4>QbK#)yR~+es56t3=bX%mg=f)dYLGV;39?iE-B-d9%zG)99cf zaZrfpvG!uk2FmLxLx6HsveyX6tQb}t3|ByIKKO=Zh#e=HMFPeQ74082M_tNzJ)DTJ z*ejw?G|2}{_?6$sEfGZlpgltfej>LB7dK1h;s_U|VbY_fD2U2ts|(T)#-K~?kXjT( zF~KNO=}V$8Pl>~khQ+)KbHuGus>8~})><|^lUl>en?^yUpe>cgd7#*5kwm}6vE%6b zbu(>nAc|l@C0?plLne8oYe0`G8C4Lc$O|n(NapWV84m1SCMl(wun1zw9gg!P!4W>T zGZA8wy5wt`oQ<*R3|!{{70b z?YL;p1WDl<2W|%jq0G2Vafnts#X3E8cPgd|L>$hQri$xMtWLhg@M0dGJ2u^+TUF=3 zlz3V+8a6PciSH!d+)`afG@f{BI6PjauHHb0o+_`+54oxpE{C8@AtG1r>>oDTBgsBP z)IpEtMWy+aYMTzaW8nndDcA>L47py&YXTiRD`^ZvM}j=e#znJ zN?EiVP7WXQKA(floX(7w{H3M+)^SA6&NhmKGi)qAQWY}hHMv3MGr!C0Hf3vRFFXrx ztA}NF27}B6{h63u7B)(CbE78fBIQrj-*hX)MP*WNe|uOJY~=x232|jPqDEdcjFED+ zDWA%2rBiRD51u9E+>v#e!l#2A1q@3q( zS)m*cHF^+d)n}+#=dnl{2QFrhVq$$@IVZbu?!Fu9`>M@R%)hpc7#w>HW~xFO{xOYq ztGHz3P5~SkM@jNV!Ep|J`v@~k3G@Pq%3q05 zglc?gS+3i`S*}`Ra>(2Mbig%>jiN>Dr8;Vx^D4Jph?c(8g2wzJwh&RCYC8Wx+)i0Z zl!QKjcm()@+@4e!`92Dqs1~`nf-Q&&A#eYTS|e2XD?e@>ugDg4(5O(zkN^%VJbw4P z3|p9nD3qyH@(ee01TR%17y`R+I7>>QRo4{w88cOsTar*n3!!00Xr=L3vv!>u$#)!|rx6%GtkR0Xyl7!G0FKbot`#D2t`nH0Mdvx+$n7W} zF;Gcbn~wC$ zEaxEizPo0H{osPVD~C%x=cg{4)h62#KfkWj6Pt`x!`f08ZBlv*_g0wCgGHrH|LP+? z>9*~LbMZ?H;LiNNI$nK8tZF!Lg*_O}ffQz{? zCM}Aty01OpTa-n#2-1gZcH0xYkNZWA`>5J?uA4&S+F~|NsR;6V60PwEqo`w|pK!bK@^$7wK6Us7y=$@Fq7WH~b5-+2%hqF@bBcsk zFxYreZIp~>GdM-AuTICd2gV-oa2%kyJD?7=Px&<{ikY1J7do4>oBj|>`yyHZ6%aks zvP2UR?20delc;Cq!2Kb-gvo0nmg^X^#)MpRdKZc3?nZiN7`gjNo2ci`#_PSXR{A>c?WnrVE(M+dE>io zp$n=On<8=~w)IRIu|No;z6+xK>RsKT>t=sP3~xlSW*9S9Y0fI$C02n)_a;R7+OuYK z)DOV`l#v(IO$dY8w;S;R8H1C5Lhr<6>o=CLQOfU=Jb ztKo`a;$>RCdQUOdgFNZHg(=Jc(iVNuI87s=6>qMP_XbXW_lz;KzX;nKS-`js7|{-R z6!^Er>)Nw5wh7zwyzI*PRB&>ecG8up-uW^`H>1V-0*pPR68NMk$+ia=4dDQw2Zd4p zl(KJjdmx_ZyK!yUG|;<#9&PBn4B4U=PWyhWsIOV}VB7Vc>-T@k>rMk0c_OiJ1Aimj zfEEKwU~H8GE7E%R#jZE|rdMDH3EM9C5?%K%mH0m2Of%gHHt4j#hIBLEvs z>|WFvAigha2*YBg2H~KY-jM!lKd;X8z4W!e_xUjv*|eFS{O@f%^={(pMc>Ee7QCrS$vbEMrk><|1{v$RH=TN-N&UqYRDbW8RN@glTEE%$Cw1E54nVX;&OPg8hdYz9be%FVBxEORPl@ zMd{_CMv1L?uYKn;8JpkM7foQ@6@p`3%Gx+y%`%9bP<#NTh`rE-kC=N8hoNiNi>UdH zBGYBPEGmZX*O45uX^Sp+4c}jP(S|fepGSo5_P?w;_BYsV($H((;dXnPRPj`}RSp2f z-iSucTt0rmMnop(@sts)bMu7UGiH-97&#AtRIjUs_Y*~!2AeIdc_-#-O<^`RU7$I} z3<{6c(>ElKep?h38s(qzlbWI4$mc8?*+ah^*?UaG2QZr zED`%8=Gg!tl8PQO^hf_11g7UDc;j1lq#W>J%j%6YOg66zzK+SR1cH%so^q_34qOnV z#H;D^&Cw~#Kmjissy|-tc0T*hk=!wR+CPXlnO%6+YX{k-!i4RTSq%oXZePEFutjuo z9!EYp&PIJEshKinn58uhZ^}7?uGPM%(NN#HUYBb8=X%Yyl%%C9#qdo;J zvRt6&o;6ad>_V?U@TqL#9KcF?K1JlljM)U=vHmBK_!2If{m;aGfOm?PZmsq`pIWZM zFJaZ?f+NH2b))9+!ddp;PgN^Mn+eah(Uorc6<+|^hfn8pvs+esa8AeEMY881-^`)s z#RRw|A~4~)_$Ofu=IBJ{cqInyOmq_3&(<(1frD>e*&y<&JYb9^e>Fm)G!|_8Z|_O_ z=FmmUPXBG_<3(?;Y3sZL06_cm4J-&<$R+$q%)5CEJ}FiR8+;x2AncsMisJUym~r75>ep5uB7!mB|KW;vD+os z%wg)z3s|iCu_Len$_!+99tndgaFYrEwS-O}*z3*7EbXmnr522~&u(D%2pe@g;qC<3KmdGkE5SU@xjf>j1 zPHS5o78RO~LzZL`-hid!^rZC2@C zZHEZzL9uO8o{Ef1wH*rzsD^PAxLUi6=Y{UY;I31HAZH0&RMj~G8<~|Gp>GEHx$c!d zG{;{3k{SBht8cx6k7vtpF+;sQ@9%Z}3=|MEW{{CNG-#QnPWt?|O3U_XW9PKr80q@kjPqs0wovSZHW=$rRjh&7CSEWEw4G_gNJSiK zjA@I|iDyWgh_FMfIZa;7QKH8Frn&ULe5dVkn?XgKx|hqnzz*Nw@P_*srZLmDs(p;T zZ0fk)8$l46K}K#&CcDk`V(zZ``q0DsdZ$%J3!ut6lIJ-h!k52%8DIO_*Ra+RLLf~X zW$CG>jxq}Mp@nR_WFfuHKpUr#UcMR`8<-LrIfw6-3EtW3dh9E+tF+hl+ zN_(0nXjCLBp(q*1GK_jcSt6vM5oa50zw-_pbkMW;=#j^Bb1}%{ANM%YSmKEU0f`nz ziqn$ScmAD)+dYK(q9Gv1#fZ}ydq4SUT>q<|a@_Inf}_30O2Lb(_+tH+cP#Z)WAHm8fdPjnT<>+hLhD25T+WSxg#f3@jqm z8uh5${Ojv^#(~eLDG9qiWEXz*{r_P!C{`Q~__tj+>@7#INFXzoJO1`}maSY)y6sjB zEnmTkWFb1zf*3J?jXIutV(r>B{PQ-?%7;D#l?dXh&;eC419#nW7r*`W?$T0yBB z9KeD0IVQcX@AZtSzt5%NKyQ+(**GTp<)7$b{EvsQ_QQ`k+w*4XtIjdDGkqtsydRn2 zd2^DqPX2z{x|uDb-)wdFnisvsyw^BxET6N^(XM{qSHE@kT8|qFyv%b~&ljrL?hqyb z`3jt$0HR}T0~W@N6M$N4Y2^iX+_@S6CmtV^)oa(&99fOxDqtc;tQew3qQ*8rs}nW> zZ8A}vkQdSuu-LLjmZF>8Xq(6eGN&lMN0wl}szclmeuI0+NLQU%g!RKuy}91Sy>crR zW@m1l3-Q^U;KSw|+zpjo8|h`D-3z9Z1t-RFaQEt~zr{7zT#H7*h{cEMH?M+|NItrb z+g7h-q*b*2!#knsbC-<{CkZyCM&z=yPvi4voysX6JDHIh9Dd9@c=RJ4O2gH0x8BAz zmtMy0cl?ttUHv;g_4x}4`D*@f&5t?beIMp_pn2V|dHCTk;rz=l;%C439q)bL*{Ch> zWr0yhOoGHfEOTnM#A!iZ3c=NgzRqF)elQ>Z=<(e0w-L@e|Hqtn%o{oQC*NbNwG}N} z;PL?yaU{BqzyI|*uDRvaoO8+flts!`iG}qES6=xgPCxEdoOi)x3@obgoo}B{9!t!C z<2&E^DK9=<qV5=OCFD|o1e~=DEMJO_jEa*B9t*w zt3havP?HSLAT8^t$yyp(QjCEgsNu^2oXLSE7)u#E;uc^+#Kab23R;Q5lwc*ns9+UZ z4zeH^O~E>Xtfw{R7#NJ?MM(&bT2@l}L4u1Unlmi0u4p1|0M#m0?0iVcyi}#)QVFru8@y-u_ zmbV=JPTW8LA18nMDo!}zC^7@;6>r;#RJBT;=}XV$U-=5d!wcAcyX{%Nd^y9z!!!mO z7-#!jVw)GhkX{?{3}J2B=TL8gIM||F%7)-GvOyR2dFd8FJMO71cxr~2UKwNVF#w=X zS*J7D_EVh!4V!;bkJDY~7MjF8uWNflh?y}XQm1WI6_XzlLttcNgu@OyjP1AIo}mQ` zc*7fBM?G~!A1NinO2DWmV(>V=asHVMKm2JtdXL@7{PgT3#+Y_UUiGZ2uDWUyvXhkS zF>1(MowB$ctz}>hF88FiPV|ryFxFy>B3fW&0ePO2WC>9l#Ab`(v<~%zv9iits|G@7 z=f#eKQHw}K%bc1^@FCLjkPdpJ2xuH6S}a{fDOx#3JrW>El8WF{EUL;tl8}#$kR^4>+%w=TA(VJ4C#voMRqf|tX6BemaMKMHar|4`a4~`}xYC%m~ z5Qd3W)7h!jB1GaezpU6Mk2HuH)Oad_({{Eoq}{fOs;cK{dvR3@usZ3ONNd(fV<7De z9F^i=D%Z{NWTO*8d*XTH=Kn;~+hbx>5f!3HyUzB~me6jWjQ6F}mg*VOPOa8{VpqO& z(P^VW6lFXQh)mv0X7hZTetJ+L&@n<*ucJlI(MP|Xo7d&M^{69x%$|F4$Ir*m>+S?` ztXZ>$9d~>nBEpL8R&c{_|BUwmmspg7G)ZvIA_7I(Ld4>I(au`3foQHwfrlq{%HCFM zFCoyWqk9AHyhx${P>Br{3^IdNJ=1qHn&UEE57j;dpiG6=P5(k{Ht=2h7=x+fuRYo& zb3A8r8jzbqfA3oXn5wBUH=l{l_WpW}Z|wa(W%KuKZr;wh1KE4NVwpY;(}qf?@RUux zPG?u}dMbSzx-Ms%pYs6Ozg(#l=nA~(gc*k5Nz=6AON)_9FTIR!ed}r-{_sa|*kNzv z_S#3e3tGZ6aMI|eZ$De~-hT<; z`ECG?`%#~IIXRenn^%a=Eyv`Zo_PJreBDeK<_3DFOtU@OVRZm^-gzgt-+nvCyyG1h z5sp0SZEU@KdkP+5ep>-UsdQW zR;xHdMJauWF<_`+l7u1#B8I_QLOwD^a0#jwV=TGPF)k$!C8@EbUKz8Vn5Jkbu*Lum zui(lm6(ly0x5j9s8HM-MvWzlBLX24BFeVZs6s^EPFL^dXN>&D(x0HoIEfBOIN&_nn zAI2c5k%b!K!8%JGTHq8@mS~a?5`$_%tyBtA;A4rw5;R3rP$LA5)N2FS0D(wZmVlwm zOVTt!LLgB~Dk;G^LeWB@~K$;B@2@nc26`~p>f|HaGJen$Az%XbdE(7&Vk8R-}+^z{3crcw(pstIAN27=sZdU_dJXC)XAdBheWI zNTU)=poOI#3`u}7qa+AT2c{HkjFdK#i6Ll}YlEm@l+sy}N^`)gVy$38i5No&(56tu z8%HfTO!NdP5ph-96_kLnT9KoQ1yv$SDUmD$j7cixMzv!6HWi>@LKwBulP+&GFTMJx~}7%M~#fk+;1) zI{lzOJ^|G>3N-b{(EiT#Mr^d|&$JkLGgY|9aR6>oT<%Q9jCSL7vHhEwL0RZ(*j!|w z-G;Ar0Jd&RuIo7+?>d>%rtz2GqM(yO~ znrxtUK>rz;^0lkcGNUZl9@A!=4^O)4*RCls6X2p#-sp_i=^XZhj+5O^RsK8u?!3RM zs#vv2%%rIxy=)5Y&CwaVUym|5`$0DXOV>Q;x<+I!O+|YDH06}a^ss@18OCuoC2=Yc z7H3{x)0(Lpp@4R+vx)S*8v)49m-|HtA+To6S}wTYd{nD{TWfjAOI|{fIGl6rzyEXC zci(-f)iO+*|I8S{TA;BQLWvfm+%c*M1rlSm<~tyX*D2d#o8V5QOS+pG#pqlR#-@eZ zN#8f$U8_Sk5a^yXZgUuT8_EGZ=fQ&Z+au}EepKdWK%F83=`#b3Nbl!#%jR+Uq8$R- z8MsWQayfNmp7aK^`?i+5_jI!f;KGdhX!S?8zA(mY3e5ljAOJ~3K~&c(o&^5)Lk8OC z+z{P_#dS9VS(CnRgS{fchd%TnRKZBZiXdVsMM$zbnKt?TZ+^`eF8w-hc+=r%tBFne zUwIo1V%Sibq}C`tH`LT&gLV-QRcFPbV8ymkl>)RKp=T`7S0N+fg%AyblAtIKZ=p4o zv&c4B=RC~-Nem=-6ex;>%sOJoNsLeigNUK#+Qzy|8S^>LrHGE;3(r7fF-`6ym;_7> zGKVA)A3VdUQ09@Kk>SM+){eBO=R*Xe6k3376Dt{8q30ONXpuOA#)Miu!HS_ZqLdL# z>L^<+>JFR;)Kc=iz+f0iYIxs7T?Hz{GU8(-O$_yVgRp*t#MJP{B8)LKP$$nz@>W46 zP;-{`MTt)oS94^dG)LDGWRSErh;=ExnUmUtC|dCnsE{UxR#}p|8WG6L66;bDlM`J= z?gcG!(mI$Rgs4Of)M~?&g%Twvw=Kk`Bt-_HAonHW42rN|V3;-Q*N`O*C>86gcV#1# zQu#PIHTa+ymy@Q$c$LaiM*=<%1ZyxDVo*#PkYI?e4jM^ZfiFs8ni2Dedd1cg67{T= zK+QEUEkP)VYVc9X1nNo3m~RqnMRX=w!C<#HyY=cq+qe59UkpV}dftD|-@%0f?vy?@Nv5sb*GdNHugp$HKme@dE6pWOS zL92+(Dj+CSWU5{SYYpXClhnE@Rj(6#s{&y(M7%F)w#G>6HO3RBD&x84b#!1AK zfT9cxEu0vn2yiTS(S7h#^1)W2X_W~)j$Mfbo(U|moZ=YH*Oee4GIUm9#(WTR8$F#Oe zmqFTXXiLv`!pu$vGhQciiuxHjV#ggTx{U;bjstem<&-mZkm7h8J>&oF&Z5xvCTkl? zGLxmRU26z4JE)+2Dl=1olbICc8BV$8{7(9KF7&q+=TQD8FQwS_Dx5F>Dy72UmBot} zbMV0jkH>p=&*c~+S(cF`DOr{hLZH>kE7|Q^tkI02fsFsT@^b!k(@pHL+ry{=m@Cg* zQNhTBg(qFu^n@_4KAYm{u(4q$(r@S(Y>*rK=C!f3|6gS@RV#hmr(K69QiTiY6#RQG z4)G0nZqINH*@Vy-o&1|hSAEiF)L}1>L^o`%%51vXrcN8_Jvn8r`n9)8C>t}kbd#s} z#`k|(qx&4#89Ez7rN`!Z&Y_1M%HqX~IqlRBBN{Ow0G3iC+FZj07o5k-4m*~u>VnmP zk*vKi<|g;tXi1y|hl`a$8O5}-UO>bUrE+<%Kny|DRN(?`1IwsjI_%4x@B>r1?AN^@ z!7@-72E?(}l(Zz0`Gf_ofds*b!z4}Aws4XlMIDpYDdH$TA45VUZ?>>85{rbhKYA+r zJ!KCxtV4?hnARc+9b`QPp*BWTM>duqxw7|Dp1{jq@j_fkQ4=U_pfnXQV#E=>N3_I< zu+`!nc>7yl$)FuzpJzOiBi{3Ni~+3BECX3u$Hah-Io?4`QmjqDd$5k&fLbL=K%J#@ zu)aA0R>-AE)2zdY&|Ei)1jRKPy!M^{!2?$;qb5pi@z%Wg*mtvFpv7>uh+2{{+H5f* z%3XtofwIIBAS`uwgGc}xKL{c=X^X7LPh&2Vabf77tso;XCLV_p|3D~5Dm4Z-s zLg^@@;ncG~fK6(o)*u#gCzQIb0*jgnkKAKVUVG%rshc{(d4@^~3x+uI@VD~dmCIOQ zJuZ~k00TuNs;vwiA1m$!`O%+A{lCniiYj`PX1}LJTjDT2zkCbJBvqIv8vTRb8O@>@T79f)<;c#PcfKo%{ z?V;cTxY!0W`bJVad>ar&s_a0{Q4}SsR_(}h_un5QhJ_0ka^z8O;m$i(bJUS-L(9;t@bdJE!u6x@dIYbL_rtpZ)m!a#Vx$`VPQ3qYe(}anSbb|+R za@&OV@1YYvv;~DsY`3WsG}Qfn4^mcb>9kR1c-eEcvtPlsW%bIgYsz-G&r~HPPQ+JX zCJVMnb(gb0Ut*sNLfhjTCXe%mjM-eD4H@L_qr+*JhA9_z&z;WyoH-Jpaw|?DK(O09_z*+tFyxcx5w2QgfK*1>I{Y^X;?=R12ip& zGEz%uEgYb+sKLWlY)#@Fr43k(43t8>wD@RANk~G05ZU+nFX92KmZAkLT(S+@KWG(o z=g90@d}?#`&I1uq`lc@iK7TgVH~Dbrz`_j;SM)OaL_6uC2g?52BMER z1fwOXwiwhvV=Ph&DWwX=WHf?D%L26}A>2V43eNfH=UBf^dE{dri#8%ADKT1*M8%Wv z!~+lHF*__Jj;<#H#c1W)jms0sT#^D*qa>oP>3lm-uZ^pkk$%MZbt zND)UFNereriXo?E3ara$nqgd?Q?mu76j)QoYeGW}l%TGLi8%uchuLcBB5GD}sv=dK$@{`$l=bbS|Hc@iS7LY&Ho54=c&6&2MnC0J8IhV%`zV@F~gpO0&Ay$NAqRAbQ@#R4kIz7Y04uXxho>V=;$azLqk0M z=}+hJkAFOagF{$brDPQ&MD=|s?-haQeb89kQsBTGme{=tZj_B52GtFx?b=evgKlO} z55_u26hjb;ixsVaO&dJ* zDNo~BPkjRWJ@>^t^kKUogmyH?eFVg)5F79siN>%d5qu(82bxv}U@LL#^4Q(^!ew9L zob$iHgLYnlDGIU$LwxYlpWv(Cyqfoa_#@OzL7^qd*4uLGr_Sd5%g<-oPRoeavtE@| zyFZSzFZcu(UvUY0J$?@wS|S4rdDD?^;`}dN!fW2}dMr6kD_35lkOm1dIuW`yp-_Me* z>qtJv>OZgMuD`D#F_ton(TrnYA;dtM8J_)|r}2#|zKeAUo)#hnP9tr|gl1ObJ!hT4 zzPql%nx!27krR0AUJoZJl!e=D%Y|2dkgIQ8@l-Y7#cf@P?>Q$HW#y7s6 zMM;BH4K``gbi$6iK7?!j?{ZeJ^NbaX*?RjOdH)$F@#QPO!Y9u;iQyqjI=GP2K5-sb zee)_lde+BTwsaU5#`w$4H}jV@V;CI;pOd;2qEa^l_{K2LIpE1$e%a?}iQ$H}Ib}AqF#Mvy}Zduz&A!1_{@w7Q?(GWb=+BWHDXp@!&RF$GAaL$pY*(NDc z6Ml#?J31{rFpu%n6CDF>5(G2h>MhLd)@s^@v&{B9nNm;3NQ`XMNp;^+%(ab4nJ~1+ zjcwDT&r{pqZJo<~K!MP%ch~vBcNvPNhs$LTZI_K4B!oy&TS0EZ*= zOX)9^WD9^86Ye8vnvUns1~ArC@q%eG-LqV~*s~O@O^A7cja9A+8-;=8OF8z~cXR$Z zpW%ut{+r`Jb}|DChFCv3!pA;&Hg7oMC`OWuH@x;`3=C$R_36)Y^DlqKX&*R+eC?e? zF${0J4R3zuA$I+Uw-#}es}W?TyyR93^fLMz=M|Y)R(@R7w^A6?|IL${OV7? zs$3{8iwTF_AHOUe~sR5(Po_jx$r@in1-gV3| zoN&T%+Ucu!2ayNr~|-GGh=Dg0py=v2?4gSkh?n?H~P` z(m0Tacu!U;Rw4lyadlSg`Y48L1tBb8r-y9M&~PAzk~`L~<)o9|!`Hw2UEX=Z@z`>V z{a^7qp7W$fbMkRVbIs5GkI`UIJBpF@&`5ahfzRjKE5E|18f4K5PXFj>{Nwk(JjvFJ7;I$x z;(xE98HKN1eH9-*{eyht8~@FrhaSp@KlmXibKZCInY{L`N3-7n`|+S{8~o^dSK*Tc zZxhPgV@e@3TuheF7+*G2FyHc7sT>opy|e9clZy?J>~KIM^;?v37nSEh;XSDk&j? z631=x%Ckm&`~C`?(~S-CiDdjI+=(w_GtfRVYs{dj0WwV zw=%;!*%Z(z);BFoYrfn!P2)D;V2qv7V?wn`EnZz`aLyqD)|&C(J9=@FBxuyG6HDnW zv^!K3+DW4x=d_tOe45`oEKTj=qV9#WZb{!bnL}Oa|Kd4@Rj2ne>fF_D5GS$p`CYHS zXUaGx8wVC?zxu%}?|qX1BORfv0^eRP|147?9)wRkHo`{Jc+ zvvLJzfAE8xbN1O}X~us0?Z+u+oDN0FD_?pL*I$4Aq&H)vQqY{SBt;+%KoM|>!vL!u z@i4CY>HqQLuYHrXjX|F9vRzoXd?h3Q_y>o-{t%wJ_r9zfcHDTu?JQlgh<|(V&Yb?L z&+@x}EMeVU2NAW#gCD#Ldp~_g_TKfAmCl+LEZS--9`>j`xbXCo_}veG$?{d(uq6* z@-udQ?1TB-8~5Y?)@Gcw%U9XsSoe`L3R+mq(%+j-(kU&x6^96`Eh3FX>wT=tmDK~fmI3o~#U#b}OajMPD9Qlfe=j$AW#edMF~^^Y#+_Vt07+A4e` z9m1$5&>|Q|PQqxgge21PmYVZ8Ge$C)vbMRFKV0`4e)5}JIOxUCXW?Lt2R?FFKKZ_P za^p|`!0^_K*z2jQXk{bRoMZX++px=n9>jS+{}Ulw$j}bkQjD(Vg40hWk2^5r3M>m5 z9?CfQWzXRG&wW0BFAO(*`#c7cNK*qXO$aI&Q4%Y76cNX+d;Wj+&O6MmvRwPWx2(1H z-ZPUPAoNb8Nf%-P2^f&3A}CEz5IZ0qJsjm6MUH~lu^&+c1t|hjq=jB2p_2qiCB4ri zGntuP)_TkL$Gi5PJ;{VjAn5TsY_7Q`?AfzsmA5?4{oK!ezk?ShJxh6YiYgALySAFH z?he*;cd%&DBC=vagks_BcJ7-rm2Z6an|$f$akO{!;D-(&tJO&AIkYOGlc5~A$Ikrz z@_%RAlh09Y+nWQ&kD+DQ7|uTTL@qjEf2O_CfvtkJf+R{voeNf~KEoimbA$h3b8lr2s^6+yG6Rk1tdJ0Hmr{5ELf0XswYd>^3gP zHKbg7bCL)d^&!JTAeF*qK0h9tE~)V@+(Q+vOVn3rRj>4cH5p1recGfxPi9pg0v=qD z3{j*T9Vpg0q!b7l)b4$g-*ymMc1YcK-=q@jcYxRwXnx%i*?Oez^AoOjf=L~PbuFu* zbrKSL8N~Dy!EW1oiIN1{LkVeSiKFmP8u!lkIg{cILdK+3LtM~dLv;q>J;r22ItnV< z`Th4M3?QL`(-1&f%ZQU`U0|*GI$zIW1D`u*seW&#S&C8uE&Bug#)4L09Z0FErez9o ziE`OdD#;wIut=#wPrZQ5SR$=J3M$na#X^F0HvCQDkV@jMN2mY2s|E~(YZ@>OQLrVq_)0iv=@I~&-<<{BX`s`P2zdf@4 z@8-SL=aWinjdN~bZG?P2_BoN+hSS^nmfV5Nuhz~<-%0>ueeuCylW(yeZR}00=Q!xQ z;5X*O8#qoASl#^np8+Wicy9#*+jI`}VX+DSxLEhOi%oHHZ?)g=`r0rA8y1uH8+oq& zA2b*?K@w5V6S6GhUGI8V1C+4NA(g^gPoyQ9nD@Nry^I+xx#sbnW*zfE{rOD_Vn`|I z>FME&Gd@8iJzc9iNRk946^}gp2*H^#AHGuO^76W zD?JR~Y%~T-tUaY<7}ZSiiBFwLq*9JO{4lmZ<|CZ6Ly1bIOi@Eysl?FM0>~KSN_4F% z^Y}fta@m)DgRv4{txTER}dkHsPcQZ9tz`8P(3U~@sC{U9Hx~e@CrNC8zsD)EMc@}^E z{zoXO2xGyC5@izh9k&}ZUwQ)5V~L%h+M6<@Z3{%Slq{j6CWxD^~g8ob-XidF}Zp=v*zyTFRv9DuxVeL&XW@8WhqZBF0lwmUr)W5RczCk@tV>I9{E37q*t7v>+9b7=f@weAbJtcJq#1cj3wV zpT)b3-fBcM{+nEl{?k9Q6sBdoP(orIrQh~j#}=W?nU z=vYzLONgR4uU1tNqJYgnMSV!Gw}FmnO)UTqXoQetCMAjkg+r>SQH}OwA?LMG06$c7YIzX`VQ|*Nc#H#jUY5}8_rQ-tV1hFv5+*_9ZKfHl;l7e z8lj-r5@WO6L8|^7UeAK}f8op7z%bFV?g!G;UvVMs57fQgF7x(l(AaXArCJ9oRbvds zn0}zdn_dTn>gO`_`Y77qQoMhb{>4Ay@BT|p-+$tI>NjP3sd!61`cItToA5+8%2N0L zYv>oPH6Xa*h8xf_=ue^~M;-k^;C{+v_n=5~F4QKxJk4(JduM~$5 z#nZ`*IaBb3FLCyzn=|#P7fE`m+;htvGkQWWWI;)bR4c0^YHb*cI$_rCC>74V~ZjagA{FlG6?f&~P?$GzrF?A-Rs|DJ10hf5X zIy-SI+PU+ubJ*&Dy|I*8RRibR*ki&`jOyxPNFr!WGoJj*Z@B8JFO#;6q_=%F)1RNj zlo#hx%L>eyxtfokc_^R!+}XS~d%OwlO8Iu?`X^sdbil@~*#d&5tf& z)re8-y6p(`@_7vDjhLm1y!Vg;`Th4VVoi%i7|)vd?aZ3FieKM+H#gpWCnGYr_0LbR zbVWO3w*4@B?QsR$M;i zC5(_nkwjWpv$&lD4<5(qpZ*jsE^+-IAL2)sT*ylg{e>HDyOUqv@;45DYCeB^U=pSx zDHIdRz6-Au!b@&=_z5om=}+j6w?K^tgILq56*yA3D{75R#5;ayjMsWA%w)6oN_M|A>U0g zsUuDbp(QOMMC2V zEfo4txMM z!U@C#Cm01b%p)0rrJFd?Kxv(Fm^C(lVE!K-2Mw-DB*OEQN@FvNit4uRP5orK69nGB zex>Z~*W0i4rGeg4tJSd9636j|S%z;(Qa<<%exM=l-*B3_F*-S7&}-ov-9F#k^!*pm zi+alz(>Fg5H|pO1E3C6(6P~m97rO4Z5G8cZ=9)mB2@@tDrDXKzQK1-!#5$kLbA`f! z_X;7hocTHUo2P(py^#~#A{v%cPX(E(S?;YNGgj9GTFqzGD zGJM|49RBSHeSs}XFmK5cx|g>TIYDMD?WOZjHwYhY4tl+hoGwAB5 zVVq#ulEtiCvW$fb7I5nww@~Ztp=U)WOXsg(S$QRFvkRvkzCXA8;zv|OE5-=Cj)|P( zrRSbu(dyN7E?>r+`3vao>0wR#Dnu<~($mj!&)s+7Y|6{ezrex;3rRCeIjb;p#!R}~ zdwAlmhp@s@>+a?Dn{K3i#R@tWui$}+_tLexo6e3^yf%9t-Q6ilXyz`O#f#5PCR^RZ zqIq*^@3x$L%2BN9n9uF^yohuOD2I@Wo^qL|o_msKCO$}SZ!cKMyqR;DIcpl#EaR^? z-^wdfXHZQurq7toiiJzT31+-Jjm66rGk4)^Cf@%5HuKD#Ih#iwc$m)a3dL===Mx_| zj2nLT3pz~!?G&-rJn_(j%$hNSO0~wqg-ckmbR{!qPv@DZp9U*fyl@^5-u*YayUSQ1 znZIZ;FHL)eHLI61Z`N!&E1vh9ej20Nr}MYl?k5#(SRsftRMu2@`uQg)r@hR1buR6z zI?2*bCf+-da#ixsV}Ga8TV~a&)o2vP8fr<3a2o9#E0?Zh;@$USOb-j^&g1dB?j`N5 zG5wY4Joe;dD&;b(+gGz-!D6bZA@v?_9I;X;=SZZWyR(D4Z@G!il`E+9_OksB+q3sR zdxMntCe-o`DJ)fC$S!Z^Aay~qs>B!0AeT!HdlHUI)H!1gwHz%Sqk63 z?C(4~sfYdc8y!Si^Ilw=g2}?a>tbOp3xG!IJz5HC)*z(}(wd^l1f}U~{nwF$ARD?? z(KK9YAW{&K!r5TOEe!aI;45FegYie~hSFfV0Ru8th*U!bTnbW4vhH9neELaOapx^l zXkRphy>}f!6ou>bWpK47Kqq&q^@gB_Q`bWA0&7%gQ4tZwsL)d7MF04AeSOj!_!0tC ztqw*hY%Q>-go}_W;0w}%7YSK6L{WmNXg>d`>pAw=cS41*x7Grw{LGWD;?^6VXY}y> z*=9_xO=`k@5e6>$%3U1x?p?z@&cb`%D1;OET0RCGs5qhS@Zy{3z1F(G>d@+SugeS8 z(ZlfrXb`URIo9(z18X}@decw7rvH1w=dE6nN;kwgDCMj{#fQ4yeCaF9(w2@#*ZK055U+ofN!(tZrOUhX6>oeB zti4{du|r`)2Y#KQ(wx%%Q{8n2jPd-sL3gu5$)@;X1F^-lp^{ zTFSEvmZQB0)Oz7ayrR2(6;D0(B+5fE%Wz2%jAP-fdCZye0_Bz%)nkd3CNWiJJ@pK8 zk`}C}f=?;Qm~7<=UheF|P$e@x6ygYFYb=}J!GhUy(Y`|D1fDh!HJtFl0#=C73#lCM z*>5i{|L%X`F`201NN6D{MjSK#P@bLqJiS$cC`zOY2_H7a3B#mEo}(md zIHBonU(KwJDa?x$J()#_4Bwsc>?4m;i!_nY*fkZVte8q95?XtfFu8pZ%1fkA0YTCC z(muP!{F!r63aD9p+yb=>U9B?v+2@&)L98M&Rp8Kr_vgnyyo{=cZYL|z1E4Xv2^)HEUq%2avr*_W^sXu&dn=1dmOoQLoZB{Pto z4~>5>Q=WN|YAr^^1(Y{PQ^pyX`rK439s@)+s9VmRI-Ob8gHIt%DJBKHbHvpio_^vP zilP^t8I&H%;rqXXKV1G*3`M-qIFpf?8d+@#GhS_HhBxS%$0~U2;YV;T!Z=6!YcuE& zl9N7kB3o{@1r|p&t|8L`^Jh-y;Rhe3ym%o`&VLy}M2nM1t7%oR{I$6(oHYmGJxWN9 zIrezA+-56eXl^HwnwisQ@W8_lP+Q&0+%;Wv+bSP8>Zl+%NMC*x;`o0W6$2pzt5&b# znm_y@BomctnDG{zmHmhp*1`NGg0?on(~r+17Lvn`+!omaH{P(A#~!$k&wb`pda7#} zwdH0^d#TEOw?0m9S0^1^OW60IW4Y*}AM%Z_pUkeiws6lqPf@Av1QO2s>>V8W{&5_4 z+yQ+5N0Sf&4n6ccjNNG$BC!SSYYg7P&O2PhYcnqAPuEp=8&VH`E#$Lzb$ySVJT zX0XE!=dp0nm3;E-Td9>5*IaV~+iv?M=FR&VU;XNX%$c>6TkbfWi4QE}=%dGQ$+zz2 z$Rj2&Zd}4$_jDi-?7Yhe=FaJ1zXMu%?)ld^^nE*0DCHuuIY{CRM1@>X(*{sLN_c4s z?6nuno&|H~u4J#hNAu!~%g{<=jpeS}ALONHmhjl$|BV->WVClI<<8shC#|&7R@$0J zC)Rjv-b^Mv^)fC$X!Xj&zTK5`{C)QGq&C);-#0C zbJS6rbKiZ-!3plT?GcVV;{Dt|@g9yp{&=2y?pey^9%{83JMX+RQ>VVjhfh3?8~=Pa zhrNG)taV)Zvs3xW70)qa#!NtR;DP(|_otrZkJo-0FW}m17xMe7o@ByNNAlo9cVVny z_RIy`bkil=bi+y>diZXlNU_V#yD7k!D5BjJGu+u45mQS7i=4hVUVk^5L4?=ia)&%TxQ4B2JwX^uUg`tj z?3;pr5i~XGNk$#$sAD45^(9~R?+qYCG@&AEnBbFBSf4qh_lrJ}R6oFB#o9%rfdoUv#$jPTk8SIFdJqYh2rsEtY7~nFTxN-s!q^O} zLMyMe4j~k~?6wyd>@gnU%lY~-xPka4K#VZ_Jp$qDjb098Gol1s=CQR59ZM_@fd{Yg zQsHr!v_{ktg9a@WIFIvSlt4R=@IKsI5svSqZ}f00Bu)slFjzd@sl(|K+PFY1kAl*D zBB3n0aXv=*h|Cx~ikdGIX-P>I5#C`1WVKqT)(J@}1!Q~{$RyvrEwHL1^tmBzbJ8Lm=A`T|}GjI(GN;f2Hr zh3l<_zRpO{BQms=lyYII-po_c8LDh4wkD)mjfjL^<7r6>h|FOtJ$UJ;ngEwTXupJb zNP!D4_;-3&ETzp3g`y>i6HFxomr@cQCki+vNqvSA5{X86AC^xUdY5CN@R+cud4&`) zWR6&b^07zYMT}IAhi|@_hn%GxmGYkfK8Rg;?@=DSln7zMyfhAl2#z07F(i^gl2}U& z2nq-R+8BQNrO(r`ssrm3v4#iuA00L4- zoOk^;YLSo0e|)4Jmh9ofhjIP|=cA?_1pY2t~$8gU1FH>dB%-gdg%ombP$|<=Cw?l_6a_B&W(KO zi|2B}3Gm$05oryq-Gy4V6+Nrq(`Q`E{Q2kMYkT9CQnTD221nxQRKl&nN2{&wpI_D33hy0j~djH_t!+5@()y0!O^R zm8YM8U;b(uKl{NPPCD%XuK4ziTyp7Wx#Igj#HB^Pb=m2B|B4@Q!Uxaf#_R8*6e`c$f&@UH!T#1DUTA^YvK9fytoG=)-N zUaVXU`yaR$=U%WE0E=e9_z6d&OPy#jjP9k-x>=uqYoy@9ukTD@_|DiAI#<9+ryPfD zg|5Yr894ew=O7|jJQI#SZUXJA;g}OYPy13h@q}-4`>p?l%R*B7ms`qw}&Ma{qnj zqZEAb!zZ(10i1a9Cs?`^KJdXK5I%qcl^Vp+{&Xyb34c0^at~B9P_6Kp&+U&MvNsVC z9Ubt=FYX+4TRoid>23JL*&k=cN;u{0Gcg7#JvX14_2` zi#Lr0=Z5rSZx{pK4eblCBxDxStR-(j3ej1Xg3U(vJDxRg%Po`m`hSjVMEN#_KVJI` zmtS#2NJfl9dCAu=dX(>c@6fmLi~obKIuI~o#7Hi<;DUZOs+;a5LmOu9f>J2NWLXxN z%XzXA1ha!>tBy1RNHb3y4P363tjl^3YuN*f27nR3$hDIdAqM!cSPQ_&=bdpibgnT} zzm9biqWw@+}Ut4TB!{dp4SmP6&o=a5C|>%CBiaaYrF~f#N}ZixTA%rL_aXV^Ex)Nf_`Ippr1ECaqC>j7dJRXMT04_Az@dKMG1xX z7UO~>f<%Y5*E*O5WHmvQV0U_wjM1tM)+`{Ca4ycRSn@{Vj(~n(nLVFCMqect%7r8 zQZ#`qV6liuBchBnONkr^>o9SQ6usayR%nb)L%*?8NMFH9fz@qz(Zvwa#(e3;Vn|Z3f7h5kGc$>2jRKMzj zs(Wqb1rF7W4EM8f$}JlO2r234Xb;Nck;1rI&S+BoTEx8&;8oKC;c?!hrDFNYmBCgm zr-KGSBTQn#kWiCw%*mSt z?Tb4thBBFg4}7$U&ESuBR)|W5VOs|62k*eyq4f5`u+7Sh+7=K7=R|JSS_T>1bA-|q zO3*UAijg58?5!HS04rCs?Ji++>FV;h4BCc{VC$Vh#btUb6@&yEtzgX9us3&CSK%Eb zZ87gV-eW5fdyM@iFFtn(i{@@axhGuy(#1t^uyl!_T!G=k!TC}k5(^!^OIY3AN!5Ue zbRgDg!g?X<-#tzQ0i^KVg)xy=n8SMpFD+Jr6Cwz|3WXIW9H;S~#-Z}}a)DTBvC@zM zs+i$)FIK|l+rg4mP%7@r?AeRy?HP`W5hJ&eji74i7#cbtsfDd}4N{k5hK7WA#29$w>Hp}bZTZ=Y;((^0 zIqu_{<4^l008|#75)iQ2 zR=G}x4Z0XD1&4QnEq01<9tuODD1xWD9+`{Tq8PD-Lz3n-#{yD@Hli_Ghag33Xd4zp zN=I%U-isqo9EHGY#29d-fMc883*>k)LWZz5bB;(#W_6y90DgPp@qtkVe0BfPT(#3G z(hQrV83Eqbt(;T#)_sg_iGVNJO^FIc>t)W7{2*!km4?U!DlRE%>L z4^%2uk|YVtJ?Zcmtg~3_^B5zXLmj|yZawEC26HVou&G=YK!|!QwH74en|tZ<#ZgF4 zmI{Oe6|KgzMG$6g%)ZxQZVuR+7h<*4REc#G5;t8y2)DtkBqU91u{iwt?umch^Aq(A zW9|Mm(L4YYV90EWQV~DDY$ny!yAtadSzN+-|GDMs7JN4Lr5l;LZ2;x#+S>c|-k<&) z4Aoe0tq<;QaU3_Xbp{5O>c^_H0gSasp^z5-_Lm;!&nSS)fh=W0@Zteg4Lzf-h_^v! zDk@aieP6}lN5{FFnB|T3M+9I-;;2`!qfBdE`8xNj-x!nRhIb@M4z^@jV04nj@hHz0Hs921&)Rd%#W=N9Yde@+RN<})X&4{HU z6-AsfBo-noky4Y`8mVGhU^5JY&YQXzdfi1b01qlP9bI);oKMw~h-ovZ2gh9oN?yu?b0^Z*r4n z2-uWZ3WSV9pQMNpUXrP>AeM|r7%;N#=`9+b;&DjmcgeDV~RMdzfh4f)TsRx6R6$lGT#aJh>z8c`q znnienREE@*@zGFX>(IiIDMPVRq%y3A>G2euq-J|@i6l)eUPuZm23G*lLn&#c(wpM7 z!Ne7^w1rkNG_>pJ6lq#9@fwtm5LORjV_OgJ$2p3{B59W9fp)Nn>?hv#CSNW`Z*>&J zXX7X)P1Bs-DK<JN78wpUNr8U@=7IK>i55y&d_268A)CqLa(_JYeW6(t%0x1V3EubZs zOo#U8egTqiREK+e%NPS`TKfk-ujXJaY`i*hc*b0UqbRGog<(L_U zQaJC3v}DVziut$=$$K@+C`4f_2bCytP(T5SYPCk85Ogb)>Wdv@z9rWy6?In@>nuV@ zjO`)PEkK%sKoW_>`Wi|kL9|tXvsvywBI%2hT)m?sfN&0!R2b*bxt);^g6?uVt)<~8 znI}pll+%=A9M=m%q(CXzj7WnPP%VQNBf{Lv1g7rp;&K|h{b#H_n+9#LX^Js1wQ7x8 zEu$DmZ)fmtzc&3?6&_0ns2M1CZjMT~qLtlfu|-4|WW~I7kI<6UHUdOk2X(U? zY^@g&G#e^!h`cc4@PV;QpOZzuxW|h;&N1O{$fO}~6UAXs*k#yl6iWgxD@bKFc+GW1 zynZ{76bv0Yl$&q%TX?0kYKL z7STxJtTPR;#}lE*^@SM#03ZNKL_t(kMAifMKk!%Xy7%I64#}3hbNg?D9R@NNee&@J z!IRfZqr-k6IPSRP_~IA$K&d`(e}hY5bIX7aF^Q{-dApcmD-?!%Q(IUjPbCZAV}#MQ*)mCHw9AKbTAoTI*#X)(cq^;&7ENuDR;h9CXBS z%zEJo?t8e%k>d|XQyv_!_r_C`C|`IDR#*}%5k}%l1-}08-(=@)_GHe>uX6QOSFyUQ z3o9aOG9|W9um%-7x;*Uh-ecK+v!VR;Pk*4Alz=!#V1oa4Audv_Ryp9HgE;pK=b=fF zwH5T}R(^EJFFE+Ied+36%42st%;-JF^2rY$!Ij_p0c8_mkVHO1c#F(?*5bAy{N}3v zX6d|TR91E}@s7Xp;`HeVukj)PB#9L`nUcvE?xc0 zTV4Q~mSRXy6Wxlj6(Sw1ON9@|sMR1z1ewXO))D8_&H%XEz_xFAup;w7`_=$yln zm0M6O3?YsS6pF0@##t;E2VU!AN=knA^Ar2pC=7h(ThqAfzKio)lYx(Tar%F4 zN<6ipHLzeIJpb|%jInuZiQnMR^1gXIgmI#j${9rwHdEYp&l4C^TZc8^2jXG~fs_%I zN=mU9Q)-F$+Qr8p;&oT^z9Z!T7%DWhB$|`Y7|qG2d}E_@LI%1o*1)V;@YD;7!(TGF zzSD*n9C>^BhTM0A(3lMFyY~@Hst_W{&)HyImG`}WPrmbmV{kxfEJKE2I$d| za&XJSd&a}$7cUAKyJ-Sn-XLaQ*Tkqh*Ug^`Pra~gfXQh49e5z!6pu$LgN`!ZbLg%V z3s6YFxoRjTWiWNhER>3P=YFHYxTzP0g{87)70Z__VE29YW}6`ym;UA+-uJ%4iPYe( z6AT$Tf;|q{3*!nD#4=XSTENo9?bL+oU+o=i zHO$$N*yLP6Yi&N6HeEF7T@Z1VNNQP)D2f{3#9&bJy5yD+!R|22OkiUmFxH@z4y`3? ztv)_y3ppP)6)jbulzNvZFfvfnMHt2Qe{B>dnxG8dcuOgXwHY3h+h&HZtee~hR5#p8 zMGkI6Q3)mUgf0pW3H`*aa@Pdw`WkY5213;GAR}qSDne%c&dgbXF_u~tq9`Ua1|>Dl z8@wY_!}6++F&R>Utxqf~&^p0bu-5clwfCf1(9p^1We5qzL6j82wMamiP@xuqp+d7* zmlweGHM7rB12QAQc*)0Op4$NQLpsZL(I&iL^^H=W?BnU zIjmPmA^Tbnh>R~g^|>R-L?+QxxjRBzKV`Z*eqOoW&$7(4qm2VcPjF^ zqWd0IU{VBI(R%->BSoU|KFTp8Nl>KSYxByM%6J=NU(hq<1 zqam>wsK7s1SIvBVRZ+k{rc8y8o&5X$;055l3WY{5LH33(A2h}O=l7qcWpsuR(VNcO zTN1~yAKkhU?}em!sspKlBb<|P0*-7pq1wIQ%}d`+6~@D z;U9(iIv(X;ZyW(_j;pra6}H=T%Z&hj^9Xfq)z7@HBxP_YJMKd#;A-p9#1I2wZXwRe zz(o)j8@$IRWK+*j-RH}$)-%`ULr zZdq3Wl}}<<(~}hxy!z@CL-AxpeSZ=#gmgTvXz2X)Vx9XCVg0`D76j8W@U!7w$Q8AP_vTK zm_7LYP2c3tH{H(4v?+@O?rI&w;D=s*TJEj?~{>^V$`r0eZ zp0|QiPxu%ooqPf1Re~+H*pA=acpZQK#((phYp=$u7#T&_YKpgE0&5f|&A8%<%ZXWm z^BEVOe;!pl2Os?j&ieTKSlM1?i@25Vz49L{?@8Ha^ASAzz${*wyO6n^HI(gT_|98V z?do87HD$rj zZi?|yXaguH+N?*@iB+Il$54vk`ilvZ^wvI-7$0n|Z)d3Oj zJyFhK_k&$_>Z-|kX(7u@qsmcdlGH!9P9U9MOHdH+OE5*w+j96pEA{^Ki%CAuQ$?df z;JhVHbV%G)Qz3wQ_dphcr`)l`+dfAR5rv+9DMh1opkBU_Lg%cUzT?$}iJED8Le#DB zg7>755kklSCU}X~I#<{hA&^sXL$O)N09Zs(P9awz8A5`JQtH%QDFr$L>$@A3hM>F+ z)&v^=+F(H=2%s(yCi_IPnyjRx+~|RvC#Qa$&B*6JU+-`dF8{RvIEcIg;amV?Wsvhh zctJ}`OAuewA(#$qhC;BnHPlIU{pHyktp#F$#?I#1CUUSK?yFif+Abm_akGY5sJ02a zuGp0{`L%1L%KddIn z?-%}cG5|>cY}OGtdz*>ze-FOMMY*gsd3&(PtJrRnu|b`44YnajybVc!_Zs12q(~6N z2p{)30RY4nV%Ick)%qy~YCynsQuLZKN*NVlj z_x}WaZM2OcFFotZXu5UY^J^C=^LP7-gi)ix-&skPf>N5{BZi=~Ce9Q%XlZSsTB{Kk zB9cN8V=a|(ncm(qqeqVn#gvU$x85jH5T^^JrNkZ~9F~n%x7c{s8l*JGPH3BYi z`v!~~u86oVG42>Q{3@C#>Q7Vz5yd?SZlFlqaYJ^PVP@{_?sKZ1-yc=y^f}$9@9lf% z4$KT)yqdYUZ}&M>b*i3vp3n0B;}X*`NMY z%)58=`n|y9>Vv`La*wK^=Y%`_$pbn0#HaGH(_c&1XYBLfefY-N=W^U(CvwGaZsh5w zKAp9$#~WYuE}ru2f8}lOektwVPx$^7H_}1J;F?KS-T#_O10J>N#2_-e9*dp4Y=I|+zkj0vElb7Waol9L93 z=m+FnRoUxSN9pJW;JxI7Tty|8E;D5`X!u)-XlJO7RRkL(A53KdT@wzP>KtoL!n;`U zpjw-%@+?Y=IrdXrB(1U-4V*Nhfu^z`+`@py=#bEtR_wd2W7z^Cd1YR7{PK`VS8iQ3 z&~YcH#3QllvJwa6v0uLV=!k?}Qak1wG{6b!pX&kx8!Zvms{gT2uGQB16UdE<$gd)t zrHZV`XkSW%HK{O3u8Q~UP|Kr^DMh6(K>Xx&-?ivVy96PEyQS(;ZI6!K6e?qtXu=Ko zCCt#|*@pMh!5D|tbs2m-@Yg4CKl!=_)g-cpADk{+>aXE3uAJ;v9qY-QM2qyxP&lV; zS8c(dPSX@?ZHz9=OTM!eS+fdCHi&RA^I*Cly?R)8?HnkCg&&xc)pJuP8rtNq+D0&t z7qYPp8tZK{M_|pFB^2q|x*W5ef74utz&AxMk@2 za_hak6u5m|y(|V+hFZ7*&@<_JpbvBdfKkq&VT24-hL-Ed^|oz{x-H+I=u9ycHt4~F zbgqT^H1Jl%hy=lfe`Vnblu2F^Xd95MqI*_wqUxOb#phDBqQ5L?@0bba&s21hoWMOZ zhfU`&=^SF``O*bn;G5sLkp1`Fhkfs`7tZw%EA;vqYc}k~eIIZTAN%k}`20Cv=E=uB z0TbhyCu!RdiQzE{)?0EcJ*(u2a_t|l=1nhu9ec0cz=uEnRyOrITz^%c-~a3?F8=(v zBZa>3$?h1n>}Fczw`^uEF|2CuORv8rA@BA`TOCNi=sZSeSS_>M z)Ssi(nq?+&bmwm5;&1#9w_L4!`)l8at1n~2eGccw>wiz8{SXRc+w?MzCQ4>zr_Op)TieF#FzrW{9p74(k;0-_iEvskxT=2!ua80knKfe4K z^tSd;XW61%bhnhwoCD+Wy^L88Kl{xuIqS5R{ME~YK-Ay-=ulsAh^zn1p+TNR& zJoyNWQ=G|}NoL9E;}b^@VJ2Bid)9I8e}9ZGefARmWY0VDcdPcqB#v)=^`g;@kkNkfJzsG6Xb4LsJH3iA3dT5OFY^xjEn+QJF7} zszHvi+JVto>XGdTPMFdL6fG1}(_GlRJlx%23_F0bJ^(eeG9PjLWxhb*b}31k;y6dx zz-2KGmujensrUnP2BD{R)Hol9_i2;0T%1cQE9-`Jzi6SXU z;1(UP3~GaxhDXeIDe3;JN<_mrwa{S9cmuvR^rrW+@I$2%L2Yws6+F!bKuekPMKH7` zwen?QPo7H9r>i!=?F8SRww(dOhy?S_Ir{xj&8^*TuW*@Fi81O$&R7=Zu?xhIW@{vp z(gA7C2!SVMh-0A5bBjArL?(mjWC#eY6_Ti)>$3|$fy3ind^1I^=LtbXKLlJ1of~H4 zkf?mK&Q&IrKFWfKkmBhh%*S~DcD*|$ube$as;b;}GOL<)G+a_sR@d~Og)dlooZ`8# z3D~W4I@QmWp)*Vh@ZDAB#!ftxTOCkV#;ltKI=kecPx{?!)KbllGmdvNl6y*lNh@!nsxXjRQ&mdNCnP-L;wDNS-OB2dms52j{TS8Sl8>a z`R0wN2>adj&V2Cfvv~a9KLXp&uv_|^^Z(A{sN)~cUp?f09QnY5xa9Mn<#$*Ap3c4x zG?hrR;-C%Qpb;aX z_-xW_K$Ou7Qt|+FaJP$VwbbMPHF2P^hZ0G!e{6MR}>R#TQC>kw;unM2ooH z;biR+SYVRK&=IHRsCRH^jWxy~yBY|ii-w#G7j8Gyqa=oV)0$kdkTE)D(`TxY7_~o! z_iDI@f{>1EM}rF>-30oOm(eBPWO|4$uGeV=n_67YHj64b!QN|L3^I`+Hf^^P7^5WW zHpVbBGqWA6Oj8mnmtd#b_%ijMOF3yPa4q zOhT%;T+mP&Wb3d6KO73s6fHaa+41)?bbvJs2$zfD7r;)8xVsJIi8dX{BlB#SnDBJu z3|mD>C_55%sAKM1aU6}Xk8GRWzvA1o*7w0L)>vF5pp`|UeU=<0RmB+>)NN$xvm-0G z?=*?}N{hk~6;f0J_sQ44dNJR-_@@YMdc7VZ9%JJqq%+v|3>|9_yBhI5@Ldp%w!5!K zP*xNL(QN=RoB=fw^1c^!nHFnm4?G&wlD$&i%~UY`NunUi8v8a>Oym z@cK8umO~%)Xy!NN7|HqB4=(2S*Zh$;y!~Aq@~FoIbzcSbgxb1z42n@l$~-q;|3_}V zIV05;ByH}0uY2(Nmz~V!EpvSKtP42kK@VobhV?NI*x?F~l?oy`E#1uj{_HpOvK;5K z0K#-P^5Y-#Zg(;HvM^G^J~B%L*6H(bvf-uPBd_}Axg+!G#0 z+Mgk{EfN*{e4j0ww{Y#oO(d;Olz+jXvm(sR&j;d2<5|CIhNnH@*?j0DALCu`c{_Wq zYV)IWKgXNj_AyR-<6GIVZap7($A{=p^-<9Y01|HS*=@m7BEy|443Po2l?yz<6Z zzlEcp{0ts-=)-yai(g0XLh4L5H_t6MY~;r4HU$vByOlSc`f{Yb8!vs?%Xr6Y|AWiF z`#qfWdF9LA&6A(}WDY<0L7eu|Q`y{K9l%))<^G)Wi1lcGJwLzvSEN3rgJa94o4Mh- z9(KORRe!viIW;66ZvOdincLXoum1c1`tw_O?n~dyM?UgjJoWhF@%{ePi8ZNp#gGxb z6M;WB+;Af=fB7k#^72=(b#88q@fv^fLpF7E&WPIheo^FXNKq@n1Yc9zDj^gj#$(mt zG{}Xo8e(SX8iX=hrGg`a41{4UAo4K`j<-PQT7V+D;0a*|V1^D^YE_WMSMpZ}2`k~x zsuPmjYUw?c8mXw(IXl$U0UT{OL5HIbDw9$NHilnSMJfC)*z)V)C2C46BPFci$_4K#u^<#QiqPP2pYNp-Bg za%Eq+|Aa3cOiS%mj7XzWSwsdk;5P;ns}zjlKQ+9iNt})Gs!EfmNX^Kjseu@(W#8+m zMT9g?8t|?OeO9t;BWl`qRdD{=Yp>;$Q(nPYXPq^uT((l!G6s>sS>a>V1oeVy64Exb zFci{8@M5su4XQR)gIioGNFjqU<<9{)G39%sw&;9(se!T1LSv>5MDRhKWDBgtJrY>lpZ{ zs-~V)p7n?>BDKA>UJ(~BiK7;NpTyxb8^!+NF2)<Ds*T~_;yJlhJZW-&fv-NqYu<%?bt z7rw=~cC5mXMuk){c&vk2ZseR#z8^s`x;t%pyy=uz0gxDB>;Ha-V~@Q!2&eeqJ+=be zc&@wZN}l$#6Jy|*0Db1R{FLKPI04Z%q82NHb6sY)T+JI#Ihi-9$M_KXl1P_JzVcu=(uqn?Phkzmk-dcMWHCrJX{VR}8DedOcfOy=={gi;Kj z{@|zh)CWHqrOhggY~_Yu{fMU=`_Cn?E6kEG&#%7seV%go6Daf8NI*UL4Y%-;W1k)Y zvJ}G{&I>Pj!5cC9?|?__Y7#%kwU=DTu}6G48Vv@a6KY5&CZJzVNF?L>KYW8@kNPHv zC6P^RyyC08{1u_+@A|<{&~!bCQ$Bp!n?c~(^Uvqo=U))Ep15$7rfa6!(30`xAvAj@ zXnX9jC$E0>Yp~YxtYqGPO)>f6$2eU5ytARZO1_eDyB zs0uF>AchahqYP5%q-J#H6%By}thA=iN*!SyqtX-#ZV0lIAreKIIJCJ8S}BPSeWlc$ zjPKvbV1_-|gYeM0A-V$FqSl3k#t_viaoafd;36fciFUiGuUg!X1^`Ve1C`~CQMtviTB=nZ(j47S4RSFWyONqMMK$R zwH464DE~N#*{{uK-Zsb~p+PRxhC35eAxe+j)@$T z!Zv^yolc7^>*1VZ#&k&9Dd%5!9>2cqR~S5*v@qnT<~;oH!}yy&JrD!*U4}^$#5)oX zUK}p&EJetTM!uO42&0A!+E)Fo@*V@~W* zz(T6eLR_Ukel*r!n+QDfZ3ivJYSJ{!VfFo{D2Ij%sB9aUuw3nNC9uuLxgR$kS^Pa& zM5(GOjuSATk5!gr5gew1AuP5IbgWUNBUqJ;!LY_^vD2y})3yY08UO}e(ao;7QO&JJ z;HhJ_oos)x)F=Y%!ZKzVJUQEdyWGl)VT{o_1^^v^2D)Or4g<2{rRD|<&V1drn>PR& z4SQxd>ap>9(s16HTj{|sGKVteV+|#@Rfkkgon#%!cvMe!ghOdz5Op-Mv>^Hn5p3^! z-+P&z?Qq=j$Hfeblzz8IVpCK-S(cHAkYyQgwmX)8gm7acM4DEJ^M7rpr{{cMT`qnq`>N)ilr?USY_5+l!T=-Sq@s79i z*MIqE{QGNP!5#Ko2cN&Z0-MCZg2sAbe)%BAOxi}B$9ct=7T#FAG2|L|5DudbNd?-1 zY0n_^>AF6N2pvtxOiDtxLew^30rn=8MI9wgTN!y=d{Sd9PyoYRs6qgt@_@Jjk&;yM za#T^TWe9hYgp!KhyFlz`WUA;)+qCV#xWaIxmM;HN+jRbE#gj3T4x)>XLB}Yr3xuLA zsBBa_HWgvhwxb&FQ(%1BcDW`iYtohx{wuY&mzDaqlEP=l-StaKF>62=w&kfyC8~Nk zOHOyB^{*?uWOlZ*RByZSt@!k-l4YS(>W4n`A*@aC-eHVHN{Dd3gC2SifAhBovfJto zxzDj;L0pA{#Fk+P6621vA`6xcVx*=`KkFjmaGD_|K_$f`Df{fPo_1?BbT{*do35qT z^{l(&e(X1+%=;d6Gj6)-ChQ)2vewMA@%n49X~J&1@4=?4ZzRW)Tfuox8!%q+B6wBA z*sx_)QT4GJKruvZ%yCmyj0xZ0ZMWUnZMWTUx#NZ#ZXnMU=U|`x_N1K(i8yZAcr!QO zG)UDDOfA)~W^@Q7Hlf$=1Q$q?~h^Ar}QFeGg*y{yy z;C=k$0&LO77@VIBoEV$;yO29Y2OTdyr+|7-!PB`^2ayiR%}Le9+Wf7sLvh-6ROCQ+ z>ai$sAIs!s)AVScw(VyKOHGze97VCf0Wq-zuP*lC(D@al{#aO^H2+x}4D~7wAeEzV zIrBCAy~d%A8@~*mmn?JKlLWTt0#CTjVpZ zl7+C;`I+GJvV^_ii)qk0znWA*Sk<6|s*~#Ov9^cLLnRA!36DGaSRVbT2k^b` zUdH}=?Zun^<1Msj4R86td%66)PqJ~-O}Nc{&OQ4}9RI=>a?C^T!Lf%vnhpEiftS7h zwLJ5XW4PI6WUZ7|yG1(R2j^i;hq?KAtS`z&X$`=D@gzOPgj-{H?Q34cRaafbeeQc- zF8$%9y!p-lL6)bS`QIPmi(mLWH(mD!F8}clxZ%dD0E5xUEG$xH(GMFI&O5BNQ!QuO zrtLO1utbDBkH5R-gQ-*wBLiTg@>Aul9O8oM_4^nbxo=iaWI>8bj$CEkfLRji# z>f#$pC){T`q_fs^&RXqy!5G0>OSju&-+lLGc6Js}@~EJzY9RDPV5qVIZayTv<#PDU!w}Ob%(vl8`9da0-`Sknw^x2*Jr`+N*v|Vzy$< z{y0s;cg3?HyRa^S33uA2ZQ5486|zm0Z+{#1-+%u=YvY;^F^x#^SZjOeFsW|)+1Xi6 zI`KDr^kc`5-SeiE;=L!!Ah8yggI%tYwG)9z6CcRaw(}wvvJ4U>$hSN4} zD`o6eqTgg2ImVVGdQ~T~HYcjivy=`_P_1LJOX@O3#fltFD9LoM4*Oor(qCj zTOw6%b?%z4^2;ZFPSOTXX48;5$K-|u760Udu8bv4WFpfesXief`qQV*K-c-5(| z;04b+nOB^88Z)bBFfD^=fu=1kIQLvG{oxOI<6GWLW(A+N_~gew&e4xQh7J4fgBNf( z#K3nhzKBns{TXiByb13;34-Q%2$f#ubUWuTA{c9V$xB|s`Ct4Zy-9;~=_8u9o!G$I!GILASf{G?SXG%9o#;Kr1mc$wv(t2` zX0=)-{EK9n5(Z#t=ceDwpoWgZ6iq{gU?oL znOEnuTKQ=jg|W-*L0yJYG+dQ;F&)n@6!Sd9S))yxsPem~EI7X4(6nJt)vVh|`D>x3*LHL2(TyB@mF?g76D3i@eAoV;9;dCv`*Y@S;>js>G#vh zY_TPwLX+&NZN0a!Y$7KkD>>kh>LCKo=luQOKa7J8Iw;uCD)Fyz1*<2^`t&4{ zWv9H7Bab|SGd}QMP{lcq7=!oChyAkCNmR1XT^}}N?lQp8>Z~F+n>qWlpX2a{KZN_- z^KZ%K!TT;7Z`#bYH{M83QcSvvwm8-$t2yhe&v5k9o{q^JJ$3Z69{0Z2y?M|>9?Y5V z{Qx@OBkT7_k`~T~JZ}**q<{prQ>TD2fQxzJ1LMc5^1K(lfQ_3r@vis0hr}41%V@Pd ztu^b>Ea9>rU%^rT^bkZo6`}EpBBwrvvrN^PX}k3WvZzK-4ZI60(W~xoK!&Pk%a$!z zYsc8t6*F8K01FA%P#u!}Ma7KDHBH5`_dcMyMhsa7?GE@X{LQ&K=Y;p3ty`ee>6Dx~ zHGyvi-5e&Snwy)W)0r6|1)QIQwQJWz03?tOf9uGHXA-DO7$h&O_5s%F-{RwCFH)Ix@H8QY9;I zG?H*P6azp;$GD(0tB5}%qum%=%wPo!|LrC2MoC%Y0~Iqmce*lLYO#rtqGN>uHf|y& zB-d08PsR+?vw<8b)hQiv7FBet;)6>e^OX9zwwS3p3DBkEo}GrZX-%ZrRKm zUi&%>4mEQK9?@XYsL7{_%(UH#ij$10C91JXPZ0PfNpPC&P^s$k^Ia~#{Bo?d^!xou zRj5)(YQ?x28~}=7$}n%J6O1wSDjpT( zDD>GxFd;^aR7gCnH3LqM3fZv4gYT%Lj^eei>(Xwu@Tz2eXti4Odf{^qf5bhx=Gvm) z0oWJ-DMG6SUR@{|J%Ed9)}x|$!dS#qfT!4hBTP9lcIsxD_3`i@5~|~AB*I(RN^Y{<7S{}_;K5=x(?rCc zReB8>Zy`1EF4jl`wX951wT}t|NgL)tmKce#5Lcou252-!a4mw{Xa&q&(6W{xptbo^ zW{3}CDR#Xy^jVkkoVOh(socUWk2G^qq-r9>O%~o(OW>{u`?g`sIz+*0Dhlr~*^Jcn z;03v8q@mkYAY(bv84c@BDtM}?rECsGN+ZD*kMi+d?|e7v6|n|mgkz6+9D8io9V3RT zulgN7{NbhSv0--(ddP$L#y7vgFMjcJp7yk-l6w!LnCJ~LBp4l(SyjyPK@8gw`tvy^ zH?$;YD?Pe?3#Y&B{cL{w&8(eINt2wMKFm4(?XaUsZrX~O>5!_S?S+4T-iz7W%wQGH zdjE$x>x};gA~=`xo_D{C4}Rc%^m;uELSiM9Ww8m0LCeYj10^R{4H}0iB3cc84ydO0 zJn%mEr_-4MReJODSYx^HD_`boU;k?0%uw*Tqa_9tNlQh+i=XP`({|gM6(V3QxEy0G zVoe~+NerrP2hsLm_3G6;-~kW77{i%oo;m&)TOe#aq>V8OFzDbQFiK%7WLb#jNz*hcvO?~{=acx}LaK;P zExKu|02kYGKvsbY5m;163~PtQMWnzh+#;H&54waj-gx9|x(tJ1+IEq(9S!MKt5$K} z``#C0WCbHI80f4;sHM~o8Hou}gO^zloX{m|tA#qLmS)t5O?dM+Z^x-CTIivoVK@T4!7>{YjE zdoUsXx!^6l^B6IBr##{jk041B4*2r}f>X{9@+j_lm%H$1fBL70Dwkh=Ip=-xd|rOa zD`~f95NlDdc=e$gU_&11vO$b2S5iAe+qdZ%Pu80wZMT^3Z^e3W(j!YO*bay#-7-f$ zYata{YQVuNFKkM4L>2NJFW}XaBnka)50fNVo08=jntNPo*pEsjdv3@8(&22+#ZdYM!o9OBr`L-mQOy{|q6wTZFh4(!_a1A* z^>r8lskk+~_ay0fsjfbP7y@h7tlUzx#@x#l0?g=Sb6(cDs#D;7Lz<5=R{Q zHFn>9cdUgxgQJf=nj;_kE%x7kfAW5YG4K!n@DCh%==t3Le)mJYuSIxntKv9q)3$Wo zX@Pok)p&F94HwCP46uVxAGHOxx{gO%`nXX_}B_8OE&a zDhR0~W`e%xNQ&c9G>S*9HcQlyb+J?Kn>lSV@|)D=abd8aCI@3~iU6%v0B|>L+QdEYc~5#>*sx(YuDSYZ_S|#NGAd-xJ@(+- zFMNSJ-|5aJdcS_%I?lV|3LgBR2USv5mU+NW5B*)Q?I5y7OHjc_Fv2%z{?ThKwnjc% z9n2sl%Z1NHg?ohZLy6w-449T0y4@{%6O?abY#^Ew6VnC#%A}JlbrZ_+_szJoWM?Z- z;&+CUp*xTey-nsyZ{zvb9qiHHo@Ay3`#VW2mtFQFW@b9+~ zPB`HNo^;#^n50EN%du&S%k!9;;zv_Nw*|!T^NQ(MTmoaNnk|ZT_j#kg(SI?kXPAZ1^`8hhPSJCZuF~&sXW-uAXgj%v* z>j#H-KIXxP_wqcDdL}k4-%|-jf+Uvt`FUpB9lUdx#FFK~JF3nFVnAa`;^AuknvvS$5K=u9=n@Q!zU zn(tnGR3kW222+UOUFE(m1e_ ztKBAxpiJ#RB621#psSSRqSLm^gBI4<8aIYUa$T!+rCFBgupY_RE_gO%KoCasN=xLk z2tY*9%-~-MeG?K~u9zh9y5t7ImQ3n_Ah$xE5iO;FCwjo4QxM~J*VGRH-84;@o0}&| zk`)9nbO5|ni3J-_(!LDr~3-Lo6Z5g+2bjYPtMK zjF}!z+N|DiS3Y^cC%Ed;3wYs4Co(s0*|6p=yx>KT2eHg;?Q-2!SM%|Ye3mYVvaiW4#0?jRbJFg&CM zSYqseBv2}q4_2VOly2j?+S#smdmQshUvC67(?F(ZO@fiU1o?Je#jt_wTSn8|C)L76 z=FosfeuohL%Ytbqd7cM_#ScQ$(=-j_q~iihnzYCg`W13&ul2|VYd z-K%yfzhDxF|#CgD{?$b?EQ zfjqQemM&H3SiK~G#j%PBm6ET1;q@B;=N4#X}cY7jx5Va63fcQ{0|TUtyFp{EA&7kV4@3v5`|GeA&1`x zfnTkpw~WcR-$px0SpF1_^7hK|#9Tum2pB_X$P$eBrBs$^`0k`%TZkZ48`s4!f$I2i z=^}H!c;|GKZ(^AxKv%>VUuaUBYS`5-0Sb|MUo~Z$3>pySQ`QsoaX%MSHI#*U;Z+!&MF@9$bVq3efAE2b8hVJ zUN(p^*PUZ__tiY(l`rPpPo2p_9&$epy5GTk^Mdbj#J@a?JGZan1uuRJTU?55gB43J z%dlyRL9kwM23(AKFdAsH7M(*4boe9tV6nL8BC^5{F{sN>NP87gs@zg5|1K_Er5T}G z?y?vwU3BHHw+($9ceu)O$1G!xqh-&bGz;GqeK0~k%-blp}FF~Ogksy;4-I8#^T2`>IOUAg5lacRtv8|&n$>)Xp7 zU6FZE*-jU}jV5JcecnvA;3g;myHojrDv#SH!2visJIkN^$)943rPXTVe2zLFBa1`T zr##PSr73IIuH~3xj^XRy_%;0Ji&i(V8W-i#S*PcGZ7u%@*ace&%8xXYbWKKlOu z^Rqx1q}V= zy||U0$*p)j3WBd))74!C6-&Z)WdvAUHe2-2lv@LNmXl>!47HR>9{tMbtVxp4o$tm< zDFr`I_?z?Kyythn>*74T?)8u6jc>dXV+>iIV+67+Z2Xy-8P=>$C-#aI0XxozT9HXo z%$=6`h{a)=MS#R62`_x%W4PicqdJK;xZ|b)7rQ!;5r5u640sPn8bAh@gYT~i7(|S| zEMTs^ds#$pJzkis#8%w%cLms0$GY_7mKE@)f*yo6WOD_6f^7-kCn6~Y?*JK26 zSa}2MwwEN^%YKp_@sW<`5W4dGUpvZhRhu7*+zJnWN3-R3KuGQ0~FNStTG?i)Ds%rm**f-iIEp^v8BZj)sh&bhJEx$H=c4k@=>eI2L0 z;NQq^p5w|Jf6ZIo_AWMiN1lL5JiUAl-5bfK^jTbj&6(_?UYyNag22^?DhJo@&zlbiut?6o-|OziAw& z#u8hCqT&&iQBHt~0pLoV?J@nU8Idv+C03aM!BysjK{%smNya9svH$fkdodL|eS$^nDbESxKN33RAzcs+pD zc_Qc>Sfvj*0B*P;UQ3I)maCTEQmaJ01jDP1y_{; z@EP7YL=sLq@f95TPp_pAzk!=O3+ICh*0h-03Td*Aq&3KccMiNuum%(hI8cj9KF|rG zpugtoEo|8cyU$J^{j_ZZNTCw>RfZ0kh8o2?9ly{Hm_m4)cS#lptCrhQ-4UvoRMZG* zBxqZ~PHwBRCu6#^k}0s&lpWnp3v~?F#zZq+{m^#V9EF?{2{pgwe#ohjbnVA{V#FA&( z2zA6Zff&|x@mqzPf4PyCYm=Heet78*(e^4XzwAD5xkdYS^L$*=Hb1 z^s0#3XjUs;6ty5~%AS~LTpJXRK_eZQ3k6vTwY)F2r#G&OrBu0hiqVt2W_No z4CB@!_RS#DC*&sEXz;}O5LX(UiF%w^yns=`dc~U@FE-4q`gmuLU_zN&1aAsswg5ZA z({W<4(V|yT5EuANDw=X2#Uwrr9}7?vIn+`b$_bE*jV1QNTo`ZhCXd*HAR3`84fhIQ zOA@c?;wlN(5G~eOv5vw#xr#^IR1?U{S#p6BL*sLmz@Q(VTO_b5cnWs5a$F<$W8D}Y z4zW-TMtF6cTKV5dN?}8d?9uC2-j7^rt5&U|+v^4r%&LJ_`4Q~L|N2DC=H={1^W z+MpQf0**-;P*_#(Wnx%vO*Qa}YL19v(l*XRKbyhz!Ol{aL|28}Lt;$1SG#@3+*X)v zgTrCO@}Uo&%}ZZ)FKnwgA3!A^bkZ2oHhABnl}@9MZnqGGDyEW4RB%>J6b)H=;LKn{ zX!UTa9uk9HP`z|G3$#KlpyREXF_JgzTVFee7IOI1&(=vu;zk`})&3V%11CEdboAU= z=I5lr_^zNuqcBO_Vavr}tpN@R4&jptz3+A|B^ zA5tu2q1jkr0Heu>UyQ4aQ6_Q`M>eB>UYUFFu{54q$*wX}L zNb}#T#J*ZzqJyuOJU+g>sj4SUQ{MKrw=pv_OP*(BE~k~IL8tXW*F5mR1G(ot?@69V z$AK~YpS%4Zw$5z{oN*F}l}8I}$hLtP-b3CEc`Ui480ldoL2}`K2 zWML4aFdB`5mjK16LZV>2;zUs63yQSxo(-Gc$N3Nsx$#(ya}$A`;9*EWB^c~#zzC{& z1Uv-m!t+!Uff*NSVFm|FG);y@LnX#}ha zA`~0FY%Tx{&YUaf_iV`C!qry?u#kC5uYO_-4q2BXm`c@w3QO<6diD!> z983d7ql{SmS_K1Stg$FnCxr}MQU;DxQ@KVlC)HqE)mjfLeXQQlmpZ--hk0w|;yMI& z=op}*4B?H>L@UnA(!v}XyYb+IE-uA0h@^m0Bec}ImkD1wY0Ih;1sYuv9~Vy7VE;%B z>>jKK8F!CH>_hA8tfqI=E31@O8k#SyuH*7@zN#749bZ-Ea-jy>H3CcVk%b}CN+7MrA4lklYrF5t3DFQuR5{KF$3#caD1zJrfv zcp&vIpO2X3gBY0&Vc;$r=2ioVR_?m!)|+_ViT_5YWwGi>oG@c%@tUE=;B$j9f|zdD zTM$7D1JXxHDmCC^n5X3U5-rpivTU*9LHanE#j2oUW2m{}1)M+{%O(~n5k@IJ;l+HU z&R{j<-59k9^Qd-E@fh`3AH3l{8mFe{S51zGC=Zn;d`w0OMQeHeQhG865fF;e@SuG3 z3>mdJIvBbUV6j-2;tK;s7FMweAtQwO@x@k?01hPH&}-%8gc)B%LWJlSqY~zs5XDk} zHevI4&~|xXYK+KG@+eHgopWi$_zY8SG*76wp<*iOD8=)aiqbiV^;dl819>#|DTS^` zj`(zN@KjKC(c#0p5WGre!<-Y1Hq{h0^N6-$)J1sTs3MYqCNUZ)j}aLyz=dcTO`<@N z_`Tw&3kr=4M+8J5NbmzND6T1qN{-YHyB)KwUcH*b4?i4h4PW@e7aBfMpaw-~n5!e| z5$Ts9WZ1_v1v?DF%NUQ+64Px|{Y0_x^K)~w+ijeShL0~wiwb!T?RIsX)iR|KU@Muy z{ZLAm4jrnsP>2zryw!&F>(_DPP2qQ!4-TntQ0Riri(#l=D+-i|UoUf&wmfcAM+~AY*}Qa zvPPve=sX^^kSVkY0AkvjDRFO)q9Q1^2JB7S?P|#*BCK7zmPbGO(TJF`V$;I)*{Ufd zcF6rHjmDakp5iR%PDS*qq5OG(%nhBZRrYE#+_xCjH1fTL7^=tANgnG0HX{JepxMz| zRS$VH?tA09DPvsVvPd5ncw{m(hK6g@x-5&#TO7AEP0vDOkp(^{i_CRnpKPV#wA8O9 z3(ct+?U2aGTvwwoYjZW+Cz*7tqhpcb+YdhvX;@k#{V|P8Z}7HR{BaK5Lx$`-VqE*^ zO;~&`#ykgU{HqzADjDAE%HvDE0E<-gPqLBCyHR0lnC(e+IkJld@G)fu|67aMF0$1iY77D#9$5NOQUcx8` z$D2fR+k?0gNz`b#hEKQ>N^}yUG6K+&#z>L^lnIe0gQ^wd&r}lu6K`VLj1mAEQ^Z&p zRQed-3LbLtC@CdsSSx%irf(jT6GVyqwK_rU+gAd?~=_6Vo=^iKTXyk_(aIyBK zsEgyWUV?;?(>Eq}QG^){HmP$lM<*7(`+Ntl8tHFujO&+{& zRk&L6u!bafOH4#m#Y^dFkrGfUQ0fBsDN=w2(F37&u)#;rLnP*MlLQb*0w@9nL+_5w zI1oydhBaI*S2|*elsa5KB51V%t7!tI7p%j*9ijmQn3(0ZgU-e|3@$`#s-Kd8K!*#%4GPUxiZ9MSR2uoC)Q97b?jjpF42Ta8i7^I~yQo{`N zxC0AN&%ih|?| zAHCSys0^arCP}N<#sg*Arfu4$ZJRY@N*Sg_erZ)KHsLxFB6*y5s48jNBFp;Z zdB)642k-sZ0tm|nF`UUTK1Ey`F>|E>nOX&xk$AzH1XY9g4x1YK-eXM*%@sqA)x2Wh z#Q+~4guV49a#;voLW;qFd_4H8Qy)MF6-emQGaVA0$7&u;Swk*ua>)Wqff}3?=^7f9 zl3+j^^*Ez}cR@YYCphcMSuc!O(KM2NdC&wypUirUx1o>51++;E?hPp;LZnhZQe(dd z5e7cSETnizF`8rPh4DqcijY1LjAyHYNC1y=v4r|^i+KhHDJdEWL5(M*Z>Yx!cpEr3 zjCNvF2{;3Z8{|Lx7$%mg4?jm@fskyHAR6Yt8ILnLiRPf47)jtjLup;34sdu85>@i( zSqsrHrVIznBzPcNn#G6P6TH&W}KsM(g=q*FgfA{FRd8a;t*A=3c2y6(&8gWhLT495XFh2rVnZ` z5kL#jnu#e>A?*g=#>KTLF*QeJ2V~6}v&sSirD^c`RFR5>z-QSO`ML6TST(BEd*

n2?a*$OLjx5_O2raWaGRN-GjxX2w#mRTy>T;^~=$ zb~J^3OiL&hM^Uw)2zOVBeU6|>sv3AHu=G-)k7sS>B1{BM!3UO8#OD}^M3YAGnxkq^ zV^I_v^L;}3NP{Mx+-$|>?Qn0^$YGL`k${QUB;@OPn}YNLC_pX~KDtyw#YfkD?#9KrhUTzH)dH|Lb9w8A^MjBXa79=whiNuoDmd|~(*+?V_Xo`1W zQ-+f5mS!;&074LmVPA!;jJWn1Fd!Y90};W?V&7EKG(PwFm-6s~|C4_IZX}&L9#ak9 zE5>aqxwC`$9B<5(CB{_|W6J1;LFoSAx>*+HI!V*g04oj)?>%;bNaymbN`Z4Jyli(dP=?6_^uM}YP941*y=C>{h*s~Qw z^4xR2##`R{Kxow&(5B1z_PWM(5aOI9%+1ZyN|Vuab@dnn&UvgIgyILjqrgsWtdz2~ z3`MV2(-Ue@eZ5|pj2%YyNv1~metIPIafPeOv^ zGt6rbZ7FIz;sl>3xK>V@rgXP-F^NSxf^;ouE9J@0cm^#y!{y)k7~lW)&oDBJXkduI zcF`ok_Z-akkfg(39C#oH?7y0?op&CIY2%EL`5ezTW{`T5`!m1EEpR+)E*xq z9+HgIbjW)K?*b1;CK-84+5ZlEaLez01<5+*vy4oFL2kq{5#l=d`SlJ0Ipf z)(FkAjCQ+S8Y;!V{mfx(LSj>VZ!3wYroODBMjY(Oxw!?qC{Z+i=X{XYg&oLORNig1F)NqS4 z9Q?rh;%>Z(pIH^ zFz)i_ci~-cejhnXD{bLac*@hBLAu8Bzu!KaZ+_#aXwt#~v+WJ+xxUX;*Zd&>4(f5n z;9O3Ur1a+JnMo|(3C_%d^_Z67geUzAKRWxZ+>-6Z>tFgTUh~rb%bc6R<~>X&C3k|s zl1iUEwHQq?rcd9(AxAu(O~1I1i+*q=*beK~Z{UefIEFd?$mh;Fm(AC0!6pgwCc!d~ zce6a~pa=2if4Z78&iEXev{}FVdLI73`*PkV&%);|ut@;*wkL!k(C_CYNrEwfoHMK? zPnxy{%)&;-fE1D9v8W!>1ivceIT#C~9m>i{C8)4;if$3O;~novmSts`R4Mr?Dk%@z zU<)l^ER_IAY`brOZnsO4RIru`H$*`IT7Wn~DUF$uA_X5}NuXKlNPMH;4<%ML3iARy zkO~4)9vUJ+*h`qtP*T-klVofTzt<2^Qsdk(Lw2bSK$#-{+qq_=HSpO6LaTObK=3$i zyLJ;9!|=X<=Vd8~hBW0lL)h3Nkyx?1fm|tjY|>vECr$+NjD^jcJh;&g$uwI3cB`Nl zYb?vMplGW`V(q~SDhQmDqyR1yW0F9YXNu}1CO9n$UX93?6`U}NbB;6(AjPkL{cCQz z=_dBtYcJNVTSu$aqSb1}2**0pWv|y|>(;I8vBw_e-YkHZ#aiLjRe?=|c7n%7Jw=bYJP?X{lg^L(Dq zXOBJh*l3_cO38{9D`;(LC5j`0ARtZCKEGE=g>x1m1dl%YCuu3;N&b=}}~$@lcT?z)>p5B=Q6ua{C{t>x{v=QC>5HoXc5eR;2Qu6B(iFTeaU zJMOsSMlZ0k7Q=OScT*~r7&dGeS(a_|bMox7&$8dX`(d3$YmK$GalU=I*3NmfRy_aw z^X#?PUb$n%M$W;-iKnzkzT9`nMc%V4BM1U!&Ya2Ev19xE-u2zPS+i#I z(U0!f>p5_)Z%Sfy1{lDD4?f5thaA$UYr3KPZmng-iscL&Hf*ETisp6k>8GD&-+lLC z)TmMHw%cwE5Tf1!BI>$;egnC{THr+eIO_AgQKa~=+et(wh=Q1r+it@*zjG=N-1``5 zxq{XOj0$jKC4mJkB8=4-LaZ&LBZpQY)y{-A9ieqhCJj<~tVxIh&1x)4X`=Qbt2)*o zDG&>b%RG-gHj&fM{27D8=}dj$ZCq6l#Q_6HZ_5pTyoi)Ce+^iZDSDn{^_kzzB9uKexK`Q}jvlKKjP45;`DIt-yA@B~<@2&E7vz*SPB_9B%^1uYd? zO2RN~Or+)wL@0%^xjP5WA)UiUfQuPDb`0M>?Ice7!)+u!BZ>knhAh%}Cuk`d%F2*N zf&h9?Z|BW9o-m9VG-7jxZ8Zex9f_0# znZf&nN1uFzuYLDywrPEbXD7Wu>LpG=v3(GKxc(fDI_6l?E{_To-C4?@0%YE!qX?Uo ziNYdj#bJVwky~!b>8E^!um0s?e0zb>9~o5>9~G!p$(oKTQ9)oaXctlrV+M*U-djZ2 z#+SZ+5^p}($#XBfj>{}rW_kGW$GPOXvv~Nv$5~^$a0se}kp5DV`hcOshcRrpCQz{X z@R8hb(;wM(F=HaC)hbB>I#{cUx;|WFgM4_=un}!Omkj|j z>mbrnQtIwTct{f{#yJowo9sfwdascnOQ2rl%Z#Z70UNf7Yd)1MgD?QC6j_=ztR>gJ zO+k=5m;MoLZ{1c%WHK6TX5rRH_Odm&W zvWlQq(bl!Up@Cw~j3i)#G1GdAI=%74XgwO)n&a4D#Mvv8%|1xRvIs4Eojd_%NWC`QKQ&lhjEM;F@hmOhS1*LkCv7e zf*@EY;aIU^1#{=lW6wSJAWgFdBT~G_M(jNez@V=A@#dRzIOdoye<09q`t<1x7%+e> zwiubWlq=n*&&#q5tu@9N;y7l?lqnp0?3XwCIP#dq@#K?F@aa!~dYz>3z2hO@h^3{J z+;PX99DD4s8=cJNdRl_V9($Za5B(fzn$}#%`gT0V7=*}+OWobw8+|PGgrAW~>E!JAXFnk|5hhOKM-|gQY4v3LTMs2$-$9?U1rp|mBLwA0 zlD$5%E9KQGzxvG;luRqEz4qLV?v<t1b?71_!`O3R#ZQG3R{P??!9Jv{9%$&xxms|$XW&}Zj@1J}$gSX$2 zmnPlIt+(GpwbIRNZ@fll(t!$cTdF`sq|W04FxC>(XoO0~$WZasubjYMAKwX6Df8#6 zujH-LX8h#rUo&RAt@!=5SMmDtmHgsgujBjQIgv*nyq7m-bkTqGPF(n%gZRZSFC%Ih z$ybm65@WUS7U!BKGb7yn-@yBq$$M@jHm!9WO*Z&1% z#NZ*DbNcaLWb;wm5+S+t^6OZhc4DQ(V;DSSD90XkI={R8DuO}`sbt8IL2N#B5DOPB zLg+!fGVK)>E!z*Ir4UBgBw@>~x8m3nzRI*2uTUv>VA3K9DNYNb;jaVwuad|wVc z@K6@dpU;0>b|Gms!%9I_wXyTAJM-9`H?gJ?lC~S#2extHg}-67Yv;xrucT|*YCe1D zf%Iz$x$mzN_~<^LWS2n|9(nK~&bjDPmX*6$vGi?~ z&w4$0MIRYnK3LDOx=pux)9Ae!2zq$~3jtb7jLCTJx#yTTaT2Yq?HqaJar7JLSh>=0 z`)&8o(q87ULr!GSpn+sr8DmmF*AfHWV2B!HX}0aESM1lWn&!?a?QFT_mQ7V>`c&bs zS6Azm&nz=dN#DjZF-DFYNf3l+tx41TJe=!W)w%xNTFY_A9oMMPZfv3+MIk%xxMS@c z6=|9_wfnp`w5qjk{Qd8K_l%7`mb`!v)adrI#(EG~Yx_TPX14|FWX zIUWM2XGU|+w`uD!f z!8(JWJ{Rk(q>X(Zgb?hp$H!~T6jgH?QhjFNh7UrA5+%VH&*#4IWe)n> zr}^FQujSU;ZzZ#q&4-TR69?_ethsNn>mHwA`N~d?IQl?TA?DdBQ@Hw?YnV509%uge z6ugc&@f#=e`J)fz`oG-3fXzqnUDA<8=(A)AlflDV^{kOpmB@w-2; z@Av~4IHVu9-E$u|-Fh42#*M+*DyUpU^jkkUi}_2Ia_#T`okbh9=pyXVD zK6(s0jN6_O142gc{4rGPAdWl!I8HqNNN&CLZ|rvPVf^q1-@#kQxR372j^noF#v5i{`mRtV9R@?2ynLqkAao|afCsm$U2Po-rSXOs+ zaovr7V)^2^Tzu)pTyfPE$WU|a318>$zyDK?*8DGydR%cu@=$MZ%cFfjPy4P^&l~*xu$x?P4 zyA6TLJ<_Gf^;FPcy~A42LCoDFgbai#n-QC5PzVsy)En372FTB9Qg$ud)=D%_H;fFJ6(nMZ={q-CjrkN4o z`@Hf94?q9OtK5F?u{jC6*%sT&Ua}ZDh*5X&=^aDXf1#T+HR}7G-%ypeM)ei%iBhcP z2GeL6@f(h7YZQwtGdUAV*U#-;u1!q{Sk|Tpo*W#ifeB)*Kk{x4Y=MS3bLMd7nR}9% zS}TjB!SDATrcZ~FBV$aX=dk|%x>k_vrB9dp+xvzYgs4%eo9*cXmAmH{$0ioW|Mnqz zz{I}#BJ|}Yz1pstUhl>Fg4#ULYkn^C#LVFvR@-&08N1$t@x6f2_0P%m9|+jvaG07DjfcW!_iuC$6fcKL&uFb+{DLs-sp@&d5c4cQi`p%+Pc^Lc)wq99AQjG zTU#6b`}eQW_Ba0XcGzJYrp6wv-^-r1+IRn|GsdNoj2*k(hVOaJ!n6)FG`HspWaAf7 zNVLwGdRl9;EUg_^{aVWno~v_?Fw6lRr4-w2vrS`OZ_IV^-ZO02Frp~JIZG5peSWWV zJ!^oqmOb|Pc!Qm?vB%<^W5kFNc<&ps1M9xFN#m`t7C@S%J@+h(8i0kYF>bOnuV~eE z)OxzZ|1q2AMuQk>nhSQ$e`g+5pWAGDXOVbbe)$y!4c?p+zjhLT8)scbaX6W z>WeSX(UI}g)6Y|AP05h#yz4Gpbm4`3;qW8aY(xuT+`{-zf0}V0+ny_~yo^uo|0%|Q z`cs5~MhS`6ib@8t_9RZQ#i;FAxo{?5`NpZ7|I6R-(0%tHL+I@2V!_g76k7{;X{lxj z(mN(kp3Ikz|0;X!w=d63nT&J^rL4k&`AeAl#%$)i`3i3>?xufx8(J!k{Nhnua>d1L z`LS{AGH zdFT-?y66%z<7sOjz-JFVfZzW1S3Eu8c_utIh2_gDNSU%?&3r29Y8EYA$nr&HR8r#R zyB=nb0}i7tNI77?9r*3PT|wCv@XoS$_GDgoX$H?tnaY~g9W|ucaKhKW&IP~vH7?8W z-gD4F2l2oI5Ax%)&SL9rw&s_=`XwTaaY%-2HG(07H=}d@Vw{#teeDefjM$2w{p?44 z@07zS_HSj*a>4lVSvSeJrvELyOb zQn`X9BefNzQb?(oF=GakCQjnIKiNUU*xGLpQoI5k-83kasF?3e$q=^bL}Ljh+Fz$k}{cf?7Zvl9QxUVc=N5fm|UEQKuN4~h#LE# zF|Wz#{yl=wV!ibIT!$lnulJrxrLw_FXfFmwZGJ~l-V!VTV;yN$t+5yt9?>Ka-M9F| zIoH#|+-R}sTihb0XjIGVt}Ie+bgEJVAo9dCNz#ViMqdiqJ!0mHEX&U)%d$Q*PMg^X zLI{j^`D)`l-dJ=kNo`0?)xg>Hu?3oiWvw-}e{Vcfq3(R6RL)HCzAk3Iv7bkS@sQtp z(Exz@dY_b1jrNWFKDiCz>$b+uRo>^<(|e?^0Y<*Qs(Y--|G(Zs-xOpc6RkpaVGnH)nCpb=Na*Hf&x^NtdqT@kgG-c#qSDVOx&k>n9w` z&wluQGEv~C7aoCkU=zdTmt4++_dJ5sZA4j0+L7kHgjTYd4N1ci<pSfArK8wCvSewQCCfa&`u3^3y-HEBRg!WyLJEpTgBwiQ zDO~KZX%~Tr`Qz2U=k7oKnXWZm2od5^4MNu_Q|=X$qSUB3!Ws+trOzl9sksPB`gA zc0YJLr+xPnUV7+0N)?5VLXJ6lKVE+A8J5@>;TJRatyLWJ>HR@=vC}8M0MkG$zrd1} z)46WtGRkR8Tgx!Km`NcJnC@qEH!7Gzq=WC~)#--<-N9Xbq z*0-Rx8-Y(2G3UieST~Z`g(RYz1c@X-DuGr4mpQ^LLSzxL5R*g(tjA}PfEI)eC>BBz z(}7nBq!5&gByj;Bc%-*TZ_&z95G_cX)GBLjcqjO;i?8LO`yRqrg-s1<28sg5AN6r& z&YZ;Z>L9cSEhNqhbWq5hODxr@N zF2MK#vALWElL4v+u$pLyb;XSwdK+xh&FTk^-7rr>?y z!%I-ik9ZW5^!Sl@m9(r7?&z3Cak%O));I0q-#-U4%CzQ_d_kO3uxvA2(b?E z^8RBS+3WAug}VEC9(g?D8$e25^&-|T4At+6^B!kBpZV0M5YCb~jYFWd!e1WQr%NT zTl;$SbzTd?FlYjG#0T1onnBU}^R~WcOw{nD?DqJ2LAs8F%oT&p~kv|rHdWMo_HejXHMhxJ09ntgO6d` zZTs=sjOlb%Qadsir|>UxSMu7+t0}+vCWX&$!zdvzRmI4ca$5o`LOyNwb6i%Tp64 zvs_ld8cd~1W(`|!J&M6?0~j{2NVa4Zs~4c?b zmhQ!iIPl}UaP|+r$L@!m#Li;|lV-1T`~CNF_Rp?H70%~t-#(UWuJ{8Cm%7I&&V{Cahe%grko=neG0*oICD#kXS~H9=<)FKkNvO{q8ro?z+oJ zO+c2eV(Gk@obh0cxU=dPW|4I9C_3{=FWVb*I!vcQVvmpuyaS~;bvGrih?Q<1)&4<~W-Pl;2 z!&=ApzV|(nB*A;ngAYE)%9SfQ_0&_*TGQIv!jviVNYj+LbLTQ(K>tRyL*Rv|<$$D3w33Q>Q?5xIT3Uk_f=OwaJL=h;KCAw8Mi;7jl0t>Yoh z5RLS`sTV!u5!agmTdVB#t#-C){NH;XazLR_D00GyC*;-8%upy4DVNK%w6svIR@rBt zeb{H8ebHJmb?Q{^yYD{EIq!Vp)*@-Ar&{Tz(%nI6@luomq4Mf%pf!@*y-wCFLe>@_ z?6E5J@0pl3_FTX224S5;Hn|Z<6<|CFE%4S5$wH1G8c!y`hecG9f;R|nYQ2fF50`AM zV|ibd>mEVv587AXEXutxA62)%eZS{Z2OJ88VqUFw9+x?Epix0U>MD7gj*W<=BB|ys za9V3jlC9VE>HP>EzMu2^u%ru`^&6VMZ_K@4C!VXV(N^T_r`D`W;51f5WFCag2&BfE zs74tDYXyn`LK0oh+y!V4VpHgV7^+1XW zD;N`!R3V512vQ2tkfeF>Ez+96YmD>wZ~!uKC|?2L@JL+EVN>`IcL&{Q5W^V|!Vt=o zOMmejKD+-;C?8P_BHo!jgW&^4v*YL;xZ|%6@bDuK(O&onS6{n?H1SBEFlW|l+*|gn zSlq#PPCtWjJA8ze{t?$*_TSiYnFsH^kGB`S&BwMMN5N@2R(Im1AP6jX{^bvJFJHjm zcnGGlfLXI&vN=%v&>Fr)UPTG$~_*Ae2QOy7hJ{rBz&Y<(2IB+AHihb{q>|oWV2COhkJ4 zxAV{Ez)y~4(C{HFTegC(Qi4(u-oiz{{57B1e{W1;YgUvwWhn^iVqum*8E(1pznJ&R zGi*P0bWY>TJl-ohmoDavQ%+&$k8X`ftE7oXTf_4cp5kZU{V7xLyBF&n%62k)<|}ls ztP&{@!jTz^@CusA;w(_bM}8$qBcnFy~kZolaUx)(2I zSZfQVN(S1aeVI!xxPUK=-y;_}lo=1)ekWZ^R?@%!K(4yv*F62$y#y*EQiA(#zJaLY zP1@SqF-c17Az874lfL>*cH3tUv?!2tSDAF*Z5;RZE9|uMPPE2tRLd2d60~nSl4@6p zH>b@+M#DiE(q)VI-9?wP#n5ed;=%iQ{jrB|cwU+E65l=LG{$T@gm?b_5>HHef-(_G zSf0J-HZCi_&cK2F>FBm32t-)nlJkGb!3Ta4k%c7Hgr^?6g%eMHmXB<;9rIqF!;4d& z#+xe7KR1P4_t~364*4YKe&ZxwoG=w913v8D>^gl=O3?r?N~IEi``ayy9lIS@TyX`v z?6NC+?ztxuCr;$T3opbu$G5-zZNe~Y(Elox3WY+UMs>`Cuwt>WE+w$m>kvnhNEcDm zSrxS;MS#TSE{Z{bDTAU7NqFQ9-YT4SB|LncX#gWz*r}~XA2iI92*5)lR zVF+245`>VYDf{j-k}S*Jd+Sw+hFz-z=N(}fQ>~VX#_02xlTY$F#p7p%jH#5-Jp2yAFE6rU~4egA)HL&?v^2l2I z()%y2d)5*M7CnEf`FMNj*EF%9{H7`W&pDhYO;ZYmIB&ra)b=_{p-{jW(-3T|yDU8L zzyri_ftzmn6GMg!=eRF?lKuAGo2xIs7+qb+QC~V8E%R2roN?fKv{(LNSd2hPiLQbMiKKBZy=E{Fh6 zC>0{B5NUz28JY7)5s-_xZ|ceH^%&MPy9Ay%3-Ab>v`Fc}II6}I1d5a_Z{0xPg~1z- zB|&5^Zzt3lCebJ*aLyn%P3ZOR|Hh<*VT|{J%t@rz6k?APDVfPofyT=q*IF|v4nyJv zi7gU{4DC~#_ZTVjz4srmv~4tq5laom5W>(he*gd=07*naR0o=_`SZE+zh6gW8B#_p zpZ6*czCD8^k*H7+s*uu}x!iTntE5gK!-US&D|mT%i70Ai+DlWJ{^C@^sDMhl33QAT zFk|X8X1p+!VycOQyqC;nRdnij-;avV}LEeGZ+K$&BH#dmqMR z87)DGGhKMmO4hNAhaP+fr#(T%5a|M$@kH9QbpG4iaq~iyv1F-*IH#OT4;YIS21OZF zsq*(JFY?l}FOnrGMPU&^eG~EPZ=EH(kE~ zD=kKP7SDg1+von3EOYok5`{5oY6)bB$jaPv|2=q9LK9Gstt6>o!5ed!GjkS!R0I@= zO_^CQO=HfqnP~6uHbE3x*y|H}@Yv%|kyua3R}fmG(=w0W^$^yks7NDAj;OtWN-O+* z;*-pHR*;u11V)5-48^pACm(tUCt@5SMrgd~VClT2+tYu-5%= z@&xv*Q_bzwcJK2_8g&pu_?~t%eBUdFq6b)M=y9wCx~vZ`^y>jIHjRJgfZh`XA=X-q zG3X!{?u?=W#^ifposn4YIX~x|a{!Uk+m*nGjDrsR6bF9lK(tIy6sfqhr{zOK8J6ODO39SQ$(XV|dkSEq+ zQ*yoT46bWoI89b7x@e!WZ22;XM>3EqsmoZhd>Kj!h7Q?=LJ+Z{ql4uuRx!B$01AT! z(B9fgxmreOg?DCCtg@aMT?aANmM|ve?S*B+w3D!Zz|diBMAGx-+wb6g#-O1ii6q#x z%7PWE3Hoi$;P#k+ycI2#ApJikAl&$dDxaXGz?LLYY6%=DA@C+c8G}qiia`v<;Sz%; z#Sjt%FznAmHT!6>pk;0*Tipo+< z>TwndVMsy@nIZ5J^OjI40xR$`!aG5r9rzTRn>LTQ{)#5T+;vRO(?7~m@@MODCf zheO~jXc-}8D^etol~Epq4T)qM$~uHg(X|T2{Dp6D=C{vf)#?--6){PL7M0|Ps8<9c z!lafU6c}$2&Qj0;g{TEB6k3N!9Fh!;Ao49F)dVFYuo_H)lVy}>L5m_LsS+s(Sw`d} z%7u2NmTVzeU*|)DFy?mD2bMq1+UKF zydVFZ)JF(Z&ZhDHLt3Ni{kA=nvSwkWdW%V2Ovrmr5awV;?xEciu+=O5`Oh>oG1lj2 zUtb_|vF{{jl~b#%HZ&}nlXN)#lwv*y32Mhl?P@BleV4VdSD$cCViQJStf5i?sn?(U z09^2!@ALA@FXyclYk5fedLova#vr982-sk`R;(reDTJW2tCKLyt9sqt-Ms-4dIZ6m z?^%ynSW}SmF40b00B1o+Nwo@5)B|1!nQMEvhK7j3)M%E@K^%j30m5sX&oDM;b|75d zTkY%26!0Vnm5Zi^F^&7WY5X6rHr5aVu@+e29MrnX8m%FXXS}(8tk#ZHpYzuBtOK&~ z;w=AIm$M0*!Lx?kjc2WAvq>|f=|83H`MqG>_iktn*);x{<9X2#Qw)LtXB|-#)>w?b zv4`dbfgHGS&f%RyOK{d=RK5^PpOMKBmn8%>R|?}?KCk}=)R4V%NU6B*{`-0B&9}Jq zmRp(h{PP%Nx$2UOnKu0e{_@BFV%lqOuyoZL&OhhpyguhuF1hG3x;nZxA$sp)EM2;k z`Sa)V*kg}!{q;8hg1_EzE3dx#3OC>MSDu~t44tKATz1v9ELijoSO4zcS-r9YAq_%o zVqvVCuzEyY^i?`C9iY5s@w_)V`=>wPfr-;m(sAAI|AS|rd7A(HkE@yd%&SzpI=K9* zt9gC)8(eh3Su9wbkW>wLhp`5~X^~%V5bRw*pflcC^ac+RVk50_oc!o|x-WstYY30EsbeE98{8_mS13Q4FW#2)D! zMhXl96@)miad?ca=C)BLx4Ox66(k+3bi0&-0PP$Gja3RMJgG2*R-sMa?A?tinQjn5V7_1kBL2es_@T9_Fo#vf4XH!fRVNk#dLu*>4 z8tB^f0^<|BG>Db~B(j1R3Cg!pNDCls&9*TB8xhb&q_VtiC3aLqg@Vuo1SF}Yr65?f z`tP_EMVt)De2NZ2WGO`V)0jjcy&{z0>BbAQn|hwv`|@ zblNTg-G(-XzzR|s(d|896cU$91R@8aswGQSh!NJIe1ga02=Fcjp)sxtNC~6{rx9Ke z>VBk|rJPmp+7ft&kb=y6@BtAS-rEno3euxWy{-sTT~w^c!KBA`-8WOlYvNFKF{)gA zO1`UdcCN`#Z6q^ZQuEJ-G_j53!8#y>K=$AWJ=RTf!})uo3M+d0EJdEs=WZOLNeFX2 zAclZ(J3z;pHF<0qh+cjD4H($xpo(`6t#}s(LeDYC+Wf2wnAO=XxfmK`8NIyeAr~&K z15J%<=-<0X@`3wdn^AcnT`c6{pLIbn?;JIo@R}xwqEIYQtu{<|FqtEWG&<YDhgvoyqb z>tIGwfvBxeV?}w;4&ifQJc~i2d)Yhp7QtJqjipx-w-zB})7UiLbCc{@d!6f1?C-JR zq-mdQ7S78NGASg+2?CM(mn2RSON+5Kccqf@e{?o_JVzdRBwA^NRJ^%#8Cx$J%F4yd z`Qh37vvgXKJEy!r`|vH<{ZmJB;O9SsSySPiH)qj**PR=VEbEIIHjSSBew%GZv3m7t zN~IE8?XV4HBbf7cH|HF4AUlnj&maEl_Y_+`OV~P!J@*;UvZWc%PMpNyNAHby@p{B2 z|Jko-UNMm>!uSy9OQe+&k3I4z$A9IU%q{h!+C78SYgTgVcg|$RzPobM?K2tGzrshx z?Z~J96!-`Vc419H6vRd=S_q<6G7WQ43@ouH!w`VVMli=i#}tp-q9vyut- zzsT8tynvSKvt0K3zp+{WffS3a9Cp|t>@;k1ro23zK_d^RNC~j0`eyG%LkRgpI?9b3 zaoCSa)nU8^u^0?^6JwnuF=eVQ1rMQA1WI6iMi3P+K0yX97qKJAdy|C0*IYFcX~<*^ znRXd*BuRv&lx8R?(Ri#gsL&ybA!V!ZKET^GIGN!{iHi{`jHy~!V=Y>wL>K}UlNpE2 zQoOHO8`+F7P$bskk!02o6ck=qlFT5aCP^|f=SZcr4(UC@=RHN<3s4rRQ@qPiUZH%1lSQoSrb0J_j`lbN6C)|)7D3t)Mgpv+ z%Tz&1ycIYFnKwBVT;@rg$qd#xv{Ja5c%!V@cWD#|g^>`FQZO1V0!k7(BS}D&SmxKp zI0$tMR-|-g6)-u&Hw_#zQb-(@45JJlLqW!nYK#jA2efn6ug=5>@FE0RK%fbnMF|I5 zP;nK?X$dFVuz0*Efh=KNg7ua_7fF49*MdM-@dl)i8MpIJ9DeMPB;MeKskJTS_ol2A z5ed>8oC>hIKqfS?PSI3Q-r}92ib9g2v6!Tau$D+lgmE}8DG5QR%n<2HY&3u`pKvU# z1wmH!IBm&n3Yj3*0f||I%`An&05o~8<0?_5sLK?h80#aP3~)h)Rci=^rYQOW0bM~A z+a?iN#O^!q&L=*z8*NIWWK3CsD<}d_ZZB-@KWL%#8x)P|9jN%;7Hy*WwQG0H`zF8e zbu)=Pk=MO|fux!P3{ji|7xgM_eS@igRFdVLky1;PdKY-N$;+`0fT+9^u1EcQeV~J1 z8|djvjOo`UVKixW2qEm`YIzY7KV z+;gwtpz$D;BF*YaWsfRoeFJwj{fq8Wg)qpklLusw;KWj#mxOVC98ze65R}W%QtYv) z&96e%Z2qum{9g`pq`-NHR1g5=uAU{pdC);_dz?SKX+FnI4v-n^@U^z@215gfak)wM ziN{RLi@p-@5#Vxgy0=SlpIrueJ-Aw&(O z^G$1g56bpGh68w?11B{Ad)4YyJoLx|eE#5rShKQ=!oU$EDxf_UESvu(rH&E<29G3B zntm+>oT*}M-8xlncz@qC8baA)$Bt#-z|DC5_1Ea^>cmh)3d_Luh)P!%ix;gTZXJSG zmiGQ33+BI#@CCfvq`Iop#xaDK9+E zz)=WjWfP?49=BMB9DUF25rGfr+=OA|KdEh8#fkLS%Vas@0|4${_y)N ziG{;wpb$u}34}&?OI!#@OoG=jUTB;*q`r(-9(1m>8pbiHGg$9QZIw)WQU#6_lct1m zhzkTGwjRMnzx)MHPJWybTaN$@S(c!LM|xLNWzChHy$5eI3fdw~7v6PaL`Dz_HXFVb zXMFD`glR@#M9u9$fz3#42Z@k`QH+opClskS$WT#iEn;gG(l88>*5~adDi1W3%*8$N z9#`{64t*7E6^4KTBZl+L)Mwao)JPl_8RV3IYb_O}2(=;*0a65HE}Lw6r3H{veO- z|5H(6-fQ0bTmtp~v3K5Kc2#BD|E;q3K4*Gnl1zF}fP?^{NfD5$NU?#S(z}9ElqyOG zr3i>1f(X(f)X+PG8d@NP5FkhiCA~~JW$(S#`u<|0?6%r- zKlgLr-qj*;^Rl(YR9siPka!@_Q0}ZJii*|X$9?J+)OE*Cd`X4v@GN)p3G!M0<7{m= zk;Ce2ZK|ve=TZ<3CA;A!;^P~iGy@vE4XURvhoJ{vk|aTEMTLy9GO@*q>qzBKIWA|c z3PTw9?^)TsZok91oO|vdw90ulNStNXWjnt)`X^YUNMe6|LTKWoL~Si}c2-W7T~tN% zv$uWiBRiW>?8_xM{p8ol)nw&HcmdsF$+IkcpY+1kx8C4|!IhF!rZcA?PQX}6;QfYa zU)aT_0Ne4Wrn( z7$IWX+7~l$@BobQdUL(A(u#Ao#3BfzfGCut#S-^D_z-j6d6&EIzKdBipT*f6%a*s1 zq%oyZLKH$xV?=v<2UyKp@4QD{O#muI1uAo>>t(!r95Aw!lQPyqzkY+*b(dW@;+UUv z`z?2&bq&R&la8fD;y9(IriOm~`Z9mP0!(V?Oe{?e_4t|$=Na^I?APNjxJ!TTH&xKbLl`z@I6*G)>i$|&+ozUcqlVJjw}n6i#BrJ9PdtSOpPGTwi@_C$lpxGS zoN>{gX=rNT=KCM!h~ti=Pm`u);6QGF;2!??r_*pYMhi(GBraD^zrKALI&2Vq2K7Z~ zPz{X?9x{?a0|qc?cq_Hd4ImW8IvV>lF?7Tb`VMF!3>>A{A##m0wDzTS#6apA0<0|Y z?%ek{{rJ;)3(me*x!>`0J`RMybkr-30un;EsrU}~cRI+thQ@KxFR zJ3BIPP=5vwX~epKmXgp|%bbX@!cq=un7HO7X20+>Z+-M3QA-0+J_HkENl>i==o5v+ z#!}llfFXkiP~Wc}sVvS&oY2%Y2MiqEmtmua5#;I-K^;a*QmKh`LhFDAa#%_+#7beC z>KVP-SYCPNaoXGGan@;P^U%|e6Gt&hrbv+|7uE5>;>G;(sKa>oo!1CdKt7)*mJZce zLrcGAS_ijck`hU|li!_sGWS3G0wJAfZE;#+1I@6pW7%w*O&L6TFs-e9$Z1D{q`sw* zenSS))TfDjAW#7qQ>G&ojGr)(In$@{_FMCDc|}6Xz`;Z4GoU|pT4Dv%^sgu1k^>aQ zy8blw$>T(s+LpeYaKh=_eET&drbr+x3*Vp1X(#`Ye=T|s-4J1g7X%Z!k=Fjh=sR>E zHGLF{HiW9-$UmG!V{M+_{_0n}`~EwWYyl?~?(49y3EAClpxVL!PWjzTo|yJaQj?WX zbPF~rr8G*yGQ?PeYMDf@~Q zYlSlbv5Q)!fW~iR*Epz(({a|ICpm&^q z3eML>otDmI!m<+S(jL&d=tf8U--HAzLfvs3bNg+#vGGP56Gl0fE?mmUk%Q5Z;qj?Y zaO>1*eCu0Vu=e=TJa*45yzuDbJpA0V^c^$=9fVbu$rn>j;-7?D@yS(KE*~4$?qh#* z*?;^SIOzW}=kA>2<(FS()~r{VGUX124;w~HQv-wh)o||vk1*x_N7-Q0%~@mhwOG2~ z4Q9`r#f(>8W3$aS$Mk|jpV(D|a6~yxxooMcZQ|Su&f|as_vJ_1Z^s&Ijis)697<~L zzxQ@7{rf*yYxU8L7&n~Rug>DBsndAo#hGj{X*EhoJ3>jc@_KW1Q>?zqX!D{sAr39F7H zM~qMs=LE)9Y}&sh7OO%x(9pMu4ZrgZ4%%xo_TAw~IzMX1s2uBTvMq=GZbzQI>k%4| zTz1*DM2%~5%antd`pioV9WaS_c_$CvcL7)2^eW%nd`kum=*N1KCh`89hdAquD_L#* zbvX3cU-Qi4_p`+s>vQvUH*o7c_alNFK`1GS6sINSVvZrhhR`nG~R+-ewqW4~7r){=izx@y8 zwb=_$x)G6+pt9Oq=a7LWF`jlOGYX_#={Bz+Y~=miW=>9Q~`qSRzxblAr@bF(nX|L|DoSj7sQ0vh`+LamxjNB2jq` zI_hv{KJ+v*pL~kNuE37_AAoywD(|KVzdqv>UVq>oMy#V&7S6pxb4?g$+ zBCqI>0oW94%UEd$qk#3kF%i|$k4fL&n$ia!@Zc@?u+|3KaO&~<@!-_Q*l^++JbK%$ zTy@*+XbUQ;W$TSM=e~2} z$ELgP$@fPt;nWjO;#*s7%O=}>kNEWg{QiVX07+a-a44)uDHqxZ1)wx*Znh2QA9o1% zO?{k=zA=JVr`?Tp2_q*A=7pP|W5!c6C@>RSvkWWqxQZ>|S9~#A5ybGp2BdK-VLlr> zQu&N8zI zD5Uf#f{>MZ>xy8gtXe=yNmW49V=#s^CG)J$?$Ks8svVlT-b45A^2N>7KYZC#u6s0uRU-u*n=g#Aar+$Ra>8xJ|h`_@kLP&6BoQr^< zhm*;Q4z8~Hp~yBlp30wQnn}iV4>Y<@Nw!;-xtF5RK%o)#|uB@d~qs`9he zA9)zt)2X|F3yBw5_t<)60hIp-qM1I7kW#YgW}DI0-o}UX<`d`!T3Q-7;kZM1^W6@{ z{a`1Cj~az`F~=YM8`>7NaqwY>(Wigku2RUV9?0i+9S~Warpx9PUtsCsPK6+Kst1T_ zghf05dj>F4XNzEhbRtU>`e(Yby8Hjzo+@Z=Zf5S>xvabHx{Mw@l0Zpz+hr$azWx!L zZZ(|otBoPTarmM8Fy~+IvG<+_)7YnpF!H7ay>*!@faW-yGo-O43iBl8fH)~pAEb;P z(uy9?9~TYb&_jO3>}%|B$N&H!07*naRJUGXl~Fq}X6&j2LBc@??904)%Q)ol-!pJ< zJ)sCljm0>d?U{Ppiv2f95`;`>?px2ic_r3d)$?US%NN)Vc)oi!fSc>8`UVd=~9m9t( z`?Xg&^5CsmB>S0EZs6H+S4W1!omWYLLo-loX>r3>rO=#V^Oc~RGJb?i$K%Te=u*)eUk_8yPx;o`2bglX^bN7 zfBb2NH!Gff&AMF%K*Jq9v`xih!k|Wc78|XTpH~w7vHM)1I6`eK3mCkKKjC4?cwH z&%Z)0m&ddzIwefL>t43saw8tN>;~QzjWnjUOn&MGzPHo9)cs>Bn{G0a^Upq)IM&qF z2fX<7_5A7Vi%D$_d66e6ClD%5Kjhbx=PsZWX$%Ri!-lf<$W>VM!AHFD;XAB4aaCqd zeTKZ$qyuXiGjR+TANMke?8A&VKO`E^$}dm;CDUJigubKvJI08~rhtLtJFep=mwj6ig@(R0dJBFLD`a8Bw;#{8X_S=i;&rajD zm)^h$#~w%T&mFftPN!H()KbHw_1EFnKfDHE14(LW|L83)_}!5#v}3sN(F=$g8fd_g zk2`2ojznr~30!Qs?)n>P8Zn;Lltm~>IX37h!b*uWicm;MV{DXTs~_*q9T%U(CD%U6 zJG=dWH73-Oi&M@#;eNn=@|45lJ17tbdQu$|(1S9@ zlMQEUvq#ybO;H60sJL)I;3U#^p&i!3g_rJ$&UIT{U6*T26-<#JO#_rcC~#6%g$?~7 z9-;P328FeLfiA^9J3Vjyd^X$&roSk8<{211%v10y`XEwz(Ni60M7fO31U97rn=w9o zps76Z#W`P^COzomH{}v3GmBNB{m&`_t1kPlv`kIj|J|8P6N4CGC55&&0+;8tnVqb& z);a_(3mfHsU3~NPidwq?dsg`VT@||M!v08&@g_714*ZQ{+_3X``<+A4k?t1%H(-p{ z(RLQBScah<}AXUr|^8N=5K1;{v;{;S*V5jjfD@VzI=G z8L!aT*vQC{BQeH)>b_i5xUZrDQe=I|scsy-48{;H13SLB9L2|9jmR)4!DM8bZKj$MBer#hAq#lG9(vNGAqJ$$(rPp6_ z78OX!E^^7kZxs_r{yx}v;q_55Ag4EHhoPOQWZ z`ZERGQZRJrP=*d2TK&ubwRKIbvu-~hCkXEX*S~*X`t~1y6$HKWg(A3O&S>KizKXxoATqd&i#dD!P_q)?Bgj6A2 zfYaVLTNptuP)xb&a!x$%M1HdC9^5$jdIk^bhoz2&`hc9O!I)Y)3x=F4&~NZ4F1+Z^ z3?0}^|2{2%rC2CYUt3Sxq9sI9^7PYBbJS7CBdnlQ>>!mXp%I9JKoC*irnX&iOLiIkIsf@tL1TW`d|d2?7;fW(zh zsU}G^v5**-Viqr_v96x74Mox;=rHo!J$W;$bP4u$u8ThPmrnmercyXDbZRp zeAsY?4I9?Yc5WrvJxNztiSq$YsSNc|8)=-)b-E&gR@rN4DrE~lKYVz$fmC1><&pRbq<6TNJm!BoPiT0)ydO}m85%sBn2rv zjU+LkwC-`D%EaHLiY*XQaq6khu-2qZtkZ#UmNU=1i3zLIRYM}7SnS$dr)g4k8L57$ z>$wRR+3$L`iLEB9jmfS-b_K7M4E&*oUeD>L|Hzk>SbwZ_P*WFETi-!0s-Y%?D=xo+ zlTTh7n*c(4%6_jy+*fp`Sm~zSHXNcGBGt_vRSG{Az4=cWW5O!6$yF@MR)GfJyzhvCa^L855)1 zIE%|G#N`!%-xY6vh4K7f-5gKYOKa#bDiLu0H}N90CK*+i+XqOK1J+=RWm&{wK`}uqYe~=?vP%XzT6WjR;6p z4MGG&S|TZvCLL()h*L-rIB5|Ww8e|+IF;qATtPO*T(*xA+3Pi# z)n`}cO!guepMqNp5YmH-!s3Lj(26siE5CUbB6|f*Igp@@_4IPO@a^5)?@WS+$_`%LUM4FhgH9Y_9i~RBzzan2(heDEz zB8sI>o_b;`ug{!CQZ_U<)H3~<7ujI#jhQ!hF3T3p@prj*(fR$Xm8tBqd`8zje#qTlw zh4jnn=rKTzf)&u8C1=Ok|Z+2B7N&$IqNjud2t%c zmzOD&Q-n*1l*ZWrsRDEmpp?Q{%O6fZgWdPo9fTlBQ;t36SRQ@!QTF}Cfo!tLCY*fi z@su6d=ArEVqwRR~j>)u@L0HR&@4UdWc-+s zd~chzx$pLSDJKS#6p${Dbty6ku%rl=(lM`t1NSDNa z@+AeFOdw6rwnXRRl!5)nF?Q8e>DyFKU=2B!vS`5~)>!Xbj2u3MNC^TN(%8_#$dM!2 z`Fopl|Lu3s(H`T9G3kO27&3e{#;-n+zRmqm7EH%NRO0{!jv33iRYnsfF|!|ioVCC4 zO*)biOFsIDd9&wGwgpUGD?4txDRo9)8>I~}NgiB+DjWDG=;iBP<`Hxe^*m0x@ER9+f`ShsSnJ26-sfL=bqn%Gtc_7 zzrP;!#MwD4EL*k=tvs8eQbJOWV`^$4P7Lx|oz*w-q8?QTEQ z11odgGL{}i5Z#y>T6;0to*PBk#b|J{sylhlSN8NOG`5?EiF||h|l*hS5PDp zUxL?MVMmH?43g@C(WUS7g`LyU1?nKG<(9%(q;Mpu7o(5UGDbL@Fi2Cz$cR`4SmDqv zA+RahD1_75_f+r+kqL)O5T;xCPKD&Kg2gm#&z<+sI$#7>U41<{vy7!hL0pc}wgAKw(;D8IHJ!&FdyHFdp2GE$Z(_km%ZUqZ zoOkBAyz}~d+;qc@+;Ghm4Cp@`>lD()TzAPO3>dyKlkdHg4c8jZoS82&VeRj5)8%(_ z#b5u*w%ct(d!Ycpyw_glp{dXCk85vc@7;DMkd__3vpM(Nc{h`9xS3mSx`pX4&%hMD z-Klksa=~GW?JR5S;H)#xWI*4(Jn-;?{Cu~cG5v+v*mA;wd+*MsTYi^Y@1DZ>e>#u8 zeOeF*f>d(#C0FsMbI#?oGfpEP<%kT_;E={)tiUOObDbQ1_=z+&_T%Pz?%?Vx|IQi{ z$C3{t=Kt$mCSN<5zur8BKmPF~63T?aasOR+(=uQ%S6^|tr~hfNCMHcZLTZE*7@Ja# z%NS$m)6|5J5~VfHIs&CAl?o6@3WXxXU<8Ey`_sRnj%g1*fC_U66Owc+XTf`Kans~m z`ODv~VZr+!kaL}!b@n;D^I;p;-gFDA^b0upxD$!f6zO6*+Y(|MQ;5sNwnP+2R6+B> zoVo12=bn6j=j|CZqAxC2%dX$sl>6>{5Um@K+TesGl!7VO-NWFP(M-PM4%S^~EkXgZ z)WHo`Uc-^UJ%e-4IU7vM8#CWvtqE&$$DLDX?Gtj;k;fri4xJ`U`Nu`{X&K3NH{HfJ zHrNp19P{6u#XqmRoy)Jhk+aV{hlW6M<3;E4{L63hkE?FtrfY9t;;4~WZ4mVhj2PI= z;}6|US?4fDk+v_Qqb=c<`=)Z};Rkc-u_u$J3HRRh053iJDz{C!jbH8m6JB}aO^PYh z>43W?Ux%t4%(b`P#zyN;LRw2)w*2M1%Q^0Z-*d*fe;^D5SDt+yv)*6Ib+_Hf-h2Iw zSueg$UPyM{`A1B??;)Oe`Vn5f_g*I7{V+y+xnW}|z3rOMs@kg6+$Ya;uA9|oN@_vn z6NCbx0PH{$zcqBf-)?EEAeSXxvLuU3GmB6QI#=0`XdifMpD`|#>C%%}A<%bU@NUvr z=Ya@1X=;J#(`OO{5lTa`P#{Sl3`16?C~C9Ju-2Ml!DsuzAix?QG!~0KIK1@Ie==c$ zcO8+UXRZIgi%VBrfGV5x9*a~KxH=1w_TWXJJsTr2%Q*Pgt5WDJ5#%73&rWVYsmNc8 z?G+~by6$B@DJbz2?QV9v4veu~fM7LRstkWs1rS9w2zD8>JFF76DlI94O&ZWExfZ3o zJ#%6!`Y~Cl6yS`ndsa$OD)qk3FACfGZwd@5poLNj=Nw6r_KGk5hf|7jDH7vd_C$mc z8m$DK%N9~9bkg3oltP@4$N&-Kxc9c3Iqvr-v8cU(L7<#L6QgZc$(uu9BnZzd$-1la z3CaE)#iu)H(jga~b0HURFP%I-5E!a5bymKz# z!V4~=n8wr_g|iYNi?p}B!A{%lOke|4+KHlUWb{nsc3IwH&Wu$2iWCtf6 ze=4UOcQQF+2(04|zdeROoPMIu(3A~1Cwbzo`*58d)a3$dL_KGn@O%0e97zy!z;1h> zZH{sP4aE+EC`WneGWPk&{y0$}iUmg0P;w9kn&%#RhV|E47gvaYa2slhZCGki zuEb-HJjM$TwINjA+fh1)4ntz+_{Bj#C$b)~)LIdzCbJZ>NUMm#9A#}7KWPlpo_LmW zTNxX~DA7o{Z85w2cpp%L1tJ5mKQr6c)^7*B!r$ zGzm2}#km@SG-lePPq6N~TaxQ2Ayp%f-v2NgKDYtSlo3&hT3JsT2i{6|$p?J@2YXVc zgfSAW^5p9TSDyEGuKUwJv1x)8WsF|N-_H9JSN!>}xN;GpQqn-P)>`A3{^T^u%SsSP zgbfhIMf_x^pLx+KnGlsUK|a7H1x`Qua8CQ}FGxfk`7|Za{h?fhrE~c4&byFvhBS^L z$|I!Vl^334!?iX-+88171a>KBopb`HpK=mL#ndQ6zjDO!hx~$p*KWi~zd3@7et$BH zBkMDaxGycm**S+X7Oi!ag|X6o-inllj9ME8V8lWkgR12XJ#`gaRVzbjAIv*ID2oyj ztwCsT7_bVde2I#6jxef5THeE5BwdChqU-vpP~Sh(UdCmyde4KE6qF4JRq`UgIFckL zjy-r0MV?i`3eF0??p~lZnADKVLz<=(i$$yhB_)lGzEq`D_5z$DV_IpQ;Rdc}>Bwh| zL8KI302PtZ@v}ujLu?=le70LlNNp#@BGlE?qe57J!?D#e0&9d9UkyYr75{4=xPAiO zAv~xsefqnoKzr?$DljGxD}JBrp7+(+ak|Sc*g8Zi2qTapPf`XQ^gOS%5J(Ro9)8$V zHvZ z9RJNAM#lEYv@lYkQCQj*&FAL_>`hx*i;NTMT3T3dlP&q>!3Xfp8?W%;hh>nJh2COB zf)>hC+=XXj7$Ffd?pBteu{KBGEDr0zRw24sym#A>R2Uh;^{!vNt+##2+viT}mP!Q> z3C4C1xLRzgJX1nMh*CfxO9)#+I!mA=(v@+EB}r34DRIRTiPGrQB5i`hV4c931_B&` z%*2lp4XGuuo<<#{0YR!MN<%E#NFqVGWI5}AgUB@wV)E^GanLUgK)3{rAe5k-@`68^ zT3t>$gmK=hUTTaIsA37F9Eo!%tI4N^SQ&(KL_%PNA+iD|BGNcPTZ6M+I4G1M&Ki{V zwfQv;zv~y0M1YYNm#MKk2O3Fa9WFJ*af(zHR41uRNn;3Q4rwEttwl?LO(4uE0%_4j zL0Q)#l|Tzlu}shOIdWL7_F5c$ebNg<48qFDHTi{ zKZfTXet?c83qeW}rzt5(!8k;c62%D-0;eUhaA*xV1Ul6it#N@u0)e%J5(1+zCJ#CU z%4ku>CK5@GP=$m7RP2x@M!Fd7B-%vet-@+UNtAFpz}Xxi*ksZe?!WUPglQm1LAV08 z+(u|2umRFYjD&KW5GINm6Ctfa*b;FdNwq}b2mz6L3tAy_SfQ~ZMp4G57Ko5SqQr6{ z<;a;lfvX|04kIFzR?K_p1rFTh$FvhtOFhbcN@V&aXZOgs4Rop ztw#N0E*BZq-1>mpIlq@dB-P^+1EK5+ID){JO1R1Ztem?dNG2qyg`vZS(b?JI?My#& z{OwT)(T!Eo-5F)rh!%{!_gW7CCTT( z#1^Sx_=v`=1cpqtzISo`T1Il83COH4dprw6O4)NfS1fqD;{r}~8HzZW>1{++X%}M* zgqd)yTzT0-R=$hYg8T2kpCN;X^q4|!zwLI$jT_gM!O0wXl+xXFX;vPXyNX6gZ&34Z z(5tU0l#|VCTI)>AviEiWr_CZTLSckLUEL%R0nJ+)b>#xq_gZDq8moBP!kc|jyh@t?K zq)4YxsYZ}``!s2>NDPL^XyObh4Un0#vjvfQsXnW)Hs>7(oI|2XW1CsC7*gx9jfx;} zhP>9KF2qV`FE8Vy-<^QaeXk*NmO$u?W(Epd)iSeA5D1q^ z?PN;lP9Rf<6afN@!4hkbT2V?ILMo(9(L&;q)VmmDBB4$i!1|!hW&YX-LYrblfJI?# z3Nj>6zWyB`r$ej>KsZ!pT_~g%w6V@-P5pOe_KMbK>eoKdRV!I+B?=`ZR!E$(D3fx< z`DYWBAd-2M1SOXsObyNkIOQY8$a%X+hgYpu7(y#ZP#`VNSu7!tqAGOWKv61pP~-9* zDc4|Zk!K&ek?D7}BZFFmNHAEW4oH)foD7l1V3L@U76`3BMPShb0 zDA_?xt`W)xX$^^!2od<6BRtS05NM}J6X_X4svoq`UiHH2|sLww3R4x+*BDHT?XbZ?Mx&f5RF`j6tZ+<{d6@ z-R{E&Q;BiJWx>yO{~ehRnKY#w!`*k?#k~24anzAZaMo6jt6)5pjdO)z zQDs}KkVpFA4pE4Si=6RZy+)0I7Va5l~6JJszoTLnd`0-vr+xm#w*sWgQ^1z~JrzaXrmyP&KW023a>=$`mo;ha)JoPyMGRS!dJzg4lR zXLx*KrIPEZKPf8AH6d`B3sJ@CD0L7;Ub7>^0Kd@Z@U`g-pT$A5yYTDRIq=pm%h17t*m~=&dFZ)0ESR?ln5Aa=&_u3+G*6*)^-#46)Q(Fds}boZC|?fx$|Y#SyBT@Ss_?Vsnm`!5}ZakgLK|; z-P;ZttV~c&BCU5907+m}W|`ysaRN*=YpbwMpaVscSY!}_4zNm+kP_$=6UQiHQ9!B` zaZXSwFGD+nu$siA6s$zXC34PSEX1Nx7niBqI+2Mb8m|_dO|9CBs=_K&_js=%0iIpp ztic*zYo`N^P*8RTqlzdkaZaMc2qR3@TC3uToyq`ZB{+#gduvbxahf1>M4&Y~S?*8!=|JQ8>5Uvv)|U!^a7SBtWBxgg^;L;-KghwZxPP1&j(XnboC`?SvJp)gVVK zQgTjWaWpnJGiu~$;xGoC;*_eYS$l5G*JM$3pE4D>sOK?aC6R?#@n_vW?{10aKHz-PBcn2Fmb}#-2dRU^l7feIf+1`o$(t8ClOAfoJ3nN!rKZ-XR*SdZI)3*de8!a zm2tMw2oOqBh?gT!q)w2pt7pdRGkJC48~o~!p8yH2RH9aDidN93QaXuoHl`uaNF+br zcYiK9|4-COhtQf-D`dAiIEiOy2+~X?*=KT;tptt&oWue~I*jz6Nhb-VAQGC?ILc|t zq;IUpjCpVH%*-bkIBXEs3gS}itq(KdA%VpRL0RNbCWmw?y9$j}0-+Q-YUZ08eUrvq z0ij|XDFTUf65$LMnb}}vOF?Eaw4$>@2ANa?-%Qd7trfyKYIKMKLL(7I($K#TQ(tbY$k*k0an@_Rw&)FxI&y!6h$+P}GK!G-JR5Gh4pBorsS7;lA`Oxl z;UvNZgia!?#5#%7lCUY_zNhbH^;OqE)V1>aKb$~Ky(4ugMwYX@o`0{-C_oV?hqEyv zinwa>ZS1%64{@o%g(1UN9mnhsUT4OPCz&v2G%^vC%4JMWpm7)?w)pN2{AACaFlre^ zn=)ptb-4Wc8*vzfcHZLX>i}d3n*}nq3dmJ6Gfwy1M|{k_U4HTgy24WUSQz7aI!1Oo zrjpe)DFiwopNC>O?iQ4#?? zRO`w}4Xq=VaOac<5ytD^RIVsV%u4%J4~ZSworNe?I$a2$l&O4L`zZm-=fUE{r~LV^ zfvtL5Z~ON>tiqA9Xlz!B7=@abXUycHi~hpGB}@3lfqOEzUqoUhE^4H?rJmJR8HzOt zi3>RIoQpW=w6po^WtWhcwa@fuDRw(hU(>5 z+KB@1ys^g-2lI~$FX7a4FXH%pj`^WDC(8IRq0 z4V_CC@U1Pk<)$0&#OfR=DZ(lo0rQqG>m{|Kd)Y1)d}??p-zy* z5Jh1%W^>iTecKHH5i0>uOjWBwE3fTZlhf4IW-zVvClZyC7UvMoGs!}g!=&I$>PyR3 z2qY?0SLeE(Q5a}yYvB3kp5@e2H^-!q%S9xqzeuIYzSE!_p0$OqA=I)06eZm#_!-z) z5m{V`OPO_$CKes|r(uOb2A-KGGeA!`)w})s%7Ygx(D>ZPK@8Dt+&Sq1^NJRp%TPj@ zja^@^k@nQi>w&iGjI{|$SW*h8XaQ1cPwdG6kG=N2j7>NB7Op#vvBJ0jF9gcRbZz`S zRitndYYBo7DItuy7o3Ueyx_8ORF{45sagnCn^Bjg5B&e#C&2>$V_;+@!X1^*x}ST+ z<38(W|6!NuDzk7a2Y|$X0Ew&!0DaNd|5-1icVhb@pC(2qQYn#G0#_ukDHcJ&N;cYR zJFdU^R&KifDz@BY5}_z!tuG-LT9CwT*rdR+u7-&s4m<569)9ULrcQf;9k*H!tJ;u#HOHNG0?$vM#+5f;$>{os+N8|< zg$r3yF0xRDq$;9KYoeje?6BS1oc!xQ^5=itNS~nt88EybN1T2pr(SRYd;R>USY0Hx zDbhHGZu4!fdi*XPdEp_hz5YsSY80z(`a_T*qt}?k)i?f=C!TtWv;O#N`Zh_lle8~h!cu23(s=tQCrDgCQEL(< zvDOk=OO3Gvqx*B-rRVeXvrq8wlMk@zI(?}hF@nEbdojblF`Db2oXQQ8uOtZval=hl z^4)K*h6!SZue&ub&whrwhLF+YR^!IouI9dJQ+Vo`huLtw^&kyUx|u)RauvV(<$?4m zmr+jOti%Y1QI;fvQV2puB*wBR?8EvSZqD6*xquH%6TklZnQZvoNklrOXlvQ(H^=gw z?bb#24>|s?7xK)D&vV`t=h4^_gR+dAum;!O`%fN!dMb}Tbvu*3F@e-+N)}vD&pO*| z%Ei}SN=-f_HkN{vtTtgBX;Cq6uEDyLjt`fysC_9(P$F>NB6sx2QCxk^4eY+(p42vL z!Z1W2DJ#L++i$_muRO-Z*PqYo{Ts=bN)$^)+RE+3X$RnlQW#Lv%q3Udz<@Q!bH}5P zaNRAJK==`|p_QXf{~eFN@FX|f{!d1Xtw-u2L7HM~MzZ!g+j8Hfmr|5PjE2?$!@2+d zsZ4wFN%q@fEpit2Joa#Yec>sj!m-uQ_TjW+en!7sfm^5C&Edx###Y;}gA*mRb%ZFE zFE7v*mnkL|=M>~>utO*Cw<|8;xmO1R4=vHzdx~3x!Rv14qRX_)c2kJgGNg}HtMPPlwTE+KX zR7_4h;VaXo0qqRZQq^)05}z`59u*MowwOtJY|48E4kgJE` zu)+Uh@4VyWs>(P1J>}jzGh5OVLXjf9BSnxVO$bE=1*G??fPjF26lp?2F?0yM2!bFY zDop_u6cnjH2|bhq(l(pjotaxs`TcQcW@k4WN(c(L=ac)%?(EE+d(S=h-1mLX^FB|X z!x9RFP&L+(sl_6_BDW1ZQHTN1PSGb3)iQq`_p&7GUnFS|-kNrkYb$*iJE5_nP`md; zzuQ18IDo+bYQw(C!O4rJt^a#I!fW%v4n8^^qls?PtYzez$JHmq)mRAo`UK!bmKml^ zo5q|ubC6OF3VeRTX~x$k58*|F!5!0vmqS=G=wkS=p$MgMD#WQaq;02An2qxh&JQ8f z&=Li#zUmtM^|6Q9bB}%KoMUNM@ABFkukeE-f5>U4ol0h6mRMvFmRVsFR$g-rZvDl@ z?6cP%oObF@DED;diwUwB8}qfN^xy>^Y=iBy+!k2a)2EJCU;Z~+ZnH5L-+m@SM>s#1 zKo;0?%biGEg>gG>k4g-kU6$ditwnjxJDh#dHSD?bF`PH)Qa1YfTKwVVhq!p+MO<;w zg)B981ZVv8yWIZptNi4|W105WGiS#EeZ`eL`MY}= z);fgL%x1)5i*vyxm$Lk#0gwOfA-?*(eOYwTrI|eSZT8q}7i<=@=MTQm;rs5wxtHC; znde{3*=Ov_TW`$d$_Zz(_10T*=bgWYppZM5Sc|nON`?q235yXeLZebgTuF&SxcKrD z`2F2?bH+(Wp+!K_`5v>gm_rUan2F<0;)MNoXGSN4jOM<-JkAz7?ZKlD|C6;gU5;z6 zoJ6H8S$wIbdHLlhnQ-7FRF)C>UB1n0(_Urdh|&D*jkz3u%nvBfc$Mw8K9fy% z+KUS({)~eTIFTh58^iR;PjJX-`=CWYceQ{VGK?%N^1c1`N1Hb01_Q&o_PE(jQ`Pj zY`N|JoPO?U9B}aAq~#bdhTwgbk_zZfQf73`=8(gV;Finx;(NOuK($;Y5t;*zI-WTG zH`{OjbvEC06HcBmfxUM>l&}&~E_gQEd?U_2^k-DP#`=sovuE+6{SLqum*A31kLS)m zA3*_&sKgXAiHTYWirrM>Ic&Av)*SY;(^#Gq?;TiycODy9WEc>Y3YaVfS>T|94rlgD zFS6sYXR_UQw_>qH4MtYE-)yOdB9XlCY(n|f%Fh6fw$DEU|r2A#nmN7 zvCdL`4 zgY_xKfwqvQj?qg<)?R;Utj{$CJfb1^S?_Q!Y7R2q4}9?k+h@Cp{@G(KDXojghu=&&n>VL2wRZS7L zyStk#%NRLwWX_TuL^2lIl;Z`o4;#tpXPm|8(W9xvF@=^Qna#incG+WZ))N(4OKohq z^*B~ve|;7mJ%-~?K9T=>^HnGWNDre%j-B0U4sRWu-7!X&h@D5P+*o{_^l^WXe(LN$8pt)6r3-l0*ce#5!xQ z$q!CF29stK7F&w0`)_CL;)}EL>Z3U6=p!g+ZFrv%#|e>^^x#pxL=+TB^jrcZ=&@CP zaLD)h#zvd-{8RtpoHNg1*zh4NwbXDPd*mTHJLZDZ2-`y7QjAG)9xAgtshSaJ-NA1j ze2j1Hdp?XokUU*>VUoX zKa34FUW=}-xxDrIt9*IsrTO78r(yA|xyDlXWG1}vRhIeE(p-M|Pw9AXI*&d3I;S7H89E9`OiKBkw@5qZQjLaEF;Ehcu}dt??)$IE zIX~Eg%ADC`u7!e>Fk(?QS#MRof7yBTI7bjh7^4tYk$FjmK#2?&cVUAP#!8H~NR^AK zW^sy-EuB+m^0#N-ovXWu{1YscQ~HhL*@GF=|*F zQfN|X$Q;-##i%OXv4tSQdWRAU=OosoSgS})f(!)K!E#G{iN9Vlk@u%gV@Bs3Ml7lc ze8R)O{xvVY^d$Qpuorj!=Jy5TlVx z3{WGoAi?j4u(TTnB_an4@}#ATj{1iyu6OxS$lOhaa_*G!p@%l_LDUl>=Nz`?>>*_? zTqyJ`M12WZ^oj_3FFyA93J=M7I zKM%#C#A_HeHf+itdI742UTK}iAqMFfUzpaof#e4aM={@V>KvHNqJ?7I4y&-;HdpY% z^M}>!R&~uHSu{j9bKJz|rAbXCC=E9QNA74Qa=i>=>_;DJHF+ATaz`Ge^WSyT?a*J- zMeE!q`6B}u^BejLoF9q*-h~$*5iIZ@^K|^e7?b~@UhsLDZWxBFu)+#`UK?x+7tRn; zvC1l|pp+uZGOY7?To#2CzO)#v8kB<(i;QN`WxhmLmJqZSSYypK$ecw;nLEY_!SX9C zkM}-zbu)%Ssf5-6%P+TFV{s*pt7xrjzk3D&jKMb8{s%yeNTj4r5ERUbyy{P;(Eaqtm)bHg1KG$AiN{Q^fla};Z>^ksw>EV9VTj2hL>p@;54 z;D&S4&4(eA9=f}#Jo3*+x%HY~(VfAlv`lO)Nf_gtV@NoLEUl7?a&ECHQ&i?@DTGui zT}YKDD!QeGmiEzzLPUz8Vm0r+_;2>zdq0MaY~e2t|CZzTdxtqPLmSUcSKL5%WLZDe z6l8&&ciox4{OvJry5d^4-Roes8V<8MW-(_roOtYUyf!T*Fl7o};9Y=~g3y)lR)X}z zX%{*QxMbqbx&G2gh{WT(r?az@xLRQBqAM`3Q)r3Ee1-{fX2M&q zzQvPIzsk->97b{aJItEife)8L3W==7glJT#NMZxlQV0a=d}Ax@>o4)fDqmV;7_(-~rD7CLMMxq7r%0<+3Q<5&R+;(M6fQmMR<5}C8oqzf z-c0`YYb0^Xm}S1iw0EcQ&O5Im!{G$6&mTSvrPvZsP7E#5;<6B#S%g){upMNAw?#@p ziL7kFg^UxI#jRNZ~iY|Lykg_!CVOY1eE`n^Q+f|s z+Bc)kSsSH_1RnnO>jzn4i(R?t&P&O9axm>$bLbBa=lqG6aP~Rp z@$%%!y!FmZidqmji*SNCcC;#u@Q#Q6_y`+*Z4<_yaTYsoyDjf@RO#yM;H(o*VZw!1 za?RD(GU?Kb88K=R5+VXLk3RfIF1zG1&OG}Jf`UW}gK&m4Nif!ekR+Mm$46ho(MO!g zjkn&&W!H{p-L*$j5zskv4pZNJmn(0$k#o;Gl9Y^gFZk6hx3bfrhw$Vb_k$FqBEefr ziyn>>mYzz2tI^S0gy7I)k7SQscE=e{)n-(?yGY}dG>&=w^*898(}i*|rLkXPmE{)Y zq2Jy`N|DHA^vv#NWWjUW4Yza4ZO@W+O{JRtn+q?S#Kv3g&y|y|_jxX-t!OrMTQMsey-ujTTImvX}uKcy||;V-|Ng787_O7~|n z0Lg;~tu)LO_Jog1A(qp2{!>Z!q>}je3KqQ{S@5b6LzrW ziJ#0_Lf(P*30aokUk%rw9A;~}?OmUet@+>2o||r-#;U8XiZL|lyZEMY*aWE6?{8Tv zQK^+=&DRjfgT)iaF>S37MNPM8vkRkGfJ9v^gbD70j$z@Y@CzBh@C$x!^JBM&Pah=l z-jihpW3pN@JP2TXDszQWIkT$X@756nIa9(3uxXn2yA-5pit>QWE$U^BHmHECk%6Ekt72slh2g1x+{3rGzMB?PKvWY7V}>zwjh`_4hW$A64|iZ~LfkVKleANu zI)#J2y(hSCgfrmUxaMaQu~J}E6|X8tCwTs*TL`U1VTfeF*+(BrdtynZO8%~UOf~=j zAOJ~3K~(UkKM}eFD+Jm*gb(@Sy}#mt-~SS6JT0n1HPwiq9p|cCJ@E=|zW8!vRt7Ru z7!r8VsK*l{Eq9>kD#%b0T8Z)=uM|QGZ00EF0E=Vs zCB`u2)i-(P?RUwZpUmk|N1u(KI8ANyv`qPoP*Nsyf*nc_TK$lXeV&W6J;5# zO7X<+f63#&_joxP6?78Yn5+!DY4m=j?!P^Q^D0%Ijm)Lvv1MvtHDcU6jb~vM# z8pf=tQ+fBbcZkK3C?TO|CR=Ycjwq7YvP22Uw7|amJcoYsYeY6Cv7;Gb12Q)nr(<6E z*F9{$)olbyk;RrMD&f^suAg)Pw_hJ26NmH#UVrmp_TB3alo>@yORANrM5QG;@h8W! z-0Dkl){lR{p*!qI)kZj7cc%I5CW!T*AWwqj0Gb%Jtids_-1|Vuz`_hHYIXh2L*{Ct zp_ZBwDESYbhAg26r0#-n1gb7`C_zYz^A=eXBlIq3Z{#N}`iNyhT_{Wv1Yn)b%SjZq z)#m=alLIB*dt5{H|3k3oMk$BKxtc49&uQ>d<~kqV6&e}?QW<0b8Smwvb2$UVD|mD2 zsoXG;njLJ0s`Zf|%uOPEF4UOYw&rIjY9fJN&_~Z?kcII%Y@fn4Yb5=(QYYE4K-InX zU7vh803Xs@-Zj6WFSz;Un^}9^ZxIIcGNU{LWWJItGvf|05tq zIVt0XMEiiE1tknfkFAN~cpo9XXsGyms#w>K7a4)-1SG}{C-4s8bS=QJWGX`12;~58 zAahuyNL;>T>TQ^N>p?q@M-i(OheOCBSrQ?nCz2T@<qr5s-O>be=*`A~6PpK^jjn)MQGNFWNZ*tMWb*0xu%0j@W1CaXkFMqgaXv znc{r`>3WbtU=j!=SXTskXvsXGRb;Y2Df3uwAq4LYMcIOuAwtCH0%Ta6lN4nUGE1NY zN`OmJT6KXea~Kz(gVB%~w5&37+IvhGe>qj7$)XV3&wlf>8HDt$7oImT=<5>jAI)=} z#YvSrfyfF{m6+cGOm95YcSSfTfKXLL4e)@v_JwR{1N1WTMXjdaA!_!H0?~-OrS!e! zDIc^-=|8AuLp0@dIu{|-`A0GXUYJ}kZAh=yg?9og;F_C`p=WxYoO$PvK~AB!9s*6T zo{4XEpOa0wWmD}z{{`DYNsPhPFNw~baWWbv#a<6wj^*p1!-r_=Kezx z><8IhX$!Z|YyhGT(BXet?Vkbo!_6w&nfch9=UlH^luPdsJ`dWh&#OMF@- zYL)cFDN0AY@$|Foy2p_W8(E?{cLtU`5XTv`4)6$4X9U~<&nR!IA`(BpoG9!0ZyVsA!o`8SF88UP3RCFTdP<1 z!V}Yi5(1?IoXOBylGHq(gm)V)WTUm{vm-=U<-uOfmgI>ixn;xL99u%ht}ZpK<>~_wdAhH<5}~l+frbCel!~B6o-|4y845l7cqKK%=}s+6?Cl zgptT8gg8{-K^D=K6kX6{QecE5bB0nBl2j6;ijaZAdXJO&^1Jk!(1NxCDQtd4WJux- zKR@j!6jOuJCA4z*3`9W?IDzy6Ra?;$RuCG6@Bv0S3fkdJN@i1{Vv*EFn6wKW<`v&w zYFwt!)}p1syZoLBWdxZ(>j0-D(n(s4r>YEbXD83hnnP4-CG$2{YWUngYu!goQa*l? zG*2ijno=DBXoUhy22mjwBJN|yIAGuQJx&?Hm_;MJcL*;*WYh7=x0u5V?Yvs4MSc`VhIsJX&YD|j47Zget5m|x~4iS{dRDhKR?J|@HBNWm*Y|SQ6d5})x zd;~}&7KyDfN@`4py05j#KxnM>SmSC`HIE=83^bYXXw`xw%)RY%w|TtC_u;+9y9nN1-fx;p2HC75f#5cg58ge@z3;LSQPnEvvBz;fQSt#x2aW|G(Hwp5*&}c~bv?S0{UGz`KL|7;>H`fc#(YhwJjzyL3L5$I0>zAW?$@L#}S z>bAqpgWvc0@e98-PJMu~FX87MDt>Sq+y=ML?Y%zmyek$ih*9u@2t`}l2=>_Py9{Y> zLs(DfJb}wgM3fT<@9@%KRFd0Y$`DI{HhH*W;qJor~mMo;0r7X4D+HAl1*D1wSRO%5gxq`o!U~11+u#gFd^A=t6Joi}zn}KqYC>+WzdmYNi z5sEiK_(=r@?<)9f^nH5A_5(-c$Z$o}I+9aQIgwK6a8_bujCT?vLabB-Hb$zH zSPD{AByoZrcKimbuf7CnV)3-mHe?Kk9dalK9lSGzq9TflI1fg6L|6i+*!=73v+Ew) zP>u{j6=+>-4C8j#hOs7~C3PtKMOb3e4R;UOf7wLjIt#vEP@a#&503ik6jRx6hLa-C znd&)|(m4~u8%>}Ca5XHV9vr&-Kz#i>T}{_Qg7=z2?Rb1Hgn>ubj@$G~Yv5|%SNp8j zIaucMV-$^`RQ7R0X^6J^UH~D_vP7StE#J3oFuS|}S0jETF@zzcF@zetvjibj%24jg zcULS*oPknU12qzmUZJVebaS8wXYw;>D*f>y&8Kb&#$`FaGMa@pN zIXe)&Kt%mVAuOT>-g!i0{PvpD`23y};A($!uBCQEHh~C8yzuh^mZCq1>6@@l-{fZE z`pW5w{602j{`qKq*m_?l4|yQ#Egx&lO*uQUS9hif-w_|BFZ=ahA27!c08sdOfeBHU zEA>tIlAjkG`b0T;^TgEqyI)YSM0~*hn=|sduxm50<@@%DpIgVZ%#0UvK|J@3S zBVQ!7BLBPpAL^IEZE*WSZeG@&x2hH&*Z!|3;f)efMV%MFU~8$O1A}4N&DRnC$3b**TX-AAgE1zqJ`7Mh(Mf2E4&(Ln0l{OQcueJyt`N7%ws+Es@?4Qo(tR9=ie) zZ@PiuE!|`$!wa1Y6p4&D03kH7ER(b<<_gF#bOZ@TnLH3g<4u4O0_i;gK2NSPhr| z-jVv0vi4M3LWV5bO1sOzrg%J&4hThviL1=it?Yi(k)-iESl`XV4?fJIYpl$A<2FZI zk3`cGNETUfIgb3%4;eLb2-bNjB^a~f;*9+AqL|izR@aGB6|{Fe^59=-UFoZAvDF$# zZ%`tl5^IJnx&~KXe0d(V7Y}JCr~M=pZF1Y4$nbIJVwuBb0D>@96BD!xK+D!InFZ zW8~OH@J67dLTiak5{$_hJ7y$HEjETk1jv<^;KpCv%mo*p%P$_gol)(Jp&E>y&u|Ll z9o1@;N~PK-80&q5hK9O|tCa>d(h?kUo?x{(w(i5u=oeh?u`jf?=W(a2(;0g!_ z);Vlb9GzFb`37%7$h-=~)hs(Cee3OmnxJI>dFc}%(RcyU%SL^kN7sNA(TCMxG7!$9 zeBJ}X2S@=@IHb(?DP(so)(-QkGZ!>sbG*g*oad1TZs3GyfCd5$wx-*XL!CiN4ywvk~rR;oh>+Utg+gG5m$UDSyewjTdW2HWSd^-a*~ z;9izwK#jVSLJ)ye7MEvUcrOsz*F-XN0Nmj5uAYT9*#Nnq&sVv|-CqM5YhYwOzm<*q zUp8|)n&t-4M`OoYhme8~ax1Rq(KVw3YKpzg`=P!`uW0I%0W})>bO78yBfp=Rcj9VB zWxN<*=~xWROrTd|O!P-$76xm=_ur2gc>T+Ntc9}qpSx)4A0hhom6#X*`QU?S($GMB z!1b8VarD6}>VQgb`JeY5sdA7mO%t*#s|i6a93Z1sV2sD%$jgqM_xQ}AoyJsBY&j(~ z3YmgPT)w6MTN@wq^}%g$8{9r$n@l~V4pLit%^*^T$~;+5j7mMe>Zs1?p(w)z*Tjf} z5=$?$61(jGJsyAl3A(c`Tp;K)9x-$g_S}7QHe3B`#4|cM=iKpR!DvoCVPCv*%z1w{ z%a2}yD<_=Kl!{{9uG_NFMjKI?*~MK~|Cq_sJV_y9>mA3j)u!w6?^oX8{wpWZQB8Pj z+6-oP8`?!D&;!1mV(T!*Uw#&+ob^+loBTQ#oP0b_J@_DB-}NxQwCb06?&T+O+EKPK zzNAnim*&Jn4rTPH0`I=_GFMNSL}|%YIr{Jem^1S&T86gpf3Lm7-PhcSR0WRv$w>?y zrFrS)7r6C;D@l9{g^|NKVE=D2a>-?Q>e0J-_}<4bsbTWuDHtiS-l3$+S1KiVQ9yW! zw-zBKRw&faq3rUVZ?Vz3>(eu5F2B0@eBPKdl4H(3k408onzMg?7B6&kaq1b9IPS>9 zc;MbUnLN3Z)>YTx^!>Nt^fS+;G-gQ-++$b1wBoXaHeuq#OL^_h={Qkfmm~L~`;}*T z=z;q%K?{l!RtjWh5JK}I2DhN=tLc*C#p&~4W+yJGp)9=r3}GcWPlORvx)VZ(9T zBc@H^i5LHm?RVduZ8lhse?9jcH(z@#nP{bT9j9g?9cH4Vj z{`u@b$&%S*zJQ7fRBX&DYple%=bp#p*IwZENmmkA%1oIug}1x98RAor`&I z{wn@{?|rnk6zPiR;(Yq))v7Oy^hXHdIObpf`WI5FMzA+e(A!9u`_{($Mp@Fpi+CTF zdMV{=(O@1BTL(6SAdo1PC;auGRn|aVDFl|LMd#Y;tN$QQ^#EdX65EGuB5FJH0)%v! zBxhSn5)0lbgp+6u#%36o^csY?njof-c%P5Ed`!6p)2SinQX4a#zANngD_R<0 zreNMBdf)4elMDEX56&8!EP;F9D(5p^eIGUwe*TPuc`8#rOaSzw)D(yT_S5Xhu`qIJ z@c~H02c5&mJwM+>m+5mo#D}}Q`1#U2=Ka-wNS}TneZ==I;S;%|P8?VH$3Onb(4j+F zZMD^rLgKLt=LQqaTCW}@Bu_u}PyYSvixk5GEm4Um2y+`z310|#tOXgPgL)@}ZLocT zZ*kFx3Hs+2));~yAk8ugQAC=i3l3rk(azLqGZ;VqbS7SN3}I2AyDeIl*lPPd*=eWs zIQ!U>7+V^SwJ>t2<=JABb-3|2zh;B4ZOOY&y}*Ir-iD*EyN`dp_;3F4^s{WU;U@g} zw8PkK-z(T{zg^k(TU&6&1sCz%U3cTiqYmffbH?K>WGSG^c#)9Uh-DXBmRWE9gHD%n z-K1Z#=JKPt?y~XRea~*l!EahN23n#D=3!J&X5WeU4w>eK#YAwxTht_?7i} z``y{B^rcZe{>&fQc*g^IFok!x-LJ)Dx@rSe8mzUwDt8Yaa&Fqet z_h-!D>PeTg;$lZ~(Zv_?W|9%eBBPgBobT?uBbWZ{I+kC56Gjei#mAnntiJ(kuCoFc zUoweELdVPw$Q)s@z~(*I&mPEA7H1mt9JARu>4%HoNW5Vf$>%8E2f!&f9*2V^27NlTN=HC1TP@ z@{Ms@am%@fV|)t<5>*I!{O^x5YK-CJpPkA9yZwk2R$GboF)iHjizgVl;wr2#vIF8S z&YLii1CKe5HCJAlJLI2{zJgXQcpsAa0HH)K$|@tiz1uf9bS+;C&2{QYU(e{%{I*M$gbii@Ao=A5HcDzWj#8zZIU)?07sb-3ssu!;T?m1wwo z;ApVf`z)4ySis)Q681U!Kq-jhIk{^`4Y25?S{(4u7mCd}X)H10ZqiJ%3Y<5YIeG@+iSr+FIip&$oMVEvi%K9$0 z`dov?*<3Tj`Mku&Q~BUztkyH4Cfe-#EyMc$EE)QE*ABMNd2_}gb%b*X-bIXE0!}*l z_l(=-AdCSiEnc|3#08(1Ny#Sr%iImBUQS|bJV7xJ!=jE_G>@wWc;*@K{R`|Q$~=R2 z0C;ZxK*)ckc^0nl=0JeK{D7d3($RhazVDM9>n8zL=J~joR}IE{ip3HeZn$ASuY1cv zH7W7+GN`<`PT85DBosQ=8*&~g>dd3TjDD~Uw!!wrf-&3xAO<35#3?C} zDqkQKLM%9lQ8eJPn3=D?MV1Q4G9qX3#xVJB&#~AcTXW#vdvom-ljw}gfWvma&A%T1 z2dl2UJr6wkG<$rll`h@QN-M0uemn0 z_6x6KyG>W){PRztQVDS>JpbIEdHSh;p=5-#25&Nwz;o1|yFpsTxFU)I8-8Oej{D($ zJp88z`OUrev*+&nGvS<{kyW}Gz05F1jcsS@t8XETV|n_8*I0JsA{>9pu{{0qBV`8p*+dDGh(o3dg@1DHOuJaDl8S($!n zB^Jiy42))bvw;IQH9#EJ%f$F3XR0JrZT`OsZh^*fk)f+v#NffT`)nt*}&VWzeB+4>ycs4ExE2B;#P z=PNSfa$(OZr~p=24VGPg?0l1jK8vPJ@I495Dc;e0;DOOb0@dkr`0F2^z!yr)Ex}SNZ^2>veUG30k3Yb!yM2@2K72pl-*acm$}&92nEcFZOrKG}mMm@Q zaCSXv52B?aet78NY_i*7Y}6)6e3coq3}+pE3~#(|a3pm0q&N-6bR%4dN9T?r)*`&- zx@&Lbt~+j|+TDp09!WqLwha7`yRr^+ik{C z#~#H)cmIx_v`Qfy&c3^C&Hp|BIAz<0jJuff_H=gJc2jHw>uk9@T~nvfQ|YE$O^9TK zcakVD#N|%Xz+z;K?eXZIc0@5D&0-2-6mw^l`S&wZ*!R$V`1#pq@aDT+w1$fiEVTlK zIND`Z)K@)8j{q*erBE9I_EJcguN zMp;l{3toW5pj7~sGTqKVYa6;8;>rfPI=T9itGMsCPm;=3tW2mR-E_ANXV+b}=AX~~ zjrVK-L53j!BuWYd8mERLf|M-HC`GM=bWsuxDTdNLH$e|g&_POO6wZfe3Yg5|WH;rs zj3Yw$5(-6}&0xsT03&VA!0+)`Q$lx5=eIXq#UJl|h}-VDpRKlCmRoOp5<&5^8&i9a za~7>NQc8@m1N*PnCn+D!TbfZ9coTJ@%LOP$5WcaRW-aWr^A_kZ7uB_mB*;-0Fcgh? z?0OomKm2?9Qx)ohYqixmC&7uXTv!y14CWFQy;)g#s}@cZWg0 zyU#ax(LBf-q#K3FVsubIsVK+o0tkWtlZhr>fjThJd~eAbfYE!O)yuBxV5j(qxP||a z!L_jR-<*(;pO=*Xfj*yK8OcCN{rp~Ah#fHBULNRs1}blv-`_usN#Z}Ce?-57^8@tN zK;J#kyxngozGz`bEq?7!l=jABt)UPUFjB0^M%Vf*zr?b3x31<=a%;ml3UtyN$!FibT@*8Zn^FC}bxrfOwJL2~{7`x`8 zOrC66YmF7KStrN<892=B4x(jOXZ!sQz|D&J^<97CyWc;MS6=%&FaG;^+73H{wh?2< z;wsJ={_yC-m|}@iRzj#ADoK^)mspF%7a7f{_JC^H(KW4u#~yx^{ZBiKU)*priw6mWVXRrNv^QGsx_nx~zIA%=iU?vtxjQ5_oUGH%9qzAb4s@u8bqVYJB zFlEYfyzoL7bGmzI9X6b;cls{xcFy2W5B?F+J%{^l`vsTYbPIQ#b~qx=(1n1+7)&hD z0uoa}r~=*>K(%w-AAV2IQ%`WvK6_FXnqS?07ey_(<%VB!+YPrMoF$D)xWZ7rv-zf+ zbmUcru$Z-=cHBd%J$y3r!sd)M9gIK)f&|LmQ2 zm>p%=|36Qea?b8%chgBoC=!Y^DT0UysNkz2@*;MzzK9}N5b2136sanrps(U9sPvl9 zLJb5$kPu1;H55Y{NjBMX&Y78~{Qj8PvuCrL3jP$Yy}9PfGUuG%g` z^G6@WdFTI>&YB?#QkJ|hmt8-9FrPelDKEUdgxOEMN>_Cea@5v*@V`EZTG7GOTW;Z= zTW{i*=l+7l_FR$`OIiHtvpn&}oA^>2pM2kLTzKm7^dvD}WVCwEu=Ww`wEuqm*T)WH z%*rm7cP?etQ}g-y5hrs0fp;)ztC57=izvl0GBqri{TjO*`8D=GXd(0Fy~IukvmtCub;-5LoI!;iw=)e%%@i##pTBt)g-TL1qpV z_L6e;jUx)O(EJ|lQ_NaT&@B2u(q2^(v6?ld5p-K;&|x)~sx=0$ef@q>>i@$zU)}%v z^;`S?8Pi#(xvWnG#rjTlb!{RI?MiC|I@a|4e(lqaqOiCY9Nzl^czb)!ckg)K3j_kK z6y8{rR48;#>RJ%c%D1RMJ7hyPWJC6E8AB9n@v3~xqF5u1^&nN@%fE$o5{E=fL1;66 zamr6PVDCK!zfgPcg4!4qKl1-<*EpMSNh-J(&NG1-$m^3XFGj{^MniIqXO5@UC$zSUevSc%Ga& zjdySREIyn)OZfH0zhdu~UuMfK-@}3zUS!%s zcS4$R_Q@ym{*S(w%}0;HcJ+{@7OexM@SJN!58#}o6f1hF3Br1$2tYWbNN~<@ z#qa*ai%-mAn=QxVeT6ws)(BjUo+XR<-nYKXKHI;WNH>$&m}V{b`@NGn`A0Qo+%y># zHX*Z>yz;`++_kcXRDx2_jICvOEQW*=fBYlDf4qjZf+#N0WgMAJP+lNW=tyCVWAr8y z=y`KF|9J6bvY;JTjmSEd@sm@2!b1-{$V0Q1F?XwBB(|GdC*R8Qr7PHW)6Kc;?4L9J z!6|st#==)#Vrn(RI;ffq8%Zj)4z9ZR3ZkwW7o2w?haC7{DA|IEn)vd;yK&hu=U{A^ zpzt(Rpt<>mo9OB4VszUGUaC~6nS^pA_~n@w@bUk8e=dKg40qpl7imw*h*9JC)fuPo z_3En_zWgPIj~Yqssh0@7qkHKhzV(%(*nQ`n$%G>Fmb-7dhC>&>&JJ5| zL)_NJ6HDGiX-QjaD`6$!l_%#SR2xD#!j45;e9nbToVYcQO@El*-F6d63Xe{o!S@gQ zE<0>9lBLhT!1R0XqG}?-K=HtBH*-$5gt6@#QR$jPASFpwh+4^Fv; zuPuIz9k<$!7oK^VXCMDNTBJPs*h9SM1N-vnkMGMdU;84@JotDnKcxSimmr@9(cagA zj6o|giufqA9SAkDAb~6*gx(5+4f3-4d!_Y)TgAY@$BRNHQlJpjdO$dZ=p`XscSsj} zP^hld!yiyOR~#u=Lsgt7pj|R({J?tD z4KSthg(FQ;l!i(L%ESLffYSPc32XBd{kpyF?Lj@(07Ce;@!Skn0V39YE@CynfcH63 zthKKGY!gDf{VgxWvolxQkqD`Bm68U@W6`jx4=t}lHe^HgpJ+}DlCKr3a3-!|u72S;lnSYl@sHQ%a_ziVNfU!qd0Do#FEQn&r%{0;F^gEfvdYZG3C(5A ztVd_@#LTDB!jjo8Vi6O2!Q4lm;N=;!aDhWqQiP6hKEtIo?!5X=v`Ww@QUnj(_Y|ib zeI)1p`d9QUSjLJaUHC9YRI5Dwz{5O0EyKDD6EyR}-yh@2nKMyV6I(^9O1$y(b0jT> zL}WCfnDfw`n6wKWm$~h_TR}uPVTo0Zk_yPWmvhf8ckpPM5~zsOX^fO8VOj9nt6XvU zYY5v-C@dO@lospyWa1P+(!Iealq~B+M@{I=P!bVyXU$>$#a_7|h=&WQ^!WwbVN?7S)%H(@c!XcKtzypg)#9@qaf|(CYO@LQrMl z{1;jLy1`kE2{jgv%u?>U`5u~d6PZc4_1ar0$!50LX(uK$g}n6YYgCNLl^T*RWV5DL zUCC{?+(_EfMQfz6zJ#n*>0DUl%0Jymljy|y5({6M&sDEnmBVQwCGZY`g3J>53fJFw zJ3@A%yhhm)C8c@o*}1&-)EtaS(Yi!ChUaI@;H5`qB9z6D;+^1=ANl|f-uEE21ZsiD zplC~%asQM%$y|oCAu3A|I;Mo@iTm&2g$KH+iV&eaPDCgt$&!^!xorw1>u_m;3IepM z(X(U`fByadQPzSmjPYp)&rDyzoar-(Wr$4#LV9LCcsH*ueS@*BZ9Fn<2382X*8FGW zUpztzWVjT9NkucT@5}s(4Rvx0XRkpXufA&#q__7YNQ`-j=@7q_9$_f$E ztG<)}~&}wGsOoVB!Bb6RQEh{#8#!QP4>jAY#$1*P#5=*T5o% zD&2-`$cF4c(7af^+W4xr;q4@(NG%X5LYoR#otMEhz%$LX7AHkrBrwOybFez#4}to?yy(bR?-(9XgbxbW?IM zT8B6!N=TGf=pZJQo?5L+;6X}7XkwB~W3VYvFF+@hs)m|X5J_TX=}82! zRCs6b)}fU`5+GCynXjR!5n6%v8tpV9Q3N7DiIlEdMi_e_Aak1e3+Hjn;V08wNg)W3 zHY12Uh9q|sgA&ax!yw5hIRRQwwT2)HF-Zw&C4mAX9LD8s0K$2+3;>Omf~pmG8=_T! z4g)Gl6`>3kjqw4Yi)b|&Ro4xKq)rmVCAySl_Tvxm;OqaON0+c#fzZ^FWkfze`v{z; zrWNI|371%ODrl0;)PgG2(4kO>N`ep?DLuB9qJn^$al~2?)hrlG<{VBtVicB;ninLQ zrAY;(LKD^;Z_a&=Q;t2JKsDoKjk@&bI~~XHkY@0$BliFU)*4i#>8AzSVA8MRO#6JF zSbMnBi@DOPgwXH&6&Yr0asp7TYX||>*N`G!$Ac-BQIR#c{Hj6S1SDBbY2hGr5a4hq zq*6soM!^kW1wy3A7>+;r>sSXeT%H4u1I~#@JCKDGr7L&`WUn-8oxjxd#Vz&YHhl^9 zItC*Lo;p9!=Xdy={-aPj>Xl*@#dxZ>6=$f}`5g>kFi7=8(IV3OrwqV{{nSI8uz5ug zviP{i3zxsv`#$f%=Pi&cI`aEbDnWNw7gDYvX!Zl+&fa`r$*@ z%|Wft)s~Dm;9;MA;CtVj%Ku%n-2S4cUK!Z7_%t zA`wbabq1wFx-thUL}Z4tb2wSeNf%Xww-zC*=&*z`3R^V@>2RS0ZxGfJ5|CMtr5cWa z#4C&wL^{Tkq40PME^`Q%;^%b(nqfum;B$OeBDue=h+Hh(K&g8G7x^YQNpe&JS!UBm(=?V?nN@AG< z1J#+ThV@;P(vYTb7(H1^EojEOB}8FNmUV&k z=xih*F@dDUXVOBZBs!9$S%#FJ zKq?l_noU`iiPDJJX{1l-6b9!cf`|-FyAgEyuKeMz13E-VSD59&OGE)!IP!{#ULQ~TL4xg7 zd`saQYchNvfKh)>3PfnYdf0p4?FuQ@VD0_j^*Ep?jVA*V5HZk~QqJ2uvbtXk-h+v-24v8I6RjL~^447Sm+3ih<8mTUVL`Z3F{UcoNYkelI+(m)x^K)y0b(7_#-26;ZF=mtGX#qlccb@5uOeuq(f}x~YL^ zw}F^9Czf^fV8~O*pccSdy?R1`^b+YMwHlO~$C8q_L6u4|sewTCUAK)&LA|hu@VSg@ zZ^l|6>ncCqyFwMG7u;xw#(Vp$9AH3b^6EW5*vi$b7UA;elyi(70}ns)FrJST_kKga zR=%jNMkcnt7}Cfov(7$tC1&kXp8=v(8~&L89l zA|%4EJ>Ay(o_^mk2-Ma1zWN(&fnYZZMT@>U_dO*0;dWw`c?x8mblDeya*ltm&$C$c zvDW#b&#?FR6MpR=Qa`Xmtmg%^wztW3yeQXoynhjpAclxShHS{*>F~Pf=c}&SHkfNW z6dId4bP!_IGIS_0nMF8>awVLzIGbnb5@Ugw%w!0!(IQ5RkWdKHH0QS$$`L4mGr5#o zNd;)@QCfhMWZIBuNVLX89^X74F(RuGhapudSg9;E(Mfj}(56bnM&N}c z3@lz)jDk$$vRRo;=}N13AqZ%KY#3e&qz=eDIO!46ptK}OYskQpm`a{WR}e)dtnmmL zASCG08z6g7s)P{3$ShcAz-L97phtL*vl)37*dcI8m4ftSCdHTxml?EFq-l*rTB;Hp z0m56zYIp=rXwsx;Rq=U`y!W2W7?f5;cp-4sB9+7y6oOgi2qdVe8|fw9Wu$>4Ybv3# z6c1Qq3ADnA40H)^b0;x12|5f=E(7l{X@b_0nlU(4qE^cgE~3;noWssM91$is43>-_ zk|--cE+?~!FlfPMoe+e$a0E8!Kt;>)ISI&wqgK`=kw@7s5T06Q*#7-H@X=3ym`GWC zmJtRqo!-$AY0^kBihmHQ3?~Dmj1VHA%lD8d2O_|x4jc9$q6}9rHBM1p5?O&UJ=8)= zPbes}6gzBlKKr%Lpw%LT7IcLPgq}dfC>ev6#LiJhqYzXCbi^$v*8?uYs(>njG?1je z0~I*%E)V`>Kuc=zGG^2F?!-q9{1Brmj*|BzKpIGDNjISp1zki#wn2CXHb01>)HLrO zFPIhz07KNBxO%4aZG7qv1TDm%`+B=Abm3=W0Vx|Ys|~)Y%auN?Ye*XT26@kZKDX*S z2fd+JU9Kq~U-`Zmq&_p1zwgH<9fU6RjU#Q|>aiw2PPm*LQ<4HPhSDX)L^K7{hf3VD}G<$^F7&f59`FpFass36uj} zfMZrd*mTc7p8#>l{(0tgLvYm)fc0iMMIVsWTV>H$-BcIC5qXo5rH{ixh#cM%&f*0a zA}r+9bW*LtiQ)R)a=us>L~j6DEC51Cob_l4Qp)_hq(UJ3aM}XQIQr;_0}l8DqKL3LgVq$LI7SltgvW? zk=Pm@gTT>K>Bb@PBB9(=;>4ex$>fKpbM*JWNazhNbr3|T))tOB{F{9H$KPX%?cPmE zX*7<*k2#Jj|MxP&BtaU5@De8-2^m@`VyloS+J_AT=W(flT0-V4WEn&{K;bb)khp}f z6q48k9S6Dcf>LB@HzH7s9JeuNpYt;wd}J!4$F$+S$6D|z!byqH0_QTk&mA}^b4N@E z60Z!I%W$D*#Ks$Q?1?AR))MA~fKdr!9Xcn{pr%TAJlf@QpGJX@BG)mOfg4R8cCVTC<>IWU0)xw_ME;M;!{v z<1)u)8*jp?r=7+_Pd>ot&Bv2iOCSTh7le_=I>k2IZOe|kZh=+-Y23~+KRJneX57VB zfADqMbc{mh(W74n(g-h$iVK7AK?7_d^YZID$nkb2LIgcMJzRI)bzFDdb!1sOc-c0C zsAm+i8mqP^TbsvKI;1a}ZuI#ENVFFCtj|FUuX=lokx05!DFGuBGwfnTX92Q(rT zq7*p)22$lJC%w6G54hCh9BjU28+-04&D4|8LL`hG4Rsi_k9bboo zMPnsKLlWJ25Z)n$M@YyBsCjV4dAN^a}dM# z=fEkIptEy%pTf>+=y&P~^M*TLkb@(>A)7yFWZnDO0SXyn-P1;54WLJTpImo9M96g` z-5iu~Qw%t^0o(dqR~t&|>DK|Q8Ss8dKVsRnZLA_w-3zBVk5Y<`j^$i`{q;Qh=%YBl zo|TLP5O>kP3PTJ$!J>Zc`38oJoMUNCpwiGbv<+=T+dFYvU)x|1qlO|AiiL|8bJ&sJ zqNlS1XDlKpv&?#0Mz%3-lO$ueWdkDzT_8}aaF#*Q6Ji}EDa5w$ckdgJj7 z8!?azgwRd6=9m#x3qgJe`;=CdypmlgFZ6k*>a%6yW z73RJ28YiA|D)Se0phE?&f|3~)L5e1GIdWm0Lk0m^5Hf7|aE6Z_!LU(nG{uTkE5>cH z74O?;FGh@Org>ycDq=>B8bxztkjhevM=@$lE1{H>!WM>)Ze{GakxZB{mN3+K;}J$u zZfmExTtW&*>Jp4KIgsINc%9({lxZf*Y6vx)cW=EDFV1%4(ijmN9LkBZiM?W7ADHLSP8A!e%KoV=#eW^q5iDS`8-x zd{#ycYi8@6x8~_59;at{2dDq!WNyFjeiA8B(&CgtM`ac)S;kR6IFhce1-auOH6l3}qJ@-0BBl9YU)Ky4r5eV2-5_GGsfDPGWvBs6p^*fh- zb?*~qz&YxxYBqTlM*`b!SH^n!p(LVVpk5HeO9L2$hjKYairh)o0f-PFD-OrikFi!u zDMgS}D^#lR?eA^|qS}`rFHTK4YQ@=x1Y|DOSAV@XL;R($?^+KkRj+c3^n)gN{VMT{ zXbAjzL5ws_0f@E0+oI|t7-}c^&jL3FE7dmrN|FStR>l{>muL(U^U6a9Sg%N{pkx_q zZ2#hg-co%*y|qO6zOr|n_mW_Yp{=crrAt>?P5Q~S`Bmal*YC;}ejww4SOwHr>nhog zLu8O;b$we%)>tL6CICSUtO9vE6hPjNg1xt^=+CcVpI^=2Z{aw5TZ@Lbw{yrLhp_wZ zyXWA%UyrgHzqSdjf)@9?90u;|1=xqSp>1dz+Wup<^?Vd;u$NiUWyXvg&c2`eApiZ1 zo%znEe#n~(Q@W*O$B*yJ89(?U58r(+ElzO7t2^4N^2?DL-8n0)zfxaICytUy7Q z6F62yf{#ZqVZsCgzmz}xbuyp(&N1w;!!}IVs*Tbn?_;OE58&YUXb$<*!8kdZ&Qx;9 zcMs>in~Y`IyjO^$CeFX$EcQ6?^BlbYZj5e9dH%I|4BK`GPXGM>kmxd>{K}!U#GdW< z-<>OuIf1`Fv5Z5$emJ}CIhI#nna}6;+l3>(aV&FR>L!tznrI@)65=q$Bs~zvIGWIC zY+B7TmR_>QK?m}^uY8H;pPWsaR=Mx5zh~A$#jbnqL)*CFeCX33WZu#R+{eV=LNo7VXOEilNLk&|7Db)gWAn77zzYpxoEx-8%U0!g)$;UJ6&fA!E-!vB2 zEMNJ~kt}=YT9$gnv8SKU)A!!Z4!iBhr5BvZgVW~mo;~*FyQdt&?1!Fac#!hf%m2oT zBt?aipge}-&p)08FV5zVKfi!56gV@Iz4v=BZ!CR{oer5{J3-Z=pkQWpRt{s zbJiv7zu%GUdEf`B&EAwVPyZdE4lv%3IZdi1U1>MoX9%I$`6Hj;CqFxsNAG`(4{x_U z^JYy)h$bdY8pR`1ALIFl=koFcFCdzhk;#@ma=o?s!g;w>&eGy=UW3SxWH@3(Jy?GG zOiGD!jxW% zP$|Yc&cyn>)z_;o>R?~LKt3CoeQzj-TD)il_umOwswlRp%lvXyHCAXrQZef&*KGJ9XqLiD+4mH=l`t$CI1YG)&JPy zB#(3bCByT7Jz|Y#sgY$vHaxgg{#@}vzHp0P6({jFqdh}5WJ5M&?=1IYeOA8>1~DSv zL-(?j|NGsa+4tc0gE7Ruhgt^hEu$cT84pe4)j2P*yhAf=w4!t68~p8Wf8z^>PUOZv z{)vP37*496VaDULdH2%sEPDAFzWL1$P>ui2m%n}p&p&+|&pq`r&bIU2EgaSA7zU%f`@0{#IEI^F@Dr2=Dxa+>Gw@z?5Hww z%tjn|z`i`S0GjyM;@9@sdW?1I_|3+`|YE6`jNTdLR6HXY3HU}Ze-v0Z^K=`znV9?nn0Af z;l9WC(m`M2maC?)=iXaz&IOmz)vakN1w1kPF3!8?ai#7Fo422HVL@grAp=^0b$k}08V zqtx0?);64ze(`;lJn|@4T=GYvrctP*hf37OH$L}qCf|Gw9j1v$I*1h3brX=1S^=U; zcjqFmy!uK$_VMkAqUKyq(&S`__dPh3pPhXw8}IWat~lxI%)I?}YH}nQ4s=3lBtj@E zJt>!8_W%d&JC570{4L9uCzz;>&m8sxo}GCQ&p!MJvS&Gm9rXilxN!y~-Hg~|6uWG{ zBR8G#3^7eK`6gDrFppoKbSfReR$PA5B{U8H1Cg&%HYId5C3R)WJ|R*SF1_Mcu*Epu zOBcpwcoC6g4jc90b%nAn(e0W*2ORLFgP3-~#r)yAdsuMDfxKsnHdJjn7oL0}KRM?F zKJ(cFx$J^-nKpGAX=>Mj^;;I@*1b-80qFRaqVKGNu)`3-B7`X%GEwI~X#96|&baq} zP;kN*<=sILAf?QS_TEY)AoHMnvq{b9#(51oEw}E3VgW#kplOZXy3l}38UmwLg0zBA zLKIvR&L`-86I?^kAskcg`xf0@Ik6$o*N)OVcJD7*09Vf0oI^l?CoO#Ar2Y}DBD~9$ zR)SFFBZ$7Ln_fOZBLzx@#HGBYBy%7{K2H57^96{}%OQdVrwR~;0VNvcaxs3LPGN{- z?VTF3tu=fVeaawxEnuz>@GFM!-XnZ8C@|&$??R*jZv%|Uh{F68AqBk>IK8suq5wsF zK@=KES={oHAb@JE+OK)b)2CR15Pkd|zG%slSq|L!4Rf^C=yCaVKmqDXOk(vwME}y1 z4fFB_9cvJ;agcF+W01Z+N*{04gB7be7k(|@E!K7pHyoVz-XjaLA7czb5DfYQH$J2C zzvU{=Ofi5w)cb;mLkumLLpEeXHe~C(HgD($RhFV{PVAW~gV#-9%apZd>K%XNoU_j0 z(64=mzuxc{Mo)|g^e}t{NJDHE;Y2e|m1rBj5r6*kui1RFc1ooZD$ocO(K4)=j%7>e z?(E|6C!XZEv5Y)gNh!K}G=*fien{1vmi_O&oUEVVrgD>5LxHPPx=X z7${m=o3S3!N)Il{xb)KBbJyK>6NMqdIUq!!2!jBn6dp+&muYTkLWsPywp4DW6e-?Z zvJfp5&7~%s2UBFQqcFl{7HmM^Yur8c0m>sbVWZ7AVc#9cbLU<6V`Ui+1R}*+OOiQ+ zkc2^ike*pHXE5*8R|&!xB{fHV?|a1MknbLS91q<45MTJ%$FZcypq&HudoPQYKF=cK zkV%#Kue`|?n{UQ8+wH)|4*VJ$we+xfMGs9)!+B%gi#T7UqXxq_8iP^^N_nWoC@0W5 z4|2+-5;~07cI)@Ea@i6TDOt@TER;4L!A8S}G5xN)u`lyGmb|caTUVf8j5RF1hNNeDV#JB{E&2S!)8pJXu^37#a!P4C*@_mYlEAM59M-Ra5FsQ|g0U`dYbq2Otmz~6M1a--m;VuNz2yXK zTBwB3N8alTl3rhL2@CR0GMlgGP8PIYvRW^`l2X&VI(p~(>C6ixJ(W(vkcR4_Tv6xH zufqQO{b9rLo&n$_4eeZSGp;1w+FH8W@%&oAnsps_L)ZGX0S$f~_Wcv%e`_6q)>vz> z&Jl*;;D;eZ@$B@)-+jP^by=QZkP`1R@Bvcg)oRAL^aSyhcp#J*Mq4nCtJ3hZ-4oleC&(g;Pc-)3?)(sN^G&?Cd`@h zEZuK*BGMW{XaaBOUTPV?(Kc*8X$!`-wWBO(3*DVnw%=(_wwyE`DN_Ow(bhDUE#CER z4*J+0+n=NehX;$XF`#I~cq1PHaAD3q}r)kTyY?h{}qHO($*6rdy3gij?R7 zK9?^acOt{Ln!vEp%?PD1E@0=~c4LzX<4L-^Sp1JSDCrg?vQD12T4%3NsIkr>rRT|6 zGdTO_zvPz}ozG1-+{hQc@Kp}^^ndZczyA|yYWk5?dhapDpp?Qni&6?>3_{2{aN)ft zj$^XS3_b;)J9ARLRWH8i1{Qtli1q3axoSJ|DycWIh9Lz+WeCE-obA33ItSw?!ruFA zRXC_QA?g5vxgy$kMX;0yWR8Ko(5o)Hd3HGiKCthWSd(*942X<`#kpMR$$ObsALGy} zLKdJ$pz~oJRips%Kn}m^ol96$LpZF1aufOcG#F=AWd)tj2}Q+FHS~Xmt>yly7cdByua5$}Q+TWK zZ!1<9#*Z%uu6xI-zlnmxQ{xOImHgh&DwjUa@id1e$+P1=uRp67?|YSB-wx7Z*byVz zND`=})jslH0j$Zi ztZCn1M9=Ux@p<@y$W!D%wQ~iqzY14ULxp0N8MIc#n&|VS3ZBelNU3x3dZB=v++B$E zMWu#p$cAjlhCqxBvNTJ{ObsCn*ZyfT=U;Fh7hHS}P0b~C+Hz-pf7x%j`|hbsoUj#_ z{OW&cl08(aHL_ZQNGrIso1R+A{Fh!}=0h{Nb@JWZbn_IJ&99+qi@4~Vi=deRR&vP#=z;;&o;n)6$z4t$u8}Gl9%YJboW7|gHML=Rb zS6+HGm;LSvetyAugk_0VmIy^iIq!kQqo{JsF=sG*)TUg2$L(Bs^VRI}zTI)5X3_ll zTz}2=TzcKL{QTn65rHR2dbs1p>lr?NA~*c;nq1+8DoIi$H6;`wN@O@`utF14re(w! zv=mrdBXczZ=Rri^G)Z@gNpc4=oG_O0BU^dwo_k20!UE}vrL0)6fUEy{J-__*pLpYi zmx*+hi+*)69bJ-ZuDO|s%{?4@%um6TsrIC-aEeSh%1RR0gou=E=?YfPe}gZ4<48XF zxi2z)9FW245g}=C%h>Sn~>H;2r{8|2b_03#&^$qOyo}IvZO5-OornQCpr`}G7 z4v{jzR+qA}mT~Rv_i^I!M|1v(KclOwhbdF;W#*$#aqI0ja^zv3;mPM;Cvzp5Jlyry zzY(=;##Oi8&R%=&K`a$A6a4;Hf90%m&*h>^FCdNtS6*~6GhUj{Uv9geBYt=|bLKve z*NV>{dMH!wyN5gOn9R$MPUq^Y@4>mXv&{EO5T;p5S9b@0xa<;U{{2}Vnl_co|L{kU zWhT9A6gS^^8*jX^u%EP)ytdga{E} zEl8m;8Hgao`)Wg(dLbz*JDC;1iGTVJJ!JouXotwJ|K8#x57x+B-g{L~!^{3MuEGFz6?KrjQcMC#H&~nV z21uz^XSFrb#t09YULNM*NEBVl^SK+KesR$vOcxCQ% z4mfBsUS`DMBA$J6DRUpoXey21ytB{c{Iky_(lIhCp>&L}374IBKEFNpOr#S;L5<8r zXjNln=WBfB69;2!j%L%0Xlmo>m5Q(IK8pQ!+Y($0rUFJv!s?q`_@h&~;OG-D-k?N@ z-<*6hzc}S+oVIy((<)|MeLYSXoH0bzkl&nm6tQe4t95b2!Jj3u0u=-}(?b|&x|h!5 zYoGsGp4F`-2sI2#K(jJTzxNKd-)dU|Agg(T5CUp=pXTiwQsQlfUb2K!e|QY1|LACp zsbNJ4sVxyP|NG0|aoySHlZ7Q5iXe0>oc|2_?Yt}PriE&l5Xu^l-hVF-KNO*b!8?T! z9^tE~isrPhe;Kd9m0K_=4D)3Yu^dD|W(BAS<5G6n{XNWhs6yV0$_V%H33#FXXca9E>6%%$DPnrX`d- zIei*CzWYOXnUV>ZHgzid+%uIl%}`N@2x2;GHR3?ivv@vV`0p>{D>b~7=&+11n!o(+ zVy^t{?}${4Vi;}8@cVPl<*Vm9vgJf%8v2126SDk)aDO^0ICPm1-BA zOP6u{Nf)u%2*Y>3e+;Lceh#CX7V+&PE~Trx+5ln*gvhxuj4@1|I+bUioy&v?8*{(` zpCpPxo_Jz5)2B~o+il;?zWeU`Pw@r&WK9c4Us#|EXdCF?iZyt!sSBCWTAC87u&8cuMHP%1hyyn&=JoDdh5w>o09QEfR)CTTQ7Iz`iiE4^ zRglrexGo1ldi7!*Kv0*YbT&jv*yDpCQd?G?P#^+AnS(N6b1o%Yih`WZBY;nSrj!SF zaY&Xz5ar;e#G|#uQE+S&P>l0#z&ZW@TH8Hj|32oUK3WH10ZUqgS6+f@4YUf;I?mhw zaK*hL@CDyRFYbXz8-Tio0AF})2JhSLJ3Ra9Z^_d9{;MnP&|Fs`%886qAg&MM4E*}S zsr>N!`%&ph@|F~nH2?^<&gZcz6@wp$H@U^n!8 z-hv{_Ixw^Rt1tcm2bfnNN>;2`!P8Ga&GhNhX=!QUQwJTysF9;suwVg`Cr=@Y%6#&Z zAEmv0RFNFWD;Eb=`VHBT4cU+l+3IYAD={j8rYbC%=_1TxOxBGFYgBbW)sDoflqm9$ z)v%S71hPWv9YTa?Cy{A}77A)TNGE|f?<5z?pe7MELQ;St9oM{dg>Cf~@BM;wM&*+Z%%%6Cz*8JSl|pO;T3k;Rn-neZS>2w6tC z`~f1$f|{sO4qEASOEAilWf53~uUSGBAiPH+a86)thLh#IblVt=F=(ssPLdf%9LHcC zKFJVP;B7%uCM-4slpxJgq*Dl6BTS*>EW%i&G8|TDQlC=!f9$Kt(|$jjl)#1tfzaIW7oD5>Q}q5fE7gS4jeb zhy*1_4nt;g=Z>TaR!(sJw$Hy2zuZLkMcMIb^Yp-19u~#N*7G zHH+)6`!9CcWoK@??KYl$_PO`WTw{#y3RP4htu@0ZsdzOo{h84Al!vT+}Fe zF=%U3MP$MNcp@wIK`qOwkqxR*9)?Vf;hFlp6_B@u_Z&RU!3`$EFFrH3B#K1ik@V9OR+MfC6@p*S&? zscByAc7R0lUtFOzkj*pPRs5$#Cf-?nF5+}Xz$0-YfpI}tqJX0he~I4t>3R;aBx;{P zLHb-w1~H$i(J+Ju8HRubGRl6N;Xnt09KU zE$>9DH2ReGM-uut7&7*outt#tK@;h%az#N@V{QI@9h(73kwtj7z7usG^7|-!y3@qK zwnU(9TX-BVy!=8EJ{2F*nHEF8`}QX;geQG)Amp9LYViAfsKTKCkb9%=yn2RGNMgm1 z4bX$WgX2YvI9HOJoY0C0p-@pELPQI&?Si%sj5Lxc4Ui$CBl>_34(Y3eM=6BV z*wQ0~$0$P~97s>_O>$)@S|trrCmM~5X-;0^2=pLT7tXdZN|B+7p#?S~l|&jvuqCF1 zRxS~3MO_M-VkR-~!juMEION8Ix zT8MPep}J_(i&Gi+5(xyUh-E+;4@%+iL=|yC<86TFIwUGMWE6M-Uic)u8o?G8 zF9Sw;veM#J613H5ax&)AqKJ}qv=}L=3j=NeDl=FHQx*DD?~qfInC^nD9U}VB1KW4wPN|@m*<2NPQW?Gq)C&QHf zCQRUqU)+`FpMQ?kR{MA*U{=8am6%zd)oS0r7ncr70w`PUgXu4JQv1ITJkVr}9=gjF z*dZ2|F%J^d*8$|vIsr-2e-N?~>=X($s37VaLyx#Lm+z^-3^`OlUJP`UMPEJAN9`Cq zrW19)73DyQ=#ZqxGoh%}5@kGA1yk&^SVgAq2om3~85jwO05y}u6sz4JdeF%Jf-uu* zo5zfWrI$|NOvECqSs7EQa%Ng!Dy@+;GNKR_i^DmP6hKa&mcWoB9>q|IWmp^IpS-sb z7l%s@X_0#hN}pId5f8;0^fpJ~`-ETVNJr8U8*0Wn}97Vp+E_=mWd<0uyWyEM|*cS9#QCL+8Vj-%}_WunWxhNs#xG;vbS&bHlfIKo7lja&E>YSUr3A6EU-dB} zVkCsXQcEtu8E2fyE3dxBk;k1*!lvpRx+GB$wC<#L0LrL&0jL309(+n*-R2$_okcwcy>j3Qn+8!SwnO0~UXa>Wm zJQ8idM~U_xr40d%cP^y|5}dW^^VTWyS|>%&n4oDzuoTp!B?v`S8WB7)rouw{n7T6x zTmUOPI%}ayO>~g!POK|IMWPT@cSsAMma_FC0Ues5hzbxOBwa)65J3<#gAAI|Le@Ew)z({{XYPA}7&M3)N(qbw zCmMJmA$Sy)LN_SgY=kbbUXuxp5CvLlN|E7lm@t=2)+l-`OH5pnQC)T3e)=_}~A zRFZW*z7j7#^*4$*5}_j^dUTYDabYBFFQ_*Rp=t3(Q)tboFc%r?cwz7Yga|}>5HX_A zsesm+PHL2Xl)|Hdn#r)ir!L%J(MT~UBNhX6e}iP~$kBXl|NVKh z=@}JjcszwJkwrki;9;YS!f zdQ|_cZ=Pp(?+3Y*MDDrgUI47MD5d&Y9gDJJ4MVbA3*>_c3~`X#M*5E;PkSgTgSse& z027PL-31B2ct?VsJXZV9Ieh?xSUB&lL<8mGii5@Qprd4{4#vQUNkmnwhS(25$ruL} z3j>0l{XOfb`YH2$@0e$GYyn+e4y8a#h<%j&YF`4L;M+(A#(;HcG^Ag2HC8|pl28?! zscx)95*>sBbPaSi1EVISWSc&>ls?xQvB<+9{;9gG!||R~WgJ?YOD#|)S@bHAQ!E(E z4L97))B`@BfG=qfvJy88eZ{dL#x~X^Q0=B)Jd>Q1oER7ri6jfum_$5=o-^N%Yd*gObB=^QX8xfpd`(*#3)Vh0-e=qJ7{|kMv?{(N)R5S zI%!i<#saMcE|dhFVKNxe)yWf2Kgo;JU*w?g>_(Q=z$s#gSR={u64g;h>KxZ>Vq#?1 zz4zh7laB*wK`La3w3;QE%n?MgPDPLuj_>&%FLTe}4RKmYc8~xTLO*i8yHq{j^s}h#6ibW(grV95^3|Qc&yY z?txs{w-FGr^!bnV%dxq9w{et&wr}gMVOykF7!0 zHHK48`5p~QOw912jWH7K1tW9=X&p6Xm^*Jax&f*y<3De_nLT&imP`g>6s)|`syy}F z^E~q0LrhwEB1UGosF^P_d}tBYG`Xxn)e(Hx z1Wba|3R@K1bo~uXd*e0c&7RHlnQt?D)=Xx+HJvwS&!g4uL2E?_fwC-d&T-&@2Xgq~ z-(~sbm&bcgmSwEB-g?ZOIfF){!L(^FQu{g#04_eH<<4-NhqBV`D_5Dsn zF^Iu2fY69IXbgqHudV)WKS0$7f`maV2!+ZatIwrqrGy$;4y{O{!i5%O>0zUxx3?HF zFd+MS%VO~HOeuUO6KL{Yyh$5g(Y8+&`POCYWJg@aLx&Wdmfrz!BvRYyrxrkC8rFjSKC&qvTTE2-= zaN6l(`O=r}PFvYGveLgJmFH_;A15|A_Pf7I!kJRw0^~WU2-X5pASIM#`1@9kzef{d zP@x}uiIR#?R!kKE84Bwmih^d*4ShiDU`!=`NauQ?)$RY{D=WMs#wj6WJ(QLQfwn+?IN!DF|UFOc7N!~GvcDso!dU<>L zd__Lh^@DfKIQPW80 zQ3QMlkTvKDGg0{{N}Ey7YeZ(zwi0PF%HYY2#+SX&nIQ{Fa3xu-px1YRg~D~S;io^# zAt#(hUVEJ)z6oj^$S(Gnx*yxFH-Q&t&!%2REqbeiuD}uyQlo@PjtsJmxAP!tP?jF4BUU;Df*?!UULqSA)-|b#h%kb}TONJlQ6{gy z5f@x_97SoVg z8sR)zOM0U~VvsuGZ6FAR)e0pn#<$T?)9PvC^DZDTN(o|7V3eX4M;0}u&{!c*AyRXK zC@ofvVA<6t@xrX9c>2ZXC|!eE5J(>o+OfdoboxMWnrLcx-6X0&-qsju6f!bPg3yuD z&L!95$i2td`OKFNV# zNZy>cfLm_+A8gMILKufMIWm`o%+pdy1k?A~z!ng4b-8qEULNzDdqvqjx_YS9v9Cq1U5bUCZ8whC`8c#)@He2Jd%tI(_| z+Nz`^qD4Vj3Y5%ozJ<^|SaM9qDCU$U-jv+=+gs^*?I~P24~gXMmtSMc4Y%gvtBxVo z18v_+6m{BiBqDFqih^haLO5(!i_d=JKz80{3toTjNlrcLXf8bd3|hifbK38MOh$o_ z9w9ZazB!Bg9())@MsL%TXE{OzL`~3|*TLHBtitMRtVBdqqa=xK)7#t2Pk(w9?RJZH zyUp&q@6Pz~K#kesnOn(nZ3En~0 zfY1R!`Ng=CEMhO?Aj{R^_?NtnrAkaZ2}qHrYlco_fJP2VNZLKk1agRKoGOeVMgy`W zcmqDi$R%+0HH3hGLrMc^q=*;;fgY3wUs`Yjpj-d?AwbAT86i_Sh!Uxw)oN1DZFPCu zLFj_3z|+c2CFFF^8Cx-vGMr6Mx%MY5_WEWWDK*YPt=94PRKkD9z3m4^(;zV{1U&wi zxvag;Xd+O?#HNarH1du#Hzw4(z`Y+PnkD})UDoO18Zv_@=;`Su&vTSiXswAcjei)O zQIu_pq9BBb5D}#eKfnGuUVGzpdYT2>ZnG0@uh?LNbvge(&O-IR#rB`y8zW?8g|3tS zOAN!y%5V(Fa16&s4Pwkw9eirTbvf%tXESE>0)Bq|fAjjZ`H1>B%$TJ(|AKwlY|^J` z&+6ezU)`H#Hj!Wd`V6|;ZRSqzVbZwq9DU$E-0|dWjy~l`w%&SkbUSjv@l(0}&PVAO zIg#%keh6D_vmS3vo52YOPUVF+7cjHC$pY_LqV#yzgV#&ZIkJnLKes0P?R`9E%qY%0 z@@tG(Qt*vKPGhStevUBhL2kME25c}CCNO@}4Y~BVV_0sa=9L+*u+MJ$GHHXIx%jMO zne+BEL}LWcyzvl+ef22Dj$W24uDOC`mNq>1#`7Gt$G*&*oiSq6b=hNFPnL8IO5@j^)Vm?_7f)O}pP(q-hBZ@#NYILr!3}+p8EbFeh7DjkZoBCDm z>+ayZ|GJ7v%hmbyU;e=Juf58bc0Q47Z#;(|Uvwt--Z70OH{G6JUv>zaZ@eR8#;?IC z=N!d~%PfHgzP0}$JoM+k5p^f$UVjO%-}yT(`Qf=VwFesrS%$VA9iWsXrUxbSF>JB< z7X0@m=P;*T=j4kn;O-l4;jstqVU}yM$03K}AA5+qA9|c4PdkavY`6-4{p(-&{=vu4 zGZ#L#+L|15-mxq*Zb_DkZ4TN07#^DT5=Gv{2s4rmr>x6fo?eR6jh%S`%e0-f% z$!eN8Z_c7-Ml)ktM$;xOfIJG?W)u@w`Z(uYcRfoqn*8OOGnh6T&?QLE)?01G<_}Ea znb)7^)PoMTO= zCaus+!kZ#TkDka$XYa@4Pj8Nx{WgF6@9QwFaqRxJ?YQLR|KO2d-$N!OQb=qcC;MG0 zM~O@NpSr<8-#v~)mFMaE|HzT&{FqbEI*(PC9nIW%FLA)PE~2~LMrImsU6p{PB)Fra zlLHStfEXiM>wX~O&_fT!dym!{@6)JA0xSf^K&@7zC`!C{Y4k{nMUImUt>@!HwgBdR zWSJj+bzI5%eRQjBKLb5}plw_Dl{OUU983nSRstOe^=e0S)gFh{*PMWliSwf5LV!gd zm{JugeY4(ukF?vpsdyxyw4@Az=+*n$W;qOC{AmwYPpB-1zdimqJ5AY{r=EWv+wP?<1+HDP z{f=LxQ*^TRR-5tVU3cN_*Jq+ejAGWz7diQ~lbF4r;H)#wWt&YuK|5N`IQu+y+~MEY z?;HEF`(As19>bAGAHu7zJi(6J?!=A%eH&+-dn!r`gcMj+lKDWrHWn*7S$Wk}c=Dm& z@wwf1@)RnLbC8E^~x7(d8we&a^%$vzq_uP{ypWT5!{rO3ze)aP-#*O6oGfv~| z6OUxuZU2pBmS34Ix1NGl5$!C_yEIOt1lEBMIXbV=IZBgtIHHIcvlQo_dpv*m!>#<= z_EY%c=XT)tk3GhlFTTh@Q@_bekH5$lw%Uc=_B{j}N`Cj~V|;ekJs1(2thUKIT>X=u z(YwI1!o&%@^u&GaFlA@9p0Xp4KK3{si=p5zciqL`o_`jb*D0)^rVUa!JZ)lZQ&)nf zYZA1?)ifJ!v_22r{(GeAcjmL7-JS;@dmNW_Ff-KHY45MF)0D{^cj#f1KF1ao zL>K$+`dJ=*@LuNI4qC?2nMI(NK${o>Md5hql~>s2v)eH3<(IKViOFlUiZ<3+9)93H zzOena+<5yVoOafU7}X}rYm{`+r9ILm;mzrD=W@V)hw}Dwe`eb)w%{v!euHjN zLeKo?*k-FwbL~yP+M4UHzLAhQq=y@?xP=o>ID-@a;|Fvtng(Mr-hgus7X{ihl9;F#6t3jK zhaYD4tl8Xk=NYth=QMLXs`&byZ(&jW}OK_ps-)L@yHa5dFK5WUIr z*MRl&aDEoo{%mb7yx>kWNk1sWh>)RbjOUa^8c)e<^&}R)@CJt*8PYvguQw`94h1pY z-+5N+2RlFwXtq>;qPN)u4P7I<5V8`stXzFk*S)%b-_MZ|fA4Wltf@ZYLvSFKA=6{< z?eyD&4?GhYLDGppRSqR$@C`cvl!^~}PKP5M!LW!mIO#w2eJ8CXovyC{J_KR}=RA2< zBlv(ZI)M{X5QRaCNTvmCOLBe+0U>2_M_bHAGrWQh$8ZeCa4b4LFcZUgk8hVmo8y$i zm<(y>lgk$W^Q-@4%hkWe56`=Vhwi_ZYkqb!-gnTYJ-zd~S+KzK{IoaN_~T1aA32uq z9eWrnE5rHP_zlYL<)_y0$FRYW!R^C6{ITz1J{${1{eQeLQCzbr>_-lA7& zoT!sgBFh4rp7yL3Z9S4)x4H3_JGlJf3mG-Oi|sf0IA{F(kMJ~zp`<`3P!D)w5 zk)mCoI&!W&|3_%!2tJaT4!-{NZ?MUx>oBK#HqSo!Bl(v_7 zvn>YC3(vj6CstX8ownJSHD6who@lvwUcrhhFVByDcoB2noWY%sJj2NcZVOVNbcUZl zjh-3PnNt=>IU3b+ti19kSoxc)@q^ty&-6EDQut1~4EPaC@|ks3hoZ56bNlDxA4GfF!1PP8<%TfNMg@fLr4_yx{7a0fc;l39hrI!VBz zqC^Qv;T**Rhir8d=J(QU8pu2O*rZRg&e&0G_Q{Q**2T+jy@DkYM9zBat%mX~=1nii zbd6j{ge{r*_S?KxdY+%tO=Fb7n21(}vheiCfU}-VMP_u*!584k@IoX3PZ{wdptA;A zX2<}U(kwe+Y3{%1G@3K#F?0GnmL8WQWXW%?{uR$W_$XiA=kwfr{{tMc!;U=k)NAj7 zJti?%sTD>jytB9%nXu}|SZj@s@w2P1(n>C2xy6K<3h+CBNMto-nWaEuC;m;4k`k#SX z4z;dn>2{4h7!{Eyg7;VlWig7@3@BYhX^#+4w4u@Hq3HF<9F|{xX%@_bd=%6=8WcTA zzol>%ZJ^!TrnjeAU0;1Hgi4s&)tIvRk=%3l8Q4-*ObCfqInhD0X;BJdl+Y&>T#16l zm~=CohwfG{g@?i=z)z{P=6DCr`wtZh>R%lPQ!-+5bi=}*?|+pq?K}w&6g{0u%d;HS zza~i4=S(FQ{l>!;@P~rpIcxhreG;HGAjc}DN|wGtRseE(ju462CWXL9a3Ot0#^6EL z5VU|amLi7*kcVS9hGRJXaUh1UifBDDD`>@xHc6nfqvn{m;8hMe;G2wIc1`v_a5r|J z`US4PC6L*`(~msP@qhgR>#eyeWhNLic3H-asPm1zzryHI%kjf=_NOb)nLT43xBlr? zuD$vf2sMs+rx9uJ001BWNkluih8E8Dq$Gs~HK{PcrNT=Z>Pn-d zLw1cKc8#FuE~o`~^V#RvYmePoa*0lE``s-pnE5tVXgYGmm6!aOZq?1CodcbsgFW}% zgFiidFPH!53bxy4Dm$)Vm^W({z4Jqo)(C)MY}9fSqG(exZVav=R{f3E(^5j zJ>Rl8;@|kV7Wx*RS@Z3{RW257$Yp>yo zpI!`g!Ai@F=eoB()RrEp5IZ5(THLJFF*StKR9GBUi{k=d~EHlXimSA z8ND-@-qYk$>rdv%zy5;p<7&*GK8MgY2(M6iNeE4(>_AIHr&i2-{$@&(02-CrrciEf=@41cMRu|DkVrWy+W{z#4M~-6Rh&pAv&GdPJRX({ULP?~U zi;tcdAd4E=_Q|of;!?&Pn3S~8I9KyDF z;pt~N^r%xAJLwaAX5*E4_?Z`p)ZR7!3aI4b;Y$?YWkx=3Db6_g2TXhU4d%?7&&Z`l zu-v$bWL2l=AFD&LaFv_JQTjB^g6PJ_F=+lVq6#sFsyq7;FSZhTODM{M!Ud!ZD52k1 z$s?5{&lA<%xe99#A<1fp7W9D_!o9QdR0~%%i+ZoXbvK?#R4`auE`&JHn3S~yX2|$H z0gC^XIpc8r6O?-2`1*3}vFA1X>@$1PYIbwaACIV9C<}^q8z~HrJbVDZxZy=M++bzC zxaA*MXVV-P+LT3d8#(Fp$s9cO_tff86!YmYiJLWN-fTwZ%h0HUpn;Uo$TRSf-~m(V zT2w;7T8u)v37mQIlX#o;7nsfov;b#QktwPCQ$=;ZRE|L|NP+@Wt0fRYR&FT1LIMRz zi2Lurg5LA@ivkse2Nbm7SHJiR=bpDE5zt~O_Wv5>BbK3EKrNR;-{{?@?1l^EzvG+< z-v1m|@}iYfr{V?DwMz!)xVm499Q`$KM$rc$}nZB(@{6K+NH; zdtT@4D=y>M^N*pGTejMIS1$e8l^l5dk*v7p+C2K`<1}P5b!-byYhEuQYI-HioBc9% zm2vtR7jw)BXOjCKvhG(n@0?56c*-7Jc=;7vdg_s^J#ht`7nIKO!0&El-7WXy$5;NC z$?LApv=^UenbkkeT{qs(smC9~T5ErtdCt<42i|)51zz`_Q?I&`gAe~UrcqafJByP}JAsZ4g^DFQ2HMStb1AtJ zaxVXV_yz3q-D5cK>dQIpg8yLh2FuZCEXC_@&E~1;Z}9zVuHd+H4q{gKOhyLFP1jt@ zZU=pf-(2xSvU(jM8{i{W_mHU$Wbi~h)XEsaK;mV7D%!NPw9jdU@;#-_`;gzhj)xMnn zql@^#?vpua|DzDTi`cfz_L80~Q1=4aY7;^)Bc$iWU){sEzjr2woqZ}@qd>}xDch~j z&u{oO&T8rgvQpxu;I`WzX48#7$K^l1jL&TLX+&_Cvf%!^?&Y`>&*4ALJ(EnqoEdZ3 zaKkB_ckxd-c;C(W_Ws{P#nII0p#+;{tfthnCxT=vte+43_R zV0-5wqv5x=Kgv-joXDByoyLfU<|k)gz}lOCk#m1?Av=G0GiJ-V)0&9M1ZN@LR0<}g4(Z5rP{UZ-g zvQ@3RnjwQQU?rG$^pNKOF1h=B)Wz68D0j3Tq)NIWxt3_D-}ieY)w;aiOYc2$m3r7CrqxL`ILR0E{$mf^*4!TA0dWHc)X#-mm!Fa8mxS9zH+_p3*}{ zV-zFDLRJH79ZJ*@A!F55iFpI~QNN4)JI6wA=+9*3j@t)Sg*<86j*L8eDyZQlq$ ze>bKgL1aiqpac}IMNuXO#(<-0=pWn?mkNEzcBf@*0vb#pj| zV>pK6eZ~KRiP55-XGl@wzs@|Fo6dHa2*GLo@S9(9|1W=v&uX9)McZNIYizy4FTr>y zrt$RSp2zN=PT3Kdy6;r7Xz-yIr5rXYo_g{j{(bk)(_s|SYNT8MXu4vXNAA6gtseM2 zvS$w3XVjKlfu&bGjTBIN!YwV2B_H# zG-Nm5z@M7)$Qw0kp~a7mJP;LIaQ{s1zGFvpR;ODOjCDQaBSvuZt(S7k9hcH*8widl z>I5qpON$H6IhD(55^PB>GDJ{_*o#)l2i%nb5ldcv@E*Rk+i!8LCV5t;%p4<(0xLLf zpZ&<&ZET*?$j8&$JD=B{`Xd{EY%R>(xpapb9kn_9{*GI@``!Yf8$=HXi7^pt=d;(A z+fw%}WK?7-rz|~+n37H*$m(6>g=6JqSLBVCU*@$pUOR-5rsw4$vyY{ng<@9i*Y09(OoE`=$$o}FMM`qR8%~8 z_g&n3`z?$xbsDwt?6u{ZeDl)tX@@%2wGa}l);#>+o&4LB4QRL$PeG=~AZ?SYF1dv3 zFSmFv2(iY`ul^a=U;AH_y>04U2H{f%s6~xu|L_NP+WF693+50@L1wyWsEot*nF=z) zHRsbII(g}dKe7L=cadcs*k%unT;NrmaT6ypecBt$e(6;*xjZ32`RZ%zw&hN=V-qjB z7}47cDyKQ^3BIwzhIGj`UI`klg2*^@2wFvdkEc7n2kEJhA$S_MLo9DD2w zL*IZ+637=35d}Vg$uqP{8U!L}M1T-;f~@Q-`veO`Rpf|1nNImG$3YNKsA`{u?6a&a zi6KG&>r0HvkR%^*;r>Y6a@%0E(DG^>UN~gQ7T!#hCz1D0`OiE!x&l)oVQ4sbSZ=crNNd3T7fHD z@4S=5Anf1+jZ!VlIMWq~2r>0M_|ttQU;WBbTz#3xxse1}z0Q+dG(>XBkkz`;4_*%b zc#uQaG!a>(7U;suYqDVe2=3;=?7ukZ!2SOQHyM1;2CboS5GouO+6OV{b%SJ_BLdlf zfd~+O$T96(OT#c_W;ljpIELeW#|LI&bkrKSwncjAv@LQcp)}yq2!|%4E)=0vqGLph z+4PpZlx;~aYP9ChBX>1ud58tx_o9WO9XrWQPA&x|1VkBVIZxXs0VU-M^56+&fzCUT zHAVNlH#qNm$8pT}FXZA&E@$uE_rNbG=*%>6K@ZA%gcW3T;7y$df-W*@qJ~s8WT??y z%pwR6p@#GgL^&T@2Al_J1R<1+l8Q1YnuR78BhaNn1_8PTD!DTVqiARYJ`iIP_7qX0 zgha_y7-S}*V?gxG#aNG#8Qw=^6Uuo_d}{$xIHc|XpHVbjdQP(>^fVEwPDjlU=*1~$ z`%!?vs0y)Ju;J8byXilB<9g5hW!`N^HA8BiQ2$+i~Odx8P!(1;qlakmSyx zMF%1_$Q{h@R^-NDe1Z0kvb1Pj!{l{D=FnMyqJgj((e@BS8-=D88yJ%z%91*U;3VsB zzA=7A$;|2V@k*d%04eYY@)*cM>M#jarwkq~a+Iq<(8Qq7Mq#7`>*=WFP$r6|Fg3h~ z22eH&ylfD3P85!!XCA6_h{BSo4s6H>nWKYFGONh54%$JHMc8ZSZMox)KViiPVzekU z^|~Pxg3M)T?{QfNh#I*o>GAug>J+^L~sl8L`x83^FPRGR@rfB}yWu?@ALv2!W3R zk3>j;@R5j0ogwd1CQrnB_o0rG#Q4rZ`h_182qZa@`oLRPg}jz&Hr}WVL6*9oaZsXH zy&eHmc1jL|aiK(T;Q#4)qT@}nTGh3Tfp;(o2K@nn>wiGR_fDu&->mP6Vn{_3gGw$j z9;A6AN%v*&t%KJm6448FV*8{fBDUI=GKmY~(J@&ru0Oeh6+WH{zYI}XHmiPqgzBFR zN@=rd2PIXX^Z?$=eg<_%{#5@tjUIfo0|zI> zVH*2CWh_DuV-b@97nM4!g%R9uOJ4>*g-o23$6yLHf2 zU1)&s9KAlKk=ROEULzEvXqZZeCxde?`ZT}06{MMf5J^n5SvJzRoK=An zD&f!^rRx;Mk~ptX8iE~#Z}&N>WC&nE_gt2nIFj>DpN0q%5*Q=XJsAM4lfHqL0XeW- z4J~Bj2R+(iNFCg_LU{pBc$x)R*Flt^=qAWsf`r0lgOEg9je&(l!wq66b|4mmB@CR{ z^tfDT`#!A8YhZD$SR-N)*P8f$d=Csdrz(l54BFP%zXrx3pT9#%HE}QzPEf7Au>z_E zf{?!B;&*u%3?7c*7>?oikU@;H4N_&4u1tLcUm``IR;vLTq@dkulPg0mLB-@1Pt1|z zDQOOpJZ8vB^w5R0z=!CQ*kIH|ABoBnqe_b0L}Uo$0emd6BA}w@nLpjn=fAukha7ek zS6y)h8G=w2bSRB58YdLq3y6UzBB3RTUg3m86S2Y*iFlVf{YnWkG}Z=Go}qO>Mh~V& zyB)}5je4jfq=Fcd7@@SZiQoeGm@MC_LQ_OZ^brvQ1c$_sNe!+pka-9*+ePl7N~A?E(gkQgyVhfr^9>(K2=QM98p(Pe6^L8i$bJAw~g0 zA_5o`E^3@ocok4}g@^*>L4}xRn`IybQ+44$NrUu?^G`XJ>#o0vw#-o`Ld-}*7X}d| zGFr;$h(VJpgO&mjYe+JJPVqq|%zlctc zP5In1j2UZ&X6cK_4EvJyzdvnEz(D`|S3Y@CRt1BAb=DhO0dr}jr0@BC1wyQZO#zIl zQ&`CACE9=zNe4hHkfxxhm@_h_E^-tx-Kvm4)&4>>fn7v7G^>sa6)dq2SQ0}Koe0tE z0pO@2d3YbI#%bh)<2OQT2;QPKC?T;0iDWbE75L8?f6tO2Acm1_y~W8K`Tf6sMYIfZIh0uOS}M+=a>v0diWu}_x;V0Y6%dE(n1KG?7im$NC~A)qf%N6 z)?5o7xOXnWjb`e;u;RoE5@ zLZGyyC<>l_`YB#{W!j*v??Z-$Aj8!`6#ZcLz|R9eB@m;}t!sFYWH^RnIELdN17egA zF*pHE286N*QBe9eEf=s#@c*%Q-f@-{W%~cVRdvp}x4S2kB}ooqKm|k=5k-OoF`)ba zK^6&uh^VVzW?>Z-6pXv8E^FFF5D^tbg1QPuL_idfATWf1fk_?iJ*TSP-yd~u=$;;! z0Q^7$zX1fn`}(;!-F@x}bt=5?Q}6pcd81Qv9}-XzhicxW3M5cO2^n6j2U;Syry-?XmRaXviZkEQRo2iKpr7u0b(a0z(@WJ=`nXNE+nkljZ#>YliUXOBMPY+;2rwc{F$ZULj z4NML`B)R0O1-YG##+3cw9E2b!9%U&-qXx&~53%rYtm zJG}I{EM8DMFj8VI#As+$7J`qMyzSF z#6AGcA&?@KIOM`aUQqcqKD6*nM`gy52}@xdYD%I7F&V+Pv6AXeMKe%`kql?sR4isJ zgO(bMDbNxeg4Y(&v}?!Abkhif2{pQJQB2CFP{qUq9F#6nx>RQ3LP5k*=3@v-+d3kS zJa`&$89_2i+t_LV9|>eEF@j`*(+ZJ5lz>yugv~&0%Bm5i zaF)fCv|S{|igMW~)-}mQ7xv?q;!tOqL?_&knu#Z_?R+|923deCu7(z zX1HBa#0)J1nYK~!1gWI}W57C#_r5ES`gbv*7=v?;dGqEnZ{9qdb5H&KO`2pnwau~b zZ)FeHh|R|q*i8);ahm6y zQiJoDU+Bb?+?HH4z*_L2IJDyeA^y3V6#AyL0EA_YxxbI7U_^Y2vB`XVZYNwjxgnc(hur zBxPv~nX{=xB@6gSE%gj#yG@=aj-bqCELpmQVO#I8*8pdTQ2H^P6GDK@^{GH<2L%mJ zQA-E5-VR>8{pP5LW&wX>mP1uz3ryYpl;~1l3OZeH(uVVlIw>P=VF+Rg(4ZQWd ze6QE?&J&NqnRa5&$5L8|ogH)WSV~5-2EllojA3Y#S51P{*$XC6TES?_P5>o}5HW~Z ziitTAi=@aAmyz3*=&u@A={e-54sus2d@R z5sOC&w5!O-&}#hKS*P*)TW;aMzxE9_TzAd1;VT9&3MQtq727~#17n-)v)?{^`s`D| zE=5d0B%`Vvip8o&v{z11^PFJ?j4PP2=2~3y*FSRIZ8vkpr5E$!t+&Eefzo6Yv5nI< zl?{w#6~0=8H*+Y10||IvVo)k0M3XX6f*MS;#89H**mbwvdEoGGLvs=l*Zv8(;ncO)9WC6RzOs55JeI=ikI9|MLuH<{mATe5Do>~hbl4TjrIYJ1Jo{8~TKnYpN*{$zb zU&ZAD^5jVSKCjh1w)JdSg}cz zR%$ZPuEIiRT&`j}t>~cyFw5#yqL)?TT)J*RF$qUz_FT(oD`h98QYXWpn){h;LL#fR zR>_CPX#=8p0*5^e&zVo9i^zwe)rJ|(H3%L;tV`m$ov-S)UFjqex=stRV#T#d|vkpfi+F(P7U6wqq7Xf~US1*m*Q zyA4qjndk7Zqw*yhlO$^&O4kJppPF8+L<_d@5M@Yv385|zoeI=yl&8stzK8xSw$AS~ z?|~doL1T+dGo2gL-7pXl%65r0?r&6VGq8aTY+wWXn}Qfo+qBAM{P~s}SlSL4*TjgU zJyG%IHy_Emn{LRje)oT93>X~(8tnOoJ^A=2Pv_KsJDu%z+>WM>Q>^!FjyUQNnv{%; z%;B)N?SZQdZ}@+QvfDm;AmupE-(@!rKjif|0zMY})64hb_!AGr8V?}{EpVBlp(Jw# zA3PISc7FXKeBi{laKym}vd=EBsK#3~H8L?V zMtcY}6G~q)clI2t7+Pi8{i#fYcf9u`itrG7@3ALuee1E@dfVNUK2n;Jm+rC~&suj) ztO*3~!6?P>I-K~P;~AOh5Su0GHnCj=f-EYI$dSxa8*yE37?r){H1Wx1_L=7vW-ya%69UaNyAgV?(4JlQ&~D;0%;yo1x(uobs=yve!O) zF*Kt+pe7PGPfWuZJ<3SlWq5Oyv_+gcyb4p<#Y;`48A>`<*!b3t!?L z?>LTOQ?YDF*z@)KbNZQ|;m||hOw)LB6xW!^aVMV0M?Ur;w%h4>%+(3T+B10JF1zuu zGfw66pFW-a_ud`#u;;7y;o!p#<~6U~mrtH~CdVA}7OV(phS-QYLo|`fE4gd%uOB&? zSMPlQLzy6Pg7FY|^+5;nlIOgb{a>{^cQ5!0Z$9A=j5hFYgp0m?F1tN%EB@()FXX!4 z{Eo_3oO|B4IpWA8xcC11Q+dk@I3At~i&1fPhKTn8Yb}o(!|3PC>~uX{h40qKy4+0j z-IOhX)M>;LS23$MBr;I(z1g%A>}L&P^oW=maF{M*Vwy=gUr4GDa(FGtEL?i|hv_D? zgtP;$q`;0C>&NO(ZC=$tW(wlbZL@Krrd`O+DPs-BSOkrUljVaSIfRj!btVR7y1~IF znnpF7!6n)-VLiXp%z z5L!b-eInVkb=Fi>*5_!X9IBbKW>Z#h?H_)}7rwX`MFSpwcwxQapUN?uvqX(l6*%X* z^omgb0$oT zgQZ!E55?}dmwFz1%&63=0>zKZY7y@~o*HXag-+u?} zqXQe*zy>w|F&@+MW0k1ww7V`1Ds@CvEW(d|a4Fxr_y@djhZkV1ftVw9n1kQ?2Cn)2 zZ@KPX zF(QJPjGcFV6|Z>3&SW;=jO!xzhLRX7#8^VsWV?M|$L0U?U;OpPKXLCv%lPFDH(+V; z@ci3YFxF=G{SRQsDYo!9u?!VWj7VZ4r;T@tIz!oRBVNHZ5K-)`ne4vLtNG!7p3Oaf zxq}=3a5MKku#AxB1nb#!tF2jU&DGJ+qR0gG8ETvCzSnLjB{2phX2f6#Mo|g4+7Frk z;1~xV_6BefH?$u6?ztn?nCE~uy@o@NJdm&d&v~r9-WHs2;$c`Zyy*G=#5UV(#Mi&| z4Zd^X`P_g1{fx|<$H}Lj#!r8G1?QdnO>V#AFANP0bIA{X#CN`X0o%Q3TQUt~IP#&j zIOU`NLf%v^yXtDb__hCKmlthLw#Mr0{l@+I?)T0kgviMscqf%_V{JAivV;=Lde7d7 z7i_s6AN$xD1OW;|6eW)i(SWMQn#6Om;K4E0TyG9*Z@3;qGiUMV8?Pf1P30B{Vhz4* zV~jF7wuJYd^e_C^IpE;_L+a-G(bTB6zeI!5n-i7SA(~cZ`+S6ulMeth|5{Oqb<@Y&CPmJ7dk9#vIg z#i1r91-#5)oFjPUUq5&fU-{3kqpGNQOm5j`hwZuYyWi()XPnI)cmI_azkUy93=J{6 z*<|jVd2GJb)``T;2C_!ZUGwKNXWl%9MuxGOC93u|-=E?gxQ+uVBA8mL^KmDN^p^{- zO7JjUJ-C_bvDbVd0^%SthK98ByK)Y+CN&XCpxx_ywZwptf<(Hv0(pn&=GNjqL?EL; zLki$rF&R5F^^8Y^PzuJ9NOD)Cyt1loRp074Uq2bs?38SmNpM61woc{m60h2hvO=gi ze+HQt&vX+M*MN1_ox{YEXJIvs{ZNm2lL2ltRUl^fvd?S4TJRxKRTVz4oD8Ut_%n*3 z7Ex2lQY=BMIx$F6#)N7l1ZcNmVxq!27#&;6&@imMKIBD-DtJF$->VQqz!(TVkzd*x zBv4J@tF(B;ZRVvOCsk~VX+OUF5w=LR;gfeq|w z24aYbIAgJ8a=Ffs0lu_ELTbLU^~8LqkLznK5fQrhZpRDA68Q#kT1@1|^*RDLw= zp2i|^Ow{b$-U|8LVeb3W-Lywrti8#GeEHn3Gb4D4YLt(C`pfLR$9}lD6cgH52kq#I z#!{6PnGIBBRWDQ#YYfo{W$@Tpv#3;QELu!cghDK8En9E*V!nUL4>?Nn8q11jR9W_d;gP9cHzyH{m7w zypBaTUWaEUd+xIvo31y)r$2f++rDI1_ITaC*gR(_%ed>-KXCPLujR_Ce#PjrajJwXkAKKn|M_|Ledjxwx6$_Ow8L6_`K*s~@sEDTS1$M|FMs70v_>D`-ud%6 z`k3QcYwZpB^G&~}4CAy~ZSK7LJ|0@Om^*L3nS~E7!XOwtQ6p+BRjlxu6eXM_Da!Gn zcu}$(vgQa6J#Z(dz5gTp`SyG9tIs8~CHLHN9jAT%i)0&b$u}?l0qbuvmx*$MD8jte zS3|5JL|xALl>s-{N;vBMOrNQQL~^kCc)~jD%KD~4FPsP@@zW`RKwVCUnw2`+*`I$e z-b3l3>2jh*M5goXBe8AL-FW@rT3Y3B+W9gFn7%LxO)&}cPnj~>*vHa?6dt)x5`i0MP_yLFThJ#+ktM@yM(Xvg;KZpqm!6SBv zR)q2KQQmp@QGEW(=dj1?Ue8&dI32g-VW35*l&Vx(&aqHRN>@>7+MyCtkq?1bN5d3Y zi8LCHvRzSDOBop%rWZ5!JdD+5&7|$y9Pxqoa`s6dVR+LG*#8Z0WTeW_%rIxOjTjjj z##SY`z^jjWBbR>RfBD?I-_QF$`hMPi*2k%uLe^|BclKk_g!{S0JVUlh;`w ziSWYv7RH&R>_Nm?!H1F%JiYpbND}%}?z`hUzJAU{y!+%cSZlM5Va5b1V-&-y6PQE8 zHZbL4#x-K3q;L+S1x0fnOC=I@AxHo7!F=KqpWzjIzlJkE{%;gbq4J4jwiKnXk>E3| zbwr;?S-r2-U7Ql6Bt#)dMJsPoIfI089)4gUFL?Ia{Ngt^kT>SBRdc-a_X$r2v8 z>2@wT|Hs_(zyv#NyAduE%Bte$SN(zq9(*tbmu9dNw8}wx|CXgXh_Lx{-~}&U6XR+p zdZo@~Njr)K-Sp`Gz-&3s{n7k;W%I4l4rN}`bC?yBp_qIz#zG89O`%EN2hN1Y9|d$W zS4<}utmS+A!I(*pR9|yNgr@_01_BvAWdX%CLBPjyl8mRu;RF-r9$Rm`JFx_BaakvT zjkN;KIL-uY4s+H7&v=r|?$6eUIYiiV_jTE0_iu9VopTTw#yMEBWGNaTmw>2JwLn9* zQi>eLdOT2;C8)s&STjO3k(5!gLI?_C{FBnIfmB9v(7^|>*=AorM92g@6==pq=feGW ztXUtO1C6>Zp9W)2r~XiH7FAG9%qF%W_@~bZ7}&rDHn4#`%|Hw%1w^GRm$7)6=e4hY zEw9@1rQ{8dlbreY+{rrYZo&SqKY+dW-324ckU;QlVwT~vCY7m>haaNRoX6pBeW8p03}!r$ zZfPRQ%zVYSKYJ!S9r8}zbi|RoZ2K+I(S=O3Rzt-y)Lz0*F1?s_p0^bf4a4GVuIHt% zdO0tB^Xob4xMNsvS;^?w7?NkSTH_RjCDQ=WU`E?~{_|hr!~c68uR35~UVq45?6~cg zlnWN~z}-t(wB$kV{Odj3b=N(VWt-7uW6XcxW?JpQ{P_#H_x^jS#ZD4;Lo~RU$V=PV zV!m|V<-B(9H?r2)ja+v9?bx`8?|=Vl-hBM2?DX0ldC9gfWUYoHj~P{LQc6IKrhM; zzIEZb%$`?JRTUa?Hh9)1JbQzUm@%`#W?QaD2yKiQHr;eHF1_pr9CYCSqbf@@#-xN% zE5D>06atQH*6OUc#rnMCsKZ&y4AWTTxcoa;aNK7ui2O9J@>f`4-Mh0r4Lye*s9EU zovZ?}grF%=qLWy#vizPM8VYr!j1IGcC_*3c$BxXhBDnh6Wkp@85<6PL_06S!TzmZ) zm;#JjOlT*XypD>3c1t=|A+ngJA9Z$vjkSQqM^c}g)}5D0!YBj({+W*m8XQ#%3gd}E z$!$hNQC#XbVFZcrFYkUGc}>V!DOj7htF*J0M1Z7@C6fAnXb&M}gJxFav~@~`Xe#$o z^1Hn32v$qVYMe$>D6*lHaTMlq>W6<# zZlT>8B?{z2t?BhiWCg!lk^E*yQ&wft_j`Cv>?#y@WUIZ03_+^deDvhMAQ9Ae<{qW1 z>hWmz)=k+9_2&sD(40zmJmu4Q`H>@cd*uVxhuz@`~7V%iW7$d|Oa5nO}_npo@FMB!7#!RZJ zfN1d#+|GO7{vNj6oR%hg-boxD$BEj@z;N zyg7XCtgm77CRtG4d(?60XvJsFIE#I@e=d#YOj@lmety{x*=~zH$np`&312G!B-uo% zmAI`27T$U@2kd_!Ub2hx%~W#SbEQ0Y_r9dn2B-jx$im}jg06c zKlu7LFiR)u>;a4S0l)BWj(N**lr2krDsMQA9#>*;WF-cXcU_b;lyB!5RD`y7!|Cvd3eE{{OTt+u*WN2 z#?pK5<%q+ML?!3y3(w=Y1;1m!c!LcYDH;$JF@2Ju(?`fpdT_H+Ua_pCPbg2|W9gLZsDKEa zB%hA#U*h$ca!q+OY{K>cKA8?I(G@XjjuX{jx}Z;=)T>TK zlcXl2wY4%yU0R|Ya6@_G#lco)!5bjO0~^@Do_V&aZvJBiF%r3(f<~6!JfCmea!w6q z41&9hNhRZL!3PEWq{OG^`yOwAZ?zrh@?zs6Tk}^dkh~g%WXEjYl6NwT63%;!7j&Ig7N z^$5yuFZxmXj%#9w_+^Xv*+rM9vY%QK(ujZt?zsL&?zsL&gho9^Oi%eLrD9M)&`xs(8qx zOUWZ}amsW)r>uxk-_WgD@4tQxG&my^b-KNVbebOC;HJG`Cm}+#fejrzB2JD~ChauJ z@(-}0pLH70M^d?-MeA!RDl+L3iYyP{3^psD**hVo;!Ou;Ni_2qKM8`wV#h#|do@NWuYOx^jFzB*~0y8P8z@J1&u6zNCpOFt#{M`LX;eiDUce3whwCZKbDk+ohMY<`6 zolr-#@BZkNF-*>-)Jdt3y}ya}y|ivS%9JCu4jFoHc)1OrDecrRv)DPGc3VUzwMS=1 zq;p)9>6Xpug5$|~`3WeQ+$U6YnguxhK3&sH)6SEx)CtUu2(HnM4wl zjbW|B`!+E~T;Y=BX#E~~e2CKbey4NOWKw%{`k?mpbHh9ECWrx57%+r`FJ0Z*1o%RSXtD?^!yJ)saD^1t|OM{x1G_%a@=@&Fz$Uwfd_XoH4xZZD(`A`N#9=&rMKykA!r( zop)Uwn?v+NQ-)@&0gHSx07;`aMkfImmTvZ#PUG9Hs#dEkKi9+>VTm$~Qc+|hm@fT# z@4X0-vVt-wq2@>-=9KjvMv^)Vv8-ipI+I&D=}esry6M$p1ZQ#-htQtf7AwSadM61X zuwcOhoPPT0Y_Y`_?6=>3s|L~!Y+wT$*uWln8CgZA+dbVLP@_^-Wg-!)JVwm4LDImU zVd$JHWxK>VM+m9vddbqIR8@s@j?87X>|S_E9E%tu8dGZN{Q2{_`|i7mF_9cvYw^L; zXf(QtH}VV(?*w7B)mG+&m*lFL#w9g-T;%l>?Y!GCHz=kXz6qwFKXEd6& zm(jec&7ghch3ZxgJehuwtsEkz`SNnN-JroRP#*k8^Eyf}Lyz#%1rba19+O#GEG3wG z*?g-8y-2^;Z(8j@R#-A?+4@B`4V^M&J{YlGwt`*vS_=`6Mu*E`WOgJ1xsB}gnyne0 z1tFGw{jv`@Hi{S_&ofL8zy8D7WEuSW$8-46Z*~9+p|Z?de=*IG+1Sibdawq{2w9d9 zLPb_&z55rfL4-~wLVsJ7D5Eu?ux1x0aIUdJ+_v%_YoRC#LI^2S#K62YYrdRd`{uS# zUpsU&rg|rH)4Y~G`K9Hgt7Gq(YHpL2n`?dgo@U*-+|((j5&@ebK$vcV6CuwNf$*lA zZOQ`=z}z*aVyYEM$5(o5h34HPiV$kRLuwh+X|m{Ilv-N1Kule-XZp%XR z&|0^>*+@HWK6s?AS=FZ?YBgf8&Jsc&kWvE}VgzR`Wm(odD9_-|DhbdL5sWc}5TD7j z(JFRgSq`-cA(fb@S=udImsJG-)J>^Jf2{9rWx)>Hr$EwICiC|I7!B-6ot0C(-l{kH z%?eBP6}ocJ89B%z_THxxG}7xs5%O$OZUeDQSs3>&cz_skvLeOlr+#Ahlp&EBG6$P) zIfD&12Z!Y|XCH=-kY~fwjzb?3$5mOjsUo;0lnQ|+MFX-NtPMmnO3OnaP&8p^*yG2b z-7>XWR%g=doIEEn+tfrH(*OGz2nD0#qXeK;LN&3X3WC@fLu;X?|;z$5iDnW4|zDFKXXI)fPDzB?r>DUnCa`zph!J5=Q zehgv-KD6m&j#PahhAwx1&N-}g7-PsXN4sp*&oY?w1~#yP4Qv2ntjtoWipv}>bBLH4 z4UdC$z zL04r)AL{~1oi>f}34R6>2m!=)fEk0y>1B=im2NMIs;%7Af6EvRUEK&VQ=<1O;Oaa<6=3`)FeI$h5!A=pI1?-d2%L}Sg6{t@ZKgLe&2PJ zZE9Yv0_*EOlUCGDiP5sf@Z%r-z9xNTQih4C zNrobk=Wcae;zG-8hb4@+=>elMHNN0~^=?#8{PRF)i>~ z2TOts7LtDmce(SJy&LwQdC z4md207l~kv0m<+cl-|-9N-D&rT9*zcB0>VFErcMPc>I_82zhlmFfgOR==cOKPtT*F zmpjp^HtU~fL?HN-*(Y74p#r1OuEv&E59tx%P*vbE_`pd&nKE~ECcH?>KJfK#!=&5Q z2b(JhB1Sd^C0wo76VZ&y+X$++(`8cm%j4ls2zgyf&V$Mfemb60}x|i z0~^@Do@|RN7i&Kv81tAen83IgG&%hy)H|rF82C%oy@$_!=>w>NcBpXHrV{n|#5R|> zrPv|`NvYlHXp%UM?BoE%c*X-6(^N4`9v%sl00^N(JT$~$e7^%?`^qmIFhYl*QfwlE zba5RZ;2enyLjY4jEGF@>^o#hcvwoBHHwGU{+N~DGKv}kyBh1x0kjH{CZH$7%CROFQ z>3X>C+S_SYFf;^&LKEg2U|5BWUy`&0}IV(AYr7mmh+AK*qqH@xw7`QLo^yy};O4 zZLBT8fGvPoD;-I zDmxJEi-=8qXuWqI(|_uJR(DVnG2>HI4c!O8w*U7 z&?w-tOK-r(1Yp?uRvL|r=vOd=bej2ZIyvLIw97hh4#t+Qh@8{W<(Zgn?y8A1S847L z5*S;bQv@a^V9V#gx*NmKu59D0l{@9v{`+pW+jej6xh<8&XjceHfQp2I#mN&_oN~`P zb%L(J3DZtEh(?S}G#k#QY#3`+b~PBd&jTCSzy|iT1u?V(hWCNu(v|V-6-akd`=pkn zR3J(^I!f$HZcaUS=OZd~l0JI^r}fYEwWGvZ-ZY|;ey`m`#Hsq|-Mi>kLw2;;lN@Tg zf0f?Po#WH*dLpao9<}coYFSl!ZDUl3qLVfb6zR|WB#QKj(+zen2R5)L4G@`D4&-q% ztLP&E7g}-&IGov=B2+JG6Rp%@aJOyyKmF;|ge3qi9UKy&;Ba z9(*8C@{Bj_fCjBc8~`G=PyRQOWwxs-F)4MTBOTpQ?lBQm@d1u`cY!aL5Gs0HjGCqX zKZ#J(C2C3qgJ--AFMjbxRP7d_sz;AdiRAd+_Srzm|02FMt%AaB@ojE)d|0%&}Ft|~z6Rz~(Dw{7f{DfW48-C#ZR zp4|qb)^f2Pd<8>|xxH$g<;v7-?Zl*i_gB*C3W3$Za zgp1QTp6MP;{Yf;TEzUV-bAEQEVl9Ym1LL3tFkEN4n8`jDpyc$Ci=bVq)( z`apt7YTtd}M`zb$O3t?c5{aUCBd7>g!PKi~4W&%$btM3yuBFso?WS}=nAqn~b!eXi zMEgLL{=4kaLZPWnvjf1Ok``r&s0c<4HVRP$#netkx=O-zwCf{ufDxSn>a6gy+@d=O zAf8zP!!++!rUSmrBY2=sR%El1*I$*}lqn9gtl$`7)vv8_GQp_ov@YzlBTF6yz*sI& zGhL^<|L@wVuU=Wup)+?xj}cHR1fd|uNSdWI^r@XZTHDA9?tAKYO$xk_y{)B!vgYr9IrZ#s0_PPNE#T}1HphDWFo1EFxR_G(~t zani$yu?7-kG8kEp3tPJ*#x7&V5EL1_<;WM36{LCrb%00IVImk@`i=+DEOBgzkU38k zGMpJoLALFiUZu_sAno3(2Pe%g$z)OrG7$5GOsJ)qrC{6#h~8ifG)x!kz*q3?bMH*v zP94nWdBm#XV+R8DDd5&TWu>45EqkxTWH_f-mv}Ihythom<$(<8imR9|#@fLoeWdp7 zdh>3_Lz*<#byP~F>iKh0XV7GcGbCEb>|A2iKMSULw~7&Cz@q7W2m~8hRzZk;a;e%x zJv9ggq3ro?5zjZi301WkG4ybjNzpA=y~3}rESMNgaFFS6wIF?MlpwB2oKHN4 z(!YTXY+wT$*i#Q;Bme+J({6^TBW*ro#3j;?sdxO7&bcBY zvF8@oWMEMsNV=}kSl^R$L5IoScOM{82MirJBBq>Jj0#4z1{f5h+6OdrGA{bJLwcYI zB#@%(PpNb>QnbDutAeRPj-DsfyGQLhfc;*^D!r8fi1CcHDSbt!M)Xf8Hu<HteFOr&TpoiMGY#IIMxu!fM&i7nyF(u1U@ll4?pPA{XhY3n?Yg>H!DFAu;7$wOWzjVPs?k zTUUCxy1J!o1G)jN7UX#&1(mYWhgxh0{)w&M>gS5QNd=t2~G)o_Dlc4_rLc$G=T3YJ*lPLPQXlW)pXr^ zEK9iKmXbve)>!t`Y!wA#5}HGq*8wb)7T zR!ux$D!7;mVoFO&snj0$(ambtUUrAnK#laJf#Lk|&N+;a zk59i_wKA$Ck9NF1!GOkN1wbCxv3B{}kd-@U2$1EDwbq)&yw&QEp{u7VOv>qASrwR7 zX?vB)Ak&}{?jIY>Sm|TU3i_k;5P{A>5YP&&O`8MndH3Oo47W~!NSgzu8(oK>ppiTi z@&=s$-H#ykuDq#gx`u8EmqX0bIs>2{jX8)dsVZnsOaO4=Nn2s;7}j)Tg`wrUUO=j* z>G1TLG1Nk&MPM1e9Pb_@nvye|Zo*!>U&|#IABk9Kcif2p#=&@iMguAzh=HV}5o1!& zhzYduF)5FfZ3ybHWL;Vv;|!3evWnOz-D-ULNbCXN2+6bM-jqI71fQC_+$9DmGN z#B#k!88)4+QiaroqKtKcE>DS%1u`@rOfwO)+T^N?9P2<+^Q%frl18%vZj84wqkkIU!Ul(ykrYzy>z3f&D#$7-N}bJQGS6 z@Wx;@aZ*Gv*r+r^n;{>_1K2v1(nX7n2|NfQv>mvR<6B_Z*dLc6PPElR+=FH`v|M{Oe{P4pW9v-e!ai>H)5~nX;7(|0BJ#%+TbxjO0H1(l5PXs(-uHFS>T=@tj*MOG!(|S{ z&~B9}OUO>?$6XT94suYW0F@RYz;-*q`Wxot8O)m9K;?c8Is%#_3sc!eOu=#CAP}iS zsOuuMPLS)!l{PV~iE9jXb~6wM-~S)D=9)wJ^Uc4Gj&xh6_LX%Prm;5+C3 zDy39~dSWqxYrt==eh8xLVl+@qD)Kb~7k>Mm1+F< zopbKJRnQ_b@woXAfTf4Jx@Q=Q&nB}+<3n4J@5Cvwvv9W|LRw5z#|XA zZ8yP_Po^Omo%B1BBg`;l;9>+1+K*e$B3*)5P%!r~FtpNQnM&9NZe0hdEWiDJf#8NBD;yQyq*jb8-i$(J_L#=f|;O}m6#IA zyF7{~iXvceqVSTyjXZdAr!m1`b)aK1to8^V69gS=$iBauQj0z%5FvJ3tT-oO92<68>u!wf+@5r(&I?_x9g_cM}x_ zCxE$&6X6TbZ`%W4V-PP6ekYRzGbd?X2vF2|4I$}CsX!m(kD>QLPth1owd%O|!h~Le zEV)yJp=8Gy)8YXnz(m*9q@|sQX+6kNE5mDEw^UjvvB_Peau9;T=txxKKE{!xQxV9x z=-gVTLIq+JVuWrtX)stFp|KS-^5qhVqpaOmyao#rr_(K6 z{kSkhvO2d3sr^Jjx3UC3JZ`ny(=dxbEtZN@bQ9LVg%|F{&Tl`5NB#)^cUS$0k_nn3 znZ{Z$xe)5{N}bBYUTziPhvzP!sszaK)>D=ig`g!zt0uvXvpp(nF*>hxK79S`^ZjeC zC`@{m#tiS4{5%t-5S4PtEX$}Ww*=j_K@4ILgBZj=G>DNoM-~-GTFIzLlew0F35H5E zT>@4I93I4@eU5h)WgJo$YUf%i&6$rnn=WpttG1sJ}M#x5h18f>ah)V0umF9 zU}FcRdb${%DC3Ifpa>y@P$JP2L{lkErd*O8k7;6dS_tC73Q;Mdi5Lku?TKDcF+)Tr zYGP2R7*OKz(bwPah|PjEUcNfURVZb!N)p2Yi;ERVtq~%T80#KO0!kaYArdv@I;3?E zrSQEq5Y>Y}>Xs9omZg03IB$s(2+^Um5TYlo{B8zD!k;$zH ze+i&?wiS>(%fEXuZ-d22(!9vGoys7HMH{o2@dWxyyA#Kwp67lI9wme5)vV9;dRX-H zi{NgPKO2eBPq?-%Dhr4RN@|UW7;6Sn51DiIvxvIjPSd+G={2ntNJX&I(OKDdIZ*;i zr|&Wff*<44|FJn@AhIA~C^=*ec<4U()zyzBrsf1&;aH1;h5&OP%c(4QRFd40G={1I zlSyCGW8S=(*bdk#u^Vo`Dd{ueIk|^O=!* zxk$fp1gmLo^#VTc6?k+&NJ-I&2by~Wxf8hNf?bS4Am>qCQybsd$X{I{(L@PK`#W~- zH3Rc?llO?UH~;A{OZKZw0=hoZm|B?px;Epe(&A(4-W~ru-MR7>#yof&#u$Yc_PLiA zxq&pfmRX~T>x4GsE0oLUKZC^q!w`rj4nM&D+JK<`UA!!0qe?S{)7tghaKnxY-MigE zHm#nG^Xehc`0pXa9N7yQS!Anv8ILGsOeG&47a#7K%4#MaPa|i7b1HR%-aauNx0hCm zO82wEIqfuV#ux{iBz=$Mr*XpP)6o%QK`1a6N*HK}QhRBTwNs1TV5k~%+$qj0>YKO1 z>ZoO{4dF6gXC8s8iLmBC7cX;EHl9Cq{t0_CbedEpJI7SjiB&hw5)!9hRV7NPl^urx z7I%*vArBp{thFj1Ps&q1hUWdI7SF%06%Mbaaat8P0mq|iMjs+cnMCVEMXt6|5&~SB znOyaP&(L$HUJPer2>rd4XKof}vSp<~X#`5Hwu4OmIBH98MVe|JLB~q*P>24{DH@*owXw!{&Me z{`B?*5HW{G5e%k(OpnII7HcojAkT_)saY@u+ z^9I>DeEJ8amfjP+q7~7TNNGZi!(LG=lN!R9=L(v~;rspoS^q222Cc4eegO^s#>tnP zVtbhzkne*f*oijtQDA5k3o?G&lBFXAmnpHgT$d+vW^=Bj0vPr9e!d#wQ7U8=j~zcF z1zQZzHzyy(|Hiob+HM<&r|;(>qN+mhl%Z2hGKWrmQl9`2(N{wil7;->px86b$;${L zwrX0aR1v;W=i%I*NXEV?v8{K^C$&hV1nB5xJ@t>|E5J})qF9NTj2?xRSwW-4K@`bXCJKgK)#+<;y+M@%dLn?TI>`d?XG0AoJk4%#PH zcweFR1@dEN&w#W^NC8i%5jP>6Hs@F?`q02<1XDQD9$86sH7y+RnZ#DQKGp0<8Z-`_ zQ2T5m(2uVq$hQ3%T%}}WlR{KH16sHw_a?MBh@Ff~z9^YWtpz7^9#Hg7DK@tlS$XRO zZdt}|9KrXqVYz}8vdEJcRKZC}#GMGv>mF3Xud#^CTJUq)o{tcRzYkO9+-EKY`<=Uj zA*u?zd3FAW0aQBSSojlGT~AiaecUlKIEkEpRg_1fS)jaV%bQ4{;9ZNfta#P9Ca$Oq z%*9HGG@wJR5f)$wAd1bk*Wh4J&v`x}R5$9gSP)-j86Zm1GaGmxfX8QZv}b|7!%THj!u-&cPr zrCb#I@WWbb@A|BHcVfHpD4MW9^|Uxz<|Zw%TF%-=ftC_W%zk*0`&_hB4>PHKwp5Sz zOpAmiYQeQzmzi|TWb1jj+2PK-e}THuTH&}Tsn4PV^F1{~NCo&h8sQzT8i&peuY%onpAVp){HMYn{Fq;1 z!%2)-?tg5vK_1G~+|m(IXy@(wruJf+qGJFwK6VFFCT7BY`;&d_yK_AmahY8QDo-v? z;2_X=KJR?T6f3vhkzq*gj*AoJz(Do{fQosI@L0=H3}E)Uk_&(~DB}ecogmHYHdSH+ ztxFEyJR_oX`>LbU(uE33NbvI^%yZ2NsF?+U>0gai_SEr8?mugoM&_~Q3+3|Jsp;Y1 zMLV~p3uE3UF+cX0xg!5lff<#NM9)gsJU`>=6F%b+yRMC%wg7&1jgL=qS?oh)@7L3A z9){y}0WP$LUmKcP^Ar6|T7HL`cY>G!8MY4HQM35(&>HIT){}Iz>w>SyoF7jZwI9cf z>z*ZU#8v|DuPHBe4!&Xf&)ew4%y1?eH|vZ6`MB5b;H&!2J_;XukXahsj_LS_Tlob$ zzM#Dw_pQFivW6yOZh=y01)*g>Oc0vJ?jw~*Hk81_l9hQFREA*)H>`pd)J{#HNb~Ol z95EvNkD%<|FGKKL2KFnue#4M6%Pz^CldhmsEgpmrZYSV=U%StI?I4cfV%ta=hg;r^ zqsH&I_*|V-6Xs4=+13tJ{i*ARZ{3}6mYo%$9u|(k8>uH0NNqOk#=z9`roR5{d(RTc z-5)Mw3Beln5%h#w)%&)!?|8&O;Jy$o1e!9BH+-)l0gj8mT5qyu{lkmLY#Hw(?^{+V zfgSMII1HuN8Nxdw%!5GrMO%6CuGY~b^bFa)Ho}!PK`h<+f;&=(XV2;Y>(1a$8~)1M zUtq}q3zQ<)qJ!o0{#5FuD$pwX8aX0~TsARgqdSA7f)cBS;gv*A;yQ7v{vgffOpylQ z_>d(nx@tu&FsuYt%Qy`}jO$}gwe7Ye-#|22&ZH&@o1O0_;-ORHX3!-drw1}90+!RWiuT$GzLK0@Y}Eu+W_TO~ zUFxqmMCATMzrG(p3!@FK7W|QfqdG`&t`IhHHiUZM8Eb}cQD)DBs#)As|pL#@6pEK_zWo{D-f5S871&wFRrip4i&lT6sXTC?$ zd910R6W%q?%MS^=h;V8s0i?9QZ#cBhr#{aXbshvWya*dH%Lb1u2KOD0Yv!YIYaXM1 zYi@vUE&Ju;$TBf-^-dyB5)mTG&{!ey!Xfa4RS4$@d)VtO4~L%nkZb3w=iX88^R8rN z08@Km+Hmwi_WAWOy~B2-#UN^ze3M8ClQ?J+@rw5=PnU5>^V&g8jtlT-skDsF(Kw#Y z6I#xcAU3|1<+$t&3m&0u!QkH?_?Mn`msOnJoD}}Tt01i zJ?B|bJDEhnAn?5q?R2e|T{PX#*fmdcW(l}Z6ns2H<$eq^{$18>HG4%*2VC;@ zEXq7svCluL+ZZ#5qd`5^pRsR0P@F_AK`Wq`>)(O(aQ&Q*N>jXk#B4l>O?fJ! z#LIaiUJ~TTu|JKOD%1Q3{r+a9KmAT2)4YT0#Qg3@Z08$nE6~n+u3P(y14)C?T^!jIZcEN%E5eMm7 zl9>>WaJp46#FBf}9EsWXk}KhgcS^QkX=xQjNEIP7gk1q2@MsFV`t|%SK;NuE*hSiaD5ZG{Bjl#e^-2sRnlSVW4N{PtXnym8 z?az&EFRS!}+(sE!_+YEWKA()Cjg&p?4Ns9mjinoCgs6DA(rfOAxjz*Wt494Lf6pjv zRd~3*M;(lILo~==*q&&DCl-HhIyW*lybH_6N)yd2Yx0lfLK3~DQL$8Pfw->likSOB zm(p2Hx%Ywbn6eTHrH4Gi`Z!xrp^Zq@Xtw;$EidQdxk)7eUeTE&Gsq&9fA&LRCESKj zP!Kv@%Z}+#>9ROn>DBf;;c%?K(O1|1qv^U!tJ_13Andm`QO@ggfrhxwB{895|$(g{ZHNvd`L#iJrYg)^wZ$QC~frL6YY;4|<6 zc&GC2krb!%Cday7<+9rO&a2;f1Y9m6xQ}fOOP6y+==wcGR1{}jg0GYJm63z3Iwlf_ zlj+=VQ6ia_W~eLihq+*jQPowCvS4#JUU~pBi!SEUe^mI9$mBfbM}mSa-@G))HM{AP zvVWoe+ZBcaOY$F>2Q~LLQUI|S*)4%w83POY0C4wU);VTo3ei`>y!cRR9H!kbit`tk zBR7)m3l{aNyaA)xq$5u1s=LgXO|=(VhkNhv#Juby+8w(8j-mP;_%raF45Jq)XCA@T zAE~}QxUl|U`f3h}6%R<5ePl+?NX$MJZz&r2t}EK6ck20_WSQdhz~ygDW5c_Uh8jr> z!;^tV`6iN~{Mv(% z)@;<-s3Z_)T+L>EY$&v7e4MuT4%k}3jDWMpaqvB4zUTt5G>=9EMv$<{#rE5JPk#pV8B_18v7L^tUs@sG+}a*p1vggl7kOELvrH3= zlvLlC+v-ke^yC7*d~DhPnWOO|yX|UA<`a3Mo@6!t;mho-S&Y1ZWD}Bh7E)0|jcZSX zkoGbU&f3u}20PVQBb=m!>z~`{MM)@vp`4yLTgRY1jQ$`~G3sT@ww36jF)}Xe|7aP& z7B=}Oa~T#eetBnv=e#?Mcvb8IM8m)uBSG+q>m|1e_ol}r4PEo7K7wGspBwRJZL1yc zDT#E=vUPjiV|BajwAfHZD&@4wY}E!gRYyq`M7v_F3hr**Q?*DfM}jq?f?1$YD-t|) zMyt&zbbYcXHc>pE27nq;4i!#>gXJNtTqJKN2r=w>ch)$jGq7P>HxOJuR?!$cMR+_f z_yHWSPZ@tj8kLNq3YnXEg~+c2;0w0jn2gB8!2ZNXmmpF9JEU~`GKhG8$8IQ&0XITb zylBP}Hi{`QV=l-Q;c?lF33<_lINJSe>UtsK?{=wg_xEw;qGR^S_Y5}TMoB5Na4e5$ z<+ftQHGDXEH1}0;%CZrjGqft?4u~Ji*HdDtgC!LFFhbaJ5PS*%bhaIROcqTjiKlSd z)w9=mEl*m7!Z;-%2~B4X`yj5Rn3q{p$cc8FG;HWFT4rp!7Y9)mBbigIMaY|JSBN(( zj{#paNR0=76<$T}I{z9B0iTiKQ%h|$@hrYzEz9BHr5_WEtXFHT`EZ}#&qIcXi%&Mt zf&r_=zmm+~+uEugu5)y#OCy>0{Dhuyiodh{Vq*e1od#58^8v)Z0uf*BXdW#4Xx($B zF-sT=F~OL>llR+`3lCVkIO^9vWQ$`%URI;pe%l^1{z!=ljz$$;=lVI6C@vHWJ;PHY zAzYN*Bzcm;KFZwo^-HG_GZo~wi~>bqItg@?tj@7!bSRzYYK9^be82lVG5Rgx_fUF8 z*T|)Mm8s~DJoCb2nox41=%b;e6c|@Scw&MH4zX@NijaK_v<+ikqb89htR3OMvPn7G z6(1T3yq7i_`qEJ7p?mZEt#NN_Pzdz;=>`M_kKqheKU+XCzlrLpiK4QK;o>YnAZB3-Q7rEjU_m&~CDHV>TnA2N3^>bUnFIq;BRT7RiRaPD! zUUlw2Z4xRl(1*$MkT-8xA#z=4bng1i?##EHT6Rg9qmYL$H(y zXX)6p)Ic2{EorGK=ISWvkhkKMqWrz(6mo>by8TbF5~dTFGJKC%U;Eth|3tm``+4ho z{NOOy#F3%zi*OI{8G_!tYyw@>c;mEaD!XYH6`U%$m*9G$-8~v&ED(9FkBB_RGUR#c z^{m()nG-TsW%5$sJffSs$1;hOB-D}GoH_ZAlSy#r%|*a(B7xdZ7~Q3dU_b-;kax>> z^V(}v_}%#qn{poqdr*7?#FpwF4xb1XohMyPow>X_P<7@Fn$q_l?G&MYpIUF?pSq41 z?e7*63K#`Apt-?Ux>k0C2i@n=k15AtMIv)D0Pk6RL`1;~ELdM?ahB{}zlov!KAzX> z=r)h*dg|V@Vky>5CRNk-{0JC^VY)k*eeR&3%+vFE%y=8~enshXAza&Y?#{8}=wc64 zkC?E{mqpToXzMydp8X%$w&b60_7%T*zy01gUtgdL0Oz}($W9MVK#Pda%h8$TUHxIg z$th#*z!>3)=?7K;-)-;9{T9PQB%bdO^Ggon$q*u;KuzG74t>Qd9B^|3Vi!XGAuTA3c5C&Q}V|90rOub?%4}kng>;un)h50xV-!*HGHpHjq-Z zZF`SfJ2#YD6FtvrF`jY|HJ(cQ*58Q#DLv%&M5Z0)j=xy_@eL zDSJY8c3o0R5jeUDU(1A?(~a)%erBiibWvXmK7WsCAahAE} z@r4VZel>{I#%^+H{dWMS>+7+O z5UL#Uzxur_@=)fAP5n(}nC}fJp*a^W#iYbd2`N~XOboc5@BA#+*W#}El*HYZ@=%uE zeK&0T?Ixyh1=3>aKJ5v;)>v)lRhWBH+s$m(HGaf`(qhu7mEbE8|EbdW47PIl-^`8$}HYbc0mZ`xew|y z6X?XLYG5#-K7{R&x?5Nf@?uVFJJ%$2*?3<`C6Or9BB)rRvf(O&p$qe=aD?>8k~gqX zEk|vn0%D?RT4f7WJZwlOyDqrzm!AeaGc46%BwQ?L{nN)sg9!&QJ)*1}S;fx9XhVMk zS$l2>KlUc$D@(;Aa>Eb>2KFH@L|M;~xocthFGF|st4#Rh%elqAecoASFg=f5DpP)UhbOi>GbcdUuH z$ez42T-02d3d4Ggic*>Q_@?4{uOd~V3qFo0sY(rWc$uTzh-4vH-U`&e+rmCvie3a) z-2;W;4u(4nzEuuLq9?$eOSIg<(|*K8y^Q|WK=~`@T&Y3D%)c#hdu&c!9Mckkjrj z%g}FTrwwggD!SJOTG31G$3jQkwoui0Kiw4pXFUxm-*cbmV#KEKy%WkCFl&I&T3t5WflMV!9{^ z6B|?HLaU|<8Y_;7v7pNq?I1z^#7vqr@I1G(bWz{W)I1;_;o7-h+~;@7zI zWcAQGz(1@T;RDGWGTlG`WvL|7R~v2mjfI-o%dB4{lc$+;B~>(&PLz6hsd916JVT#q zvcUomfgt^VU8Bbz?yn6&J*Ev}R&LgNyjyY#tMG8k5;@^N*`ui@93(h-oVUc1+LDd< zOlgpqkuyg6GioF=UUWes@lOjSA>nrimOeZ9xx1F zHY#&{o{oL*?+XACyMl%tfPcAvyJF9NK9DJg2EfDLaVEZXlUlq>SO9cMc8I^b5Y=M&_U}L zB}{$~DRKX_gaT_~j+{P??jOdG!OJ>BG096k#?efhd&J@&(kdTZ)549AX@gw5$n#b-@HdEK}P@2-N$!eR`V2PdT_IJZ+|%D>k;7TBmyu+ zobD!qo(o5;yWBfr`-sq8Cd2383m0c^76M6DxtjdpT`k7ey{W=PRP=}h%M=c=1` zmC8!hU;tN)9Kv+31b}B2Ph_i|Gf6F24Vp^<9_cJGV_^e+YIvWPj_D2dPS7*G90X2+ zv3Zq3cCB0b*UYM_iFwc*39ITE*UJ{BK5(cJ)!aXk8XK^66G z#F$*50X&2>zOB3pMDenkLmu@iod2w-?_#T?N=NNdGJG+h4>SU4~WNmX6i zr3j`XbAQ+bCWNX#T|d=D43~+StjBt#k1K*I1S1YbytA69?68of!WZ1cabnL@CWuT_iSmCB6dB0BwgRR+;)xCAyjlY6ocxYem-= zEGjr1uas?GJ5)-EosoEiM>*LE*tPU?KkZ_4B}C=ZcK8dT43qQJ%s>eXwQmqpJe_5* z?Cj;BAdxV~Yewd z6rc%?FI4vy{5{0+tAz3-Z3Ii?6!U=p$tse#W@zc1t#Kp9KCy?06W==9P z-Pw&75{S7fIg+!VCO2|<8&WY?UT<<5eSU}^yxs=_@_lp@v|Oio^1ZiglJ0FCQ~0)nXj&$V13Wf_LNb2Kh;gDx zI1Uj3bOIlg6@&9-D)1fZyJ}8B-7Zkh20EJGw?|${U|O>MM?@XEAt=o4Bd!bF-^Xs> zgLSpN$ff8MvY1gS(MTXYPwUMqD{zD5ifG;Kh$G*%8KSYh&#u7e20WJkPFDg*kwEcu zNVJ(ECmd%~%z|OiCxo%fi;9b}LA~HSoxn=|Ld}TI+o)LlbS{d_<~z|+Ry&|#O@K!r zG$#I;P)>ygM76l0cvHoWBL!*LRv0kfwI$NYsza=3auLpH3!}+a2*(m2?dAUIm8F^ldi*9?nm9bK z7ilEHuK9){Yv{bvpHqN@^L}12*zJm_`r-z1ZDz4_&ReoQVEDH-cPdnz1X)#NGqGO; zjAxRooJpT#3`_7mfvIb+(yC69c{q}YAhWW}{F(choP;@T)5rh z+wr;H&ot#we8&?y$5VQl6NgXBy}gWEvu!m*olqZRlag8~?hlD)WsW0zin{w`;7Q4l z+Qy*SU}nkw0S7>17AZ+XCed@=hcfq}U?$`ION5$M4`vIhQ*LThQxlZ$y7e}YkAjt- zJwgB)s`Q*z5r~LBS+lgiP>?@e6!9xvC{^h%hA%~#lWpFY;@J6p zb|!U|h3`}*IBwS&yxhOKn3SJiUmsM8khjeoM_%#G>V#v8f;ROo$G^utZnXZVHe(VF zF%hVtbz$VTR6vbbQaRkv%uVZMD+P}XK~=?!kP?qdf!<=^W6q@`;3IDkAD9@~kh@wF z$rPEvE!&xjgv^aAE0m;BRx#-7AIw?ZBFV(s6W4-l5-%dmqie`$=-n@O-OA(cizkvu z>fxG%ad7uz@f3+Nr5my6UD_D4+@OrR3fHocB-!7jQtK84%`pXYqHCDD=8oFRuWFM) z$e{(-5OL*FZM-r~YmuatqsZqjh~*aE=cYqrt%`%ov&w<9nBWn|+>iZ1upa_m013jd zf!OaoK!`h=8Ynb`xC9lwyt3S6#N5t)X>khe9DJF4*0h4IgXBdlGA?~bPaF(^DVm1Y z5G5qCvURb2CWbN_MjR^SNU^+$w3)U76!LWnyDe0@f+bk#qD;(;vzV=~Yo^|z$ZvMO zFprBTj_9)fQ8l;-B&HFgnlLQt=;FGsZ6C&$zy+96b@=Sep@tiTUn(2rt|(jH_Z+~(mdKh1B7H{?B_IqC>4t9b96+?b>#0dok#jm9khw|1 z{mYYZ!A8EjYJoKA;i+ieYA^S_{4@bw`aLa~VI;v%s<5az9f_ijivPUokX${7-o~^u zbn5QfLGvCndLRJIa8LMS(ik@Y)saa-j1Z&o7dSsN0D8AhY|Lf23A>VJ-(y=k(%Q-f38C=;?35)ur8=PG2|cfxpl8kd`M>p>_-#zWh@loIMx2HpUOlJ$B2 z@?di2hS^f+N;1ye+&q(c+i;rZ;L4``$in0j9U|iVv6b@6nt4thjKvhl2h;p?ZG!AS zQfTBzY#nloGG=TY@Z#WNE2Uv>E=UP@RWzh&)xC0L(56oOxtmi_Ri-Sm3`}VD5>OMn z9}t)#gmcy$IWd-x%R;j?t^5*5W4I94eOSbcFyaw~-=WrJ*UBc5^%i+l{Teq*s@N;a zyo+Nm#?L}nwTi33i=ozD^R%J;}a&u2QW z%c;BB>W4ueEmR%?GtZDZ8inXtZms7@{woB~mf*KfIbyIq@-8g}0}5yOt~3c>;sR{Sm!~Nm?v>v?+r#b-kMy+VD=`lQo&;C{`1z0r_ zyN{ObwkO1A$7u(LeSDu@dg}6bh>_zX^Wk`(o;3K~4r15fYiIX$7t5=*XAb-JZvugjB+;`b*~oAUR_!uzJClk?aZ8+7 zqY~3<9O8;dYM2#0Tl)C-qxz{#{=BZSTlQv_(53i&(L$;_f85|_0^46E0xy9mVTk!y zsQ9dtENYidm?8_RSshyq)h#0pT727us5zvnk+)PeLU6@Ak%(f*Rcj;d;Gu=VGBF@g84;=1j5t3X5qLQ@c`-vm`!!OqS1GQ7vOA}FjiXQ(n-a9Uey!|>~DVNhS;}uhN^Db zBHn7{%q)gs4m_?F1jZpu!u7a3DTmq-^H(z25>HB9mco#Tw&4uj_#;or+7uz*LZ2rC zypczTrwR`19-Z+3csuXj#A0tEt1+f52>+Fui1PhDmaoW^MT!Yz#KgtL94AmyMZrZe z7U(X?QN&RI%FW0uB5*@=rfdU~2E76f0p}PO4QDylGdf|U3VcDE?g5ClJy+-#Z2;a( z3e~o&w_V5A^<9%G5oTKoLeJ+fy5#qAN$^53ez<^>*7_r=1~s2wkzB@WQLv#$t|bp=|Z;i+HW5BwJ&

{+hKY^3qrZrk5?Ab;`tAgp243_GB1$4~KNb5z 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

I16=L4_Xp4ea1b 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 literal 0 HcmV?d00001 From a23208cbc82b1d2eb8d4971b6355408b6a13e567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20G=C3=B3mez-Miguelez?= Date: Sat, 8 Mar 2014 12:26:01 +0000 Subject: [PATCH 12/25] Update README.md --- README.md | 68 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 539310895..b4f0fb748 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,20 @@ The license is LGPLv3. The project contains a set of Python tools for the automatic code generation of modules for popular SDR frameworks, including GNURadio, ALOE++, IRIS, and OSSIE. These tools are easy to use and adapt for generating targets for specific platforms or frameworks. +Current Status +=============== + +The following parts are available: + * Physical Broadcast channel (PBCH) eNodeB and UE part. The UE supports multi-antenna transmitters + * Synchronization and CFO estimation/correction + * Equalization + * UE receiver verified with live LTE signals + +Hardware +======== + +The library currently uses Ettus Universal Hardware Driver (UHD). Thus, any hardware supported by UHD can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. + Support ======== @@ -16,6 +30,8 @@ Mailing list: https://lists.sourceforge.net/lists/listinfo/liblte-users Download & Install Instructions ================================= +* Requirements: Currently, the library requires libfftw, although we plan make this dependency optional in the future. Also, QT4 and Qwt6 are needed for graphics visualization. If they are not present, though, compilation is still possible although graphics will be disabled. + ``` git clone https://github.com/ismagom/libLTE.git cd libLTE @@ -23,42 +39,40 @@ mkdir build cd build cmake ../ make -sudo make install ``` -Cell Search Example -==================== - -This program uses any hardware supported by the UHD driver to scan an LTE band for active cells. See http://niviuk.free.fr/lte_band.php for a list of available bands. The program first obtains a power spectral density of the entire band. For all frequencies with an RSSI higher than a threshold, it tries to find the LTE Primary Synchronization Signal (PSS) and then identifies the CELL ID using the Secondary Synchronization Signal (SSS). Finally, it estimates the Carrier Frequency Offset (CFO) and Sampling Frequency Offset (SFO). +The library can also be installed using the command ```sudo make install```. -For instance, the command: +Examples +========== -``` pss_scan_usrp -b 3 ``` +* eNodeB and UE PBCH example +Setup one or two computers connected to two USRP or UHD-compatible hardware. From the eNodeB, type -Scans the LTE band 3 (1805 to 1880 MHz). Note that you need a hardware supporting these frequencies (e.g. SBX daughterboard for USRP). The program outputs the following: +``` +examples/enodeb_bch -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] +``` +From the UE, type ``` -Opening UHD device... --- Opening a USRP2/N-Series device... --- Current recv frame size: 1472 bytes --- Current send frame size: 1472 bytes -RSSI scan: 749 freqs in band 3, RSSI threshold -30.00 dBm -Freq 1879.0 Mhz - RSSI: -43.96 dBm -Done. Starting PSS search on 75 channels - -UHD Warning: - The hardware does not support the requested RX sample rate: - Target sample rate: 1.920000 MSps - Actual sample rate: 1.923077 MSps -[199/749]: EARFCN 1399 Freq. 1824.90 MHz No PSS found -[200/749]: FOUND EARFCN 1400 Freq. 1825.00 MHz, RSSI -22.43 dBm, PAR 15.86 dB, CFO=-0.25 KHz, SFO=+3.099 KHz, CELL_ID=150 -[433/749]: EARFCN 1633 Freq. 1848.30 MHz No PSS found - -Done +examples/mib_track -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] ``` -indicating that a Cell with ID 150 has been found at 1825.0 MHz. PAR indicates the peak-to-average ratio (in dB) at the output of the PSS correlator. +And the output should look something like the following video. In this example, we removed the transmitter and receiver antennas in the middle of the demonstration, showing how reception is still possible (despite with some erros). + +https://www.dropbox.com/s/txh1nuzdb0igq5n/demo_pbch.ogv + +![Screenshopt of the PBCH example output](pbch_capture.png "Screenshopt of the PBCH example output") + +* Cell Search Example + +This program uses any hardware supported by the UHD driver to scan an LTE band for active cells. See http://niviuk.free.fr/lte_band.php for a list of available bands. The program first obtains a power spectral density of the entire band. For all frequencies with an RSSI higher than a threshold, it tries to find the LTE Primary Synchronization Signal (PSS) and then identifies the CELL ID using the Secondary Synchronization Signal (SSS). Finally, it estimates the Carrier Frequency Offset (CFO) and Sampling Frequency Offset (SFO) and decodes the Master Information Block (MIB) from the PBCH. + +For instance, the command: + +``` examples/mib_scan_usrp -b 3 ``` + -For more command arguments, type ``` pss_scan_usrp --help ``` +Scans the LTE band 3 (1805 to 1880 MHz). Note that you need a hardware supporting these frequencies (e.g. SBX daughterboard for USRP). For more command arguments, type ``` examples/mib_scan_usrp -h ``` From 74af8dd9f83ce9cb0b7d704c8d7dd0d481a45162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20G=C3=B3mez-Miguelez?= Date: Sat, 8 Mar 2014 12:26:27 +0000 Subject: [PATCH 13/25] Update README.md --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b4f0fb748..ac8273ebc 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,6 @@ Hardware The library currently uses Ettus Universal Hardware Driver (UHD). Thus, any hardware supported by UHD can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. -Support -======== - -Mailing list: https://lists.sourceforge.net/lists/listinfo/liblte-users - Download & Install Instructions ================================= @@ -76,3 +71,9 @@ For instance, the command: Scans the LTE band 3 (1805 to 1880 MHz). Note that you need a hardware supporting these frequencies (e.g. SBX daughterboard for USRP). For more command arguments, type ``` examples/mib_scan_usrp -h ``` + + +Support +======== + +Mailing list: https://lists.sourceforge.net/lists/listinfo/liblte-users From bea03bfdd2b37289d57d5a88bda9984cdf4b5f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20G=C3=B3mez-Miguelez?= Date: Sat, 8 Mar 2014 12:33:26 +0000 Subject: [PATCH 14/25] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index ac8273ebc..0c4f592b5 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,21 @@ https://www.dropbox.com/s/txh1nuzdb0igq5n/demo_pbch.ogv ![Screenshopt of the PBCH example output](pbch_capture.png "Screenshopt of the PBCH example output") + +If you don't have a pair of USRP, you can also test the demo by writing the samples to a file and then reading them: + +From the eNodeB, type + +``` +examples/enodeb_bch -o [output_file] -c [cell_id] [-h for more commands] +``` + +From the UE, type +``` +examples/mib_track -i [input_file] -c [cell_id] [-h for more commands] +``` + + * Cell Search Example This program uses any hardware supported by the UHD driver to scan an LTE band for active cells. See http://niviuk.free.fr/lte_band.php for a list of available bands. The program first obtains a power spectral density of the entire band. For all frequencies with an RSSI higher than a threshold, it tries to find the LTE Primary Synchronization Signal (PSS) and then identifies the CELL ID using the Secondary Synchronization Signal (SSS). Finally, it estimates the Carrier Frequency Offset (CFO) and Sampling Frequency Offset (SFO) and decodes the Master Information Block (MIB) from the PBCH. From 67b8cf3ee2bb5462f5a0e2f7e6098e0b0df63398 Mon Sep 17 00:00:00 2001 From: ismagom Date: Tue, 11 Mar 2014 13:42:51 -0500 Subject: [PATCH 15/25] Added scrambling, ratematching and layer mapping tests --- CMakeLists.txt | 4 + CTestCustom.cmake.in | 15 ++ examples/CMakeLists.txt | 48 ++--- examples/{enodeb_bch.c => pbch_enodeb.c} | 0 examples/{mib_track.c => pbch_ue.c} | 0 examples/{mib_scan_usrp.c => scan_mib.c} | 0 examples/{pss_scan_usrp.c => scan_pss.c} | 0 examples/{rssi_scan_usrp.c => scan_rssi.c} | 0 examples/{synch_test.c => synch_file.c} | 14 +- lte/include/lte/common/base.h | 8 +- lte/include/lte/mimo/layermap.h | 21 ++- lte/include/lte/mimo/precoding.h | 20 ++- lte/include/lte/modem/demod_hard.h | 4 +- lte/lib/ch_estimation/test/CMakeLists.txt | 1 + lte/lib/common/src/fft.c | 3 + lte/lib/common/src/lte.c | 24 +++ lte/lib/common/src/phch_sequence.c | 3 +- lte/lib/common/test/CMakeLists.txt | 34 ++++ lte/lib/common/test/fft_test.c | 141 +++++++++++++++ lte/lib/mimo/src/layermap.c | 194 ++++++++++++++++++--- lte/lib/mimo/src/precoding.c | 157 +++++++++++++---- lte/lib/mimo/test/CMakeLists.txt | 65 +++++++ lte/lib/mimo/test/layermap_test.c | 163 +++++++++++++++++ lte/lib/mimo/test/precoding_test.c | 163 +++++++++++++++++ lte/lib/modem/src/demod_hard.c | 6 +- lte/lib/modem/test/CMakeLists.txt | 40 +++++ lte/lib/modem/test/modem_test.c | 184 +++++++++++++++++++ lte/lib/phch/src/pbch.c | 34 ++-- lte/lib/ratematching/test/CMakeLists.txt | 33 ++++ lte/lib/ratematching/test/rm_conv_test.c | 134 ++++++++++++++ lte/lib/scrambling/test/CMakeLists.txt | 35 ++++ lte/lib/scrambling/test/scrambling_test.c | 165 ++++++++++++++++++ 32 files changed, 1579 insertions(+), 134 deletions(-) create mode 100644 CTestCustom.cmake.in rename examples/{enodeb_bch.c => pbch_enodeb.c} (100%) rename examples/{mib_track.c => pbch_ue.c} (100%) rename examples/{mib_scan_usrp.c => scan_mib.c} (100%) rename examples/{pss_scan_usrp.c => scan_pss.c} (100%) rename examples/{rssi_scan_usrp.c => scan_rssi.c} (100%) rename examples/{synch_test.c => synch_file.c} (94%) create mode 100644 lte/lib/common/test/CMakeLists.txt create mode 100644 lte/lib/common/test/fft_test.c create mode 100644 lte/lib/mimo/test/CMakeLists.txt create mode 100644 lte/lib/mimo/test/layermap_test.c create mode 100644 lte/lib/mimo/test/precoding_test.c create mode 100644 lte/lib/modem/test/CMakeLists.txt create mode 100644 lte/lib/modem/test/modem_test.c create mode 100644 lte/lib/ratematching/test/CMakeLists.txt create mode 100644 lte/lib/ratematching/test/rm_conv_test.c create mode 100644 lte/lib/scrambling/test/CMakeLists.txt create mode 100644 lte/lib/scrambling/test/scrambling_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e3d4d66ce..143fd68ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,10 @@ INCLUDE(libLTEPackage) #setup cpack include(CTest) set( CTEST_MEMORYCHECK_COMMAND valgrind ) +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake" + IMMEDIATE @ONLY) ######################################################################## # Install Dirs diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in new file mode 100644 index 000000000..a66fe53e2 --- /dev/null +++ b/CTestCustom.cmake.in @@ -0,0 +1,15 @@ +SET(CTEST_CUSTOM_MEMCHECK_IGNORE + +# Ignore memcheck for plots. QT errors + + waterfallplot_test + scatterplot_test + realplot_test + complexplot_test + +# Ignore these to, they take too lonk + fft_normal + fft_extened + chest_test_all_cellids +) + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b82f3fadd..a3f33e811 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,8 +19,9 @@ # and at http://www.gnu.org/licenses/. # + ################################################################# -# TO BE MOVED TO UNIT TESTS +# EXAMPLES ################################################################# add_executable(hl_example hl_example.c) @@ -29,8 +30,13 @@ target_link_libraries(hl_example lte) add_executable(ll_example ll_example.c) target_link_libraries(ll_example lte) -add_executable(synch_test synch_test.c) -target_link_libraries(synch_test lte) +add_executable(synch_file synch_file.c) +target_link_libraries(synch_file lte) + + +################################################################# +# TO BE MOVED TO UNIT TESTS +################################################################# add_executable(mib_test mib_test.c) target_link_libraries(mib_test lte) @@ -47,26 +53,26 @@ LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND) # These two can be compiled without UHD or graphics support ################################################################# -add_executable(mib_track mib_track.c) -target_link_libraries(mib_track lte) +add_executable(pbch_ue pbch_ue.c) +target_link_libraries(pbch_ue lte) -add_executable(enodeb_bch enodeb_bch.c) -target_link_libraries(enodeb_bch lte) +add_executable(pbch_enodeb pbch_enodeb.c) +target_link_libraries(pbch_enodeb lte) IF(${CUHD_FIND} EQUAL -1) - SET_TARGET_PROPERTIES(mib_track PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") - SET_TARGET_PROPERTIES(enodeb_bch PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") + SET_TARGET_PROPERTIES(pbch_ue PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") + SET_TARGET_PROPERTIES(pbch_enodeb PROPERTIES COMPILE_DEFINITIONS "DISABLE_UHD") ELSE(${CUHD_FIND} EQUAL -1) - target_link_libraries(mib_track cuhd) - target_link_libraries(enodeb_bch cuhd) + target_link_libraries(pbch_ue cuhd) + target_link_libraries(pbch_enodeb cuhd) ENDIF(${CUHD_FIND} EQUAL -1) IF(${GRAPHICS_FIND} EQUAL -1) - SET_TARGET_PROPERTIES(mib_track PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") - SET_TARGET_PROPERTIES(enodeb_bch PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") + SET_TARGET_PROPERTIES(pbch_ue PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") + SET_TARGET_PROPERTIES(pbch_enodeb PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS") ELSE(${GRAPHICS_FIND} EQUAL -1) - target_link_libraries(mib_track graphics) - target_link_libraries(enodeb_bch graphics) + target_link_libraries(pbch_ue graphics) + target_link_libraries(pbch_enodeb graphics) ENDIF(${GRAPHICS_FIND} EQUAL -1) @@ -77,14 +83,14 @@ ENDIF(${GRAPHICS_FIND} EQUAL -1) IF(${CUHD_FIND} GREATER -1) - add_executable(rssi_scan_usrp rssi_scan_usrp.c) - target_link_libraries(rssi_scan_usrp lte cuhd ) + add_executable(scan_rssi scan_rssi.c) + target_link_libraries(scan_rssi lte cuhd ) - add_executable(pss_scan_usrp pss_scan_usrp.c) - target_link_libraries(pss_scan_usrp lte cuhd ) + add_executable(scan_pss scan_pss.c) + target_link_libraries(scan_pss lte cuhd ) - add_executable(mib_scan_usrp mib_scan_usrp.c) - target_link_libraries(mib_scan_usrp lte cuhd ) + add_executable(scan_mib scan_mib.c) + target_link_libraries(scan_mib lte cuhd ) MESSAGE(STATUS " UHD examples will be installed.") diff --git a/examples/enodeb_bch.c b/examples/pbch_enodeb.c similarity index 100% rename from examples/enodeb_bch.c rename to examples/pbch_enodeb.c diff --git a/examples/mib_track.c b/examples/pbch_ue.c similarity index 100% rename from examples/mib_track.c rename to examples/pbch_ue.c diff --git a/examples/mib_scan_usrp.c b/examples/scan_mib.c similarity index 100% rename from examples/mib_scan_usrp.c rename to examples/scan_mib.c diff --git a/examples/pss_scan_usrp.c b/examples/scan_pss.c similarity index 100% rename from examples/pss_scan_usrp.c rename to examples/scan_pss.c diff --git a/examples/rssi_scan_usrp.c b/examples/scan_rssi.c similarity index 100% rename from examples/rssi_scan_usrp.c rename to examples/scan_rssi.c diff --git a/examples/synch_test.c b/examples/synch_file.c similarity index 94% rename from examples/synch_test.c rename to examples/synch_file.c index 1a468b3ed..db9a8338b 100644 --- a/examples/synch_test.c +++ b/examples/synch_file.c @@ -37,20 +37,18 @@ char *input_file_name; char *output_file_name="abs_corr.txt"; int nof_slots=100, frame_length=9600, symbol_sz=128; float corr_peak_threshold=25.0; -int file_binary = 0; int out_N_id_2 = 0, force_N_id_2=-1; #define CFO_AUTO -9999.0 float force_cfo = CFO_AUTO; void usage(char *prog) { - printf("Usage: %s [onlt] -i input_file\n", prog); + printf("Usage: %s [olntsNfc] -i input_file\n", prog); printf("\t-o output_file [Default %s]\n", output_file_name); printf("\t-l frame_length [Default %d]\n", frame_length); printf("\t-n number of frames [Default %d]\n", nof_slots); printf("\t-t correlation threshold [Default %g]\n", corr_peak_threshold); printf("\t-s symbol_sz [Default %d]\n", symbol_sz); - printf("\t-b Input files is binary [Default %s]\n", file_binary?"yes":"no"); printf("\t-N out_N_id_2 [Default %d]\n", out_N_id_2); printf("\t-f force_N_id_2 [Default %d]\n", force_N_id_2); printf("\t-c force_cfo [Default disabled]\n"); @@ -58,7 +56,7 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "ionltsbNfc")) != -1) { + while ((opt = getopt(argc, argv, "ionltsNfc")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; @@ -78,9 +76,6 @@ void parse_args(int argc, char **argv) { case 's': symbol_sz = atof(argv[optind]); break; - case 'b': - file_binary = 1; - break; case 'N': out_N_id_2 = atoi(argv[optind]); break; @@ -130,12 +125,11 @@ int main(int argc, char **argv) { gettimeofday(&tdata[1], NULL); printf("Initializing...");fflush(stdout); - data_type_t type = file_binary?COMPLEX_FLOAT_BIN:COMPLEX_FLOAT; - if (filesource_init(&fsrc, input_file_name, type)) { + if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { fprintf(stderr, "Error opening file %s\n", input_file_name); exit(-1); } - if (filesink_init(&fsink, output_file_name, type)) { + if (filesink_init(&fsink, output_file_name, COMPLEX_FLOAT_BIN)) { fprintf(stderr, "Error opening file %s\n", output_file_name); exit(-1); } diff --git a/lte/include/lte/common/base.h b/lte/include/lte/common/base.h index ae41c2d78..8212b43d5 100644 --- a/lte/include/lte/common/base.h +++ b/lte/include/lte/common/base.h @@ -34,8 +34,8 @@ #define MAX_PORTS 4 #define MAX_PORTS_CTRL 4 -#define MAX_LAYERS 4 -#define MAX_CODEWORDS 4 +#define MAX_LAYERS 8 +#define MAX_CODEWORDS 2 typedef enum {CPNORM, CPEXT} lte_cp_t; @@ -85,7 +85,7 @@ int lte_voffset(int symbol_id, int cell_id, int nof_ports); typedef enum { - TX_DIVERSITY, SPATIAL_MULTIPLEX + SINGLE_ANTENNA,TX_DIVERSITY, SPATIAL_MULTIPLEX } mimo_type_t; @@ -103,6 +103,8 @@ int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int earfcn_start, int e int lte_band_get_fd_band_all(int band, lte_earfcn_t *earfcn, int max_nelems); int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, int max_elems); +int lte_str2mimotype(char *mimo_type_str, mimo_type_t *type); +char *lte_mimotype2str(mimo_type_t type); #endif diff --git a/lte/include/lte/mimo/layermap.h b/lte/include/lte/mimo/layermap.h index 5527d6d7e..9a2f6f9bb 100644 --- a/lte/include/lte/mimo/layermap.h +++ b/lte/include/lte/mimo/layermap.h @@ -31,14 +31,23 @@ typedef _Complex float cf_t; -/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" +/* Generates the vector of layer-mapped symbols "x" based on the vector of data symbols "d" */ -void layermap_decode(cf_t *x[MAX_LAYERS], cf_t *d[MAX_CODEWORDS], int nof_layers, int nof_cw, - int nof_layer_symbols, mimo_type_t type); +int layermap_single(cf_t *d, cf_t *x, int nof_symbols); +int layermap_diversity(cf_t *d, cf_t *x[MAX_LAYERS], int nof_layers, int nof_symbols); +int layermap_multiplex(cf_t *d[MAX_CODEWORDS], cf_t *x[MAX_LAYERS], int nof_cw, int nof_layers, + int nof_symbols[MAX_CODEWORDS]); +int layermap_type(cf_t *d[MAX_CODEWORDS], cf_t *x[MAX_LAYERS], int nof_cw, int nof_layers, + int nof_symbols[MAX_CODEWORDS], mimo_type_t type); -/* Generates the vector of layer-mapped symbols "x" based on the vector of data symbols "d" + +/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" */ -void layermap_encode(cf_t *d[MAX_CODEWORDS], cf_t *x[MAX_LAYERS], int nof_layers, int nof_cw, - int nof_symbols, mimo_type_t type); +int layerdemap_single(cf_t *x, cf_t *d, int nof_symbols); +int layerdemap_diversity(cf_t *x[MAX_LAYERS], cf_t *d, int nof_layers, int nof_layer_symbols); +int layerdemap_multiplex(cf_t *x[MAX_LAYERS], cf_t *d[MAX_CODEWORDS], int nof_layers, int nof_cw, + int nof_layer_symbols, int nof_symbols[MAX_CODEWORDS]); +int layerdemap_type(cf_t *x[MAX_LAYERS], cf_t *d[MAX_CODEWORDS], int nof_layers, int nof_cw, + int nof_layer_symbols, int nof_symbols[MAX_CODEWORDS], mimo_type_t type); #endif diff --git a/lte/include/lte/mimo/precoding.h b/lte/include/lte/mimo/precoding.h index 708c61f9c..ea68c5496 100644 --- a/lte/include/lte/mimo/precoding.h +++ b/lte/include/lte/mimo/precoding.h @@ -36,15 +36,21 @@ typedef _Complex float cf_t; * resources on each of the antenna ports. */ -/* Estimates the vector "x" based on the received signal "y" and the channel estimates "ce" - */ -void precoding_decode(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], - cf_t *x[MAX_LAYERS], int nof_ports, int nof_symbols, - mimo_type_t type); - /* Generates the vector "y" from the input vector "x" */ -void precoding_encode(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, +int precoding_single(cf_t *x, cf_t *y, int nof_symbols); +int precoding_diversity(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, int nof_symbols); +int precoding_type(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_layers, int nof_ports, int nof_symbols, mimo_type_t type); + +/* Estimates the vector "x" based on the received signal "y" and the channel estimates "ce" + */ +int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols); +int predecoding_diversity_zf(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], + cf_t *x[MAX_LAYERS], int nof_ports, int nof_symbols); +int predecoding_type(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], + cf_t *x[MAX_LAYERS], int nof_ports, int nof_layers, int nof_symbols, + mimo_type_t type); + #endif /* PRECODING_H_ */ diff --git a/lte/include/lte/modem/demod_hard.h b/lte/include/lte/modem/demod_hard.h index 8675c9758..4525ff7a1 100644 --- a/lte/include/lte/modem/demod_hard.h +++ b/lte/include/lte/modem/demod_hard.h @@ -42,8 +42,8 @@ typedef struct { void demod_hard_init(demod_hard_t* q); -void demod_hard_table(demod_hard_t* q, enum modem_std table); -int demod_hard_demodulate(demod_hard_t* q, const cf_t* symbols, char *bits, int nsymbols); +void demod_hard_table_set(demod_hard_t* q, enum modem_std table); +int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols); diff --git a/lte/lib/ch_estimation/test/CMakeLists.txt b/lte/lib/ch_estimation/test/CMakeLists.txt index 04b6d85b8..9f4be5ea8 100644 --- a/lte/lib/ch_estimation/test/CMakeLists.txt +++ b/lte/lib/ch_estimation/test/CMakeLists.txt @@ -27,5 +27,6 @@ ADD_EXECUTABLE(chest_test chest_test.c) TARGET_LINK_LIBRARIES(chest_test lte) ADD_TEST(chest_test_all_cellids chest_test) +ADD_TEST(chest_test_cellid chest_test -c 1) diff --git a/lte/lib/common/src/fft.c b/lte/lib/common/src/fft.c index e5fae9226..e6319a5aa 100644 --- a/lte/lib/common/src/fft.c +++ b/lte/lib/common/src/fft.c @@ -71,6 +71,9 @@ int lte_fft_init_(lte_fft_t *q, lte_cp_t cp_type, int nof_prb, dft_dir_t dir) { void lte_fft_free_(lte_fft_t *q) { dft_plan_free(&q->fft_plan); + if (q->tmp) { + free(q->tmp); + } bzero(q, sizeof(lte_fft_t)); } diff --git a/lte/lib/common/src/lte.c b/lte/lib/common/src/lte.c index 74b2734fd..8147b68d1 100644 --- a/lte/lib/common/src/lte.c +++ b/lte/lib/common/src/lte.c @@ -134,6 +134,30 @@ struct lte_band lte_bands[NOF_LTE_BANDS] = { }; #define EOF_BAND 9919 +int lte_str2mimotype(char *mimo_type_str, mimo_type_t *type) { + if (!strcmp(mimo_type_str, "single")) { + *type = SINGLE_ANTENNA; + } else if (!strcmp(mimo_type_str, "diversity")) { + *type = TX_DIVERSITY; + } else if (!strcmp(mimo_type_str, "multiplex")) { + *type = SPATIAL_MULTIPLEX; + } else { + return -1; + } + return 0; +} + +char *lte_mimotype2str(mimo_type_t type) { + switch(type) { + case SINGLE_ANTENNA: + return "single"; + case TX_DIVERSITY: + return "diversity"; + case SPATIAL_MULTIPLEX: + return "multiplex"; + } +} + float get_fd(struct lte_band *band, int earfcn) { return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset); } diff --git a/lte/lib/common/src/phch_sequence.c b/lte/lib/common/src/phch_sequence.c index 6b2797859..8525d5d6a 100644 --- a/lte/lib/common/src/phch_sequence.c +++ b/lte/lib/common/src/phch_sequence.c @@ -25,10 +25,11 @@ * */ - +#include #include "lte/common/base.h" #include "lte/common/sequence.h" int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { + bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, CP_ISNORM(cp)?1920:1728, cell_id); } diff --git a/lte/lib/common/test/CMakeLists.txt b/lte/lib/common/test/CMakeLists.txt new file mode 100644 index 000000000..18ed7f8fb --- /dev/null +++ b/lte/lib/common/test/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# FFT TEST +######################################################################## + +ADD_EXECUTABLE(fft_test fft_test.c) +TARGET_LINK_LIBRARIES(fft_test lte) + +ADD_TEST(fft_normal fft_test) +ADD_TEST(fft_extended fft_test -e) + +ADD_TEST(fft_normal_single fft_test -n 6) +ADD_TEST(fft_extended_single fft_test -e -n 6) + diff --git a/lte/lib/common/test/fft_test.c b/lte/lib/common/test/fft_test.c new file mode 100644 index 000000000..e82206eb8 --- /dev/null +++ b/lte/lib/common/test/fft_test.c @@ -0,0 +1,141 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int nof_prb = -1; +lte_cp_t cp = CPNORM; + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-n nof_prb [Default All]\n"); + printf("\t-e extended cyclic prefix [Default Normal]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "ne")) != -1) { + switch (opt) { + case 'n': + nof_prb = atoi(argv[optind]); + break; + case 'e': + cp = CPEXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + lte_fft_t fft, ifft; + cf_t *input, *outfft, *outifft; + float mse; + int n_prb, max_prb, n_re; + int i; + + parse_args(argc, argv); + + if (nof_prb == -1) { + n_prb = 6; + max_prb = 100; + } else { + n_prb = nof_prb; + max_prb = nof_prb; + } + while(n_prb <= max_prb) { + n_re = CP_NSYMB(cp) * n_prb * RE_X_RB; + + printf("Running test for %d PRB, %d RE... ", n_prb, n_re);fflush(stdout); + + input = malloc(sizeof(cf_t) * n_re); + if (!input) { + perror("malloc"); + exit(-1); + } + outfft = malloc(sizeof(cf_t) * SLOT_LEN_CPNORM(lte_symbol_sz(n_prb))); + if (!outfft) { + perror("malloc"); + exit(-1); + } + outifft = malloc(sizeof(cf_t) * n_re); + if (!outifft) { + perror("malloc"); + exit(-1); + } + + if (lte_fft_init(&fft, cp, n_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + exit(-1); + } + if (lte_ifft_init(&ifft, cp, n_prb)) { + fprintf(stderr, "Error initializing iFFT\n"); + exit(-1); + } + + for (i=0;i= 0.05) { + printf("MSE too large\n"); + exit(-1); + } + + lte_fft_free(&fft); + lte_ifft_free(&ifft); + + free(input); + free(outfft); + free(outifft); + + n_prb++; + } + fftwf_cleanup(); + exit(0); +} diff --git a/lte/lib/mimo/src/layermap.c b/lte/lib/mimo/src/layermap.c index 07de82911..cd685dcd0 100644 --- a/lte/lib/mimo/src/layermap.c +++ b/lte/lib/mimo/src/layermap.c @@ -33,46 +33,182 @@ #include "lte/common/base.h" #include "lte/mimo/layermap.h" -/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" + + +int layermap_single(cf_t *d, cf_t *x, int nof_symbols) { + memcpy(x, d, sizeof(cf_t) * nof_symbols); + return nof_symbols; +} + +int layermap_diversity(cf_t *d, cf_t *x[MAX_LAYERS], int nof_layers, int nof_symbols) { + int i, j; + for (i=0;i MAX_CODEWORDS) { + fprintf(stderr, "Maximum number of codewords is %d (nof_cw=%d)\n", MAX_CODEWORDS, nof_cw); + return -1; + } + if (nof_layers > MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", MAX_LAYERS, nof_layers); + return -1; + } + if (nof_layers < nof_cw) { + fprintf(stderr, "Number of codewords must be lower or equal than number of layers\n"); + return -1; + } - switch(nof_layers) { - case 1: - memcpy(d[0], x[0], nof_layer_symbols * sizeof(cf_t)); + switch(type) { + case SINGLE_ANTENNA: + if (nof_cw == 1 && nof_layers == 1) { + return layermap_single(x[0], d[0], nof_symbols[0]); + } else { + fprintf(stderr, "Number of codewords and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } break; - case 2: - switch(type) { - case TX_DIVERSITY: - for (i=0;i MAX_CODEWORDS) { + fprintf(stderr, "Maximum number of codewords is %d (nof_cw=%d)\n", MAX_CODEWORDS, nof_cw); + return -1; + } + if (nof_layers > MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", MAX_LAYERS, nof_layers); + return -1; + } + if (nof_layers < nof_cw) { + fprintf(stderr, "Number of codewords must be lower or equal than number of layers\n"); + return -1; + } + + switch(type) { + case SINGLE_ANTENNA: + if (nof_cw == 1 && nof_layers == 1) { + nof_symbols[0] = layerdemap_single(x[0], d[0], nof_layer_symbols); + nof_symbols[1] = 0; + } else { + fprintf(stderr, "Number of codewords and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case TX_DIVERSITY: + if (nof_cw == 1) { + if (nof_layers == 2 || nof_layers == 4) { + nof_symbols[0] = layerdemap_diversity(x, d[0], nof_layers, nof_layer_symbols); + nof_symbols[1] = 0; + } else { + fprintf(stderr, "Number of layers must be 2 or 4 for transmit diversity\n"); + return -1; + } + } else { + fprintf(stderr, "Number of codewords must be 1 for transmit diversity\n"); + return -1; + } + break; + case SPATIAL_MULTIPLEX: + return layerdemap_multiplex(x, d, nof_layers, nof_cw, nof_layer_symbols, nof_symbols); break; - default: - printf("Error: Unsupported nof_ports=%d\n", nof_layers); - return; } + return 0; } - diff --git a/lte/lib/mimo/src/precoding.c b/lte/lib/mimo/src/precoding.c index 75f64ecfc..340ef2f0d 100644 --- a/lte/lib/mimo/src/precoding.c +++ b/lte/lib/mimo/src/precoding.c @@ -31,56 +31,141 @@ #include #include #include +#include #include "lte/common/base.h" #include "lte/mimo/precoding.h" #include "lte/utils/vector.h" +int precoding_single(cf_t *x, cf_t *y, int nof_symbols) { + memcpy(y, x, nof_symbols * sizeof(cf_t)); + return nof_symbols; +} +int precoding_diversity(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, int nof_symbols) { + int i; + if (nof_ports == 2) { + /* FIXME: Use VOLK here */ + for (i=0;i MAX_PORTS) { + fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", MAX_PORTS, nof_ports); + return -1; + } + if (nof_layers > MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", MAX_LAYERS, nof_layers); + return -1; + } + + switch(type) { + case SINGLE_ANTENNA: + if (nof_ports == 1 && nof_layers == 1) { + return precoding_single(x[0], y[0], nof_symbols); + } else { + fprintf(stderr, "Number of ports and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case TX_DIVERSITY: + if (nof_ports == nof_layers) { + return precoding_diversity(y, x, nof_ports, nof_symbols); + } else { + fprintf(stderr, "Error number of layers must equal number of ports in transmit diversity\n"); + return -1; + } + case SPATIAL_MULTIPLEX: + fprintf(stderr, "Spatial multiplexing not supported\n"); + return -1; + } + return 0; +} + +/* ZF detector */ +int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols) { + vec_div_ccc(y, ce, x, nof_symbols); + return nof_symbols; +} + +/* ZF detector */ +int predecoding_diversity_zf(cf_t *y[MAX_PORTS], cf_t *ce[MAX_PORTS], + cf_t *x[MAX_LAYERS], int nof_ports, int nof_symbols) { int i; cf_t h0, h1, r0, r1; float hh; - - switch(nof_ports) { - case 1: - vec_div_ccc(y[0], ce[0], x[0], nof_symbols); - break; - case 2: - switch(type) { - case TX_DIVERSITY: - /* FIXME: Use VOLK here */ - // 6.3.4.3 - for (i=0;i MAX_PORTS) { + fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", MAX_PORTS, nof_ports); + return -1; + } + if (nof_layers > MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", MAX_LAYERS, nof_layers); + return -1; + } + + + switch(type) { + case SINGLE_ANTENNA: + if (nof_ports == 1 && nof_layers == 1) { + return predecoding_single_zf(y[0], ce[0], x[0], nof_symbols); + } else{ + fprintf(stderr, "Number of ports and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case TX_DIVERSITY: + if (nof_ports == nof_layers) { + return predecoding_diversity_zf(y, ce, x, nof_ports, nof_symbols); + } else { + fprintf(stderr, "Error number of layers must equal number of ports in transmit diversity\n"); + return -1; + } break; - default: - printf("Error: Unsupported nof_ports=%d\n", nof_ports); - return; + case SPATIAL_MULTIPLEX: + fprintf(stderr, "Spatial multiplexing not supported\n"); + return -1; } + return 0; } + diff --git a/lte/lib/mimo/test/CMakeLists.txt b/lte/lib/mimo/test/CMakeLists.txt new file mode 100644 index 000000000..031f29175 --- /dev/null +++ b/lte/lib/mimo/test/CMakeLists.txt @@ -0,0 +1,65 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# LAYER MAPPING TEST +######################################################################## + +ADD_EXECUTABLE(layermap_test layermap_test.c) +TARGET_LINK_LIBRARIES(layermap_test lte) + +ADD_TEST(layermap_single layermap_test -n 1000 -m single -c 1 -l 1) + +ADD_TEST(layermap_diversity_2 layermap_test -n 1000 -m diversity -c 1 -l 2) +ADD_TEST(layermap_diversity_4 layermap_test -n 1000 -m diversity -c 1 -l 4) + +ADD_TEST(layermap_multiplex_11 layermap_test -n 1000 -m multiplex -c 1 -l 1) +ADD_TEST(layermap_multiplex_12 layermap_test -n 1000 -m multiplex -c 1 -l 2) +ADD_TEST(layermap_multiplex_13 layermap_test -n 1002 -m multiplex -c 1 -l 3) +ADD_TEST(layermap_multiplex_14 layermap_test -n 1000 -m multiplex -c 1 -l 4) +ADD_TEST(layermap_multiplex_15 layermap_test -n 1000 -m multiplex -c 1 -l 5) +ADD_TEST(layermap_multiplex_16 layermap_test -n 1002 -m multiplex -c 1 -l 6) +ADD_TEST(layermap_multiplex_17 layermap_test -n 994 -m multiplex -c 1 -l 7) +ADD_TEST(layermap_multiplex_18 layermap_test -n 1000 -m multiplex -c 1 -l 8) + + +ADD_TEST(layermap_multiplex_22 layermap_test -n 1000 -m multiplex -c 2 -l 2) +ADD_TEST(layermap_multiplex_23 layermap_test -n 1002 -m multiplex -c 2 -l 3) +ADD_TEST(layermap_multiplex_24 layermap_test -n 1000 -m multiplex -c 2 -l 4) +ADD_TEST(layermap_multiplex_25 layermap_test -n 1002 -m multiplex -c 2 -l 5) +ADD_TEST(layermap_multiplex_26 layermap_test -n 1002 -m multiplex -c 2 -l 6) +ADD_TEST(layermap_multiplex_27 layermap_test -n 1000 -m multiplex -c 2 -l 7) +ADD_TEST(layermap_multiplex_28 layermap_test -n 1000 -m multiplex -c 2 -l 8) + + +######################################################################## +# LAYER MAPPING TEST +######################################################################## + + +#ADD_EXECUTABLE(precoding_test precoding_test.c) +#TARGET_LINK_LIBRARIES(precoding_test lte) + + + + + + diff --git a/lte/lib/mimo/test/layermap_test.c b/lte/lib/mimo/test/layermap_test.c new file mode 100644 index 000000000..bcb75d7e1 --- /dev/null +++ b/lte/lib/mimo/test/layermap_test.c @@ -0,0 +1,163 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int nof_symbols = 1000; +int nof_cw = 1, nof_layers = 1; +char *mimo_type_name = NULL; + +void usage(char *prog) { + printf("Usage: %s -m [single|diversity|multiplex] -c [nof_cw] -l [nof_layers]\n", prog); + printf("\t-n num_symbols [Default %d]\n", nof_symbols); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "mcln")) != -1) { + switch (opt) { + case 'n': + nof_symbols = atoi(argv[optind]); + break; + case 'c': + nof_cw = atoi(argv[optind]); + break; + case 'l': + nof_layers = atoi(argv[optind]); + break; + case 'm': + mimo_type_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!mimo_type_name) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int i, j, num_errors, symbols_layer; + cf_t *d[MAX_CODEWORDS], *x[MAX_LAYERS], *dp[MAX_CODEWORDS]; + mimo_type_t type; + int nof_symb_cw[MAX_CODEWORDS]; + int n[2]; + + parse_args(argc, argv); + + if (lte_str2mimotype(mimo_type_name, &type)) { + fprintf(stderr, "Invalid MIMO type %s\n", mimo_type_name); + exit(-1); + } + + if (nof_cw > 1) { + n[0] = nof_layers / nof_cw; + n[1] = nof_layers - n[0]; + nof_symb_cw[0] = nof_symbols * n[0]; + nof_symb_cw[1] = nof_symbols * n[1]; + } else { + nof_symb_cw[0] = nof_symbols; + nof_symb_cw[1] = 0; + } + + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int nof_symbols = 1000; +int nof_cw = 1, nof_layers = 1; +char *mimo_type_name = NULL; + +void usage(char *prog) { + printf("Usage: %s -m [single|diversity|multiplex] -c [nof_cw] -l [nof_layers]\n", prog); + printf("\t-n num_symbols [Default %d]\n", nof_symbols); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "mcln")) != -1) { + switch (opt) { + case 'n': + nof_symbols = atoi(argv[optind]); + break; + case 'c': + nof_cw = atoi(argv[optind]); + break; + case 'l': + nof_layers = atoi(argv[optind]); + break; + case 'm': + mimo_type_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!mimo_type_name) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int i, j, num_errors, symbols_layer; + cf_t *d[MAX_CODEWORDS], *x[MAX_LAYERS], *dp[MAX_CODEWORDS]; + mimo_type_t type; + int nof_symb_cw[MAX_CODEWORDS]; + int n[2]; + + parse_args(argc, argv); + + if (lte_str2mimotype(mimo_type_name, &type)) { + fprintf(stderr, "Invalid MIMO type %s\n", mimo_type_name); + exit(-1); + } + + if (nof_cw > 1) { + n[0] = nof_layers / nof_cw; + n[1] = nof_layers - n[0]; + nof_symb_cw[0] = nof_symbols * n[0]; + nof_symb_cw[1] = nof_symbols * n[1]; + } else { + nof_symb_cw[0] = nof_symbols; + nof_symb_cw[1] = 0; + } + + for (i=0;itable = table; } -int demod_hard_demodulate(demod_hard_t* q, const cf_t* symbols, char *bits, int nsymbols) { +int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols) { int nbits=-1; switch(q->table) { @@ -68,7 +68,7 @@ int demod_hard_demodulate(demod_hard_t* q, const cf_t* symbols, char *bits, int int demod_hard_initialize(demod_hard_hl* hl) { demod_hard_init(&hl->obj); - demod_hard_table(&hl->obj,hl->init.std); + demod_hard_table_set(&hl->obj,hl->init.std); return 0; } diff --git a/lte/lib/modem/test/CMakeLists.txt b/lte/lib/modem/test/CMakeLists.txt new file mode 100644 index 000000000..7b1689b17 --- /dev/null +++ b/lte/lib/modem/test/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# MODEM TEST +######################################################################## + +ADD_EXECUTABLE(modem_test modem_test.c) +TARGET_LINK_LIBRARIES(modem_test lte) + +ADD_TEST(modem_bpsk modem_test -n 1020 -m 1) +ADD_TEST(modem_qpsk modem_test -n 1020 -m 2) +ADD_TEST(modem_qam16 modem_test -n 1020 -m 4) +ADD_TEST(modem_qam64 modem_test -n 1020 -m 6) + +ADD_TEST(modem_bpsk_soft modem_test -n 1020 -m 1 -s) +ADD_TEST(modem_qpsk_soft modem_test -n 1020 -m 2 -s) +ADD_TEST(modem_qam16_soft modem_test -n 1020 -m 4 -s) +ADD_TEST(modem_qam64_soft modem_test -n 1020 -m 6 -s) + + + diff --git a/lte/lib/modem/test/modem_test.c b/lte/lib/modem/test/modem_test.c new file mode 100644 index 000000000..1582b611a --- /dev/null +++ b/lte/lib/modem/test/modem_test.c @@ -0,0 +1,184 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int num_bits = 1000; +enum modem_std modulation; +bool soft_output = false, soft_exact = false; + +void usage(char *prog) { + printf("Usage: %s [nmse]\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-m modulation (1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64) [Default BPSK]\n"); + printf("\t-s soft outputs [Default hard]\n"); + printf("\t-e soft outputs exact algorithm [Default approx]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nmse")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 's': + soft_output = true; + break; + case 'e': + soft_exact = true; + break; + case 'm': + switch(atoi(argv[optind])) { + case 1: + modulation = LTE_BPSK; + break; + case 2: + modulation = LTE_QPSK; + break; + case 4: + modulation = LTE_QAM16; + break; + case 6: + modulation = LTE_QAM64; + break; + default: + fprintf(stderr, "Invalid modulation %d. Possible values: " + "(1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64)\n", atoi(argv[optind])); + break; + } + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + int i; + modem_table_t mod; + demod_hard_t demod_hard; + demod_soft_t demod_soft; + char *input, *output; + cf_t *symbols; + float *llr; + + parse_args(argc, argv); + + /* initialize objects */ + if (modem_table_std(&mod, modulation, soft_output)) { + fprintf(stderr, "Error initializing modem table\n"); + exit(-1); + } + + /* check that num_bits is multiple of num_bits x symbol */ + if (num_bits % mod.nbits_x_symbol) { + fprintf(stderr, "Error num_bits must be multiple of %d\n", mod.nbits_x_symbol); + exit(-1); + } + + if (soft_output) { + demod_soft_init(&demod_soft); + demod_soft_table_set(&demod_soft, &mod); + demod_soft_alg_set(&demod_soft, soft_exact?EXACT:APPROX); + } else { + demod_hard_init(&demod_hard); + demod_hard_table_set(&demod_hard, modulation); + } + + /* allocate buffers */ + input = malloc(sizeof(char) * num_bits); + if (!input) { + perror("malloc"); + exit(-1); + } + output = malloc(sizeof(char) * num_bits); + if (!output) { + perror("malloc"); + exit(-1); + } + symbols = malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol); + if (!symbols) { + perror("malloc"); + exit(-1); + } + + llr = malloc(sizeof(float) * num_bits); + if (!llr) { + perror("malloc"); + exit(-1); + } + + + /* generate random data */ + srand(time(NULL)); + for (i=0;i=0 ? 1 : 0; + } + } else { + demod_hard_demodulate(&demod_hard, symbols, output, num_bits / mod.nbits_x_symbol); + } + + /* check errors */ + for (i=0;inof_symbols; + cf_t *x[MAX_LAYERS]; - cf_t *x[MAX_LAYERS], *d[MAX_CODEWORDS]; /* number of layers equals number of ports */ for (i=0;ipbch_x[i]; } memset(&x[MAX_PORTS_CTRL], 0, sizeof(cf_t*) * (MAX_LAYERS - MAX_PORTS_CTRL)); - /* always one codeword only */ - d[0] = q->pbch_d; - memset(&d[1], 0, sizeof(cf_t*) * (MAX_CODEWORDS - 1)); - /* extract symbols */ if (q->nof_symbols != pbch_get(slot1_symbols, q->pbch_symbols[0], nof_prb, @@ -441,8 +437,14 @@ int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int no INFO("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); - precoding_decode(q->pbch_symbols, q->ce, x, nant, q->nof_symbols, TX_DIVERSITY); - layermap_decode(x, d, nant, 1, q->nof_symbols/nant, TX_DIVERSITY); + /* in conctrol channels, only diversity is supported */ + if (nant == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pbch_symbols[0], q->ce[0], q->pbch_d, q->nof_symbols); + } else { + predecoding_diversity_zf(q->pbch_symbols, q->ce, x, nant, q->nof_symbols); + layerdemap_diversity(x, q->pbch_d, nant, q->nof_symbols/nant); + } /* demodulate symbols */ demod_soft_sigma_set(&q->demod, ebno); @@ -482,9 +484,10 @@ void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL] int i; int nof_bits = 2 * q->nof_symbols; - /* Set pointers for layermapping & precoding */ assert(nof_ports < MAX_PORTS_CTRL); - cf_t *x[MAX_LAYERS], *d[MAX_CODEWORDS]; + + /* Set pointers for layermapping & precoding */ + cf_t *x[MAX_LAYERS]; /* number of layers equals number of ports */ for (i=0;ipbch_d; - memset(&d[1], 0, sizeof(cf_t*) * (MAX_CODEWORDS - 1)); - - if (q->frame_idx == 0) { /* pack MIB */ pbch_mib_pack(mib, q->data); @@ -517,8 +515,12 @@ void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL] /* layer mapping & precoding */ - layermap_encode(d, x, nof_ports, 1, q->nof_symbols/nof_ports, TX_DIVERSITY); - precoding_encode(x, q->pbch_symbols, nof_ports, q->nof_symbols/nof_ports, TX_DIVERSITY); + if (nof_ports > 1) { + layermap_diversity(q->pbch_d, x, nof_ports, q->nof_symbols/nof_ports); + precoding_diversity(x, q->pbch_symbols, nof_ports, q->nof_symbols/nof_ports); + } else { + memcpy(q->pbch_symbols[0], q->pbch_d, q->nof_symbols * sizeof(cf_t)); + } /* mapping to resource elements */ diff --git a/lte/lib/ratematching/test/CMakeLists.txt b/lte/lib/ratematching/test/CMakeLists.txt new file mode 100644 index 000000000..1c4ded6df --- /dev/null +++ b/lte/lib/ratematching/test/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# RATEMATCHING TEST +######################################################################## + +ADD_EXECUTABLE(rm_conv_test rm_conv_test.c) +TARGET_LINK_LIBRARIES(rm_conv_test lte) + +ADD_TEST(rm_conv_test_1 rm_conv_test -t 480 -r 1920) +ADD_TEST(rm_conv_test_2 rm_conv_test -t 1920 -r 480) + + + diff --git a/lte/lib/ratematching/test/rm_conv_test.c b/lte/lib/ratematching/test/rm_conv_test.c new file mode 100644 index 000000000..a7d9a47e1 --- /dev/null +++ b/lte/lib/ratematching/test/rm_conv_test.c @@ -0,0 +1,134 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int nof_tx_bits=-1, nof_rx_bits=-1; + +void usage(char *prog) { + printf("Usage: %s -t nof_tx_bits -r nof_rx_bits\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "tr")) != -1) { + switch (opt) { + case 't': + nof_tx_bits = atoi(argv[optind]); + break; + case 'r': + nof_rx_bits = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (nof_tx_bits == -1) { + usage(argv[0]); + exit(-1); + } + if (nof_rx_bits == -1) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int i; + char *bits, *rm_bits; + float *rm_symbols, *unrm_symbols; + int nof_errors; + + parse_args(argc, argv); + + bits = malloc(sizeof(char) * nof_tx_bits); + if (!bits) { + perror("malloc"); + exit(-1); + } + rm_bits = malloc(sizeof(char) * nof_rx_bits); + if (!rm_bits) { + perror("malloc"); + exit(-1); + } + rm_symbols = malloc(sizeof(float) * nof_rx_bits); + if (!rm_symbols) { + perror("malloc"); + exit(-1); + } + unrm_symbols = malloc(sizeof(float) * nof_tx_bits); + if (!unrm_symbols) { + perror("malloc"); + exit(-1); + } + + for (i=0;i 0) != bits[i]) { + nof_errors++; + } + } + if (nof_rx_bits > nof_tx_bits) { + if (nof_errors) { + printf("nof_errors=%d\n", nof_errors); + exit(-1); + } + } + + free(bits); + free(rm_bits); + free(rm_symbols); + free(unrm_symbols); + + printf("Ok\n"); + exit(0); +} diff --git a/lte/lib/scrambling/test/CMakeLists.txt b/lte/lib/scrambling/test/CMakeLists.txt new file mode 100644 index 000000000..bf9709cc5 --- /dev/null +++ b/lte/lib/scrambling/test/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# SCRAMBLING TEST +######################################################################## + +ADD_EXECUTABLE(scrambling_test scrambling_test.c) +TARGET_LINK_LIBRARIES(scrambling_test lte) + +ADD_TEST(scrambling_pbch_bit scrambling_test -s PBCH -c 50) +ADD_TEST(scrambling_pbch_float scrambling_test -s PBCH -c 50 -f) +ADD_TEST(scrambling_pbch_e_bit scrambling_test -s PBCH -c 50 -e) +ADD_TEST(scrambling_pbch_e_float scrambling_test -s PBCH -c 50 -f -e) + + + diff --git a/lte/lib/scrambling/test/scrambling_test.c b/lte/lib/scrambling/test/scrambling_test.c new file mode 100644 index 000000000..8c4473bc1 --- /dev/null +++ b/lte/lib/scrambling/test/scrambling_test.c @@ -0,0 +1,165 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +char *sequence_name = NULL; +bool do_floats = false; +lte_cp_t cp = CPNORM; +int cell_id = -1; + +void usage(char *prog) { + printf("Usage: %s [ef] -c cell_id -s [PBCH, PDSCH, PDCCH, PMCH, PUCCH]\n", prog); + printf("\t -e CP extended [Default CP Normal]\n"); + printf("\t -f scramble floats [Default bits]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "csef")) != -1) { + switch (opt) { + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'e': + cp = CPEXT; + break; + case 'f': + do_floats = true; + break; + case 's': + sequence_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (cell_id == -1) { + usage(argv[0]); + exit(-1); + } + if (!sequence_name) { + usage(argv[0]); + exit(-1); + } +} + +int init_sequence(sequence_t *seq, char *name) { + if (!strcmp(name, "PBCH")) { + return sequence_pbch(seq, cp, cell_id); + } else { + fprintf(stderr, "Unsupported sequence name %s\n", name); + return -1; + } +} + + +int main(int argc, char **argv) { + int i; + sequence_t seq; + char *input_b, *scrambled_b; + float *input_f, *scrambled_f; + + parse_args(argc, argv); + + if (init_sequence(&seq, sequence_name) == -1) { + fprintf(stderr, "Error initiating sequence %s\n", sequence_name); + exit(-1); + } + + if (!do_floats) { + input_b = malloc(sizeof(char) * seq.len); + if (!input_b) { + perror("malloc"); + exit(-1); + } + scrambled_b = malloc(sizeof(char) * seq.len); + if (!scrambled_b) { + perror("malloc"); + exit(-1); + } + + for (i=0;i Date: Tue, 11 Mar 2014 21:46:54 -0500 Subject: [PATCH 16/25] Added precoding test --- lte/lib/mimo/src/precoding.c | 17 ++-- lte/lib/mimo/test/CMakeLists.txt | 8 +- lte/lib/mimo/test/layermap_test.c | 4 +- lte/lib/mimo/test/precoding_test.c | 122 ++++++++++++++++++----------- 4 files changed, 92 insertions(+), 59 deletions(-) diff --git a/lte/lib/mimo/src/precoding.c b/lte/lib/mimo/src/precoding.c index 340ef2f0d..4bb5176eb 100644 --- a/lte/lib/mimo/src/precoding.c +++ b/lte/lib/mimo/src/precoding.c @@ -46,10 +46,10 @@ int precoding_diversity(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, if (nof_ports == 2) { /* FIXME: Use VOLK here */ for (i=0;i MAX_PORTS || nof_layers > MAX_LAYERS) { + fprintf(stderr, "Invalid number of layers or ports\n"); exit(-1); } - if (nof_cw > 1) { - n[0] = nof_layers / nof_cw; - n[1] = nof_layers - n[0]; - nof_symb_cw[0] = nof_symbols * n[0]; - nof_symb_cw[1] = nof_symbols * n[1]; - } else { - nof_symb_cw[0] = nof_symbols; - nof_symb_cw[1] = 0; + if (lte_str2mimotype(mimo_type_name, &type)) { + fprintf(stderr, "Invalid MIMO type %s\n", mimo_type_name); + exit(-1); } - for (i=0;i MSE_THRESHOLD) { + printf("MSE: %f\n", mse); exit(-1); } else { printf("Ok\n"); From 6459f516d8164caf89922e46b0bc1960b3c28f50 Mon Sep 17 00:00:00 2001 From: ismagom Date: Wed, 12 Mar 2014 10:57:27 -0500 Subject: [PATCH 17/25] Fixed some minor bugs --- examples/pbch_ue.c | 21 +++++++--- examples/scan_mib.c | 10 ++--- examples/scan_pss.c | 8 ++-- lte/include/lte/modem/demod_hard.h | 2 +- lte/lib/common/src/lte.c | 2 + lte/lib/mimo/src/precoding.c | 50 ++++++++++++++++++++++-- lte/lib/ratematching/test/rm_conv_test.c | 1 + lte/lib/utils/src/cexptab.c | 3 ++ 8 files changed, 79 insertions(+), 18 deletions(-) diff --git a/examples/pbch_ue.c b/examples/pbch_ue.c index fedb9c345..e2f82bbf9 100644 --- a/examples/pbch_ue.c +++ b/examples/pbch_ue.c @@ -58,7 +58,7 @@ #define NOF_PORTS 2 float find_threshold = 40.0, track_threshold = 8.0; -int max_track_lost = 9, nof_slots = -1; +int max_track_lost = 20, nof_slots = -1; int track_len=300; char *input_file_name = NULL; int disable_plots = 0; @@ -80,7 +80,7 @@ cfo_t cfocorr; enum sync_state {FIND, TRACK}; void usage(char *prog) { - printf("Usage: %s [iagfndv]\n", prog); + printf("Usage: %s [iagfndvp]\n", prog); printf("\t-i input_file [Default use USRP]\n"); #ifndef DISABLE_UHD printf("\t-a UHD args [Default %s]\n", uhd_args); @@ -90,6 +90,7 @@ void usage(char *prog) { printf("\t UHD is disabled. CUHD library not available\n"); #endif printf("\t-n nof_frames [Default %d]\n", nof_slots); + printf("\t-p PSS threshold [Default %f]\n", find_threshold); #ifndef DISABLE_GRAPHICS printf("\t-d disable plots [Default enabled]\n"); #else @@ -100,7 +101,7 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "iagfndv")) != -1) { + while ((opt = getopt(argc, argv, "iagfndvp")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; @@ -114,6 +115,9 @@ void parse_args(int argc, char **argv) { case 'f': uhd_freq = atof(argv[optind]); break; + case 'p': + find_threshold = atof(argv[optind]); + break; case 'n': nof_slots = atoi(argv[optind]); break; @@ -423,7 +427,9 @@ int main(int argc, char **argv) { find_idx += track_idx - track_len; if (nslot != sync_get_slot_id(&strack)) { INFO("Expected slot %d but got %d\n", nslot, sync_get_slot_id(&strack)); - printf("\r\n");fflush(stdout); + printf("\r\n"); + fflush(stdout); + printf("\r\n"); state = FIND; } } @@ -431,7 +437,9 @@ int main(int argc, char **argv) { /* if we missed too many PSS go back to FIND */ if (frame_cnt - last_found > max_track_lost) { INFO("%d frames lost. Going back to FIND", frame_cnt - last_found); - printf("\r\n");fflush(stdout); + printf("\r\n"); + fflush(stdout); + printf("\r\n"); state = FIND; } @@ -447,6 +455,9 @@ int main(int argc, char **argv) { last_found = frame_cnt; if (verbose == VERBOSE_NONE) { if (!nof_found_mib) { + printf("\r\n"); + fflush(stdout); + printf("\r\n"); pbch_mib_fprint(stdout, &mib); } } diff --git a/examples/scan_mib.c b/examples/scan_mib.c index 16654ff53..90bec1080 100644 --- a/examples/scan_mib.c +++ b/examples/scan_mib.c @@ -53,11 +53,11 @@ #define IS_SIGNAL(i) (10*log10f(rssi[i]) + 30 > rssi_threshold) int band, earfcn=-1; -float find_threshold = 40.0, track_threshold = 8.0; +float find_threshold = 10.0, track_threshold = 8.0; int earfcn_start=-1, earfcn_end = -1; -float rssi_threshold = -30.0; +float rssi_threshold = -45.0; int max_track_lost=9; -int nof_frames_find=8, nof_frames_track=100, nof_samples_rssi=50000; +int nof_frames_find=20, nof_frames_track=100, nof_samples_rssi=50000; int track_len=500; cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; @@ -72,7 +72,7 @@ int *idx_v, *idx_valid, *t; float *p2a_v; void *uhd; int nof_bands; -float uhd_gain = 30.0; +float uhd_gain = 20.0; #define MAX_EARFCN 1000 lte_earfcn_t channels[MAX_EARFCN]; @@ -232,7 +232,7 @@ void base_free() { int i; #ifndef DISABLE_UHD - cuhd_close(&uhd); + cuhd_close(uhd); #endif sync_free(&sfind); diff --git a/examples/scan_pss.c b/examples/scan_pss.c index 599a6cb06..fb754acab 100644 --- a/examples/scan_pss.c +++ b/examples/scan_pss.c @@ -49,11 +49,11 @@ int band, earfcn=-1; -float find_threshold = 40.0, track_threshold = 8.0; +float find_threshold = 10.0, track_threshold = 8.0; int earfcn_start=-1, earfcn_end = -1; -float rssi_threshold = -30.0; +float rssi_threshold = -45.0; int max_track_lost=9; -int nof_frames_find=8, nof_frames_track=100, nof_samples_rssi=50000; +int nof_frames_find=20, nof_frames_track=100, nof_samples_rssi=50000; int track_len=500; cf_t *input_buffer; @@ -184,7 +184,7 @@ int base_init(int frame_length) { void base_free() { - cuhd_close(&uhd); + cuhd_close(uhd); free(input_buffer); free(idx_v); free(idx_valid); diff --git a/lte/include/lte/modem/demod_hard.h b/lte/include/lte/modem/demod_hard.h index 4525ff7a1..c7608b5d8 100644 --- a/lte/include/lte/modem/demod_hard.h +++ b/lte/include/lte/modem/demod_hard.h @@ -54,7 +54,7 @@ typedef struct { enum modem_std std; // Symbol mapping standard (see modem_table.h) } init; - const cf_t* input; + cf_t* input; int in_len; char* output; diff --git a/lte/lib/common/src/lte.c b/lte/lib/common/src/lte.c index 8147b68d1..e475cf161 100644 --- a/lte/lib/common/src/lte.c +++ b/lte/lib/common/src/lte.c @@ -30,6 +30,7 @@ #include #include +#include #include "lte/common/base.h" @@ -156,6 +157,7 @@ char *lte_mimotype2str(mimo_type_t type) { case SPATIAL_MULTIPLEX: return "multiplex"; } + return NULL; } float get_fd(struct lte_band *band, int earfcn) { diff --git a/lte/lib/mimo/src/precoding.c b/lte/lib/mimo/src/precoding.c index 4bb5176eb..fad5947c0 100644 --- a/lte/lib/mimo/src/precoding.c +++ b/lte/lib/mimo/src/precoding.c @@ -53,8 +53,29 @@ int precoding_diversity(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, } return i; } else if (nof_ports == 4) { - fprintf(stderr, "Error not implemented\n"); - return -1; + int m_ap = (nof_symbols%4)?(nof_symbols*4-2):nof_symbols*4; + for (i=0;i 0) != bits[i]) { nof_errors++; diff --git a/lte/lib/utils/src/cexptab.c b/lte/lib/utils/src/cexptab.c index 235b69389..d1e13ab93 100644 --- a/lte/lib/utils/src/cexptab.c +++ b/lte/lib/utils/src/cexptab.c @@ -68,6 +68,9 @@ void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { if (phase >= (float) h->size) { phase -= (float) h->size; } + if (phase <= 0) { + phase += (float) h->size; + } } } From f588d670adccc4c1eef7291f880b418a2a95b432 Mon Sep 17 00:00:00 2001 From: ismagom Date: Thu, 13 Mar 2014 18:03:39 +0000 Subject: [PATCH 18/25] Fixed bug when receiving all zeros samples --- examples/pbch_ue.c | 46 +++++++++++++++++++++++++---------------- lte/lib/phch/src/pbch.c | 20 ++++++++++++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/examples/pbch_ue.c b/examples/pbch_ue.c index e2f82bbf9..73d418b8c 100644 --- a/examples/pbch_ue.c +++ b/examples/pbch_ue.c @@ -57,7 +57,7 @@ #define NOF_PORTS 2 -float find_threshold = 40.0, track_threshold = 8.0; +float find_threshold = 30.0, track_threshold = 10.0; int max_track_lost = 20, nof_slots = -1; int track_len=300; char *input_file_name = NULL; @@ -65,7 +65,7 @@ int disable_plots = 0; int go_exit=0; -float uhd_freq = 2400000000.0, uhd_gain = 20.0; +float uhd_freq = 2600000000.0, uhd_gain = 20.0; char *uhd_args = ""; filesource_t fsrc; @@ -328,6 +328,7 @@ int main(int argc, char **argv) { float cfo; int n; int nof_found_mib = 0; + float timeoffset = 0; #ifdef DISABLE_UHD if (argc < 3) { @@ -398,18 +399,23 @@ int main(int argc, char **argv) { INFO("FIND %3d:\tPAR=%.2f\n", frame_cnt, sync_get_peak_to_avg(&sfind)); if (find_idx != -1) { /* if found peak, go to track and set track threshold */ - frame_cnt = -1; - last_found = 0; - sync_set_threshold(&strack, track_threshold); - sync_force_N_id_2(&strack, sync_get_N_id_2(&sfind)); cell_id = sync_get_cell_id(&sfind); - mib_decoder_init(cell_id); - nof_found_mib = 0; - nslot = sync_get_slot_id(&sfind); - nslot=(nslot+10)%20; - cfo = 0; - printf("\n"); - state = TRACK; + if (cell_id != -1) { + frame_cnt = -1; + last_found = 0; + sync_set_threshold(&strack, track_threshold); + sync_force_N_id_2(&strack, sync_get_N_id_2(&sfind)); + mib_decoder_init(cell_id); + nof_found_mib = 0; + nslot = sync_get_slot_id(&sfind); + nslot=(nslot+10)%20; + cfo = 0; + timeoffset = 0; + printf("\n"); + state = TRACK; + } else { + printf("cellid=-1\n"); + } } if (verbose == VERBOSE_NONE) { printf("Finding PSS... PAR=%.2f\r", sync_get_peak_to_avg(&sfind)); @@ -423,8 +429,10 @@ int main(int argc, char **argv) { if (track_idx != -1) { /* compute cumulative moving average CFO */ cfo = (sync_get_cfo(&strack) + frame_cnt * cfo) / (frame_cnt + 1); + /* compute cumulative moving average time offset */ + timeoffset = (float) (track_idx-track_len + timeoffset * frame_cnt) / (frame_cnt + 1); last_found = frame_cnt; - find_idx += track_idx - track_len; + find_idx = (find_idx + track_idx - track_len)%FLEN; if (nslot != sync_get_slot_id(&strack)) { INFO("Expected slot %d but got %d\n", nslot, sync_get_slot_id(&strack)); printf("\r\n"); @@ -432,6 +440,9 @@ int main(int argc, char **argv) { printf("\r\n"); state = FIND; } + } else { + /* if sync not found, adjust time offset with the averaged value */ + find_idx = (find_idx + (int) timeoffset)%FLEN; } /* if we missed too many PSS go back to FIND */ @@ -448,11 +459,10 @@ int main(int argc, char **argv) { cfo_correct(&cfocorr, input_buffer, -cfo/128); - if (nslot == 0) { + if (nslot == 0 && find_idx + 960 < FLEN) { INFO("Finding MIB at idx %d\n", find_idx); if (mib_decoder_run(&input_buffer[find_idx], &mib)) { INFO("MIB detected attempt=%d\n", frame_cnt); - last_found = frame_cnt; if (verbose == VERBOSE_NONE) { if (!nof_found_mib) { printf("\r\n"); @@ -466,8 +476,8 @@ int main(int argc, char **argv) { INFO("MIB not found attempt %d\n",frame_cnt); } if (frame_cnt) { - printf("SFN: %4d\tCFO: %+.4f KHz\tTimeOffset: %4d\tErrors: %4d/%4d\tErrorRate: %.1e\r", mib.sfn, - cfo*15, find_idx, frame_cnt-2*(nof_found_mib-1), frame_cnt, + printf("SFN: %4d, CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Errors: %4d/%4d, ErrorRate: %.1e\r", mib.sfn, + cfo*15, timeoffset/5, find_idx, frame_cnt-2*(nof_found_mib-1), frame_cnt, (float) (frame_cnt-2*(nof_found_mib-1))/frame_cnt); fflush(stdout); } diff --git a/lte/lib/phch/src/pbch.c b/lte/lib/phch/src/pbch.c index c3f98f40f..b92533bbd 100644 --- a/lte/lib/phch/src/pbch.c +++ b/lte/lib/phch/src/pbch.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "phch.h" #include "lte/phch/pbch.h" @@ -57,6 +58,7 @@ bool pbch_exists(int nframe, int nslot) { int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, bool put) { int i; cf_t *ptr; + assert(cell_id >= 0); if (put) { ptr = input; output += nof_prb * RE_X_RB / 2 - 36; @@ -110,6 +112,9 @@ int pbch_get(cf_t *slot1_data, cf_t *pbch, int nof_prb, lte_cp_t cp, int cell_id /** Initializes the PBCH channel receiver */ int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp) { int ret = -1; + if (cell_id < 0) { + return -1; + } bzero(q, sizeof(pbch_t)); q->cell_id = cell_id; q->cp = cp; @@ -374,9 +379,24 @@ int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, int n /* unrate matching */ rm_conv_rx(q->temp, q->pbch_rm_f, 4 * nof_bits, 120); + /* FIXME: If channel estimates are zero, received LLR are NaN. Check and return error */ + for (j=0;j<120;j++) { + if (isnan(q->pbch_rm_f[j]) || isinf(q->pbch_rm_f[j])) { + return 0; + } + } + /* decode */ viterbi_decode_f(&q->decoder, q->pbch_rm_f, q->data); + int c=0; + for (j=0;j<40;j++) { + c+=q->data[j]; + } + if (!c) { + c=1; + } + if (!pbch_crc_check(q->data, nof_ports)) { /* unpack MIB */ pbch_mib_unpack(q->data, mib); From 6a514e41077407a551b05b4d7cd8081cd2f5c7b7 Mon Sep 17 00:00:00 2001 From: ismagom Date: Sat, 15 Mar 2014 01:04:21 +0000 Subject: [PATCH 19/25] All tests completed --- COPYRIGHT | 27 +--- examples/CMakeLists.txt | 9 +- lte/include/lte/phch/pbch.h | 2 +- lte/lib/fec/src/viterbi.c | 8 +- lte/lib/mimo/src/precoding.c | 49 +++--- lte/lib/mimo/test/CMakeLists.txt | 3 +- lte/lib/mimo/test/precoding_test.c | 2 +- lte/lib/phch/src/pbch.c | 3 + lte/lib/phch/test/CMakeLists.txt | 42 +++++ lte/lib/phch/test/h3g.mib.dat | Bin 0 -> 230400 bytes .../lib/phch/test/pbch_file_test.c | 25 ++- lte/lib/phch/test/pbch_test.c | 129 +++++++++++++++ lte/lib/sync/test/CMakeLists.txt | 44 +++++ lte/lib/sync/test/cfo_test.c | 119 ++++++++++++++ lte/lib/sync/test/sync_test.c | 152 ++++++++++++++++++ lte/lib/utils/src/cexptab.c | 11 +- 16 files changed, 555 insertions(+), 70 deletions(-) create mode 100644 lte/lib/phch/test/CMakeLists.txt create mode 100644 lte/lib/phch/test/h3g.mib.dat rename examples/mib_test.c => lte/lib/phch/test/pbch_file_test.c (92%) create mode 100644 lte/lib/phch/test/pbch_test.c create mode 100644 lte/lib/sync/test/CMakeLists.txt create mode 100644 lte/lib/sync/test/cfo_test.c create mode 100644 lte/lib/sync/test/sync_test.c diff --git a/COPYRIGHT b/COPYRIGHT index de839a6ac..6b399f6a5 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,32 +1,7 @@ Copyright (C) 2013-2014 Ismael Gomez Miguelez, . All rights reserved. -The following copyright notices are for libraries used within Iris_Modules: +The following copyright notices are for libraries used within libLTE: ------------------------------------------------------------ -Boost Software License - Version 1.0 - August 17th, 2003 ------------------------------------------------------------ - -Permission is hereby granted, free of charge, to any person or -organization obtaining a copy of the software and accompanying -documentation covered by this license (the "Software") to use, reproduce, -display, distribute, execute, and transmit the Software, and to prepare -derivative works of the Software, and to permit third-parties to whom -the Software is furnished to do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated -by a source language processor. - -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, TITLE AND NON-INFRINGEMENT. IN NO -EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE -BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------- CLibrary.py diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a3f33e811..21d3cdba8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -30,16 +30,13 @@ target_link_libraries(hl_example lte) add_executable(ll_example ll_example.c) target_link_libraries(ll_example lte) -add_executable(synch_file synch_file.c) -target_link_libraries(synch_file lte) - ################################################################# -# TO BE MOVED TO UNIT TESTS +# Applications ################################################################# -add_executable(mib_test mib_test.c) -target_link_libraries(mib_test lte) +add_executable(synch_file synch_file.c) +target_link_libraries(synch_file lte) ################################################################# diff --git a/lte/include/lte/phch/pbch.h b/lte/include/lte/phch/pbch.h index 36cbb7315..4d0dd1366 100644 --- a/lte/include/lte/phch/pbch.h +++ b/lte/include/lte/phch/pbch.h @@ -90,7 +90,7 @@ void pbch_free(pbch_t *q); int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int nof_prb, float ebno, pbch_mib_t *mib); void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL], int nof_prb, int nof_ports); - +void pbch_decode_reset(pbch_t *q); void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib); bool pbch_exists(int nframe, int nslot); diff --git a/lte/lib/fec/src/viterbi.c b/lte/lib/fec/src/viterbi.c index 614fbb48a..0b7351a10 100644 --- a/lte/lib/fec/src/viterbi.c +++ b/lte/lib/fec/src/viterbi.c @@ -175,7 +175,13 @@ void viterbi_free(viterbi_t *q) { /* symbols are real-valued */ int viterbi_decode_f(viterbi_t *q, float *symbols, char *data) { - vec_quant_fuc(symbols, q->symbols_uc, 32, 127.5, 255, 3 * (q->framebits + q->K - 1)); + int len; + if (q->tail_biting) { + len = 3 * q->framebits; + } else { + len = 3 * (q->framebits + q->K - 1); + } + vec_quant_fuc(symbols, q->symbols_uc, 32, 127.5, 255, len); return q->decode(q, q->symbols_uc, data); } diff --git a/lte/lib/mimo/src/precoding.c b/lte/lib/mimo/src/precoding.c index fad5947c0..dd136a01a 100644 --- a/lte/lib/mimo/src/precoding.c +++ b/lte/lib/mimo/src/precoding.c @@ -51,10 +51,11 @@ int precoding_diversity(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_ports, y[0][2*i+1] = x[1][i]/sqrtf(2); y[1][2*i+1] = conjf(x[0][i])/sqrtf(2); } - return i; + return 2*i; } else if (nof_ports == 4) { - int m_ap = (nof_symbols%4)?(nof_symbols*4-2):nof_symbols*4; - for (i=0;ipbch_llr) { free(q->pbch_llr); } + if (q->temp) { + free(q->temp); + } if (q->pbch_rm_f) { free(q->pbch_rm_f); } diff --git a/lte/lib/phch/test/CMakeLists.txt b/lte/lib/phch/test/CMakeLists.txt new file mode 100644 index 000000000..d9e11a510 --- /dev/null +++ b/lte/lib/phch/test/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# PBCH TEST +######################################################################## + +ADD_EXECUTABLE(pbch_test pbch_test.c) +TARGET_LINK_LIBRARIES(pbch_test lte) + +ADD_TEST(pbch_test_6 pbch_test -p 6 -c 100) +ADD_TEST(pbch_test_50 pbch_test -p 50 -c 50) + +######################################################################## +# PBCH FILE TEST +######################################################################## + +ADD_EXECUTABLE(pbch_file_test pbch_file_test.c) +TARGET_LINK_LIBRARIES(pbch_file_test lte) + +ADD_TEST(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/h3g.mib.dat) + + + diff --git a/lte/lib/phch/test/h3g.mib.dat b/lte/lib/phch/test/h3g.mib.dat new file mode 100644 index 0000000000000000000000000000000000000000..92d4a06ba4f7ed64705b1a5b38a6def91d46c318 GIT binary patch literal 230400 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 z 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%0DRcx^Mx2%tF9MHu47%_0hDY}q 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
-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
C=>Lz1iiMr=2mx_upbvoxPeAzuG=%2 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 literal 0 HcmV?d00001 diff --git a/examples/mib_test.c b/lte/lib/phch/test/pbch_file_test.c similarity index 92% rename from examples/mib_test.c rename to lte/lib/phch/test/pbch_file_test.c index 026b1d7cd..0d30f0b7e 100644 --- a/examples/mib_test.c +++ b/lte/lib/phch/test/pbch_file_test.c @@ -142,7 +142,7 @@ int base_init() { return 0; } -void base_close() { +void base_free() { int i; filesource_free(&fsrc); @@ -159,6 +159,10 @@ void base_close() { chest_free(&chest); lte_fft_free(&fft); + cfo_free(&cfocorr); + + pbch_free(&pbch); + } @@ -278,6 +282,11 @@ int main(int argc, char **argv) { switch(state) { case SYNC: INFO("State Sync, Slot idx=%d\n", frame_cnt); + fprintf(fmatlab, "input_buffer="); + vec_sc_prod_cfc(input_buffer, 1000.0, input_buffer, FLEN); + vec_fprint_c(fmatlab, input_buffer, FLEN); + vec_sc_prod_cfc(input_buffer, 0.0001, input_buffer, FLEN); + fprintf(fmatlab, ";\n"); mib_idx = sync_run(&synch, input_buffer); if (mib_idx != -1) { cell_id = sync_get_cell_id(&synch); @@ -320,8 +329,16 @@ int main(int argc, char **argv) { frame_cnt++; } - base_close(); + base_free(); + + fftwf_cleanup(); - printf("Exit\n"); - exit(0); + if (mib.nof_ports == 2 && mib.nof_prb == 50 && mib.phich_length == NORMAL + && mib.phich_resources == R_1 && mib.sfn == 28) { + printf("This is the h3g.mib.dat file\n"); + exit(0); + } else { + printf("Exit\n"); + exit(-1); + } } diff --git a/lte/lib/phch/test/pbch_test.c b/lte/lib/phch/test/pbch_test.c new file mode 100644 index 000000000..dec99d797 --- /dev/null +++ b/lte/lib/phch/test/pbch_test.c @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "lte.h" + +int cell_id = 1; +int nof_prb = 6; + + +void usage(char *prog) { + printf("Usage: %s [cpv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell_id); + printf("\t-p nof_prb [Default %d]\n", nof_prb); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpv")) != -1) { + switch(opt) { + case 'p': + nof_prb = atoi(argv[optind]); + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + pbch_t pbch; + cf_t *buffer = NULL; + pbch_mib_t mib_tx, mib_rx; + int i, j; + cf_t *ce[MAX_PORTS_CTRL]; + int nof_re; + cf_t *slot1_symbols[MAX_PORTS_CTRL]; + + parse_args(argc,argv); + + nof_re = CPNORM_NSYMB * nof_prb * RE_X_RB; + + /* init memory */ + buffer = malloc(sizeof(cf_t) * nof_re); + if (!buffer) { + perror("malloc"); + exit(-1); + } + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +#define MAX_MSE 0.1 + +float freq = 0; +int num_samples = 1000; + +void usage(char *prog) { + printf("Usage: %s -f freq -n num_samples\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nf")) != -1) { + switch (opt) { + case 'n': + num_samples = atoi(argv[optind]); + break; + case 'f': + freq = atof(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int i; + cf_t *input, *output; + cfo_t cfocorr; + float mse; + + if (argc < 5) { + usage(argv[0]); + exit(-1); + } + + parse_args(argc, argv); + + input = malloc(sizeof(cf_t) * num_samples); + if (!input) { + perror("malloc"); + exit(-1); + } + output = malloc(sizeof(cf_t) * num_samples); + if (!output) { + perror("malloc"); + exit(-1); + } + + for (i=0;i MAX_MSE) { + printf("MSE too large\n"); + exit(-1); + } else { + printf("Ok\n"); + exit(0); + } +} diff --git a/lte/lib/sync/test/sync_test.c b/lte/lib/sync/test/sync_test.c new file mode 100644 index 000000000..348910894 --- /dev/null +++ b/lte/lib/sync/test/sync_test.c @@ -0,0 +1,152 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte.h" + +int cell_id = -1, offset = 0; + +#define FLEN 9600 + +void usage(char *prog) { + printf("Usage: %s [co]\n", prog); + printf("\t-c cell_id [Default check for all]\n"); + printf("\t-o offset [Default %d]\n", offset); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "co")) != -1) { + switch (opt) { + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'o': + offset = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int N_id_2, ns, find_ns; + cf_t *buffer, *fft_buffer; + cf_t pss_signal[PSS_LEN]; + float sss_signal0[SSS_LEN]; // for subframe 0 + float sss_signal5[SSS_LEN]; // for subframe 5 + int cid, max_cid, find_idx; + sync_t sync; + lte_fft_t ifft; + + parse_args(argc, argv); + + buffer = malloc(sizeof(cf_t) * FLEN); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(sizeof(cf_t) * 2 * FLEN); + if (!fft_buffer) { + perror("malloc"); + exit(-1); + } + + if (lte_ifft_init(&ifft, CPNORM, 6)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + + if (sync_init(&sync, FLEN)) { + fprintf(stderr, "Error initiating PSS/SSS\n"); + return -1; + } + + sync_set_threshold(&sync, 20); + sync_force_N_id_2(&sync, -1); + + if (cell_id == -1) { + cid = 0; + max_cid = 149; + } else { + cid = cell_id; + max_cid = cell_id; + } + while(cid <= max_cid) { + N_id_2 = cid%3; + + /* Generate PSS/SSS signals */ + pss_generate(pss_signal, N_id_2); + sss_generate(sss_signal0, sss_signal5, cid); + + for (ns=0;ns<2;ns++) { + memset(buffer, 0, sizeof(cf_t) * FLEN); + pss_put_slot(pss_signal, buffer, 6, CPNORM); + sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, CPNORM); + + /* Transform to OFDM symbols */ + memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN); + lte_ifft_run(&ifft, buffer, &fft_buffer[offset]); + + find_idx = sync_run(&sync, fft_buffer); + find_ns = sync_get_slot_id(&sync); + printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset, + ns, find_ns); + if (find_idx != offset + 960) { + printf("offset != find_offset: %d != %d\n", find_idx, offset + 960); + exit(-1); + } + if (ns*10 != find_ns) { + printf("ns != find_ns\n", 10 * ns, find_ns); + exit(-1); + } + } + cid++; + } + + free(fft_buffer); + free(buffer); + + sync_free(&sync); + lte_ifft_free(&ifft); + + fftwf_cleanup(); + + printf("Ok\n"); + exit(0); +} diff --git a/lte/lib/utils/src/cexptab.c b/lte/lib/utils/src/cexptab.c index d1e13ab93..158a19aaa 100644 --- a/lte/lib/utils/src/cexptab.c +++ b/lte/lib/utils/src/cexptab.c @@ -62,15 +62,16 @@ void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { float phase=0; for (i = 0; i < len; i++) { - idx = (unsigned int) phase; - x[i] = h->tab[idx]; - phase += phase_inc; - if (phase >= (float) h->size) { + while (phase >= (float) h->size) { phase -= (float) h->size; } - if (phase <= 0) { + while (phase < 0) { phase += (float) h->size; } + idx = (unsigned int) phase; + x[i] = h->tab[idx]; + phase += phase_inc; + } } From 0130fbe6bc3d20f4da7df29f454c623ba6954585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20G=C3=B3mez-Miguelez?= Date: Fri, 21 Mar 2014 12:23:54 +0000 Subject: [PATCH 20/25] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c4f592b5..a5b406e30 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ The library currently uses Ettus Universal Hardware Driver (UHD). Thus, any hard Download & Install Instructions ================================= -* Requirements: Currently, the library requires libfftw, although we plan make this dependency optional in the future. Also, QT4 and Qwt6 are needed for graphics visualization. If they are not present, though, compilation is still possible although graphics will be disabled. +* Requirements: Currently, the library requires libfftw, although we plan make this dependency optional in the future. Also, QT4 is needed for graphics visualization. Compilation is possible without QT4, although graphics will be disabled. + +To install QT4 and libfftw use your distribution packet management system, for instance in ubuntu you can run: `sudo apt-get install libfftw3-dev libqt4-dev` to install all requirements. ``` git clone https://github.com/ismagom/libLTE.git From f0f2eb3e7f185a8a643e8c87246034f1f0467d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ismael=20G=C3=B3mez-Miguelez?= Date: Fri, 21 Mar 2014 12:24:30 +0000 Subject: [PATCH 21/25] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a5b406e30..0fc8f3b7f 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Download & Install Instructions To install QT4 and libfftw use your distribution packet management system, for instance in ubuntu you can run: `sudo apt-get install libfftw3-dev libqt4-dev` to install all requirements. + +Finally, to download and build libLTE, just run: ``` git clone https://github.com/ismagom/libLTE.git cd libLTE From 06b3741984f77671921413fa84a4be494773b07c Mon Sep 17 00:00:00 2001 From: ismagom Date: Fri, 21 Mar 2014 13:41:28 +0000 Subject: [PATCH 22/25] Updated README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0fc8f3b7f..b7a2d5dd3 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,12 @@ Examples Setup one or two computers connected to two USRP or UHD-compatible hardware. From the eNodeB, type ``` -examples/enodeb_bch -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] +examples/pbch_enodeb -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] ``` From the UE, type ``` -examples/mib_track -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] +examples/pbch_ue -f [frequency_in_Hz] -c [cell_id] [-a [UHD args]] [-h for more commands] ``` And the output should look something like the following video. In this example, we removed the transmitter and receiver antennas in the middle of the demonstration, showing how reception is still possible (despite with some erros). @@ -70,12 +70,12 @@ If you don't have a pair of USRP, you can also test the demo by writing the samp From the eNodeB, type ``` -examples/enodeb_bch -o [output_file] -c [cell_id] [-h for more commands] +examples/pbch_enodeb -o [output_file] -c [cell_id] [-h for more commands] ``` From the UE, type ``` -examples/mib_track -i [input_file] -c [cell_id] [-h for more commands] +examples/pbch_ue -i [input_file] -c [cell_id] [-h for more commands] ``` @@ -85,7 +85,7 @@ This program uses any hardware supported by the UHD driver to scan an LTE band f For instance, the command: -``` examples/mib_scan_usrp -b 3 ``` +``` examples/scan_mib -b 3 ``` Scans the LTE band 3 (1805 to 1880 MHz). Note that you need a hardware supporting these frequencies (e.g. SBX daughterboard for USRP). For more command arguments, type ``` examples/mib_scan_usrp -h ``` From de553d34529c92e6987a3580f93dd5ecae032818 Mon Sep 17 00:00:00 2001 From: ismagom Date: Thu, 27 Mar 2014 16:31:25 +0000 Subject: [PATCH 23/25] Added PCFICH and PHICH Tx/Rx. --- examples/pbch_enodeb.c | 2 +- examples/pbch_ue.c | 6 +- examples/scan_mib.c | 4 +- examples/scan_pss.c | 4 +- examples/synch_file.c | 8 +- lte/include/lte.h | 3 + lte/include/lte/common/base.h | 5 + lte/include/lte/common/sequence.h | 3 +- lte/include/lte/phch/pbch.h | 7 +- lte/include/lte/phch/pcfich.h | 82 +++ lte/include/lte/phch/phich.h | 96 +++ lte/include/lte/phch/regs.h | 93 +++ lte/include/lte/scrambling/scrambling.h | 25 +- lte/include/lte/sync/pss.h | 7 +- lte/include/lte/sync/sync.h | 45 +- lte/lib/ch_estimation/test/chest_test.c | 4 +- lte/lib/phch/src/pbch.c | 39 +- lte/lib/phch/src/pcfich.c | 261 ++++++++ lte/lib/phch/src/phich.c | 376 +++++++++++ lte/lib/phch/src/{phch.c => prb.c} | 14 +- lte/lib/phch/src/{phch.h => prb.h} | 6 +- lte/lib/phch/src/regs.c | 625 ++++++++++++++++++ .../phch_sequence.c => phch/src/sequences.c} | 21 + lte/lib/phch/test/CMakeLists.txt | 54 +- lte/lib/phch/test/h3g.mib.dat | Bin 230400 -> 0 bytes lte/lib/phch/test/pbch_file_test.c | 256 ++----- lte/lib/phch/test/pbch_test.c | 43 +- lte/lib/phch/test/pcfich_file_test.c | 251 +++++++ lte/lib/phch/test/pcfich_test.c | 157 +++++ lte/lib/phch/test/phich_test.c | 205 ++++++ lte/lib/phch/test/signal.1.92M.dat | Bin 0 -> 76808 bytes lte/lib/phch/test/signal.10M.dat | Bin 0 -> 61448 bytes lte/lib/scrambling/src/scrambling.c | 58 +- lte/lib/scrambling/test/scrambling_test.c | 8 +- lte/lib/sync/src/sync.c | 82 ++- lte/lib/sync/test/CMakeLists.txt | 2 + lte/lib/sync/test/sync_test.c | 26 +- matlab/rate_algorithm.m | 20 + 38 files changed, 2581 insertions(+), 317 deletions(-) create mode 100644 lte/include/lte/phch/pcfich.h create mode 100644 lte/include/lte/phch/phich.h create mode 100644 lte/include/lte/phch/regs.h create mode 100644 lte/lib/phch/src/pcfich.c create mode 100644 lte/lib/phch/src/phich.c rename lte/lib/phch/src/{phch.c => prb.c} (80%) rename lte/lib/phch/src/{phch.h => prb.h} (83%) create mode 100644 lte/lib/phch/src/regs.c rename lte/lib/{common/src/phch_sequence.c => phch/src/sequences.c} (72%) delete mode 100644 lte/lib/phch/test/h3g.mib.dat create mode 100644 lte/lib/phch/test/pcfich_file_test.c create mode 100644 lte/lib/phch/test/pcfich_test.c create mode 100644 lte/lib/phch/test/phich_test.c create mode 100644 lte/lib/phch/test/signal.1.92M.dat create mode 100644 lte/lib/phch/test/signal.10M.dat create mode 100644 matlab/rate_algorithm.m diff --git a/examples/pbch_enodeb.c b/examples/pbch_enodeb.c index 8a01c51d2..be6dbbb11 100644 --- a/examples/pbch_enodeb.c +++ b/examples/pbch_enodeb.c @@ -220,7 +220,7 @@ int main(int argc, char **argv) { mib.nof_ports = 1; mib.nof_prb = 6; - mib.phich_length = NORMAL; + mib.phich_length = PHICH_NORM; mib.phich_resources = R_1; mib.sfn = 0; diff --git a/examples/pbch_ue.c b/examples/pbch_ue.c index 73d418b8c..c63a5f9c4 100644 --- a/examples/pbch_ue.c +++ b/examples/pbch_ue.c @@ -344,8 +344,8 @@ int main(int argc, char **argv) { exit(-1); } - sync_pss_det_peakmean(&sfind); - sync_pss_det_peakmean(&strack); + sync_pss_det_peak_to_avg(&sfind); + sync_pss_det_peak_to_avg(&strack); if (!input_file_name) { #ifndef DISABLE_UHD @@ -405,6 +405,7 @@ int main(int argc, char **argv) { last_found = 0; sync_set_threshold(&strack, track_threshold); sync_force_N_id_2(&strack, sync_get_N_id_2(&sfind)); + sync_force_cp(&strack, sync_get_cp(&sfind)); mib_decoder_init(cell_id); nof_found_mib = 0; nslot = sync_get_slot_id(&sfind); @@ -468,6 +469,7 @@ int main(int argc, char **argv) { printf("\r\n"); fflush(stdout); printf("\r\n"); + printf(" - Phy. CellId:\t%d\n", cell_id); pbch_mib_fprint(stdout, &mib); } } diff --git a/examples/scan_mib.c b/examples/scan_mib.c index 90bec1080..e94ad6c18 100644 --- a/examples/scan_mib.c +++ b/examples/scan_mib.c @@ -371,8 +371,8 @@ int main(int argc, char **argv) { exit(-1); } - sync_pss_det_peakmean(&sfind); - sync_pss_det_peakmean(&strack); + sync_pss_det_peak_to_avg(&sfind); + sync_pss_det_peak_to_avg(&strack); nof_bands = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); printf("RSSI scan: %d freqs in band %d, RSSI threshold %.2f dBm\n", nof_bands, band, rssi_threshold); diff --git a/examples/scan_pss.c b/examples/scan_pss.c index fb754acab..2afec3330 100644 --- a/examples/scan_pss.c +++ b/examples/scan_pss.c @@ -282,13 +282,13 @@ int main(int argc, char **argv) { fprintf(stderr, "Error initiating PSS/SSS\n"); exit(-1); } - sync_pss_det_peakmean(&sfind); + sync_pss_det_peak_to_avg(&sfind); if (sync_init(&strack, track_len)) { fprintf(stderr, "Error initiating PSS/SSS\n"); exit(-1); } - sync_pss_det_peakmean(&strack); + sync_pss_det_peak_to_avg(&strack); nof_bands = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); printf("RSSI scan: %d freqs in band %d, RSSI threshold %.2f dBm\n", nof_bands, band, rssi_threshold); diff --git a/examples/synch_file.c b/examples/synch_file.c index db9a8338b..a7157b539 100644 --- a/examples/synch_file.c +++ b/examples/synch_file.c @@ -43,7 +43,7 @@ int out_N_id_2 = 0, force_N_id_2=-1; float force_cfo = CFO_AUTO; void usage(char *prog) { - printf("Usage: %s [olntsNfc] -i input_file\n", prog); + printf("Usage: %s [olntsNfcv] -i input_file\n", prog); printf("\t-o output_file [Default %s]\n", output_file_name); printf("\t-l frame_length [Default %d]\n", frame_length); printf("\t-n number of frames [Default %d]\n", nof_slots); @@ -52,11 +52,12 @@ void usage(char *prog) { printf("\t-N out_N_id_2 [Default %d]\n", out_N_id_2); printf("\t-f force_N_id_2 [Default %d]\n", force_N_id_2); printf("\t-c force_cfo [Default disabled]\n"); + printf("\t-v verbose\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "ionltsNfc")) != -1) { + while ((opt = getopt(argc, argv, "ionltsNfcv")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; @@ -85,6 +86,9 @@ void parse_args(int argc, char **argv) { case 'c': force_cfo = atof(argv[optind]); break; + case 'v': + verbose++; + break; default: usage(argv[0]); exit(-1); diff --git a/lte/include/lte.h b/lte/include/lte.h index 520003cac..53415a8ef 100644 --- a/lte/include/lte.h +++ b/lte/include/lte.h @@ -71,7 +71,10 @@ #include "lte/mimo/precoding.h" #include "lte/mimo/layermap.h" +#include "lte/phch/regs.h" #include "lte/phch/pbch.h" +#include "lte/phch/pcfich.h" +#include "lte/phch/phich.h" #include "lte/ratematching/rm_conv.h" diff --git a/lte/include/lte/common/base.h b/lte/include/lte/common/base.h index 8212b43d5..5592c3d5b 100644 --- a/lte/include/lte/common/base.h +++ b/lte/include/lte/common/base.h @@ -61,9 +61,11 @@ typedef enum {CPNORM, CPEXT} lte_cp_t; #define SLOT_LEN_CPNORM(symbol_sz) (symbol_sz+CP(symbol_sz,CPNORM_0_LEN)+(CPNORM_NSYMB-1)*(symbol_sz+CP(symbol_sz,CPNORM_LEN))) #define SLOT_LEN_CPEXT(symbol_sz) (CPEXT_NSYMB*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) +#define SLOT_LEN(symbol_sz, cp) CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz) #define SF_LEN_CPNORM(symbol_sz) 2*SLOT_LEN_CPNORM(symbol_sz) #define SF_LEN_CPEXT(symbol_sz) 2*SLOT_LEN_CPEXT(symbol_sz) +#define SF_LEN(symbol_sz, cp) 2*SLOT_LEN(cp, symbol_sz) #define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) #define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) @@ -88,6 +90,9 @@ typedef enum { SINGLE_ANTENNA,TX_DIVERSITY, SPATIAL_MULTIPLEX } mimo_type_t; +typedef enum { PHICH_NORM, PHICH_EXT} phich_length_t; +typedef enum { R_1_6, R_1_2, R_1, R_2} phich_resources_t; + typedef struct { int id; diff --git a/lte/include/lte/common/sequence.h b/lte/include/lte/common/sequence.h index 31f480a50..c52f44602 100644 --- a/lte/include/lte/common/sequence.h +++ b/lte/include/lte/common/sequence.h @@ -42,6 +42,7 @@ void sequence_free(sequence_t *q); int sequence_LTEPRS(sequence_t *q, int len, int seed); int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id); -int sequence_pbch_crc(sequence_t *seq, int nof_ports); +int sequence_pcfich(sequence_t *seq, int nslot, int cell_id); +int sequence_phich(sequence_t *seq, int nslot, int cell_id); #endif diff --git a/lte/include/lte/phch/pbch.h b/lte/include/lte/phch/pbch.h index 4d0dd1366..081388289 100644 --- a/lte/include/lte/phch/pbch.h +++ b/lte/include/lte/phch/pbch.h @@ -45,15 +45,12 @@ typedef _Complex float cf_t; -enum phich_length { NORMAL, EXTENDED}; -enum phich_resources { R_1_6, R_1_2, R_1, R_2}; - typedef struct { int nof_ports; int nof_prb; int sfn; - enum phich_length phich_length; - enum phich_resources phich_resources; + phich_length_t phich_length; + phich_resources_t phich_resources; }pbch_mib_t; /* PBCH object */ diff --git a/lte/include/lte/phch/pcfich.h b/lte/include/lte/phch/pcfich.h new file mode 100644 index 000000000..df8d87e17 --- /dev/null +++ b/lte/include/lte/phch/pcfich.h @@ -0,0 +1,82 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef PCFICH_ +#define PCFICH_ + +#include "lte/common/base.h" +#include "lte/mimo/precoding.h" +#include "lte/mimo/layermap.h" +#include "lte/modem/mod.h" +#include "lte/modem/demod_hard.h" +#include "lte/scrambling/scrambling.h" +#include "lte/phch/regs.h" + +#define PCFICH_CFI_LEN 32 +#define PCFICH_RE PCFICH_CFI_LEN/2 +#define PCFICH_MAX_DISTANCE 5 + +typedef _Complex float cf_t; + +/* PCFICH object */ +typedef struct { + int cell_id; + lte_cp_t cp; + int nof_symbols; + int nof_prb; + int nof_tx_ports; + + /* handler to REGs resource mapper */ + regs_t *regs; + + /* buffers */ + cf_t ce[MAX_PORTS_CTRL][PCFICH_RE]; + cf_t pcfich_symbols[MAX_PORTS_CTRL][PCFICH_RE]; + cf_t pcfich_x[MAX_PORTS_CTRL][PCFICH_RE]; + cf_t pcfich_d[PCFICH_RE]; + + /* bit message */ + char data[PCFICH_CFI_LEN]; + + /* tx & rx objects */ + modem_table_t mod; + demod_hard_t demod; + sequence_t seq_pcfich[NSUBFRAMES_X_FRAME]; + +}pcfich_t; + +int pcfich_init(pcfich_t *q, regs_t *regs, int cell_id, int nof_prb, int nof_tx_ports, lte_cp_t cp); +void pcfich_free(pcfich_t *q); +int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], int nsubframe, int *cfi, int *distance); +int pcfich_encode(pcfich_t *q, int cfi, cf_t *slot_symbols[MAX_PORTS_CTRL], int nsubframe); + +bool pcfich_exists(int nframe, int nslot); +int pcfich_put(regs_t *h, cf_t *pcfich, cf_t *slot_data); +int pcfich_get(regs_t *h, cf_t *pcfich, cf_t *slot_data); + +#endif diff --git a/lte/include/lte/phch/phich.h b/lte/include/lte/phch/phich.h new file mode 100644 index 000000000..80028a785 --- /dev/null +++ b/lte/include/lte/phch/phich.h @@ -0,0 +1,96 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef PHICH_ +#define PHICH_ + +#include "lte/common/base.h" +#include "lte/mimo/precoding.h" +#include "lte/mimo/layermap.h" +#include "lte/modem/mod.h" +#include "lte/modem/demod_hard.h" +#include "lte/scrambling/scrambling.h" +#include "regs.h" + +typedef _Complex float cf_t; + +#define PHICH_NORM_NSEQUENCES 8 +#define PHICH_EXT_NSEQUENCES 4 +#define PHICH_MAX_SEQUENCES PHICH_NORM_NSEQUENCES +#define PHICH_NBITS 3 + +#define PHICH_NORM_MSYMB PHICH_NBITS * 4 +#define PHICH_EXT_MSYMB PHICH_NBITS * 2 +#define PHICH_MAX_NSYMB PHICH_NORM_MSYMB +#define PHICH_NORM_C 1 +#define PHICH_EXT_C 2 +#define PHICH_NORM_NSF 4 +#define PHICH_EXT_NSF 2 + +/* phich object */ +typedef struct { + lte_cp_t cp; + int nof_prb; + int nof_tx_ports; + + /* handler to REGs resource mapper */ + regs_t *regs; + + /* buffers */ + cf_t ce[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; + cf_t phich_symbols[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; + cf_t phich_x[MAX_PORTS_CTRL][PHICH_MAX_NSYMB]; + cf_t phich_d[PHICH_MAX_NSYMB]; + cf_t phich_d0[PHICH_MAX_NSYMB]; + cf_t phich_z[PHICH_NBITS]; + + /* bit message */ + char data[PHICH_NBITS]; + + /* tx & rx objects */ + modem_table_t mod; + demod_hard_t demod; + sequence_t seq_phich[NSUBFRAMES_X_FRAME]; + +}phich_t; + +int phich_init(phich_t *q, regs_t *regs, int cell_id, int nof_prb, int nof_tx_ports, lte_cp_t cp); +void phich_free(phich_t *q); +int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], + int ngroup, int nseq, int nsubframe, char *ack, int *distance); +int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, + cf_t *slot_symbols[MAX_PORTS_CTRL]); + + +void phich_reset(phich_t *q, cf_t *slot_symbols[MAX_PORTS_CTRL]); +int phich_ngroups(phich_t *q); +bool phich_exists(int nframe, int nslot); +int phich_put(regs_t *h, cf_t *phich, cf_t *slot_data); +int phich_get(regs_t *h, cf_t *phich, cf_t *slot_data); + +#endif diff --git a/lte/include/lte/phch/regs.h b/lte/include/lte/phch/regs.h new file mode 100644 index 000000000..1f7eef0ff --- /dev/null +++ b/lte/include/lte/phch/regs.h @@ -0,0 +1,93 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#ifndef _REGS_H_ +#define _REGS_H_ + +#include +#include "lte/common/base.h" + +#define REGS_PHICH_NSYM 12 +#define REGS_PHICH_REGS_X_GROUP 3 + +#define REGS_PCFICH_NSYM 16 +#define REGS_PCFICH_NREGS 4 + +#define REGS_RE_X_REG 4 + + +typedef _Complex float cf_t; + +typedef struct { + int k[4]; + int k0; + int l; + bool assigned; +}regs_reg_t; + +typedef struct { + int nof_regs; + regs_reg_t **regs; +}regs_ch_t; + +typedef struct { + int cell_id; + int nof_prb; + int max_ctrl_symbols; + int ngroups_phich; + int refs_in_symbol1; + lte_cp_t cp; + phich_resources_t phich_res; + phich_length_t phich_len; + int nof_cce; + regs_ch_t pcfich; + regs_ch_t *phich; // there are several phich + regs_ch_t *pdcch; // there are several pdcch + int nof_regs; + regs_reg_t *regs; +}regs_t; + +int regs_init(regs_t *h, int cell_id, int nof_prb, int refs_in_symbol1, + phich_resources_t phich_res, phich_length_t phich_len, lte_cp_t cp); +void regs_free(regs_t *h); + +int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb); +int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb); +int regs_get_reg(regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, int nof_prb); +int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, int nof_prb); + +int regs_pcfich_put(regs_t *h, cf_t pcfich_symbols[REGS_PCFICH_NSYM], cf_t *slot_symbols); +int regs_pcfich_get(regs_t *h, cf_t *slot_symbols, cf_t ch_data[REGS_PCFICH_NSYM]); + +int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup, cf_t *slot_symbols); +int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup); +int regs_phich_ngroups(regs_t *h); +int regs_phich_reset(regs_t *h, cf_t *slot_symbols); + + +#endif diff --git a/lte/include/lte/scrambling/scrambling.h b/lte/include/lte/scrambling/scrambling.h index d1f9084e3..65f12cfb4 100644 --- a/lte/include/lte/scrambling/scrambling.h +++ b/lte/include/lte/scrambling/scrambling.h @@ -32,23 +32,28 @@ #include "lte/common/sequence.h" #include "lte/common/base.h" +typedef _Complex float cf_t; + /* Scrambling has no state */ -void scrambling_bit(sequence_t *s, char *data); -void scrambling_bit_offset(sequence_t *s, char *data, int offset, int len); +void scrambling_b(sequence_t *s, char *data); +void scrambling_b_offset(sequence_t *s, char *data, int offset, int len); + +void scrambling_f(sequence_t *s, float *data); +void scrambling_f_offset(sequence_t *s, float *data, int offset, int len); -void scrambling_float(sequence_t *s, float *data); -void scrambling_float_offset(sequence_t *s, float *data, int offset, int len); +void scrambling_c(sequence_t *s, cf_t *data); +void scrambling_c_offset(sequence_t *s, cf_t *data, int offset, int len); /* High-level API */ /* channel integer values */ -#define PDSCH 0 /* also PUSCH */ -#define PCFICH 1 -#define PDCCH 2 -#define PBCH 3 -#define PMCH 4 -#define PUCCH 5 +#define SCRAMBLING_PDSCH 0 /* also PUSCH */ +#define SCRAMBLING_PCFICH 1 +#define SCRAMBLING_PDCCH 2 +#define SCRAMBLING_PBCH 3 +#define SCRAMBLING_PMCH 4 +#define SCRAMBLING_PUCCH 5 typedef struct { sequence_t seq[NSUBFRAMES_X_FRAME]; diff --git a/lte/include/lte/sync/pss.h b/lte/include/lte/sync/pss.h index a76e5abdc..f1329dd97 100644 --- a/lte/include/lte/sync/pss.h +++ b/lte/include/lte/sync/pss.h @@ -47,10 +47,15 @@ typedef _Complex float cf_t; /* this is only a shortcut */ -/** The pss_synch_t object provides functions for fast computation of the crosscorrelation +/** + * The pss_synch_t object provides functions for fast computation of the crosscorrelation * between the PSS and received signal and CFO estimation. Also, the function pss_synch_periodic() is designed * to be called periodically every subframe, taking care of the correct data alignment with respect * to the PSS sequence. + * + * The object is designed to work with signals sampled at 1.92 Mhz centered at the carrier frequency. + * Thus, downsampling is required if the signal is sampled at higher frequencies. + * */ diff --git a/lte/include/lte/sync/sync.h b/lte/include/lte/sync/sync.h index b0c2c0d16..78cf88d5d 100644 --- a/lte/include/lte/sync/sync.h +++ b/lte/include/lte/sync/sync.h @@ -29,10 +29,23 @@ #ifndef SYNC_ #define SYNC_ +#include + #include "pss.h" #include "sss.h" #include "sfo.h" +/** + * + * This object performs time and frequency synchronization using the PSS and SSS signals. + * The object is designed to work with signals sampled at 1.92 Mhz centered at the carrier frequency. + * Thus, downsampling is required if the signal is sampled at higher frequencies. + * + * Correlation peak is detected comparing the maximum at the output of the correlator with a threshold. + * The comparison accepts two modes: absolute value or peak-to-mean ratio, which are configured with the + * functions sync_pss_det_absolute() and sync_pss_det_peakmean(). + */ + enum sync_pss_det { ABSOLUTE, PEAK_MEAN}; typedef struct { @@ -46,21 +59,43 @@ typedef struct { int N_id_1; int slot_id; float cfo; + lte_cp_t cp; + bool detect_cp; }sync_t; + +int sync_init(sync_t *q, int frame_size); +void sync_free(sync_t *q); + +/* Runs the synchronization algorithm. input signal must be sampled at 1.92 MHz and should be frame_size long at least */ int sync_run(sync_t *q, cf_t *input); -float sync_get_cfo(sync_t *q); + +/* Sets the threshold for peak comparison */ +void sync_set_threshold(sync_t *q, float threshold); +/* Set peak comparison to absolute value */ void sync_pss_det_absolute(sync_t *q); -void sync_pss_det_peakmean(sync_t *q); +/* Set peak comparison to relative to the mean */ +void sync_pss_det_peak_to_avg(sync_t *q); + +/* Forces the synchronizer to check one N_id_2 PSS sequence only (useful for tracking mode) */ void sync_force_N_id_2(sync_t *q, int force_N_id_2); +/* Forces the synchronizer to skip CP detection (useful for tracking mode) */ +void sync_force_cp(sync_t *q, lte_cp_t cp); + +/* Gets the slot id (0 or 10) */ int sync_get_slot_id(sync_t *q); +/* Gets the last peak-to-average ratio */ float sync_get_peak_to_avg(sync_t *q); +/* Gets the N_id_2 from the last call to synch_run() */ int sync_get_N_id_2(sync_t *q); +/* Gets the N_id_1 from the last call to synch_run() */ int sync_get_N_id_1(sync_t *q); +/* Gets the Physical CellId from the last call to synch_run() */ int sync_get_cell_id(sync_t *q); -void sync_set_threshold(sync_t *q, float threshold); -int sync_init(sync_t *q, int frame_size); -void sync_free(sync_t *q); +/* Gets the CFO estimation from the last call to synch_run() */ +float sync_get_cfo(sync_t *q); +/* Gets the CP length estimation from the last call to synch_run() */ +lte_cp_t sync_get_cp(sync_t *q); #endif diff --git a/lte/lib/ch_estimation/test/chest_test.c b/lte/lib/ch_estimation/test/chest_test.c index 0162f3222..16160e7d7 100644 --- a/lte/lib/ch_estimation/test/chest_test.c +++ b/lte/lib/ch_estimation/test/chest_test.c @@ -151,7 +151,7 @@ int main(int argc, char **argv) { if (cell_id == -1) { cid = 0; - max_cid = 149; + max_cid = 504; } else { cid = cell_id; max_cid = cell_id; @@ -222,7 +222,7 @@ int main(int argc, char **argv) { } } chest_free(&eq); - cid++; + cid+=10; INFO("cid=%d\n", cid); } diff --git a/lte/lib/phch/src/pbch.c b/lte/lib/phch/src/pbch.c index 95ed022e4..4158cf855 100644 --- a/lte/lib/phch/src/pbch.c +++ b/lte/lib/phch/src/pbch.c @@ -35,13 +35,12 @@ #include #include -#include "phch.h" +#include "prb.h" #include "lte/phch/pbch.h" #include "lte/common/base.h" #include "lte/utils/bit.h" #include "lte/utils/vector.h" #include "lte/utils/debug.h" -#include "lte/io/udpsink.h" const char crc_mask[4][16] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -52,7 +51,7 @@ const char crc_mask[4][16] = { bool pbch_exists(int nframe, int nslot) { - return (!(nframe % 4) && nslot == 1); + return (!(nframe % 5) && nslot == 1); } int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, bool put) { @@ -69,16 +68,16 @@ int pbch_cp(cf_t *input, cf_t *output, int nof_prb, lte_cp_t cp, int cell_id, bo /* symbol 0 & 1 */ for (i=0;i<2;i++) { - phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); + prb_cp_ref(&input, &output, cell_id%3, 4, 6, put); } /* symbols 2 & 3 */ if (CP_ISNORM(cp)) { for (i=0;i<2;i++) { - phch_cp_prb(&input, &output, 6); + prb_cp(&input, &output, 6); } } else { - phch_cp_prb(&input, &output, 6); - phch_cp_prb_ref(&input, &output, cell_id%3, 4, 6, put); + prb_cp(&input, &output, 6); + prb_cp_ref(&input, &output, cell_id%3, 4, 6, put); } if (put) { return input - ptr; @@ -109,7 +108,7 @@ int pbch_get(cf_t *slot1_data, cf_t *pbch, int nof_prb, lte_cp_t cp, int cell_id return pbch_cp(slot1_data, pbch, nof_prb, cp, cell_id, false); } -/** Initializes the PBCH channel receiver */ +/** Initializes the PBCH transmitter and receiver */ int pbch_init(pbch_t *q, int cell_id, lte_cp_t cp) { int ret = -1; if (cell_id < 0) { @@ -251,9 +250,9 @@ void pbch_mib_unpack(char *msg, pbch_mib_t *mib) { break; } if (*msg) { - mib->phich_length = EXTENDED; + mib->phich_length = PHICH_EXT; } else { - mib->phich_length = NORMAL; + mib->phich_length = PHICH_NORM; } msg++; @@ -293,7 +292,7 @@ void pbch_mib_pack(pbch_mib_t *mib, char *msg) { } bit_pack(bw, &msg, 3); - *msg = mib->phich_length == EXTENDED; + *msg = mib->phich_length == PHICH_EXT; msg++; switch(mib->phich_resources) { @@ -317,7 +316,7 @@ void pbch_mib_pack(pbch_mib_t *mib, char *msg) { void pbch_mib_fprint(FILE *stream, pbch_mib_t *mib) { printf(" - Nof ports: %d\n", mib->nof_ports); printf(" - PRB: %d\n", mib->nof_prb); - printf(" - PHICH Length: %s\n", mib->phich_length==EXTENDED?"Extended":"Normal"); + printf(" - PHICH Length: %s\n", mib->phich_length==PHICH_EXT?"Extended":"Normal"); printf(" - PHICH Resources: "); switch(mib->phich_resources) { case R_1_6: @@ -370,7 +369,7 @@ int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, int n memcpy(&q->temp[dst*nof_bits], &q->pbch_llr[src*nof_bits], n*nof_bits*sizeof(float)); /* descramble */ - scrambling_float_offset(&q->seq_pbch, &q->temp[dst*nof_bits], dst*nof_bits, n*nof_bits); + scrambling_f_offset(&q->seq_pbch, &q->temp[dst*nof_bits], dst*nof_bits, n*nof_bits); for (j=0;jtemp[j] = RX_NULL; @@ -422,7 +421,9 @@ int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, int src, int dst, int n, int n * Returns 1 if successfully decoded MIB, 0 if not and -1 on error */ int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int nof_prb, float ebno, pbch_mib_t *mib) { - int src, dst, res, nb, nant; + int src, dst, res, nb; + int nant_[3] = {1, 2, 4}; + int na, nant; /* Set pointers for layermapping & precoding */ int i; @@ -455,8 +456,8 @@ int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce[MAX_PORTS_CTRL], int no res = 0; /* Try decoding for 1 to 4 antennas */ - /** currently only 2 TX antennas are supported */ - for (nant=1;nant<=2 && !res;nant++) { + for (na=0;na<3 && !res;na++) { + nant = nant_[na]; INFO("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); @@ -507,7 +508,7 @@ void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL] int i; int nof_bits = 2 * q->nof_symbols; - assert(nof_ports < MAX_PORTS_CTRL); + assert(nof_ports <= MAX_PORTS_CTRL); /* Set pointers for layermapping & precoding */ cf_t *x[MAX_LAYERS]; @@ -532,14 +533,14 @@ void pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS_CTRL] } - scrambling_bit_offset(&q->seq_pbch, &q->pbch_rm_b[q->frame_idx * nof_bits], + scrambling_b_offset(&q->seq_pbch, &q->pbch_rm_b[q->frame_idx * nof_bits], q->frame_idx * nof_bits, nof_bits); mod_modulate(&q->mod, &q->pbch_rm_b[q->frame_idx * nof_bits], q->pbch_d, nof_bits); /* layer mapping & precoding */ if (nof_ports > 1) { - layermap_diversity(q->pbch_d, x, nof_ports, q->nof_symbols/nof_ports); + layermap_diversity(q->pbch_d, x, nof_ports, q->nof_symbols); precoding_diversity(x, q->pbch_symbols, nof_ports, q->nof_symbols/nof_ports); } else { memcpy(q->pbch_symbols[0], q->pbch_d, q->nof_symbols * sizeof(cf_t)); diff --git a/lte/lib/phch/src/pcfich.c b/lte/lib/phch/src/pcfich.c new file mode 100644 index 000000000..5422cf54f --- /dev/null +++ b/lte/lib/phch/src/pcfich.c @@ -0,0 +1,261 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lte/phch/regs.h" +#include "lte/phch/pcfich.h" +#include "lte/common/base.h" +#include "lte/utils/bit.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" + + +// Table 5.3.4-1 +static char cfi_table[4][PCFICH_CFI_LEN] = { + {0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1}, + {1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0}, + {1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // reserved +}; + + + +bool pcfich_exists(int nframe, int nslot) { + return true; +} + +/** Initializes the pcfich channel receiver */ +int pcfich_init(pcfich_t *q, regs_t *regs, int cell_id, int nof_prb, int nof_tx_ports, lte_cp_t cp) { + int ret = -1; + if (cell_id < 0) { + return -1; + } + bzero(q, sizeof(pcfich_t)); + q->cell_id = cell_id; + q->cp = cp; + q->regs = regs; + q->nof_prb = nof_prb; + q->nof_tx_ports = nof_tx_ports; + + if (modem_table_std(&q->mod, LTE_QPSK, false)) { + goto clean; + } + + demod_hard_init(&q->demod); + demod_hard_table_set(&q->demod, LTE_QPSK); + + for (int nsf=0;nsfseq_pcfich[nsf], 2*nsf, q->cell_id)) { + goto clean; + } + } + + q->nof_symbols = PCFICH_RE; + + ret = 0; +clean: + if (ret == -1) { + pcfich_free(q); + } + return ret; +} + +void pcfich_free(pcfich_t *q) { + for (int ns=0;nsseq_pcfich[ns]); + } + modem_table_free(&q->mod); +} + +/** Finds the CFI with minimum distance with the vector of received 32 bits. + * Saves the CFI value in the cfi pointer and returns the distance. + */ +int pcfich_cfi_decode(char bits[PCFICH_CFI_LEN], int *cfi) { + int i, j; + int distance, index=-1; + int min = 32; + + for (i=0;i<3;i++) { + distance = 0; + for (j=0;j 3) { + fprintf(stderr, "Invalid CFI %d\n", cfi); + return -1; + } + memcpy(bits, cfi_table[cfi-1], PCFICH_CFI_LEN * sizeof(char)); + return 0; +} + + +/* Decodes the PCFICH channel and saves the CFI in the cfi pointer. + * + * Returns 1 if successfully decoded the CFI, 0 if not and -1 on error + */ +int pcfich_decode(pcfich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], int nsubframe, int *cfi, int *distance) { + int dist; + + /* Set pointers for layermapping & precoding */ + int i; + cf_t *x[MAX_LAYERS]; + cf_t *ce_precoding[MAX_PORTS]; + cf_t *symbols_precoding[MAX_PORTS]; + + if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", nsubframe); + return -1; + } + + /* number of layers equals number of ports */ + for (i=0;ipcfich_x[i]; + } + for (i=0;ice[i]; + symbols_precoding[i] = q->pcfich_symbols[i]; + } + + /* extract symbols */ + if (q->nof_symbols != regs_pcfich_get(q->regs, slot_symbols, q->pcfich_symbols[0])) { + fprintf(stderr, "There was an error getting the PCFICH symbols\n"); + return -1; + } + + /* extract channel estimates */ + for (i=0;inof_tx_ports;i++) { + if (q->nof_symbols != regs_pcfich_get(q->regs, ce[i], q->ce[i])) { + fprintf(stderr, "There was an error getting the PCFICH symbols\n"); + return -1; + } + } + + /* in control channels, only diversity is supported */ + if (q->nof_tx_ports == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->pcfich_symbols[0], q->ce[0], q->pcfich_d, q->nof_symbols); + } else { + predecoding_diversity_zf(symbols_precoding, ce_precoding, x, q->nof_tx_ports, q->nof_symbols); + layerdemap_diversity(x, q->pcfich_d, q->nof_tx_ports, q->nof_symbols/q->nof_tx_ports); + } + + /* demodulate symbols */ + demod_hard_demodulate(&q->demod, q->pcfich_d, q->data, q->nof_symbols); + + /* Scramble with the sequence for slot nslot */ + scrambling_b(&q->seq_pcfich[nsubframe], q->data); + + /* decode CFI */ + dist = pcfich_cfi_decode(q->data, cfi); + if (distance) { + *distance = dist; + } + if (dist < PCFICH_MAX_DISTANCE) { + return 1; + } else { + return 0; + } +} + + +/** Encodes CFI and maps symbols to the slot + */ +int pcfich_encode(pcfich_t *q, int cfi, cf_t *slot_symbols[MAX_PORTS_CTRL], int nsubframe) { + int i; + + if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", nsubframe); + return -1; + } + + /* Set pointers for layermapping & precoding */ + cf_t *x[MAX_LAYERS]; + cf_t *symbols_precoding[MAX_PORTS]; + + /* number of layers equals number of ports */ + for (i=0;inof_tx_ports;i++) { + x[i] = q->pcfich_x[i]; + } + for (i=0;ipcfich_symbols[i]; + } + + /* pack MIB */ + pcfich_cfi_encode(cfi, q->data); + + /* scramble for slot sequence nslot */ + scrambling_b(&q->seq_pcfich[nsubframe], q->data); + + mod_modulate(&q->mod, q->data, q->pcfich_d, PCFICH_CFI_LEN); + + /* layer mapping & precoding */ + if (q->nof_tx_ports > 1) { + layermap_diversity(q->pcfich_d, x, q->nof_tx_ports, q->nof_symbols); + precoding_diversity(x, symbols_precoding, q->nof_tx_ports, q->nof_symbols/q->nof_tx_ports); + } else { + memcpy(q->pcfich_symbols[0], q->pcfich_d, q->nof_symbols * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i=0;inof_tx_ports;i++) { + if (regs_pcfich_put(q->regs, q->pcfich_symbols[i], slot_symbols[i]) < 0) { + fprintf(stderr, "Error putting PCHICH resource elements\n"); + return -1; + } + } + + return 0; +} + + diff --git a/lte/lib/phch/src/phich.c b/lte/lib/phch/src/phich.c new file mode 100644 index 000000000..d38c5ea67 --- /dev/null +++ b/lte/lib/phch/src/phich.c @@ -0,0 +1,376 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prb.h" +#include "lte/phch/regs.h" +#include "lte/phch/phich.h" +#include "lte/common/base.h" +#include "lte/utils/bit.h" +#include "lte/utils/vector.h" +#include "lte/utils/debug.h" + +/** Table 6.9.1-2 */ +const cf_t w_normal[PHICH_NORM_NSEQUENCES][4] = { { 1, 1, 1, 1 }, + { 1, -1, 1, -1 }, { 1, 1, -1, -1 }, { 1, -1, -1, 1 }, { I, I, I, I }, { + I, -I, I, -I }, { I, I, -I, -I }, { I, -I, -I, I } }; +const cf_t w_ext[PHICH_EXT_NSEQUENCES][2] = { { 1, 1 }, { 1, -1 }, { I, I }, { +I, -I } }; + +bool phich_exists(int nframe, int nslot) { + return true; +} + +int phich_ngroups(phich_t *q) { + return regs_phich_ngroups(q->regs); +} + +void phich_reset(phich_t *q, cf_t *slot_symbols[MAX_PORTS_CTRL]) { + int i; + for (i=0;iregs, slot_symbols[i]); + } +} + +/** Initializes the phich channel receiver */ +int phich_init(phich_t *q, regs_t *regs, int cell_id, int nof_prb, + int nof_tx_ports, lte_cp_t cp) { + int ret = -1; + bzero(q, sizeof(phich_t)); + q->cp = cp; + q->regs = regs; + q->nof_prb = nof_prb; + q->nof_tx_ports = nof_tx_ports; + + if (modem_table_std(&q->mod, LTE_BPSK, false)) { + goto clean; + } + + demod_hard_init(&q->demod); + demod_hard_table_set(&q->demod, LTE_BPSK); + + for (int nsf = 0; nsf < NSUBFRAMES_X_FRAME; nsf++) { + if (sequence_phich(&q->seq_phich[nsf], 2 * nsf, cell_id)) { + goto clean; + } + } + + ret = 0; + clean: if (ret == -1) { + phich_free(q); + } + return ret; +} + +void phich_free(phich_t *q) { + for (int ns = 0; ns < NSUBFRAMES_X_FRAME; ns++) { + sequence_free(&q->seq_phich[ns]); + } + modem_table_free(&q->mod); +} + +/* Decodes ACK + * + */ +char phich_ack_decode(char bits[PHICH_NBITS], int *distance) { + int i, n; + + n = 0; + for (i = 0; i < PHICH_NBITS; i++) { + n += bits[i]; + } + INFO("PHICH decoder: %d, %d, %d\n", bits[0], bits[1], bits[2]); + if (n >= 2) { + if (distance) { + *distance = 3-n; + } + return 1; + } else { + if (distance) { + *distance = n; + } + return 0; + } +} + +/** Encodes the ACK + * 36.212 + */ +void phich_ack_encode(char ack, char bits[PHICH_NBITS]) { + memset(bits, ack, 3 * sizeof(char)); +} + +/* Decodes the phich channel and saves the CFI in the cfi pointer. + * + * Returns 1 if successfully decoded the CFI, 0 if not and -1 on error + */ +int phich_decode(phich_t *q, cf_t *slot_symbols, cf_t *ce[MAX_PORTS_CTRL], + int ngroup, int nseq, int nsubframe, char *ack, int *distance) { + + /* Set pointers for layermapping & precoding */ + int i, j; + cf_t *x[MAX_LAYERS]; + cf_t *ce_precoding[MAX_PORTS]; + cf_t *symbols_precoding[MAX_PORTS]; + + DEBUG("Decoding PHICH Ngroup: %d, Nseq: %d\n", ngroup, nseq); + + if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", nsubframe); + return -1; + } + + if (CP_ISEXT(q->cp)) { + if (nseq < 0 || nseq > PHICH_EXT_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return -1; + } + } else { + if (nseq < 0 || nseq > PHICH_NORM_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return -1; + } + } + if (ngroup >= regs_phich_ngroups(q->regs)) { + fprintf(stderr, "Invalid ngroup %d\n", ngroup); + return -1; + } + + /* number of layers equals number of ports */ + for (i = 0; i < MAX_PORTS_CTRL; i++) { + x[i] = q->phich_x[i]; + } + for (i = 0; i < MAX_PORTS; i++) { + ce_precoding[i] = q->ce[i]; + symbols_precoding[i] = q->phich_symbols[i]; + } + + /* extract symbols */ + if (PHICH_MAX_NSYMB + != regs_phich_get(q->regs, slot_symbols, q->phich_symbols[0], + ngroup)) { + fprintf(stderr, "There was an error getting the phich symbols\n"); + return -1; + } + + /* extract channel estimates */ + for (i = 0; i < q->nof_tx_ports; i++) { + if (PHICH_MAX_NSYMB + != regs_phich_get(q->regs, ce[i], q->ce[i], ngroup)) { + fprintf(stderr, "There was an error getting the phich symbols\n"); + return -1; + } + } + + /* in control channels, only diversity is supported */ + if (q->nof_tx_ports == 1) { + /* no need for layer demapping */ + predecoding_single_zf(q->phich_symbols[0], q->ce[0], q->phich_d0, + PHICH_MAX_NSYMB); + } else { + predecoding_diversity_zf(symbols_precoding, ce_precoding, x, + q->nof_tx_ports, PHICH_MAX_NSYMB); + layerdemap_diversity(x, q->phich_d0, q->nof_tx_ports, + PHICH_MAX_NSYMB / q->nof_tx_ports); + } + DEBUG("Recv!!: \n",0); + DEBUG("d0: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d0, PHICH_MAX_NSYMB); + + if (CP_ISEXT(q->cp)) { + if (ngroup % 2) { + for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { + q->phich_d[2 * i + 0] = q->phich_d0[4 * i + 2]; + q->phich_d[2 * i + 1] = q->phich_d0[4 * i + 3]; + } + } else { + for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { + q->phich_d[2 * i + 0] = q->phich_d0[4 * i]; + q->phich_d[2 * i + 1] = q->phich_d0[4 * i + 1]; + } + } + } else { + memcpy(q->phich_d, q->phich_d0, PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + DEBUG("d: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d, PHICH_EXT_MSYMB); + + scrambling_c(&q->seq_phich[nsubframe], q->phich_d); + + /* De-spreading */ + if (CP_ISEXT(q->cp)) { + for (i=0;iphich_z[i] = 0; + for (j=0;jphich_z[i] += conjf(w_ext[nseq][j]) * + q->phich_d[i*PHICH_EXT_NSF+j]/PHICH_EXT_NSF; + } + } + } else { + for (i=0;iphich_z[i] = 0; + for (j=0;jphich_z[i] += conjf(w_normal[nseq][j]) * + q->phich_d[i*PHICH_NORM_NSF+j]/PHICH_NORM_NSF; + } + } + } + + DEBUG("z: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_z, PHICH_NBITS); + + demod_hard_demodulate(&q->demod, q->phich_z, q->data, PHICH_NBITS); + + if (ack) { + *ack = phich_ack_decode(q->data, distance); + } + + return 0; +} + +/** Encodes ACK/NACK bits, modulates and inserts into resource. + * The parameter ack is an array of phich_ngroups() pointers to buffers of nof_sequences chars + */ +int phich_encode(phich_t *q, char ack, int ngroup, int nseq, int nsubframe, + cf_t *slot_symbols[MAX_PORTS_CTRL]) { + int i; + + if (nsubframe < 0 || nsubframe > NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", nsubframe); + return -1; + } + + if (CP_ISEXT(q->cp)) { + if (nseq < 0 || nseq > PHICH_EXT_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return -1; + } + } else { + if (nseq < 0 || nseq > PHICH_NORM_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return -1; + } + } + if (ngroup >= regs_phich_ngroups(q->regs)) { + fprintf(stderr, "Invalid ngroup %d\n", ngroup); + return -1; + } + + /* Set pointers for layermapping & precoding */ + cf_t *x[MAX_LAYERS]; + cf_t *symbols_precoding[MAX_PORTS]; + + /* number of layers equals number of ports */ + for (i = 0; i < q->nof_tx_ports; i++) { + x[i] = q->phich_x[i]; + } + for (i = 0; i < MAX_PORTS; i++) { + symbols_precoding[i] = q->phich_symbols[i]; + } + + /* encode ACK/NACK bit */ + phich_ack_encode(ack, q->data); + + mod_modulate(&q->mod, q->data, q->phich_z, PHICH_NBITS); + + DEBUG("data: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_z, PHICH_NBITS); + + /* Spread with w */ + if (CP_ISEXT(q->cp)) { + for (i = 0; i < PHICH_EXT_MSYMB; i++) { + q->phich_d[i] = w_ext[nseq][i % PHICH_EXT_NSF] + * q->phich_z[i / PHICH_EXT_NSF]; + } + } else { + for (i = 0; i < PHICH_NORM_MSYMB; i++) { + q->phich_d[i] = w_normal[nseq][i % PHICH_NORM_NSF] + * q->phich_z[i / PHICH_NORM_NSF]; + } + } + + DEBUG("d: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d, PHICH_EXT_MSYMB); + + scrambling_c(&q->seq_phich[nsubframe], q->phich_d); + + /* align to REG */ + if (CP_ISEXT(q->cp)) { + if (ngroup % 2) { + for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { + q->phich_d0[4 * i + 0] = 0; + q->phich_d0[4 * i + 1] = 0; + q->phich_d0[4 * i + 2] = q->phich_d[2 * i]; + q->phich_d0[4 * i + 3] = q->phich_d[2 * i + 1]; + } + } else { + for (i = 0; i < PHICH_EXT_MSYMB / 2; i++) { + q->phich_d0[4 * i + 0] = q->phich_d[2 * i]; + q->phich_d0[4 * i + 1] = q->phich_d[2 * i + 1]; + q->phich_d0[4 * i + 2] = 0; + q->phich_d0[4 * i + 3] = 0; + } + } + } else { + memcpy(q->phich_d0, q->phich_d, PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + DEBUG("d0: ",0); + if (VERBOSE_ISDEBUG()) vec_fprint_c(stdout, q->phich_d0, PHICH_MAX_NSYMB); + + + /* layer mapping & precoding */ + if (q->nof_tx_ports > 1) { + layermap_diversity(q->phich_d0, x, q->nof_tx_ports, PHICH_MAX_NSYMB); + precoding_diversity(x, symbols_precoding, q->nof_tx_ports, + PHICH_MAX_NSYMB / q->nof_tx_ports); + /**FIXME: According to 6.9.2, Precoding for 4 tx ports is different! */ + } else { + memcpy(q->phich_symbols[0], q->phich_d0, PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->nof_tx_ports; i++) { + if (regs_phich_add(q->regs, q->phich_symbols[i], ngroup, + slot_symbols[i]) < 0) { + fprintf(stderr, "Error putting PCHICH resource elements\n"); + return -1; + } + } + + return 0; +} + diff --git a/lte/lib/phch/src/phch.c b/lte/lib/phch/src/prb.c similarity index 80% rename from lte/lib/phch/src/phch.c rename to lte/lib/phch/src/prb.c index bd5a81f59..bf3651867 100644 --- a/lte/lib/phch/src/phch.c +++ b/lte/lib/phch/src/prb.c @@ -29,10 +29,10 @@ #include #include -#include "phch.h" +#include "prb.h" #include "lte/common/base.h" -void phch_cp_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, +void prb_cp_ref(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb, bool advance_output) { int i; @@ -62,19 +62,19 @@ void phch_cp_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, } } -void phch_cp_prb(cf_t **input, cf_t **output, int nof_prb) { +void prb_cp(cf_t **input, cf_t **output, int nof_prb) { memcpy(*output, *input, sizeof(cf_t) * RE_X_RB * nof_prb); *input += nof_prb * RE_X_RB; *output += nof_prb * RE_X_RB; } -void phch_put_prb_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, +void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb) { - phch_cp_prb_ref(input, output, offset, nof_refs, nof_prb, false); + prb_cp_ref(input, output, offset, nof_refs, nof_prb, false); } -void phch_get_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, +void prb_get_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb) { - phch_cp_prb_ref(input, output, offset, nof_refs, nof_prb, true); + prb_cp_ref(input, output, offset, nof_refs, nof_prb, true); } diff --git a/lte/lib/phch/src/phch.h b/lte/lib/phch/src/prb.h similarity index 83% rename from lte/lib/phch/src/phch.h rename to lte/lib/phch/src/prb.h index 83b69dac4..4adf29389 100644 --- a/lte/lib/phch/src/phch.h +++ b/lte/lib/phch/src/prb.h @@ -28,10 +28,10 @@ typedef _Complex float cf_t; -void phch_cp_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, +void prb_cp_ref(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb, bool advance_input); -void phch_cp_prb(cf_t **input, cf_t **output, int nof_prb); -void phch_put_prb_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, +void prb_cp(cf_t **input, cf_t **output, int nof_prb); +void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb); void phch_get_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs, int nof_prb); diff --git a/lte/lib/phch/src/regs.c b/lte/lib/phch/src/regs.c new file mode 100644 index 000000000..9d38f4549 --- /dev/null +++ b/lte/lib/phch/src/regs.c @@ -0,0 +1,625 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2014 The libLTE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the libLTE library. + * + * libLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "lte/common/base.h" +#include "lte/phch/regs.h" +#include "lte/utils/debug.h" + +regs_reg_t *regs_find_reg(regs_t *h, int k, int l); + + + + +/*************************************************************** + * + * PDCCH REG ALLOCATION + * + ***************************************************************/ + +/** Initialize REGs for PDCCH + * 36.211 10.3 section 6.8.5 + */ +int regs_pdcch_init(regs_t *h) { + return 0; +} + +void regs_pdcch_free(regs_t *h) { + if (h->pdcch) { + free(h->pdcch); + } +} + + + + + +/*************************************************************** + * + * PHICH REG ALLOCATION + * + ***************************************************************/ + +/** Initialize REGs for PHICH + * 36.211 10.3 section 6.9.3 + */ +int regs_phich_init(regs_t *h) { + float ng; + int i,ni,li,n[3],nreg,mi; + regs_reg_t **regs_phich[3]; + int ret = -1; + + switch(h->phich_res) { + case R_1_6: + ng = (float) 1/6; + break; + case R_1_2: + ng = (float) 1/2; + break; + case R_1: + ng = 1; + break; + case R_2: + ng = 2; + break; + } + h->ngroups_phich = (int) ceilf(ng * ((float) h->nof_prb/8)); + h->phich = malloc(sizeof(regs_ch_t) * h->ngroups_phich); + if (!h->phich) { + perror("malloc"); + return -1; + } + INFO("Creating %d PHICH mapping units. %s length, Ng=%.2f\n",h->ngroups_phich, + h->phich_len==PHICH_EXT?"Extended":"Normal",ng); + for (i=0;ingroups_phich;i++) { + h->phich[i].nof_regs = REGS_PHICH_REGS_X_GROUP; + h->phich[i].regs = malloc(sizeof(regs_reg_t*) * REGS_PHICH_REGS_X_GROUP); + if (!h->phich[i].regs) { + perror("malloc"); + goto clean_and_exit; + } + } + + /** Here begins the mapping algorithm */ + + /* Step 2. Count REGs not assigned to PCFICH */ + bzero(n, 3*sizeof(int)); + for (i=0;inof_regs;i++) { + if (h->regs[i].l < 3 && !h->regs[i].assigned) { + n[h->regs[i].l]++; + } + } + + bzero(regs_phich, sizeof(regs_reg_t*) * 3); + for (i=0;i<3;i++) { + regs_phich[i] = malloc(n[i] * sizeof(regs_reg_t*)); + if (!regs_phich[i]) { + perror("malloc"); + goto clean_and_exit; + } + } + + bzero(n, 3 * sizeof(int)); + /* Step 3. Number REGs not assigned to PCFICH */ + for (i=0;inof_regs;i++) { + // they are already sorted starting from the REG with the lowest frequency-domain index + if (h->regs[i].l < 3 && !h->regs[i].assigned) { + regs_phich[h->regs[i].l][n[h->regs[i].l]++] = &h->regs[i]; + } + } + + nreg=0; + for (mi=0;mingroups_phich;mi++) { // here ngroups is the number of mapping units + for (i=0;i<3;i++) { + li=h->phich_len==PHICH_EXT?i:0; // Step 7 + ni=((h->cell_id*n[li]/n[0])+mi+i*n[li]/3) % n[li]; // Step 8 + h->phich[mi].regs[i] = regs_phich[li][ni]; + h->phich[mi].regs[i]->assigned = true; + INFO("Assigned PHICH REG#%d (%d,%d)\n",nreg,h->phich[mi].regs[i]->k0,li); + nreg++; + } + } + + // now the number of mapping units = number of groups for normal cp. For extended cp + // ngroups = 2 * number mapping units + if (CP_ISEXT(h->cp)) { + h->ngroups_phich *= 2; + } + ret = 0; + +clean_and_exit: + if (ret == -1) { + if (h->phich) { + for (i=0;ingroups_phich;i++) { + if (h->phich[i].regs) { + free(h->phich[i].regs); + } + } + free(h->phich); + } + } + for (i=0;i<3;i++) { + if (regs_phich[i]) { + free(regs_phich[i]); + } + } + return ret; +} + +void regs_phich_free(regs_t *h) { + int i; + if (h->phich) { + if (CP_ISEXT(h->cp)) { + h->ngroups_phich /= 2; + } + for (i=0;ingroups_phich;i++) { + if (h->phich[i].regs) { + free(h->phich[i].regs); + } + } + free(h->phich); + } +} + +int regs_phich_ngroups(regs_t *h) { + return h->ngroups_phich; +} + +/** + * Adds the PHICH symbols to the resource grid pointed by slot_symbols. + * + * Each subframe, the user shall call the regs_phich_reset function before adding PHICH symbols. + * + * Returns the number of written symbols, or -1 on error + */ +int regs_phich_add(regs_t *h, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup, cf_t *slot_symbols) { + int i; + if (ngroup < 0 || ngroup > h->ngroups_phich) { + fprintf(stderr, "Error invalid ngroup %d\n", ngroup); + return -1; + } + if (CP_ISEXT(h->cp)) { + ngroup /= 2; + } + regs_ch_t *rch = &h->phich[ngroup]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_add_reg(rch->regs[i], &phich_symbols[i*REGS_RE_X_REG], slot_symbols, h->nof_prb); + } + return i*REGS_RE_X_REG; +} + +/** + * Resets the PHICH symbols + * + * Returns the number of written symbols, or -1 on error + */ +int regs_phich_reset(regs_t *h, cf_t *slot_symbols) { + int i; + int ngroup, ng; + for (ngroup = 0;ngroup < h->ngroups_phich;CP_ISEXT(h->cp)?ngroup+=2:ngroup++) { + if (CP_ISEXT(h->cp)) { + ng = ngroup/2; + } else { + ng = ngroup; + } + regs_ch_t *rch = &h->phich[ng]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_reset_reg(rch->regs[i], slot_symbols, h->nof_prb); + } + } + return 0; +} + +/** + * Gets the PHICH symbols from the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int regs_phich_get(regs_t *h, cf_t *slot_symbols, cf_t phich_symbols[REGS_PHICH_NSYM], int ngroup) { + int i; + if (ngroup < 0 || ngroup > h->ngroups_phich) { + fprintf(stderr, "Error invalid ngroup %d\n", ngroup); + return -1; + } + if (CP_ISEXT(h->cp)) { + ngroup /= 2; + } + regs_ch_t *rch = &h->phich[ngroup]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_get_reg(rch->regs[i], slot_symbols, &phich_symbols[i*REGS_RE_X_REG], h->nof_prb); + } + return i*REGS_RE_X_REG; +} + + + + + + + + + + + + + + +/*************************************************************** + * + * PCFICH REG ALLOCATION + * + ***************************************************************/ + + +/** Initialize REGs for PCFICH + * 36.211 10.3 section 6.7.4 + */ +int regs_pcfich_init(regs_t *h) { + int i, k_hat, k; + regs_ch_t *ch = &h->pcfich; + + ch->regs = malloc(sizeof(regs_reg_t*) * REGS_PCFICH_NREGS); + if (!ch->regs) { + perror("malloc"); + return -1; + } + ch->nof_regs = REGS_PCFICH_NREGS; + + INFO("PCFICH allocating %d regs. CellID: %d, PRB: %d\n", ch->nof_regs, h->cell_id, h->nof_prb); + + k_hat = (RE_X_RB / 2) * (h->cell_id % (2 * h->nof_prb)); + for (i = 0; i < REGS_PCFICH_NREGS; i++) { + + k = (k_hat + (i * h->nof_prb / 2) * (RE_X_RB / 2)) + % (h->nof_prb * RE_X_RB); + ch->regs[i] = regs_find_reg(h, k, 0); + if (!ch->regs[i]) { + fprintf(stderr, "Error allocating PCFICH: REG (%d,0) not found\n", + k); + return -1; + } else if (ch->regs[i]->assigned) { + fprintf(stderr, + "Error allocating PCFICH: REG (%d,0) already allocated\n", + k); + return -1; + } else { + ch->regs[i]->assigned = true; + INFO("Assigned PCFICH REG#%d (%d,0)\n", i, k); + } + } + return 0; +} + +void regs_pcfich_free(regs_t *h) { + if (h->pcfich.regs) { + free(h->pcfich.regs); + } +} + +/** + * Maps the PCFICH symbols to the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int regs_pcfich_put(regs_t *h, cf_t pcfich_symbols[REGS_PCFICH_NSYM], cf_t *slot_symbols) { + regs_ch_t *rch = &h->pcfich; + + int i; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { + regs_put_reg(rch->regs[i], &pcfich_symbols[i*REGS_RE_X_REG], slot_symbols, h->nof_prb); + } + return i*REGS_RE_X_REG; +} + +/** + * Gets the PCFICH symbols from the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int regs_pcfich_get(regs_t *h, cf_t *slot_symbols, cf_t ch_data[REGS_PCFICH_NSYM]) { + regs_ch_t *rch = &h->pcfich; + int i; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { + regs_get_reg(rch->regs[i], slot_symbols, &ch_data[i*REGS_RE_X_REG], h->nof_prb); + } + return i*REGS_RE_X_REG; +} + + + + + + + + + + + + + + +/*************************************************************** + * + * COMMON FUNCTIONS + * + ***************************************************************/ + +regs_reg_t *regs_find_reg(regs_t *h, int k, int l) { + int i; + for (i=0;inof_regs;i++) { + if (h->regs[i].l == l && h->regs[i].k0 == k) { + return &h->regs[i]; + } + } + return NULL; +} + +/** + * Returns the number of REGs in a PRB + * 36.211 Section 6.2.4 + */ +int regs_num_x_symbol(int symbol, int refs_in_symbol1, lte_cp_t cp) { + + switch (symbol) { + case 0: + return 2; + case 1: + switch (refs_in_symbol1) { + case 1: + case 2: + return 3; + case 4: + return 2; + default: + return -1; + } + break; + case 2: + return 3; + case 3: + if (CP_ISNORM(cp)) { + return 3; + } else { + return 2; + } + default: + return -1; + } +} + +/** + * Initializes the indices of a REG + * 36.211 Section 6.2.4 + */ +int regs_reg_init(regs_reg_t *reg, int symbol, int nreg, int k0, int maxreg, int vo) { + int i, j, z; + + reg->l = symbol; + reg->assigned = false; + reg->k0 = k0 + nreg * 6; + + switch (maxreg) { + case 2: + /* there are two references in the middle */ + j = z = 0; + for (i = 0; i < vo; i++) { + reg->k[j] = k0 + nreg * 6 + i; + j++; + } + for (i = 0; i < 2; i++) { + reg->k[j] = k0 + nreg * 6 + i + vo + 1; + j++; + } + z = j; + for (i = 0; i < 4 - z; i++) { + reg->k[j] = k0 + nreg * 6 + vo + 3 + i + 1; + j++; + } + if (j != 4) { + fprintf(stderr, "Something went wrong: expected 2 references\n"); + return -1; + } + break; + + case 3: + /* there is no reference */ + for (i = 0; i < 4; i++) { + reg->k[i] = k0 + nreg * 4 + i; + } + break; + default: + fprintf(stderr, "Invalid number of REGs per PRB: %d\n", maxreg); + return -1; + } + return 0; +} + +void regs_free(regs_t *h) { + if (h->regs) { + free(h->regs); + } + regs_pcfich_free(h); + regs_phich_free(h); + regs_pdcch_free(h); + + bzero(h, sizeof(regs_t)); +} + +/** + * Initializes REGs structure. + * Sets all REG indices and initializes PCFICH, PHICH and PDCCH REGs + * Returns 0 if OK, -1 on error + */ +int regs_init(regs_t *h, int cell_id, int nof_prb, int refs_in_symbol1, + phich_resources_t phich_res, phich_length_t phich_len, lte_cp_t cp) { + int ret = -1; + int i, j, n, p, k; + int vo = cell_id % 3; + int max_ctrl_symbols = nof_prb<10?4:3; + + bzero(h, sizeof(regs_t)); + + h->cell_id = cell_id; + h->nof_prb = nof_prb; + h->max_ctrl_symbols = max_ctrl_symbols; + h->phich_res = phich_res; + h->phich_len = phich_len; + h->cp = cp; + h->refs_in_symbol1 = refs_in_symbol1; + + h->nof_regs = 0; + for (i = 0; i < max_ctrl_symbols; i++) { + n = regs_num_x_symbol(i, refs_in_symbol1, cp); + if (n == -1) { + return -1; + } + h->nof_regs += nof_prb * n; + } + INFO("Indexing %d REGs. CellId: %d, %d PRB, CP: %s\n", h->nof_regs, h->cell_id, h->nof_prb, + CP_ISNORM(cp)?"Normal":"Extended"); + h->regs = malloc(sizeof(regs_reg_t) * h->nof_regs); + if (!h->regs) { + perror("malloc"); + goto clean_and_exit; + } + + k = 0; + for (i = 0; i < max_ctrl_symbols; i++) { + n = regs_num_x_symbol(i, refs_in_symbol1, cp); + for (p = 0; p < nof_prb; p++) { + for (j = 0; j < n; j++) { + if (regs_reg_init(&h->regs[k], i, j, p * RE_X_RB, n, vo)) { + fprintf(stderr, "Error initializing REGs\n"); + goto clean_and_exit; + } + DEBUG("Available REG #%3d: %d:%d:%d (k0=%d)\n", k, i, p, j, + h->regs[k].k0); + k++; + } + } + } + + if (regs_pcfich_init(h)) { + fprintf(stderr, "Error initializing PCFICH REGs\n"); + goto clean_and_exit; + } + if (regs_phich_init(h)) { + fprintf(stderr, "Error initializing PHICH REGs\n"); + goto clean_and_exit; + } + if (regs_pdcch_init(h)) { + fprintf(stderr, "Error initializing PDCCH REGs\n"); + goto clean_and_exit; + } + + ret = 0; + + clean_and_exit: if (ret == -1) { + regs_free(h); + } + return ret; +} + +#define REG_IDX(r, i, n) r->k[i]+r->l*n*RE_X_RB + +/** + * Puts one REG data (4 symbols) in the slot symbols array + */ +int regs_put_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb) { + int i; + for (i = 0; i < REGS_RE_X_REG; i++) { + if (reg->assigned) { + DEBUG("PUT REG: i=%d, (k=%d,l=%d)\n", i, REG_IDX(reg, i, nof_prb),reg->l); + slot_symbols[REG_IDX(reg, i, nof_prb)] = reg_data[i]; + } else { + fprintf(stderr, "Error REG not assigned\n"); + return -1; + } + } + return REGS_RE_X_REG; +} + +/** + * Adds one REG data (4 symbols) in the slot symbols array + * Used by PHICH + */ +int regs_add_reg(regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, int nof_prb) { + int i; + for (i = 0; i < REGS_RE_X_REG; i++) { + if (reg->assigned) { + slot_symbols[REG_IDX(reg, i, nof_prb)] += reg_data[i]; + DEBUG("ADD REG: i=%d, (k=%d,l=%d): %.1f+%.1fi\n", i, REG_IDX(reg, i, nof_prb),reg->l, + __real__ slot_symbols[REG_IDX(reg, i, nof_prb)], + __imag__ slot_symbols[REG_IDX(reg, i, nof_prb)]); + } else { + fprintf(stderr, "Error REG not assigned\n"); + return -1; + } + } + return REGS_RE_X_REG; +} + + +/** + * Reset REG data (4 symbols) in the slot symbols array + */ +int regs_reset_reg(regs_reg_t *reg, cf_t *slot_symbols, int nof_prb) { + int i; + for (i = 0; i < REGS_RE_X_REG; i++) { + if (reg->assigned) { + DEBUG("RESET REG: i=%d, (k=%d,l=%d)\n", i, REG_IDX(reg, i, nof_prb),reg->l); + slot_symbols[REG_IDX(reg, i, nof_prb)] = 0; + } else { + fprintf(stderr, "Error REG not assigned\n"); + return -1; + } + } + return REGS_RE_X_REG; +} + +/** + * Gets one REG data (4 symbols) from the slot symbols array + */ +int regs_get_reg(regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, int nof_prb) { + int i; + for (i = 0; i < REGS_RE_X_REG; i++) { + if (reg->assigned) { + reg_data[i] = slot_symbols[REG_IDX(reg, i, nof_prb)]; + DEBUG("GET REG: i=%d, (k=%d,l=%d): %.1f+%.1fi\n", i, REG_IDX(reg, i, nof_prb),reg->l, + __real__ reg_data[i], __imag__ reg_data[i]); + } else { + fprintf(stderr, "Error REG not assigned\n"); + return -1; + } + } + return REGS_RE_X_REG; +} + diff --git a/lte/lib/common/src/phch_sequence.c b/lte/lib/phch/src/sequences.c similarity index 72% rename from lte/lib/common/src/phch_sequence.c rename to lte/lib/phch/src/sequences.c index 8525d5d6a..566d2c589 100644 --- a/lte/lib/common/src/phch_sequence.c +++ b/lte/lib/phch/src/sequences.c @@ -25,11 +25,32 @@ * */ + #include #include "lte/common/base.h" #include "lte/common/sequence.h" +/** + * 36.211 6.6.1 + */ int sequence_pbch(sequence_t *seq, lte_cp_t cp, int cell_id) { bzero(seq, sizeof(sequence_t)); return sequence_LTEPRS(seq, CP_ISNORM(cp)?1920:1728, cell_id); } + +/** + * 36.211 6.7.1 + */ +int sequence_pcfich(sequence_t *seq, int nslot, int cell_id) { + bzero(seq, sizeof(sequence_t)); + return sequence_LTEPRS(seq, 32, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); +} + + +/** + * 36.211 6.9.1 + */ +int sequence_phich(sequence_t *seq, int nslot, int cell_id) { + bzero(seq, sizeof(sequence_t)); + return sequence_LTEPRS(seq, 12, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); +} diff --git a/lte/lib/phch/test/CMakeLists.txt b/lte/lib/phch/test/CMakeLists.txt index d9e11a510..e7f3a8fd5 100644 --- a/lte/lib/phch/test/CMakeLists.txt +++ b/lte/lib/phch/test/CMakeLists.txt @@ -26,17 +26,59 @@ ADD_EXECUTABLE(pbch_test pbch_test.c) TARGET_LINK_LIBRARIES(pbch_test lte) -ADD_TEST(pbch_test_6 pbch_test -p 6 -c 100) -ADD_TEST(pbch_test_50 pbch_test -p 50 -c 50) +ADD_TEST(pbch_test_6 pbch_test -p 1 -n 6 -c 100) +ADD_TEST(pbch_test_62 pbch_test -p 2 -n 6 -c 100) +ADD_TEST(pbch_test_64 pbch_test -p 4 -n 6 -c 100) +ADD_TEST(pbch_test_50 pbch_test -p 1 -n 50 -c 50) +ADD_TEST(pbch_test_502 pbch_test -p 2 -n 50 -c 50) +ADD_TEST(pbch_test_504 pbch_test -p 4 -n 50 -c 50) + ######################################################################## -# PBCH FILE TEST +# PCFICH TEST ######################################################################## -ADD_EXECUTABLE(pbch_file_test pbch_file_test.c) -TARGET_LINK_LIBRARIES(pbch_file_test lte) +ADD_EXECUTABLE(pcfich_test pcfich_test.c) +TARGET_LINK_LIBRARIES(pcfich_test lte) -ADD_TEST(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/h3g.mib.dat) +ADD_TEST(pcfich_test_6 pcfich_test -p 1 -n 6) +ADD_TEST(pcfich_test_62 pcfich_test -p 2 -n 6) +ADD_TEST(pcfich_test_64 pcfich_test -p 4 -n 6) +ADD_TEST(pcfich_test_10 pcfich_test -p 1 -n 10) +ADD_TEST(pcfich_test_102 pcfich_test -p 2 -n 10) +ADD_TEST(pcfich_test_104 pcfich_test -p 4 -n 10) + +######################################################################## +# PHICH TEST +######################################################################## + +ADD_EXECUTABLE(phich_test phich_test.c) +TARGET_LINK_LIBRARIES(phich_test lte) + +ADD_TEST(phich_test_6 phich_test -p 1 -n 6) +ADD_TEST(phich_test_62 phich_test -p 2 -n 6) +ADD_TEST(phich_test_64 phich_test -p 4 -n 6 -g 1/6) + +ADD_TEST(phich_test_6e phich_test -p 1 -n 6 -e) +ADD_TEST(phich_test_62e phich_test -p 2 -n 6 -e -l) +ADD_TEST(phich_test_64e phich_test -p 4 -n 6 -e -l -g 2) +ADD_TEST(phich_test_10 phich_test -p 1 -n 10 -e) +ADD_TEST(phich_test_102 phich_test -p 2 -n 10 -g 2) +ADD_TEST(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2) + + + +######################################################################## +# FILE TEST +######################################################################## + +ADD_EXECUTABLE(pbch_file_test pbch_file_test.c) +TARGET_LINK_LIBRARIES(pbch_file_test lte) +ADD_EXECUTABLE(pcfich_file_test pcfich_file_test.c) +TARGET_LINK_LIBRARIES(pcfich_file_test lte) +ADD_TEST(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.dat) +ADD_TEST(pcfich_file_test pcfich_file_test -c 150 -n 50 -p 2 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.10M.dat) + \ No newline at end of file diff --git a/lte/lib/phch/test/h3g.mib.dat b/lte/lib/phch/test/h3g.mib.dat deleted file mode 100644 index 92d4a06ba4f7ed64705b1a5b38a6def91d46c318..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 230400 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 z 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%0DRcx^Mx2%tF9MHu47%_0hDY}q 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
-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
C=>Lz1iiMr=2mx_upbvoxPeAzuG=%2 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 diff --git a/lte/lib/phch/test/pbch_file_test.c b/lte/lib/phch/test/pbch_file_test.c index 0d30f0b7e..bc03a0b34 100644 --- a/lte/lib/phch/test/pbch_file_test.c +++ b/lte/lib/phch/test/pbch_file_test.c @@ -34,11 +34,12 @@ #include "lte.h" char *input_file_name = NULL; -int nof_slots=100; -float corr_peak_threshold=30; -int force_N_id_2=-1; +char *matlab_file_name = NULL; +int cell_id = 150; +lte_cp_t cp = CPNORM; +int nof_prb = 6; -FILE *fmatlab; +FILE *fmatlab = NULL; #define NOF_PORTS 2 #define FLEN 9600 @@ -48,35 +49,33 @@ cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; pbch_t pbch; lte_fft_t fft; chest_t chest; -sync_t synch; -cfo_t cfocorr; void usage(char *prog) { - printf("Usage: %s [onlt] -i input_file\n", prog); - printf("\t-n number of frames [Default %d]\n", nof_slots); - printf("\t-t correlation threshold [Default %g]\n", corr_peak_threshold); + printf("Usage: %s [vcoe] -i input_file\n", prog); + printf("\t-o output matlab file name [Default Disabled]\n"); + printf("\t-c cell_id [Default %d]\n", cell_id); + printf("\t-e Set extended prefix [Default Normal]\n"); printf("\t-v [set verbose to debug, default none]\n"); - printf("\t-f force_N_id_2 [Default %d]\n", force_N_id_2); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "intvf")) != -1) { + while ((opt = getopt(argc, argv, "iovce")) != -1) { switch(opt) { case 'i': input_file_name = argv[optind]; break; - case 'n': - nof_slots = atoi(argv[optind]); + case 'c': + cell_id = atoi(argv[optind]); break; - case 't': - corr_peak_threshold = atof(argv[optind]); + case 'o': + matlab_file_name = argv[optind]; break; case 'v': verbose++; break; - case 'f': - force_N_id_2 = atoi(argv[optind]); + case 'e': + cp = CPEXT; break; default: usage(argv[0]); @@ -97,10 +96,14 @@ int base_init() { exit(-1); } - fmatlab = fopen("output.m", "w"); - if (!fmatlab) { - perror("fopen"); - return -1; + if (matlab_file_name) { + fmatlab = fopen(matlab_file_name, "w"); + if (!fmatlab) { + perror("fopen"); + return -1; + } + } else { + fmatlab = NULL; } input_buffer = malloc(FLEN * sizeof(cf_t)); @@ -109,35 +112,40 @@ int base_init() { exit(-1); } - fft_buffer = malloc(CPNORM_NSYMB * 72 * sizeof(cf_t)); + fft_buffer = malloc(CP_NSYMB(cp) * nof_prb * RE_X_RB * sizeof(cf_t)); if (!fft_buffer) { perror("malloc"); return -1; } for (i=0;i +#include +#include +#include +#include + +#include "lte.h" + +char *input_file_name = NULL; +char *matlab_file_name = NULL; +int cell_id = 0; +lte_cp_t cp = CPNORM; +int nof_prb = 6; +int nof_ports = 1; +int flen; + +FILE *fmatlab = NULL; + +filesource_t fsrc; +cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS_CTRL]; +pcfich_t pcfich; +regs_t regs; +lte_fft_t fft; +chest_t chest; + +void usage(char *prog) { + printf("Usage: %s [vcoe] -i input_file\n", prog); + printf("\t-o output matlab file name [Default Disabled]\n"); + printf("\t-c cell_id [Default %d]\n", cell_id); + printf("\t-p nof_ports [Default %d]\n", nof_ports); + printf("\t-n nof_prb [Default %d]\n", nof_prb); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "iovcenp")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'n': + nof_prb = atoi(argv[optind]); + break; + case 'p': + nof_ports = atoi(argv[optind]); + break; + case 'o': + matlab_file_name = argv[optind]; + break; + case 'v': + verbose++; + break; + case 'e': + cp = CPEXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + int i; + + if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + if (matlab_file_name) { + fmatlab = fopen(matlab_file_name, "w"); + if (!fmatlab) { + perror("fopen"); + return -1; + } + } else { + fmatlab = NULL; + } + + flen = SLOT_LEN(lte_symbol_sz(nof_prb), cp); + + input_buffer = malloc(flen * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(CP_NSYMB(cp) * nof_prb * RE_X_RB * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i +#include +#include +#include +#include + +#include "lte.h" + +int cell_id = -1; +int nof_prb = 6; +int nof_ports = 1; + +void usage(char *prog) { + printf("Usage: %s [cpv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell_id); + printf("\t-p nof_ports [Default %d]\n", nof_ports); + printf("\t-n nof_prb [Default %d]\n", nof_prb); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpnv")) != -1) { + switch(opt) { + case 'p': + nof_ports = atoi(argv[optind]); + break; + case 'n': + nof_prb = atoi(argv[optind]); + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + pcfich_t pcfich; + regs_t regs; + int i, j; + cf_t *ce[MAX_PORTS_CTRL]; + int nof_re; + cf_t *slot_symbols[MAX_PORTS_CTRL]; + int cfi, cfi_rx, nsf, distance; + int cid, max_cid; + + parse_args(argc,argv); + + nof_re = CPNORM_NSYMB * nof_prb * RE_X_RB; + + /* init memory */ + for (i=0;i +#include +#include +#include +#include + +#include "lte.h" + +int cell_id = -1; +int nof_prb = 6; +int nof_ports = 1; +lte_cp_t cp = CPNORM; +phich_resources_t phich_res = R_1; +phich_length_t phich_length = PHICH_NORM; + +void usage(char *prog) { + printf("Usage: %s [cpvgel]\n", prog); + printf("\t-c cell id [Default %d]\n", cell_id); + printf("\t-p nof_ports [Default %d]\n", nof_ports); + printf("\t-n nof_prb [Default %d]\n", nof_prb); + printf("\t-g phich ng factor: 1/6, 1/2, 1, 2 [Default 1]\n"); + printf("\t-e phich extended length [Default normal]\n"); + printf("\t-l extended cyclic prefix [Default normal]\n"); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpnvgel")) != -1) { + switch(opt) { + case 'p': + nof_ports = atoi(argv[optind]); + break; + case 'n': + nof_prb = atoi(argv[optind]); + break; + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'g': + if (!strcmp(argv[optind], "1/6")) { + phich_res = R_1_6; + } else if (!strcmp(argv[optind], "1/2")) { + phich_res = R_1_2; + } else if (!strcmp(argv[optind], "1")) { + phich_res = R_1; + } else if (!strcmp(argv[optind], "2")) { + phich_res = R_2; + } else { + fprintf(stderr, "Invalid phich ng factor %s. Setting to default.\n", argv[optind]); + } + break; + case 'e': + phich_length = PHICH_EXT; + break; + case 'l': + cp = CPEXT; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + phich_t phich; + regs_t regs; + int i, j; + cf_t *ce[MAX_PORTS_CTRL]; + int nof_re; + cf_t *slot_symbols[MAX_PORTS_CTRL]; + char ack[50][PHICH_NORM_NSEQUENCES], ack_rx; + int nsf, distance; + int cid, max_cid; + int ngroup, nseq, max_nseq; + + parse_args(argc,argv); + + max_nseq = CP_ISNORM(cp)?PHICH_NORM_NSEQUENCES:PHICH_EXT_NSEQUENCES; + + nof_re = CPNORM_NSYMB * nof_prb * RE_X_RB; + + /* init memory */ + for (i=0;iREK!nn%DyCuP-KZjl!Wj-&sZu6 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 literal 0 HcmV?d00001 diff --git a/lte/lib/phch/test/signal.10M.dat b/lte/lib/phch/test/signal.10M.dat new file mode 100644 index 0000000000000000000000000000000000000000..27a3195c69bdd55eb19ade1e1d784fc0198cd383 GIT binary patch literal 61448 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 literal 0 HcmV?d00001 diff --git a/lte/lib/scrambling/src/scrambling.c b/lte/lib/scrambling/src/scrambling.c index c0140fd29..d9fbcab5e 100644 --- a/lte/lib/scrambling/src/scrambling.c +++ b/lte/lib/scrambling/src/scrambling.c @@ -32,17 +32,11 @@ #include #include "lte/scrambling/scrambling.h" -/** - * @ingroup Soft-bit Scrambling - * Scrambles the input softbit-sequence (floats) with the scrambling - * sequence (32-bit integers). - * - */ -void scrambling_float(sequence_t *s, float *data) { - scrambling_float_offset(s, data, 0, s->len); +void scrambling_f(sequence_t *s, float *data) { + scrambling_f_offset(s, data, 0, s->len); } -void scrambling_float_offset(sequence_t *s, float *data, int offset, int len) { +void scrambling_f_offset(sequence_t *s, float *data, int offset, int len) { int i; assert (len + offset <= s->len); @@ -51,12 +45,20 @@ void scrambling_float_offset(sequence_t *s, float *data, int offset, int len) { } } -/** - * @ingroup Bit Scrambling - * Directly scrambles the input bit-sequence (char) with the scrambling - * sequence. - */ -void scrambling_bit(sequence_t *s, char *data) { +void scrambling_c(sequence_t *s, cf_t *data) { + scrambling_c_offset(s, data, 0, s->len); +} + +void scrambling_c_offset(sequence_t *s, cf_t *data, int offset, int len) { + int i; + assert (len + offset <= s->len); + + for (i = 0; i < len; i++) { + data[i] = data[i]*(1-2*s->c[i+offset]); + } +} + +void scrambling_b(sequence_t *s, char *data) { int i; for (i = 0; i < s->len; i++) { @@ -64,7 +66,7 @@ void scrambling_bit(sequence_t *s, char *data) { } } -void scrambling_bit_offset(sequence_t *s, char *data, int offset, int len) { +void scrambling_b_offset(sequence_t *s, char *data, int offset, int len) { int i; assert (len + offset <= s->len); for (i = 0; i < len; i++) { @@ -77,14 +79,18 @@ void scrambling_bit_offset(sequence_t *s, char *data, int offset, int len) { int compute_sequences(scrambling_hl* h) { switch (h->init.channel) { - case PBCH: - return sequence_pbch(&h->obj.seq[0], h->init.nof_symbols == CPNORM_NSYMB, + case SCRAMBLING_PBCH: + return sequence_pbch(&h->obj.seq[0], h->init.nof_symbols == CPNORM_NSYMB?CPNORM:CPEXT, h->init.cell_id); - case PDSCH: - case PCFICH: - case PDCCH: - case PMCH: - case PUCCH: + case SCRAMBLING_PDSCH: + case SCRAMBLING_PCFICH: + for (int ns=0;nsobj.seq[ns], 2*ns, h->init.cell_id); + } + return 0; + case SCRAMBLING_PDCCH: + case SCRAMBLING_PMCH: + case SCRAMBLING_PUCCH: fprintf(stderr, "Not implemented\n"); return -1; default: @@ -103,7 +109,7 @@ int scrambling_initialize(scrambling_hl* h) { /** This function can be called in a subframe (1ms) basis for LTE */ int scrambling_work(scrambling_hl* hl) { int sf; - if (hl->init.channel == PBCH) { + if (hl->init.channel == SCRAMBLING_PBCH) { sf = 0; } else { sf = hl->ctrl_in.subframe; @@ -112,10 +118,10 @@ int scrambling_work(scrambling_hl* hl) { if (hl->init.hard) { memcpy(hl->output, hl->input, sizeof(char) * hl->in_len); - scrambling_bit(seq, hl->output); + scrambling_b(seq, hl->output); } else { memcpy(hl->output, hl->input, sizeof(float) * hl->in_len); - scrambling_float(seq, hl->output); + scrambling_f(seq, hl->output); } hl->out_len = hl->in_len; return 0; diff --git a/lte/lib/scrambling/test/scrambling_test.c b/lte/lib/scrambling/test/scrambling_test.c index 8c4473bc1..b246c4a84 100644 --- a/lte/lib/scrambling/test/scrambling_test.c +++ b/lte/lib/scrambling/test/scrambling_test.c @@ -118,8 +118,8 @@ int main(int argc, char **argv) { scrambled_b[i] = input_b[i]; } - scrambling_bit(&seq, scrambled_b); - scrambling_bit(&seq, scrambled_b); + scrambling_b(&seq, scrambled_b); + scrambling_b(&seq, scrambled_b); for (i=0;iforce_N_id_2 = -1; q->threshold = 1.5; q->pss_mode = PEAK_MEAN; + q->detect_cp = true; for (N_id_2=0;N_id_2<3;N_id_2++) { if (pss_synch_init(&q->pss[N_id_2], frame_size)) { @@ -76,7 +77,7 @@ void sync_free(sync_t *q) { void sync_pss_det_absolute(sync_t *q) { q->pss_mode = ABSOLUTE; } -void sync_pss_det_peakmean(sync_t *q) { +void sync_pss_det_peak_to_avg(sync_t *q) { q->pss_mode = PEAK_MEAN; } @@ -88,6 +89,11 @@ void sync_force_N_id_2(sync_t *q, int force_N_id_2) { q->force_N_id_2 = force_N_id_2; } +void sync_force_cp(sync_t *q, lte_cp_t cp) { + q->cp = cp; + q->detect_cp = false; +} + int sync_get_cell_id(sync_t *q) { if (q->N_id_1 >=0 && q->N_id_2 >= 0) { return q->N_id_1*3 + q->N_id_2; @@ -116,10 +122,15 @@ float sync_get_peak_to_avg(sync_t *q) { return q->peak_to_avg; } +lte_cp_t sync_get_cp(sync_t *q) { + return q->cp; +} + int sync_run(sync_t *q, cf_t *input) { - int N_id_2, peak_pos[3], sss_idx; + int N_id_2, peak_pos[3], sss_idx_n, sss_idx_e; int m0, m1; - float m0_value, m1_value; + float m0_value_e, m1_value_e,m0_value_n, m1_value_n; + int slot_id_e, N_id_1_e, slot_id_n, N_id_1_n; float peak_value[3]; float mean_value[3]; float max=-999; @@ -163,28 +174,69 @@ int sync_run(sync_t *q, cf_t *input) { } if (peak_detected) { - q->cfo = pss_synch_cfo_compute(&q->pss[N_id_2], &input[peak_pos[N_id_2]-128]); INFO("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold, q->cfo); - sss_idx = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); - if (sss_idx>= 0) { - sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx], - &m0, &m0_value, &m1, &m1_value); + /* Make sure we have enough room to find SSS sequence */ + sss_idx_n = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); + sss_idx_e = peak_pos[N_id_2]-2*(128+CP(128,CPEXT_LEN)); + + if (q->detect_cp) { + if (sss_idx_n < 0 || sss_idx_e < 0) { + INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e); + return -1; + } + } else { + if (CP_ISNORM(q->cp)) { + if (sss_idx_n < 0) { + INFO("Not enough room to decode SSS (%d)\n", sss_idx_n); + return -1; + } + } else { + if (sss_idx_e < 0) { + INFO("Not enough room to decode SSS (%d)\n", sss_idx_e); + return -1; + } + } + } + + /* try Normal CP length */ + if (q->detect_cp || CP_ISNORM(q->cp)) { + sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_n], + &m0, &m0_value_n, &m1, &m1_value_n); + + slot_id_n = 2 * sss_synch_subframe(m0, m1); + N_id_1_n = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + } - q->N_id_2 = N_id_2; - q->slot_id = 2 * sss_synch_subframe(m0, m1); - q->N_id_1 = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + if (q->detect_cp || CP_ISEXT(q->cp)) { + /* Now try Extended CP length */ + sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_e], + &m0, &m0_value_e, &m1, &m1_value_e); - INFO("SSS detected N_id_1=%d, slot_idx=%d, m0=%d, m1=%d\n", - q->N_id_1, q->slot_id, m0, m1); + slot_id_e = 2 * sss_synch_subframe(m0, m1); + N_id_1_e = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + } - return peak_pos[N_id_2]; + /* Correlation with extended CP hypoteshis is greater than with normal? */ + if ((q->detect_cp && m0_value_e * m1_value_e > m0_value_n * m1_value_n) + || CP_ISEXT(q->cp)) { + q->cp = CPEXT; + q->slot_id = slot_id_e; + q->N_id_1 = N_id_1_e; + /* then is normal CP */ } else { - return -1; + q->cp = CPNORM; + q->slot_id = slot_id_n; + q->N_id_1 = N_id_1_n; } + q->N_id_2 = N_id_2; + INFO("SSS detected N_id_1=%d, slot_idx=%d, %s CP\n", + q->N_id_1, q->slot_id, CP_ISNORM(q->cp)?"Normal":"Extended"); + return peak_pos[N_id_2]; + } else { return -1; } diff --git a/lte/lib/sync/test/CMakeLists.txt b/lte/lib/sync/test/CMakeLists.txt index e810e412c..425edff73 100644 --- a/lte/lib/sync/test/CMakeLists.txt +++ b/lte/lib/sync/test/CMakeLists.txt @@ -28,6 +28,8 @@ TARGET_LINK_LIBRARIES(sync_test lte) ADD_TEST(sync_test_100 sync_test -o 100) ADD_TEST(sync_test_400 sync_test -o 400) +ADD_TEST(sync_test_100_e sync_test -o 100 -e) +ADD_TEST(sync_test_400_e sync_test -o 400 -e) ######################################################################## # CFO TEST diff --git a/lte/lib/sync/test/sync_test.c b/lte/lib/sync/test/sync_test.c index 348910894..a74504b82 100644 --- a/lte/lib/sync/test/sync_test.c +++ b/lte/lib/sync/test/sync_test.c @@ -37,18 +37,21 @@ #include "lte.h" int cell_id = -1, offset = 0; +lte_cp_t cp = CPNORM; #define FLEN 9600 void usage(char *prog) { - printf("Usage: %s [co]\n", prog); + printf("Usage: %s [coev]\n", prog); printf("\t-c cell_id [Default check for all]\n"); printf("\t-o offset [Default %d]\n", offset); + printf("\t-e extended CP [Default normal]\n"); + printf("\t-v verbose\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "co")) != -1) { + while ((opt = getopt(argc, argv, "coev")) != -1) { switch (opt) { case 'c': cell_id = atoi(argv[optind]); @@ -56,6 +59,12 @@ void parse_args(int argc, char **argv) { case 'o': offset = atoi(argv[optind]); break; + case 'e': + cp = CPEXT; + break; + case 'v': + verbose++; + break; default: usage(argv[0]); exit(-1); @@ -87,7 +96,7 @@ int main(int argc, char **argv) { exit(-1); } - if (lte_ifft_init(&ifft, CPNORM, 6)) { + if (lte_ifft_init(&ifft, cp, 6)) { fprintf(stderr, "Error creating iFFT object\n"); exit(-1); } @@ -106,7 +115,7 @@ int main(int argc, char **argv) { } else { cid = cell_id; max_cid = cell_id; - } +} while(cid <= max_cid) { N_id_2 = cid%3; @@ -116,8 +125,8 @@ int main(int argc, char **argv) { for (ns=0;ns<2;ns++) { memset(buffer, 0, sizeof(cf_t) * FLEN); - pss_put_slot(pss_signal, buffer, 6, CPNORM); - sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, CPNORM); + pss_put_slot(pss_signal, buffer, 6, cp); + sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, cp); /* Transform to OFDM symbols */ memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN); @@ -135,6 +144,10 @@ int main(int argc, char **argv) { printf("ns != find_ns\n", 10 * ns, find_ns); exit(-1); } + if (sync_get_cp(&sync) != cp) { + printf("Detected CP should be %s\n", CP_ISNORM(cp)?"Normal":"Extended"); + exit(-1); + } } cid++; } @@ -150,3 +163,4 @@ int main(int argc, char **argv) { printf("Ok\n"); exit(0); } + diff --git a/matlab/rate_algorithm.m b/matlab/rate_algorithm.m new file mode 100644 index 000000000..0edd42697 --- /dev/null +++ b/matlab/rate_algorithm.m @@ -0,0 +1,20 @@ +F=100000./(1:100); +PQ=(1:50); + +PRB=50; +Mmax=1024; +N=PRB*12; + +Fmin=N*15*1.1; +Fs=F(F>Fmin); + +for i=length(Fs):-1:1 + for p=PQ + for q=PQ + if (mod(p/q*Fs(i),15)==0 && p/q*Fs(i)/15 Date: Fri, 28 Mar 2014 10:27:49 +0000 Subject: [PATCH 24/25] Added SSS disable option to sync --- lte/include/lte/sync/sync.h | 4 ++ lte/lib/sync/src/sync.c | 97 ++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/lte/include/lte/sync/sync.h b/lte/include/lte/sync/sync.h index 78cf88d5d..706c45fd2 100644 --- a/lte/include/lte/sync/sync.h +++ b/lte/include/lte/sync/sync.h @@ -61,6 +61,7 @@ typedef struct { float cfo; lte_cp_t cp; bool detect_cp; + bool sss_en; }sync_t; @@ -81,6 +82,9 @@ void sync_pss_det_peak_to_avg(sync_t *q); void sync_force_N_id_2(sync_t *q, int force_N_id_2); /* Forces the synchronizer to skip CP detection (useful for tracking mode) */ void sync_force_cp(sync_t *q, lte_cp_t cp); +/* Enables/Disables SSS detection (useful for tracking mode) */ +void sync_sss_en(sync_t *q, bool enabled); + /* Gets the slot id (0 or 10) */ int sync_get_slot_id(sync_t *q); diff --git a/lte/lib/sync/src/sync.c b/lte/lib/sync/src/sync.c index 28d8a5cbf..de777e8ca 100644 --- a/lte/lib/sync/src/sync.c +++ b/lte/lib/sync/src/sync.c @@ -94,6 +94,10 @@ void sync_force_cp(sync_t *q, lte_cp_t cp) { q->detect_cp = false; } +void sync_sss_en(sync_t *q, bool enabled) { + q->sss_en = enabled; +} + int sync_get_cell_id(sync_t *q) { if (q->N_id_1 >=0 && q->N_id_2 >= 0) { return q->N_id_1*3 + q->N_id_2; @@ -179,62 +183,67 @@ int sync_run(sync_t *q, cf_t *input) { INFO("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold, q->cfo); - /* Make sure we have enough room to find SSS sequence */ - sss_idx_n = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); - sss_idx_e = peak_pos[N_id_2]-2*(128+CP(128,CPEXT_LEN)); + if (q->sss_en) { - if (q->detect_cp) { - if (sss_idx_n < 0 || sss_idx_e < 0) { - INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e); - return -1; - } - } else { - if (CP_ISNORM(q->cp)) { - if (sss_idx_n < 0) { - INFO("Not enough room to decode SSS (%d)\n", sss_idx_n); + /* Make sure we have enough room to find SSS sequence */ + sss_idx_n = peak_pos[N_id_2]-2*(128+CP(128,CPNORM_LEN)); + sss_idx_e = peak_pos[N_id_2]-2*(128+CP(128,CPEXT_LEN)); + + if (q->detect_cp) { + if (sss_idx_n < 0 || sss_idx_e < 0) { + INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e); return -1; } } else { - if (sss_idx_e < 0) { - INFO("Not enough room to decode SSS (%d)\n", sss_idx_e); - return -1; + if (CP_ISNORM(q->cp)) { + if (sss_idx_n < 0) { + INFO("Not enough room to decode SSS (%d)\n", sss_idx_n); + return -1; + } + } else { + if (sss_idx_e < 0) { + INFO("Not enough room to decode SSS (%d)\n", sss_idx_e); + return -1; + } } } - } - /* try Normal CP length */ - if (q->detect_cp || CP_ISNORM(q->cp)) { - sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_n], - &m0, &m0_value_n, &m1, &m1_value_n); + /* try Normal CP length */ + if (q->detect_cp || CP_ISNORM(q->cp)) { + sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_n], + &m0, &m0_value_n, &m1, &m1_value_n); - slot_id_n = 2 * sss_synch_subframe(m0, m1); - N_id_1_n = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); - } + slot_id_n = 2 * sss_synch_subframe(m0, m1); + N_id_1_n = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + } - if (q->detect_cp || CP_ISEXT(q->cp)) { - /* Now try Extended CP length */ - sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_e], - &m0, &m0_value_e, &m1, &m1_value_e); + if (q->detect_cp || CP_ISEXT(q->cp)) { + /* Now try Extended CP length */ + sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_e], + &m0, &m0_value_e, &m1, &m1_value_e); - slot_id_e = 2 * sss_synch_subframe(m0, m1); - N_id_1_e = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); - } + slot_id_e = 2 * sss_synch_subframe(m0, m1); + N_id_1_e = sss_synch_N_id_1(&q->sss[N_id_2], m0, m1); + } - /* Correlation with extended CP hypoteshis is greater than with normal? */ - if ((q->detect_cp && m0_value_e * m1_value_e > m0_value_n * m1_value_n) - || CP_ISEXT(q->cp)) { - q->cp = CPEXT; - q->slot_id = slot_id_e; - q->N_id_1 = N_id_1_e; - /* then is normal CP */ - } else { - q->cp = CPNORM; - q->slot_id = slot_id_n; - q->N_id_1 = N_id_1_n; + /* Correlation with extended CP hypoteshis is greater than with normal? */ + if ((q->detect_cp && m0_value_e * m1_value_e > m0_value_n * m1_value_n) + || CP_ISEXT(q->cp)) { + q->cp = CPEXT; + q->slot_id = slot_id_e; + q->N_id_1 = N_id_1_e; + /* then is normal CP */ + } else { + q->cp = CPNORM; + q->slot_id = slot_id_n; + q->N_id_1 = N_id_1_n; + } + q->N_id_2 = N_id_2; + + INFO("SSS detected N_id_1=%d, slot_idx=%d, %s CP\n", + q->N_id_1, q->slot_id, CP_ISNORM(q->cp)?"Normal":"Extended"); } - q->N_id_2 = N_id_2; - INFO("SSS detected N_id_1=%d, slot_idx=%d, %s CP\n", - q->N_id_1, q->slot_id, CP_ISNORM(q->cp)?"Normal":"Extended"); + return peak_pos[N_id_2]; } else { From 644e91e5df72ad39af77aacb3e29f9978087ba9c Mon Sep 17 00:00:00 2001 From: nodeuser Date: Fri, 28 Mar 2014 12:49:40 +0000 Subject: [PATCH 25/25] Fixed bug in sync --- lte/lib/sync/src/sync.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lte/lib/sync/src/sync.c b/lte/lib/sync/src/sync.c index de777e8ca..6e2172b46 100644 --- a/lte/lib/sync/src/sync.c +++ b/lte/lib/sync/src/sync.c @@ -41,6 +41,7 @@ int sync_init(sync_t *q, int frame_size) { q->threshold = 1.5; q->pss_mode = PEAK_MEAN; q->detect_cp = true; + q->sss_en = true; for (N_id_2=0;N_id_2<3;N_id_2++) { if (pss_synch_init(&q->pss[N_id_2], frame_size)) { @@ -207,7 +208,10 @@ int sync_run(sync_t *q, cf_t *input) { } } } - + N_id_1_e = -1; + N_id_1_n = -1; + slot_id_e = -1; + slot_id_n = -1; /* try Normal CP length */ if (q->detect_cp || CP_ISNORM(q->cp)) { sss_synch_m0m1(&q->sss[N_id_2], &input[sss_idx_n],