mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-10-30 19:53:35 +00:00 
			
		
		
		
	Compare commits
	
		
			86 Commits
		
	
	
		
			fairwaves/
			...
			rel-0.3.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 71637150a1 | ||
|  | 2393964b77 | ||
|  | 9ee4df8760 | ||
|  | c92dad32dd | ||
|  | 61837c0420 | ||
|  | f83e11fefd | ||
|  | 01aff88ce9 | ||
|  | 11d50d950c | ||
|  | 8bd111c942 | ||
|  | 3808e479aa | ||
|  | bd45a979f8 | ||
|  | b7095c7bc5 | ||
|  | 77ce99ac67 | ||
|  | f58cd8ac83 | ||
|  | 99eb07e232 | ||
|  | 89be118a3b | ||
|  | 6fafd33b13 | ||
|  | 6e55d51747 | ||
|  | 6cae1d7b4b | ||
|  | 28ce315a32 | ||
|  | 10d76b6863 | ||
|  | 708b8b44ae | ||
|  | cb0fc9b21a | ||
|  | 8639fee504 | ||
|  | ca46896cfe | ||
|  | 4a25d6b8f6 | ||
|  | 79baee3a8f | ||
|  | c2ba427b52 | ||
|  | 611212676b | ||
|  | 4ebb289c90 | ||
|  | 2f376a3edf | ||
|  | 2edbe4d366 | ||
|  | a3694bd303 | ||
|  | 2652f2bc39 | ||
|  | 93d9b114b7 | ||
|  | 2ac788b2c3 | ||
|  | d36ef2f57b | ||
|  | caf2abc58f | ||
|  | de1685f6d7 | ||
|  | f3837d26f9 | ||
|  | ddf4743306 | ||
|  | 82f83ced73 | ||
|  | cff4ed9b4c | ||
|  | 6ec26bb788 | ||
|  | a1ff991402 | ||
|  | d09843c692 | ||
|  | e5448ff972 | ||
|  | e48c1367dc | ||
|  | aa60dda99a | ||
|  | 1468a5c3dc | ||
|  | b0e1bd8c22 | ||
|  | 78e1cd20e2 | ||
|  | db9c1b54cb | ||
|  | 099a44abfb | ||
|  | 8c80095017 | ||
|  | d49a6aa136 | ||
|  | 81486e053c | ||
|  | 28d8081e25 | ||
|  | 87ed77b937 | ||
|  | f9d996813d | ||
|  | aa5acc953c | ||
|  | 934da48618 | ||
|  | 7c405a0c1f | ||
|  | 4cafb0fa15 | ||
|  | f611569018 | ||
|  | 354741326c | ||
|  | d2e5c5694e | ||
|  | a3dce85ffc | ||
|  | bb0c68ae61 | ||
|  | 87d158cc2d | ||
|  | 7278a87767 | ||
|  | 63eef9faf2 | ||
|  | d67bd603e9 | ||
|  | 988a464d5d | ||
|  | 1b6ab7d7ee | ||
|  | 980525c8a9 | ||
|  | 70134a01eb | ||
|  | 1fb0ce67d8 | ||
|  | 8ca237b5c2 | ||
|  | 082bbbf8fe | ||
|  | 3bd763d2a1 | ||
|  | ee57357682 | ||
|  | 8537b90dbe | ||
|  | 038fd7fd70 | ||
|  | 0cd246c27a | ||
|  | 61fbf2ec95 | 
							
								
								
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,16 +5,17 @@ | ||||
| Transceiver52M/osmo-trx | ||||
|  | ||||
| # tests | ||||
| CommonLibs/BitVectorTest | ||||
| CommonLibs/ConfigurationTest | ||||
| CommonLibs/F16Test | ||||
| CommonLibs/InterthreadTest | ||||
| CommonLibs/LogTest | ||||
| CommonLibs/RegexpTest | ||||
| CommonLibs/SocketsTest | ||||
| CommonLibs/TimevalTest | ||||
| CommonLibs/URLEncodeTest | ||||
| CommonLibs/VectorTest | ||||
| tests/CommonLibs/BitVectorTest | ||||
| tests/CommonLibs/F16Test | ||||
| tests/CommonLibs/InterthreadTest | ||||
| tests/CommonLibs/LogTest | ||||
| tests/CommonLibs/RegexpTest | ||||
| tests/CommonLibs/SocketsTest | ||||
| tests/CommonLibs/TimevalTest | ||||
| tests/CommonLibs/URLEncodeTest | ||||
| tests/CommonLibs/VectorTest | ||||
| tests/CommonLibs/PRBSTest | ||||
| tests/Transceiver52M/convolve_test | ||||
|  | ||||
| # automake/autoconf | ||||
| *.in | ||||
| @@ -40,6 +41,11 @@ ltmain.sh | ||||
| missing | ||||
| stamp-h1 | ||||
| INSTALL | ||||
| tests/package.m4 | ||||
| tests/testsuite | ||||
| tests/atconfig | ||||
| tests/testsuite.dir | ||||
| tests/testsuite.log | ||||
|  | ||||
| # vim | ||||
| *.sw? | ||||
|   | ||||
							
								
								
									
										83
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -1,18 +1,18 @@ | ||||
| # | ||||
| # Copyright 2008, 2009 Free Software Foundation, Inc. | ||||
| #  | ||||
| # | ||||
| # This file is part of GNU Radio | ||||
| #  | ||||
| # | ||||
| # GNU Radio is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 3, or (at your option) | ||||
| # any later version. | ||||
| #  | ||||
| # | ||||
| # GNU Radio is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| #  | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License along | ||||
| # with this program; if not, write to the Free Software Foundation, Inc., | ||||
| # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| @@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com: | ||||
|     CLI/CLI.h | ||||
|     CommonLibs/Assert.h | ||||
|     CommonLibs/BitVector.cpp | ||||
|     CommonLibs/BitVectorTest.cpp | ||||
|     CommonLibs/Configuration.cpp | ||||
|     CommonLibs/Configuration.h | ||||
|     CommonLibs/ConfigurationTest.cpp | ||||
|     CommonLibs/Interthread.h | ||||
|     CommonLibs/InterthreadTest.cpp | ||||
|     CommonLibs/LinkedLists.cpp | ||||
|     CommonLibs/LinkedLists.h | ||||
|     CommonLibs/Regexp.h | ||||
|     CommonLibs/RegexpTest.cpp | ||||
|     CommonLibs/Sockets.cpp | ||||
|     CommonLibs/Sockets.h | ||||
|     CommonLibs/SocketsTest.cpp | ||||
|     CommonLibs/Threads.cpp | ||||
|     CommonLibs/Threads.h | ||||
|     CommonLibs/Timeval.cpp | ||||
|     CommonLibs/Timeval.h | ||||
|     CommonLibs/TimevalTest.cpp | ||||
|     CommonLibs/Vector.h | ||||
|     CommonLibs/VectorTest.cpp | ||||
|     Control/CallControl.cpp | ||||
|     Control/ControlCommon.cpp | ||||
|     Control/ControlCommon.h | ||||
|     Control/FACCHDispatch.cpp | ||||
|     Control/MobilityManagement.cpp | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     Control/SDCCHDispatch.cpp | ||||
|     GSM/GSM610Tables.cpp | ||||
|     GSM/GSM610Tables.h | ||||
|     GSM/GSMCommon.cpp | ||||
| @@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com: | ||||
|     GSM/GSMTransfer.cpp | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
|     SIP/SIPEngine.h | ||||
|     SIP/SIPInterface.h | ||||
|     SMS/SMSMessages.cpp | ||||
|     SMS/SMSMessages.h | ||||
|     SMS/SMSTransfer.cpp | ||||
|     SMS/SMSTransfer.h | ||||
|     TRXManager/TRXManager.cpp | ||||
|     Transceiver/Complex.h | ||||
|     apps/OpenBTS900.cpp | ||||
|     apps/OpenBTS850.cpp | ||||
|     apps/OpenBTS25c3.cpp | ||||
|     tests/AGCHTest.cpp | ||||
|     tests/BeaconTest.cpp | ||||
|     tests/CallTest.cpp | ||||
|     tests/CallTest2.cpp | ||||
|     tests/LAPDmTest.cpp | ||||
|     tests/LoopbackTest.cpp | ||||
|     tests/RegistrationTest.cpp | ||||
|     tests/TRXSimulator.cpp | ||||
|     tests/CommonLibs/BitVectorTest.cpp | ||||
|     tests/CommonLibs/InterthreadTest.cpp | ||||
|     tests/CommonLibs/SocketsTest.cpp | ||||
|     tests/CommonLibs/TimevalTest.cpp | ||||
|     tests/CommonLibs/VectorTest.cpp | ||||
|  | ||||
| Harvind S. Samra, hssamra@kestrelsp.com: | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
| @@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com: | ||||
|     Transceiver/testRadio.cpp | ||||
|  | ||||
| Raffi Sevlian, raffisev@gmail.com: | ||||
|     Control/CallControl.cpp | ||||
|     Control/ControlCommon.cpp | ||||
|     Control/ControlCommon.h | ||||
|     Control/FACCHDispatch.cpp | ||||
|     Control/MobilityManagement.cpp | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     GSM/GSMCommon.h | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSML1FEC.h | ||||
| @@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com: | ||||
|     GSM/GSMSAPMux.h | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
|     SIP/SIPEngine.cpp | ||||
|     SIP/SIPInterface.cpp | ||||
|     SIP/SIPInterface.h | ||||
|     SIP/SIPMessage.cpp | ||||
|     SIP/SIPMessage.h | ||||
|     SIP/SIPUtility.cpp | ||||
|     SIP/SIPUtility.h | ||||
|     SMS/CMMessage.cpp | ||||
|     SMS/CMMessage.h | ||||
|     SMS/CMProcessor.cpp | ||||
|     SMS/CMProcessor.h | ||||
|     SMS/CMTest.cpp | ||||
|     SMS/RLMessage.cpp | ||||
|     SMS/RLMessage.h | ||||
|     SMS/RLProcessor.cpp | ||||
|     SMS/RLProcessor.h | ||||
|     SMS/SMSMessages.cpp | ||||
|     SMS/SMSMessages.h | ||||
|     SMS/SMSProcessors.cpp | ||||
|     SMS/SMSProcessors.h | ||||
|     SMS/SMSTransfer.cpp | ||||
|     SMS/SMSTransfer.h | ||||
|     SMS/TLMessage.cpp | ||||
|     SMS/TLMessage.h | ||||
|     SMS/TLProcessor.cpp | ||||
|     SMS/TLProcessor.h | ||||
|     TRXManager/TRXManager.h | ||||
|  | ||||
| Alon Levy, alonlevy1@gmail.com | ||||
|     RRLPMessages.cpp | ||||
|     RRLPMessages.h | ||||
|     RRLPTest.cpp | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								COPYING
									
									
									
									
									
								
							| @@ -673,16 +673,16 @@ on the AGPLv3 text. | ||||
| ========================================================================= | ||||
|  | ||||
|  | ||||
| ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS | ||||
| ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX | ||||
|  | ||||
|  | ||||
| Permissive Terms Supplementing the License | ||||
|  | ||||
| 1. Remote Interaction Through IP Networks. | ||||
|  | ||||
| OpenBTS includes an implementation of the GSM network cellular air interface, | ||||
| OsmoTRX is an implementation of the GSM network cellular air interface, | ||||
| as well as other interfaces to IP networks.  The interaction of cellular | ||||
| handsets with the OpenBTS software is considered "remote network interaction" | ||||
| handsets with the OsmoTRX software is considered "remote network interaction" | ||||
| for the purposes of the Affero General Public License and cellular users are | ||||
| subject to the source code access requirements of Section 13 of AGPLv3 ("Remote | ||||
| Network Interaction; Use with the GNU General Public License"). | ||||
| @@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13 | ||||
| is an additional permission granted to you.   | ||||
|  | ||||
|  | ||||
| Non-Permissive Terms Supplementing The License | ||||
|  | ||||
| 1. Trademarks. | ||||
|  | ||||
| "OpenBTS" is a trademark of Range Networks, Inc., registered with | ||||
| the US Patent and Trademark Office.  Your use of OpenBTS software under a GPL | ||||
| license does not include the right to use the OpenBTS trademark in commerce. | ||||
| This additional non-permissive term is consistent with Section 7 of the AGPLv3 | ||||
| license. | ||||
|  | ||||
|  | ||||
| END OF ADDITIONAL TERMS | ||||
|  | ||||
|  | ||||
| @@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS | ||||
| How to comply with Section 13 of the AGPLv3 license. | ||||
|  | ||||
| The recommended method for compliance with Section 13 of the AGPLv3 license is | ||||
| to deliver a text message to each handset that attaches to the OpenBTS cellular | ||||
| network.  At a minimum, that text message should include the string "OpenBTS | ||||
| AGPLv3" and a URL that can be used to access the OpenBTS source code.  This | ||||
| to deliver a text message to each handset that attaches to the cellular | ||||
| network which uses OsmoTRX.  At a minimum, that text message should include the string | ||||
| "OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code.  This | ||||
| message need not be delivered to handsets that are denied registration with the | ||||
| network, since those handsets have been denied service. | ||||
|  | ||||
| In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message" | ||||
| feature.  See the OpenBTS.config.example file for more information on the use of | ||||
| this feature for AGPLv3 compliance. | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,422 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * Copyright 2011, 2012 Range Networks, Inc. | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef CONFIGURATION_H | ||||
| #define CONFIGURATION_H | ||||
|  | ||||
|  | ||||
| #include "sqlite3util.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <regex.h> | ||||
|  | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
| #include <iostream> | ||||
|  | ||||
| #include <Threads.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| /** A class for configuration file errors. */ | ||||
| class ConfigurationTableError {}; | ||||
| extern char gCmdName[];	// Gotta be global, gotta be char*, gotta love it. | ||||
|  | ||||
| /** An exception thrown when a given config key isn't found. */ | ||||
| class ConfigurationTableKeyNotFound : public ConfigurationTableError { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mKey; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationTableKeyNotFound(const std::string& wKey) | ||||
| 		:mKey(wKey) | ||||
| 	{ } | ||||
|  | ||||
| 	const std::string& key() const { return mKey; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| class ConfigurationRecord { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mValue; | ||||
| 	long mNumber; | ||||
| 	bool mDefined; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationRecord(bool wDefined=true): | ||||
| 		mDefined(wDefined) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationRecord(const std::string& wValue): | ||||
| 		mValue(wValue), | ||||
| 		mNumber(strtol(wValue.c_str(),NULL,0)), | ||||
| 		mDefined(true) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationRecord(const char* wValue): | ||||
| 		mValue(std::string(wValue)), | ||||
| 		mNumber(strtol(wValue,NULL,0)), | ||||
| 		mDefined(true) | ||||
| 	{ } | ||||
|  | ||||
|  | ||||
| 	const std::string& value() const { return mValue; } | ||||
| 	long number() const { return mNumber; } | ||||
| 	bool defined() const { return mDefined; } | ||||
|  | ||||
| 	float floatNumber() const; | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** A string class that uses a hash function for comparison. */ | ||||
| class HashString : public std::string { | ||||
|  | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	uint64_t mHash; | ||||
|  | ||||
| 	void computeHash(); | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	HashString(const char* src) | ||||
| 		:std::string(src) | ||||
| 	{ | ||||
| 		computeHash(); | ||||
| 	} | ||||
|  | ||||
| 	HashString(const std::string& src) | ||||
| 		:std::string(src) | ||||
| 	{ | ||||
| 		computeHash(); | ||||
| 	} | ||||
|  | ||||
| 	HashString() | ||||
| 	{ | ||||
| 		mHash=0; | ||||
| 	} | ||||
|  | ||||
| 	HashString& operator=(std::string& src) | ||||
| 	{ | ||||
| 		std::string::operator=(src); | ||||
| 		computeHash(); | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	HashString& operator=(const char* src) | ||||
| 	{ | ||||
| 		std::string::operator=(src); | ||||
| 		computeHash(); | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	bool operator==(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash==other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	bool operator<(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash<other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	bool operator>(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash<other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t hash() const { return mHash; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap; | ||||
| typedef std::map<HashString, ConfigurationRecord> ConfigurationMap; | ||||
| class ConfigurationKey; | ||||
| typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap; | ||||
|  | ||||
| /** | ||||
| 	A class for maintaining a configuration key-value table, | ||||
| 	based on sqlite3 and a local map-based cache. | ||||
| 	Thread-safe, too. | ||||
| */ | ||||
| class ConfigurationTable { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	sqlite3* mDB;				///< database connection | ||||
| 	ConfigurationMap mCache;	///< cache of recently access configuration values | ||||
| 	mutable Mutex mLock;		///< control for multithreaded access to the cache | ||||
| 	std::vector<std::string> (*mCrossCheck)(const std::string&);	///< cross check callback pointer | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic | ||||
|  | ||||
| 	ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap()); | ||||
|  | ||||
| 	/** Generate an up-to-date example sql file for new installs. */ | ||||
| 	std::string getDefaultSQL(const std::string& program, const std::string& version); | ||||
|  | ||||
| 	/** Generate an up-to-date TeX snippet. */ | ||||
| 	std::string getTeX(const std::string& program, const std::string& version); | ||||
|  | ||||
| 	/** Return true if the key is used in the table.  */ | ||||
| 	bool defines(const std::string& key); | ||||
|  | ||||
| 	/** Return true if the application's schema knows about this key. */ | ||||
| 	bool keyDefinedInSchema(const std::string& name); | ||||
|  | ||||
| 	/** Return true if the provided value validates correctly against the defined schema. */ | ||||
| 	bool isValidValue(const std::string& name, const std::string& val); | ||||
|  | ||||
| 	/** Return true if the provided value validates correctly against the defined schema. */ | ||||
| 	bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); } | ||||
|  | ||||
| 	/** Return a map of all similar keys in the defined schema. */ | ||||
| 	ConfigurationKeyMap getSimilarKeys(const std::string& snippet); | ||||
|  | ||||
| 	/** Return true if this key is identified as static. */ | ||||
| 	bool isStatic(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a string parameter from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	std::string getStr(const std::string& key); | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 		Get a boolean from the table. | ||||
| 		Return false if NULL or 0, true otherwise. | ||||
| 	*/ | ||||
| 	bool getBool(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a numeric parameter from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	long getNum(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a vector of strings from the table. | ||||
| 	*/ | ||||
| 	std::vector<std::string> getVectorOfStrings(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a float from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	float getFloat(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a numeric vector from the table. | ||||
| 	*/ | ||||
| 	std::vector<unsigned> getVector(const std::string& key); | ||||
|  | ||||
| 	/** Get length of a vector */ | ||||
| 	unsigned getVectorLength(const std::string &key)  | ||||
| 		{ return getVector(key).size(); } | ||||
|  | ||||
| 	/** Set or change a value in the table.  */ | ||||
| 	bool set(const std::string& key, const std::string& value); | ||||
|  | ||||
| 	/** Set or change a value in the table.  */ | ||||
| 	bool set(const std::string& key, long value); | ||||
|  | ||||
| 	/** Create an entry in the table, no value though. */ | ||||
| 	bool set(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Remove an entry from the table. | ||||
| 		Will not alter required values. | ||||
| 		@param key The key of the item to be removed. | ||||
| 		@return true if anything was actually removed. | ||||
| 	*/ | ||||
| 	bool remove(const std::string& key); | ||||
|  | ||||
| 	/** Search the table, dumping to a stream. */ | ||||
| 	void find(const std::string& pattern, std::ostream&) const; | ||||
|  | ||||
| 	/** Return all key/value pairs stored in the ConfigurationTable */ | ||||
| 	ConfigurationRecordMap getAllPairs() const; | ||||
|  | ||||
| 	/** Define the callback to purge the cache whenever the database changes. */ | ||||
| 	void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64)); | ||||
|  | ||||
| 	/** Define the callback for cross checking. */ | ||||
| 	void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&)); | ||||
|  | ||||
| 	/** Execute the application specific value cross checking logic. */ | ||||
| 	std::vector<std::string> crossCheck(const std::string& key); | ||||
|  | ||||
| 	/** purege cache if it exceeds a certain age */ | ||||
| 	void checkCacheAge(); | ||||
|  | ||||
| 	/** Delete all records from the cache. */ | ||||
| 	void purge(); | ||||
|  | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	/** | ||||
| 		Attempt to lookup a record, cache if needed. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 		Caller should hold mLock because the returned reference points into the cache. | ||||
| 	*/ | ||||
| 	const ConfigurationRecord& lookup(const std::string& key); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| typedef std::map<HashString, std::string> HashStringMap; | ||||
|  | ||||
| class SimpleKeyValue { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	HashStringMap mMap; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Take a C string "A=B" and set map["A"]="B". */ | ||||
| 	void addItem(const char*); | ||||
|  | ||||
| 	/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */ | ||||
| 	void addItems(const char*s); | ||||
|  | ||||
| 	/** Return a reference to the string at map["key"]. */ | ||||
| 	const char* get(const char*) const; | ||||
| }; | ||||
|  | ||||
|  | ||||
| class ConfigurationKey { | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	enum VisibilityLevel | ||||
| 	{ | ||||
| 		CUSTOMER, | ||||
| 		CUSTOMERSITE, | ||||
| 		CUSTOMERTUNE, | ||||
| 		CUSTOMERWARN, | ||||
| 		DEVELOPER, | ||||
| 		FACTORY | ||||
| 	}; | ||||
|  | ||||
| 	enum Type | ||||
| 	{ | ||||
| 		BOOLEAN, | ||||
| 		CHOICE_OPT, | ||||
| 		CHOICE, | ||||
| 		CIDR_OPT, | ||||
| 		CIDR, | ||||
| 		FILEPATH_OPT, | ||||
| 		FILEPATH, | ||||
| 		IPADDRESS_OPT, | ||||
| 		IPADDRESS, | ||||
| 		IPANDPORT, | ||||
| 		MIPADDRESS_OPT, | ||||
| 		MIPADDRESS, | ||||
| 		PORT_OPT, | ||||
| 		PORT, | ||||
| 		REGEX_OPT, | ||||
| 		REGEX, | ||||
| 		STRING_OPT, | ||||
| 		STRING, | ||||
| 		VALRANGE | ||||
| 	}; | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mName; | ||||
| 	std::string mDefaultValue; | ||||
| 	std::string mUnits; | ||||
| 	VisibilityLevel mVisibility; | ||||
| 	Type mType; | ||||
| 	std::string mValidValues; | ||||
| 	bool mIsStatic; | ||||
| 	std::string mDescription; | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription): | ||||
| 		mName(wName), | ||||
| 		mDefaultValue(wDefaultValue), | ||||
| 		mUnits(wUnits), | ||||
| 		mVisibility(wVisibility), | ||||
| 		mType(wType), | ||||
| 		mValidValues(wValidValues), | ||||
| 		mIsStatic(wIsStatic), | ||||
| 		mDescription(wDescription) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationKey() | ||||
| 	{ } | ||||
|  | ||||
| 	const std::string& getName() const { return mName; } | ||||
| 	const std::string& getDefaultValue() const { return mDefaultValue; } | ||||
| 	void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; } | ||||
| 	void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); } | ||||
| 	const std::string& getUnits() const { return mUnits; } | ||||
| 	const VisibilityLevel& getVisibility() const { return mVisibility; } | ||||
| 	const Type& getType() const { return mType; } | ||||
| 	const std::string& getValidValues() const { return mValidValues; } | ||||
| 	bool isStatic() const { return mIsStatic; } | ||||
| 	const std::string& getDescription() const { return mDescription; } | ||||
|  | ||||
| 	static bool isValidIP(const std::string& ip); | ||||
| 	static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping); | ||||
| 	template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger); | ||||
| 	static const std::string visibilityLevelToString(const VisibilityLevel& visibility); | ||||
| 	static const std::string typeToString(const ConfigurationKey::Type& type); | ||||
| 	static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os); | ||||
| 	static void printDescription(const ConfigurationKey &key, std::ostream& os); | ||||
| 	static const std::string getARFCNsString(); | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
| @@ -1,149 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
| #include "Configuration.h" | ||||
| #include <iostream> | ||||
| #include <string> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| ConfigurationKeyMap getConfigurationKeys(); | ||||
| ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys()); | ||||
|  | ||||
| void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) | ||||
| { | ||||
| 	//cout << "update hook" << endl; | ||||
| 	gConfig.purge(); | ||||
| } | ||||
|  | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
| 	gConfig.setUpdateHook(purgeConfig); | ||||
|  | ||||
| 	const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"}; | ||||
|  | ||||
| 	for (int i=0; i<5; i++) { | ||||
| 		gConfig.set(keys[i],i); | ||||
| 	} | ||||
|  | ||||
| 	for (int i=0; i<5; i++) { | ||||
| 		cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) <<  endl; | ||||
| 		cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) <<  endl; | ||||
| 	} | ||||
|  | ||||
| 	for (int i=0; i<5; i++) { | ||||
| 		cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) <<  endl; | ||||
| 	} | ||||
|  | ||||
| 	gConfig.set("key5","100 200 300  400 "); | ||||
| 	std::vector<unsigned> vect = gConfig.getVector("key5"); | ||||
| 	cout << "vect length " << vect.size() << ": "; | ||||
| 	for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i]; | ||||
| 	cout << endl; | ||||
| 	std::vector<string> svect = gConfig.getVectorOfStrings("key5"); | ||||
| 	cout << "vect length " << svect.size() << ": "; | ||||
| 	for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":"; | ||||
| 	cout << endl; | ||||
|  | ||||
| 	cout << "bool " << gConfig.getBool("booltest") << endl; | ||||
| 	gConfig.set("booltest",1); | ||||
| 	cout << "bool " << gConfig.getBool("booltest") << endl; | ||||
| 	gConfig.set("booltest",0); | ||||
| 	cout << "bool " << gConfig.getBool("booltest") << endl; | ||||
|  | ||||
| 	gConfig.getStr("newstring"); | ||||
| 	gConfig.getNum("numnumber"); | ||||
|  | ||||
|  | ||||
| 	SimpleKeyValue pairs; | ||||
| 	pairs.addItems(" a=1 b=34 dd=143 "); | ||||
| 	cout<< pairs.get("a") << endl; | ||||
| 	cout<< pairs.get("b") << endl; | ||||
| 	cout<< pairs.get("dd") << endl; | ||||
|  | ||||
| 	gConfig.set("fkey","123.456"); | ||||
| 	float fval = gConfig.getFloat("fkey"); | ||||
| 	cout << "fkey " << fval << endl; | ||||
|  | ||||
| 	cout << "search fkey:" << endl; | ||||
| 	gConfig.find("fkey",cout); | ||||
| 	cout << "search fkey:" << endl; | ||||
| 	gConfig.find("fkey",cout); | ||||
| 	gConfig.remove("fkey"); | ||||
| 	cout << "search fkey:" << endl; | ||||
| 	gConfig.find("fkey",cout); | ||||
|  | ||||
| 	try { | ||||
| 		gConfig.getNum("supposedtoabort"); | ||||
| 	} catch (ConfigurationTableKeyNotFound) { | ||||
| 		cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ConfigurationKeyMap getConfigurationKeys() | ||||
| { | ||||
| 	ConfigurationKeyMap map; | ||||
| 	ConfigurationKey *tmp; | ||||
|  | ||||
| 	tmp = new ConfigurationKey("booltest","0", | ||||
| 		"", | ||||
| 		ConfigurationKey::DEVELOPER, | ||||
| 		ConfigurationKey::BOOLEAN, | ||||
| 		"", | ||||
| 		false, | ||||
| 		"" | ||||
| 	); | ||||
| 	map[tmp->getName()] = *tmp; | ||||
| 	free(tmp); | ||||
|  | ||||
| 	tmp = new ConfigurationKey("numnumber","42", | ||||
| 		"", | ||||
| 		ConfigurationKey::DEVELOPER, | ||||
| 		ConfigurationKey::VALRANGE, | ||||
| 		"0-100", | ||||
| 		false, | ||||
| 		"" | ||||
| 	); | ||||
| 	map[tmp->getName()] = *tmp; | ||||
| 	free(tmp); | ||||
|  | ||||
| 	tmp = new ConfigurationKey("newstring","new string value", | ||||
| 		"", | ||||
| 		ConfigurationKey::DEVELOPER, | ||||
| 		ConfigurationKey::STRING, | ||||
| 		"", | ||||
| 		false, | ||||
| 		"" | ||||
| 	); | ||||
| 	map[tmp->getName()] = *tmp; | ||||
| 	free(tmp); | ||||
|  | ||||
| 	return map; | ||||
| } | ||||
| @@ -1,69 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2009 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
| #include <iostream> | ||||
| #include <iterator> | ||||
|  | ||||
| #include "Logger.h" | ||||
| #include "Configuration.h" | ||||
|  | ||||
| ConfigurationTable gConfig; | ||||
| //ConfigurationTable gConfig("example.config"); | ||||
|  | ||||
| void printAlarms() | ||||
| { | ||||
|     std::ostream_iterator<std::string> output( std::cout, "\n" ); | ||||
|     std::list<std::string> alarms = gGetLoggerAlarms(); | ||||
|     std::cout << "# alarms = " << alarms.size() << std::endl; | ||||
|     std::copy( alarms.begin(), alarms.end(), output ); | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	gLogInit("LogTest","NOTICE",LOG_LOCAL7); | ||||
|  | ||||
| 	LOG(EMERG) << " testing the logger."; | ||||
| 	LOG(ALERT) << " testing the logger."; | ||||
| 	LOG(CRIT) << " testing the logger."; | ||||
| 	LOG(ERR) << " testing the logger."; | ||||
| 	LOG(WARNING) << " testing the logger."; | ||||
| 	LOG(NOTICE) << " testing the logger."; | ||||
| 	LOG(INFO) << " testing the logger."; | ||||
| 	LOG(DEBUG) << " testing the logger."; | ||||
|     std::cout << "\n\n\n"; | ||||
|     std::cout << "testing Alarms\n"; | ||||
|     std::cout << "you should see three lines:" << std::endl; | ||||
|     printAlarms(); | ||||
|     std::cout << "----------- generating 20 alarms ----------" << std::endl; | ||||
|     for (int i = 0 ; i < 20 ; ++i) { | ||||
|         LOG(ALERT) << i; | ||||
|     } | ||||
|     std::cout << "you should see ten lines with the numbers 10..19:" << std::endl; | ||||
|     printAlarms(); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -32,44 +32,15 @@ | ||||
| #include <stdarg.h> | ||||
| #include <sys/time.h>	// For gettimeofday | ||||
|  | ||||
| #include "Configuration.h" | ||||
| #include "Logger.h" | ||||
| #include "Threads.h"	// pat added | ||||
|  | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| // Switches to enable/disable logging targets | ||||
| // MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY | ||||
| bool gLogToConsole = true; | ||||
| bool gLogToSyslog = false; | ||||
| FILE *gLogToFile = NULL; | ||||
| Mutex gLogToLock; | ||||
|  | ||||
|  | ||||
| // Reference to a global config table, used all over the system. | ||||
| extern ConfigurationTable gConfig; | ||||
|  | ||||
|  | ||||
| /**@ The global alarms table. */ | ||||
| //@{ | ||||
| Mutex           alarmsLock; | ||||
| list<string>    alarmsList; | ||||
| void            addAlarm(const string&); | ||||
| //@} | ||||
|  | ||||
|  | ||||
|  | ||||
| // (pat) If Log messages are printed before the classes in this module are inited | ||||
| // (which happens when static classes have constructors that do work) | ||||
| // the OpenBTS just crashes. | ||||
| // Prevent that by setting sLoggerInited to true when this module is inited. | ||||
| static bool sLoggerInited = 0; | ||||
| static struct CheckLoggerInitStatus { | ||||
| 	CheckLoggerInitStatus() { sLoggerInited = 1; } | ||||
| } sCheckloggerInitStatus; | ||||
|  | ||||
|  | ||||
| // Global log level threshold: | ||||
| int config_log_level; | ||||
|  | ||||
| /** Names of the logging levels. */ | ||||
| const char *levelNames[] = { | ||||
| @@ -96,22 +67,6 @@ int levelStringToInt(const string& name) | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /** Given a string, return the corresponding level name. */ | ||||
| int lookupLevel(const string& key) | ||||
| { | ||||
| 	string val = gConfig.getStr(key); | ||||
| 	int level = levelStringToInt(val); | ||||
|  | ||||
| 	if (level == -1) { | ||||
| 		string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue(); | ||||
| 		level = levelStringToInt(defaultLevel); | ||||
| 		_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG"; | ||||
| 		gConfig.set(key, defaultLevel); | ||||
| 	} | ||||
|  | ||||
| 	return level; | ||||
| } | ||||
|  | ||||
| static std::string format(const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| @@ -138,127 +93,22 @@ std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
| 	return os << ss.str(); | ||||
| } | ||||
|  | ||||
| int getLoggingLevel(const char* filename) | ||||
| { | ||||
| 	// Default level? | ||||
| 	if (!filename) return lookupLevel("Log.Level"); | ||||
|  | ||||
| 	// This can afford to be inefficient since it is not called that often. | ||||
| 	const string keyName = string("Log.Level.") + string(filename); | ||||
| 	if (gConfig.defines(keyName)) return lookupLevel(keyName); | ||||
| 	return lookupLevel("Log.Level"); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| int gGetLoggingLevel(const char* filename) | ||||
| { | ||||
| 	// This is called a lot and needs to be efficient. | ||||
|  | ||||
| 	static Mutex sLogCacheLock; | ||||
| 	static map<uint64_t,int>  sLogCache; | ||||
| 	static unsigned sCacheCount; | ||||
| 	static const unsigned sCacheRefreshCount = 1000; | ||||
|  | ||||
| 	if (filename==NULL) return gGetLoggingLevel(""); | ||||
|  | ||||
| 	HashString hs(filename); | ||||
| 	uint64_t key = hs.hash(); | ||||
|  | ||||
| 	sLogCacheLock.lock(); | ||||
| 	// Time for a cache flush? | ||||
| 	if (sCacheCount>sCacheRefreshCount) { | ||||
| 		sLogCache.clear(); | ||||
| 		sCacheCount=0; | ||||
| 	} | ||||
| 	// Is it cached already? | ||||
| 	map<uint64_t,int>::const_iterator where = sLogCache.find(key); | ||||
| 	sCacheCount++; | ||||
| 	if (where!=sLogCache.end()) { | ||||
| 		int retVal = where->second; | ||||
| 		sLogCacheLock.unlock(); | ||||
| 		return retVal; | ||||
| 	} | ||||
| 	// Look it up in the config table and cache it. | ||||
| 	// FIXME: Figure out why unlock and lock below fix the config table deadlock. | ||||
| 	// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel(). | ||||
| 	sLogCacheLock.unlock(); | ||||
| 	int level = getLoggingLevel(filename); | ||||
| 	sLogCacheLock.lock(); | ||||
| 	sLogCache.insert(pair<uint64_t,int>(key,level)); | ||||
| 	sLogCacheLock.unlock(); | ||||
| 	return level; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| // copies the alarm list and returns it. list supposed to be small. | ||||
| list<string> gGetLoggerAlarms() | ||||
| { | ||||
|     alarmsLock.lock(); | ||||
|     list<string> ret; | ||||
|     // excuse the "complexity", but to use std::copy with a list you need | ||||
|     // an insert_iterator - copy technically overwrites, doesn't insert. | ||||
|     insert_iterator< list<string> > ii(ret, ret.begin()); | ||||
|     copy(alarmsList.begin(), alarmsList.end(), ii); | ||||
|     alarmsLock.unlock(); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** Add an alarm to the alarm list. */ | ||||
| void addAlarm(const string& s) | ||||
| { | ||||
|     alarmsLock.lock(); | ||||
|     alarmsList.push_back(s); | ||||
| 	unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max"); | ||||
|     while (alarmsList.size() > maxAlarms) alarmsList.pop_front(); | ||||
|     alarmsLock.unlock(); | ||||
| } | ||||
|  | ||||
|  | ||||
| Log::~Log() | ||||
| { | ||||
| 	if (mDummyInit) return; | ||||
| 	// Anything at or above LOG_CRIT is an "alarm". | ||||
| 	// Save alarms in the local list and echo them to stderr. | ||||
| 	if (mPriority <= LOG_ERR) { | ||||
| 		if (sLoggerInited) addAlarm(mStream.str().c_str()); | ||||
| 		cerr << mStream.str() << endl; | ||||
| 	} | ||||
| 	// Current logging level was already checked by the macro. So just log. | ||||
| 	// Log to syslog | ||||
| 	if (gLogToSyslog) { | ||||
| 		syslog(mPriority, "%s", mStream.str().c_str()); | ||||
| 	} | ||||
| 	// Log to file and console | ||||
| 	if (gLogToConsole||gLogToFile) { | ||||
| 		int mlen = mStream.str().size(); | ||||
| 		int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 		ScopedLock lock(gLogToLock); | ||||
| 		if (gLogToConsole) { | ||||
| 			// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, | ||||
| 			// so just use std::cout. | ||||
| 			std::cout << mStream.str(); | ||||
| 			if (neednl) std::cout<<"\n"; | ||||
| 		} | ||||
| 		if (gLogToFile) { | ||||
| 			fputs(mStream.str().c_str(),gLogToFile); | ||||
| 			if (neednl) {fputc('\n',gLogToFile);} | ||||
| 			fflush(gLogToFile); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	int mlen = mStream.str().size(); | ||||
| 	int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 	ScopedLock lock(gLogToLock); | ||||
| 	// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, | ||||
| 	// so just use std::cout. | ||||
| 	std::cout << mStream.str(); | ||||
| 	if (neednl) std::cout<<"\n"; | ||||
| } | ||||
|  | ||||
|  | ||||
| Log::Log(const char* name, const char* level, int facility) | ||||
| { | ||||
| 	mDummyInit = true; | ||||
| 	gLogInit(name, level, facility); | ||||
| } | ||||
|  | ||||
|  | ||||
| ostringstream& Log::get() | ||||
| { | ||||
| 	assert(mPriority<numLevels); | ||||
| @@ -268,64 +118,11 @@ ostringstream& Log::get() | ||||
|  | ||||
|  | ||||
|  | ||||
| void gLogInit(const char* name, const char* level, int facility) | ||||
| void gLogInit(const char* level, char *fn) | ||||
| { | ||||
| 	// Set the level if one has been specified. | ||||
| 	if (level) { | ||||
| 		gConfig.set("Log.Level",level); | ||||
| 	} | ||||
|  | ||||
| 	// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file: | ||||
| 	string str = gConfig.getStr("Log.File"); | ||||
| 	if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) { | ||||
| 		const char *fn = str.c_str(); | ||||
| 		if (fn && *fn && strlen(fn)>3) {	// strlen because a garbage char is getting in sometimes. | ||||
| 			gLogToFile = fopen(fn,"w"); // New log file each time we start. | ||||
| 			if (gLogToFile) { | ||||
| 				time_t now; | ||||
| 				time(&now); | ||||
| 				fprintf(gLogToFile,"Starting at %s",ctime(&now)); | ||||
| 				fflush(gLogToFile); | ||||
| 				std::cout << "Logging to file: " << fn << "\n"; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Open the log connection. | ||||
| 	openlog(name,0,facility); | ||||
| } | ||||
|  | ||||
|  | ||||
| void gLogEarly(int level, const char *fmt, ...) | ||||
| { | ||||
| 	va_list args; | ||||
|  | ||||
| 	va_start(args, fmt); | ||||
|  | ||||
| 	if (gLogToSyslog) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vsyslog(level | LOG_USER, fmt, args_copy); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	if (gLogToConsole) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vprintf(fmt, args_copy); | ||||
| 		printf("\n"); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	if (gLogToFile) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vfprintf(gLogToFile, fmt, args_copy); | ||||
| 		fprintf(gLogToFile, "\n"); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	va_end(args); | ||||
| 	if (level) | ||||
| 		config_log_level = levelStringToInt(level); | ||||
| } | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
|   | ||||
| @@ -32,19 +32,27 @@ | ||||
| #ifndef LOGGER_H | ||||
| #define LOGGER_H | ||||
|  | ||||
| #include <syslog.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <sstream> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <string> | ||||
|  | ||||
| extern int config_log_level; | ||||
|  | ||||
| #define LOG_EMERG       0       /* system is unusable */ | ||||
| #define LOG_ALERT       1       /* action must be taken immediately */ | ||||
| #define LOG_CRIT        2       /* critical conditions */ | ||||
| #define LOG_ERR         3       /* error conditions */ | ||||
| #define LOG_WARNING     4       /* warning conditions */ | ||||
| #define LOG_NOTICE      5       /* normal but significant condition */ | ||||
| #define LOG_INFO        6       /* informational */ | ||||
| #define LOG_DEBUG       7       /* debug-level messages */ | ||||
|  | ||||
| #define _LOG(level) \ | ||||
| 	Log(LOG_##level).get() << pthread_self() \ | ||||
| 	<< timestr() << " " __FILE__  ":"  << __LINE__ << ":" << __FUNCTION__ << ": " | ||||
|  | ||||
| #define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) | ||||
| #define IS_LOG_LEVEL(wLevel) (config_log_level>=LOG_##wLevel) | ||||
|  | ||||
| #ifdef NDEBUG | ||||
| #define LOG(wLevel) \ | ||||
| @@ -54,40 +62,8 @@ | ||||
| 	if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel) | ||||
| #endif | ||||
|  | ||||
| // pat: And for your edification here are the 'levels' as defined in syslog.h: | ||||
| // LOG_EMERG   0  system is unusable | ||||
| // LOG_ALERT   1  action must be taken immediately | ||||
| // LOG_CRIT    2  critical conditions | ||||
| // LOG_ERR     3  error conditions | ||||
| // LOG_WARNING 4  warning conditions | ||||
| // LOG_NOTICE  5  normal, but significant, condition | ||||
| // LOG_INFO    6  informational message | ||||
| // LOG_DEBUG   7  debug-level message | ||||
|  | ||||
| // (pat) added - print out a var and its name. | ||||
| // Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name); | ||||
| #define LOGVAR2(name,val) " " << name << "=" << (val) | ||||
| #define LOGVAR(var) (" " #var "=") << var | ||||
| #define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec | ||||
| #define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec | ||||
| // These are kind of cheesy, but you can use for bitvector | ||||
| #define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")" | ||||
| #define LOGBV(bv) LOGBV2(#bv,bv) | ||||
| #define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")" | ||||
|  | ||||
|  | ||||
| #define OBJLOG(wLevel) \ | ||||
| 	LOG(wLevel) << "obj: " << this << ' ' | ||||
|  | ||||
| #define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x); | ||||
|  | ||||
|  | ||||
| #include "Threads.h"		// must be after defines above, if these files are to be allowed to use LOG() | ||||
|  | ||||
| /** | ||||
| 	A C++ stream-based thread-safe logger. | ||||
| 	Derived from Dr. Dobb's Sept. 2007 issue. | ||||
| 	Updated to use syslog. | ||||
| 	This object is NOT the global logger; | ||||
| 	every log record is an object of this class. | ||||
| */ | ||||
| @@ -99,28 +75,19 @@ class Log { | ||||
|  | ||||
| 	std::ostringstream mStream;		///< This is where we buffer up the log entry. | ||||
| 	int mPriority;					///< Priority of current report. | ||||
| 	bool mDummyInit; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Log(int wPriority) | ||||
| 		:mPriority(wPriority), mDummyInit(false) | ||||
| 		:mPriority(wPriority) | ||||
| 	{ } | ||||
|  | ||||
| 	Log(const char* name, const char* level=NULL, int facility=LOG_USER); | ||||
|  | ||||
| 	// Most of the work is in the destructor. | ||||
| 	/** The destructor actually generates the log entry. */ | ||||
| 	~Log(); | ||||
|  | ||||
| 	std::ostringstream& get(); | ||||
| }; | ||||
| extern bool gLogToConsole;	// Output log messages to stdout | ||||
| extern bool gLogToSyslog;	// Output log messages to syslog | ||||
|  | ||||
|  | ||||
|  | ||||
| std::list<std::string> gGetLoggerAlarms();		///< Get a copy of the recent alarm list. | ||||
|  | ||||
| const std::string timestr();		// A timestamp to print in messages. | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); | ||||
| @@ -128,11 +95,7 @@ std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); | ||||
| /**@ Global control and initialization of the logging system. */ | ||||
| //@{ | ||||
| /** Initialize the global logging system. */ | ||||
| void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER); | ||||
| /** Get the logging level associated with a given file. */ | ||||
| int gGetLoggingLevel(const char *filename=NULL); | ||||
| /** Allow early logging when still in constructors */ | ||||
| void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||||
| void gLogInit(const char* level=NULL, char* fn=NULL); | ||||
| //@} | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,7 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread | ||||
| AM_CXXFLAGS = -Wall -O3 -g -lpthread | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	example.config \ | ||||
| @@ -36,59 +36,15 @@ libcommon_la_SOURCES = \ | ||||
| 	Sockets.cpp \ | ||||
| 	Threads.cpp \ | ||||
| 	Timeval.cpp \ | ||||
| 	Logger.cpp \ | ||||
| 	Configuration.cpp \ | ||||
| 	sqlite3util.cpp | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	BitVectorTest \ | ||||
| 	InterthreadTest \ | ||||
| 	SocketsTest \ | ||||
| 	TimevalTest \ | ||||
| 	VectorTest \ | ||||
| 	ConfigurationTest \ | ||||
| 	LogTest | ||||
|  | ||||
| #	ReportingTest  | ||||
| 	Logger.cpp | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	BitVector.h \ | ||||
| 	PRBS.h \ | ||||
| 	Interthread.h \ | ||||
| 	LinkedLists.h \ | ||||
| 	Sockets.h \ | ||||
| 	Threads.h \ | ||||
| 	Timeval.h \ | ||||
| 	Vector.h \ | ||||
| 	Configuration.h \ | ||||
| 	Logger.h \ | ||||
| 	sqlite3util.h | ||||
|  | ||||
| BitVectorTest_SOURCES = BitVectorTest.cpp | ||||
| BitVectorTest_LDADD = libcommon.la $(SQLITE3_LIBS) | ||||
|  | ||||
| InterthreadTest_SOURCES = InterthreadTest.cpp | ||||
| InterthreadTest_LDADD = libcommon.la | ||||
| InterthreadTest_LDFLAGS = -lpthread | ||||
|  | ||||
| SocketsTest_SOURCES = SocketsTest.cpp | ||||
| SocketsTest_LDADD = libcommon.la | ||||
| SocketsTest_LDFLAGS = -lpthread | ||||
|  | ||||
| TimevalTest_SOURCES = TimevalTest.cpp | ||||
| TimevalTest_LDADD = libcommon.la | ||||
|  | ||||
| VectorTest_SOURCES = VectorTest.cpp | ||||
| VectorTest_LDADD = libcommon.la $(SQLITE3_LIBS) | ||||
|  | ||||
| ConfigurationTest_SOURCES = ConfigurationTest.cpp | ||||
| ConfigurationTest_LDADD = libcommon.la 	$(SQLITE3_LIBS) | ||||
|  | ||||
| # ReportingTest_SOURCES = ReportingTest.cpp | ||||
| # ReportingTest_LDADD = libcommon.la $(SQLITE_LA) | ||||
|  | ||||
| LogTest_SOURCES = LogTest.cpp | ||||
| LogTest_LDADD = libcommon.la $(SQLITE3_LIBS) | ||||
|  | ||||
| MOSTLYCLEANFILES += testSource testDestination | ||||
|  | ||||
|  | ||||
| 	Logger.h | ||||
|   | ||||
							
								
								
									
										110
									
								
								CommonLibs/PRBS.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								CommonLibs/PRBS.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| /* | ||||
|  * Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co> | ||||
|  * | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #ifndef PRBS_H | ||||
| #define PRBS_H | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| /** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */ | ||||
| class PRBS { | ||||
| public: | ||||
|  | ||||
|   PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01) | ||||
|     : mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen) | ||||
|   { assert(wLen<=64); } | ||||
|  | ||||
|   /**@name Accessors */ | ||||
|   //@{ | ||||
|   uint64_t coeff() const { return mCoeff; } | ||||
|   uint64_t state() const { return mState; } | ||||
|   void state(uint64_t state) { mState = state & mask(); } | ||||
|   unsigned size() const { return mLen; } | ||||
|   //@} | ||||
|  | ||||
|   /** | ||||
|     Calculate one bit of a PRBS | ||||
|   */ | ||||
|   unsigned generateBit() | ||||
|   { | ||||
|     const unsigned result = mState & 0x01; | ||||
|     processBit(result); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     Update the generator state by one bit. | ||||
|     If you want to synchronize your PRBS to a known state, call this function | ||||
|     size() times passing your PRBS to it bit by bit. | ||||
|   */ | ||||
|   void processBit(unsigned inBit) | ||||
|   { | ||||
|     mState >>= 1; | ||||
|     if (inBit) mState ^= mCoeff; | ||||
|   } | ||||
|  | ||||
|   /** Return true when PRBS is wrapping through initial state */ | ||||
|   bool isFinished() const { return mStartState == mState; } | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   uint64_t mCoeff;      ///< polynomial coefficients. LSB is zero exponent. | ||||
|   uint64_t mStartState; ///< initial shift register state. | ||||
|   uint64_t mState;      ///< shift register state. | ||||
|   unsigned mLen;        ///< number of bits used in shift register | ||||
|  | ||||
|   /** Return mask for the state register */ | ||||
|   uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); } | ||||
|  | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 9-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^9 + x^5 + 1 | ||||
| */ | ||||
| class PRBS9 : public PRBS { | ||||
|   public: | ||||
|   PRBS9(uint64_t wState = 0x01) | ||||
|   : PRBS(9, 0x0110, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 15-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^15 + x^14 + 1 | ||||
| */ | ||||
| class PRBS15 : public PRBS { | ||||
| public: | ||||
|   PRBS15(uint64_t wState = 0x01) | ||||
|   : PRBS(15, 0x6000, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 64-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^64 + x^63 + x^61 + x^60 + 1 | ||||
| */ | ||||
| class PRBS64 : public PRBS { | ||||
| public: | ||||
|   PRBS64(uint64_t wState = 0x01) | ||||
|   : PRBS(64, 0xD800000000000000ULL, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| #endif // PRBS_H | ||||
| @@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout) | ||||
|  | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort) | ||||
| UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| 	open(wSrcPort, wSrcIP); | ||||
| } | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort, | ||||
|           	 const char * wDestIP, unsigned short wDestPort ) | ||||
| UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort, | ||||
| 		     const char *wDestIP, unsigned short wDestPort) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| 	open(wSrcPort, wSrcIP); | ||||
| 	destination(wDestPort, wDestIP); | ||||
| } | ||||
|  | ||||
| @@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) | ||||
| } | ||||
|  | ||||
|  | ||||
| void UDPSocket::open(unsigned short localPort) | ||||
| void UDPSocket::open(unsigned short localPort, const char *wlocalIP) | ||||
| { | ||||
| 	// create | ||||
| 	mSocketFD = socket(AF_INET,SOCK_DGRAM,0); | ||||
| @@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort) | ||||
| 	size_t length = sizeof(address); | ||||
| 	bzero(&address,length); | ||||
| 	address.sin_family = AF_INET; | ||||
| 	address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||||
| 	address.sin_addr.s_addr = inet_addr(wlocalIP); | ||||
| 	address.sin_port = htons(localPort); | ||||
| 	if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { | ||||
| 		perror("bind() failed"); | ||||
| @@ -284,50 +284,4 @@ unsigned short UDPSocket::port() const | ||||
| 	return ntohs(name.sin_port); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| UDDSocket::UDDSocket(const char* localPath, const char* remotePath) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	if (localPath!=NULL) open(localPath); | ||||
| 	if (remotePath!=NULL) destination(remotePath); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void UDDSocket::open(const char* localPath) | ||||
| { | ||||
| 	// create | ||||
| 	mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0); | ||||
| 	if (mSocketFD<0) { | ||||
| 		perror("socket() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
|  | ||||
| 	// bind | ||||
| 	struct sockaddr_un address; | ||||
| 	size_t length = sizeof(address); | ||||
| 	bzero(&address,length); | ||||
| 	address.sun_family = AF_UNIX; | ||||
| 	strcpy(address.sun_path,localPath); | ||||
| 	unlink(localPath); | ||||
| 	if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { | ||||
| 		perror("bind() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void UDDSocket::destination(const char* remotePath) | ||||
| { | ||||
| 	struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination; | ||||
| 	strcpy(unAddr->sun_path,remotePath); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| // vim:ts=4:sw=4 | ||||
|   | ||||
| @@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket { | ||||
| public: | ||||
|  | ||||
| 	/** Open a USP socket with an OS-assigned port and no default destination. */ | ||||
| 	UDPSocket( unsigned short localPort=0); | ||||
| 	UDPSocket(const char *localIP, unsigned short localPort); | ||||
|  | ||||
| 	/** Given a full specification, open the socket and set the dest address. */ | ||||
| 	UDPSocket( 	unsigned short localPort,  | ||||
| 			const char * remoteIP, unsigned short remotePort); | ||||
| 	UDPSocket(const char *localIP, unsigned short localPort, | ||||
| 		  const char *remoteIP, unsigned short remotePort); | ||||
|  | ||||
| 	/** Set the destination port. */ | ||||
| 	void destination( unsigned short wDestPort, const char * wDestIP ); | ||||
| @@ -157,7 +157,7 @@ public: | ||||
| 	unsigned short port() const; | ||||
|  | ||||
| 	/** Open and bind the UDP socket to a local port. */ | ||||
| 	void open(unsigned short localPort=0); | ||||
| 	void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1"); | ||||
|  | ||||
| 	/** Give the return address of the most recently received packet. */ | ||||
| 	const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; } | ||||
| @@ -166,26 +166,6 @@ public: | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** Unix Domain Datagram Socket */ | ||||
| class UDDSocket : public DatagramSocket { | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	UDDSocket(const char* localPath=NULL, const char* remotePath=NULL); | ||||
|  | ||||
| 	void destination(const char* remotePath); | ||||
|  | ||||
| 	void open(const char* localPath); | ||||
|  | ||||
| 	/** Give the return address of the most recently received packet. */ | ||||
| 	const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; } | ||||
|  | ||||
| 	size_t addressSize() const { return sizeof(struct sockaddr_un); } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -118,8 +118,8 @@ template <class T> class Vector { | ||||
| 	/** Build an empty Vector of a given size. */ | ||||
| 	Vector(size_t wSize=0):mData(NULL) { resize(wSize); } | ||||
|  | ||||
| 	/** Build a Vector by shifting the data block. */ | ||||
| 	Vector(Vector<T>& other) | ||||
| 	/** Build a Vector by moving another. */ | ||||
| 	Vector(Vector<T>&& other) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) | ||||
| 	{ other.mData=NULL; } | ||||
|  | ||||
|   | ||||
| @@ -1,154 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * All rights reserved. | ||||
| */ | ||||
|  | ||||
|  | ||||
| #include <sqlite3.h> | ||||
| #include "sqlite3util.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| // Wrappers to sqlite operations. | ||||
| // These will eventually get moved to commonlibs. | ||||
|  | ||||
| int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query) | ||||
| { | ||||
|         int src = SQLITE_BUSY; | ||||
|         while (src==SQLITE_BUSY) { | ||||
|                 src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL); | ||||
|                 if (src==SQLITE_BUSY) { | ||||
|                         usleep(100000); | ||||
|                 } | ||||
|         } | ||||
|         if (src) { | ||||
|                 fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB)); | ||||
|                 sqlite3_finalize(*stmt); | ||||
|         } | ||||
|         return src; | ||||
| } | ||||
|  | ||||
| int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt) | ||||
| { | ||||
| 	int src = SQLITE_BUSY; | ||||
| 	while (src==SQLITE_BUSY) { | ||||
| 		src = sqlite3_step(stmt); | ||||
| 		if (src==SQLITE_BUSY) { | ||||
| 			usleep(100000); | ||||
| 		} | ||||
| 	} | ||||
| 	if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) { | ||||
| 		fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB)); | ||||
| 	} | ||||
| 	return src; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool sqlite3_exists(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData) | ||||
| { | ||||
| 	size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	// Anything there? | ||||
| 	return (src == SQLITE_ROW); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, unsigned &valueData) | ||||
| { | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		valueData = (unsigned)sqlite3_column_int64(stmt,0); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by the caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, char* &valueData) | ||||
| { | ||||
| 	valueData=NULL; | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		const char* ptr = (const char*)sqlite3_column_text(stmt,0); | ||||
| 		if (ptr) valueData = strdup(ptr); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by tha caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, unsigned keyData, | ||||
| 		const char* valueName, char* &valueData) | ||||
| { | ||||
| 	valueData=NULL; | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20; | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		const char* ptr = (const char*)sqlite3_column_text(stmt,0); | ||||
| 		if (ptr) valueData = strdup(ptr); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| bool sqlite3_command(sqlite3* DB, const char* query) | ||||
| { | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Run the query. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return src==SQLITE_DONE; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,29 +0,0 @@ | ||||
| #ifndef SQLITE3UTIL_H | ||||
| #define SQLITE3UTIL_H | ||||
|  | ||||
| #include <sqlite3.h> | ||||
|  | ||||
| int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query); | ||||
|  | ||||
| int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt); | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, unsigned &valueData); | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, char* &valueData); | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by the caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, unsigned keyData, | ||||
| 		const char* valueName, char* &valueData); | ||||
|  | ||||
| bool sqlite3_exists(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData); | ||||
|  | ||||
| /** Run a query, ignoring the result; return true on success. */ | ||||
| bool sqlite3_command(sqlite3* DB, const char* query); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										22
									
								
								INSTALLATION
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								INSTALLATION
									
									
									
									
									
								
							| @@ -2,32 +2,18 @@ Installation Requirements | ||||
|  | ||||
|  | ||||
|  | ||||
| OpenBTS compiles to a simple Unix binary and does not require special | ||||
| osmo-trx compiles to a simple Unix binary and does not require special | ||||
| installation. | ||||
|  | ||||
| One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to | ||||
| running configure. | ||||
|  | ||||
| To run OpenBTS, the following should be installed: | ||||
|  | ||||
| 	Asterisk (http://www.asterisk.org), running SIP on port 5060. | ||||
|  | ||||
| 	libosip2 (http://www.gnu.org/software/osip/) | ||||
|  | ||||
| 	libortp (http://freshmeat.net/projects/ortp/) | ||||
|  | ||||
| 	libusrp (http://gnuradio.org). | ||||
| To run osmo-trx, the following should be installed: | ||||
| 	libuhd (https://gnuradio.org). | ||||
| 	This is part of the GNURadio installation. | ||||
| 	It is the only part used by OpenBTS. | ||||
|  | ||||
|  | ||||
| OpenBTS logs to syslogd as facility LOG_LOCAL7.  Please set your /etc/syslog.conf | ||||
| accordingly. | ||||
|  | ||||
|  | ||||
| For information on specific executables, see tests/README.tests and | ||||
| apps/README.apps. | ||||
|  | ||||
| See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more | ||||
| See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more | ||||
| information. | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							| @@ -1,5 +1,8 @@ | ||||
| OpenBTS | ||||
|  | ||||
| The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details. | ||||
|  | ||||
| The related copyrights: | ||||
| Most parts copyright 2008-2011 Free Software Foundation. | ||||
| Some parts copyright 2010 Kestrel Signal Processing, Inc. | ||||
| Some parts copyright 2011 Range Networks, Inc. | ||||
| @@ -12,17 +15,9 @@ patented technologies.  The user of this software is required to take whatever | ||||
| actions are necessary to avoid patent infringement. | ||||
|  | ||||
|  | ||||
| Trademark | ||||
|  | ||||
| "OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a | ||||
| California corporation.  Range reserves the right to control the use of this  | ||||
| trademark.  Do not use this trademark in commerce without permission and do not | ||||
| rebrand OpenBTS under a different trademark. | ||||
|  | ||||
|  | ||||
| Telecom and Radio Spectrum Laws | ||||
|  | ||||
| The primary function of OpenBTS is the provision of telecommunications service | ||||
| The primary function of OsmoTRX is the provision of telecommunications service | ||||
| over a radio link.  This activity is heavily regulated nearly everywhere in | ||||
| the world.  Users of this software are expected to comply with local and national | ||||
| regulations in the jurisdictions where this sortware is used with radio equipment. | ||||
| @@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive. | ||||
|  | ||||
| Note to US Government Users | ||||
|  | ||||
| The OpenBTS software applications and associated documentation are "Commercial | ||||
| The OsmoTRX software applications and associated documentation are "Commercial | ||||
| Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of | ||||
| "Commercial Computer Software" and "Commercial Computer Software Documentation," | ||||
| as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as | ||||
| @@ -54,13 +49,12 @@ and AGPLv3. | ||||
| Note to US Government Contractors | ||||
|  | ||||
| GPL is not compatible with "government purpose rights" (GPR).  If you receive | ||||
| OpenBTS software under a GPL and deliver it under GPR, you will be in violation | ||||
| OsmoTRX software under a GPL and deliver it under GPR, you will be in violation | ||||
| of GPL and possibly subject to enforcement actions by the original authors and | ||||
| copyright holders, including the Free Software Foundation, Inc. | ||||
|  | ||||
|  | ||||
| Software Licensing and Distribution | ||||
|  | ||||
| A subset of OpenBTS is distributed publicly under AGPLv3.  Range reserves the right to | ||||
| distribute most of this source code other licenses as well.  See the COPYING file | ||||
| The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file | ||||
| for more information on the license for this distribution. | ||||
|   | ||||
							
								
								
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							| @@ -21,16 +21,17 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| ACLOCAL_AMFLAGS = -I config | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) $(SQLITE3_CFLAGS) | ||||
| AM_CXXFLAGS = -Wall -pthread -ldl | ||||
| #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
| #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -pthread | ||||
| #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread | ||||
| #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread | ||||
|  | ||||
| # Order must be preserved | ||||
| SUBDIRS = \ | ||||
| 	CommonLibs \ | ||||
| 	GSM \ | ||||
| 	Transceiver52M | ||||
| 	Transceiver52M \ | ||||
| 	tests | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	autogen.sh \ | ||||
| @@ -39,6 +40,9 @@ EXTRA_DIST = \ | ||||
| 	COPYING \ | ||||
| 	README | ||||
|  | ||||
| .PHONY: release | ||||
|  | ||||
| @RELMAKE@ | ||||
|  | ||||
| dox: FORCE | ||||
| 	doxygen doxconfig | ||||
|   | ||||
| @@ -32,4 +32,10 @@ STD_DEFINES_AND_INCLUDES = \ | ||||
| COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la | ||||
| GSM_LA = $(top_builddir)/GSM/libGSM.la | ||||
|  | ||||
| if ARCH_ARM | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arm/libarch.la | ||||
| else | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/x86/libarch.la | ||||
| endif | ||||
|  | ||||
| MOSTLYCLEANFILES = *~ | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include <string.h> | ||||
| #include <cstdio> | ||||
|  | ||||
| #include "Logger.h" | ||||
| #include "Channelizer.h" | ||||
|  | ||||
| extern "C" { | ||||
|   | ||||
| @@ -22,17 +22,11 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common | ||||
| AM_CXXFLAGS = -ldl -lpthread | ||||
| AM_CXXFLAGS = -lpthread | ||||
|  | ||||
| SUBDIRS = arm x86 | ||||
|  | ||||
| if ARCH_ARM | ||||
| ARCH_LA = arm/libarch.la | ||||
| else | ||||
| ARCH_LA = x86/libarch.la | ||||
| endif | ||||
|  | ||||
| if USRP1  | ||||
| if USRP1 | ||||
| AM_CPPFLAGS += $(USRP_CFLAGS) | ||||
| else | ||||
| AM_CPPFLAGS += $(UHD_CFLAGS) | ||||
| @@ -97,12 +91,13 @@ osmo_trx_LDADD = \ | ||||
| 	libtransceiver.la \ | ||||
| 	$(ARCH_LA) \ | ||||
| 	$(GSM_LA) \ | ||||
| 	$(COMMON_LA) $(SQLITE3_LIBS) | ||||
| 	$(COMMON_LA) \ | ||||
| 	$(FFTWF_LIBS) | ||||
|  | ||||
| if USRP1  | ||||
| if USRP1 | ||||
| libtransceiver_la_SOURCES += USRPDevice.cpp | ||||
| osmo_trx_LDADD += $(USRP_LIBS) | ||||
| else | ||||
| libtransceiver_la_SOURCES += UHDDevice.cpp | ||||
| osmo_trx_LDADD += $(UHD_LIBS) $(FFTWF_LIBS) | ||||
| osmo_trx_LDADD += $(UHD_LIBS) | ||||
| endif | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include <string.h> | ||||
| #include <malloc.h> | ||||
| #include <iostream> | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "Resampler.h" | ||||
|  | ||||
| @@ -35,6 +36,8 @@ extern "C" { | ||||
|  | ||||
| #define MAX_OUTPUT_LEN		4096 | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| static float sinc(float x) | ||||
| { | ||||
| 	if (x == 0.0) | ||||
| @@ -43,32 +46,19 @@ static float sinc(float x) | ||||
| 	return sin(M_PI * x) / (M_PI * x); | ||||
| } | ||||
|  | ||||
| bool Resampler::initFilters(float bw) | ||||
| void Resampler::initFilters(float bw) | ||||
| { | ||||
| 	size_t proto_len = p * filt_len; | ||||
| 	float *proto, val, cutoff; | ||||
| 	float cutoff; | ||||
| 	float sum = 0.0f, scale = 0.0f; | ||||
| 	float midpt = (float) (proto_len - 1.0) / 2.0; | ||||
|  | ||||
| 	/*  | ||||
| 	 * Allocate partition filters and the temporary prototype filter | ||||
| 	 * according to numerator of the rational rate. Coefficients are | ||||
| 	 * real only and must be 16-byte memory aligned for SSE usage. | ||||
| 	 */ | ||||
| 	proto = new float[proto_len]; | ||||
| 	if (!proto) | ||||
| 		return false; | ||||
|  | ||||
| 	partitions = (float **) malloc(sizeof(float *) * p); | ||||
| 	if (!partitions) { | ||||
| 		delete[] proto; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < p; i++) { | ||||
| 		partitions[i] = (float *) | ||||
| 				memalign(16, filt_len * 2 * sizeof(float)); | ||||
| 	} | ||||
| 	auto proto = vector<float>(p * filt_len); | ||||
| 	for (auto &part : partitions) | ||||
| 		part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>)); | ||||
|  | ||||
| 	/*  | ||||
| 	 * Generate the prototype filter with a Blackman-harris window. | ||||
| @@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw) | ||||
| 	else | ||||
| 		cutoff = (float) q; | ||||
|  | ||||
| 	for (size_t i = 0; i < proto_len; i++) { | ||||
| 	float midpt = (proto.size() - 1) / 2.0; | ||||
| 	for (size_t i = 0; i < proto.size(); i++) { | ||||
| 		proto[i] = sinc(((float) i - midpt) / cutoff * bw); | ||||
| 		proto[i] *= a0 - | ||||
| 			    a1 * cos(2 * M_PI * i / (proto_len - 1)) + | ||||
| 			    a2 * cos(4 * M_PI * i / (proto_len - 1)) - | ||||
| 			    a3 * cos(6 * M_PI * i / (proto_len - 1)); | ||||
| 			    a1 * cos(2 * M_PI * i / (proto.size() - 1)) + | ||||
| 			    a2 * cos(4 * M_PI * i / (proto.size() - 1)) - | ||||
| 			    a3 * cos(6 * M_PI * i / (proto.size() - 1)); | ||||
| 		sum += proto[i]; | ||||
| 	} | ||||
| 	scale = p / sum; | ||||
|  | ||||
| 	/* Populate filter partitions from the prototype filter */ | ||||
| 	for (size_t i = 0; i < filt_len; i++) { | ||||
| 		for (size_t n = 0; n < p; n++) { | ||||
| 			partitions[n][2 * i + 0] = proto[i * p + n] * scale; | ||||
| 			partitions[n][2 * i + 1] = 0.0f; | ||||
| 		} | ||||
| 		for (size_t n = 0; n < p; n++) | ||||
| 			partitions[n][i] = complex<float>(proto[i * p + n] * scale); | ||||
| 	} | ||||
|  | ||||
| 	/* For convolution, we store the filter taps in reverse */  | ||||
| 	for (size_t n = 0; n < p; n++) { | ||||
| 		for (size_t i = 0; i < filt_len / 2; i++) { | ||||
| 			val = partitions[n][2 * i]; | ||||
| 			partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)]; | ||||
| 			partitions[n][2 * (filt_len - 1 - i)] = val; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	delete[] proto; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void Resampler::releaseFilters() | ||||
| { | ||||
| 	if (partitions) { | ||||
| 		for (size_t i = 0; i < p; i++) | ||||
| 			free(partitions[i]); | ||||
| 	} | ||||
|  | ||||
| 	free(partitions); | ||||
| 	partitions = NULL; | ||||
| 	/* Store filter taps in reverse */ | ||||
| 	for (auto &part : partitions) | ||||
| 		reverse(&part[0], &part[filt_len]); | ||||
| } | ||||
|  | ||||
| static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| @@ -159,14 +128,6 @@ static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void Resampler::computePath() | ||||
| { | ||||
| 	for (int i = 0; i < MAX_OUTPUT_LEN; i++) { | ||||
| 		in_index[i] = (q * i) / p; | ||||
| 		out_path[i] = (q * i) % p; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len) | ||||
| { | ||||
| 	int n, path; | ||||
| @@ -180,8 +141,8 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len | ||||
| 		path = out_path[i];  | ||||
|  | ||||
| 		convolve_real(in, in_len, | ||||
| 			      partitions[path], filt_len, | ||||
| 			      &out[2 * i], out_len - i, | ||||
| 			      reinterpret_cast<float *>(partitions[path]), | ||||
| 			      filt_len, &out[2 * i], out_len - i, | ||||
| 			      n, 1, 1, 0); | ||||
| 	} | ||||
|  | ||||
| @@ -190,14 +151,18 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len | ||||
|  | ||||
| bool Resampler::init(float bw) | ||||
| { | ||||
| 	if (p == 0 || q == 0 || filt_len == 0) return false; | ||||
|  | ||||
| 	/* Filterbank filter internals */ | ||||
| 	if (!initFilters(bw)) | ||||
| 		return false; | ||||
| 	initFilters(bw); | ||||
|  | ||||
| 	/* Precompute filterbank paths */ | ||||
| 	in_index = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	out_path = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	computePath(); | ||||
| 	int i = 0; | ||||
| 	for (auto &index : in_index) | ||||
| 		index = (q * i++) / p; | ||||
| 	i = 0; | ||||
| 	for (auto &path : out_path) | ||||
| 		path = (q * i++) % p; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| @@ -208,7 +173,7 @@ size_t Resampler::len() | ||||
| } | ||||
|  | ||||
| Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
| 	: in_index(NULL), out_path(NULL), partitions(NULL) | ||||
| 	: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p) | ||||
| { | ||||
| 	this->p = p; | ||||
| 	this->q = q; | ||||
| @@ -217,8 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
|  | ||||
| Resampler::~Resampler() | ||||
| { | ||||
| 	releaseFilters(); | ||||
|  | ||||
| 	delete in_index; | ||||
| 	delete out_path; | ||||
| 	for (auto &part : partitions) | ||||
| 		free(part); | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,9 @@ | ||||
| #ifndef _RESAMPLER_H_ | ||||
| #define _RESAMPLER_H_ | ||||
|  | ||||
| #include <vector> | ||||
| #include <complex> | ||||
|  | ||||
| class Resampler { | ||||
| public: | ||||
| 	/* Constructor for rational sample rate conversion | ||||
| @@ -63,14 +66,11 @@ private: | ||||
| 	size_t p; | ||||
| 	size_t q; | ||||
| 	size_t filt_len; | ||||
| 	size_t *in_index; | ||||
| 	size_t *out_path; | ||||
| 	std::vector<size_t> in_index; | ||||
| 	std::vector<size_t> out_path; | ||||
| 	std::vector<std::complex<float> *> partitions; | ||||
|  | ||||
| 	float **partitions; | ||||
|  | ||||
| 	bool initFilters(float bw); | ||||
| 	void releaseFilters(); | ||||
| 	void computePath(); | ||||
| 	void initFilters(float bw); | ||||
| }; | ||||
|  | ||||
| #endif /* _RESAMPLER_H_ */ | ||||
|   | ||||
| @@ -24,8 +24,8 @@ | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| #include <cstdio> | ||||
| #include <iostream> | ||||
|  | ||||
| #include "Logger.h" | ||||
| #include "Synthesis.h" | ||||
|  | ||||
| extern "C" { | ||||
|   | ||||
| @@ -112,16 +112,17 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un | ||||
| } | ||||
|  | ||||
| Transceiver::Transceiver(int wBasePort, | ||||
|                          const char *wTRXAddress, | ||||
|                          const char *TRXAddress, | ||||
|                          const char *GSMcoreAddress, | ||||
|                          size_t tx_sps, size_t rx_sps, size_t chans, | ||||
|                          GSM::Time wTransmitLatency, | ||||
|                          RadioInterface *wRadioInterface, | ||||
|                          double wRssiOffset) | ||||
|   : mBasePort(wBasePort), mAddr(wTRXAddress), | ||||
|     mClockSocket(wBasePort, wTRXAddress, mBasePort + 100), | ||||
|   : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress), | ||||
|     mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100), | ||||
|     mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface), | ||||
|     rssiOffset(wRssiOffset), | ||||
|     mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), | ||||
|     mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false), | ||||
|     mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0), | ||||
|     mWriteBurstToDiskMask(0) | ||||
| { | ||||
| @@ -197,8 +198,8 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge) | ||||
|     d_srcport = mBasePort + 2 * i + 2; | ||||
|     d_dstport = mBasePort + 2 * i + 102; | ||||
|  | ||||
|     mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport); | ||||
|     mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport); | ||||
|     mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport); | ||||
|     mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport); | ||||
|   } | ||||
|  | ||||
|   /* Randomize the central clock */ | ||||
| @@ -273,7 +274,7 @@ bool Transceiver::start() | ||||
|                             TxUpperLoopAdapter, (void*) chan); | ||||
|   } | ||||
|  | ||||
|   writeClockInterface(); | ||||
|   mForceClockInterface = true; | ||||
|   mOn = true; | ||||
|   return true; | ||||
| } | ||||
| @@ -297,6 +298,10 @@ void Transceiver::stop() | ||||
|   LOG(NOTICE) << "Stopping the transceiver"; | ||||
|   mTxLowerLoopThread->cancel(); | ||||
|   mRxLowerLoopThread->cancel(); | ||||
|   mTxLowerLoopThread->join(); | ||||
|   mRxLowerLoopThread->join(); | ||||
|   delete mTxLowerLoopThread; | ||||
|   delete mRxLowerLoopThread; | ||||
|  | ||||
|   for (size_t i = 0; i < mChans; i++) { | ||||
|     mRxServiceLoopThreads[i]->cancel(); | ||||
| @@ -315,11 +320,6 @@ void Transceiver::stop() | ||||
|     mTxPriorityQueues[i].clear(); | ||||
|   } | ||||
|  | ||||
|   mTxLowerLoopThread->join(); | ||||
|   mRxLowerLoopThread->join(); | ||||
|   delete mTxLowerLoopThread; | ||||
|   delete mRxLowerLoopThread; | ||||
|  | ||||
|   mOn = false; | ||||
|   LOG(NOTICE) << "Transceiver stopped"; | ||||
| } | ||||
| @@ -430,7 +430,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan) | ||||
|   case V: | ||||
|     state->fillerModulus[timeslot] = 51; | ||||
|     break; | ||||
|     //case V:  | ||||
|     //case V: | ||||
|   case VII: | ||||
|     state->fillerModulus[timeslot] = 102; | ||||
|     break; | ||||
| @@ -545,7 +545,7 @@ void writeToFile(radioVector *radio_burst, size_t chan) | ||||
|  | ||||
| /* | ||||
|  * Pull bursts from the FIFO and handle according to the slot | ||||
|  * and burst correlation type. Equalzation is currently disabled.  | ||||
|  * and burst correlation type. Equalzation is currently disabled. | ||||
|  */ | ||||
| SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid, | ||||
|                                          double &timingOffset, double &noise, | ||||
| @@ -656,7 +656,7 @@ void Transceiver::reset() | ||||
|     mTxPriorityQueues[i].clear(); | ||||
| } | ||||
|  | ||||
|    | ||||
|  | ||||
| void Transceiver::driveControl(size_t chan) | ||||
| { | ||||
|   int MAX_PACKET_LENGTH = 100; | ||||
| @@ -678,9 +678,6 @@ void Transceiver::driveControl(size_t chan) | ||||
|  | ||||
|   sscanf(buffer,"%3s %s",cmdcheck,command); | ||||
|  | ||||
|   if (!chan) | ||||
|     writeClockInterface(); | ||||
|  | ||||
|   if (strcmp(cmdcheck,"CMD")!=0) { | ||||
|     LOG(WARNING) << "bogus message on control interface"; | ||||
|     return; | ||||
| @@ -805,7 +802,7 @@ void Transceiver::driveControl(size_t chan) | ||||
|       LOG(WARNING) << "bogus message on control interface"; | ||||
|       sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode); | ||||
|       return; | ||||
|     }      | ||||
|     } | ||||
|     mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode; | ||||
|     setModulus(timeslot, chan); | ||||
|     sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode); | ||||
| @@ -853,14 +850,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan) | ||||
|     frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]); | ||||
|  | ||||
|   LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot); | ||||
|    | ||||
|  | ||||
|   int RSSI = (int) buffer[5]; | ||||
|   BitVector newBurst(burstLen); | ||||
|   BitVector::iterator itr = newBurst.begin(); | ||||
|   char *bufferItr = buffer+6; | ||||
|   while (itr < newBurst.end())  | ||||
|   while (itr < newBurst.end()) | ||||
|     *itr++ = *bufferItr++; | ||||
|    | ||||
|  | ||||
|   GSM::Time currTime = GSM::Time(frameNum,timeSlot); | ||||
|  | ||||
|   addRadioVector(chan, newBurst, RSSI, currTime); | ||||
| @@ -874,9 +871,9 @@ void Transceiver::driveReceiveRadio() | ||||
| { | ||||
|   if (!mRadioInterface->driveReceiveRadio()) { | ||||
|     usleep(100000); | ||||
|   } else { | ||||
|     if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) | ||||
|       writeClockInterface(); | ||||
|   } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) { | ||||
|     mForceClockInterface = false; | ||||
|     writeClockInterface(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -946,7 +943,7 @@ void Transceiver::driveTxFIFO() | ||||
| { | ||||
|  | ||||
|   /** | ||||
|       Features a carefully controlled latency mechanism, to  | ||||
|       Features a carefully controlled latency mechanism, to | ||||
|       assure that transmit packets arrive at the radio/USRP | ||||
|       before they need to be transmitted. | ||||
|  | ||||
| @@ -957,7 +954,7 @@ void Transceiver::driveTxFIFO() | ||||
|  | ||||
|  | ||||
|   RadioClock *radioClock = (mRadioInterface->getClock()); | ||||
|    | ||||
|  | ||||
|   if (mOn) { | ||||
|     //radioClock->wait(); // wait until clock updates | ||||
|     LOG(DEBUG) << "radio clock " << radioClock->get(); | ||||
|   | ||||
| @@ -89,15 +89,17 @@ struct TransceiverState { | ||||
| /** The Transceiver class, responsible for physical layer of basestation */ | ||||
| class Transceiver { | ||||
| public: | ||||
|   /** Transceiver constructor  | ||||
|   /** Transceiver constructor | ||||
|       @param wBasePort base port number of UDP sockets | ||||
|       @param TRXAddress IP address of the TRX manager, as a string | ||||
|       @param TRXAddress IP address of the TRX, as a string | ||||
|       @param GSMcoreAddress IP address of the GSM core, as a string | ||||
|       @param wSPS number of samples per GSM symbol | ||||
|       @param wTransmitLatency initial setting of transmit latency | ||||
|       @param radioInterface associated radioInterface object | ||||
|   */ | ||||
|   Transceiver(int wBasePort, | ||||
|               const char *TRXAddress, | ||||
|               const char *GSMcoreAddress, | ||||
|               size_t tx_sps, size_t rx_sps, size_t chans, | ||||
|               GSM::Time wTransmitLatency, | ||||
|               RadioInterface *wRadioInterface, | ||||
| @@ -152,7 +154,8 @@ public: | ||||
|  | ||||
| private: | ||||
|   int mBasePort; | ||||
|   std::string mAddr; | ||||
|   std::string mLocalAddr; | ||||
|   std::string mRemoteAddr; | ||||
|  | ||||
|   std::vector<UDPSocket *> mDataSockets;  ///< socket for writing to/reading from GSM core | ||||
|   std::vector<UDPSocket *> mCtrlSockets;  ///< socket for writing/reading control commands from GSM core | ||||
| @@ -169,7 +172,7 @@ private: | ||||
|  | ||||
|   GSM::Time mTransmitLatency;             ///< latency between basestation clock and transmit deadline clock | ||||
|   GSM::Time mLatencyUpdateTime;           ///< last time latency was updated | ||||
|   GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO  | ||||
|   GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO | ||||
|   GSM::Time mLastClockUpdateTime;         ///< last time clock update was sent up to core | ||||
|  | ||||
|   RadioInterface *mRadioInterface;	  ///< associated radioInterface object | ||||
| @@ -208,6 +211,7 @@ private: | ||||
|  | ||||
|   bool mEdge; | ||||
|   bool mOn;	                           ///< flag to indicate that transceiver is powered on | ||||
|   bool mForceClockInterface;           ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started | ||||
|   bool mHandover[8][8];                ///< expect handover to the timeslot/subslot | ||||
|   double mTxFreq;                      ///< the transmit frequency | ||||
|   double mRxFreq;                      ///< the receive frequency | ||||
| @@ -275,4 +279,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
| /** transmit queueing thread loop */ | ||||
| void *TxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Device support for Ettus Research UHD driver  | ||||
|  * Device support for Ettus Research UHD driver | ||||
|  * | ||||
|  * Copyright 2010,2011 Free Software Foundation, Inc. | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
| @@ -21,6 +21,7 @@ | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <map> | ||||
| #include "radioDevice.h" | ||||
| #include "Threads.h" | ||||
| #include "Logger.h" | ||||
| @@ -37,12 +38,6 @@ | ||||
| #include <uhd/utils/msg.hpp> | ||||
| #endif | ||||
|  | ||||
| #define B2XX_CLK_RT      26e6 | ||||
| #define B2XX_MCBTS_CLK_RT   51.2e6 | ||||
| #define E1XX_CLK_RT      52e6 | ||||
| #define LIMESDR_CLK_RT   (GSMRATE*32) | ||||
| #define B100_BASE_RT     400000 | ||||
| #define USRP2_BASE_RT    390625 | ||||
| #define USRP_TX_AMPL     0.3 | ||||
| #define UMTRX_TX_AMPL    0.7 | ||||
| #define LIMESDR_TX_AMPL  0.3 | ||||
| @@ -73,15 +68,6 @@ enum uhd_dev_type { | ||||
| 	X3XX, | ||||
| 	UMTRX, | ||||
| 	LIMESDR, | ||||
| 	NUM_USRP_TYPES, | ||||
| }; | ||||
|  | ||||
| struct uhd_dev_offset { | ||||
| 	enum uhd_dev_type type; | ||||
| 	size_t tx_sps; | ||||
| 	size_t rx_sps; | ||||
| 	double offset; | ||||
| 	const std::string desc; | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -109,76 +95,44 @@ struct uhd_dev_offset { | ||||
|  * Notes: | ||||
|  *   USRP1 with timestamps is not supported by UHD. | ||||
|  */ | ||||
| static struct uhd_dev_offset uhd_offsets[] = { | ||||
| 	{ USRP1, 1, 1,       0.0, "USRP1 not supported" }, | ||||
| 	{ USRP1, 4, 1,       0.0, "USRP1 not supported"}, | ||||
| 	{ USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" }, | ||||
| 	{ USRP2, 4, 1, 7.6547e-5, "N2XX 4/1 SPS" }, | ||||
| 	{ B100,  1, 1, 1.2104e-4, "B100 1 SPS" }, | ||||
| 	{ B100,  4, 1, 7.9307e-5, "B100 4 SPS" }, | ||||
| 	{ B200,  1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" }, | ||||
| 	{ B200,  4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" }, | ||||
| 	{ B210,  1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" }, | ||||
| 	{ B210,  4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" }, | ||||
| 	{ B2XX_MCBTS, 4, 4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" }, | ||||
| 	{ E1XX,  1, 1, 9.5192e-5, "E1XX 1 SPS" }, | ||||
| 	{ E1XX,  4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" }, | ||||
| 	{ E3XX,  1, 1, 1.84616e-4, "E3XX 1 SPS" }, | ||||
| 	{ E3XX,  4, 1, 1.29231e-4, "E3XX 4/1 Tx/Rx SPS" }, | ||||
| 	{ X3XX,  1, 1, 1.5360e-4, "X3XX 1 SPS"}, | ||||
| 	{ X3XX,  4, 1, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS"}, | ||||
| 	{ UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" }, | ||||
| 	{ UMTRX, 4, 1, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS" }, | ||||
| 	{ USRP2, 4, 4, 4.6080e-5, "N2XX 4 SPS" }, | ||||
| 	{ B200,  4, 4, B2XX_TIMING_4_4SPS, "B200 4 SPS" }, | ||||
| 	{ B210,  4, 4, B2XX_TIMING_4_4SPS, "B210 4 SPS" }, | ||||
| 	{ X3XX,  4, 4, 5.6567e-5, "X3XX 4 SPS"}, | ||||
| 	{ UMTRX, 4, 4, 5.1503e-5, "UmTRX 4 SPS" }, | ||||
| 	{ LIMESDR, 4, 4, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" }, | ||||
|  | ||||
| /* Device Type, Tx-SPS, Rx-SPS */ | ||||
| typedef std::tuple<uhd_dev_type, int, int> dev_key; | ||||
|  | ||||
| /* Device parameter descriptor */ | ||||
| struct dev_desc { | ||||
| 	unsigned channels; | ||||
| 	double mcr; | ||||
| 	double rate; | ||||
| 	double offset; | ||||
| 	std::string str; | ||||
| }; | ||||
| #define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0])) | ||||
|  | ||||
| /* | ||||
|  * Select sample rate based on device type and requested samples-per-symbol. | ||||
|  * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum | ||||
|  * usable channel spacing of 400 kHz. | ||||
|  */ | ||||
| static double select_rate(uhd_dev_type type, int sps, | ||||
| 			  RadioDevice::InterfaceType iface) | ||||
| { | ||||
| 	if ((sps != 4) && (sps != 1)) | ||||
| 		return -9999.99; | ||||
|  | ||||
| 	if (iface == RadioDevice::MULTI_ARFCN) { | ||||
| 		switch (type) { | ||||
| 		case B2XX_MCBTS: | ||||
| 			return  4 * MCBTS_SPACING; | ||||
| 		default: | ||||
| 			LOG(ALERT) << "Invalid device combination"; | ||||
| 			return -9999.99; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch (type) { | ||||
| 	case USRP2: | ||||
| 	case X3XX: | ||||
| 		return USRP2_BASE_RT * sps; | ||||
| 	case B100: | ||||
| 		return B100_BASE_RT * sps; | ||||
| 	case B200: | ||||
| 	case B210: | ||||
| 	case E1XX: | ||||
| 	case E3XX: | ||||
| 	case UMTRX: | ||||
| 	case LIMESDR: | ||||
| 		return GSMRATE * sps; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	LOG(ALERT) << "Unknown device type " << type; | ||||
| 	return -9999.99; | ||||
| } | ||||
| static const std::map<dev_key, dev_desc> dev_param_map { | ||||
| 	{ std::make_tuple(USRP2, 1, 1), { 1, 0.0,  390625,  1.2184e-4,  "N2XX 1 SPS"         } }, | ||||
| 	{ std::make_tuple(USRP2, 4, 1), { 1, 0.0,  390625,  7.6547e-5,  "N2XX 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(USRP2, 4, 4), { 1, 0.0,  390625,  4.6080e-5,  "N2XX 4 SPS"         } }, | ||||
| 	{ std::make_tuple(B100,  1, 1), { 1, 0.0,  400000,  1.2104e-4,  "B100 1 SPS"         } }, | ||||
| 	{ std::make_tuple(B100,  4, 1), { 1, 0.0,  400000,  7.9307e-5,  "B100 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(B200,  1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS"   } }, | ||||
| 	{ std::make_tuple(B200,  4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(B200,  4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } }, | ||||
| 	{ std::make_tuple(B210,  1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS"    } }, | ||||
| 	{ std::make_tuple(B210,  4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(B210,  4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } }, | ||||
| 	{ std::make_tuple(E1XX,  1, 1), { 1, 52e6, GSMRATE, 9.5192e-5,  "E1XX 1 SPS"         } }, | ||||
| 	{ std::make_tuple(E1XX,  4, 1), { 1, 52e6, GSMRATE, 6.5571e-5,  "E1XX 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(E3XX,  1, 1), { 2, 26e6, GSMRATE, 1.8462e-4,  "E3XX 1 SPS"         } }, | ||||
| 	{ std::make_tuple(E3XX,  4, 1), { 2, 26e6, GSMRATE, 1.2923e-4,  "E3XX 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(X3XX,  1, 1), { 2, 0.0,  390625,  1.5360e-4,  "X3XX 1 SPS"         } }, | ||||
| 	{ std::make_tuple(X3XX,  4, 1), { 2, 0.0,  390625,  1.1264e-4,  "X3XX 4/1 Tx/Rx SPS" } }, | ||||
| 	{ std::make_tuple(X3XX,  4, 4), { 2, 0.0,  390625,  5.6567e-5,  "X3XX 4 SPS"         } }, | ||||
| 	{ std::make_tuple(UMTRX, 1, 1), { 2, 0.0,  GSMRATE, 9.9692e-5,  "UmTRX 1 SPS"        } }, | ||||
| 	{ std::make_tuple(UMTRX, 4, 1), { 2, 0.0,  GSMRATE, 7.3846e-5,  "UmTRX 4/1 Tx/Rx SPS"} }, | ||||
| 	{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0,  GSMRATE, 5.1503e-5,  "UmTRX 4 SPS"        } }, | ||||
| 	{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS"  } }, | ||||
| 	{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } }, | ||||
| }; | ||||
|  | ||||
| /* | ||||
|     Sample Buffer - Allows reading and writing of timed samples using osmo-trx | ||||
| @@ -189,8 +143,8 @@ class smpl_buf { | ||||
| public: | ||||
| 	/** Sample buffer constructor | ||||
| 	    @param len number of 32-bit samples the buffer should hold | ||||
| 	    @param rate sample clockrate  | ||||
| 	    @param timestamp  | ||||
| 	    @param rate sample clockrate | ||||
| 	    @param timestamp | ||||
| 	*/ | ||||
| 	smpl_buf(size_t len, double rate); | ||||
| 	~smpl_buf(); | ||||
| @@ -218,7 +172,7 @@ public: | ||||
| 	*/ | ||||
| 	std::string str_status(size_t ts) const; | ||||
|  | ||||
| 	/** Formatted error string  | ||||
| 	/** Formatted error string | ||||
| 	    @param code an error code | ||||
| 	    @return a formatted error string | ||||
| 	*/ | ||||
| @@ -254,7 +208,9 @@ private: | ||||
| class uhd_device : public RadioDevice { | ||||
| public: | ||||
| 	uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type, | ||||
| 		   size_t chans, double offset); | ||||
| 		   size_t chans, double offset, | ||||
| 		   const std::vector<std::string>& tx_paths, | ||||
| 		   const std::vector<std::string>& rx_paths); | ||||
| 	~uhd_device(); | ||||
|  | ||||
| 	int open(const std::string &args, int ref, bool swap_channels); | ||||
| @@ -294,6 +250,11 @@ public: | ||||
| 	double getRxFreq(size_t chan); | ||||
| 	double getRxFreq(); | ||||
|  | ||||
| 	bool setRxAntenna(const std::string &ant, size_t chan); | ||||
| 	std::string getRxAntenna(size_t chan); | ||||
| 	bool setTxAntenna(const std::string &ant, size_t chan); | ||||
| 	std::string getTxAntenna(size_t chan); | ||||
|  | ||||
| 	inline double getSampleRate() { return tx_rate; } | ||||
| 	inline double numberRead() { return rx_pkt_cnt; } | ||||
| 	inline double numberWritten() { return 0; } | ||||
| @@ -326,6 +287,7 @@ private: | ||||
|  | ||||
| 	std::vector<double> tx_gains, rx_gains; | ||||
| 	std::vector<double> tx_freqs, rx_freqs; | ||||
| 	std::vector<std::string> tx_paths, rx_paths; | ||||
| 	size_t tx_spp, rx_spp; | ||||
|  | ||||
| 	bool started; | ||||
| @@ -339,9 +301,9 @@ private: | ||||
| 	std::vector<smpl_buf *> rx_buffers; | ||||
|  | ||||
| 	void init_gains(); | ||||
| 	double get_dev_offset(); | ||||
| 	int set_master_clk(double rate); | ||||
| 	int set_rates(double tx_rate, double rx_rate); | ||||
| 	void set_channels(bool swap); | ||||
| 	void set_rates(); | ||||
| 	bool set_antennas(); | ||||
| 	bool parse_dev_type(); | ||||
| 	bool flush_recv(size_t num_pkts); | ||||
| 	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls); | ||||
| @@ -400,18 +362,22 @@ static void thread_enable_cancel(bool cancel) | ||||
| } | ||||
|  | ||||
| uhd_device::uhd_device(size_t tx_sps, size_t rx_sps, | ||||
| 		       InterfaceType iface, size_t chans, double offset) | ||||
| 		       InterfaceType iface, size_t chans, double offset, | ||||
| 		       const std::vector<std::string>& tx_paths, | ||||
| 		       const std::vector<std::string>& rx_paths) | ||||
| 	: tx_gain_min(0.0), tx_gain_max(0.0), | ||||
| 	  rx_gain_min(0.0), rx_gain_max(0.0), | ||||
| 	  tx_spp(0), rx_spp(0), | ||||
| 	  started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), | ||||
| 	  prev_ts(0,0), ts_initial(0), ts_offset(0) | ||||
| 	  prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) | ||||
| { | ||||
| 	this->tx_sps = tx_sps; | ||||
| 	this->rx_sps = rx_sps; | ||||
| 	this->chans = chans; | ||||
| 	this->offset = offset; | ||||
| 	this->iface = iface; | ||||
| 	this->tx_paths = tx_paths; | ||||
| 	this->rx_paths = rx_paths; | ||||
| } | ||||
|  | ||||
| uhd_device::~uhd_device() | ||||
| @@ -470,105 +436,49 @@ void uhd_device::init_gains() | ||||
|  | ||||
| } | ||||
|  | ||||
| double uhd_device::get_dev_offset() | ||||
| void uhd_device::set_rates() | ||||
| { | ||||
| 	struct uhd_dev_offset *offset = NULL; | ||||
| 	dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)); | ||||
| 	if (desc.mcr != 0.0) | ||||
| 		usrp_dev->set_master_clock_rate(desc.mcr); | ||||
|  | ||||
| 	/* Reject USRP1 */ | ||||
| 	if (dev_type == USRP1) { | ||||
| 		LOG(ERR) << "Invalid device type"; | ||||
| 		return 0.0; | ||||
| 	} | ||||
| 	tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate; | ||||
| 	rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate; | ||||
|  | ||||
| 	/* Search for matching offset value */ | ||||
| 	for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) { | ||||
| 		if ((dev_type == uhd_offsets[i].type) && | ||||
| 			(tx_sps == uhd_offsets[i].tx_sps) && | ||||
| 			(rx_sps == uhd_offsets[i].rx_sps)) { | ||||
| 			offset = &uhd_offsets[i]; | ||||
| 			break; | ||||
| 	usrp_dev->set_tx_rate(tx_rate); | ||||
| 	usrp_dev->set_rx_rate(rx_rate); | ||||
| 	tx_rate = usrp_dev->get_tx_rate(); | ||||
| 	rx_rate = usrp_dev->get_rx_rate(); | ||||
|  | ||||
| 	ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate); | ||||
| 	LOG(INFO) << "Rates configured for " << desc.str; | ||||
| } | ||||
|  | ||||
| bool uhd_device::set_antennas() | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	for (i = 0; i < tx_paths.size(); i++) { | ||||
| 		if (tx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 		if (!setTxAntenna(tx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!offset) { | ||||
| 		LOG(ERR) << "Invalid device configuration"; | ||||
| 		return 0.0; | ||||
| 	for (i = 0; i < rx_paths.size(); i++) { | ||||
| 		if (rx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 		if (!setRxAntenna(rx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	std::cout << "-- Setting " << offset->desc << std::endl; | ||||
|  | ||||
| 	return offset->offset; | ||||
| } | ||||
|  | ||||
| int uhd_device::set_master_clk(double clk_rate) | ||||
| { | ||||
| 	double actual, offset, limit = 1.0; | ||||
|  | ||||
| 	try { | ||||
| 		usrp_dev->set_master_clock_rate(clk_rate); | ||||
| 	} catch (const std::exception &ex) { | ||||
| 		LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate; | ||||
| 		LOG(ALERT) << ex.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	actual = usrp_dev->get_master_clock_rate(); | ||||
| 	offset = fabs(clk_rate - actual); | ||||
|  | ||||
| 	if (offset > limit) { | ||||
| 		LOG(ALERT) << "Failed to set master clock rate"; | ||||
| 		LOG(ALERT) << "Requested clock rate " << clk_rate; | ||||
| 		LOG(ALERT) << "Actual clock rate " << actual; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int uhd_device::set_rates(double tx_rate, double rx_rate) | ||||
| { | ||||
| 	double offset_limit = 1.0; | ||||
| 	double tx_offset, rx_offset; | ||||
|  | ||||
| 	/* B2XX and E1xx are the only device where we set FPGA clocking */ | ||||
| 	if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) { | ||||
| 		if (set_master_clk(B2XX_CLK_RT) < 0) | ||||
| 			return -1; | ||||
| 	} else if (dev_type == E1XX) { | ||||
| 		if (set_master_clk(E1XX_CLK_RT) < 0) | ||||
| 			return -1; | ||||
| 	} else if (dev_type == B2XX_MCBTS) { | ||||
| 		if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0) | ||||
| 			return -1; | ||||
| 	} | ||||
| 	else if (dev_type == LIMESDR) { | ||||
| 		if (set_master_clk(LIMESDR_CLK_RT) < 0) | ||||
| 			return -1; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	// Set sample rates | ||||
| 	try { | ||||
| 		usrp_dev->set_tx_rate(tx_rate); | ||||
| 		usrp_dev->set_rx_rate(rx_rate); | ||||
| 	} catch (const std::exception &ex) { | ||||
| 		LOG(ALERT) << "UHD rate setting failed"; | ||||
| 		LOG(ALERT) << ex.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	this->tx_rate = usrp_dev->get_tx_rate(); | ||||
| 	this->rx_rate = usrp_dev->get_rx_rate(); | ||||
|  | ||||
| 	tx_offset = fabs(this->tx_rate - tx_rate); | ||||
| 	rx_offset = fabs(this->rx_rate - rx_rate); | ||||
| 	if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) { | ||||
| 		LOG(ALERT) << "Actual sample rate differs from desired rate"; | ||||
| 		LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/" | ||||
| 			   << this->rx_rate << ")"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| 	LOG(INFO) << "Antennas configured successfully"; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| double uhd_device::setTxGain(double db, size_t chan) | ||||
| @@ -641,86 +551,41 @@ double uhd_device::getRxGain(size_t chan) | ||||
|  */ | ||||
| bool uhd_device::parse_dev_type() | ||||
| { | ||||
| 	std::string mboard_str, dev_str; | ||||
| 	uhd::property_tree::sptr prop_tree; | ||||
| 	size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str, | ||||
| 	       b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str; | ||||
| 	uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree(); | ||||
| 	std::string devString = prop_tree->access<std::string>("/name").get(); | ||||
| 	std::string mboardString = usrp_dev->get_mboard_name(); | ||||
|  | ||||
| 	prop_tree = usrp_dev->get_device()->get_tree(); | ||||
| 	dev_str = prop_tree->access<std::string>("/name").get(); | ||||
| 	mboard_str = usrp_dev->get_mboard_name(); | ||||
| 	const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap { | ||||
| 		{ "B100",     { B100,    TX_WINDOW_USRP1 } }, | ||||
| 		{ "B200",     { B200,    TX_WINDOW_USRP1 } }, | ||||
| 		{ "B200mini", { B200,    TX_WINDOW_USRP1 } }, | ||||
| 		{ "B205mini", { B200,    TX_WINDOW_USRP1 } }, | ||||
| 		{ "B210",     { B210,    TX_WINDOW_USRP1 } }, | ||||
| 		{ "E100",     { E1XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "E110",     { E1XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "E310",     { E3XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "E3XX",     { E3XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "X300",     { X3XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "X310",     { X3XX,    TX_WINDOW_FIXED } }, | ||||
| 		{ "USRP2",    { USRP2,   TX_WINDOW_FIXED } }, | ||||
| 		{ "UmTRX",    { UMTRX,   TX_WINDOW_FIXED } }, | ||||
| 		{ "LimeSDR",  { LIMESDR, TX_WINDOW_FIXED } }, | ||||
| 	}; | ||||
|  | ||||
| 	usrp1_str = dev_str.find("USRP1"); | ||||
| 	usrp2_str = dev_str.find("USRP2"); | ||||
| 	b100_str = mboard_str.find("B100"); | ||||
| 	b200_str = mboard_str.find("B200"); | ||||
| 	b210_str = mboard_str.find("B210"); | ||||
| 	e100_str = mboard_str.find("E100"); | ||||
| 	e110_str = mboard_str.find("E110"); | ||||
| 	e310_str = mboard_str.find("E310"); | ||||
| 	e3xx_str = mboard_str.find("E3XX"); | ||||
| 	x300_str = mboard_str.find("X300"); | ||||
| 	x310_str = mboard_str.find("X310"); | ||||
| 	umtrx_str = dev_str.find("UmTRX"); | ||||
| 	// LimeSDR is based on STREAM board, so it's advertized as such | ||||
| 	limesdr_str = dev_str.find("STREAM"); | ||||
|  | ||||
| 	if (usrp1_str != std::string::npos) { | ||||
| 		LOG(ALERT) << "USRP1 is not supported using the UHD driver"; | ||||
| 		LOG(ALERT) << "Please compile with GNU Radio libusrp support"; | ||||
| 		dev_type = USRP1; | ||||
| 		return false; | ||||
| 	// Compare UHD motherboard and device strings */ | ||||
| 	auto mapIter = devStringMap.begin(); | ||||
| 	while (mapIter != devStringMap.end()) { | ||||
| 		if (devString.find(mapIter->first) != std::string::npos || | ||||
| 		    mboardString.find(mapIter->first) != std::string::npos) { | ||||
| 			dev_type = std::get<0>(mapIter->second); | ||||
| 			tx_window = std::get<1>(mapIter->second); | ||||
| 			return true; | ||||
| 		} | ||||
| 		mapIter++; | ||||
| 	} | ||||
|  | ||||
| 	if (b100_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_USRP1; | ||||
| 		dev_type = B100; | ||||
| 	} else if (b200_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_USRP1; | ||||
| 		dev_type = B200; | ||||
| 	} else if (b210_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_USRP1; | ||||
| 		dev_type = B210; | ||||
| 	} else if (e100_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = E1XX; | ||||
| 	} else if (e110_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = E1XX; | ||||
| 	} else if (usrp2_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = USRP2; | ||||
| 	} else if ((e310_str != std::string::npos) || | ||||
| 		   (e3xx_str != std::string::npos)) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = E3XX; | ||||
| 	} else if (x300_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = X3XX; | ||||
| 	} else if (x310_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = X3XX; | ||||
| 	} else if (umtrx_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_FIXED; | ||||
| 		dev_type = UMTRX; | ||||
| 	} else if (limesdr_str != std::string::npos) { | ||||
| 		tx_window = TX_WINDOW_USRP1; | ||||
| 		dev_type = LIMESDR; | ||||
| 	} else { | ||||
| 		LOG(ALERT) << "Unknown UHD device type " | ||||
| 			   << dev_str << " " << mboard_str; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (tx_window == TX_WINDOW_USRP1) { | ||||
| 		LOG(INFO) << "Using USRP1 type transmit window for " | ||||
| 			  << dev_str << " " << mboard_str; | ||||
| 	} else { | ||||
| 		LOG(INFO) << "Using fixed transmit window for " | ||||
| 			  << dev_str << " " << mboard_str; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| 	LOG(ALERT) << "Unsupported device " << devString; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -743,6 +608,45 @@ static bool uhd_e3xx_version_chk() | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void uhd_device::set_channels(bool swap) | ||||
| { | ||||
| 	if (iface == MULTI_ARFCN) { | ||||
| 		if (dev_type != B200 && dev_type != B210) | ||||
| 			throw std::invalid_argument("Device does not support MCBTS"); | ||||
| 		dev_type = B2XX_MCBTS; | ||||
| 		chans = 1; | ||||
| 	} | ||||
|  | ||||
| 	if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels) | ||||
| 		throw std::invalid_argument("Device does not support number of requested channels"); | ||||
|  | ||||
| 	std::string subdev_string; | ||||
| 	switch (dev_type) { | ||||
| 	case B210: | ||||
| 	case E3XX: | ||||
| 		if (chans == 1) | ||||
| 			subdev_string = swap ? "A:B" : "A:A"; | ||||
| 		else if (chans == 2) | ||||
| 			subdev_string = swap ? "A:B A:A" : "A:A A:B"; | ||||
| 		break; | ||||
| 	case X3XX: | ||||
| 	case UMTRX: | ||||
| 		if (chans == 1) | ||||
| 			subdev_string = swap ? "B:0" : "A:0"; | ||||
| 		else if (chans == 2) | ||||
| 			subdev_string = swap ? "B:0 A:0" : "A:0 B:0"; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if (!subdev_string.empty()) { | ||||
| 		uhd::usrp::subdev_spec_t spec(subdev_string); | ||||
| 		usrp_dev->set_tx_subdev_spec(spec); | ||||
| 		usrp_dev->set_rx_subdev_spec(spec); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| { | ||||
| 	const char *refstr; | ||||
| @@ -773,27 +677,15 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	// Verify and set channels | ||||
| 	if (iface == MULTI_ARFCN) { | ||||
| 		if ((dev_type != B200) && (dev_type != B210)) { | ||||
| 			LOG(ALERT) << "Unsupported device configuration"; | ||||
| 			return -1; | ||||
| 		} | ||||
| 	try { | ||||
| 		set_channels(swap_channels); | ||||
|         } catch (const std::exception &e) { | ||||
| 		LOG(ALERT) << "Channel setting failed - " << e.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 		dev_type = B2XX_MCBTS; | ||||
| 		chans = 1; | ||||
| 	} else if (chans == 2) { | ||||
| 		if (dev_type == B210) { | ||||
| 		} else if (dev_type == UMTRX) { | ||||
| 			uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0"); | ||||
| 			usrp_dev->set_tx_subdev_spec(subdev_spec); | ||||
| 			usrp_dev->set_rx_subdev_spec(subdev_spec); | ||||
| 		} else { | ||||
| 			LOG(ALERT) << "Invalid device configuration"; | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} else if (chans != 1) { | ||||
| 		LOG(ALERT) << "Invalid channel combination for device"; | ||||
| 	if (!set_antennas()) { | ||||
| 		LOG(ALERT) << "UHD antenna setting failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @@ -820,14 +712,12 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
|  | ||||
| 	usrp_dev->set_clock_source(refstr); | ||||
|  | ||||
| 	// Set rates | ||||
| 	double _rx_rate = select_rate(dev_type, rx_sps, iface); | ||||
| 	double _tx_rate = select_rate(dev_type, tx_sps, iface); | ||||
|  | ||||
| 	if ((_tx_rate < 0.0) || (_rx_rate < 0.0)) | ||||
| 		return -1; | ||||
| 	if (set_rates(_tx_rate, _rx_rate) < 0) | ||||
| 	try { | ||||
| 		set_rates(); | ||||
|         } catch (const std::exception &e) { | ||||
| 		LOG(ALERT) << "UHD rate setting failed - " << e.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	// Set RF frontend bandwidth | ||||
| 	if (dev_type == UMTRX) { | ||||
| @@ -860,18 +750,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 	for (size_t i = 0; i < rx_buffers.size(); i++) | ||||
| 		rx_buffers[i] = new smpl_buf(buf_len, rx_rate); | ||||
|  | ||||
| 	// Set receive chain sample offset. Trigger the EDGE offset | ||||
| 	// table by checking for 4 SPS on the receive path. No other | ||||
| 	// configuration supports using 4 SPS. | ||||
| 	double offset = get_dev_offset(); | ||||
| 	if (offset == 0.0) { | ||||
| 		LOG(ERR) << "Unsupported configuration, no correction applied"; | ||||
| 		ts_offset = 0; | ||||
| 	} else  { | ||||
| 		ts_offset = (TIMESTAMP) (offset * rx_rate); | ||||
| 	} | ||||
|  | ||||
| 	// Initialize and shadow gain values  | ||||
| 	// Initialize and shadow gain values | ||||
| 	init_gains(); | ||||
|  | ||||
| 	// Print configuration | ||||
| @@ -1092,7 +971,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|  | ||||
| 		rx_pkt_cnt++; | ||||
|  | ||||
| 		// Check for errors  | ||||
| 		// Check for errors | ||||
| 		rc = check_rx_md_err(metadata, num_smpls); | ||||
| 		switch (rc) { | ||||
| 		case ERROR_UNRECOVERABLE: | ||||
| @@ -1236,7 +1115,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) | ||||
|  | ||||
| 	/* Find center frequency between channels */ | ||||
| 	rf_spread = fabs(freqs[!chan] - freq); | ||||
| 	if (rf_spread > B2XX_CLK_RT) { | ||||
| 	if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { | ||||
| 		LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n"; | ||||
| 		return treq; | ||||
| 	} | ||||
| @@ -1331,6 +1210,78 @@ double uhd_device::getRxFreq(size_t chan) | ||||
| 	return rx_freqs[chan]; | ||||
| } | ||||
|  | ||||
| bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	std::vector<std::string> avail; | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	avail = usrp_dev->get_rx_antennas(chan); | ||||
| 	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { | ||||
| 		LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; | ||||
| 		LOG(INFO) << "Available Rx antennas: "; | ||||
| 		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i) | ||||
| 			LOG(INFO) << "- '" << *i << "'"; | ||||
| 		return false; | ||||
| 	} | ||||
| 	usrp_dev->set_rx_antenna(ant, chan); | ||||
| 	rx_paths[chan] = usrp_dev->get_rx_antenna(chan); | ||||
|  | ||||
| 	if (ant != rx_paths[chan]) { | ||||
| 		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string uhd_device::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	return usrp_dev->get_rx_antenna(chan); | ||||
| } | ||||
|  | ||||
| bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	std::vector<std::string> avail; | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	avail = usrp_dev->get_tx_antennas(chan); | ||||
| 	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { | ||||
| 		LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; | ||||
| 		LOG(INFO) << "Available Tx antennas: "; | ||||
| 		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i) | ||||
| 			LOG(INFO) << "- '" << *i << "'"; | ||||
| 		return false; | ||||
| 	} | ||||
| 	usrp_dev->set_tx_antenna(ant, chan); | ||||
| 	tx_paths[chan] = usrp_dev->get_tx_antenna(chan); | ||||
|  | ||||
| 	if (ant != tx_paths[chan]) { | ||||
| 		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string uhd_device::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	return usrp_dev->get_tx_antenna(chan); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Only allow sampling the Rx path lower than Tx and not vice-versa. | ||||
|  * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed | ||||
| @@ -1352,7 +1303,7 @@ TIMESTAMP uhd_device::initialReadTimestamp() | ||||
| double uhd_device::fullScaleInputValue() | ||||
| { | ||||
| 	if (dev_type == LIMESDR) | ||||
| 		return (double) 2047 * LIMESDR_TX_AMPL; | ||||
| 		return (double) SHRT_MAX * LIMESDR_TX_AMPL; | ||||
| 	if (dev_type == UMTRX) | ||||
| 		return (double) SHRT_MAX * UMTRX_TX_AMPL; | ||||
| 	else | ||||
| @@ -1361,7 +1312,6 @@ double uhd_device::fullScaleInputValue() | ||||
|  | ||||
| double uhd_device::fullScaleOutputValue() | ||||
| { | ||||
| 	if (dev_type == LIMESDR) return (double) 2047; | ||||
| 	return (double) SHRT_MAX; | ||||
| } | ||||
|  | ||||
| @@ -1618,7 +1568,9 @@ std::string smpl_buf::str_code(ssize_t code) | ||||
| } | ||||
|  | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       InterfaceType iface, size_t chans, double offset) | ||||
| 			       InterfaceType iface, size_t chans, double offset, | ||||
| 			       const std::vector<std::string>& tx_paths, | ||||
| 			       const std::vector<std::string>& rx_paths) | ||||
| { | ||||
| 	return new uhd_device(tx_sps, rx_sps, iface, chans, offset); | ||||
| 	return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths); | ||||
| } | ||||
|   | ||||
| @@ -27,17 +27,16 @@ | ||||
| 	Compilation Flags | ||||
|  | ||||
| 	SWLOOPBACK	compile for software loopback testing | ||||
| */  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include "Logger.h" | ||||
| #include "Threads.h" | ||||
| #include "USRPDevice.h" | ||||
|  | ||||
| #include <Logger.h> | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| @@ -81,7 +80,7 @@ USRPDevice::USRPDevice(size_t sps) | ||||
|   else | ||||
|     pingOffset = 0; | ||||
|  | ||||
| #ifdef SWLOOPBACK  | ||||
| #ifdef SWLOOPBACK | ||||
|   samplePeriod = 1.0e6/actualSampleRate; | ||||
|   loopbackBufferSize = 0; | ||||
|   gettimeofday(&lastReadTime,NULL); | ||||
| @@ -94,9 +93,9 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|   writeLock.unlock(); | ||||
|  | ||||
|   LOG(INFO) << "opening USRP device.."; | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   string rbf = "std_inband.rbf"; | ||||
|   //string rbf = "inband_1rxhb_1tx.rbf";  | ||||
|   //string rbf = "inband_1rxhb_1tx.rbf"; | ||||
|   m_uRx.reset(); | ||||
|   if (!skipRx) { | ||||
|   try { | ||||
| @@ -145,7 +144,7 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|  | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
|    | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   switch (dboardConfig) { | ||||
| @@ -176,19 +175,19 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|   samplesRead = 0; | ||||
|   samplesWritten = 0; | ||||
|   started = false; | ||||
|    | ||||
|  | ||||
|   return NORMAL; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| bool USRPDevice::start()  | ||||
| bool USRPDevice::start() | ||||
| { | ||||
|   LOG(INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx && !skipRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
|    | ||||
|  | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
|  | ||||
| @@ -218,8 +217,8 @@ bool USRPDevice::start() | ||||
|   hi32Timestamp = 0; | ||||
|   isAligned = false; | ||||
|  | ||||
|   | ||||
|   if (!skipRx)  | ||||
|  | ||||
|   if (!skipRx) | ||||
|   started = (m_uRx->start() && m_uTx->start()); | ||||
|   else | ||||
|   started = m_uTx->start(); | ||||
| @@ -230,14 +229,14 @@ bool USRPDevice::start() | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bool USRPDevice::stop()  | ||||
| bool USRPDevice::stop() | ||||
| { | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
|    | ||||
|  | ||||
|   delete[] currData; | ||||
|    | ||||
|  | ||||
|   started = !(m_uRx->stop() && m_uTx->stop()); | ||||
|   return !started; | ||||
| #else | ||||
| @@ -258,7 +257,7 @@ double USRPDevice::minTxGain() | ||||
| double USRPDevice::maxRxGain() | ||||
| { | ||||
|   return m_dbRx->gain_max(); | ||||
| }  | ||||
| } | ||||
|  | ||||
| double USRPDevice::minRxGain() | ||||
| { | ||||
| @@ -314,28 +313,68 @@ double USRPDevice::setRxGain(double dB, size_t chan) | ||||
|   return dB; | ||||
| } | ||||
|  | ||||
| bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string USRPDevice::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
|  | ||||
| bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string USRPDevice::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
|  | ||||
|  | ||||
| // NOTE: Assumes sequential reads | ||||
| int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|                             TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) | ||||
| { | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx) | ||||
|     return 0; | ||||
|  | ||||
|   short *buf = bufs[0]; | ||||
|  | ||||
|   timestamp += timestampOffset; | ||||
|    | ||||
|  | ||||
|   if (timestamp + len < timeStart) { | ||||
|     memset(buf,0,len*2*sizeof(short)); | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
|   if (underrun) *underrun = false; | ||||
|   | ||||
|  | ||||
|   uint32_t readBuf[2000]; | ||||
|   | ||||
|  | ||||
|   while (1) { | ||||
|     //guestimate USB read size | ||||
|     int readLen=0; | ||||
| @@ -345,7 +384,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|       readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0)); | ||||
|       if (readLen > 8000) readLen= (8000/512)*512; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     // read USRP packets, parse and save A/D data as needed | ||||
|     readLen = m_uRx->read((void *)readBuf,readLen,overrun); | ||||
|     for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { | ||||
| @@ -382,13 +421,13 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	continue; | ||||
|       } | ||||
|       if ((word0 >> 28) & 0x04) { | ||||
| 	if (underrun) *underrun = true;  | ||||
| 	if (underrun) *underrun = true; | ||||
| 	LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
|       } | ||||
|       if (RSSI) *RSSI = (word0 >> 21) & 0x3f; | ||||
|        | ||||
|  | ||||
|       if (!isAligned) continue; | ||||
|        | ||||
|  | ||||
|       unsigned cursorStart = pktTimestamp - timeStart + dataStart; | ||||
|       while (cursorStart*2 > currDataSize) { | ||||
| 	cursorStart -= currDataSize/2; | ||||
| @@ -401,17 +440,17 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|       else { | ||||
| 	memcpy(data+cursorStart*2,tmpBuf+2,payloadSz); | ||||
|       } | ||||
|       if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)  | ||||
|       if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd) | ||||
| 	timeEnd = pktTimestamp+payloadSz/2/sizeof(short); | ||||
|  | ||||
|       LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
|  | ||||
|     }	 | ||||
|   }      | ||||
|   | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // copy desired data to buf | ||||
|   unsigned bufStart = dataStart+(timestamp-timeStart); | ||||
|   if (bufStart + len < currDataSize/2) {  | ||||
|   if (bufStart + len < currDataSize/2) { | ||||
|     LOG(DEBUG) << "bufStart: " << bufStart; | ||||
|     memcpy(buf,data+bufStart*2,len*2*sizeof(short)); | ||||
|     memset(data+bufStart*2,0,len*2*sizeof(short)); | ||||
| @@ -429,21 +468,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|   timeStart = timestamp + len; | ||||
|  | ||||
|   return len; | ||||
|    | ||||
|  | ||||
| #else | ||||
|   if (loopbackBufferSize < 2) return 0; | ||||
|   int numSamples = 0; | ||||
|   struct timeval currTime; | ||||
|   gettimeofday(&currTime,NULL); | ||||
|   double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +  | ||||
|   double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 + | ||||
|     (currTime.tv_usec - lastReadTime.tv_usec); | ||||
|   if (timeElapsed < samplePeriod) {return 0;} | ||||
|   int numSamplesToRead = (int) floor(timeElapsed/samplePeriod); | ||||
|   if (numSamplesToRead < len) return 0; | ||||
|    | ||||
|  | ||||
|   if (numSamplesToRead > len) numSamplesToRead = len; | ||||
|   if (numSamplesToRead > loopbackBufferSize/2) { | ||||
|     firstRead =false;  | ||||
|     firstRead =false; | ||||
|     numSamplesToRead = loopbackBufferSize/2; | ||||
|   } | ||||
|   memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead); | ||||
| @@ -461,7 +500,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|     firstRead = true; | ||||
|   } | ||||
|   samplesRead += numSamples; | ||||
|    | ||||
|  | ||||
|   return numSamples; | ||||
| #endif | ||||
| } | ||||
| @@ -472,7 +511,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
| { | ||||
|   writeLock.lock(); | ||||
|  | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uTx) | ||||
|     return 0; | ||||
|  | ||||
| @@ -519,14 +558,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
|   memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len); | ||||
|   samplesWritten += retVal; | ||||
|   loopbackBufferSize += retVal*2; | ||||
|     | ||||
|  | ||||
|   return retVal; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bool USRPDevice::updateAlignment(TIMESTAMP timestamp)  | ||||
| bool USRPDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| { | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   short data[] = {0x00,0x02,0x00,0x00}; | ||||
|   uint32_t *wordPtr = (uint32_t *) data; | ||||
|   *wordPtr = host_to_usrp_u32(*wordPtr); | ||||
| @@ -543,7 +582,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
| bool USRPDevice::setTxFreq(double wFreq, size_t chan) | ||||
| { | ||||
|   usrp_tune_result result; | ||||
| @@ -601,7 +640,9 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;}; | ||||
| #endif | ||||
|  | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       size_t chans, double) | ||||
| 			       InterfaceType iface, size_t chans, double offset, | ||||
| 			       const std::vector<std::string>& tx_paths, | ||||
| 			       const std::vector<std::string>& rx_paths) | ||||
| { | ||||
| 	return new USRPDevice(tx_sps); | ||||
| } | ||||
|   | ||||
| @@ -83,10 +83,10 @@ private: | ||||
|  | ||||
|   double rxGain; | ||||
|  | ||||
| #ifdef SWLOOPBACK  | ||||
| #ifdef SWLOOPBACK | ||||
|   short loopbackBuffer[1000000]; | ||||
|   int loopbackBufferSize; | ||||
|   double samplePeriod;  | ||||
|   double samplePeriod; | ||||
|  | ||||
|   struct timeval startTime; | ||||
|   struct timeval lastReadTime; | ||||
| @@ -179,6 +179,18 @@ private: | ||||
|   /** return minimum Rx Gain **/ | ||||
|   double minTxGain(void); | ||||
|  | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   bool setRxAntenna(const std::string &ant, size_t chan = 0); | ||||
|  | ||||
|   /* return the used RX path */ | ||||
|   std::string getRxAntenna(size_t chan = 0); | ||||
|  | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   bool setTxAntenna(const std::string &ant, size_t chan = 0); | ||||
|  | ||||
|   /* return the used RX path */ | ||||
|   std::string getTxAntenna(size_t chan = 0); | ||||
|  | ||||
|   /** Return internal status values */ | ||||
|   inline double getTxFreq(size_t chan = 0) { return 0; } | ||||
|   inline double getRxFreq(size_t chan = 0) { return 0; } | ||||
| @@ -186,7 +198,7 @@ private: | ||||
|   inline double numberRead() { return samplesRead; } | ||||
|   inline double numberWritten() { return samplesWritten; } | ||||
|  | ||||
|   std::vector<std::string> tx_paths, rx_paths; | ||||
| }; | ||||
|  | ||||
| #endif // _USRP_DEVICE_H_ | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,9 @@ | ||||
| void neon_convert_ps_si16_4n(short *, const float *, const float *, int); | ||||
| void neon_convert_si16_ps_4n(float *, const short *, int); | ||||
|  | ||||
| void convert_init(void) { | ||||
| } | ||||
|  | ||||
| /* 4*N 16-bit signed integer conversion with remainder */ | ||||
| static void neon_convert_si16_ps(float *out, | ||||
| 				 const short *in, | ||||
| @@ -54,7 +57,6 @@ static void neon_convert_ps_si16(short *out, | ||||
| 	for (int i = 0; i < len % 4; i++) | ||||
| 		out[start + i] = (short) (in[start + i] * (*scale)); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void convert_float_short(short *out, const float *in, float scale, int len) | ||||
| { | ||||
|   | ||||
| @@ -27,10 +27,14 @@ | ||||
| #include <signal.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <sched.h> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
| #include <iostream> | ||||
|  | ||||
| #include <GSMCommon.h> | ||||
| #include <Logger.h> | ||||
| #include <Configuration.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include "convolve.h" | ||||
| @@ -61,7 +65,8 @@ extern "C" { | ||||
|  | ||||
| struct trx_config { | ||||
| 	std::string log_level; | ||||
| 	std::string addr; | ||||
| 	std::string local_addr; | ||||
| 	std::string remote_addr; | ||||
| 	std::string dev_args; | ||||
| 	unsigned port; | ||||
| 	unsigned tx_sps; | ||||
| @@ -77,10 +82,11 @@ struct trx_config { | ||||
| 	double rssi_offset; | ||||
| 	bool swap_channels; | ||||
| 	bool edge; | ||||
| 	int sched_rr; | ||||
| 	std::vector<std::string> rx_paths; | ||||
| 	std::vector<std::string> tx_paths; | ||||
| }; | ||||
|  | ||||
| ConfigurationTable gConfig; | ||||
|  | ||||
| volatile bool gshutdown = false; | ||||
|  | ||||
| /* Setup configuration values | ||||
| @@ -93,6 +99,7 @@ volatile bool gshutdown = false; | ||||
| bool trx_setup_config(struct trx_config *config) | ||||
| { | ||||
| 	std::string refstr, fillstr, divstr, mcstr, edgestr; | ||||
| 	std::vector<std::string>::const_iterator si; | ||||
|  | ||||
| 	if (config->mcbts && config->chans > 5) { | ||||
| 		std::cout << "Unsupported number of channels" << std::endl; | ||||
| @@ -132,7 +139,8 @@ bool trx_setup_config(struct trx_config *config) | ||||
| 	ost << "   Log Level............... " << config->log_level << std::endl; | ||||
| 	ost << "   Device args............. " << config->dev_args << std::endl; | ||||
| 	ost << "   TRX Base Port........... " << config->port << std::endl; | ||||
| 	ost << "   TRX Address............. " << config->addr << std::endl; | ||||
| 	ost << "   TRX Address............. " << config->local_addr << std::endl; | ||||
| 	ost << "   GSM Core Address........." << config->remote_addr << std::endl; | ||||
| 	ost << "   Channels................ " << config->chans << std::endl; | ||||
| 	ost << "   Tx Samples-per-Symbol... " << config->tx_sps << std::endl; | ||||
| 	ost << "   Rx Samples-per-Symbol... " << config->rx_sps << std::endl; | ||||
| @@ -143,8 +151,16 @@ bool trx_setup_config(struct trx_config *config) | ||||
| 	ost << "   Tuning offset........... " << config->offset << std::endl; | ||||
| 	ost << "   RSSI to dBm offset...... " << config->rssi_offset << std::endl; | ||||
| 	ost << "   Swap channels........... " << config->swap_channels << std::endl; | ||||
| 	std::cout << ost << std::endl; | ||||
| 	ost << "   Tx Antennas............."; | ||||
| 		for (si = config->tx_paths.begin(); si != config->tx_paths.end(); ++si) | ||||
| 			ost << " '" << ((*si != "") ? *si : "<default>") <<  "'"; | ||||
| 		ost << std::endl; | ||||
| 	ost << "   Rx Antennas............."; | ||||
| 		for (si = config->rx_paths.begin(); si != config->rx_paths.end(); ++si) | ||||
| 			ost << " '" << ((*si != "") ? *si : "<default>") <<  "'"; | ||||
| 		ost << std::endl; | ||||
|  | ||||
| 	std::cout << ost << std::endl; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| @@ -198,9 +214,10 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio) | ||||
| 	Transceiver *trx; | ||||
| 	VectorFIFO *fifo; | ||||
|  | ||||
| 	trx = new Transceiver(config->port, config->addr.c_str(), | ||||
| 			      config->tx_sps, config->rx_sps, config->chans, | ||||
| 			      GSM::Time(3,0), radio, config->rssi_offset); | ||||
| 	trx = new Transceiver(config->port, config->local_addr.c_str(), | ||||
| 			      config->remote_addr.c_str(), config->tx_sps, | ||||
| 			      config->rx_sps, config->chans, GSM::Time(3,0), | ||||
| 			      radio, config->rssi_offset); | ||||
| 	if (!trx->init(config->filler, config->rtsc, | ||||
| 		       config->rach_delay, config->edge)) { | ||||
| 		LOG(ALERT) << "Failed to initialize transceiver"; | ||||
| @@ -239,6 +256,21 @@ static void setup_signal_handlers() | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| static std::vector<std::string> comma_delimited_to_vector(char* opt) { | ||||
| 	std::string str = std::string(opt); | ||||
| 	std::vector<std::string> result; | ||||
| 	std::stringstream ss(str); | ||||
|  | ||||
| 	while( ss.good() ) | ||||
| 	{ | ||||
| 	    std::string substr; | ||||
| 	    getline(ss, substr, ','); | ||||
| 	    result.push_back(substr); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| static void print_help() | ||||
| { | ||||
| 	fprintf(stdout, "Options:\n" | ||||
| @@ -246,6 +278,7 @@ static void print_help() | ||||
| 		"  -a    UHD device args\n" | ||||
| 		"  -l    Logging level (%s)\n" | ||||
| 		"  -i    IP address of GSM core\n" | ||||
| 		"  -j    IP address of osmo-trx\n" | ||||
| 		"  -p    Base port number\n" | ||||
| 		"  -e    Enable EDGE receiver\n" | ||||
| 		"  -m    Enable multi-ARFCN transceiver (default=disabled)\n" | ||||
| @@ -259,16 +292,21 @@ static void print_help() | ||||
| 		"  -r    Random Normal Burst test mode with TSC\n" | ||||
| 		"  -A    Random Access Burst test mode with delay\n" | ||||
| 		"  -R    RSSI to dBm offset in dB (default=0)\n" | ||||
| 		"  -S    Swap channels (UmTRX only)\n", | ||||
| 		"  -S    Swap channels (UmTRX only)\n" | ||||
| 		"  -t    SCHED_RR real-time priority (1..32)\n" | ||||
| 		"  -y    comma-delimited list of Tx paths (num elements matches -c)\n" | ||||
| 		"  -z    comma-delimited list of Rx paths (num elements matches -c)\n", | ||||
| 		"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG"); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| { | ||||
| 	int option; | ||||
| 	bool tx_path_set = false, rx_path_set = false; | ||||
|  | ||||
| 	config->log_level = "NOTICE"; | ||||
| 	config->addr = DEFAULT_TRX_IP; | ||||
| 	config->local_addr = DEFAULT_TRX_IP; | ||||
| 	config->remote_addr = DEFAULT_TRX_IP; | ||||
| 	config->port = DEFAULT_TRX_PORT; | ||||
| 	config->tx_sps = DEFAULT_TX_SPS; | ||||
| 	config->rx_sps = DEFAULT_RX_SPS; | ||||
| @@ -283,8 +321,11 @@ static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| 	config->rssi_offset = 0.0; | ||||
| 	config->swap_channels = false; | ||||
| 	config->edge = false; | ||||
| 	config->sched_rr = -1; | ||||
| 	config->tx_paths = std::vector<std::string>(DEFAULT_CHANS, ""); | ||||
| 	config->rx_paths = std::vector<std::string>(DEFAULT_CHANS, ""); | ||||
|  | ||||
| 	while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:A:R:Se")) != -1) { | ||||
| 	while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:")) != -1) { | ||||
| 		switch (option) { | ||||
| 		case 'h': | ||||
| 			print_help(); | ||||
| @@ -297,7 +338,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| 			config->log_level = optarg; | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			config->addr = optarg; | ||||
| 			config->remote_addr = optarg; | ||||
| 			break; | ||||
| 		case 'j': | ||||
| 			config->local_addr = optarg; | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			config->port = atoi(optarg); | ||||
| @@ -343,6 +387,17 @@ static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| 		case 'e': | ||||
| 			config->edge = true; | ||||
| 			break; | ||||
| 		case 't': | ||||
| 			config->sched_rr = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'y': | ||||
| 			config->tx_paths = comma_delimited_to_vector(optarg); | ||||
| 			tx_path_set = true; | ||||
| 			break; | ||||
| 		case 'z': | ||||
| 			config->rx_paths = comma_delimited_to_vector(optarg); | ||||
| 			rx_path_set = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			print_help(); | ||||
| 			exit(0); | ||||
| @@ -379,6 +434,19 @@ static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	if (!tx_path_set) { | ||||
| 		config->tx_paths = std::vector<std::string>(config->chans, ""); | ||||
| 	} else if (config->tx_paths.size() != config->chans) { | ||||
| 		printf("Num of channels and num of Tx Antennas doesn't match\n\n"); | ||||
| 		goto bad_config; | ||||
| 	} | ||||
| 	if (!rx_path_set) { | ||||
| 		config->rx_paths = std::vector<std::string>(config->chans, ""); | ||||
| 	} else if (config->rx_paths.size() != config->chans) { | ||||
| 		printf("Num of channels and num of Rx Antennas doesn't match\n\n"); | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	return; | ||||
|  | ||||
| bad_config: | ||||
| @@ -386,6 +454,21 @@ bad_config: | ||||
| 	exit(0); | ||||
| } | ||||
|  | ||||
| static int set_sched_rr(int prio) | ||||
| { | ||||
| 	struct sched_param param; | ||||
| 	int rc; | ||||
| 	memset(¶m, 0, sizeof(param)); | ||||
| 	param.sched_priority = prio; | ||||
| 	printf("Setting SCHED_RR priority(%d)\n", param.sched_priority); | ||||
| 	rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); | ||||
| 	if (rc != 0) { | ||||
| 		std::cerr << "Config: Setting SCHED_RR failed" << std::endl; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int type, chans, ref; | ||||
| @@ -397,18 +480,26 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| 	printf("Info: SSE3 support compiled in"); | ||||
| #ifdef HAVE___BUILTIN_CPU_SUPPORTS | ||||
| 	if (__builtin_cpu_supports("sse3")) | ||||
| 		printf(" and supported by CPU\n"); | ||||
| 	else | ||||
| 		printf(", but not supported by CPU\n"); | ||||
| #else | ||||
| 	printf(", but runtime SIMD detection disabled\n"); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_SSE4_1 | ||||
| 	printf("Info: SSE4.1 support compiled in"); | ||||
| #ifdef HAVE___BUILTIN_CPU_SUPPORTS | ||||
| 	if (__builtin_cpu_supports("sse4.1")) | ||||
| 		printf(" and supported by CPU\n"); | ||||
| 	else | ||||
| 		printf(", but not supported by CPU\n"); | ||||
| #else | ||||
| 	printf(", but runtime SIMD detection disabled\n"); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| 	convolve_init(); | ||||
| @@ -416,6 +507,11 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 	handle_options(argc, argv, &config); | ||||
|  | ||||
| 	if (config.sched_rr != -1) { | ||||
| 		if (set_sched_rr(config.sched_rr) < 0) | ||||
| 			return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	setup_signal_handlers(); | ||||
|  | ||||
| 	/* Check database sanity */ | ||||
| @@ -424,7 +520,7 @@ int main(int argc, char *argv[]) | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7); | ||||
| 	gLogInit(config.log_level.c_str()); | ||||
|  | ||||
| 	srandom(time(NULL)); | ||||
|  | ||||
| @@ -440,7 +536,7 @@ int main(int argc, char *argv[]) | ||||
| 		ref = RadioDevice::REF_INTERNAL; | ||||
|  | ||||
| 	usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface, | ||||
| 				 config.chans, config.offset); | ||||
| 				 config.chans, config.offset, config.tx_paths, config.rx_paths); | ||||
| 	type = usrp->open(config.dev_args, ref, config.swap_channels); | ||||
| 	if (type < 0) { | ||||
| 		LOG(ALERT) << "Failed to create radio device" << std::endl; | ||||
|   | ||||
| @@ -50,7 +50,9 @@ class RadioDevice { | ||||
|   }; | ||||
|  | ||||
|   static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, | ||||
|                            size_t chans = 1, double offset = 0.0); | ||||
|                            size_t chans = 1, double offset = 0.0, | ||||
|                            const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""), | ||||
|                            const std::vector<std::string>& rx_paths = std::vector<std::string>(1, "")); | ||||
|  | ||||
|   /** Initialize the USRP */ | ||||
|   virtual int open(const std::string &args, int ref, bool swap_channels)=0; | ||||
| @@ -136,6 +138,18 @@ class RadioDevice { | ||||
|   /** return minimum Tx Gain **/ | ||||
|   virtual double minTxGain(void) = 0; | ||||
|  | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0; | ||||
|  | ||||
|   /** return the used RX path */ | ||||
|   virtual std::string getRxAntenna(size_t chan = 0) = 0; | ||||
|  | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0; | ||||
|  | ||||
|   /** return the used RX path */ | ||||
|   virtual std::string getTxAntenna(size_t chan = 0) = 0; | ||||
|  | ||||
|   /** Return internal status values */ | ||||
|   virtual double getTxFreq(size_t chan = 0) = 0; | ||||
|   virtual double getRxFreq(size_t chan = 0) = 0; | ||||
|   | ||||
| @@ -46,14 +46,10 @@ using namespace GSM; | ||||
| #define CLIP_THRESH		30000.0f | ||||
|  | ||||
| /** Lookup tables for trigonometric approximation */ | ||||
| float cosTable[TABLESIZE+1]; // add 1 element for wrap around | ||||
| float sinTable[TABLESIZE+1]; | ||||
| float sincTable[TABLESIZE+1]; | ||||
| static float sincTable[TABLESIZE+1]; // add 1 element for wrap around | ||||
|  | ||||
| /** Constants */ | ||||
| static const float M_PI_F = (float)M_PI; | ||||
| static const float M_2PI_F = (float)(2.0*M_PI); | ||||
| static const float M_1_2PI_F = 1/M_2PI_F; | ||||
|  | ||||
| /* Precomputed rotation vectors */ | ||||
| static signalVector *GMSKRotation4 = NULL; | ||||
| @@ -64,7 +60,7 @@ static signalVector *GMSKReverseRotation1 = NULL; | ||||
| /* Precomputed fractional delay filters */ | ||||
| static signalVector *delayFilters[DELAYFILTS]; | ||||
|  | ||||
| static Complex<float> psk8_table[8] = { | ||||
| static const Complex<float> psk8_table[8] = { | ||||
|    Complex<float>(-0.70710678,  0.70710678), | ||||
|    Complex<float>( 0.0, -1.0), | ||||
|    Complex<float>( 0.0,  1.0), | ||||
| @@ -172,67 +168,7 @@ void sigProcLibDestroy() | ||||
|   GSMPulse4 = NULL; | ||||
| } | ||||
|  | ||||
| // dB relative to 1.0. | ||||
| // if > 1.0, then return 0 dB | ||||
| float dB(float x) { | ||||
|    | ||||
|   float arg = 1.0F; | ||||
|   float dB = 0.0F; | ||||
|    | ||||
|   if (x >= 1.0F) return 0.0F; | ||||
|   if (x <= 0.0F) return -200.0F; | ||||
|  | ||||
|   float prevArg = arg; | ||||
|   float prevdB = dB; | ||||
|   float stepSize = 16.0F; | ||||
|   float dBstepSize = 12.0F; | ||||
|   while (stepSize > 1.0F) { | ||||
|     do { | ||||
|       prevArg = arg; | ||||
|       prevdB = dB; | ||||
|       arg /= stepSize; | ||||
|       dB -= dBstepSize; | ||||
|     } while (arg > x); | ||||
|     arg = prevArg; | ||||
|     dB = prevdB; | ||||
|     stepSize *= 0.5F; | ||||
|     dBstepSize -= 3.0F; | ||||
|   } | ||||
|  return ((arg-x)*(dB-3.0F) + (x-arg*0.5F)*dB)/(arg - arg*0.5F); | ||||
|  | ||||
| } | ||||
|  | ||||
| // 10^(-dB/10), inverse of dB func. | ||||
| float dBinv(float x) { | ||||
|    | ||||
|   float arg = 1.0F; | ||||
|   float dB = 0.0F; | ||||
|    | ||||
|   if (x >= 0.0F) return 1.0F; | ||||
|   if (x <= -200.0F) return 0.0F; | ||||
|  | ||||
|   float prevArg = arg; | ||||
|   float prevdB = dB; | ||||
|   float stepSize = 16.0F; | ||||
|   float dBstepSize = 12.0F; | ||||
|   while (stepSize > 1.0F) { | ||||
|     do { | ||||
|       prevArg = arg; | ||||
|       prevdB = dB; | ||||
|       arg /= stepSize; | ||||
|       dB -= dBstepSize; | ||||
|     } while (dB > x); | ||||
|     arg = prevArg; | ||||
|     dB = prevdB; | ||||
|     stepSize *= 0.5F; | ||||
|     dBstepSize -= 3.0F; | ||||
|   } | ||||
|  | ||||
|   return ((dB-x)*(arg*0.5F)+(x-(dB-3.0F))*(arg))/3.0F; | ||||
|  | ||||
| } | ||||
|  | ||||
| float vectorNorm2(const signalVector &x)  | ||||
| static float vectorNorm2(const signalVector &x) | ||||
| { | ||||
|   signalVector::const_iterator xPtr = x.begin(); | ||||
|   float Energy = 0.0; | ||||
| @@ -242,64 +178,6 @@ float vectorNorm2(const signalVector &x) | ||||
|   return Energy; | ||||
| } | ||||
|  | ||||
|  | ||||
| float vectorPower(const signalVector &x)  | ||||
| { | ||||
|   return vectorNorm2(x)/x.size(); | ||||
| } | ||||
|  | ||||
| /** compute cosine via lookup table */ | ||||
| float cosLookup(const float x) | ||||
| { | ||||
|   float arg = x*M_1_2PI_F; | ||||
|   while (arg > 1.0F) arg -= 1.0F; | ||||
|   while (arg < 0.0F) arg += 1.0F; | ||||
|  | ||||
|   const float argT = arg*((float)TABLESIZE); | ||||
|   const int argI = (int)argT; | ||||
|   const float delta = argT-argI; | ||||
|   const float iDelta = 1.0F-delta; | ||||
|   return iDelta*cosTable[argI] + delta*cosTable[argI+1]; | ||||
| } | ||||
|  | ||||
| /** compute sine via lookup table */ | ||||
| float sinLookup(const float x)  | ||||
| { | ||||
|   float arg = x*M_1_2PI_F; | ||||
|   while (arg > 1.0F) arg -= 1.0F; | ||||
|   while (arg < 0.0F) arg += 1.0F; | ||||
|  | ||||
|   const float argT = arg*((float)TABLESIZE); | ||||
|   const int argI = (int)argT; | ||||
|   const float delta = argT-argI; | ||||
|   const float iDelta = 1.0F-delta; | ||||
|   return iDelta*sinTable[argI] + delta*sinTable[argI+1]; | ||||
| } | ||||
|  | ||||
|  | ||||
| /** compute e^(-jx) via lookup table. */ | ||||
| static complex expjLookup(float x) | ||||
| { | ||||
|   float arg = x*M_1_2PI_F; | ||||
|   while (arg > 1.0F) arg -= 1.0F; | ||||
|   while (arg < 0.0F) arg += 1.0F; | ||||
|  | ||||
|   const float argT = arg*((float)TABLESIZE); | ||||
|   const int argI = (int)argT; | ||||
|   const float delta = argT-argI; | ||||
|   const float iDelta = 1.0F-delta; | ||||
|    return complex(iDelta*cosTable[argI] + delta*cosTable[argI+1], | ||||
| 		   iDelta*sinTable[argI] + delta*sinTable[argI+1]); | ||||
| } | ||||
|  | ||||
| /** Library setup functions */ | ||||
| static void initTrigTables() { | ||||
|   for (int i = 0; i < TABLESIZE+1; i++) { | ||||
|     cosTable[i] = cos(2.0*M_PI*i/TABLESIZE); | ||||
|     sinTable[i] = sin(2.0*M_PI*i/TABLESIZE); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Initialize 4 sps and 1 sps rotation tables | ||||
|  */ | ||||
| @@ -311,11 +189,11 @@ static void initGMSKRotationTables() | ||||
|   GMSKReverseRotation4 = new signalVector(len4); | ||||
|   signalVector::iterator rotPtr = GMSKRotation4->begin(); | ||||
|   signalVector::iterator revPtr = GMSKReverseRotation4->begin(); | ||||
|   float phase = 0.0; | ||||
|   auto phase = 0.0; | ||||
|   while (rotPtr != GMSKRotation4->end()) { | ||||
|     *rotPtr++ = expjLookup(phase); | ||||
|     *revPtr++ = expjLookup(-phase); | ||||
|     phase += M_PI_F / 2.0F / 4.0; | ||||
|     *rotPtr++ = complex(cos(phase), sin(phase)); | ||||
|     *revPtr++ = complex(cos(-phase), sin(-phase)); | ||||
|     phase += M_PI / 2.0 / 4.0; | ||||
|   } | ||||
|  | ||||
|   GMSKRotation1 = new signalVector(len1); | ||||
| @@ -324,9 +202,9 @@ static void initGMSKRotationTables() | ||||
|   revPtr = GMSKReverseRotation1->begin(); | ||||
|   phase = 0.0; | ||||
|   while (rotPtr != GMSKRotation1->end()) { | ||||
|     *rotPtr++ = expjLookup(phase); | ||||
|     *revPtr++ = expjLookup(-phase); | ||||
|     phase += M_PI_F / 2.0F; | ||||
|     *rotPtr++ = complex(cos(phase), sin(phase)); | ||||
|     *revPtr++ = complex(cos(-phase), sin(-phase)); | ||||
|     phase += M_PI / 2.0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -401,11 +279,18 @@ static bool GMSKReverseRotate(signalVector &x, int sps) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| signalVector *convolve(const signalVector *x, | ||||
|                         const signalVector *h, | ||||
|                         signalVector *y, | ||||
|                         ConvType spanType, size_t start, | ||||
|                         size_t len, size_t step, int offset) | ||||
| /** Convolution type indicator */ | ||||
| enum ConvType { | ||||
|   START_ONLY, | ||||
|   NO_DELAY, | ||||
|   CUSTOM, | ||||
|   UNDEFINED, | ||||
| }; | ||||
|  | ||||
| static signalVector *convolve(const signalVector *x, const signalVector *h, | ||||
|                               signalVector *y, ConvType spanType, | ||||
|                               size_t start = 0, size_t len = 0, | ||||
|                               size_t step = 1, int offset = 0) | ||||
| { | ||||
|   int rc; | ||||
|   size_t head = 0, tail = 0; | ||||
| @@ -654,29 +539,6 @@ static PulseSequence *generateGSMPulse(int sps) | ||||
|   return pulse; | ||||
| } | ||||
|  | ||||
| signalVector* reverseConjugate(signalVector *b) | ||||
| { | ||||
|     signalVector *tmp = new signalVector(b->size()); | ||||
|     tmp->isReal(b->isReal()); | ||||
|     signalVector::iterator bP = b->begin(); | ||||
|     signalVector::iterator bPEnd = b->end(); | ||||
|     signalVector::iterator tmpP = tmp->end()-1; | ||||
|     if (!b->isReal()) { | ||||
|       while (bP < bPEnd) { | ||||
|         *tmpP-- = bP->conj(); | ||||
|         bP++; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       while (bP < bPEnd) { | ||||
|         *tmpP-- = bP->real(); | ||||
|         bP++; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return tmp; | ||||
| } | ||||
|  | ||||
| bool vectorSlicer(SoftVector *x) | ||||
| { | ||||
|   SoftVector::iterator xP = x->begin(); | ||||
| @@ -696,7 +558,7 @@ static signalVector *rotateBurst(const BitVector &wBurst, | ||||
|                                  int guardPeriodLength, int sps) | ||||
| { | ||||
|   int burst_len; | ||||
|   signalVector *pulse, rotated, *shaped; | ||||
|   signalVector *pulse, rotated; | ||||
|   signalVector::iterator itr; | ||||
|  | ||||
|   pulse = GSMPulse1->empty; | ||||
| @@ -713,11 +575,7 @@ static signalVector *rotateBurst(const BitVector &wBurst, | ||||
|   rotated.isReal(false); | ||||
|  | ||||
|   /* Dummy filter operation */ | ||||
|   shaped = convolve(&rotated, pulse, NULL, START_ONLY); | ||||
|   if (!shaped) | ||||
|     return NULL; | ||||
|  | ||||
|   return shaped; | ||||
|   return convolve(&rotated, pulse, NULL, START_ONLY); | ||||
| } | ||||
|  | ||||
| static void rotateBurst2(signalVector &burst, double phase) | ||||
| @@ -737,8 +595,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits) | ||||
| { | ||||
|   int burst_len, sps = 4; | ||||
|   float phase; | ||||
|   signalVector *c0_pulse, *c1_pulse, *c0_burst; | ||||
|   signalVector *c1_burst, *c0_shaped, *c1_shaped; | ||||
|   signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped; | ||||
|   signalVector::iterator c0_itr, c1_itr; | ||||
|  | ||||
|   c0_pulse = GSMPulse4->c0; | ||||
| @@ -749,13 +606,12 @@ static signalVector *modulateBurstLaurent(const BitVector &bits) | ||||
|  | ||||
|   burst_len = 625; | ||||
|  | ||||
|   c0_burst = new signalVector(burst_len, c0_pulse->size()); | ||||
|   c0_burst->isReal(true); | ||||
|   c0_itr = c0_burst->begin(); | ||||
|   signalVector c0_burst(burst_len, c0_pulse->size()); | ||||
|   c0_burst.isReal(true); | ||||
|   c0_itr = c0_burst.begin(); | ||||
|  | ||||
|   c1_burst = new signalVector(burst_len, c1_pulse->size()); | ||||
|   c1_burst->isReal(true); | ||||
|   c1_itr = c1_burst->begin(); | ||||
|   signalVector c1_burst(burst_len, c1_pulse->size()); | ||||
|   c1_itr = c1_burst.begin(); | ||||
|  | ||||
|   /* Padded differential tail bits */ | ||||
|   *c0_itr = 2.0 * (0x00 & 0x01) - 1.0; | ||||
| @@ -771,10 +627,10 @@ static signalVector *modulateBurstLaurent(const BitVector &bits) | ||||
|   *c0_itr = 2.0 * (0x00 & 0x01) - 1.0; | ||||
|  | ||||
|   /* Generate C0 phase coefficients */ | ||||
|   GMSKRotate(*c0_burst, sps); | ||||
|   c0_burst->isReal(false); | ||||
|   GMSKRotate(c0_burst, sps); | ||||
|   c0_burst.isReal(false); | ||||
|  | ||||
|   c0_itr = c0_burst->begin(); | ||||
|   c0_itr = c0_burst.begin(); | ||||
|   c0_itr += sps * 2; | ||||
|   c1_itr += sps * 2; | ||||
|  | ||||
| @@ -799,8 +655,8 @@ static signalVector *modulateBurstLaurent(const BitVector &bits) | ||||
|   *c1_itr = *c0_itr * Complex<float>(0, phase); | ||||
|  | ||||
|   /* Primary (C0) and secondary (C1) pulse shaping */ | ||||
|   c0_shaped = convolve(c0_burst, c0_pulse, NULL, START_ONLY); | ||||
|   c1_shaped = convolve(c1_burst, c1_pulse, NULL, START_ONLY); | ||||
|   c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY); | ||||
|   c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY); | ||||
|  | ||||
|   /* Sum shaped outputs into C0 */ | ||||
|   c0_itr = c0_shaped->begin(); | ||||
| @@ -808,10 +664,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits) | ||||
|   for (unsigned i = 0; i < c0_shaped->size(); i++ ) | ||||
|     *c0_itr++ += *c1_itr++; | ||||
|  | ||||
|   delete c0_burst; | ||||
|   delete c1_burst; | ||||
|   delete c1_shaped; | ||||
|  | ||||
|   return c0_shaped; | ||||
| } | ||||
|  | ||||
| @@ -885,7 +738,6 @@ static signalVector *mapEdgeSymbols(const BitVector &bits) | ||||
| static signalVector *shapeEdgeBurst(const signalVector &symbols) | ||||
| { | ||||
|   size_t nsyms, nsamps = 625, sps = 4; | ||||
|   signalVector *burst, *shape; | ||||
|   signalVector::iterator burst_itr; | ||||
|  | ||||
|   nsyms = symbols.size(); | ||||
| @@ -893,10 +745,10 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols) | ||||
|   if (nsyms * sps > nsamps) | ||||
|     nsyms = 156; | ||||
|  | ||||
|   burst = new signalVector(nsamps, GSMPulse4->c0->size()); | ||||
|   signalVector burst(nsamps, GSMPulse4->c0->size()); | ||||
|  | ||||
|   /* Delay burst by 1 symbol */ | ||||
|   burst_itr = burst->begin() + sps; | ||||
|   burst_itr = burst.begin() + sps; | ||||
|   for (size_t i = 0; i < nsyms; i++) { | ||||
|     float phase = i * 3.0f * M_PI / 8.0f; | ||||
|     Complex<float> rot = Complex<float>(cos(phase), sin(phase)); | ||||
| @@ -906,10 +758,7 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols) | ||||
|   } | ||||
|  | ||||
|   /* Single Gaussian pulse approximation shaping */ | ||||
|   shape = convolve(burst, GSMPulse4->c0, NULL, START_ONLY); | ||||
|   delete burst; | ||||
|  | ||||
|   return shape; | ||||
|   return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -923,40 +772,36 @@ signalVector *genRandNormalBurst(int tsc, int sps, int tn) | ||||
|     return NULL; | ||||
|  | ||||
|   int i = 0; | ||||
|   BitVector *bits = new BitVector(148); | ||||
|   signalVector *burst; | ||||
|   BitVector bits(148); | ||||
|  | ||||
|   /* Tail bits */ | ||||
|   for (; i < 3; i++) | ||||
|     (*bits)[i] = 0; | ||||
|     bits[i] = 0; | ||||
|  | ||||
|   /* Random bits */ | ||||
|   for (; i < 60; i++) | ||||
|     (*bits)[i] = rand() % 2; | ||||
|     bits[i] = rand() % 2; | ||||
|  | ||||
|   /* Stealing bit */ | ||||
|   (*bits)[i++] = 0; | ||||
|   bits[i++] = 0; | ||||
|  | ||||
|   /* Training sequence */ | ||||
|   for (int n = 0; i < 87; i++, n++) | ||||
|     (*bits)[i] = gTrainingSequence[tsc][n]; | ||||
|     bits[i] = gTrainingSequence[tsc][n]; | ||||
|  | ||||
|   /* Stealing bit */ | ||||
|   (*bits)[i++] = 0; | ||||
|   bits[i++] = 0; | ||||
|  | ||||
|   /* Random bits */ | ||||
|   for (; i < 145; i++) | ||||
|     (*bits)[i] = rand() % 2; | ||||
|     bits[i] = rand() % 2; | ||||
|  | ||||
|   /* Tail bits */ | ||||
|   for (; i < 148; i++) | ||||
|     (*bits)[i] = 0; | ||||
|     bits[i] = 0; | ||||
|  | ||||
|   int guard = 8 + !(tn % 4); | ||||
|   burst = modulateBurst(*bits, guard, sps); | ||||
|   delete bits; | ||||
|  | ||||
|   return burst; | ||||
|   return modulateBurst(bits, guard, sps); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -972,30 +817,26 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn) | ||||
|     return NULL; | ||||
|  | ||||
|   int i = 0; | ||||
|   BitVector *bits = new BitVector(88+delay); | ||||
|   signalVector *burst; | ||||
|   BitVector bits(88 + delay); | ||||
|  | ||||
|   /* delay */ | ||||
|   for (; i < delay; i++) | ||||
|     (*bits)[i] = 0; | ||||
|     bits[i] = 0; | ||||
|  | ||||
|   /* head and synch bits */ | ||||
|   for (int n = 0; i < 49+delay; i++, n++) | ||||
|     (*bits)[i] = gRACHBurst[n]; | ||||
|     bits[i] = gRACHBurst[n]; | ||||
|  | ||||
|   /* Random bits */ | ||||
|   for (; i < 85+delay; i++) | ||||
|     (*bits)[i] = rand() % 2; | ||||
|     bits[i] = rand() % 2; | ||||
|  | ||||
|   /* Tail bits */ | ||||
|   for (; i < 88+delay; i++) | ||||
|     (*bits)[i] = 0; | ||||
|     bits[i] = 0; | ||||
|  | ||||
|   int guard = 68-delay + !(tn % 4); | ||||
|   burst = modulateBurst(*bits, guard, sps); | ||||
|   delete bits; | ||||
|  | ||||
|   return burst; | ||||
|   return modulateBurst(bits, guard, sps); | ||||
| } | ||||
|  | ||||
| signalVector *generateEmptyBurst(int sps, int tn) | ||||
| @@ -1032,17 +873,17 @@ signalVector *generateEdgeBurst(int tsc) | ||||
|   if ((tsc < 0) || (tsc > 7)) | ||||
|     return NULL; | ||||
|  | ||||
|   signalVector *shape, *burst = new signalVector(148); | ||||
|   signalVector burst(148); | ||||
|   const BitVector *midamble = &gEdgeTrainingSequence[tsc]; | ||||
|  | ||||
|   /* Tail */ | ||||
|   int n, i = 0; | ||||
|   for (; i < tail; i++) | ||||
|     (*burst)[i] = psk8_table[7]; | ||||
|     burst[i] = psk8_table[7]; | ||||
|  | ||||
|   /* Body */ | ||||
|   for (; i < tail + data; i++) | ||||
|     (*burst)[i] = psk8_table[rand() % 8]; | ||||
|     burst[i] = psk8_table[rand() % 8]; | ||||
|  | ||||
|   /* TSC */ | ||||
|   for (n = 0; i < tail + data + train; i++, n++) { | ||||
| @@ -1050,21 +891,18 @@ signalVector *generateEdgeBurst(int tsc) | ||||
|                      (((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) | | ||||
|                      (((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2); | ||||
|  | ||||
|     (*burst)[i] = psk8_table[index]; | ||||
|     burst[i] = psk8_table[index]; | ||||
|   } | ||||
|  | ||||
|   /* Body */ | ||||
|   for (; i < tail + data + train + data; i++) | ||||
|     (*burst)[i] = psk8_table[rand() % 8]; | ||||
|     burst[i] = psk8_table[rand() % 8]; | ||||
|  | ||||
|   /* Tail */ | ||||
|   for (; i < tail + data + train + data + tail; i++) | ||||
|     (*burst)[i] = psk8_table[7]; | ||||
|     burst[i] = psk8_table[7]; | ||||
|  | ||||
|   shape = shapeEdgeBurst(*burst); | ||||
|   delete burst; | ||||
|  | ||||
|   return shape; | ||||
|   return shapeEdgeBurst(burst); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1100,7 +938,7 @@ static signalVector *modulateBurstBasic(const BitVector &bits, | ||||
| 					int guard_len, int sps) | ||||
| { | ||||
|   int burst_len; | ||||
|   signalVector *pulse, *burst, *shaped; | ||||
|   signalVector *pulse; | ||||
|   signalVector::iterator burst_itr; | ||||
|  | ||||
|   if (sps == 1) | ||||
| @@ -1110,9 +948,9 @@ static signalVector *modulateBurstBasic(const BitVector &bits, | ||||
|  | ||||
|   burst_len = sps * (bits.size() + guard_len); | ||||
|  | ||||
|   burst = new signalVector(burst_len, pulse->size()); | ||||
|   burst->isReal(true); | ||||
|   burst_itr = burst->begin(); | ||||
|   signalVector burst(burst_len, pulse->size()); | ||||
|   burst.isReal(true); | ||||
|   burst_itr = burst.begin(); | ||||
|  | ||||
|   /* Raw bits are not differentially encoded */ | ||||
|   for (unsigned i = 0; i < bits.size(); i++) { | ||||
| @@ -1120,15 +958,11 @@ static signalVector *modulateBurstBasic(const BitVector &bits, | ||||
|     burst_itr += sps; | ||||
|   } | ||||
|  | ||||
|   GMSKRotate(*burst, sps); | ||||
|   burst->isReal(false); | ||||
|   GMSKRotate(burst, sps); | ||||
|   burst.isReal(false); | ||||
|  | ||||
|   /* Single Gaussian pulse approximation shaping */ | ||||
|   shaped = convolve(burst, pulse, NULL, START_ONLY); | ||||
|  | ||||
|   delete burst; | ||||
|  | ||||
|   return shaped; | ||||
|   return convolve(&burst, pulse, NULL, START_ONLY); | ||||
| } | ||||
|  | ||||
| /* Assume input bits are not differentially encoded */ | ||||
| @@ -1145,20 +979,14 @@ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength, | ||||
|  | ||||
| static void generateSincTable() | ||||
| { | ||||
|   float x; | ||||
|  | ||||
|   for (int i = 0; i < TABLESIZE; i++) { | ||||
|     x = (float) i / TABLESIZE * 8 * M_PI; | ||||
|     if (fabs(x) < 0.01) { | ||||
|       sincTable[i] = 1.0f; | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     sincTable[i] = sinf(x) / x; | ||||
|     auto x = (double) i / TABLESIZE * 8 * M_PI; | ||||
|     auto y = sin(x) / x; | ||||
|     sincTable[i] = std::isnan(y) ? 1.0 : y; | ||||
|   } | ||||
| } | ||||
|  | ||||
| float sinc(float x) | ||||
| static float sinc(float x) | ||||
| { | ||||
|   if (fabs(x) >= 8 * M_PI) | ||||
|     return 0.0; | ||||
| @@ -1173,7 +1001,7 @@ float sinc(float x) | ||||
|  * sinc function generator. The number of filters generated is specified | ||||
|  * by the DELAYFILTS value. | ||||
|  */ | ||||
| void generateDelayFilters() | ||||
| static void generateDelayFilters() | ||||
| { | ||||
|   int h_len = 20; | ||||
|   complex *data; | ||||
| @@ -1268,31 +1096,8 @@ signalVector *delayVector(const signalVector *in, signalVector *out, float delay | ||||
|   return out; | ||||
| } | ||||
|  | ||||
| signalVector *gaussianNoise(int length,  | ||||
| 			    float variance,  | ||||
| 			    complex mean) | ||||
| static complex interpolatePoint(const signalVector &inSig, float ix) | ||||
| { | ||||
|  | ||||
|   signalVector *noise = new signalVector(length); | ||||
|   signalVector::iterator nPtr = noise->begin(); | ||||
|   float stddev = sqrtf(variance); | ||||
|   while (nPtr < noise->end()) { | ||||
|     float u1 = (float) rand()/ (float) RAND_MAX; | ||||
|     while (u1==0.0) | ||||
|       u1 = (float) rand()/ (float) RAND_MAX; | ||||
|     float u2 = (float) rand()/ (float) RAND_MAX; | ||||
|     float arg = 2.0*M_PI*u2; | ||||
|     *nPtr = mean + stddev*complex(cos(arg),sin(arg))*sqrtf(-2.0*log(u1)); | ||||
|     nPtr++; | ||||
|   } | ||||
|  | ||||
|   return noise; | ||||
| } | ||||
|  | ||||
| complex interpolatePoint(const signalVector &inSig, | ||||
| 			 float ix) | ||||
| { | ||||
|    | ||||
|   int start = (int) (floor(ix) - 10); | ||||
|   if (start < 0) start = 0; | ||||
|   int end = (int) (floor(ix) + 11); | ||||
| @@ -1332,12 +1137,9 @@ static complex fastPeakDetect(const signalVector &rxBurst, float *index) | ||||
|   return amp; | ||||
| } | ||||
|  | ||||
| complex peakDetect(const signalVector &rxBurst, | ||||
| 		   float *peakIndex, | ||||
| 		   float *avgPwr)  | ||||
| static complex peakDetect(const signalVector &rxBurst, | ||||
|                           float *peakIndex, float *avgPwr) | ||||
| { | ||||
|    | ||||
|  | ||||
|   complex maxVal = 0.0; | ||||
|   float maxIndex = -1; | ||||
|   float sumPower = 0.0; | ||||
| @@ -1410,7 +1212,7 @@ void scaleVector(signalVector &x, | ||||
| } | ||||
|  | ||||
| /** in-place conjugation */ | ||||
| void conjugateVector(signalVector &x) | ||||
| static void conjugateVector(signalVector &x) | ||||
| { | ||||
|   if (x.isReal()) return; | ||||
|   signalVector::iterator xP = x.begin(); | ||||
| @@ -1421,37 +1223,6 @@ void conjugateVector(signalVector &x) | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| // in-place addition!! | ||||
| bool addVector(signalVector &x, | ||||
| 	       signalVector &y) | ||||
| { | ||||
|   signalVector::iterator xP = x.begin(); | ||||
|   signalVector::iterator yP = y.begin(); | ||||
|   signalVector::iterator xPEnd = x.end(); | ||||
|   signalVector::iterator yPEnd = y.end(); | ||||
|   while ((xP < xPEnd) && (yP < yPEnd)) { | ||||
|     *xP = *xP + *yP; | ||||
|     xP++; yP++; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // in-place multiplication!! | ||||
| bool multVector(signalVector &x, | ||||
|                  signalVector &y) | ||||
| { | ||||
|   signalVector::iterator xP = x.begin(); | ||||
|   signalVector::iterator yP = y.begin(); | ||||
|   signalVector::iterator xPEnd = x.end(); | ||||
|   signalVector::iterator yPEnd = y.end(); | ||||
|   while ((xP < xPEnd) && (yP < yPEnd)) { | ||||
|     *xP = (*xP) * (*yP); | ||||
|     xP++; yP++; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static bool generateMidamble(int sps, int tsc) | ||||
| { | ||||
|   bool status = true; | ||||
| @@ -1528,7 +1299,7 @@ release: | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| CorrelationSequence *generateEdgeMidamble(int tsc) | ||||
| static CorrelationSequence *generateEdgeMidamble(int tsc) | ||||
| { | ||||
|   complex *data = NULL; | ||||
|   signalVector *midamble = NULL, *_midamble = NULL; | ||||
| @@ -1682,6 +1453,21 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength) | ||||
|   return energy/windowLength; | ||||
| } | ||||
|  | ||||
| static signalVector *downsampleBurst(const signalVector &burst) | ||||
| { | ||||
|   signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len()); | ||||
|   signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN); | ||||
|   memcpy(in.begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float)); | ||||
|  | ||||
|   if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN, | ||||
|                         (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) { | ||||
|     delete out; | ||||
|     out = NULL; | ||||
|   } | ||||
|  | ||||
|   return out; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Detect a burst based on correlation and peak-to-average ratio | ||||
|  * | ||||
| @@ -1771,7 +1557,6 @@ static int detectGeneralBurst(const signalVector &rxBurst, | ||||
| { | ||||
|   int rc, start, len; | ||||
|   bool clipping = false; | ||||
|   signalVector *corr; | ||||
|  | ||||
|   if ((sps != 1) && (sps != 4)) | ||||
|     return -SIGERR_UNSUPPORTED; | ||||
| @@ -1787,12 +1572,10 @@ static int detectGeneralBurst(const signalVector &rxBurst, | ||||
|  | ||||
|   start = target - head - 1; | ||||
|   len = head + tail; | ||||
|   corr = new signalVector(len); | ||||
|   signalVector corr(len); | ||||
|  | ||||
|   rc = detectBurst(rxBurst, *corr, sync, | ||||
|   rc = detectBurst(rxBurst, corr, sync, | ||||
|                    thresh, sps, &, &toa, start, len); | ||||
|   delete corr; | ||||
|  | ||||
|   if (rc < 0) { | ||||
|     return -SIGERR_INTERNAL; | ||||
|   } else if (!rc) { | ||||
| @@ -1816,12 +1599,8 @@ static int detectGeneralBurst(const signalVector &rxBurst, | ||||
|  *   head: Search 8 symbols before target | ||||
|  *   tail: Search 8 symbols + maximum expected delay | ||||
|  */ | ||||
| int detectRACHBurst(const signalVector &burst, | ||||
|             float threshold, | ||||
|             int sps, | ||||
|             complex &litude, | ||||
|             float &toa, | ||||
|             unsigned max_toa) | ||||
| static int detectRACHBurst(const signalVector &burst, float threshold, int sps, | ||||
|                            complex &litude, float &toa, unsigned max_toa) | ||||
| { | ||||
|   int rc, target, head, tail; | ||||
|   CorrelationSequence *sync; | ||||
| @@ -1845,8 +1624,8 @@ int detectRACHBurst(const signalVector &burst, | ||||
|  *   head: Search 6 symbols before target | ||||
|  *   tail: Search 6 symbols + maximum expected delay | ||||
|  */ | ||||
| int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|                         int sps, complex &litude, float &toa, unsigned max_toa) | ||||
| static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|                                int sps, complex &litude, float &toa, unsigned max_toa) | ||||
| { | ||||
|   int rc, target, head, tail; | ||||
|   CorrelationSequence *sync; | ||||
| @@ -1864,8 +1643,8 @@ int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|                     int sps, complex &litude, float &toa, unsigned max_toa) | ||||
| static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|                            int sps, complex &litude, float &toa, unsigned max_toa) | ||||
| { | ||||
|   int rc, target, head, tail; | ||||
|   CorrelationSequence *sync; | ||||
| @@ -1915,41 +1694,6 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| signalVector *downsampleBurst(const signalVector &burst) | ||||
| { | ||||
|   signalVector *in, *out; | ||||
|  | ||||
|   in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len()); | ||||
|   out = new signalVector(DOWNSAMPLE_OUT_LEN); | ||||
|   memcpy(in->begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float)); | ||||
|  | ||||
|   if (dnsampler->rotate((float *) in->begin(), DOWNSAMPLE_IN_LEN, | ||||
|                         (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) { | ||||
|     delete out; | ||||
|     out = NULL; | ||||
|   } | ||||
|  | ||||
|   delete in; | ||||
|   return out; | ||||
| }; | ||||
|  | ||||
| signalVector *decimateVector(signalVector &wVector, size_t factor) | ||||
| { | ||||
|   signalVector *dec; | ||||
|  | ||||
|   if (factor <= 1) | ||||
|     return NULL; | ||||
|  | ||||
|   dec = new signalVector(wVector.size() / factor); | ||||
|   dec->isReal(wVector.isReal()); | ||||
|  | ||||
|   signalVector::iterator itr = dec->begin(); | ||||
|   for (size_t i = 0; i < wVector.size(); i += factor) | ||||
|     *itr++ = wVector[i]; | ||||
|  | ||||
|   return dec; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Soft 8-PSK decoding using Manhattan distance metric | ||||
|  */ | ||||
| @@ -2046,8 +1790,8 @@ static signalVector *demodCommon(const signalVector &burst, int sps, | ||||
|  * 4 SPS (if activated) to minimize distortion through the fractional | ||||
|  * delay filters. Symbol rotation and after always operates at 1 SPS. | ||||
|  */ | ||||
| SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps, | ||||
|                            complex channel, float TOA) | ||||
| static SoftVector *demodGmskBurst(const signalVector &rxBurst, | ||||
|                                   int sps, complex channel, float TOA) | ||||
| { | ||||
|   SoftVector *bits; | ||||
|   signalVector *dec; | ||||
| @@ -2075,8 +1819,8 @@ SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps, | ||||
|  * through the fractional delay filters at 1 SPS renders signal | ||||
|  * nearly unrecoverable. | ||||
|  */ | ||||
| SoftVector *demodEdgeBurst(const signalVector &burst, int sps, | ||||
|                            complex chan, float toa) | ||||
| static SoftVector *demodEdgeBurst(const signalVector &burst, | ||||
|                                   int sps, complex chan, float toa) | ||||
| { | ||||
|   SoftVector *bits; | ||||
|   signalVector *dec, *rot, *eq; | ||||
| @@ -2110,7 +1854,6 @@ SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp, | ||||
|  | ||||
| bool sigProcLibSetup() | ||||
| { | ||||
|   initTrigTables(); | ||||
|   generateSincTable(); | ||||
|   initGMSKRotationTables(); | ||||
|  | ||||
| @@ -2137,26 +1880,3 @@ fail: | ||||
|   sigProcLibDestroy(); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| std::string corrTypeToString(CorrType corr) { | ||||
|   switch (corr) { | ||||
|   case OFF: | ||||
|     return "OFF"; | ||||
|   case TSC: | ||||
|     return "TSC"; | ||||
|   case RACH: | ||||
|     return "RACH"; | ||||
|   case EDGE: | ||||
|     return "EDGE"; | ||||
|   case IDLE: | ||||
|     return "IDLE"; | ||||
|   default: | ||||
|     return "unknown"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, CorrType corr) | ||||
| { | ||||
|   os << corrTypeToString(corr); | ||||
|   return os; | ||||
| } | ||||
|   | ||||
| @@ -25,14 +25,6 @@ | ||||
| #define EDGE_BURST_NBITS      444 | ||||
| #define EDGE_BURST_NSYMS      (EDGE_BURST_NBITS / 3) | ||||
|  | ||||
| /** Convolution type indicator */ | ||||
| enum ConvType { | ||||
|   START_ONLY, | ||||
|   NO_DELAY, | ||||
|   CUSTOM, | ||||
|   UNDEFINED, | ||||
| }; | ||||
|  | ||||
| /** Codes for burst types of received bursts*/ | ||||
| enum CorrType{ | ||||
|   OFF,         ///< timeslot is off | ||||
| @@ -41,8 +33,6 @@ enum CorrType{ | ||||
|   EDGE,        ///< timeslot should contain an EDGE burst | ||||
|   IDLE         ///< timeslot is an idle (or dummy) burst | ||||
| }; | ||||
| std::string corrTypeToString(CorrType corr); | ||||
| std::ostream& operator<<(std::ostream& os, CorrType corr); | ||||
|  | ||||
| enum SignalError { | ||||
|   SIGERR_NONE, | ||||
| @@ -61,66 +51,12 @@ enum SignalError { | ||||
|  */ | ||||
| #define BURST_THRESH    4.0 | ||||
|  | ||||
| /** Convert a linear number to a dB value */ | ||||
| float dB(float x); | ||||
|  | ||||
| /** Convert a dB value into a linear value */ | ||||
| float dBinv(float x); | ||||
|  | ||||
| /** Compute the energy of a vector */ | ||||
| float vectorNorm2(const signalVector &x); | ||||
|  | ||||
| /** Compute the average power of a vector */ | ||||
| float vectorPower(const signalVector &x); | ||||
|  | ||||
| /** Setup the signal processing library */ | ||||
| bool sigProcLibSetup(); | ||||
|  | ||||
| /** Destroy the signal processing library */ | ||||
| void sigProcLibDestroy(void); | ||||
|  | ||||
| /** | ||||
|         Convolve two vectors. | ||||
|         @param a,b The vectors to be convolved. | ||||
|         @param c, A preallocated vector to hold the convolution result. | ||||
|         @param spanType The type/span of the convolution. | ||||
|         @return The convolution result or NULL on error. | ||||
| */ | ||||
| signalVector *convolve(const signalVector *a, const signalVector *b, | ||||
|                        signalVector *c, ConvType spanType, | ||||
|                        size_t start = 0, size_t len = 0, | ||||
|                        size_t step = 1, int offset = 0); | ||||
|  | ||||
| /** | ||||
|         Frequency shift a vector. | ||||
|         @param y The frequency shifted vector. | ||||
|         @param x The vector to-be-shifted. | ||||
|         @param freq The digital frequency shift | ||||
|         @param startPhase The starting phase of the oscillator | ||||
|         @param finalPhase The final phase of the oscillator | ||||
|         @return The frequency shifted vector. | ||||
| */ | ||||
| signalVector* frequencyShift(signalVector *y, | ||||
|                              signalVector *x, | ||||
|                              float freq = 0.0, | ||||
|                              float startPhase = 0.0, | ||||
|                              float *finalPhase=NULL); | ||||
|  | ||||
| /** | ||||
|         Correlate two vectors. | ||||
|         @param a,b The vectors to be correlated. | ||||
|         @param c, A preallocated vector to hold the correlation result. | ||||
|         @param spanType The type/span of the correlation. | ||||
|         @return The correlation result. | ||||
| */ | ||||
| signalVector* correlate(signalVector *a, | ||||
|                         signalVector *b, | ||||
|                         signalVector *c, | ||||
|                         ConvType spanType, | ||||
|                         bool bReversedConjugated = false, | ||||
|                         unsigned startIx = 0, | ||||
|                         unsigned len = 0); | ||||
|  | ||||
| /** Operate soft slicer on a soft-bit vector */ | ||||
| bool vectorSlicer(SoftVector *x); | ||||
|  | ||||
| @@ -148,45 +84,6 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn); | ||||
| /** Generate a dummy GSM burst - 4 or 1 SPS */ | ||||
| signalVector *generateDummyBurst(int sps, int tn); | ||||
|  | ||||
| /** Sinc function */ | ||||
| float sinc(float x); | ||||
|  | ||||
| /** Delay a vector */ | ||||
| signalVector *delayVector(const signalVector *in, signalVector *out, float delay); | ||||
|  | ||||
| /** Add two vectors in-place */ | ||||
| bool addVector(signalVector &x, | ||||
|                signalVector &y); | ||||
|  | ||||
| /** Multiply two vectors in-place*/ | ||||
| bool multVector(signalVector &x, | ||||
|                 signalVector &y); | ||||
|  | ||||
| /** Generate a vector of gaussian noise */ | ||||
| signalVector *gaussianNoise(int length, | ||||
|                             float variance = 1.0, | ||||
|                             complex mean = complex(0.0)); | ||||
|  | ||||
| /** | ||||
|         Given a non-integer index, interpolate a sample. | ||||
|         @param inSig The signal from which to interpolate. | ||||
|         @param ix The index. | ||||
|         @return The interpolated signal value. | ||||
| */ | ||||
| complex interpolatePoint(const signalVector &inSig, | ||||
|                          float ix); | ||||
|  | ||||
| /** | ||||
|         Given a correlator output, locate the correlation peak. | ||||
|         @param rxBurst The correlator result. | ||||
|         @param peakIndex Pointer to value to receive interpolated peak index. | ||||
|         @param avgPower Power to value to receive mean power. | ||||
|         @return Peak value. | ||||
| */ | ||||
| complex peakDetect(const signalVector &rxBurst, | ||||
|                    float *peakIndex, | ||||
|                    float *avgPwr); | ||||
|  | ||||
| /** | ||||
|         Apply a scalar to a vector. | ||||
|         @param x The vector of interest. | ||||
| @@ -203,68 +100,6 @@ void scaleVector(signalVector &x, | ||||
| */ | ||||
| float energyDetect(const signalVector &rxBurst, | ||||
|                    unsigned windowLength); | ||||
|  | ||||
| /** | ||||
|         RACH aka Access Burst correlator/detector. | ||||
|         @param burst The received GSM burst of interest. | ||||
|         @param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity. | ||||
|         @param sps The number of samples per GSM symbol. | ||||
|         @param amplitude The estimated amplitude of received RACH burst. | ||||
|         @param toa The estimate time-of-arrival of received RACH burst. | ||||
|         @param max_toa The maximum expected time-of-arrival | ||||
|         @return 1 if threshold value is reached, | ||||
|                 negative value (-SignalError) on error, | ||||
|                 zero (SIGERR_NONE) if no burst is detected | ||||
| */ | ||||
| int detectRACHBurst(const signalVector &burst, | ||||
|                     float threshold, | ||||
|                     int sps, | ||||
|                     complex &litude, | ||||
|                     float &toa, | ||||
|                     unsigned max_toa); | ||||
|  | ||||
| /** | ||||
|         GMSK Normal Burst correlator/detector. | ||||
|         @param rxBurst The received GSM burst of interest. | ||||
|         @param tsc Midamble type (0..7) also known as TSC | ||||
|         @param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity. | ||||
|         @param sps The number of samples per GSM symbol. | ||||
|         @param amplitude The estimated amplitude of received TSC burst. | ||||
|         @param toa The estimate time-of-arrival of received TSC burst. | ||||
|         @param max_toa The maximum expected time-of-arrival | ||||
|         @return 1 if threshold value is reached, | ||||
|                 negative value (-SignalError) on error, | ||||
|                 zero (SIGERR_NONE) if no burst is detected | ||||
| */ | ||||
| int analyzeTrafficBurst(const signalVector &burst, | ||||
|                         unsigned tsc, | ||||
|                         float threshold, | ||||
|                         int sps, | ||||
|                         complex &litude, | ||||
|                         float &toa, | ||||
|                         unsigned max_toa); | ||||
|  | ||||
| /** | ||||
|         EDGE/8-PSK Normal Burst correlator/detector | ||||
|         @param burst The received GSM burst of interest | ||||
|         @param tsc Midamble type (0..7) also known as TSC | ||||
|         @param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity. | ||||
|         @param sps The number of samples per GSM symbol. | ||||
|         @param amplitude The estimated amplitude of received TSC burst. | ||||
|         @param toa The estimate time-of-arrival of received TSC burst. | ||||
|         @param max_toa The maximum expected time-of-arrival | ||||
|         @return 1 if threshold value is reached, | ||||
|                 negative value (-SignalError) on error, | ||||
|                 zero (SIGERR_NONE) if no burst is detected | ||||
| */ | ||||
| int detectEdgeBurst(const signalVector &burst, | ||||
|                     unsigned tsc, | ||||
|                     float threshold, | ||||
|                     int sps, | ||||
|                     complex &litude, | ||||
|                     float &toa, | ||||
|                     unsigned max_toa); | ||||
|  | ||||
| /** | ||||
|         8-PSK/GMSK/RACH burst detector | ||||
|         @param burst The received GSM burst of interest | ||||
| @@ -287,44 +122,6 @@ int detectAnyBurst(const signalVector &burst, | ||||
|                    float &toa, | ||||
|                    unsigned max_toa); | ||||
|  | ||||
| /** | ||||
|         Downsample 4 SPS to 1 SPS using a polyphase filterbank | ||||
|         @param burst Input burst of at least 624 symbols | ||||
|         @return Decimated signal vector of 156 symbols | ||||
| */ | ||||
| signalVector *downsampleBurst(const signalVector &burst); | ||||
|  | ||||
| /** | ||||
|         Decimate a vector. | ||||
|         @param wVector The vector of interest. | ||||
|         @param factor Decimation factor. | ||||
|         @return The decimated signal vector. | ||||
| */ | ||||
| signalVector *decimateVector(signalVector &wVector, size_t factor); | ||||
|  | ||||
| /** | ||||
|         Demodulates a GMSK burst using a soft-slicer. | ||||
|         @param rxBurst The burst to be demodulated. | ||||
|         @param gsmPulse The GSM pulse. | ||||
|         @param sps The number of samples per GSM symbol. | ||||
|         @param channel The amplitude estimate of the received burst. | ||||
|         @param TOA The time-of-arrival of the received burst. | ||||
|         @return The demodulated bit sequence. | ||||
| */ | ||||
| SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps, | ||||
|                            complex channel, float TOA); | ||||
|  | ||||
| /** | ||||
|         Demodulate 8-PSK EDGE burst with soft symbol ooutput | ||||
|         @param rxBurst The burst to be demodulated. | ||||
|         @param sps The number of samples per GSM symbol. | ||||
|         @param channel The amplitude estimate of the received burst. | ||||
|         @param TOA The time-of-arrival of the received burst. | ||||
|         @return The demodulated bit sequence. | ||||
| */ | ||||
| SoftVector *demodEdgeBurst(const signalVector &rxBurst, int sps, | ||||
|                            complex channel, float TOA); | ||||
|  | ||||
| /** Demodulate burst basde on type and output soft bits */ | ||||
| SoftVector *demodAnyBurst(const signalVector &burst, int sps, | ||||
|                           complex amp, float toa, CorrType type); | ||||
|   | ||||
| @@ -46,6 +46,7 @@ void convert_init(void) | ||||
| 	c.convert_si16_ps_16n = base_convert_short_float; | ||||
| 	c.convert_si16_ps = base_convert_short_float; | ||||
|  | ||||
| #ifdef HAVE___BUILTIN_CPU_SUPPORTS | ||||
| #ifdef HAVE_SSE4_1 | ||||
| 	if (__builtin_cpu_supports("sse4.1")) { | ||||
| 		c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n; | ||||
| @@ -60,6 +61,7 @@ void convert_init(void) | ||||
| 		c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16; | ||||
| 	} | ||||
| #endif | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void convert_float_short(short *out, const float *in, float scale, int len) | ||||
|   | ||||
| @@ -82,7 +82,7 @@ void convolve_init(void) | ||||
| 	c.conv_real4n = (void *)_base_convolve_real; | ||||
| 	c.conv_real = (void *)_base_convolve_real; | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| #if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS) | ||||
| 	if (__builtin_cpu_supports("sse3")) { | ||||
| 		c.conv_cmplx_4n = sse_conv_cmplx_4n; | ||||
| 		c.conv_cmplx_8n = sse_conv_cmplx_8n; | ||||
|   | ||||
							
								
								
									
										982
									
								
								config/ax_cxx_compile_stdcxx.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										982
									
								
								config/ax_cxx_compile_stdcxx.m4
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,982 @@ | ||||
| # =========================================================================== | ||||
| #  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   Check for baseline language coverage in the compiler for the specified | ||||
| #   version of the C++ standard.  If necessary, add switches to CXX and | ||||
| #   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard) | ||||
| #   or '14' (for the C++14 standard). | ||||
| # | ||||
| #   The second argument, if specified, indicates whether you insist on an | ||||
| #   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. | ||||
| #   -std=c++11).  If neither is specified, you get whatever works, with | ||||
| #   preference for an extended mode. | ||||
| # | ||||
| #   The third argument, if specified 'mandatory' or if left unspecified, | ||||
| #   indicates that baseline support for the specified C++ standard is | ||||
| #   required and that the macro should error out if no mode with that | ||||
| #   support is found.  If specified 'optional', then configuration proceeds | ||||
| #   regardless, after defining HAVE_CXX${VERSION} if and only if a | ||||
| #   supporting mode is found. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com> | ||||
| #   Copyright (c) 2012 Zack Weinberg <zackw@panix.com> | ||||
| #   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu> | ||||
| #   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com> | ||||
| #   Copyright (c) 2015 Paul Norman <penorman@mac.com> | ||||
| #   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu> | ||||
| #   Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com> | ||||
| # | ||||
| #   Copying and distribution of this file, with or without modification, are | ||||
| #   permitted in any medium without royalty provided the copyright notice | ||||
| #   and this notice are preserved.  This file is offered as-is, without any | ||||
| #   warranty. | ||||
|  | ||||
| #serial 7 | ||||
|  | ||||
| dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro | ||||
| dnl  (serial version number 13). | ||||
|  | ||||
| AX_REQUIRE_DEFINED([AC_MSG_WARN]) | ||||
| AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl | ||||
|   m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], | ||||
|         [$1], [14], [ax_cxx_compile_alternatives="14 1y"], | ||||
|         [$1], [17], [ax_cxx_compile_alternatives="17 1z"], | ||||
|         [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl | ||||
|   m4_if([$2], [], [], | ||||
|         [$2], [ext], [], | ||||
|         [$2], [noext], [], | ||||
|         [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl | ||||
|   m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], | ||||
|         [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], | ||||
|         [$3], [optional], [ax_cxx_compile_cxx$1_required=false], | ||||
|         [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) | ||||
|   AC_LANG_PUSH([C++])dnl | ||||
|   ac_success=no | ||||
|   AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, | ||||
|   ax_cv_cxx_compile_cxx$1, | ||||
|   [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], | ||||
|     [ax_cv_cxx_compile_cxx$1=yes], | ||||
|     [ax_cv_cxx_compile_cxx$1=no])]) | ||||
|   if test x$ax_cv_cxx_compile_cxx$1 = xyes; then | ||||
|     ac_success=yes | ||||
|   fi | ||||
|  | ||||
|   m4_if([$2], [noext], [], [dnl | ||||
|   if test x$ac_success = xno; then | ||||
|     for alternative in ${ax_cxx_compile_alternatives}; do | ||||
|       switch="-std=gnu++${alternative}" | ||||
|       cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) | ||||
|       AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, | ||||
|                      $cachevar, | ||||
|         [ac_save_CXX="$CXX" | ||||
|          CXX="$CXX $switch" | ||||
|          AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], | ||||
|           [eval $cachevar=yes], | ||||
|           [eval $cachevar=no]) | ||||
|          CXX="$ac_save_CXX"]) | ||||
|       if eval test x\$$cachevar = xyes; then | ||||
|         CXX="$CXX $switch" | ||||
|         if test -n "$CXXCPP" ; then | ||||
|           CXXCPP="$CXXCPP $switch" | ||||
|         fi | ||||
|         ac_success=yes | ||||
|         break | ||||
|       fi | ||||
|     done | ||||
|   fi]) | ||||
|  | ||||
|   m4_if([$2], [ext], [], [dnl | ||||
|   if test x$ac_success = xno; then | ||||
|     dnl HP's aCC needs +std=c++11 according to: | ||||
|     dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf | ||||
|     dnl Cray's crayCC needs "-h std=c++11" | ||||
|     for alternative in ${ax_cxx_compile_alternatives}; do | ||||
|       for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do | ||||
|         cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) | ||||
|         AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, | ||||
|                        $cachevar, | ||||
|           [ac_save_CXX="$CXX" | ||||
|            CXX="$CXX $switch" | ||||
|            AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], | ||||
|             [eval $cachevar=yes], | ||||
|             [eval $cachevar=no]) | ||||
|            CXX="$ac_save_CXX"]) | ||||
|         if eval test x\$$cachevar = xyes; then | ||||
|           CXX="$CXX $switch" | ||||
|           if test -n "$CXXCPP" ; then | ||||
|             CXXCPP="$CXXCPP $switch" | ||||
|           fi | ||||
|           ac_success=yes | ||||
|           break | ||||
|         fi | ||||
|       done | ||||
|       if test x$ac_success = xyes; then | ||||
|         break | ||||
|       fi | ||||
|     done | ||||
|   fi]) | ||||
|   AC_LANG_POP([C++]) | ||||
|   if test x$ax_cxx_compile_cxx$1_required = xtrue; then | ||||
|     if test x$ac_success = xno; then | ||||
|       AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) | ||||
|     fi | ||||
|   fi | ||||
|   if test x$ac_success = xno; then | ||||
|     HAVE_CXX$1=0 | ||||
|     AC_MSG_NOTICE([No compiler with C++$1 support was found]) | ||||
|   else | ||||
|     HAVE_CXX$1=1 | ||||
|     AC_DEFINE(HAVE_CXX$1,1, | ||||
|               [define if the compiler supports basic C++$1 syntax]) | ||||
|   fi | ||||
|   AC_SUBST(HAVE_CXX$1) | ||||
|   m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) | ||||
| ]) | ||||
|  | ||||
|  | ||||
| dnl  Test body for checking C++11 support | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 | ||||
| ) | ||||
|  | ||||
|  | ||||
| dnl  Test body for checking C++14 support | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 | ||||
| ) | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 | ||||
|   _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 | ||||
| ) | ||||
|  | ||||
| dnl  Tests for new features in C++11 | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ | ||||
|  | ||||
| // If the compiler admits that it is not ready for C++11, why torture it? | ||||
| // Hopefully, this will speed up the test. | ||||
|  | ||||
| #ifndef __cplusplus | ||||
|  | ||||
| #error "This is not a C++ compiler" | ||||
|  | ||||
| #elif __cplusplus < 201103L | ||||
|  | ||||
| #error "This is not a C++11 compiler" | ||||
|  | ||||
| #else | ||||
|  | ||||
| namespace cxx11 | ||||
| { | ||||
|  | ||||
|   namespace test_static_assert | ||||
|   { | ||||
|  | ||||
|     template <typename T> | ||||
|     struct check | ||||
|     { | ||||
|       static_assert(sizeof(int) <= sizeof(T), "not big enough"); | ||||
|     }; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_final_override | ||||
|   { | ||||
|  | ||||
|     struct Base | ||||
|     { | ||||
|       virtual void f() {} | ||||
|     }; | ||||
|  | ||||
|     struct Derived : public Base | ||||
|     { | ||||
|       virtual void f() override {} | ||||
|     }; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_double_right_angle_brackets | ||||
|   { | ||||
|  | ||||
|     template < typename T > | ||||
|     struct check {}; | ||||
|  | ||||
|     typedef check<void> single_type; | ||||
|     typedef check<check<void>> double_type; | ||||
|     typedef check<check<check<void>>> triple_type; | ||||
|     typedef check<check<check<check<void>>>> quadruple_type; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_decltype | ||||
|   { | ||||
|  | ||||
|     int | ||||
|     f() | ||||
|     { | ||||
|       int a = 1; | ||||
|       decltype(a) b = 2; | ||||
|       return a + b; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_type_deduction | ||||
|   { | ||||
|  | ||||
|     template < typename T1, typename T2 > | ||||
|     struct is_same | ||||
|     { | ||||
|       static const bool value = false; | ||||
|     }; | ||||
|  | ||||
|     template < typename T > | ||||
|     struct is_same<T, T> | ||||
|     { | ||||
|       static const bool value = true; | ||||
|     }; | ||||
|  | ||||
|     template < typename T1, typename T2 > | ||||
|     auto | ||||
|     add(T1 a1, T2 a2) -> decltype(a1 + a2) | ||||
|     { | ||||
|       return a1 + a2; | ||||
|     } | ||||
|  | ||||
|     int | ||||
|     test(const int c, volatile int v) | ||||
|     { | ||||
|       static_assert(is_same<int, decltype(0)>::value == true, ""); | ||||
|       static_assert(is_same<int, decltype(c)>::value == false, ""); | ||||
|       static_assert(is_same<int, decltype(v)>::value == false, ""); | ||||
|       auto ac = c; | ||||
|       auto av = v; | ||||
|       auto sumi = ac + av + 'x'; | ||||
|       auto sumf = ac + av + 1.0; | ||||
|       static_assert(is_same<int, decltype(ac)>::value == true, ""); | ||||
|       static_assert(is_same<int, decltype(av)>::value == true, ""); | ||||
|       static_assert(is_same<int, decltype(sumi)>::value == true, ""); | ||||
|       static_assert(is_same<int, decltype(sumf)>::value == false, ""); | ||||
|       static_assert(is_same<int, decltype(add(c, v))>::value == true, ""); | ||||
|       return (sumf > 0.0) ? sumi : add(c, v); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_noexcept | ||||
|   { | ||||
|  | ||||
|     int f() { return 0; } | ||||
|     int g() noexcept { return 0; } | ||||
|  | ||||
|     static_assert(noexcept(f()) == false, ""); | ||||
|     static_assert(noexcept(g()) == true, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_constexpr | ||||
|   { | ||||
|  | ||||
|     template < typename CharT > | ||||
|     unsigned long constexpr | ||||
|     strlen_c_r(const CharT *const s, const unsigned long acc) noexcept | ||||
|     { | ||||
|       return *s ? strlen_c_r(s + 1, acc + 1) : acc; | ||||
|     } | ||||
|  | ||||
|     template < typename CharT > | ||||
|     unsigned long constexpr | ||||
|     strlen_c(const CharT *const s) noexcept | ||||
|     { | ||||
|       return strlen_c_r(s, 0UL); | ||||
|     } | ||||
|  | ||||
|     static_assert(strlen_c("") == 0UL, ""); | ||||
|     static_assert(strlen_c("1") == 1UL, ""); | ||||
|     static_assert(strlen_c("example") == 7UL, ""); | ||||
|     static_assert(strlen_c("another\0example") == 7UL, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_rvalue_references | ||||
|   { | ||||
|  | ||||
|     template < int N > | ||||
|     struct answer | ||||
|     { | ||||
|       static constexpr int value = N; | ||||
|     }; | ||||
|  | ||||
|     answer<1> f(int&)       { return answer<1>(); } | ||||
|     answer<2> f(const int&) { return answer<2>(); } | ||||
|     answer<3> f(int&&)      { return answer<3>(); } | ||||
|  | ||||
|     void | ||||
|     test() | ||||
|     { | ||||
|       int i = 0; | ||||
|       const int c = 0; | ||||
|       static_assert(decltype(f(i))::value == 1, ""); | ||||
|       static_assert(decltype(f(c))::value == 2, ""); | ||||
|       static_assert(decltype(f(0))::value == 3, ""); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_uniform_initialization | ||||
|   { | ||||
|  | ||||
|     struct test | ||||
|     { | ||||
|       static const int zero {}; | ||||
|       static const int one {1}; | ||||
|     }; | ||||
|  | ||||
|     static_assert(test::zero == 0, ""); | ||||
|     static_assert(test::one == 1, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_lambdas | ||||
|   { | ||||
|  | ||||
|     void | ||||
|     test1() | ||||
|     { | ||||
|       auto lambda1 = [](){}; | ||||
|       auto lambda2 = lambda1; | ||||
|       lambda1(); | ||||
|       lambda2(); | ||||
|     } | ||||
|  | ||||
|     int | ||||
|     test2() | ||||
|     { | ||||
|       auto a = [](int i, int j){ return i + j; }(1, 2); | ||||
|       auto b = []() -> int { return '0'; }(); | ||||
|       auto c = [=](){ return a + b; }(); | ||||
|       auto d = [&](){ return c; }(); | ||||
|       auto e = [a, &b](int x) mutable { | ||||
|         const auto identity = [](int y){ return y; }; | ||||
|         for (auto i = 0; i < a; ++i) | ||||
|           a += b--; | ||||
|         return x + identity(a + b); | ||||
|       }(0); | ||||
|       return a + b + c + d + e; | ||||
|     } | ||||
|  | ||||
|     int | ||||
|     test3() | ||||
|     { | ||||
|       const auto nullary = [](){ return 0; }; | ||||
|       const auto unary = [](int x){ return x; }; | ||||
|       using nullary_t = decltype(nullary); | ||||
|       using unary_t = decltype(unary); | ||||
|       const auto higher1st = [](nullary_t f){ return f(); }; | ||||
|       const auto higher2nd = [unary](nullary_t f1){ | ||||
|         return [unary, f1](unary_t f2){ return f2(unary(f1())); }; | ||||
|       }; | ||||
|       return higher1st(nullary) + higher2nd(nullary)(unary); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_variadic_templates | ||||
|   { | ||||
|  | ||||
|     template <int...> | ||||
|     struct sum; | ||||
|  | ||||
|     template <int N0, int... N1toN> | ||||
|     struct sum<N0, N1toN...> | ||||
|     { | ||||
|       static constexpr auto value = N0 + sum<N1toN...>::value; | ||||
|     }; | ||||
|  | ||||
|     template <> | ||||
|     struct sum<> | ||||
|     { | ||||
|       static constexpr auto value = 0; | ||||
|     }; | ||||
|  | ||||
|     static_assert(sum<>::value == 0, ""); | ||||
|     static_assert(sum<1>::value == 1, ""); | ||||
|     static_assert(sum<23>::value == 23, ""); | ||||
|     static_assert(sum<1, 2>::value == 3, ""); | ||||
|     static_assert(sum<5, 5, 11>::value == 21, ""); | ||||
|     static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae | ||||
|   // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function | ||||
|   // because of this. | ||||
|   namespace test_template_alias_sfinae | ||||
|   { | ||||
|  | ||||
|     struct foo {}; | ||||
|  | ||||
|     template<typename T> | ||||
|     using member = typename T::member_type; | ||||
|  | ||||
|     template<typename T> | ||||
|     void func(...) {} | ||||
|  | ||||
|     template<typename T> | ||||
|     void func(member<T>*) {} | ||||
|  | ||||
|     void test(); | ||||
|  | ||||
|     void test() { func<foo>(0); } | ||||
|  | ||||
|   } | ||||
|  | ||||
| }  // namespace cxx11 | ||||
|  | ||||
| #endif  // __cplusplus >= 201103L | ||||
|  | ||||
| ]]) | ||||
|  | ||||
|  | ||||
| dnl  Tests for new features in C++14 | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ | ||||
|  | ||||
| // If the compiler admits that it is not ready for C++14, why torture it? | ||||
| // Hopefully, this will speed up the test. | ||||
|  | ||||
| #ifndef __cplusplus | ||||
|  | ||||
| #error "This is not a C++ compiler" | ||||
|  | ||||
| #elif __cplusplus < 201402L | ||||
|  | ||||
| #error "This is not a C++14 compiler" | ||||
|  | ||||
| #else | ||||
|  | ||||
| namespace cxx14 | ||||
| { | ||||
|  | ||||
|   namespace test_polymorphic_lambdas | ||||
|   { | ||||
|  | ||||
|     int | ||||
|     test() | ||||
|     { | ||||
|       const auto lambda = [](auto&&... args){ | ||||
|         const auto istiny = [](auto x){ | ||||
|           return (sizeof(x) == 1UL) ? 1 : 0; | ||||
|         }; | ||||
|         const int aretiny[] = { istiny(args)... }; | ||||
|         return aretiny[0]; | ||||
|       }; | ||||
|       return lambda(1, 1L, 1.0f, '1'); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_binary_literals | ||||
|   { | ||||
|  | ||||
|     constexpr auto ivii = 0b0000000000101010; | ||||
|     static_assert(ivii == 42, "wrong value"); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_generalized_constexpr | ||||
|   { | ||||
|  | ||||
|     template < typename CharT > | ||||
|     constexpr unsigned long | ||||
|     strlen_c(const CharT *const s) noexcept | ||||
|     { | ||||
|       auto length = 0UL; | ||||
|       for (auto p = s; *p; ++p) | ||||
|         ++length; | ||||
|       return length; | ||||
|     } | ||||
|  | ||||
|     static_assert(strlen_c("") == 0UL, ""); | ||||
|     static_assert(strlen_c("x") == 1UL, ""); | ||||
|     static_assert(strlen_c("test") == 4UL, ""); | ||||
|     static_assert(strlen_c("another\0test") == 7UL, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_lambda_init_capture | ||||
|   { | ||||
|  | ||||
|     int | ||||
|     test() | ||||
|     { | ||||
|       auto x = 0; | ||||
|       const auto lambda1 = [a = x](int b){ return a + b; }; | ||||
|       const auto lambda2 = [a = lambda1(x)](){ return a; }; | ||||
|       return lambda2(); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_digit_separators | ||||
|   { | ||||
|  | ||||
|     constexpr auto ten_million = 100'000'000; | ||||
|     static_assert(ten_million == 100000000, ""); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_return_type_deduction | ||||
|   { | ||||
|  | ||||
|     auto f(int& x) { return x; } | ||||
|     decltype(auto) g(int& x) { return x; } | ||||
|  | ||||
|     template < typename T1, typename T2 > | ||||
|     struct is_same | ||||
|     { | ||||
|       static constexpr auto value = false; | ||||
|     }; | ||||
|  | ||||
|     template < typename T > | ||||
|     struct is_same<T, T> | ||||
|     { | ||||
|       static constexpr auto value = true; | ||||
|     }; | ||||
|  | ||||
|     int | ||||
|     test() | ||||
|     { | ||||
|       auto x = 0; | ||||
|       static_assert(is_same<int, decltype(f(x))>::value, ""); | ||||
|       static_assert(is_same<int&, decltype(g(x))>::value, ""); | ||||
|       return x; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
| }  // namespace cxx14 | ||||
|  | ||||
| #endif  // __cplusplus >= 201402L | ||||
|  | ||||
| ]]) | ||||
|  | ||||
|  | ||||
| dnl  Tests for new features in C++17 | ||||
|  | ||||
| m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ | ||||
|  | ||||
| // If the compiler admits that it is not ready for C++17, why torture it? | ||||
| // Hopefully, this will speed up the test. | ||||
|  | ||||
| #ifndef __cplusplus | ||||
|  | ||||
| #error "This is not a C++ compiler" | ||||
|  | ||||
| #elif __cplusplus <= 201402L | ||||
|  | ||||
| #error "This is not a C++17 compiler" | ||||
|  | ||||
| #else | ||||
|  | ||||
| #if defined(__clang__) | ||||
|   #define REALLY_CLANG | ||||
| #else | ||||
|   #if defined(__GNUC__) | ||||
|     #define REALLY_GCC | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #include <initializer_list> | ||||
| #include <utility> | ||||
| #include <type_traits> | ||||
|  | ||||
| namespace cxx17 | ||||
| { | ||||
|  | ||||
| #if !defined(REALLY_CLANG) | ||||
|   namespace test_constexpr_lambdas | ||||
|   { | ||||
|  | ||||
|     // TODO: test it with clang++ from git | ||||
|  | ||||
|     constexpr int foo = [](){return 42;}(); | ||||
|  | ||||
|   } | ||||
| #endif // !defined(REALLY_CLANG) | ||||
|  | ||||
|   namespace test::nested_namespace::definitions | ||||
|   { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_fold_expression | ||||
|   { | ||||
|  | ||||
|     template<typename... Args> | ||||
|     int multiply(Args... args) | ||||
|     { | ||||
|       return (args * ... * 1); | ||||
|     } | ||||
|  | ||||
|     template<typename... Args> | ||||
|     bool all(Args... args) | ||||
|     { | ||||
|       return (args && ...); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_extended_static_assert | ||||
|   { | ||||
|  | ||||
|     static_assert (true); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_auto_brace_init_list | ||||
|   { | ||||
|  | ||||
|     auto foo = {5}; | ||||
|     auto bar {5}; | ||||
|  | ||||
|     static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value); | ||||
|     static_assert(std::is_same<int, decltype(bar)>::value); | ||||
|   } | ||||
|  | ||||
|   namespace test_typename_in_template_template_parameter | ||||
|   { | ||||
|  | ||||
|     template<template<typename> typename X> struct D; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_fallthrough_nodiscard_maybe_unused_attributes | ||||
|   { | ||||
|  | ||||
|     int f1() | ||||
|     { | ||||
|       return 42; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] int f2() | ||||
|     { | ||||
|       [[maybe_unused]] auto unused = f1(); | ||||
|  | ||||
|       switch (f1()) | ||||
|       { | ||||
|       case 17: | ||||
|         f1(); | ||||
|         [[fallthrough]]; | ||||
|       case 42: | ||||
|         f1(); | ||||
|       } | ||||
|       return f1(); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_extended_aggregate_initialization | ||||
|   { | ||||
|  | ||||
|     struct base1 | ||||
|     { | ||||
|       int b1, b2 = 42; | ||||
|     }; | ||||
|  | ||||
|     struct base2 | ||||
|     { | ||||
|       base2() { | ||||
|         b3 = 42; | ||||
|       } | ||||
|       int b3; | ||||
|     }; | ||||
|  | ||||
|     struct derived : base1, base2 | ||||
|     { | ||||
|         int d; | ||||
|     }; | ||||
|  | ||||
|     derived d1 {{1, 2}, {}, 4};  // full initialization | ||||
|     derived d2 {{}, {}, 4};      // value-initialized bases | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_general_range_based_for_loop | ||||
|   { | ||||
|  | ||||
|     struct iter | ||||
|     { | ||||
|       int i; | ||||
|  | ||||
|       int& operator* () | ||||
|       { | ||||
|         return i; | ||||
|       } | ||||
|  | ||||
|       const int& operator* () const | ||||
|       { | ||||
|         return i; | ||||
|       } | ||||
|  | ||||
|       iter& operator++() | ||||
|       { | ||||
|         ++i; | ||||
|         return *this; | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     struct sentinel | ||||
|     { | ||||
|       int i; | ||||
|     }; | ||||
|  | ||||
|     bool operator== (const iter& i, const sentinel& s) | ||||
|     { | ||||
|       return i.i == s.i; | ||||
|     } | ||||
|  | ||||
|     bool operator!= (const iter& i, const sentinel& s) | ||||
|     { | ||||
|       return !(i == s); | ||||
|     } | ||||
|  | ||||
|     struct range | ||||
|     { | ||||
|       iter begin() const | ||||
|       { | ||||
|         return {0}; | ||||
|       } | ||||
|  | ||||
|       sentinel end() const | ||||
|       { | ||||
|         return {5}; | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     void f() | ||||
|     { | ||||
|       range r {}; | ||||
|  | ||||
|       for (auto i : r) | ||||
|       { | ||||
|         [[maybe_unused]] auto v = i; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_lambda_capture_asterisk_this_by_value | ||||
|   { | ||||
|  | ||||
|     struct t | ||||
|     { | ||||
|       int i; | ||||
|       int foo() | ||||
|       { | ||||
|         return [*this]() | ||||
|         { | ||||
|           return i; | ||||
|         }(); | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_enum_class_construction | ||||
|   { | ||||
|  | ||||
|     enum class byte : unsigned char | ||||
|     {}; | ||||
|  | ||||
|     byte foo {42}; | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_constexpr_if | ||||
|   { | ||||
|  | ||||
|     template <bool cond> | ||||
|     int f () | ||||
|     { | ||||
|       if constexpr(cond) | ||||
|       { | ||||
|         return 13; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         return 42; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   namespace test_selection_statement_with_initializer | ||||
|   { | ||||
|  | ||||
|     int f() | ||||
|     { | ||||
|       return 13; | ||||
|     } | ||||
|  | ||||
|     int f2() | ||||
|     { | ||||
|       if (auto i = f(); i > 0) | ||||
|       { | ||||
|         return 3; | ||||
|       } | ||||
|  | ||||
|       switch (auto i = f(); i + 4) | ||||
|       { | ||||
|       case 17: | ||||
|         return 2; | ||||
|  | ||||
|       default: | ||||
|         return 1; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
| #if !defined(REALLY_CLANG) | ||||
|   namespace test_template_argument_deduction_for_class_templates | ||||
|   { | ||||
|  | ||||
|     // TODO: test it with clang++ from git | ||||
|  | ||||
|     template <typename T1, typename T2> | ||||
|     struct pair | ||||
|     { | ||||
|       pair (T1 p1, T2 p2) | ||||
|         : m1 {p1}, | ||||
|           m2 {p2} | ||||
|       {} | ||||
|  | ||||
|       T1 m1; | ||||
|       T2 m2; | ||||
|     }; | ||||
|  | ||||
|     void f() | ||||
|     { | ||||
|       [[maybe_unused]] auto p = pair{13, 42u}; | ||||
|     } | ||||
|  | ||||
|   } | ||||
| #endif // !defined(REALLY_CLANG) | ||||
|  | ||||
|   namespace test_non_type_auto_template_parameters | ||||
|   { | ||||
|  | ||||
|     template <auto n> | ||||
|     struct B | ||||
|     {}; | ||||
|  | ||||
|     B<5> b1; | ||||
|     B<'a'> b2; | ||||
|  | ||||
|   } | ||||
|  | ||||
| #if !defined(REALLY_CLANG) | ||||
|   namespace test_structured_bindings | ||||
|   { | ||||
|  | ||||
|     // TODO: test it with clang++ from git | ||||
|  | ||||
|     int arr[2] = { 1, 2 }; | ||||
|     std::pair<int, int> pr = { 1, 2 }; | ||||
|  | ||||
|     auto f1() -> int(&)[2] | ||||
|     { | ||||
|       return arr; | ||||
|     } | ||||
|  | ||||
|     auto f2() -> std::pair<int, int>& | ||||
|     { | ||||
|       return pr; | ||||
|     } | ||||
|  | ||||
|     struct S | ||||
|     { | ||||
|       int x1 : 2; | ||||
|       volatile double y1; | ||||
|     }; | ||||
|  | ||||
|     S f3() | ||||
|     { | ||||
|       return {}; | ||||
|     } | ||||
|  | ||||
|     auto [ x1, y1 ] = f1(); | ||||
|     auto& [ xr1, yr1 ] = f1(); | ||||
|     auto [ x2, y2 ] = f2(); | ||||
|     auto& [ xr2, yr2 ] = f2(); | ||||
|     const auto [ x3, y3 ] = f3(); | ||||
|  | ||||
|   } | ||||
| #endif // !defined(REALLY_CLANG) | ||||
|  | ||||
| #if !defined(REALLY_CLANG) | ||||
|   namespace test_exception_spec_type_system | ||||
|   { | ||||
|  | ||||
|     // TODO: test it with clang++ from git | ||||
|  | ||||
|     struct Good {}; | ||||
|     struct Bad {}; | ||||
|  | ||||
|     void g1() noexcept; | ||||
|     void g2(); | ||||
|  | ||||
|     template<typename T> | ||||
|     Bad | ||||
|     f(T*, T*); | ||||
|  | ||||
|     template<typename T1, typename T2> | ||||
|     Good | ||||
|     f(T1*, T2*); | ||||
|  | ||||
|     static_assert (std::is_same_v<Good, decltype(f(g1, g2))>); | ||||
|  | ||||
|   } | ||||
| #endif // !defined(REALLY_CLANG) | ||||
|  | ||||
|   namespace test_inline_variables | ||||
|   { | ||||
|  | ||||
|     template<class T> void f(T) | ||||
|     {} | ||||
|  | ||||
|     template<class T> inline T g(T) | ||||
|     { | ||||
|       return T{}; | ||||
|     } | ||||
|  | ||||
|     template<> inline void f<>(int) | ||||
|     {} | ||||
|  | ||||
|     template<> int g<>(int) | ||||
|     { | ||||
|       return 5; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
| }  // namespace cxx17 | ||||
|  | ||||
| #endif  // __cplusplus <= 201402L | ||||
|  | ||||
| ]]) | ||||
							
								
								
									
										39
									
								
								config/ax_cxx_compile_stdcxx_11.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								config/ax_cxx_compile_stdcxx_11.m4
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| # ============================================================================= | ||||
| #  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html | ||||
| # ============================================================================= | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   Check for baseline language coverage in the compiler for the C++11 | ||||
| #   standard; if necessary, add switches to CXX and CXXCPP to enable | ||||
| #   support. | ||||
| # | ||||
| #   This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX | ||||
| #   macro with the version set to C++11.  The two optional arguments are | ||||
| #   forwarded literally as the second and third argument respectively. | ||||
| #   Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for | ||||
| #   more information.  If you want to use this macro, you also need to | ||||
| #   download the ax_cxx_compile_stdcxx.m4 file. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com> | ||||
| #   Copyright (c) 2012 Zack Weinberg <zackw@panix.com> | ||||
| #   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu> | ||||
| #   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com> | ||||
| #   Copyright (c) 2015 Paul Norman <penorman@mac.com> | ||||
| #   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu> | ||||
| # | ||||
| #   Copying and distribution of this file, with or without modification, are | ||||
| #   permitted in any medium without royalty provided the copyright notice | ||||
| #   and this notice are preserved. This file is offered as-is, without any | ||||
| #   warranty. | ||||
|  | ||||
| #serial 18 | ||||
|  | ||||
| AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) | ||||
| AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) | ||||
| @@ -1,79 +0,0 @@ | ||||
| # =========================================================================== | ||||
| #   http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_GCC_X86_AVX_XGETBV | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   On later x86 processors with AVX SIMD support, with gcc or a compiler | ||||
| #   that has a compatible syntax for inline assembly instructions, run a | ||||
| #   small program that executes the xgetbv instruction with input OP. This | ||||
| #   can be used to detect if the OS supports AVX instruction usage. | ||||
| # | ||||
| #   On output, the values of the eax and edx registers are stored as | ||||
| #   hexadecimal strings as "eax:edx" in the cache variable | ||||
| #   ax_cv_gcc_x86_avx_xgetbv. | ||||
| # | ||||
| #   If the xgetbv instruction fails (because you are running a | ||||
| #   cross-compiler, or because you are not using gcc, or because you are on | ||||
| #   a processor that doesn't have this instruction), | ||||
| #   ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown". | ||||
| # | ||||
| #   This macro mainly exists to be used in AX_EXT. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com> | ||||
| # | ||||
| #   This program is free software: you can redistribute it and/or modify it | ||||
| #   under the terms of the GNU General Public License as published by the | ||||
| #   Free Software Foundation, either version 3 of the License, or (at your | ||||
| #   option) any later version. | ||||
| # | ||||
| #   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 General | ||||
| #   Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License along | ||||
| #   with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| #   As a special exception, the respective Autoconf Macro's copyright owner | ||||
| #   gives unlimited permission to copy, distribute and modify the configure | ||||
| #   scripts that are the output of Autoconf when processing the Macro. You | ||||
| #   need not follow the terms of the GNU General Public License when using | ||||
| #   or distributing such scripts, even though portions of the text of the | ||||
| #   Macro appear in them. The GNU General Public License (GPL) does govern | ||||
| #   all other use of the material that constitutes the Autoconf Macro. | ||||
| # | ||||
| #   This special exception to the GPL applies to versions of the Autoconf | ||||
| #   Macro released by the Autoconf Archive. When you make and distribute a | ||||
| #   modified version of the Autoconf Macro, you may extend this special | ||||
| #   exception to the GPL to apply to your modified version as well. | ||||
|  | ||||
| #serial 1 | ||||
|  | ||||
| AC_DEFUN([AX_GCC_X86_AVX_XGETBV], | ||||
| [AC_REQUIRE([AC_PROG_CC]) | ||||
| AC_LANG_PUSH([C]) | ||||
| AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1, | ||||
|  [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [ | ||||
|      int op = $1, eax, edx; | ||||
|      FILE *f; | ||||
|       /* Opcodes for xgetbv */ | ||||
|       __asm__(".byte 0x0f, 0x01, 0xd0" | ||||
|         : "=a" (eax), "=d" (edx) | ||||
|         : "c" (op)); | ||||
|      f = fopen("conftest_xgetbv", "w"); if (!f) return 1; | ||||
|      fprintf(f, "%x:%x\n", eax, edx); | ||||
|      fclose(f); | ||||
|      return 0; | ||||
| ])], | ||||
|      [ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv], | ||||
|      [ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv], | ||||
|      [ax_cv_gcc_x86_avx_xgetbv_$1=unknown])]) | ||||
| AC_LANG_POP([C]) | ||||
| ]) | ||||
| @@ -1,79 +0,0 @@ | ||||
| # =========================================================================== | ||||
| #     http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_GCC_X86_CPUID(OP) | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   On Pentium and later x86 processors, with gcc or a compiler that has a | ||||
| #   compatible syntax for inline assembly instructions, run a small program | ||||
| #   that executes the cpuid instruction with input OP. This can be used to | ||||
| #   detect the CPU type. | ||||
| # | ||||
| #   On output, the values of the eax, ebx, ecx, and edx registers are stored | ||||
| #   as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable | ||||
| #   ax_cv_gcc_x86_cpuid_OP. | ||||
| # | ||||
| #   If the cpuid instruction fails (because you are running a | ||||
| #   cross-compiler, or because you are not using gcc, or because you are on | ||||
| #   a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP | ||||
| #   is set to the string "unknown". | ||||
| # | ||||
| #   This macro mainly exists to be used in AX_GCC_ARCHFLAG. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> | ||||
| #   Copyright (c) 2008 Matteo Frigo | ||||
| # | ||||
| #   This program is free software: you can redistribute it and/or modify it | ||||
| #   under the terms of the GNU General Public License as published by the | ||||
| #   Free Software Foundation, either version 3 of the License, or (at your | ||||
| #   option) any later version. | ||||
| # | ||||
| #   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 General | ||||
| #   Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License along | ||||
| #   with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| #   As a special exception, the respective Autoconf Macro's copyright owner | ||||
| #   gives unlimited permission to copy, distribute and modify the configure | ||||
| #   scripts that are the output of Autoconf when processing the Macro. You | ||||
| #   need not follow the terms of the GNU General Public License when using | ||||
| #   or distributing such scripts, even though portions of the text of the | ||||
| #   Macro appear in them. The GNU General Public License (GPL) does govern | ||||
| #   all other use of the material that constitutes the Autoconf Macro. | ||||
| # | ||||
| #   This special exception to the GPL applies to versions of the Autoconf | ||||
| #   Macro released by the Autoconf Archive. When you make and distribute a | ||||
| #   modified version of the Autoconf Macro, you may extend this special | ||||
| #   exception to the GPL to apply to your modified version as well. | ||||
|  | ||||
| #serial 7 | ||||
|  | ||||
| AC_DEFUN([AX_GCC_X86_CPUID], | ||||
| [AC_REQUIRE([AC_PROG_CC]) | ||||
| AC_LANG_PUSH([C]) | ||||
| AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1, | ||||
|  [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [ | ||||
|      int op = $1, eax, ebx, ecx, edx; | ||||
|      FILE *f; | ||||
|       __asm__("cpuid" | ||||
|         : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) | ||||
|         : "a" (op)); | ||||
|      f = fopen("conftest_cpuid", "w"); if (!f) return 1; | ||||
|      fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); | ||||
|      fclose(f); | ||||
|      return 0; | ||||
| ])], | ||||
|      [ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid], | ||||
|      [ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid], | ||||
|      [ax_cv_gcc_x86_cpuid_$1=unknown])]) | ||||
| AC_LANG_POP([C]) | ||||
| ]) | ||||
| @@ -4,14 +4,13 @@ | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_EXT | ||||
| #   AX_SSE | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   Find supported SIMD extensions by requesting cpuid. When an SIMD | ||||
| #   extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if | ||||
| #   compiler supports it. For example, if "sse2" is available, then "-msse2" | ||||
| #   is added to SIMD_FLAGS. | ||||
| #   Find SIMD extensions supported by compiler. The -m"simdextensionname" is | ||||
| #   added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is | ||||
| #   available, then "-msse2" is added to SIMD_FLAGS. | ||||
| # | ||||
| #   This macro calls: | ||||
| # | ||||
| @@ -19,7 +18,7 @@ | ||||
| # | ||||
| #   And defines: | ||||
| # | ||||
| #     HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX | ||||
| #     HAVE_SSE3 / HAVE_SSE4.1 | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| @@ -41,32 +40,29 @@ AC_DEFUN([AX_SSE], | ||||
| [ | ||||
|   AC_REQUIRE([AC_CANONICAL_HOST]) | ||||
|  | ||||
|   AM_CONDITIONAL(HAVE_SSE3, false) | ||||
|   AM_CONDITIONAL(HAVE_SSE4_1, false) | ||||
|  | ||||
|   case $host_cpu in | ||||
|     i[[3456]]86*|x86_64*|amd64*) | ||||
|  | ||||
|       AC_REQUIRE([AX_GCC_X86_CPUID]) | ||||
|       AC_REQUIRE([AX_GCC_X86_AVX_XGETBV]) | ||||
|  | ||||
|       AX_GCC_X86_CPUID(0x00000001) | ||||
|  | ||||
|       AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, []) | ||||
|       if test x"$ax_cv_support_sse3_ext" = x"yes"; then | ||||
|         SIMD_FLAGS="$SIMD_FLAGS -msse3" | ||||
|         AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions]) | ||||
| 	AM_CONDITIONAL(HAVE_SSE3, true) | ||||
|         AC_DEFINE(HAVE_SSE3,, | ||||
|           [Support SSE3 (Streaming SIMD Extensions 3) instructions]) | ||||
|         AM_CONDITIONAL(HAVE_SSE3, true) | ||||
|       else | ||||
|         AC_MSG_WARN([Your compiler does not support sse3 instructions, can you try another compiler?]) | ||||
| 	AM_CONDITIONAL(HAVE_SSE3, false) | ||||
|         AC_MSG_WARN([Your compiler does not support SSE3 instructions]) | ||||
|       fi | ||||
|  | ||||
|       AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, []) | ||||
|       if test x"$ax_cv_support_sse41_ext" = x"yes"; then | ||||
|         SIMD_FLAGS="$SIMD_FLAGS -msse4.1" | ||||
|         AC_DEFINE(HAVE_SSE4_1,,[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions]) | ||||
| 	AM_CONDITIONAL(HAVE_SSE4_1, true) | ||||
|         AC_DEFINE(HAVE_SSE4_1,, | ||||
|           [Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions]) | ||||
|         AM_CONDITIONAL(HAVE_SSE4_1, true) | ||||
|       else | ||||
|         AC_MSG_WARN([Your compiler does not support sse4.1 instructions, can you try another compiler?]) | ||||
| 	AM_CONDITIONAL(HAVE_SSE4_1, false) | ||||
|         AC_MSG_WARN([Your compiler does not support SSE4.1]) | ||||
|       fi | ||||
|   ;; | ||||
|   esac | ||||
|   | ||||
							
								
								
									
										54
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -18,12 +18,15 @@ dnl You should have received a copy of the GNU General Public License | ||||
| dnl along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| dnl | ||||
|  | ||||
| AC_INIT(openbts,P2.8TRUNK) | ||||
| AC_INIT([osmo-trx], | ||||
| 	m4_esyscmd([./git-version-gen .tarball-veresion]), | ||||
| 	[openbsc@lists.osmocom.org]) | ||||
| AC_PREREQ(2.57) | ||||
| AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am]) | ||||
| AC_CONFIG_AUX_DIR([.])	 | ||||
| AC_CONFIG_AUX_DIR([.]) | ||||
| AC_CONFIG_MACRO_DIR([config]) | ||||
| AM_CONFIG_HEADER(config.h) | ||||
| AC_CONFIG_TESTDIR(tests) | ||||
|  | ||||
| AC_CANONICAL_BUILD | ||||
| AC_CANONICAL_HOST | ||||
| @@ -34,13 +37,25 @@ AM_INIT_AUTOMAKE([subdir-objects]) | ||||
| dnl Linux kernel KBuild style compile messages | ||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||||
|  | ||||
| dnl include release helper | ||||
| RELMAKE='-include osmo-release.mk' | ||||
| AC_SUBST([RELMAKE]) | ||||
|  | ||||
| AM_PROG_AS | ||||
| AC_PROG_CXX | ||||
| AX_CXX_COMPILE_STDCXX_11 | ||||
| AC_PROG_LN_S | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_INSTALL | ||||
| AC_PATH_PROG([RM_PROG], [rm]) | ||||
|  | ||||
| dnl check for pkg-config (explained in detail in libosmocore/configure.ac) | ||||
| AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) | ||||
| if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | ||||
|         AC_MSG_WARN([You need to install pkg-config]) | ||||
| fi | ||||
| PKG_PROG_PKG_CONFIG([0.20]) | ||||
|  | ||||
| AC_LIBTOOL_WIN32_DLL | ||||
| AC_ENABLE_SHARED	dnl do build shared libraries | ||||
| AC_DISABLE_STATIC	dnl don't build static libraries | ||||
| @@ -105,7 +120,6 @@ AS_IF([test "x$with_usrp1" != "xyes"],[ | ||||
|         )] | ||||
|     ) | ||||
|     AC_DEFINE(USE_UHD, 1, All UHD versions) | ||||
|     PKG_CHECK_MODULES(FFTWF, fftw3f) | ||||
| ]) | ||||
|  | ||||
| AS_IF([test "x$with_singledb" = "xyes"], [ | ||||
| @@ -120,14 +134,39 @@ AS_IF([test "x$with_sse" != "xno"], [ | ||||
|     AM_CONDITIONAL(HAVE_SSE4_1, false) | ||||
| ]) | ||||
|  | ||||
| dnl Check if the compiler supports specified GCC's built-in function | ||||
| AC_DEFUN([CHECK_BUILTIN_SUPPORT], [ | ||||
|   AC_CACHE_CHECK( | ||||
|     [whether ${CC} has $1 built-in], | ||||
|     [osmo_cv_cc_has_builtin], [ | ||||
|       AC_LINK_IFELSE([ | ||||
|         AC_LANG_PROGRAM([], [ | ||||
|           __builtin_cpu_supports("sse"); | ||||
|         ]) | ||||
|       ], | ||||
|       [AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])], | ||||
|       [AS_VAR_SET([osmo_cv_cc_has_builtin], [no])]) | ||||
|     ] | ||||
|   ) | ||||
|  | ||||
|   AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [ | ||||
|     AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1, | ||||
|       [Define to 1 if compiler has the '$1' built-in function]) | ||||
|   ], [ | ||||
|     AC_MSG_WARN($2) | ||||
|   ]) | ||||
| ]) | ||||
|  | ||||
| dnl Check if the compiler supports runtime SIMD detection | ||||
| CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports], | ||||
|   [Runtime SIMD detection will be disabled]) | ||||
|  | ||||
| AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"]) | ||||
| AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"]) | ||||
| AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"]) | ||||
|  | ||||
| AC_CHECK_LIB(sqlite3, sqlite3_open, , AC_MSG_ERROR(sqlite3 is not available)) | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBUSB, libusb-1.0) | ||||
| PKG_CHECK_MODULES(SQLITE3, sqlite3) | ||||
| PKG_CHECK_MODULES(FFTWF, fftw3f) | ||||
|  | ||||
| AC_CHECK_HEADER([boost/config.hpp],[], | ||||
|     [AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])]) | ||||
| @@ -140,6 +179,9 @@ AC_CONFIG_FILES([\ | ||||
|     Transceiver52M/Makefile \ | ||||
|     Transceiver52M/arm/Makefile \ | ||||
|     Transceiver52M/x86/Makefile \ | ||||
|     tests/Makefile \ | ||||
|     tests/CommonLibs/Makefile \ | ||||
|     tests/Transceiver52M/Makefile \ | ||||
| ]) | ||||
|  | ||||
| AC_OUTPUT | ||||
|   | ||||
| @@ -1,7 +1,63 @@ | ||||
| #!/bin/sh | ||||
| set -ex | ||||
|  | ||||
| substr() { [ -z "${2##*$1*}" ]; } | ||||
|  | ||||
| #apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot | ||||
| mychroot_nocwd() { | ||||
|         # LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container | ||||
|         # PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH. | ||||
|         # PROOT_NO_SECCOMP is requried due to proot bug #106 | ||||
|         LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@" | ||||
| } | ||||
|  | ||||
| mychroot() { | ||||
|         mychroot_nocwd -w / "$@" | ||||
| } | ||||
|  | ||||
| if [ -z "${INSIDE_CHROOT}" ]; then | ||||
|  | ||||
|         osmo-clean-workspace.sh | ||||
|  | ||||
|         # Only use ARM chroot if host is not ARM and the target is ARM: | ||||
|         if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then | ||||
|  | ||||
|                 OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh | ||||
|                 ROOTFS_PREFIX="${ROOTFS_PREFIX:-/opt}" | ||||
|                 ROOTFS="${ROOTFS_PREFIX}/qemu-img" | ||||
|                 mkdir -p "${ROOTFS_PREFIX}" | ||||
|  | ||||
|                 # Prepare chroot: | ||||
|                 if [ ! -d "$ROOTFS" ]; then | ||||
|                         mkdir -p "$ROOTFS" | ||||
|                         if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then | ||||
|                                 fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/ | ||||
|                                 # Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it: | ||||
|                                 sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script" | ||||
|                                 mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/ | ||||
|                         else | ||||
|                                 YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))') | ||||
|                                 wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz" | ||||
|                                 tar -xf rootfs.tar.xz -C "$ROOTFS/" || true | ||||
|                                 echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf" | ||||
|                         fi | ||||
|                         mychroot -b /dev apt-get update | ||||
|                         mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git | ||||
|                 fi | ||||
|                 # Run jenkins.sh inside the chroot: | ||||
|                 INSIDE_CHROOT=1 mychroot_nocwd -w /osmo-trx -b "$OSMOTRX_DIR:/osmo-trx" -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" ./contrib/jenkins.sh | ||||
|                 exit 0 | ||||
|         fi | ||||
| fi | ||||
|  | ||||
| ### BUILD osmo-trx | ||||
|  | ||||
| autoreconf --install --force | ||||
| ./configure | ||||
| ./configure $INSTR | ||||
| $MAKE $PARALLEL_MAKE | ||||
| $MAKE check \ | ||||
|   || cat-testlogs.sh | ||||
|  | ||||
| if [ -z "x${INSIDE_CHROOT}" ]; then | ||||
|         osmo-clean-workspace.sh | ||||
| fi | ||||
|   | ||||
							
								
								
									
										235
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										235
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,238 @@ | ||||
| osmo-trx (0.3.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * jenkins: use osmo-clean-workspace.sh before and after build | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * SocketsTest: Fix printing of non-nul-terminated string | ||||
|  | ||||
|   [ Piotr Krysik ] | ||||
|   * UHDDevice.cpp: add USRP B205mini support | ||||
|  | ||||
|   [ Max ] | ||||
|   * Mark release target as virtual | ||||
|   * Remove outdated references to OpenBTS | ||||
|   * Remove unused headers | ||||
|   * Update installation instructions | ||||
|   * Update legal disclaimer | ||||
|   * Update license notes | ||||
|   * tests: null-terminate buffer | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * cosmetic: Remove trailing whitespace | ||||
|   * Logger: Stop using Log.Alarms.Max from config | ||||
|   * Logger: Stop using Log.File and Log.Level from config | ||||
|   * Drop use of ConfigurationTable gConfig | ||||
|   * Remove Configuration module and libsqlite dependency | ||||
|   * cosmetic: AUTHORS: fix trailing whitespace | ||||
|   * Set up GNU Autotest infrastructure | ||||
|   * tests: InterThread: adapt to have reproducible output and enable autotest | ||||
|   * tests: Timeval: adapt to have reproducible output and enable autotest | ||||
|   * tests: Log: adapt to have reproducible output and enable autotest | ||||
|   * Sockets.cpp: Fix initialization of UDD socket | ||||
|   * tests: Sockets: adapt to have reproducible output and enable autotest | ||||
|   * utils/convolvtest: Remove uneeded libosmocore dependency | ||||
|   * Move ARCH_LA to Makefile.common | ||||
|   * tests: Migrate convtest util to autotest infrastructure | ||||
|   * arm/convert.c: Fix compilation error | ||||
|   * arm/convert.c: Add missing convert_init implementation | ||||
|   * .gitignore: Add missing test related files | ||||
|   * Remove UDDSocket class | ||||
|   * tests: SocketTests: Pick OS-assigned instead of setting one manually | ||||
|   * tests: SocketsTest: Avoid hang forever if test fails | ||||
|   * tests: SocketsTest: Fail test on write fail | ||||
|   * tests: TimevalTest: refactor and avoid double comparison | ||||
|   * contrib/jenkins.sh: Use qemu+proot+debootstrap to run tests with ARM instruction set | ||||
|   * tests: convolve: Disable due to difference in output in different archs | ||||
|   * Remove unneeded libdl dependency | ||||
|   * Fix whitespace | ||||
|   * Add support to set Rx/TxAntenna | ||||
|   * UHDDevice: Initialize async_event_thrd in constructor | ||||
|   * Logger: Drop unused gLogEarly | ||||
|   * Logger: Remove unused logging macros | ||||
|   * Logger: get rid of alarm APIs | ||||
|   * Logger: Drop syslog support | ||||
|   * Logger: Drop support to log into file | ||||
|   * Logger: Remove unused includes | ||||
|   * Logger: Remove gLogToConsole flag | ||||
|   * configure.ac: Check for pkg-config | ||||
|  | ||||
|   [ Alexander Huemer ] | ||||
|   * Unbreak `./configure --with-usrp1` build | ||||
|   * Fix USRP1 build with support for setting Rx/TxAntenna | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * jenkins.sh: fix the download url if the qemu image wasn't setup | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Mon, 05 Mar 2018 16:49:42 +0100 | ||||
|  | ||||
| osmo-trx (0.2.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Alexander Chemeris ] | ||||
|   * EDGE: Add support for UmTRX. | ||||
|   * Common: Get rid of a compilation warning. | ||||
|   * Common: Make sure gLogEarly() log to the same facilities as the normal log. | ||||
|   * transceiver: Properly handle MAXDLY. | ||||
|   * transceiver: Add an option to generate random Access Bursts. | ||||
|   * osmo-trx: Output Rx SPS as a part of configuration output. | ||||
|   * transceiver: Do not pass transceiver state struct to function where it's not used. | ||||
|   * makefile: Fix build from an external path. | ||||
|   * radioDevice: GSMRATE macro must have parentheses around its definition. | ||||
|   * uhd: Fix comment. | ||||
|   * radioInterface: Initialize power scale with a meaningful default. | ||||
|   * transceiver: Log channel number in DEBUG output of demoded bursts. | ||||
|   * transceiver: Add an option to emulate a RACH delay in random filler mode. | ||||
|   * UHD: Initial LimeSDR support. | ||||
|   * CommonLibs: Remove unused files. | ||||
|   * sigProcLib: Typo sybols -> symbols | ||||
|   * radioBuffer: Remove extra ; at the end of inline function definitions. | ||||
|   * sigProcLib: Fix documentation, sync argument names in .cpp and .h files. | ||||
|   * sigProcLib: make energyDetect() simpler by returning actual energy. | ||||
|   * sigProcLib: Rename demodulateBurst() to demodGmskBurst() for clarity. | ||||
|   * sigProcLib: Slice SoftVector instead of signalVector for GMSK demod. | ||||
|   * Call vectorSlicer() right before packing bits for transmission to osmo-bts. | ||||
|   * CommonLibs: Print soft bits with less confidence to console when printing a soft vector. | ||||
|   * BitVector: Remove convolutional codec - we don't use it in osmo-trx. | ||||
|   * BitVector: Convert SoftVector from 0..1 to -1..+1 soft bits. | ||||
|   * signalVector: Implement segment(). | ||||
|   * vector: Introduce segmentMove() method to move data inside of a vector. | ||||
|   * vector: Introduce shrink() function to shrink vector size without loosing data. | ||||
|   * Move CorrType type from Transceiver to sigProcLib. | ||||
|   * sigProcLib: rename signalError type to SignalError. | ||||
|   * Move Transceiver::detectBurst() to sigProcLib to make it reusable. | ||||
|   * Move BURST_THRESH from Transceiver.cpp to sigProcLib.h to make it reusable. | ||||
|   * sigProcLib: Add operator<< to print CorrType to a string. | ||||
|   * sigProcLib.h: Fix whitespaces. No non-whitespace changes. | ||||
|   * Move Transceiver::demodulate() to sigProcLib to make it reusable. | ||||
|   * sigProcLib: constify signalVector arguments for detectBurst() functions. | ||||
|   * sigProcLib: Constify demodulation functions burst argument. | ||||
|   * sigProcLib: Fix number of tail bits in random Normal Bursts and zero Stealing Bits. | ||||
|   * Configuration: Variables allocated with 'new' must be freed with 'delete'. | ||||
|   * BitVector: Remove Generator class. | ||||
|   * PRBS: a Pseudo-random binary sequence (PRBS) generator class. | ||||
|  | ||||
|   [ Tom Tsou ] | ||||
|   * EDGE: Fix USRP B210 device support | ||||
|   * uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts | ||||
|   * EDGE: Fix demodulation slicer input | ||||
|   * common: Restrict UDP binding to localhost only | ||||
|   * common: Add mandatory length field to UDP receive calls | ||||
|   * uhd: Update default E3XX settings | ||||
|   * uhd: Set default Tx sampling to 4 sps | ||||
|   * uhd: Make device offset check a private method | ||||
|   * uhd: Set minimum UHD version requirement for E3XX | ||||
|   * sigproc: Expand RACH, TSC, and EDGE correlation windows | ||||
|   * transceiver: Do not report error on SETTSC when radio is on | ||||
|   * transceiver: Add Rx samples-per-symbol option | ||||
|   * radioInterface: Convert diversity argument to general type | ||||
|   * iface: Add inner ring-buffer implementation | ||||
|   * mcbts: Add multi-ARFCN channelizing filters | ||||
|   * mcbts: Add multi-ARFCN radio support | ||||
|   * sigproc: Adjust burst detection threshold criteria | ||||
|   * egprs: Enable 8-PSK length vectors on the Tx interface | ||||
|   * egprs: Enable 8-PSK burst detection when EDGE is enabled | ||||
|   * transceiver: Remove HANDOVER warnings | ||||
|   * mcbts: Allow out of order channel setup | ||||
|   * radioInterface: Fix multi-channel buffer index bug | ||||
|   * uhd: Add command line option for GPS reference | ||||
|   * transceiver: Fix mixed GSMK / 8-PSK transmission | ||||
|   * transceiver: Fix 4 SPS receive TOA value | ||||
|   * sigproc: Fix missing 8-PSK tail symbols | ||||
|   * uhd: Update USRP2/N200/N210 for 4 SPS Rx | ||||
|   * sigproc: Match differential GMSK start/end bits to tail bits | ||||
|   * uhd: Add missing B200 sample timing for 4 SPS receive | ||||
|   * transceiver: Fix command build warning | ||||
|   * uhd: Set minimum supported version to 3.9.0 | ||||
|   * uhd: Add X300 sample timing for 4 SPS | ||||
|   * Revert "uhd: Set minimum supported version to 3.9.0" | ||||
|   * uhd: Add support for UHD-3.11 logging control | ||||
|   * uhd: Increase MC-BTS FPGA clock rate to 51.2 MHz | ||||
|   * Resampler: Fix initialization return checking | ||||
|   * sigProcLib: Remove unreachable code and no-effect checks | ||||
|   * sigProcLib: Check return status on downsampling | ||||
|   * sigProcLib: Fix negative value check on unsigned value | ||||
|   * Resampler: Fix non-array delete for filter taps | ||||
|   * Transceiver: Remove unsigned negative compares | ||||
|   * Configuration: Fix const and signedness compile warnings | ||||
|   * config: Remove OpenBTS style sqlite configuration | ||||
|   * radioInterface: Remove UmTRX 'diversity' option | ||||
|   * build: Require and check for gcc C++11 support | ||||
|   * uhd: Use map container for for device parameter access | ||||
|   * sigProcLib: Remove unused functions from public interface | ||||
|   * uhd: Add non-UmTRX channel swap support | ||||
|   * uhd: Fix Tx-RX timing offset setting | ||||
|   * uhd: Fix USRP2/N200/N210 device detection | ||||
|   * transceiver: Fix POWEROFF crash on USRP2/N200/X300 devices | ||||
|   * sigProcLib: Fix complex/real vector flag in Laurent modulator | ||||
|   * sigProcLib: Remove heap based signal vector allocations | ||||
|   * common: Declare explicit Vector move constructor | ||||
|   * sigProcLib: Remove trigonometric tables | ||||
|   * sigProcLib: Use explicit NaN check in sinc table generation | ||||
|   * sigProcLib: Replace dynamically allocated resampling buffers | ||||
|   * sigProcLib: Specify standard namespace for isnan() | ||||
|   * uhd: Always specify samples-per-symbol for device lookup | ||||
|   * LimeSDR: set approximate tx offset value to make GSM work | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * add basic .gitignore | ||||
|   * configure.ac: check for boost/config.hpp header | ||||
|   * The INSTALL file is being overwritten by autoreconf, but it is committed as empty file. As a result, the INSTALL file always shows as modified. Instead, remove INSTALL from git and ignore it. | ||||
|   * add contrib/jenkins.sh, for gerrit build bot | ||||
|  | ||||
|   [ pierre.baudry ] | ||||
|   * transceiver: Fix mismatched allocations and deallocations | ||||
|  | ||||
|   [ Holger Hans Peter Freyther ] | ||||
|   * debian: Require fftw3 header files for osmo-trx | ||||
|  | ||||
|   [ Max ] | ||||
|   * Add gerrit settings | ||||
|   * Integrate Debian packaging changes | ||||
|   * Remove embedded sqlite3 | ||||
|   * Fix building against sqlite3 | ||||
|   * Add autoconf-archive to dependencies | ||||
|   * debian: remove obsolete dependency | ||||
|   * deb: remove unused dependency | ||||
|   * Remove redundant explicit dependency | ||||
|   * Use release helper from libosmocore | ||||
|  | ||||
|   [ Ruben Undheim ] | ||||
|   * Do not embed sqlite3 when building | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * buildenv: Turn off native architecture builds | ||||
|   * cosmetic: Make parameter lists uniform | ||||
|   * Add test program to verify convolution implementation | ||||
|   * ssedetect: Add runtime CPU detection | ||||
|   * cosmetic: remove code duplication | ||||
|   * buildenv: Make build CPU invariant | ||||
|   * buildenv: Split up SSE3 and SSE4.1 code | ||||
|   * cosmetic: Add info about SSE support | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * buildenv: correct the ax_sse macro description | ||||
|   * buildenv: actually strip unused cpuid functionality | ||||
|   * buildenv: fix build on systems without SIMD support | ||||
|   * buildenv: cosmetic changes | ||||
|   * buildenv: check for __builtin_cpu_supports call support | ||||
|   * ssedetect: call __builtin_cpu_supports() only if supported | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * cosmetic: transciever: Remove trailing whitespaces | ||||
|   * transceiver: Avoid sending clock indications when trx is not powered on | ||||
|   * Add -j option to bind to specific address | ||||
|  | ||||
|   [ ignasj ] | ||||
|   * LimeSDR: Change device detection to work with USB and PCIe versions | ||||
|   * LimeSDR: change tx window type to TX_WINDOW_FIXED | ||||
|   * LimeSDR: Fix sample value range | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * Add '-t' command line option to enable SCHED_RR | ||||
|   * Import git-version-gen and update AC_INIT() | ||||
|  | ||||
|  -- Harald Welte <laforge@gnumonks.org>  Sat, 28 Oct 2017 17:52:32 +0200 | ||||
|  | ||||
| osmo-trx (0.1.9) trusty; urgency=medium | ||||
|  | ||||
|   * Ask Ivan, really | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -5,8 +5,6 @@ Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru> | ||||
| Build-Depends: debhelper (>= 9), | ||||
|                autotools-dev, | ||||
|                autoconf-archive, | ||||
|                libdbd-sqlite3, | ||||
|                libsqlite3-dev, | ||||
|                pkg-config, | ||||
|                dh-autoreconf, | ||||
|                libuhd-dev, | ||||
| @@ -20,7 +18,7 @@ Homepage: https://projects.osmocom.org/projects/osmotrx | ||||
|  | ||||
| Package: osmo-trx | ||||
| Architecture: any | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3 | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends} | ||||
| Description: SDR transceiver that implements Layer 1 of a GSM BTS | ||||
|  OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
|  physical layer of a BTS comprising the following 3GPP specifications: | ||||
|   | ||||
							
								
								
									
										14
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -52,20 +52,6 @@ Copyright: 2005-2009 United States Government as represented by | ||||
|                      the U.S. Army Research Laboratory. | ||||
| License: BSD-3-clause | ||||
|  | ||||
| Files: CommonLibs/sqlite3util.cpp | ||||
| Copyright: 2010 Kestrel Signal Processing Inc. | ||||
| License: none | ||||
|  No license described for file. | ||||
| Comment: In the previous version of the file in the git repository | ||||
|  at upstream it is written: | ||||
|    Written by David A. Burgess, Kestrel Signal Processing, Inc., 2010 | ||||
|    The author disclaims copyright to this source code. | ||||
|  In the git log, this is written: | ||||
|    I do not claim any copyright over this change, as it's very basic. | ||||
|    Looking forward to see it merged into mainline. | ||||
|  See revision e766abbf82f02473038a83fd2f78befd08544cab at | ||||
|  https://github.com/osmocom/osmo-trx | ||||
|  | ||||
| Files: debian/* | ||||
| Copyright: 2015 Ruben Undheim <ruben.undheim@gmail.com> | ||||
| License: GPL-3+ | ||||
|   | ||||
							
								
								
									
										151
									
								
								git-version-gen
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										151
									
								
								git-version-gen
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| #!/bin/sh | ||||
| # Print a version string. | ||||
| scriptversion=2010-01-28.01 | ||||
|  | ||||
| # Copyright (C) 2007-2010 Free Software Foundation, Inc. | ||||
| # | ||||
| # This program is free software: you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 3 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # 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 General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. | ||||
| # It may be run two ways: | ||||
| # - from a git repository in which the "git describe" command below | ||||
| #   produces useful output (thus requiring at least one signed tag) | ||||
| # - from a non-git-repo directory containing a .tarball-version file, which | ||||
| #   presumes this script is invoked like "./git-version-gen .tarball-version". | ||||
|  | ||||
| # In order to use intra-version strings in your project, you will need two | ||||
| # separate generated version string files: | ||||
| # | ||||
| # .tarball-version - present only in a distribution tarball, and not in | ||||
| #   a checked-out repository.  Created with contents that were learned at | ||||
| #   the last time autoconf was run, and used by git-version-gen.  Must not | ||||
| #   be present in either $(srcdir) or $(builddir) for git-version-gen to | ||||
| #   give accurate answers during normal development with a checked out tree, | ||||
| #   but must be present in a tarball when there is no version control system. | ||||
| #   Therefore, it cannot be used in any dependencies.  GNUmakefile has | ||||
| #   hooks to force a reconfigure at distribution time to get the value | ||||
| #   correct, without penalizing normal development with extra reconfigures. | ||||
| # | ||||
| # .version - present in a checked-out repository and in a distribution | ||||
| #   tarball.  Usable in dependencies, particularly for files that don't | ||||
| #   want to depend on config.h but do want to track version changes. | ||||
| #   Delete this file prior to any autoconf run where you want to rebuild | ||||
| #   files to pick up a version string change; and leave it stale to | ||||
| #   minimize rebuild time after unrelated changes to configure sources. | ||||
| # | ||||
| # It is probably wise to add these two files to .gitignore, so that you | ||||
| # don't accidentally commit either generated file. | ||||
| # | ||||
| # Use the following line in your configure.ac, so that $(VERSION) will | ||||
| # automatically be up-to-date each time configure is run (and note that | ||||
| # since configure.ac no longer includes a version string, Makefile rules | ||||
| # should not depend on configure.ac for version updates). | ||||
| # | ||||
| # AC_INIT([GNU project], | ||||
| #         m4_esyscmd([build-aux/git-version-gen .tarball-version]), | ||||
| #         [bug-project@example]) | ||||
| # | ||||
| # Then use the following lines in your Makefile.am, so that .version | ||||
| # will be present for dependencies, and so that .tarball-version will | ||||
| # exist in distribution tarballs. | ||||
| # | ||||
| # BUILT_SOURCES = $(top_srcdir)/.version | ||||
| # $(top_srcdir)/.version: | ||||
| #	echo $(VERSION) > $@-t && mv $@-t $@ | ||||
| # dist-hook: | ||||
| #	echo $(VERSION) > $(distdir)/.tarball-version | ||||
|  | ||||
| case $# in | ||||
|     1) ;; | ||||
|     *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; | ||||
| esac | ||||
|  | ||||
| tarball_version_file=$1 | ||||
| nl=' | ||||
| ' | ||||
|  | ||||
| # First see if there is a tarball-only version file. | ||||
| # then try "git describe", then default. | ||||
| if test -f $tarball_version_file | ||||
| then | ||||
|     v=`cat $tarball_version_file` || exit 1 | ||||
|     case $v in | ||||
| 	*$nl*) v= ;; # reject multi-line output | ||||
| 	[0-9]*) ;; | ||||
| 	*) v= ;; | ||||
|     esac | ||||
|     test -z "$v" \ | ||||
| 	&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 | ||||
| fi | ||||
|  | ||||
| if test -n "$v" | ||||
| then | ||||
|     : # use $v | ||||
| elif | ||||
|        v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ | ||||
| 	  || git describe --abbrev=4 HEAD 2>/dev/null` \ | ||||
|     && case $v in | ||||
| 	 [0-9]*) ;; | ||||
| 	 v[0-9]*) ;; | ||||
| 	 *) (exit 1) ;; | ||||
|        esac | ||||
| then | ||||
|     # Is this a new git that lists number of commits since the last | ||||
|     # tag or the previous older version that did not? | ||||
|     #   Newer: v6.10-77-g0f8faeb | ||||
|     #   Older: v6.10-g0f8faeb | ||||
|     case $v in | ||||
| 	*-*-*) : git describe is okay three part flavor ;; | ||||
| 	*-*) | ||||
| 	    : git describe is older two part flavor | ||||
| 	    # Recreate the number of commits and rewrite such that the | ||||
| 	    # result is the same as if we were using the newer version | ||||
| 	    # of git describe. | ||||
| 	    vtag=`echo "$v" | sed 's/-.*//'` | ||||
| 	    numcommits=`git rev-list "$vtag"..HEAD | wc -l` | ||||
| 	    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; | ||||
| 	    ;; | ||||
|     esac | ||||
|  | ||||
|     # Change the first '-' to a '.', so version-comparing tools work properly. | ||||
|     # Remove the "g" in git describe's output string, to save a byte. | ||||
|     v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; | ||||
| else | ||||
|     v=UNKNOWN | ||||
| fi | ||||
|  | ||||
| v=`echo "$v" |sed 's/^v//'` | ||||
|  | ||||
| # Don't declare a version "dirty" merely because a time stamp has changed. | ||||
| git status > /dev/null 2>&1 | ||||
|  | ||||
| dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= | ||||
| case "$dirty" in | ||||
|     '') ;; | ||||
|     *) # Append the suffix only if there isn't one already. | ||||
| 	case $v in | ||||
| 	  *-dirty) ;; | ||||
| 	  *) v="$v-dirty" ;; | ||||
| 	esac ;; | ||||
| esac | ||||
|  | ||||
| # Omit the trailing newline, so that m4_esyscmd can use the result directly. | ||||
| echo "$v" | tr -d '\012' | ||||
|  | ||||
| # Local variables: | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # End: | ||||
							
								
								
									
										8
									
								
								tests/CommonLibs/BitVectorTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/CommonLibs/BitVectorTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| 15 15 240 | ||||
| 000011110000 | ||||
| 101011110000 | ||||
| 175 | ||||
| 111101010000 | ||||
| ts=abcdefgh | ||||
| tp=0110000101100010011000110110010001100101011001100110011101101000000000 | ||||
| ts=abcdefgh | ||||
| @@ -35,14 +35,20 @@ using namespace std; | ||||
| InterthreadQueue<int> gQ; | ||||
| InterthreadMap<int,int> gMap; | ||||
| 
 | ||||
| int q_last_read_val = -1; | ||||
| int q_last_write_val; | ||||
| int m_last_read_val; | ||||
| int m_last_write_val; | ||||
| 
 | ||||
| void* qWriter(void*) | ||||
| { | ||||
| 	int *p; | ||||
| 	for (int i=0; i<20; i++) { | ||||
| 		p = new int; | ||||
| 		*p = i; | ||||
| 		COUT("queue write " << *p); | ||||
| 		CERR("queue write " << *p); | ||||
| 		gQ.write(p); | ||||
| 		q_last_write_val = i; | ||||
| 		if (random()%2) sleep(1); | ||||
| 	} | ||||
| 	p = new int; | ||||
| @@ -56,8 +62,14 @@ void* qReader(void*) | ||||
| 	bool done = false; | ||||
| 	while (!done) { | ||||
| 		int *p = gQ.read(); | ||||
| 		COUT("queue read " << *p); | ||||
| 		if (*p<0) done=true; | ||||
| 		CERR("queue read " << *p); | ||||
| 		if (*p<0) { | ||||
| 			assert(q_last_read_val == 19 && *p == -1); | ||||
| 			done = true; | ||||
| 		} else { | ||||
| 			assert(q_last_read_val == *p - 1); | ||||
| 			q_last_read_val = *p; | ||||
| 		} | ||||
| 		delete p; | ||||
| 	} | ||||
| 	return NULL; | ||||
| @@ -70,8 +82,9 @@ void* mapWriter(void*) | ||||
| 	for (int i=0; i<20; i++) { | ||||
| 		p = new int; | ||||
| 		*p = i; | ||||
| 		COUT("map write " << *p); | ||||
| 		CERR("map write " << *p); | ||||
| 		gMap.write(i,p); | ||||
| 		m_last_write_val = i; | ||||
| 		if (random()%2) sleep(1); | ||||
| 	} | ||||
| 	return NULL; | ||||
| @@ -81,7 +94,9 @@ void* mapReader(void*) | ||||
| { | ||||
| 	for (int i=0; i<20; i++) { | ||||
| 		int *p = gMap.read(i); | ||||
| 		COUT("map read " << *p); | ||||
| 		CERR("map read " << *p); | ||||
| 		assert(*p == i); | ||||
| 		m_last_read_val = *p; | ||||
| 		// InterthreadMap will delete the pointers
 | ||||
| 		// delete p;
 | ||||
| 	} | ||||
| @@ -109,6 +124,13 @@ int main(int argc, char *argv[]) | ||||
| 	qWriterThread.join(); | ||||
| 	mapReaderThread.join(); | ||||
| 	mapWriterThread.join(); | ||||
| 
 | ||||
| 	assert(q_last_write_val == 19); | ||||
| 	assert(q_last_read_val == 19); | ||||
| 	assert(m_last_write_val == 19); | ||||
| 	assert(m_last_read_val == 19); | ||||
| 
 | ||||
| 	printf("Done\n"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										1
									
								
								tests/CommonLibs/InterthreadTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/CommonLibs/InterthreadTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Done | ||||
| @@ -1,5 +1,6 @@ | ||||
| /*
 | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * Copyright 2009 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| @@ -23,23 +24,25 @@ | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #include "Timeval.h" | ||||
| #include <iostream> | ||||
| #include <iterator> | ||||
| 
 | ||||
| using namespace std; | ||||
| #include "Logger.h" | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	gLogInit("NOTICE"); | ||||
| 
 | ||||
| 	Timeval then(10000); | ||||
| 	cout << then.elapsed() << endl; | ||||
| 
 | ||||
| 	while (!then.passed()) { | ||||
| 		cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; | ||||
| 		usleep(500000); | ||||
| 	} | ||||
| 	cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; | ||||
| 	Log(LOG_EMERG).get() << " testing the logger."; | ||||
| 	Log(LOG_ALERT).get() << " testing the logger."; | ||||
| 	Log(LOG_CRIT).get() << " testing the logger."; | ||||
| 	Log(LOG_ERR).get() << " testing the logger."; | ||||
| 	Log(LOG_WARNING).get() << " testing the logger."; | ||||
| 	Log(LOG_NOTICE).get() << " testing the logger."; | ||||
| 	Log(LOG_INFO).get() << " testing the logger."; | ||||
|         Log(LOG_DEBUG).get() << " testing the logger."; | ||||
|     std::cout << "----------- generating 20 alarms ----------" << std::endl; | ||||
|     for (int i = 0 ; i < 20 ; ++i) { | ||||
|         Log(LOG_ALERT).get() << i; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								tests/CommonLibs/LogTest.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/CommonLibs/LogTest.err
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| EMERG  testing the logger. | ||||
| ALERT  testing the logger. | ||||
| CRIT  testing the logger. | ||||
| ERR  testing the logger. | ||||
| ALERT 0 | ||||
| ALERT 1 | ||||
| ALERT 2 | ||||
| ALERT 3 | ||||
| ALERT 4 | ||||
| ALERT 5 | ||||
| ALERT 6 | ||||
| ALERT 7 | ||||
| ALERT 8 | ||||
| ALERT 9 | ||||
| ALERT 10 | ||||
| ALERT 11 | ||||
| ALERT 12 | ||||
| ALERT 13 | ||||
| ALERT 14 | ||||
| ALERT 15 | ||||
| ALERT 16 | ||||
| ALERT 17 | ||||
| ALERT 18 | ||||
| ALERT 19 | ||||
							
								
								
									
										29
									
								
								tests/CommonLibs/LogTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/CommonLibs/LogTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| EMERG  testing the logger. | ||||
| ALERT  testing the logger. | ||||
| CRIT  testing the logger. | ||||
| ERR  testing the logger. | ||||
| WARNING  testing the logger. | ||||
| NOTICE  testing the logger. | ||||
| INFO  testing the logger. | ||||
| DEBUG  testing the logger. | ||||
| ----------- generating 20 alarms ---------- | ||||
| ALERT 0 | ||||
| ALERT 1 | ||||
| ALERT 2 | ||||
| ALERT 3 | ||||
| ALERT 4 | ||||
| ALERT 5 | ||||
| ALERT 6 | ||||
| ALERT 7 | ||||
| ALERT 8 | ||||
| ALERT 9 | ||||
| ALERT 10 | ||||
| ALERT 11 | ||||
| ALERT 12 | ||||
| ALERT 13 | ||||
| ALERT 14 | ||||
| ALERT 15 | ||||
| ALERT 16 | ||||
| ALERT 17 | ||||
| ALERT 18 | ||||
| ALERT 19 | ||||
							
								
								
									
										44
									
								
								tests/CommonLibs/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tests/CommonLibs/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) -g | ||||
|  | ||||
| EXTRA_DIST = BitVectorTest.ok \ | ||||
|              PRBSTest.ok \ | ||||
|              InterthreadTest.ok \ | ||||
|              SocketsTest.ok \ | ||||
|              TimevalTest.ok \ | ||||
|              VectorTest.ok \ | ||||
| 	     LogTest.ok | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	BitVectorTest \ | ||||
| 	PRBSTest \ | ||||
| 	InterthreadTest \ | ||||
| 	SocketsTest \ | ||||
| 	TimevalTest \ | ||||
| 	VectorTest \ | ||||
| 	LogTest | ||||
|  | ||||
| BitVectorTest_SOURCES = BitVectorTest.cpp | ||||
| BitVectorTest_LDADD = $(COMMON_LA) | ||||
|  | ||||
| PRBSTest_SOURCES = PRBSTest.cpp | ||||
|  | ||||
| InterthreadTest_SOURCES = InterthreadTest.cpp | ||||
| InterthreadTest_LDADD = $(COMMON_LA) | ||||
| InterthreadTest_LDFLAGS = -lpthread | ||||
|  | ||||
| SocketsTest_SOURCES = SocketsTest.cpp | ||||
| SocketsTest_LDADD = $(COMMON_LA) | ||||
| SocketsTest_LDFLAGS = -lpthread | ||||
|  | ||||
| TimevalTest_SOURCES = TimevalTest.cpp | ||||
| TimevalTest_LDADD = $(COMMON_LA) | ||||
|  | ||||
| VectorTest_SOURCES = VectorTest.cpp | ||||
| VectorTest_LDADD = $(COMMON_LA) | ||||
|  | ||||
| LogTest_SOURCES = LogTest.cpp | ||||
| LogTest_LDADD = $(COMMON_LA) | ||||
|  | ||||
| MOSTLYCLEANFILES += testSource testDestination | ||||
							
								
								
									
										42
									
								
								tests/CommonLibs/PRBSTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/CommonLibs/PRBSTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* | ||||
|  * Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co> | ||||
|  * | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "PRBS.h" | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| #include <assert.h> | ||||
|  | ||||
| void testPrbs(PRBS &prbs, uint64_t expectedPeriod) | ||||
| { | ||||
|   uint64_t period = 0; | ||||
|   do { | ||||
|     std::cout << prbs.generateBit(); | ||||
|     period++; | ||||
|   } while (!prbs.isFinished()); | ||||
|   std::cout << std::endl; | ||||
|   std::cout << "Period: " << period << std::endl; | ||||
|   assert(period == expectedPeriod); | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|   PRBS9 prbs9(0x01); | ||||
|   testPrbs(prbs9, (1<<9)-1); | ||||
|   PRBS15 prbs15(0x01); | ||||
|   testPrbs(prbs15, (1<<15)-1); | ||||
| } | ||||
							
								
								
									
										4
									
								
								tests/CommonLibs/PRBSTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/CommonLibs/PRBSTest.ok
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -30,21 +30,27 @@ | ||||
| #include "Threads.h" | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <unistd.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| static const int gNumToSend = 10; | ||||
| 
 | ||||
| 
 | ||||
| void *testReaderIP(void *) | ||||
| static void sigalarm_handler(int foo) | ||||
| { | ||||
| 	UDPSocket readSocket(5934, "localhost", 5061); | ||||
| 	readSocket.nonblocking(); | ||||
| 	printf("FAIL: test did not run successfully\n"); | ||||
| 	exit(EXIT_FAILURE); | ||||
| } | ||||
| 
 | ||||
| void *testReaderIP(void *param) | ||||
| { | ||||
| 	UDPSocket *readSocket = (UDPSocket *)param; | ||||
| 	readSocket->nonblocking(); | ||||
| 	int rc = 0; | ||||
| 	while (rc<gNumToSend) { | ||||
| 		char buf[MAX_UDP_LENGTH]; | ||||
| 		int count = readSocket.read(buf, MAX_UDP_LENGTH); | ||||
| 		char buf[MAX_UDP_LENGTH] = { 0 }; | ||||
| 		int count = readSocket->read(buf, MAX_UDP_LENGTH); | ||||
| 		if (count>0) { | ||||
| 			COUT("read: " << buf); | ||||
| 			CERR("read: " << buf); | ||||
| 			rc++; | ||||
| 		} else { | ||||
| 			sleep(2); | ||||
| @@ -53,51 +59,42 @@ void *testReaderIP(void *) | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void *testReaderUnix(void *) | ||||
| { | ||||
| 	UDDSocket readSocket("testDestination"); | ||||
| 	readSocket.nonblocking(); | ||||
| 	int rc = 0; | ||||
| 	while (rc<gNumToSend) { | ||||
| 		char buf[MAX_UDP_LENGTH]; | ||||
| 		int count = readSocket.read(buf, MAX_UDP_LENGTH); | ||||
| 		if (count>0) { | ||||
| 			COUT("read: " << buf); | ||||
| 			rc++; | ||||
| 		} else { | ||||
| 			sleep(2); | ||||
| 		} | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int main(int argc, char * argv[] ) | ||||
| { | ||||
|   int count; | ||||
| 
 | ||||
|   if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) { | ||||
|     perror("signal"); | ||||
|     exit(EXIT_FAILURE); | ||||
|   } | ||||
| 
 | ||||
|   /* If the test takes longer than 2*gNumToSend seconds, abort it */ | ||||
|   alarm(2* gNumToSend); | ||||
| 
 | ||||
|   UDPSocket readSocket("127.0.0.1", 0); | ||||
|   UDPSocket socket1("127.0.0.1", 0, "localhost", readSocket.port()); | ||||
| 
 | ||||
|   CERR("socket1: " << socket1.port() << ", readSocket: " << readSocket.port()); | ||||
| 
 | ||||
|   Thread readerThreadIP; | ||||
|   readerThreadIP.start(testReaderIP,NULL); | ||||
|   Thread readerThreadUnix; | ||||
|   readerThreadUnix.start(testReaderUnix,NULL); | ||||
| 
 | ||||
|   UDPSocket socket1(5061, "127.0.0.1",5934); | ||||
|   UDDSocket socket1U("testSource","testDestination"); | ||||
|    | ||||
|   COUT("socket1: " << socket1.port()); | ||||
|   readerThreadIP.start(testReaderIP, &readSocket); | ||||
| 
 | ||||
|   // give the readers time to open
 | ||||
|   sleep(1); | ||||
| 
 | ||||
|   for (int i=0; i<gNumToSend; i++) { | ||||
|     socket1.write("Hello IP land");	 | ||||
| 	socket1U.write("Hello Unix domain"); | ||||
| 	sleep(1); | ||||
|     CERR("write"); | ||||
|     count = socket1.write("Hello IP land"); | ||||
|     if (count < 0) { | ||||
|       COUT("FAIL: write"); | ||||
|       exit(EXIT_FAILURE); | ||||
|     } | ||||
|     sleep(1); | ||||
|   } | ||||
| 
 | ||||
|   readerThreadIP.join(); | ||||
|   readerThreadUnix.join(); | ||||
| 
 | ||||
|   printf("Done\n"); | ||||
| } | ||||
| 
 | ||||
| // vim: ts=4 sw=4
 | ||||
							
								
								
									
										1
									
								
								tests/CommonLibs/SocketsTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/CommonLibs/SocketsTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Done | ||||
							
								
								
									
										64
									
								
								tests/CommonLibs/TimevalTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								tests/CommonLibs/TimevalTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #include "Timeval.h" | ||||
| #include <iostream> | ||||
| #include <assert.h> | ||||
| #include <sys/time.h> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	long last_remaining = 10000; | ||||
| 	Timeval then(last_remaining); | ||||
| 	assert(then.elapsed() == -last_remaining); | ||||
| 	cerr << then << " elapsed: " << then.elapsed() << endl; | ||||
|  | ||||
| 	/* Check that last_remaining parameter affects setting time in the future */ | ||||
| 	usleep(10000); | ||||
| 	double increased_time_secs = Timeval().seconds(); | ||||
| 	assert(increased_time_secs <= then.seconds()); | ||||
|  | ||||
| 	struct timespec invariant_time  = then.timespec(); | ||||
| 	int loops = 0; | ||||
|  | ||||
| 	while (!then.passed()) { | ||||
| 		struct timespec tspecnow = then.timespec(); | ||||
| 		cerr << "now: " << Timeval().seconds() << " then: " << then << " remaining: " << then.remaining() << endl; | ||||
| 		assert(last_remaining >= then.remaining()); | ||||
| 		assert(tspecnow.tv_sec == invariant_time.tv_sec && tspecnow.tv_nsec == invariant_time.tv_nsec); | ||||
| 		usleep(500000); | ||||
| 		loops++; | ||||
| 	} | ||||
| 	cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; | ||||
| 	assert(then.remaining() <= 0); | ||||
| 	assert(loops >= 18); | ||||
|  | ||||
| 	printf("Done\n"); | ||||
| } | ||||
							
								
								
									
										1
									
								
								tests/CommonLibs/TimevalTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/CommonLibs/TimevalTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Done | ||||
| @@ -28,10 +28,6 @@ | ||||
| #include "Vector.h" | ||||
| #include <iostream> | ||||
| 
 | ||||
| // We must have a gConfig now to include Vector.
 | ||||
| #include "Configuration.h" | ||||
| ConfigurationTable gConfig; | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| typedef Vector<int> TestVector; | ||||
							
								
								
									
										10
									
								
								tests/CommonLibs/VectorTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/CommonLibs/VectorTest.ok
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| 0 1 2 3 4  | ||||
| 10 11 12 13 14  | ||||
| 0 1 2 3 4 10 11 12 13 14  | ||||
| 0 1 2  | ||||
| 3 4 10 11 12 13 14  | ||||
| 8 8 8 8 8 8 8 8 8 8  | ||||
| 8 8 8 0 1 2 3 4 8 8  | ||||
| 1 2 3  | ||||
| 8 8 8 0 9 9 9 4 8 8  | ||||
| 9 9 9  | ||||
							
								
								
									
										41
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| SUBDIRS = \ | ||||
| 	CommonLibs \ | ||||
| 	Transceiver52M \ | ||||
| 	$(NULL) | ||||
|  | ||||
| # The `:;' works around a Bash 3.2 bug when the output is not writeable. | ||||
| $(srcdir)/package.m4: $(top_srcdir)/configure.ac | ||||
| 	:;{ \ | ||||
|                echo '# Signature of the current package.' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_NAME],' && \ | ||||
|                echo '  [$(PACKAGE_NAME)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_TARNAME],' && \ | ||||
|                echo '  [$(PACKAGE_TARNAME)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_VERSION],' && \ | ||||
|                echo '  [$(PACKAGE_VERSION)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_STRING],' && \ | ||||
|                echo '  [$(PACKAGE_STRING)])' && \ | ||||
|                echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ | ||||
|                echo '  [$(PACKAGE_BUGREPORT)])'; \ | ||||
|                echo 'm4_define([AT_PACKAGE_URL],' && \ | ||||
|                echo '  [$(PACKAGE_URL)])'; \ | ||||
|              } >'$(srcdir)/package.m4' | ||||
|  | ||||
| EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) | ||||
| TESTSUITE = $(srcdir)/testsuite | ||||
| DISTCLEANFILES = atconfig $(NULL) | ||||
|  | ||||
| check-local: atconfig $(TESTSUITE) | ||||
| 	$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) | ||||
|  | ||||
| installcheck-local: atconfig $(TESTSUITE) | ||||
| 	$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' $(TESTSUITEFLAGS) | ||||
|  | ||||
| clean-local: | ||||
| 	test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean | ||||
|  | ||||
| AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te | ||||
| AUTOTEST = $(AUTOM4TE) --language=autotest | ||||
| $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 | ||||
| 	$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at | ||||
| 	mv $@.tmp $@ | ||||
							
								
								
									
										17
									
								
								tests/Transceiver52M/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/Transceiver52M/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CFLAGS = -Wall -I$(top_srcdir)/Transciever52 $(STD_DEFINES_AND_INCLUDES) -g | ||||
|  | ||||
| EXTRA_DIST = convolve_test.ok | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	convolve_test | ||||
|  | ||||
| convolve_test_SOURCES = convolve_test.c | ||||
| convolve_test_LDADD = $(COMMON_LA) $(ARCH_LA) | ||||
| if HAVE_SSE3 | ||||
| convolve_test_CFLAGS = $(AM_CFLAGS) $(SIMD_FLAGS) | ||||
| endif | ||||
| if HAVE_SSE4_1 | ||||
| convolve_test_CFLAGS = $(AM_CFLAGS) $(SIMD_FLAGS) | ||||
| endif | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <stdlib.h> | ||||
| #include "../../Transceiver52M/common/convolve.h" | ||||
| 
 | ||||
| #define TESTVEC_LEN 1000 | ||||
							
								
								
									
										53
									
								
								tests/testsuite.at
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/testsuite.at
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| AT_INIT | ||||
| AT_BANNER([Regression tests.]) | ||||
|  | ||||
| AT_SETUP([BitVectorTest]) | ||||
| AT_KEYWORDS([BitVectorTest]) | ||||
| cat $abs_srcdir/CommonLibs/BitVectorTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/BitVectorTest], [], [expout], []) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([InterthreadTest]) | ||||
| AT_KEYWORDS([InterthreadTest]) | ||||
| cat $abs_srcdir/CommonLibs/InterthreadTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/InterthreadTest], [], [expout], [ignore]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([LogTest]) | ||||
| AT_KEYWORDS([LogTest]) | ||||
| cat $abs_srcdir/CommonLibs/LogTest.ok > expout | ||||
| cat $abs_srcdir/CommonLibs/LogTest.err > experr | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/LogTest], [], [expout], [experr]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([PRBSTest]) | ||||
| AT_KEYWORDS([PRBSTest]) | ||||
| cat $abs_srcdir/CommonLibs/PRBSTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/PRBSTest], [], [expout], []) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([SocketsTest]) | ||||
| AT_KEYWORDS([SocketsTest]) | ||||
| cat $abs_srcdir/CommonLibs/SocketsTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/SocketsTest], [], [expout], [ignore]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([TimevalTest]) | ||||
| AT_KEYWORDS([TimevalTest]) | ||||
| cat $abs_srcdir/CommonLibs/TimevalTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/TimevalTest], [], [expout], [ignore]) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([VectorTest]) | ||||
| AT_KEYWORDS([VectorTest]) | ||||
| cat $abs_srcdir/CommonLibs/VectorTest.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/CommonLibs/VectorTest], [], [expout], []) | ||||
| AT_CLEANUP | ||||
|  | ||||
| AT_SETUP([convolve_test]) | ||||
| AT_KEYWORDS([convolve_test]) | ||||
| # Different results for i686, x86_64 and ARM. see  OS#2826, OS#2828, and https://lists.osmocom.org/pipermail/openbsc/2018-January/011655.html | ||||
| AT_SKIP_IF(true) | ||||
| cat $abs_srcdir/Transceiver52M/convolve_test.ok > expout | ||||
| AT_CHECK([$abs_top_builddir/tests/Transceiver52M/convolve_test], [], [expout], []) | ||||
| AT_CLEANUP | ||||
| @@ -1,18 +0,0 @@ | ||||
| all: main.o convolve_base.o convolve.o convolve_sse_3.o | ||||
| 	gcc -g -Wall ./*.o -o convtest -losmocore | ||||
|  | ||||
| clean: | ||||
| 	rm -f ./*.o | ||||
| 	rm -f ./convtest | ||||
|  | ||||
| main.o: main.c | ||||
| 	gcc -g -Wall -c main.c | ||||
|  | ||||
| convolve_base.o: ../../Transceiver52M/common/convolve_base.c | ||||
| 	gcc -std=c99 -c ../../Transceiver52M/common/convolve_base.c | ||||
|  | ||||
| convolve.o: ../../Transceiver52M/x86/convolve.c | ||||
| 	gcc -std=c99 -c ../../Transceiver52M/x86/convolve.c -I ../../Transceiver52M/common/ -msse3 -DHAVE_SSE3 | ||||
|  | ||||
| convolve_sse_3.o: ../../Transceiver52M/x86/convolve_sse_3.c | ||||
| 	gcc -std=c99 -c ../../Transceiver52M/x86/convolve_sse_3.c -I ../../Transceiver52M/common/ -msse3 -DHAVE_SSE3 | ||||
		Reference in New Issue
	
	Block a user