diff options
author | Tom Tsou <tom.tsou@ettus.com> | 2017-06-03 18:31:48 -0700 |
---|---|---|
committer | Tom Tsou <tom.tsou@ettus.com> | 2017-06-14 10:15:04 -0700 |
commit | b79895c9990c77f7aa5c72ca3795e9d0d8443dfb (patch) | |
tree | be7b53c67e18019e7619a7288bca3033580fcb6e /Transceiver52M/osmo-siggen.cpp | |
parent | 980525c8a9cc620314aa36e566d30e0592f280d5 (diff) |
siggen: Add osmo-siggen for GSM/EDGE test signal generationttsou/siggen
* Adds 4 and 1 pulse Laurent GMSK modulators
* Adds NCO based modulator
* Adds synchronization and frequency burst generators
* Adds GPIO frame trigger on GPIO-0
* Tested on USRP B210 only
* Requires C++14 support
B210 GPIO Pin Map
J504
---------
fp_gpio<0> | 1 | 2 | fp_gpio<1>
---------
fp_gpio<2> | 3 | 4 | fp_gpio<3>
---------
fp_gpio<4> | 5 | 6 | fp_gpio<5>
---------
fp_gpio<6> | 7 | 8 | fp_gpio<7>
---------
gnd | 9 | 10| gnd
---------
Change-Id: I891725d7f0cfa97c79e64f978a60dc11a206195c
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
Diffstat (limited to 'Transceiver52M/osmo-siggen.cpp')
-rw-r--r-- | Transceiver52M/osmo-siggen.cpp | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/Transceiver52M/osmo-siggen.cpp b/Transceiver52M/osmo-siggen.cpp new file mode 100644 index 0000000..6005c58 --- /dev/null +++ b/Transceiver52M/osmo-siggen.cpp @@ -0,0 +1,490 @@ +/* + * GSM Signal Generator + * + * Copyright (C) 2017 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * See the COPYING file in the main directory for details. + * + * Author: Tom Tsou <tom.tsou@ettus.com> + */ + +#include <limits.h> +#include <unistd.h> +#include <getopt.h> +#include <algorithm> +#include <functional> +#include <memory> +#include <map> +#include <GSMCommon.h> +#include <Logger.h> +#include <Configuration.h> +#include <GSMCommon.h> +#include "sigProcLib.h" +#include "radioDevice.h" + +extern "C" { +#include "convolve.h" +#include "convert.h" +} + +ConfigurationTable gConfig; + +#define DEFAULT_TX_SPS 4 +#define DEFAULT_TX_AMPL 0.5 +#define DEFAULT_TX_GAIN 50 +#define DEFAULT_TX_FREQ 1e9 +#define DEFAULT_OFFSET 0.0 + +using namespace std; + +enum GsmModType { + MOD_LAURENT4, + MOD_LAURENT2, + MOD_LAURENT1, + MOD_NCO, + NUM_MODS, +}; + +enum BurstType { + BURST_NORMAL, + BURST_ACCESS, + BURST_FREQ, + BURST_SYNC, + BURST_EDGE, + NUM_BURSTS, +}; + +enum BurstTSC { + TSC0, TSC1, TSC2, TSC3, TSC4, TSC5, TSC6, TSC7, +}; + +struct Config { + string args = ""; + string logl = "NOTICE"; + unsigned sps = DEFAULT_TX_SPS; + double offset = DEFAULT_OFFSET; + bool swap = false; + float ampl = DEFAULT_TX_AMPL; + double freq = DEFAULT_TX_FREQ; + double gain = DEFAULT_TX_GAIN; + BurstTSC tsc = TSC0; + GsmModType mod = MOD_LAURENT2; + BurstType burst = BURST_NORMAL; + RadioDevice::ReferenceType ref = RadioDevice::REF_INTERNAL; +}; + +static shared_ptr<signalVector> modulateGMSK(BitVector &bits, GsmModType modType) +{ + switch (modType) { + case MOD_LAURENT4: return shared_ptr<signalVector>(modulateBurstLaurent4(bits)); + case MOD_LAURENT2: return shared_ptr<signalVector>(modulateBurstLaurent2(bits)); + case MOD_LAURENT1: return shared_ptr<signalVector>(modulateBurstLaurent1(bits)); + case MOD_NCO: return shared_ptr<signalVector>(modulateBurstNCO(bits)); + default: return shared_ptr<signalVector>(modulateBurstLaurent2(bits)); + }; +} + +static shared_ptr<signalVector> generateNormalBurst(BurstTSC tsc, GsmModType modType) +{ + auto tail = vector<char>(3, 0); + auto data0 = vector<char>(57); + auto data1 = vector<char>(57); + auto steal = vector<char>(1, 0); + auto train = vector<char>(26); + + auto ti = begin(GSM::gTrainingSequence[tsc]); + for (auto &t : train) t = *ti++; + for (auto &d : data0) d = rand() % 2; + for (auto &d : data1) d = rand() % 2; + + auto bits = BitVector(NORMAL_BURST_NBITS); + auto bi = bits.begin(); + + for (auto t : tail) *bi++ = t; + for (auto d : data0) *bi++ = d; + for (auto s : steal) *bi++ = s; + for (auto t : train) *bi++ = t; + for (auto s : steal) *bi++ = s; + for (auto d : data1) *bi++ = d; + for (auto t : tail) *bi++ = t; + + return modulateGMSK(bits, modType); +} + +static shared_ptr<signalVector> generateRABurst(GsmModType modType) +{ + auto tail0 = vector<char>(8, 0); + auto train = vector<char>(41); + auto data = vector<char>(36); + auto tail1 = vector<char>(3, 0); + + auto ti = begin(GSM::gRACHBurst); + for (auto &t : train) t = *ti++; + for (auto &d : data) d = rand() % 2; + + auto bits = BitVector(88); + auto bi = bits.begin(); + + for (auto t : tail0) *bi++ = t; + for (auto t : train) *bi++ = t; + for (auto d : data) *bi++ = d; + for (auto t : tail1) *bi++ = t; + + return modulateGMSK(bits, modType); +} + +static shared_ptr<signalVector> generateFreqBurst(GsmModType modType) +{ + auto tail = vector<char>(3, 0); + auto fixed = vector<char>(142); + + auto bits = BitVector(148); + auto bi = bits.begin(); + + for (auto t : tail) *bi++ = t; + for (auto f : fixed) *bi++ = f; + for (auto t : tail) *bi++ = t; + + return modulateGMSK(bits, modType); +} + +static shared_ptr<signalVector> generateSyncBurst(GsmModType modType) +{ + auto tail = vector<char>(3, 0); + auto data0 = vector<char>(39); + auto data1 = vector<char>(39); + + /* 64 length synchronization sequence */ + vector<char> train { + 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, + }; + for (auto &d : data0) d = rand() % 2; + for (auto &d : data1) d = rand() % 2; + + auto bits = BitVector(148); + auto bi = bits.begin(); + + for (auto t : tail) *bi++ = t; + for (auto d : data0) *bi++ = d; + for (auto t : train) *bi++ = t; + for (auto d : data1) *bi++ = d; + for (auto t : tail) *bi++ = t; + + return modulateGMSK(bits, modType); +} + +static shared_ptr<signalVector> generateEDGEBurst(BurstTSC tsc) +{ + auto tail = vector<Complex<float>>(3); + auto data0 = vector<Complex<float>>(58); + auto train = vector<Complex<float>>(26); + auto data1 = vector<Complex<float>>(58); + + extern const Complex<float> psk8_table[8]; + for (auto &t : tail) t = psk8_table[0b111]; + for (auto &d : data0) d = psk8_table[rand() % 8]; + for (auto &d : data1) d = psk8_table[rand() % 8]; + + auto ti = begin(GSM::gEdgeTrainingSequence[tsc]); + for (auto &t : train) { + unsigned i = (*(ti + 0) & 0b001) << 0 | + (*(ti + 1) & 0b001) << 1 | + (*(ti + 2) & 0b001) << 2; + t = psk8_table[i]; + ti += 3; + } + + /* NBITS refers to 148 symbols in this case */ + auto burst = signalVector(NORMAL_BURST_NBITS); + auto bi = burst.begin(); + + for (auto t : tail) *bi++ = t; + for (auto d : data0) *bi++ = d; + for (auto t : train) *bi++ = t; + for (auto d : data1) *bi++ = d; + for (auto t : tail) *bi++ = t; + + return shared_ptr<signalVector>(shapeEdgeBurst(burst)); +} + +/* Perform float-integer conversion and write to the device */ +static void sendBurst(shared_ptr<RadioDevice> usrp, TIMESTAMP &ts, + shared_ptr<signalVector> sv, float ampl) +{ + auto buffer = vector<Complex<short>>(sv->size()); + + transform(sv->begin(), sv->end(), buffer.begin(), [ampl](Complex<float> x) { + const float scale = SHRT_MAX * ampl; + return Complex<short>(x.real()*scale, x.imag()*scale); + }); + + auto buffers = vector<short *>(1, reinterpret_cast<short *>(&buffer.front())); + ts += usrp->writeSamples(buffers, buffer.size(), nullptr, ts, true); +} + +static void print_help() +{ + fprintf(stdout, "Options:\n" + " -h, --help This text\n" + " -a, --args UHD device args\n" + " -l --log Logging level (%s)\n" + " -b, --burst Burst type (%s)\n" + " -r, --ref Frequency reference (%s)\n" + " -f, --freq Tx RF frequency\n" + " -g, --gain Tx RF gain\n" + " -s, --sps Tx samples-per-symbol (only 4 supported)\n" + " -m, --mod GSMK modulator type (%s)\n" + " -p, --ampl Tx amplitude (0.0 - 1.0)\n" + " -o, --offset Baseband frequency offset\n" + " -t, --tsc Normal and EDGE burst training sequence (0-7)\n" + " -S, --swap Swap channels\n\n", + "'err', 'warn', 'notice', 'info', 'debug'", + "'normal', 'access', 'freq', 'sync', 'edge'", + "'internal', 'external', 'gps'", + "'laurent4', 'laurent2', 'laurent1', 'nco'" + ); +} + +static void print_config(Config &config) +{ + const map<GsmModType, string> modMap = { + { MOD_LAURENT4, "Laurent-4" }, + { MOD_LAURENT2, "Laurent-2" }, + { MOD_LAURENT1, "Laurent-1" }, + { MOD_NCO, "NCO" }, + }; + + const map<BurstType, string> burstMap = { + { BURST_NORMAL, "Normal" }, + { BURST_ACCESS, "Access" }, + { BURST_FREQ, "Frequency" }, + { BURST_SYNC, "Synchronization" }, + { BURST_EDGE, "EDGE" }, + }; + + const map<RadioDevice::ReferenceType, string> refMap = { + { RadioDevice::REF_INTERNAL, "Internal" }, + { RadioDevice::REF_EXTERNAL, "External" }, + { RadioDevice::REF_GPS, "GPS" }, + }; + + auto yesno = [](bool x) { return x ? "yes" : "no"; }; + + ostringstream ost(""); + ost << "Config Settings" << endl; + ost << " Log level............... " << config.logl << std::endl; + ost << " Device args............. " << "\"" << config.args << "\"" << endl; + ost << " Samples-per-Symbol...... " << config.sps << endl; + ost << " RF frequency............ " << config.freq/1e9 << " GHz" << endl; + ost << " RF gain................. " << config.gain << " dB" << endl; + ost << " Reference............... " << refMap.at(config.ref) << endl; + ost << " Burst type.............. " << burstMap.at(config.burst) << endl; + ost << " Modulator type.......... " << modMap.at(config.mod) << endl; + ost << " Baseband offset......... " << config.offset/1e6 << " MHz" << endl; + ost << " Swap channels........... " << yesno(config.swap) << endl; + cout << ost << endl; +} + +static bool handle_options(int argc, char **argv, Config &config) +{ + int option; + + const struct option longopts[] = { + { "help", 0, nullptr, 'h' }, + { "log", 1, nullptr, 'l' }, + { "args", 1, nullptr, 'a' }, + { "ref" , 1, nullptr, 'r' }, + { "freq", 1, nullptr, 'f' }, + { "gain", 1, nullptr, 'g' }, + { "mod", 1, nullptr, 'm' }, + { "offset", 1, nullptr, 'o' }, + { "sps", 1, nullptr, 's' }, + { "ampl", 1, nullptr, 'p' }, + { "tsc", 1, nullptr, 'r' }, + { "burst", 1, nullptr, 'b' }, + { "swap", 1, nullptr, 'w' }, + }; + + const map<string, string> logMap = { + { "emerg", "EMERG" }, + { "EMERG", "EMERG" }, + { "alert", "ALERT" }, + { "ALERT", "ALERT" }, + { "err", "ERR" }, + { "ERR", "ERR" }, + { "warn", "WARNING" }, + { "WARN", "WARNING" }, + { "notice", "NOTICE" }, + { "NOTICE", "NOTICE" }, + { "info", "INFO" }, + { "INFO", "INFO" }, + { "debug", "DEBUG" }, + { "DEBUG", "DEBUG" }, + }; + + const map<string, GsmModType> modMap = { + { "laurent4", MOD_LAURENT4 }, + { "laurent2", MOD_LAURENT2 }, + { "laurent1", MOD_LAURENT1 }, + { "nco", MOD_NCO }, + }; + + const map<string, BurstType> burstMap = { + { "normal", BURST_NORMAL }, + { "access", BURST_ACCESS }, + { "freq", BURST_FREQ }, + { "sync", BURST_SYNC }, + { "edge", BURST_EDGE }, + }; + + const map<string, RadioDevice::ReferenceType> refMap = { + { "internal", RadioDevice::REF_INTERNAL }, + { "external", RadioDevice::REF_EXTERNAL }, + { "gpsdo", RadioDevice::REF_GPS }, + { "gps", RadioDevice::REF_GPS }, + }; + + while ((option = getopt_long(argc, argv, "ha:l:r:f:g:m:o:s:p:t:b:w", longopts, nullptr)) != -1) { + switch (option) { + case 'a': + config.args = optarg; + break; + case 'f': + config.freq = atof(optarg); + break; + case 'g': + config.gain = atof(optarg); + break; + case 'o': + config.offset = atof(optarg); + break; + case 's': + if (atoi(optarg) != 4) { + printf("Unsupported SPS = %i\n", atoi(optarg)); + return false; + } + break; + case 'p': + config.ampl = atof(optarg); + break; + case 't': + if (atoi(optarg) < TSC0 || atoi(optarg) > TSC7) { + printf("Invalid training sequence %i", atoi(optarg)); + return false; + } + config.tsc = static_cast<BurstTSC>(atoi(optarg)); + break; + case 'w': + config.swap = true; + break; + case 'l': + if (logMap.count(optarg) > 0) { + config.logl = logMap.at(optarg); + } else { + printf("Invalid log parameter '%s'\n\n", optarg); + return false; + } + break; + case 'r': + if (refMap.count(optarg) > 0) { + config.ref = refMap.at(optarg); + } else { + printf("Invalid reference parameter '%s'\n\n", optarg); + return false; + } + break; + case 'm': + if (modMap.count(optarg) > 0) { + config.mod = modMap.at(optarg); + } else { + printf("Invalid modulation parameter '%s'\n\n", optarg); + return false; + } + break; + case 'b': + if (burstMap.count(optarg) > 0) { + config.burst = burstMap.at(optarg); + } else { + printf("Invalid burst type parameter '%s'\n\n", optarg); + return false; + } + break; + case 'h': + default: + return false; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + Config config; + if (!handle_options(argc, argv, config)) { + print_help(); + return -EINVAL; + } + + print_config(config); + + gLogInit("osmo-siggen", config.logl.c_str(), LOG_LOCAL7); + + convolve_init(); + convert_init(); + sigProcLibSetup(); + + /* Device setup */ + shared_ptr<RadioDevice> usrp(RadioDevice::make(config.sps, config.sps, RadioDevice::NORMAL, 1, config.offset)); + usrp->open(config.args, config.ref, config.swap); + usrp->setTxFreq(config.freq); + usrp->setTxGain(config.gain); + usrp->start(true); + usrp->setPriority(0.5); + + /* Bind all burst-modulator configurations */ + auto makeBurstGenerator = [&config]()->function<shared_ptr<signalVector>()> { + switch (config.burst) { + case BURST_EDGE: return bind(generateEDGEBurst, config.tsc); + case BURST_ACCESS: return bind(generateRABurst, config.mod); + case BURST_FREQ: return bind(generateFreqBurst, config.mod); + case BURST_SYNC: return bind(generateSyncBurst, config.mod); + case BURST_NORMAL: + default: return bind(generateNormalBurst, config.tsc, config.mod); + } + }; + + auto burstGenerator = makeBurstGenerator(); + auto ts = usrp->initialWriteTimestamp(); + auto frameTrigger = []() { + static int tn = 0; + return ++tn % 8 == 0; + }; + + while (1) { + try { + if (frameTrigger()) usrp->triggerGPIO(ts); + sendBurst(usrp, ts, burstGenerator(), config.ampl); + } catch (const exception &e) { + cout << e.what() << endl; + break; + } + } + + sigProcLibDestroy(); +} |