mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-10-29 19:23:34 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 774a06369a | ||
|  | 94ce835050 | ||
|  | 09befd7a06 | ||
|  | 1303376ad1 | ||
|  | 25021dfe5a | ||
|  | e287598e6b | ||
|  | 98b1af896c | ||
|  | 14bb9c923d | ||
|  | c7f36c282a | ||
|  | f31e4bb089 | ||
|  | 1189019c30 | 
							
								
								
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,65 +0,0 @@ | ||||
| # build results | ||||
| *.o | ||||
| *.lo | ||||
| *.la | ||||
| Transceiver52M/osmo-trx-uhd | ||||
| Transceiver52M/osmo-trx-usrp1 | ||||
| Transceiver52M/osmo-trx-lms | ||||
|  | ||||
| # tests | ||||
| 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 | ||||
| tests/Transceiver52M/LMSDeviceTest | ||||
|  | ||||
| # automake/autoconf | ||||
| *.in | ||||
| .deps | ||||
| .libs | ||||
| .dirstamp | ||||
| *~ | ||||
| Makefile | ||||
| config.log | ||||
| config.status | ||||
| config.h | ||||
| config.guess | ||||
| config.sub | ||||
| config/* | ||||
| configure | ||||
| compile | ||||
| aclocal.m4 | ||||
| autom4te.cache | ||||
| depcomp | ||||
| install-sh | ||||
| libtool | ||||
| ltmain.sh | ||||
| missing | ||||
| stamp-h1 | ||||
| INSTALL | ||||
| tests/package.m4 | ||||
| tests/testsuite | ||||
| tests/atconfig | ||||
| tests/testsuite.dir | ||||
| tests/testsuite.log | ||||
|  | ||||
| # vim | ||||
| *.sw? | ||||
|  | ||||
| # manuals | ||||
| doc/manuals/*.html | ||||
| doc/manuals/*.svg | ||||
| doc/manuals/*.pdf | ||||
| doc/manuals/*__*.png | ||||
| doc/manuals/*.check | ||||
| doc/manuals/generated/ | ||||
| doc/manuals/osmomsc-usermanual.xml | ||||
| doc/manuals/common | ||||
| doc/manuals/build | ||||
| @@ -1,3 +0,0 @@ | ||||
| [gerrit] | ||||
| host=gerrit.osmocom.org | ||||
| project=osmo-trx | ||||
							
								
								
									
										192
									
								
								AUTHORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								AUTHORS
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| # | ||||
| # 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. | ||||
| # | ||||
|  | ||||
| David A. Burgess, dburgess@kestrelsp.com: | ||||
|     CLI/CLI.cpp | ||||
|     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 | ||||
|     GSM/GSMCommon.h | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSML1FEC.cpp | ||||
|     GSM/GSML1FEC.h | ||||
|     GSM/GSML2LAPDm.cpp | ||||
|     GSM/GSML2LAPDm.h | ||||
|     GSM/GSML3CCElements.cpp | ||||
|     GSM/GSML3CCElements.h | ||||
|     GSM/GSML3CCMessages.cpp | ||||
|     GSM/GSML3CCMessages.h | ||||
|     GSM/GSML3CommonElements.cpp | ||||
|     GSM/GSML3CommonElements.h | ||||
|     GSM/GSML3MMElements.cpp | ||||
|     GSM/GSML3MMElements.h | ||||
|     GSM/GSML3MMMessages.cpp | ||||
|     GSM/GSML3MMMessages.h | ||||
|     GSM/GSML3Message.cpp | ||||
|     GSM/GSML3Message.h | ||||
|     GSM/GSML3RRElements.cpp | ||||
|     GSM/GSML3RRElements.h | ||||
|     GSM/GSML3RRMessages.cpp | ||||
|     GSM/GSML3RRMessages.h | ||||
|     GSM/GSMLogicalChannel.h | ||||
|     GSM/GSMTDMA.cpp | ||||
|     GSM/GSMTDMA.h | ||||
|     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 | ||||
|  | ||||
| Harvind S. Samra, hssamra@kestrelsp.com: | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
|     Transceiver/ComplexTest.cpp | ||||
|     Transceiver/Transceiver.cpp | ||||
|     Transceiver/Transceiver.h | ||||
|     Transceiver/USRPDevice.cpp | ||||
|     Transceiver/USRPDevice.h | ||||
|     Transceiver/USRPping.cpp | ||||
|     Transceiver/radioInterface.cpp | ||||
|     Transceiver/radioInterface.h | ||||
|     Transceiver/rcvLPF_651.h | ||||
|     Transceiver/runTransceiver.cpp | ||||
|     Transceiver/sendLPF_961.h | ||||
|     Transceiver/sigProcLib.cpp | ||||
|     Transceiver/sigProcLib.h | ||||
|     Transceiver/sigProcLibTest.cpp | ||||
|     Transceiver/sweepGenerator.cpp | ||||
|     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 | ||||
|     GSM/GSML3CCElements.cpp | ||||
|     GSM/GSML3CCElements.h | ||||
|     GSM/GSML3CCMessages.cpp | ||||
|     GSM/GSML3CCMessages.h | ||||
|     GSM/GSML3CommonElements.cpp | ||||
|     GSM/GSML3CommonElements.h | ||||
|     GSM/GSML3MMElements.cpp | ||||
|     GSM/GSML3MMElements.h | ||||
|     GSM/GSML3MMMessages.cpp | ||||
|     GSM/GSML3MMMessages.h | ||||
|     GSM/GSML3Message.cpp | ||||
|     GSM/GSML3Message.h | ||||
|     GSM/GSML3RRElements.cpp | ||||
|     GSM/GSML3RRElements.h | ||||
|     GSM/GSML3RRMessages.cpp | ||||
|     GSM/GSML3RRMessages.h | ||||
|     GSM/GSMLogicalChannel.h | ||||
|     GSM/GSMSAPMux.cpp | ||||
|     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 OsmoTRX | ||||
| ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS | ||||
|  | ||||
|  | ||||
| Permissive Terms Supplementing the License | ||||
|  | ||||
| 1. Remote Interaction Through IP Networks. | ||||
|  | ||||
| OsmoTRX is an implementation of the GSM network cellular air interface, | ||||
| OpenBTS includes an implementation of the GSM network cellular air interface, | ||||
| as well as other interfaces to IP networks.  The interaction of cellular | ||||
| handsets with the OsmoTRX software is considered "remote network interaction" | ||||
| handsets with the OpenBTS 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,6 +694,17 @@ 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 | ||||
|  | ||||
|  | ||||
| @@ -701,8 +712,13 @@ 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 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 | ||||
| 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 | ||||
| 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. | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008, 2009 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -31,7 +30,6 @@ | ||||
| #include <iostream> | ||||
| #include <stdio.h> | ||||
| #include <sstream> | ||||
| #include <math.h> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| @@ -201,6 +199,49 @@ void BitVector::LSB8MSB() | ||||
|  | ||||
|  | ||||
|  | ||||
| uint64_t BitVector::syndrome(Generator& gen) const | ||||
| { | ||||
| 	gen.clear(); | ||||
| 	const char *dp = mStart; | ||||
| 	while (dp<mEnd) gen.syndromeShift(*dp++); | ||||
| 	return gen.state(); | ||||
| } | ||||
|  | ||||
|  | ||||
| uint64_t BitVector::parity(Generator& gen) const | ||||
| { | ||||
| 	gen.clear(); | ||||
| 	const char *dp = mStart; | ||||
| 	while (dp<mEnd) gen.encoderShift(*dp++); | ||||
| 	return gen.state(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void BitVector::encode(const ViterbiR2O4& coder, BitVector& target) | ||||
| { | ||||
| 	size_t sz = size(); | ||||
| 	assert(sz*coder.iRate() == target.size()); | ||||
|  | ||||
| 	// Build a "history" array where each element contains the full history. | ||||
| 	uint32_t history[sz]; | ||||
| 	uint32_t accum = 0; | ||||
| 	for (size_t i=0; i<sz; i++) { | ||||
| 		accum = (accum<<1) | bit(i); | ||||
| 		history[i] = accum; | ||||
| 	} | ||||
|  | ||||
| 	// Look up histories in the pre-generated state table. | ||||
| 	char *op = target.begin(); | ||||
| 	for (size_t i=0; i<sz; i++) { | ||||
| 		unsigned index = coder.cMask() & history[i]; | ||||
| 		for (unsigned g=0; g<coder.iRate(); g++) { | ||||
| 			*op++ = coder.stateTable(g,index); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| unsigned BitVector::sum() const | ||||
| { | ||||
| 	unsigned sum = 0; | ||||
| @@ -246,12 +287,148 @@ ostream& operator<<(ostream& os, const BitVector& hv) | ||||
|  | ||||
|  | ||||
|  | ||||
| ViterbiR2O4::ViterbiR2O4() | ||||
| { | ||||
| 	assert(mDeferral < 32); | ||||
| 	mCoeffs[0] = 0x019; | ||||
| 	mCoeffs[1] = 0x01b; | ||||
| 	computeStateTables(0); | ||||
| 	computeStateTables(1); | ||||
| 	computeGeneratorTable(); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| void ViterbiR2O4::initializeStates() | ||||
| { | ||||
| 	for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]); | ||||
| 	for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void ViterbiR2O4::computeStateTables(unsigned g) | ||||
| { | ||||
| 	assert(g<mIRate); | ||||
| 	for (unsigned state=0; state<mIStates; state++) { | ||||
| 		// 0 input | ||||
| 		uint32_t inputVal = state<<1; | ||||
| 		mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1); | ||||
| 		// 1 input | ||||
| 		inputVal |= 1; | ||||
| 		mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ViterbiR2O4::computeGeneratorTable() | ||||
| { | ||||
| 	for (unsigned index=0; index<mIStates*2; index++) { | ||||
| 		mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index]; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| void ViterbiR2O4::branchCandidates() | ||||
| { | ||||
| 	// Branch to generate new input states. | ||||
| 	const vCand *sp = mSurvivors; | ||||
| 	for (unsigned i=0; i<mNumCands; i+=2) { | ||||
| 		// extend and suffix | ||||
| 		const uint32_t iState0 = (sp->iState) << 1;				// input state for 0 | ||||
| 		const uint32_t iState1 = iState0 | 0x01;				// input state for 1 | ||||
| 		const uint32_t oStateShifted = (sp->oState) << mIRate;	// shifted output | ||||
| 		const float cost = sp->cost; | ||||
| 		sp++; | ||||
| 		// 0 input extension | ||||
| 		mCandidates[i].cost = cost; | ||||
| 		mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask]; | ||||
| 		mCandidates[i].iState = iState0; | ||||
| 		// 1 input extension | ||||
| 		mCandidates[i+1].cost = cost; | ||||
| 		mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask]; | ||||
| 		mCandidates[i+1].iState = iState1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost) | ||||
| { | ||||
| 	const float *cTab[2] = {matchCost,mismatchCost}; | ||||
| 	for (unsigned i=0; i<mNumCands; i++) { | ||||
| 		vCand& thisCand = mCandidates[i]; | ||||
| 		// We examine input bits 2 at a time for a rate 1/2 coder. | ||||
| 		const unsigned mismatched = inSample ^ (thisCand.oState); | ||||
| 		thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0]; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| void ViterbiR2O4::pruneCandidates() | ||||
| { | ||||
| 	const vCand* c1 = mCandidates;					// 0-prefix | ||||
| 	const vCand* c2 = mCandidates + mIStates;		// 1-prefix | ||||
| 	for (unsigned i=0; i<mIStates; i++) { | ||||
| 		if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i]; | ||||
| 		else mSurvivors[i] = c2[i]; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const | ||||
| { | ||||
| 	int minIndex = 0; | ||||
| 	float minCost = mSurvivors[0].cost; | ||||
| 	for (unsigned i=1; i<mIStates; i++) { | ||||
| 		const float thisCost = mSurvivors[i].cost; | ||||
| 		if (thisCost>=minCost) continue; | ||||
| 		minCost = thisCost; | ||||
| 		minIndex=i; | ||||
| 	} | ||||
| 	return mSurvivors[minIndex]; | ||||
| } | ||||
|  | ||||
|  | ||||
| const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs) | ||||
| { | ||||
| 	branchCandidates(); | ||||
| 	getSoftCostMetrics(inSample,probs,iprobs); | ||||
| 	pruneCandidates(); | ||||
| 	return minCost(); | ||||
| } | ||||
|  | ||||
|  | ||||
| uint64_t Parity::syndrome(const BitVector& receivedCodeword) | ||||
| { | ||||
| 	return receivedCodeword.syndrome(*this); | ||||
| } | ||||
|  | ||||
|  | ||||
| void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert) | ||||
| { | ||||
| 	uint64_t pWord = data.parity(*this); | ||||
| 	if (invert) pWord = ~pWord;  | ||||
| 	parityTarget.fillField(0,pWord,size()); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| SoftVector::SoftVector(const BitVector& source) | ||||
| { | ||||
| 	resize(source.size()); | ||||
| 	for (size_t i=0; i<size(); i++) { | ||||
| 		if (source.bit(i)) mStart[i]=1.0F; | ||||
| 		else mStart[i]=-1.0F; | ||||
| 		else mStart[i]=0.0F; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -261,20 +438,102 @@ BitVector SoftVector::sliced() const | ||||
| 	size_t sz = size(); | ||||
| 	BitVector newSig(sz); | ||||
| 	for (size_t i=0; i<sz; i++) { | ||||
| 		if (mStart[i]>0.0F) newSig[i]=1; | ||||
| 		if (mStart[i]>0.5F) newSig[i]=1; | ||||
| 		else newSig[i] = 0; | ||||
| 	} | ||||
| 	return newSig; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const | ||||
| { | ||||
| 	const size_t sz = size(); | ||||
| 	const unsigned deferral = decoder.deferral(); | ||||
| 	const size_t ctsz = sz + deferral*decoder.iRate(); | ||||
| 	assert(sz <= decoder.iRate()*target.size()); | ||||
|  | ||||
| 	// Build a "history" array where each element contains the full history. | ||||
| 	uint32_t history[ctsz]; | ||||
| 	{ | ||||
| 		BitVector bits = sliced(); | ||||
| 		uint32_t accum = 0; | ||||
| 		for (size_t i=0; i<sz; i++) { | ||||
| 			accum = (accum<<1) | bits.bit(i); | ||||
| 			history[i] = accum; | ||||
| 		} | ||||
| 		// Repeat last bit at the end. | ||||
| 		for (size_t i=sz; i<ctsz; i++) { | ||||
| 			accum = (accum<<1) | (accum & 0x01); | ||||
| 			history[i] = accum; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Precompute metric tables. | ||||
| 	float matchCostTable[ctsz]; | ||||
| 	float mismatchCostTable[ctsz]; | ||||
| 	{ | ||||
| 		const float *dp = mStart; | ||||
| 		for (size_t i=0; i<sz; i++) { | ||||
| 			// pVal is the probability that a bit is correct. | ||||
| 			// ipVal is the probability that a bit is incorrect. | ||||
| 			float pVal = dp[i]; | ||||
| 			if (pVal>0.5F) pVal = 1.0F-pVal; | ||||
| 			float ipVal = 1.0F-pVal; | ||||
| 			// This is a cheap approximation to an ideal cost function. | ||||
| 			if (pVal<0.01F) pVal = 0.01; | ||||
| 			if (ipVal<0.01F) ipVal = 0.01; | ||||
| 			matchCostTable[i] = 0.25F/ipVal; | ||||
| 			mismatchCostTable[i] = 0.25F/pVal; | ||||
| 		} | ||||
| 	 | ||||
| 		// pad end of table with unknowns | ||||
| 		for (size_t i=sz; i<ctsz; i++) { | ||||
| 			matchCostTable[i] = 0.5F; | ||||
| 			mismatchCostTable[i] = 0.5F; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		decoder.initializeStates(); | ||||
| 		// Each sample of history[] carries its history. | ||||
| 		// So we only have to process every iRate-th sample. | ||||
| 		const unsigned step = decoder.iRate(); | ||||
| 		// input pointer | ||||
| 		const uint32_t *ip = history + step - 1; | ||||
| 		// output pointers | ||||
| 		char *op = target.begin(); | ||||
| 		const char *const opt = target.end(); | ||||
| 		// table pointers | ||||
| 		const float* match = matchCostTable; | ||||
| 		const float* mismatch = mismatchCostTable; | ||||
| 		size_t oCount = 0; | ||||
| 		while (op<opt) { | ||||
| 			// Viterbi algorithm | ||||
| 			assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1); | ||||
| 			assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1); | ||||
| 			const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch); | ||||
| 			ip += step; | ||||
| 			match += step; | ||||
| 			mismatch += step; | ||||
| 			// output | ||||
| 			if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01; | ||||
| 			oCount++; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| // (pat) Added 6-22-2012 | ||||
| float SoftVector::getEnergy(float *plow) const | ||||
| { | ||||
| 	const SoftVector &vec = *this; | ||||
| 	int len = vec.size(); | ||||
| 	float avg = 0; float low = 1; | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		float energy = fabsf(vec[i]); | ||||
| 		float bit = vec[i]; | ||||
| 		float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5)); | ||||
| 		if (energy < low) low = energy; | ||||
| 		avg += energy/len; | ||||
| 	} | ||||
| @@ -286,12 +545,8 @@ float SoftVector::getEnergy(float *plow) const | ||||
| ostream& operator<<(ostream& os, const SoftVector& sv) | ||||
| { | ||||
| 	for (size_t i=0; i<sv.size(); i++) { | ||||
| 		if (sv[i]<-0.5) os << "0"; | ||||
| 		else if (sv[i]<-0.25) os << "o"; | ||||
| 		else if (sv[i]<0.0) os << "."; | ||||
| 		else if (sv[i]>0.5) os << "1"; | ||||
| 		else if (sv[i]>0.25) os << "|"; | ||||
| 		else if (sv[i]>0.0) os << "'"; | ||||
| 		if (sv[i]<0.25) os << "0"; | ||||
| 		else if (sv[i]>0.75) os << "1"; | ||||
| 		else os << "-"; | ||||
| 	} | ||||
| 	return os; | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008, 2009 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -32,6 +30,201 @@ | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| class BitVector; | ||||
| class SoftVector; | ||||
|  | ||||
|  | ||||
|  | ||||
| /** Shift-register (LFSR) generator. */ | ||||
| class Generator { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	uint64_t mCoeff;	///< polynomial coefficients. LSB is zero exponent. | ||||
| 	uint64_t mState;	///< shift register state. LSB is most recent. | ||||
| 	uint64_t mMask;		///< mask for reading state | ||||
| 	unsigned mLen;		///< number of bits used in shift register | ||||
| 	unsigned mLen_1;	///< mLen - 1 | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Generator(uint64_t wCoeff, unsigned wLen) | ||||
| 		:mCoeff(wCoeff),mState(0), | ||||
| 		mMask((1ULL<<wLen)-1), | ||||
| 		mLen(wLen),mLen_1(wLen-1) | ||||
| 	{ assert(wLen<64); } | ||||
|  | ||||
| 	void clear() { mState=0; } | ||||
|  | ||||
| 	/**@name Accessors */ | ||||
| 	//@{ | ||||
| 	uint64_t state() const { return mState & mMask; } | ||||
| 	unsigned size() const { return mLen; } | ||||
| 	//@} | ||||
|  | ||||
| 	/** | ||||
| 		Calculate one bit of a syndrome. | ||||
| 		This is in the .h for inlining. | ||||
| 	*/ | ||||
| 	void syndromeShift(unsigned inBit) | ||||
| 	{ | ||||
| 		const unsigned fb = (mState>>(mLen_1)) & 0x01; | ||||
| 		mState = (mState<<1) ^ (inBit & 0x01); | ||||
| 		if (fb) mState ^= mCoeff; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Update the generator state by one cycle. | ||||
| 		This is in the .h for inlining. | ||||
| 	*/ | ||||
| 	void encoderShift(unsigned inBit) | ||||
| 	{ | ||||
| 		const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01; | ||||
| 		mState <<= 1; | ||||
| 		if (fb) mState ^= mCoeff; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /** Parity (CRC-type) generator and checker based on a Generator. */ | ||||
| class Parity : public Generator { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	unsigned mCodewordSize; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize) | ||||
| 		:Generator(wCoefficients, wParitySize), | ||||
| 		mCodewordSize(wCodewordSize) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Compute the parity word and write it into the target segment.  */ | ||||
| 	void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true); | ||||
|  | ||||
| 	/** Compute the syndrome of a received sequence. */ | ||||
| 	uint64_t syndrome(const BitVector& receivedCodeword); | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /** | ||||
| 	Class to represent convolutional coders/decoders of rate 1/2, memory length 4. | ||||
| 	This is the "workhorse" coder for most GSM channels. | ||||
| */ | ||||
| class ViterbiR2O4 { | ||||
|  | ||||
| 	private: | ||||
| 		/**name Lots of precomputed elements so the compiler can optimize like hell. */ | ||||
| 		//@{ | ||||
| 		/**@name Core values. */ | ||||
| 		//@{ | ||||
| 		static const unsigned mIRate = 2;	///< reciprocal of rate | ||||
| 		static const unsigned mOrder = 4;	///< memory length of generators | ||||
| 		//@} | ||||
| 		/**@name Derived values. */ | ||||
| 		//@{ | ||||
| 		static const unsigned mIStates = 0x01 << mOrder;	///< number of states, number of survivors | ||||
| 		static const uint32_t mSMask = mIStates-1;			///< survivor mask | ||||
| 		static const uint32_t mCMask = (mSMask<<1) | 0x01;	///< candidate mask | ||||
| 		static const uint32_t mOMask = (0x01<<mIRate)-1;	///< ouput mask, all iRate low bits set | ||||
| 		static const unsigned mNumCands = mIStates*2;		///< number of candidates to generate during branching | ||||
| 		static const unsigned mDeferral = 6*mOrder;			///< deferral to be used | ||||
| 		//@} | ||||
| 		//@} | ||||
|  | ||||
| 		/** Precomputed tables. */ | ||||
| 		//@{ | ||||
| 		uint32_t mCoeffs[mIRate];					///< polynomial for each generator | ||||
| 		uint32_t mStateTable[mIRate][2*mIStates];	///< precomputed generator output tables | ||||
| 		uint32_t mGeneratorTable[2*mIStates];		///< precomputed coder output table | ||||
| 		//@} | ||||
| 	 | ||||
| 	public: | ||||
|  | ||||
| 		/** | ||||
| 		  A candidate sequence in a Viterbi decoder. | ||||
| 		  The 32-bit state register can support a deferral of 6 with a 4th-order coder. | ||||
| 		 */ | ||||
| 		typedef struct candStruct { | ||||
| 			uint32_t iState;	///< encoder input associated with this candidate | ||||
| 			uint32_t oState;	///< encoder output associated with this candidate | ||||
| 			float cost;			///< cost (metric value), float to support soft inputs | ||||
| 		} vCand; | ||||
|  | ||||
| 		/** Clear a structure. */ | ||||
| 		void clear(vCand& v) | ||||
| 		{ | ||||
| 			v.iState=0; | ||||
| 			v.oState=0; | ||||
| 			v.cost=0; | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 		/**@name Survivors and candidates. */ | ||||
| 		//@{ | ||||
| 		vCand mSurvivors[mIStates];			///< current survivor pool | ||||
| 		vCand mCandidates[2*mIStates];		///< current candidate pool | ||||
| 		//@} | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 		unsigned iRate() const { return mIRate; } | ||||
| 		uint32_t cMask() const { return mCMask; } | ||||
| 		uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; } | ||||
| 		unsigned deferral() const { return mDeferral; } | ||||
| 		 | ||||
|  | ||||
| 		ViterbiR2O4(); | ||||
|  | ||||
| 		/** Set all cost metrics to zero. */ | ||||
| 		void initializeStates(); | ||||
|  | ||||
| 		/** | ||||
| 			Full cycle of the Viterbi algorithm: branch, metrics, prune, select. | ||||
| 			@return reference to minimum-cost candidate. | ||||
| 		*/ | ||||
| 		const vCand& step(uint32_t inSample, const float *probs, const float *iprobs); | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 		/** Branch survivors into new candidates. */ | ||||
| 		void branchCandidates(); | ||||
|  | ||||
| 		/** Compute cost metrics for soft-inputs. */ | ||||
| 		void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs); | ||||
|  | ||||
| 		/** Select survivors from the candidate set. */ | ||||
| 		void pruneCandidates(); | ||||
|  | ||||
| 		/** Find the minimum cost survivor. */ | ||||
| 		const vCand& minCost() const; | ||||
|  | ||||
| 		/** | ||||
| 			Precompute the state tables. | ||||
| 			@param g Generator index 0..((1/rate)-1) | ||||
| 		*/ | ||||
| 		void computeStateTables(unsigned g); | ||||
|  | ||||
| 		/** | ||||
| 			Precompute the generator outputs. | ||||
| 			mCoeffs must be defined first. | ||||
| 		*/ | ||||
| 		void computeGeneratorTable(); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class BitVector : public Vector<char> { | ||||
|  | ||||
|  | ||||
| @@ -89,6 +282,16 @@ class BitVector : public Vector<char> { | ||||
|  | ||||
| 	void zero() { fill(0); } | ||||
|  | ||||
| 	/**@name FEC operations. */ | ||||
| 	//@{ | ||||
| 	/** Calculate the syndrome of the vector with the given Generator. */ | ||||
| 	uint64_t syndrome(Generator& gen) const; | ||||
| 	/** Calculate the parity word for the vector with the given Generator. */ | ||||
| 	uint64_t parity(Generator& gen) const; | ||||
| 	/** Encode the signal with the GSM rate 1/2 convolutional encoder. */ | ||||
| 	void encode(const ViterbiR2O4& encoder, BitVector& target); | ||||
| 	//@} | ||||
|  | ||||
|  | ||||
| 	/** Invert 0<->1. */ | ||||
| 	void invert(); | ||||
| @@ -224,20 +427,23 @@ class SoftVector: public Vector<float> { | ||||
| 	const SoftVector tail(size_t start) const { return segment(start,size()-start); } | ||||
| 	//@} | ||||
|  | ||||
| 	// How good is the SoftVector in the sense of the bits being solid? | ||||
| 	// Result of 1 is perfect and 0 means all the bits were 0.0 | ||||
| 	/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */ | ||||
| 	void decode(ViterbiR2O4 &decoder, BitVector& target) const; | ||||
|  | ||||
| 	// (pat) How good is the SoftVector in the sense of the bits being solid? | ||||
| 	// Result of 1 is perfect and 0 means all the bits were 0.5 | ||||
| 	// If plow is non-NULL, also return the lowest energy bit. | ||||
| 	float getEnergy(float *low=0) const; | ||||
|  | ||||
| 	/** Fill with "unknown" values. */ | ||||
| 	void unknown() { fill(0.0F); } | ||||
| 	void unknown() { fill(0.5F); } | ||||
|  | ||||
| 	/** Return a hard bit value from a given index by slicing. */ | ||||
| 	bool bit(size_t index) const | ||||
| 	{ | ||||
| 		const float *dp = mStart+index; | ||||
| 		assert(dp<mEnd); | ||||
| 		return (*dp)>0.0F; | ||||
| 		return (*dp)>0.5F; | ||||
| 	} | ||||
|  | ||||
| 	/** Slice the whole signal into bits. */ | ||||
|   | ||||
							
								
								
									
										88
									
								
								CommonLibs/BitVectorTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								CommonLibs/BitVectorTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| /* | ||||
| * 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 "BitVector.h" | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
|   | ||||
| using namespace std; | ||||
|  | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	BitVector v1("0000111100111100101011110000"); | ||||
| 	cout << v1 << endl; | ||||
| 	v1.LSB8MSB(); | ||||
| 	cout << v1 << endl; | ||||
| 	ViterbiR2O4 vCoder; | ||||
| 	BitVector v2(v1.size()*2); | ||||
| 	v1.encode(vCoder,v2); | ||||
| 	cout << v2 << endl; | ||||
| 	SoftVector sv2(v2); | ||||
| 	cout << sv2 << endl; | ||||
| 	for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5; | ||||
| 	cout << sv2 << endl; | ||||
| 	BitVector v3(v1.size()); | ||||
| 	sv2.decode(vCoder,v3); | ||||
| 	cout << v3 << endl; | ||||
|  | ||||
| 	cout << v3.segment(3,4) << endl; | ||||
|  | ||||
| 	BitVector v4(v3.segment(0,4),v3.segment(8,4)); | ||||
| 	cout << v4 << endl; | ||||
|  | ||||
| 	BitVector v5("000011110000"); | ||||
| 	int r1 = v5.peekField(0,8); | ||||
| 	int r2 = v5.peekField(4,4); | ||||
| 	int r3 = v5.peekField(4,8); | ||||
| 	cout << r1 <<  ' ' << r2 << ' ' << r3 << endl; | ||||
| 	cout << v5 << endl; | ||||
| 	v5.fillField(0,0xa,4); | ||||
| 	int r4 = v5.peekField(0,8); | ||||
| 	cout << v5 << endl; | ||||
| 	cout << r4 << endl; | ||||
|  | ||||
| 	v5.reverse8(); | ||||
| 	cout << v5 << endl; | ||||
|  | ||||
| 	BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000"; | ||||
| 	SoftVector mCS(mC); | ||||
| 	BitVector mU(mC.size()/2); | ||||
| 	mCS.decode(vCoder,mU); | ||||
| 	cout << "c=" << mCS << endl; | ||||
| 	cout << "u=" << mU << endl; | ||||
|  | ||||
|  | ||||
| 	unsigned char ts[9] = "abcdefgh"; | ||||
| 	BitVector tp(70); | ||||
| 	cout << "ts=" << ts << endl; | ||||
| 	tp.unpack(ts); | ||||
| 	cout << "tp=" << tp << endl; | ||||
| 	tp.pack(ts); | ||||
| 	cout << "ts=" << ts << endl; | ||||
| } | ||||
							
								
								
									
										1154
									
								
								CommonLibs/Configuration.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1154
									
								
								CommonLibs/Configuration.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										422
									
								
								CommonLibs/Configuration.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										422
									
								
								CommonLibs/Configuration.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,422 @@ | ||||
| /* | ||||
| * 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 | ||||
							
								
								
									
										149
									
								
								CommonLibs/ConfigurationTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								CommonLibs/ConfigurationTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| /* | ||||
| * 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); | ||||
|  | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										210
									
								
								CommonLibs/F16.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								CommonLibs/F16.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| /* | ||||
| * Copyright 2009 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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef F16_H | ||||
| #define F16_H | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <ostream> | ||||
|  | ||||
|  | ||||
|  | ||||
| /** Round a float to the appropriate F16 value. */ | ||||
| inline int32_t _f16_round(float f) | ||||
| { | ||||
| 	if (f>0.0F) return (int32_t)(f+0.5F); | ||||
| 	if (f<0.0F) return (int32_t)(f-0.5F); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /** A class for F15.16 fixed point arithmetic with saturation.  */ | ||||
| class F16 { | ||||
|  | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	int32_t mV; | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	F16() {} | ||||
|  | ||||
| 	F16(int i) { mV = i<<16; } | ||||
| 	F16(float f) { mV = _f16_round(f*65536.0F); } | ||||
| 	F16(double f) { mV = _f16_round((float)f*65536.0F); } | ||||
|  | ||||
| 	int32_t& raw() { return mV; } | ||||
| 	const int32_t& raw() const { return mV; } | ||||
|  | ||||
| 	float f() const { return mV/65536.0F; } | ||||
|  | ||||
| 	//operator float() const { return mV/65536.0F; } | ||||
| 	//operator int() const { return mV>>16; } | ||||
|  | ||||
| 	F16 operator=(float f) | ||||
| 	{ | ||||
| 		mV = _f16_round(f*65536.0F); | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator=(int i) | ||||
| 	{ | ||||
| 		mV = i<<16; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator=(const F16& other) | ||||
| 	{ | ||||
| 		mV = other.mV; | ||||
| 		return mV; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator+(const F16& other) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		retVal.mV = mV + other.mV; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator+=(const F16& other) | ||||
| 	{ | ||||
| 		mV += other.mV; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator-(const F16& other) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		retVal.mV = mV - other.mV; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator-=(const F16& other) | ||||
| 	{ | ||||
| 		mV -= other.mV; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator*(const F16& other) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		int64_t p = (int64_t)mV * (int64_t)other.mV; | ||||
| 		retVal.mV = p>>16; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator*=(const F16& other) | ||||
| 	{ | ||||
| 		int64_t p = (int64_t)mV * (int64_t)other.mV; | ||||
| 		mV = p>>16; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator*(float f) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		retVal.mV = mV * f; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator*=(float f) | ||||
| 	{ | ||||
| 		mV *= f; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator/(const F16& other) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		int64_t pV = (int64_t)mV << 16; | ||||
| 		retVal.mV = pV / other.mV; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator/=(const F16& other) | ||||
| 	{ | ||||
| 		int64_t pV = (int64_t)mV << 16; | ||||
| 		mV = pV / other.mV; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	F16 operator/(float f) const | ||||
| 	{ | ||||
| 		F16 retVal; | ||||
| 		retVal.mV = mV / f; | ||||
| 		return retVal; | ||||
| 	} | ||||
|  | ||||
| 	F16& operator/=(float f) | ||||
| 	{ | ||||
| 		mV /= f; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	bool operator>(const F16& other) const | ||||
| 	{ | ||||
| 		return mV>other.mV; | ||||
| 	} | ||||
|  | ||||
| 	bool operator<(const F16& other) const | ||||
| 	{ | ||||
| 		return mV<other.mV; | ||||
| 	} | ||||
|  | ||||
| 	bool operator==(const F16& other) const | ||||
| 	{ | ||||
| 		return mV==other.mV; | ||||
| 	} | ||||
|  | ||||
| 	bool operator>(float f) const | ||||
| 	{ | ||||
| 		return (mV/65536.0F) > f; | ||||
| 	} | ||||
|  | ||||
| 	bool operator<(float f) const | ||||
| 	{ | ||||
| 		return (mV/65536.0F) < f; | ||||
| 	} | ||||
|  | ||||
| 	bool operator==(float f) const | ||||
| 	{ | ||||
| 		return (mV/65536.0F) == f; | ||||
| 	} | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| inline std::ostream& operator<<(std::ostream& os, const F16& v) | ||||
| { | ||||
| 	os << v.f(); | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										55
									
								
								CommonLibs/F16Test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								CommonLibs/F16Test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
| * Copyright 2009 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 "F16.h" | ||||
|  | ||||
|  | ||||
| #include <iostream> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|  | ||||
| 	F16 a = 2.5; | ||||
| 	F16 b = 1.5; | ||||
| 	F16 c = 2.5 * 1.5; | ||||
| 	F16 d = c + a; | ||||
| 	F16 e = 10; | ||||
| 	cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl; | ||||
|  | ||||
| 	a *= 3; | ||||
| 	b *= 0.3; | ||||
| 	c *= e; | ||||
| 	cout << a << ' ' << b << ' ' << c << ' ' << d << endl; | ||||
|  | ||||
| 	a /= 3; | ||||
| 	b /= 0.3; | ||||
| 	c = d * 0.05; | ||||
| 	cout << a << ' ' << b << ' ' << c << ' ' << d << endl; | ||||
|  | ||||
| 	F16 f = a/d; | ||||
| 	cout << f << ' ' << f+0.5 << endl; | ||||
| } | ||||
| @@ -1,8 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008, 2011 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -55,7 +53,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	Fifo mQ; | ||||
| 	Fifo mQ;	 | ||||
| 	mutable Mutex mLock; | ||||
| 	mutable Signal mWriteSignal; | ||||
|  | ||||
| @@ -162,7 +160,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	Fifo mQ; | ||||
| 	Fifo mQ;	 | ||||
| 	mutable Mutex mLock; | ||||
| 	mutable Signal mWriteSignal; | ||||
|  | ||||
| @@ -258,7 +256,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 { | ||||
| 		// This recurs (and the InterthreadQueue fills up with data) | ||||
| 		// until the read thread's accumulated temporary priority causes it to | ||||
| 		// get a second pre-emptive activation over the writing thread, | ||||
| 		// resulting in bursts of activity by the read thread. | ||||
| 		// resulting in bursts of activity by the read thread.  | ||||
| 		{ ScopedLock lock(mLock); | ||||
| 		  mQ.put(val); | ||||
| 		} | ||||
| @@ -283,7 +281,7 @@ template <class T> class InterthreadQueueWithWait { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	PointerFIFO mQ; | ||||
| 	PointerFIFO mQ;	 | ||||
| 	mutable Mutex mLock; | ||||
| 	mutable Signal mWriteSignal; | ||||
| 	mutable Signal mReadSignal; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| /*
 | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -36,20 +35,14 @@ 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; | ||||
| 		CERR("queue write " << *p); | ||||
| 		COUT("queue write " << *p); | ||||
| 		gQ.write(p); | ||||
| 		q_last_write_val = i; | ||||
| 		if (random()%2) sleep(1); | ||||
| 	} | ||||
| 	p = new int; | ||||
| @@ -63,14 +56,8 @@ void* qReader(void*) | ||||
| 	bool done = false; | ||||
| 	while (!done) { | ||||
| 		int *p = gQ.read(); | ||||
| 		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; | ||||
| 		} | ||||
| 		COUT("queue read " << *p); | ||||
| 		if (*p<0) done=true; | ||||
| 		delete p; | ||||
| 	} | ||||
| 	return NULL; | ||||
| @@ -83,9 +70,8 @@ void* mapWriter(void*) | ||||
| 	for (int i=0; i<20; i++) { | ||||
| 		p = new int; | ||||
| 		*p = i; | ||||
| 		CERR("map write " << *p); | ||||
| 		COUT("map write " << *p); | ||||
| 		gMap.write(i,p); | ||||
| 		m_last_write_val = i; | ||||
| 		if (random()%2) sleep(1); | ||||
| 	} | ||||
| 	return NULL; | ||||
| @@ -95,9 +81,7 @@ void* mapReader(void*) | ||||
| { | ||||
| 	for (int i=0; i<20; i++) { | ||||
| 		int *p = gMap.read(i); | ||||
| 		CERR("map read " << *p); | ||||
| 		assert(*p == i); | ||||
| 		m_last_read_val = *p; | ||||
| 		COUT("map read " << *p); | ||||
| 		// InterthreadMap will delete the pointers
 | ||||
| 		// delete p;
 | ||||
| 	} | ||||
| @@ -125,13 +109,6 @@ 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,7 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -30,25 +29,6 @@ | ||||
| #include "LinkedLists.h" | ||||
|  | ||||
|  | ||||
| PointerFIFO::~PointerFIFO() | ||||
| { | ||||
| 	ListNode *node, *next; | ||||
|  | ||||
| 	node = mHead; | ||||
| 	while (node != NULL) { | ||||
| 		next = node->next(); | ||||
| 		delete node; | ||||
| 		node = next; | ||||
| 	} | ||||
|  | ||||
| 	node = mFreeList; | ||||
| 	while (node != NULL) { | ||||
| 		next = node->next(); | ||||
| 		delete node; | ||||
| 		node = next; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void PointerFIFO::push_front(void* val)	// by pat | ||||
| { | ||||
| 	// Pat added this routine for completeness, but never used or tested. | ||||
|   | ||||
| @@ -1,10 +1,7 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under multiple licenses; see the COPYING file in | ||||
| * the main directory for licensing information for this specific distribuion. | ||||
| * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -73,7 +70,6 @@ class PointerFIFO { | ||||
| 		:mHead(NULL),mTail(NULL),mFreeList(NULL), | ||||
| 		mSize(0) | ||||
| 	{} | ||||
| 	~PointerFIFO(); | ||||
|  | ||||
| 	unsigned size() const { return mSize; } | ||||
| 	unsigned totalSize() const { return 0; }	// Not used in this version. | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| * Copyright 2009 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -29,42 +28,42 @@ | ||||
| #include <iterator> | ||||
| 
 | ||||
| #include "Logger.h" | ||||
| extern "C" { | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include "debug.h" | ||||
| } | ||||
| #include "Configuration.h" | ||||
| 
 | ||||
| #define MYCAT 0 | ||||
| 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[]) | ||||
| { | ||||
| 	struct log_info_cat categories[1]; | ||||
| 	struct log_info linfo; | ||||
| 	categories[MYCAT] = { | ||||
| 		"MYCAT", | ||||
| 		NULL, | ||||
| 		"Whatever", | ||||
| 		LOGL_NOTICE, | ||||
| 		1, | ||||
| 	}; | ||||
| 	linfo.cat = categories; | ||||
| 	linfo.num_cat = ARRAY_SIZE(categories); | ||||
| 	gLogInit("LogTest","NOTICE",LOG_LOCAL7); | ||||
| 
 | ||||
| 	void *tall_ctx = talloc_named_const(NULL, 1, "OsmoTRX context"); | ||||
| 	msgb_talloc_ctx_init(tall_ctx, 0); | ||||
| 
 | ||||
| 	osmo_init_logging2(tall_ctx, &linfo); | ||||
| 
 | ||||
| 	log_set_use_color(osmo_stderr_target, 0); | ||||
| 	log_set_print_filename(osmo_stderr_target, 0); | ||||
| 	log_set_print_level(osmo_stderr_target, 1); | ||||
| 
 | ||||
| 	Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger."; | ||||
| 	Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger."; | ||||
| 	Log(MYCAT, LOGL_NOTICE, __BASE_FILE__, __LINE__).get() << "testing the logger."; | ||||
| 	Log(MYCAT, LOGL_INFO, __BASE_FILE__, __LINE__).get() << "testing the logger."; | ||||
| 	Log(MYCAT, LOGL_DEBUG, __BASE_FILE__, __LINE__).get() << "testing the logger."; | ||||
| 	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(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @@ -1,7 +1,8 @@ | ||||
| /* | ||||
| * Copyright (C) 2018 sysmocom - s.f.m.c. GmbH | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * Copyright 2011, 2012 Range Networks, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -29,33 +30,248 @@ | ||||
| #include <fstream> | ||||
| #include <string> | ||||
| #include <stdarg.h> | ||||
| #include <sys/time.h>	// For gettimeofday | ||||
|  | ||||
| #include "Configuration.h" | ||||
| #include "Logger.h" | ||||
| #include "Threads.h"	// pat added | ||||
|  | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
| // 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; | ||||
|  | ||||
|  | ||||
|  | ||||
| /** Names of the logging levels. */ | ||||
| const char *levelNames[] = { | ||||
| 	"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" | ||||
| }; | ||||
| int numLevels = 8; | ||||
| bool gLogToConsole = 0; | ||||
| FILE *gLogToFile = NULL; | ||||
| Mutex gLogToLock; | ||||
|  | ||||
|  | ||||
| int levelStringToInt(const string& name) | ||||
| { | ||||
| 	return os << ss.str(); | ||||
| 	// Reverse search, since the numerically larger levels are more common. | ||||
| 	for (int i=numLevels-1; i>=0; i--) { | ||||
| 		if (name == levelNames[i]) return i; | ||||
| 	} | ||||
|  | ||||
| 	// Common substitutions. | ||||
| 	if (name=="INFORMATION") return 6; | ||||
| 	if (name=="WARN") return 4; | ||||
| 	if (name=="ERROR") return 3; | ||||
| 	if (name=="CRITICAL") return 2; | ||||
| 	if (name=="EMERGENCY") return 0; | ||||
|  | ||||
| 	// Unknown level. | ||||
| 	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; | ||||
| } | ||||
|  | ||||
|  | ||||
| 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() | ||||
| { | ||||
| 	int old_state; | ||||
| 	int mlen = mStream.str().size(); | ||||
| 	int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 	const char *fmt = neednl ? "%s\n" : "%s"; | ||||
|  | ||||
| 	log_mutex_lock_canceldisable(&old_state); | ||||
| 	LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str()); | ||||
| 	log_mutex_unlock_canceldisable(old_state); | ||||
| 	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_CRIT) { | ||||
| 		if (sLoggerInited) addAlarm(mStream.str().c_str()); | ||||
| 		cerr << mStream.str() << endl; | ||||
| 	} | ||||
| 	// Current logging level was already checked by the macro. | ||||
| 	// So just log. | ||||
| 	syslog(mPriority, "%s", mStream.str().c_str()); | ||||
| 	// pat added for easy debugging. | ||||
| 	if (gLogToConsole||gLogToFile) { | ||||
| 		int mlen = mStream.str().size(); | ||||
| 		int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 		gLogToLock.lock(); | ||||
| 		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); | ||||
| 		} | ||||
| 		gLogToLock.unlock(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| Log::Log(const char* name, const char* level, int facility) | ||||
| { | ||||
| 	mDummyInit = true; | ||||
| 	gLogInit(name, level, facility); | ||||
| } | ||||
|  | ||||
|  | ||||
| ostringstream& Log::get() | ||||
| { | ||||
| 	assert(mPriority<numLevels); | ||||
| 	mStream << levelNames[mPriority] <<  ' '; | ||||
| 	return mStream; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void gLogInit(const char* name, const char* level, int facility) | ||||
| { | ||||
| 	// Set the level if one has been specified. | ||||
| 	if (level) { | ||||
| 		gConfig.set("Log.Level",level); | ||||
| 	} | ||||
|  | ||||
| 	// Pat added, tired of the syslog facility. | ||||
| 	// 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==0 && 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); | ||||
| 	vsyslog(level | LOG_USER, fmt, args); | ||||
| 	va_end(args); | ||||
| } | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -25,44 +23,72 @@ | ||||
|  | ||||
| */ | ||||
|  | ||||
| // (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h. | ||||
| // This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file. | ||||
| #ifdef WARNING | ||||
| #undef WARNING | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGGER_H | ||||
| #define LOGGER_H | ||||
|  | ||||
| #include <syslog.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <sstream> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <string> | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/logging.h> | ||||
| #include "debug.h" | ||||
| } | ||||
| #define _LOG(level) \ | ||||
| 	Log(LOG_##level).get() << pthread_self() \ | ||||
| 	<< timestr() << " " __FILE__  ":"  << __LINE__ << ":" << __FUNCTION__ << ": " | ||||
|  | ||||
| /* Translation for old log statements */ | ||||
| #ifndef LOGL_ALERT | ||||
| #define LOGL_ALERT LOGL_FATAL | ||||
| #endif | ||||
| #ifndef LOGL_ERR | ||||
| #define LOGL_ERR LOGL_ERROR | ||||
| #endif | ||||
| #ifndef LOGL_WARNING | ||||
| #define LOGL_WARNING LOGL_NOTICE | ||||
| #define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) | ||||
|  | ||||
| #ifdef NDEBUG | ||||
| #define LOG(wLevel) \ | ||||
| 	if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel) | ||||
| #else | ||||
| #define LOG(wLevel) \ | ||||
| 	if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel) | ||||
| #endif | ||||
|  | ||||
| #define LOG(level) \ | ||||
| 	Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
| // 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 | ||||
|  | ||||
| #define LOGC(category, level) \ | ||||
| 	Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
| // (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 LOGLV(category, level) \ | ||||
| 	Log(category, level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
| #define LOGCHAN(chan, category, level) \ | ||||
| 	Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "][chan=" << chan << "] " | ||||
| #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() | ||||
| #include "Utils.h" | ||||
|  | ||||
| /** | ||||
| 	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. | ||||
| */ | ||||
| @@ -72,27 +98,41 @@ class Log { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	std::ostringstream mStream;	///< This is where we buffer up the log entry. | ||||
| 	int mCategory;			///< Priority of current report. | ||||
| 	int mPriority;			///< Category of current report. | ||||
| 	const char *filename;		///< Source File Name of current report. | ||||
| 	int line;			///< Line number in source file of current report. | ||||
| 	std::ostringstream mStream;		///< This is where we buffer up the log entry. | ||||
| 	int mPriority;					///< Priority of current report. | ||||
| 	bool mDummyInit; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Log(int wCategory, int wPriority, const char* filename, int line) | ||||
| 		: mCategory(wCategory), mPriority(wPriority), | ||||
| 		  filename(filename), line(line) | ||||
| 	Log(int wPriority) | ||||
| 		:mPriority(wPriority), mDummyInit(false) | ||||
| 	{ } | ||||
|  | ||||
| 	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;	// Pat added for easy debugging. | ||||
|  | ||||
|  | ||||
|  | ||||
| std::list<std::string> gGetLoggerAlarms();		///< Get a copy of the recent alarm list. | ||||
|  | ||||
|  | ||||
| /**@ 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))); | ||||
| //@} | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -22,35 +22,92 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	example.config \ | ||||
| 	README.common | ||||
|  | ||||
| noinst_LTLIBRARIES = libcommon.la | ||||
|  | ||||
| libcommon_la_SOURCES = \ | ||||
| 	BitVector.cpp \ | ||||
| 	LinkedLists.cpp \ | ||||
| 	Sockets.cpp \ | ||||
| 	Threads.cpp \ | ||||
| 	Timeval.cpp \ | ||||
| 	Reporting.cpp \ | ||||
| 	Logger.cpp \ | ||||
| 	Utils.cpp \ | ||||
| 	trx_rate_ctr.cpp \ | ||||
| 	trx_vty.c \ | ||||
| 	debug.c | ||||
| libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS) | ||||
| 	Configuration.cpp \ | ||||
| 	sqlite3util.cpp \ | ||||
| 	URLEncode.cpp \ | ||||
| 	Utils.cpp | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	BitVectorTest \ | ||||
| 	InterthreadTest \ | ||||
| 	SocketsTest \ | ||||
| 	TimevalTest \ | ||||
| 	RegexpTest \ | ||||
| 	VectorTest \ | ||||
| 	ConfigurationTest \ | ||||
| 	LogTest \ | ||||
| 	URLEncodeTest \ | ||||
| 	F16Test | ||||
|  | ||||
| #	ReportingTest  | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	BitVector.h \ | ||||
| 	PRBS.h \ | ||||
| 	Interthread.h \ | ||||
| 	LinkedLists.h \ | ||||
| 	Sockets.h \ | ||||
| 	Threads.h \ | ||||
| 	Timeval.h \ | ||||
| 	Regexp.h \ | ||||
| 	Vector.h \ | ||||
| 	Logger.h \ | ||||
| 	Configuration.h \ | ||||
| 	Reporting.h \ | ||||
| 	F16.h \ | ||||
| 	URLEncode.h \ | ||||
| 	Utils.h \ | ||||
| 	trx_rate_ctr.h \ | ||||
| 	trx_vty.h \ | ||||
| 	debug.h \ | ||||
| 	osmo_signal.h \ | ||||
| 	config_defs.h | ||||
| 	Logger.h \ | ||||
| 	sqlite3util.h | ||||
|  | ||||
| URLEncodeTest_SOURCES = URLEncodeTest.cpp | ||||
| URLEncodeTest_LDADD = libcommon.la | ||||
|  | ||||
| BitVectorTest_SOURCES = BitVectorTest.cpp | ||||
| BitVectorTest_LDADD = libcommon.la $(SQLITE_LA) | ||||
|  | ||||
| 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 $(SQLITE_LA) | ||||
|  | ||||
| RegexpTest_SOURCES = RegexpTest.cpp | ||||
| RegexpTest_LDADD = libcommon.la | ||||
|  | ||||
| ConfigurationTest_SOURCES = ConfigurationTest.cpp | ||||
| ConfigurationTest_LDADD = libcommon.la 	$(SQLITE_LA) | ||||
|  | ||||
| # ReportingTest_SOURCES = ReportingTest.cpp | ||||
| # ReportingTest_LDADD = libcommon.la $(SQLITE_LA) | ||||
|  | ||||
| LogTest_SOURCES = LogTest.cpp | ||||
| LogTest_LDADD = libcommon.la $(SQLITE_LA) | ||||
|  | ||||
| F16Test_SOURCES = F16Test.cpp | ||||
|  | ||||
| MOSTLYCLEANFILES += testSource testDestination | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										111
									
								
								CommonLibs/MemoryLeak.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								CommonLibs/MemoryLeak.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
| * Copyright 2011 Range Networks, Inc. | ||||
| * All Rights Reserved. | ||||
| * | ||||
| * This software is distributed under multiple licenses; | ||||
| * see the COPYING file in the main directory for licensing | ||||
| * information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
|     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. | ||||
| */ | ||||
| #ifndef _MEMORYLEAK_ | ||||
| #define _MEMORYLEAK_ 1 | ||||
| #include <map> | ||||
| #include "ScalarTypes.h" | ||||
| #include "Logger.h" | ||||
|  | ||||
| namespace Utils { | ||||
|  | ||||
| struct MemStats { | ||||
| 	// Enumerates the classes that are checked. | ||||
| 	// Redundancies are ok, for example, we check BitVector and also | ||||
| 	// several descendants of BitVector. | ||||
| 	enum MemoryNames { | ||||
| 		mZeroIsUnused, | ||||
| 		mVector, | ||||
| 		mVectorData, | ||||
| 		mBitVector, | ||||
| 		mByteVector, | ||||
| 		mByteVectorData, | ||||
| 		mRLCRawBlock, | ||||
| 		mRLCUplinkDataBlock, | ||||
| 		mRLCMessage, | ||||
| 		mRLCMsgPacketDownlinkDummyControlBlock,	// Redundant with RLCMessage | ||||
| 		mTBF, | ||||
| 		mLlcEngine, | ||||
| 		mSgsnDownlinkMsg, | ||||
| 		mRachInfo, | ||||
| 		mPdpPdu, | ||||
| 		mFECDispatchInfo, | ||||
| 		mL3Frame, | ||||
| 		msignalVector, | ||||
| 		mSoftVector, | ||||
| 		mScramblingCode, | ||||
| 		mURlcDownSdu, | ||||
| 		mURlcPdu, | ||||
| 		// Must be last: | ||||
| 		mMax, | ||||
| 	}; | ||||
| 	int mMemTotal[mMax];	// In elements, not bytes. | ||||
| 	int mMemNow[mMax]; | ||||
| 	const char *mMemName[mMax]; | ||||
| 	MemStats(); | ||||
| 	void memChkNew(MemoryNames memIndex, const char *id); | ||||
| 	void memChkDel(MemoryNames memIndex, const char *id); | ||||
| 	void text(std::ostream &os); | ||||
| 	// We would prefer to use an unordered_map, but that requires special compile switches. | ||||
| 	// What a super great language. | ||||
| 	typedef std::map<std::string,Int_z> MemMapType; | ||||
| 	MemMapType mMemMap; | ||||
| }; | ||||
| extern struct MemStats gMemStats; | ||||
| extern int gMemLeakDebug; | ||||
|  | ||||
| // This is a memory leak detector. | ||||
| // Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors, | ||||
| // or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class | ||||
| // as an ancestor to the class to be memory leak checked. | ||||
|  | ||||
| struct MemLabel { | ||||
| 	std::string mccKey; | ||||
| 	virtual ~MemLabel() { | ||||
| 		Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| #if RN_DISABLE_MEMORY_LEAK_TEST | ||||
| #define RN_MEMCHKNEW(type) | ||||
| #define RN_MEMCHKDEL(type) | ||||
| #define RN_MEMLOG(type,ptr) | ||||
| #define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \ | ||||
| 	struct checkerClass {}; | ||||
| #else | ||||
|  | ||||
| #define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); } | ||||
| #define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); } | ||||
|  | ||||
| #define RN_MEMLOG(type,ptr) { \ | ||||
| 	static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \ | ||||
| 	(ptr)->/* MemCheck##type:: */ mccKey = key; \ | ||||
| 	Utils::gMemStats.mMemMap[key]++; \ | ||||
| 	} | ||||
|  | ||||
| // TODO: The above assumes that checkclass is MemCheck ## subClass | ||||
| #define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \ | ||||
| 	struct checkerClass : public virtual Utils::MemLabel { \ | ||||
| 	    checkerClass() { RN_MEMCHKNEW(subClass); } \ | ||||
| 		virtual ~checkerClass() { \ | ||||
| 			RN_MEMCHKDEL(subClass); \ | ||||
| 		} \ | ||||
| 	}; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| }	// namespace Utils | ||||
|  | ||||
| #endif | ||||
| @@ -1,112 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
							
								
								
									
										64
									
								
								CommonLibs/Regexp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								CommonLibs/Regexp.h
									
									
									
									
									
										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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef REGEXPW_H | ||||
| #define REGEXPW_H | ||||
|  | ||||
| #include <regex.h> | ||||
| #include <iostream> | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
|  | ||||
| class Regexp { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	regex_t mRegex; | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Regexp(const char* regexp, int flags=REG_EXTENDED) | ||||
| 	{ | ||||
| 		int result = regcomp(&mRegex, regexp, flags); | ||||
| 		if (result) { | ||||
| 			char msg[256]; | ||||
| 			regerror(result,&mRegex,msg,255); | ||||
| 			std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl; | ||||
| 			abort(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	~Regexp() | ||||
| 		{ regfree(&mRegex); } | ||||
|  | ||||
| 	bool match(const char *text, int flags=0) const | ||||
| 		{ return regexec(&mRegex, text, 0, NULL, flags)==0; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
| @@ -1,7 +1,6 @@ | ||||
| /*
 | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -26,36 +25,24 @@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #include "BitVector.h" | ||||
| #include "Regexp.h" | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	BitVector v5("000011110000"); | ||||
| 	int r1 = v5.peekField(0,8); | ||||
| 	int r2 = v5.peekField(4,4); | ||||
| 	int r3 = v5.peekField(4,8); | ||||
| 	cout << r1 <<  ' ' << r2 << ' ' << r3 << endl; | ||||
| 	cout << v5 << endl; | ||||
| 	v5.fillField(0,0xa,4); | ||||
| 	int r4 = v5.peekField(0,8); | ||||
| 	cout << v5 << endl; | ||||
| 	cout << r4 << endl; | ||||
| 
 | ||||
| 	v5.reverse8(); | ||||
| 	cout << v5 << endl; | ||||
| 	Regexp email("^[[:graph:]]+@[[:graph:]]+ "); | ||||
| 	Regexp simple("^dburgess@"); | ||||
| 
 | ||||
| 	const char text1[] = "dburgess@jcis.net test message"; | ||||
| 	const char text2[] = "no address text message"; | ||||
| 
 | ||||
| 	unsigned char ts[9] = "abcdefgh"; | ||||
| 	BitVector tp(70); | ||||
| 	cout << "ts=" << ts << endl; | ||||
| 	tp.unpack(ts); | ||||
| 	cout << "tp=" << tp << endl; | ||||
| 	tp.pack(ts); | ||||
| 	cout << "ts=" << ts << endl; | ||||
| 	cout << email.match(text1) << " " << text1 << endl; | ||||
| 	cout << email.match(text2) << " " << text2 << endl; | ||||
| 
 | ||||
| 	cout << simple.match(text1) << " " << text1 << endl; | ||||
| 	cout << simple.match(text2) << " " << text2 << endl; | ||||
| } | ||||
							
								
								
									
										145
									
								
								CommonLibs/Reporting.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								CommonLibs/Reporting.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| /**@file Module for performance-reporting mechanisms. */ | ||||
| /* | ||||
| * Copyright 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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
| #include "Reporting.h" | ||||
| #include "Logger.h" | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static const char* createReportingTable = { | ||||
| 	"CREATE TABLE IF NOT EXISTS REPORTING (" | ||||
| 		"NAME TEXT UNIQUE NOT NULL, " | ||||
| 		"VALUE INTEGER DEFAULT 0, " | ||||
| 		"CLEAREDTIME INTEGER NOT NULL, " | ||||
| 		"UPDATETIME INTEGER DEFAULT 0 " | ||||
| 	")" | ||||
| }; | ||||
|  | ||||
|  | ||||
| ReportingTable::ReportingTable(const char* filename) | ||||
| { | ||||
| 	gLogEarly(LOG_INFO | mFacility, "opening reporting table from path %s", filename); | ||||
| 	// Connect to the database. | ||||
| 	int rc = sqlite3_open(filename,&mDB); | ||||
| 	if (rc) { | ||||
| 		gLogEarly(LOG_EMERG | mFacility, "cannot open reporting database at %s, error message: %s", filename, sqlite3_errmsg(mDB)); | ||||
| 		sqlite3_close(mDB); | ||||
| 		mDB = NULL; | ||||
| 		return; | ||||
| 	} | ||||
| 	// Create the table, if needed. | ||||
| 	if (!sqlite3_command(mDB,createReportingTable)) { | ||||
| 		gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| bool ReportingTable::create(const char* paramName) | ||||
| { | ||||
| 	char cmd[200]; | ||||
| 	sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL)); | ||||
| 	if (!sqlite3_command(mDB,cmd)) { | ||||
| 		gLogEarly(LOG_CRIT|mFacility, "cannot create reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| bool ReportingTable::incr(const char* paramName) | ||||
| { | ||||
| 	char cmd[200]; | ||||
| 	sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName); | ||||
| 	if (!sqlite3_command(mDB,cmd)) { | ||||
| 		gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| bool ReportingTable::max(const char* paramName, unsigned newVal) | ||||
| { | ||||
| 	char cmd[200]; | ||||
| 	sprintf(cmd,"UPDATE REPORTING SET VALUE=MAX(VALUE,%u), UPDATETIME=%ld WHERE NAME=\"%s\"", newVal, time(NULL), paramName); | ||||
| 	if (!sqlite3_command(mDB,cmd)) { | ||||
| 		gLogEarly(LOG_CRIT|mFacility, "cannot maximize reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool ReportingTable::clear(const char* paramName) | ||||
| { | ||||
| 	char cmd[200]; | ||||
| 	sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName); | ||||
| 	if (!sqlite3_command(mDB,cmd)) { | ||||
| 		gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex) | ||||
| { | ||||
| 	size_t sz = strlen(baseName); | ||||
| 	for (unsigned i = minIndex; i<=maxIndex; i++) { | ||||
| 		char name[sz+10]; | ||||
| 		sprintf(name,"%s.%u",baseName,i); | ||||
| 		if (!create(name)) return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ReportingTable::incr(const char* baseName, unsigned index) | ||||
| { | ||||
| 	char name[strlen(baseName)+10]; | ||||
| 	sprintf(name,"%s.%u",baseName,index); | ||||
| 	return incr(name); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool ReportingTable::max(const char* baseName, unsigned index, unsigned newVal) | ||||
| { | ||||
| 	char name[strlen(baseName)+10]; | ||||
| 	sprintf(name,"%s.%u",baseName,index); | ||||
| 	return max(name,newVal); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool ReportingTable::clear(const char* baseName, unsigned index) | ||||
| { | ||||
| 	char name[strlen(baseName)+10]; | ||||
| 	sprintf(name,"%s.%u",baseName,index); | ||||
| 	return clear(name); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										86
									
								
								CommonLibs/Reporting.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								CommonLibs/Reporting.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| /**@file Module for performance-reporting mechanisms. */ | ||||
| /* | ||||
| * Copyright 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 REPORTING_H | ||||
| #define REPORTING_H | ||||
|  | ||||
| #include <sqlite3util.h> | ||||
| #include <ostream> | ||||
|  | ||||
|  | ||||
| /** | ||||
| 	Collect performance statistics into a database. | ||||
| 	Parameters are counters or max/min trackers, all integer. | ||||
| */ | ||||
| class ReportingTable { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	sqlite3* mDB;				///< database connection | ||||
| 	int mFacility;				///< rsyslogd facility | ||||
|  | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** | ||||
| 		Open the database connection; | ||||
| 		create the table if it does not exist yet. | ||||
| 	*/ | ||||
| 	ReportingTable(const char* filename); | ||||
|  | ||||
| 	/** Create a new parameter. */ | ||||
| 	bool create(const char* paramName); | ||||
|  | ||||
| 	/** Create an indexed parameter set. */ | ||||
| 	bool create(const char* baseBame, unsigned minIndex, unsigned maxIndex); | ||||
|  | ||||
| 	/** Increment a counter. */ | ||||
| 	bool incr(const char* paramName); | ||||
|  | ||||
| 	/** Increment an indexed counter. */ | ||||
| 	bool incr(const char* baseName, unsigned index); | ||||
|  | ||||
| 	/** Take a max of a parameter. */ | ||||
| 	bool max(const char* paramName, unsigned newVal); | ||||
|  | ||||
| 	/** Take a max of an indexed parameter. */ | ||||
| 	bool max(const char* paramName, unsigned index, unsigned newVal); | ||||
|  | ||||
| 	/** Clear a value.  */ | ||||
| 	bool clear(const char* paramName); | ||||
|  | ||||
| 	/** Clear an indexed value.  */ | ||||
| 	bool clear(const char* paramName, unsigned index); | ||||
|  | ||||
| 	/** Dump the database to a stream. */ | ||||
| 	void dump(std::ostream&) const; | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
							
								
								
									
										136
									
								
								CommonLibs/ScalarTypes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								CommonLibs/ScalarTypes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| /* | ||||
| * Copyright 2011 Range Networks, Inc. | ||||
| * All Rights Reserved. | ||||
| * | ||||
| * This software is distributed under multiple licenses; | ||||
| * see the COPYING file in the main directory for licensing | ||||
| * information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
|     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. | ||||
| */ | ||||
|  | ||||
| #ifndef SCALARTYPES_H | ||||
| #define SCALARTYPES_H | ||||
| #include <iostream>	// For size_t | ||||
| #include <stdint.h> | ||||
| //#include "GSMCommon.h"	// Was included for Z100Timer | ||||
|  | ||||
| // We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b; | ||||
| #define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \ | ||||
| 	Classname() : value(Init) {} \ | ||||
| 	Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \ | ||||
| 	operator Basetype(void) const { return value; }		/* Converts from basetype. */ \ | ||||
| 	Basetype operator=(Basetype wvalue) { return value = wvalue; } \ | ||||
| 	Basetype* operator&() { return &value; } | ||||
|  | ||||
| #define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \ | ||||
| 	Basetype operator++() { return ++value; } \ | ||||
| 	Basetype operator++(int) { return value++; } \ | ||||
| 	Basetype operator--() { return --value; } \ | ||||
| 	Basetype operator--(int) { return value--; } \ | ||||
| 	Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \ | ||||
| 	Basetype operator-=(Basetype wvalue) { return value = value - wvalue; } | ||||
|  | ||||
| #define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \ | ||||
| 	_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \ | ||||
| 	_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) | ||||
|  | ||||
|  | ||||
| #define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \ | ||||
| 	template <Basetype Init> \ | ||||
| 	struct Classname_i { \ | ||||
| 		Basetype value; \ | ||||
| 		_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \ | ||||
| 	}; \ | ||||
| 	typedef Classname_i<0> Classname_z; | ||||
|  | ||||
|  | ||||
| // Usage: | ||||
| // Where 'classname' is one of the types listed below, then: | ||||
| // 		classname_z specifies a zero initialized type; | ||||
| // 		classname_i<value> initializes the type to the specified value. | ||||
| // We also define Float_z. | ||||
| _DECLARE_SCALAR_TYPE(Int_i, 	Int_z,  	int) | ||||
| _DECLARE_SCALAR_TYPE(Char_i,	Char_z, 	signed char) | ||||
| _DECLARE_SCALAR_TYPE(Int16_i,	Int16_z,	int16_t) | ||||
| _DECLARE_SCALAR_TYPE(Int32_i,	Int32_z,	int32_t) | ||||
| _DECLARE_SCALAR_TYPE(UInt_i,  	UInt_z,  	unsigned) | ||||
| _DECLARE_SCALAR_TYPE(UChar_i,  	UChar_z,  	unsigned char) | ||||
| _DECLARE_SCALAR_TYPE(UInt16_i,	UInt16_z,	uint16_t) | ||||
| _DECLARE_SCALAR_TYPE(UInt32_i,	UInt32_z,	uint32_t) | ||||
| _DECLARE_SCALAR_TYPE(Size_t_i,	Size_t_z,	size_t) | ||||
|  | ||||
| // Bool is special because it cannot accept some arithmetic funcs | ||||
| //_DECLARE_SCALAR_TYPE(Bool_i,  	Bool_z, 	bool) | ||||
| template <bool Init> | ||||
| struct Bool_i { | ||||
| 	bool value; | ||||
| 	_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init) | ||||
| }; | ||||
| typedef Bool_i<0> Bool_z; | ||||
|  | ||||
| // float is special, because C++ does not permit the template initalization: | ||||
| struct Float_z { | ||||
| 	float value; | ||||
| 	_INITIALIZED_SCALAR_FUNCS(Float_z,float,0) | ||||
| }; | ||||
| struct Double_z { | ||||
| 	double value; | ||||
| 	_INITIALIZED_SCALAR_FUNCS(Double_z,double,0) | ||||
| }; | ||||
|  | ||||
|  | ||||
| class ItemWithValueAndWidth { | ||||
| 	public: | ||||
| 	virtual unsigned getValue() const = 0; | ||||
| 	virtual unsigned getWidth() const = 0; | ||||
| }; | ||||
|  | ||||
| // A Range Networks Field with a specified width. | ||||
| // See RLCMessages.h for examples. | ||||
| template <int Width=32, unsigned Init=0> | ||||
| class Field_i : public ItemWithValueAndWidth | ||||
| { | ||||
| 	public: | ||||
| 	unsigned value; | ||||
| 	_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init) | ||||
| 	unsigned getWidth() const { return Width; } | ||||
| 	unsigned getValue() const { return value; } | ||||
| }; | ||||
|  | ||||
| // Synonym for Field_i, but no way to do it. | ||||
| template <int Width, unsigned Init=0> | ||||
| class Field_z : public ItemWithValueAndWidth | ||||
| { | ||||
| 	public: | ||||
| 	unsigned value; | ||||
| 	_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init) | ||||
| 	unsigned getWidth() const { return Width; } | ||||
| 	unsigned getValue() const { return value; } | ||||
| }; | ||||
|  | ||||
| // This is an uninitialized field. | ||||
| template <int Width=32, unsigned Init=0> | ||||
| class Field : public ItemWithValueAndWidth | ||||
| { | ||||
| 	public: | ||||
| 	unsigned value; | ||||
| 	_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init) | ||||
| 	unsigned getWidth() const { return Width; } | ||||
| 	unsigned getValue() const { return value; } | ||||
| }; | ||||
|  | ||||
|  | ||||
| // A Z100Timer with an initial value specified. | ||||
| //template <int Init> | ||||
| //class Z100Timer_i : public GSM::Z100Timer { | ||||
| //	public: | ||||
| //	Z100Timer_i() : GSM::Z100Timer(Init) {} | ||||
| //}; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										337
									
								
								CommonLibs/Sockets.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								CommonLibs/Sockets.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| /* | ||||
| * Copyright 2008, 2010 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 <config.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <cstdio> | ||||
| #include <sys/select.h> | ||||
|  | ||||
| #include "Threads.h" | ||||
| #include "Sockets.h" | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort) | ||||
| { | ||||
| 	assert(address); | ||||
| 	assert(hostAndPort); | ||||
| 	char *copy = strdup(hostAndPort); | ||||
| 	char *colon = strchr(copy,':'); | ||||
| 	if (!colon) return false; | ||||
| 	*colon = '\0'; | ||||
| 	char *host = copy; | ||||
| 	unsigned port = strtol(colon+1,NULL,10); | ||||
| 	bool retVal = resolveAddress(address,host,port); | ||||
| 	free(copy); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
| bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port) | ||||
| { | ||||
| 	assert(address); | ||||
| 	assert(host); | ||||
| 	// FIXME -- Need to ignore leading/trailing spaces in hostname. | ||||
| 	struct hostent *hp; | ||||
| 	int h_errno_local; | ||||
| #ifdef HAVE_GETHOSTBYNAME2_R | ||||
| 	struct hostent hostData; | ||||
| 	char tmpBuffer[2048]; | ||||
|  | ||||
| 	// There are different flavors of gethostbyname_r(), but | ||||
| 	// latest Linux use the following form: | ||||
| 	if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) { | ||||
| 		CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local)); | ||||
| 		return false; | ||||
| 	} | ||||
| #else | ||||
| 	static Mutex sGethostbynameMutex; | ||||
| 	// gethostbyname() is NOT thread-safe, so we should use a mutex here. | ||||
| 	// Ideally it should be a global mutex for all non thread-safe socket | ||||
| 	// operations and it should protect access to variables such as | ||||
| 	// global h_errno. | ||||
| 	sGethostbynameMutex.lock(); | ||||
| 	hp = gethostbyname(host); | ||||
| 	h_errno_local = h_errno; | ||||
| 	sGethostbynameMutex.unlock(); | ||||
| #endif | ||||
|  	if (hp==NULL) { | ||||
| 		CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (hp->h_addrtype != AF_INET) { | ||||
| 		CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET"); | ||||
|  		return false; | ||||
|  	} | ||||
| 	address->sin_family = hp->h_addrtype; | ||||
| 	assert(sizeof(address->sin_addr) == hp->h_length); | ||||
| 	memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length); | ||||
| 	address->sin_port = htons(port); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| DatagramSocket::DatagramSocket() | ||||
| { | ||||
| 	memset(mDestination, 0, sizeof(mDestination)); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| void DatagramSocket::nonblocking() | ||||
| { | ||||
| 	fcntl(mSocketFD,F_SETFL,O_NONBLOCK); | ||||
| } | ||||
|  | ||||
| void DatagramSocket::blocking() | ||||
| { | ||||
| 	fcntl(mSocketFD,F_SETFL,0); | ||||
| } | ||||
|  | ||||
| void DatagramSocket::close() | ||||
| { | ||||
| 	::close(mSocketFD); | ||||
| } | ||||
|  | ||||
|  | ||||
| DatagramSocket::~DatagramSocket() | ||||
| { | ||||
| 	close(); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| int DatagramSocket::write( const char * message, size_t length ) | ||||
| { | ||||
| 	assert(length<=MAX_UDP_LENGTH); | ||||
| 	int retVal = sendto(mSocketFD, message, length, 0, | ||||
| 		(struct sockaddr *)mDestination, addressSize()); | ||||
| 	if (retVal == -1 ) perror("DatagramSocket::write() failed"); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
| int DatagramSocket::writeBack( const char * message, size_t length ) | ||||
| { | ||||
| 	assert(length<=MAX_UDP_LENGTH); | ||||
| 	int retVal = sendto(mSocketFD, message, length, 0, | ||||
| 		(struct sockaddr *)mSource, addressSize()); | ||||
| 	if (retVal == -1 ) perror("DatagramSocket::write() failed"); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| int DatagramSocket::write( const char * message) | ||||
| { | ||||
| 	size_t length=strlen(message)+1; | ||||
| 	return write(message,length); | ||||
| } | ||||
|  | ||||
| int DatagramSocket::writeBack( const char * message) | ||||
| { | ||||
| 	size_t length=strlen(message)+1; | ||||
| 	return writeBack(message,length); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length ) | ||||
| { | ||||
| 	assert(length<=MAX_UDP_LENGTH); | ||||
| 	int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize()); | ||||
| 	if (retVal == -1 ) perror("DatagramSocket::send() failed"); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
| int DatagramSocket::send(const struct sockaddr* dest, const char * message) | ||||
| { | ||||
| 	size_t length=strlen(message)+1; | ||||
| 	return send(dest,message,length); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| int DatagramSocket::read(char* buffer) | ||||
| { | ||||
| 	socklen_t temp_len = sizeof(mSource); | ||||
| 	int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0, | ||||
| 	    (struct sockaddr*)&mSource,&temp_len); | ||||
| 	if ((length==-1) && (errno!=EAGAIN)) { | ||||
| 		perror("DatagramSocket::read() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
| 	return length; | ||||
| } | ||||
|  | ||||
|  | ||||
| int DatagramSocket::read(char* buffer, unsigned timeout) | ||||
| { | ||||
| 	fd_set fds; | ||||
| 	FD_ZERO(&fds); | ||||
| 	FD_SET(mSocketFD,&fds); | ||||
| 	struct timeval tv; | ||||
| 	tv.tv_sec = timeout/1000; | ||||
| 	tv.tv_usec = (timeout%1000)*1000; | ||||
| 	int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv); | ||||
| 	if (sel<0) { | ||||
| 		perror("DatagramSocket::read() select() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
| 	if (sel==0) return -1; | ||||
| 	if (FD_ISSET(mSocketFD,&fds)) return read(buffer); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| } | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort, | ||||
|           	 const char * wDestIP, unsigned short wDestPort ) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| 	destination(wDestPort, wDestIP); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) | ||||
| { | ||||
| 	resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort ); | ||||
| } | ||||
|  | ||||
|  | ||||
| void UDPSocket::open(unsigned short localPort) | ||||
| { | ||||
| 	// create | ||||
| 	mSocketFD = socket(AF_INET,SOCK_DGRAM,0); | ||||
| 	if (mSocketFD<0) { | ||||
| 		perror("socket() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
|  | ||||
| 	// pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes. | ||||
| 	int on = 1; | ||||
| 	setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | ||||
|  | ||||
|  | ||||
| 	// bind | ||||
| 	struct sockaddr_in address; | ||||
| 	size_t length = sizeof(address); | ||||
| 	bzero(&address,length); | ||||
| 	address.sin_family = AF_INET; | ||||
| 	address.sin_addr.s_addr = INADDR_ANY; | ||||
| 	address.sin_port = htons(localPort); | ||||
| 	if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { | ||||
| 		perror("bind() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| unsigned short UDPSocket::port() const | ||||
| { | ||||
| 	struct sockaddr_in name; | ||||
| 	socklen_t nameSize = sizeof(name); | ||||
| 	int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize); | ||||
| 	if (retVal==-1) throw SocketError(); | ||||
| 	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 | ||||
							
								
								
									
										193
									
								
								CommonLibs/Sockets.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								CommonLibs/Sockets.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | ||||
| /* | ||||
| * Copyright 2008, 2010 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/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef SOCKETS_H | ||||
| #define SOCKETS_H | ||||
|  | ||||
| #include <arpa/inet.h> | ||||
| #include <netdb.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/un.h> | ||||
| #include <errno.h> | ||||
| #include <list> | ||||
| #include <assert.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #define MAX_UDP_LENGTH 1500 | ||||
|  | ||||
| /** A function to resolve IP host names. */ | ||||
| bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port); | ||||
|  | ||||
| /** Resolve an address of the form "<host>:<port>". */ | ||||
| bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort); | ||||
|  | ||||
| /** An exception to throw when a critical socket operation fails. */ | ||||
| class SocketError {}; | ||||
| #define SOCKET_ERROR {throw SocketError(); } | ||||
|  | ||||
| /** Abstract class for connectionless sockets. */ | ||||
| class DatagramSocket { | ||||
|  | ||||
| protected: | ||||
|  | ||||
| 	int mSocketFD;				///< underlying file descriptor | ||||
| 	char mDestination[256];		///< address to which packets are sent | ||||
| 	char mSource[256];		///< return address of most recent received packet | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/** An almost-does-nothing constructor. */ | ||||
| 	DatagramSocket(); | ||||
|  | ||||
| 	virtual ~DatagramSocket(); | ||||
|  | ||||
| 	/** Return the address structure size for this socket type. */ | ||||
| 	virtual size_t addressSize() const = 0; | ||||
|  | ||||
| 	/** | ||||
| 		Send a binary packet. | ||||
| 		@param buffer The data bytes to send to mDestination. | ||||
| 		@param length Number of bytes to send, or strlen(buffer) if defaulted to -1. | ||||
| 		@return number of bytes written, or -1 on error. | ||||
| 	*/ | ||||
| 	int write( const char * buffer, size_t length); | ||||
|  | ||||
| 	/** | ||||
| 		Send a C-style string packet. | ||||
| 		@param buffer The data bytes to send to mDestination. | ||||
| 		@return number of bytes written, or -1 on error. | ||||
| 	*/ | ||||
| 	int write( const char * buffer); | ||||
|  | ||||
| 	/** | ||||
| 		Send a binary packet. | ||||
| 		@param buffer The data bytes to send to mSource. | ||||
| 		@param length Number of bytes to send, or strlen(buffer) if defaulted to -1. | ||||
| 		@return number of bytes written, or -1 on error. | ||||
| 	*/ | ||||
| 	int writeBack(const char * buffer, size_t length); | ||||
|  | ||||
| 	/** | ||||
| 		Send a C-style string packet. | ||||
| 		@param buffer The data bytes to send to mSource. | ||||
| 		@return number of bytes written, or -1 on error. | ||||
| 	*/ | ||||
| 	int writeBack(const char * buffer); | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 		Receive a packet. | ||||
| 		@param buffer A char[MAX_UDP_LENGTH] procured by the caller. | ||||
| 		@return The number of bytes received or -1 on non-blocking pass. | ||||
| 	*/ | ||||
| 	int read(char* buffer); | ||||
|  | ||||
| 	/** | ||||
| 		Receive a packet with a timeout. | ||||
| 		@param buffer A char[MAX_UDP_LENGTH] procured by the caller. | ||||
| 		@param maximum wait time in milliseconds | ||||
| 		@return The number of bytes received or -1 on timeout. | ||||
| 	*/ | ||||
| 	int read(char* buffer, unsigned timeout); | ||||
|  | ||||
|  | ||||
| 	/** Send a packet to a given destination, other than the default. */ | ||||
| 	int send(const struct sockaddr *dest, const char * buffer, size_t length); | ||||
|  | ||||
| 	/** Send a C-style string to a given destination, other than the default. */ | ||||
| 	int send(const struct sockaddr *dest, const char * buffer); | ||||
|  | ||||
| 	/** Make the socket non-blocking. */ | ||||
| 	void nonblocking(); | ||||
|  | ||||
| 	/** Make the socket blocking (the default). */ | ||||
| 	void blocking(); | ||||
|  | ||||
| 	/** Close the socket. */ | ||||
| 	void close(); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| /** UDP/IP User Datagram Socket */ | ||||
| class UDPSocket : public DatagramSocket { | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/** Open a USP socket with an OS-assigned port and no default destination. */ | ||||
| 	UDPSocket( unsigned short localPort=0); | ||||
|  | ||||
| 	/** Given a full specification, open the socket and set the dest address. */ | ||||
| 	UDPSocket( 	unsigned short localPort,  | ||||
| 			const char * remoteIP, unsigned short remotePort); | ||||
|  | ||||
| 	/** Set the destination port. */ | ||||
| 	void destination( unsigned short wDestPort, const char * wDestIP ); | ||||
|  | ||||
| 	/** Return the actual port number in use. */ | ||||
| 	unsigned short port() const; | ||||
|  | ||||
| 	/** Open and bind the UDP socket to a local port. */ | ||||
| 	void open(unsigned short localPort=0); | ||||
|  | ||||
| 	/** Give the return address of the most recently received packet. */ | ||||
| 	const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; } | ||||
|  | ||||
| 	size_t addressSize() const { return sizeof(struct sockaddr_in); } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** 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 | ||||
|  | ||||
|  | ||||
|  | ||||
| // vim:ts=4:sw=4 | ||||
							
								
								
									
										103
									
								
								CommonLibs/SocketsTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								CommonLibs/SocketsTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| /* | ||||
| * 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 "Sockets.h" | ||||
| #include "Threads.h" | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
| static const int gNumToSend = 10; | ||||
|  | ||||
|  | ||||
| void *testReaderIP(void *) | ||||
| { | ||||
| 	UDPSocket readSocket(5934, "localhost", 5061); | ||||
| 	readSocket.nonblocking(); | ||||
| 	int rc = 0; | ||||
| 	while (rc<gNumToSend) { | ||||
| 		char buf[MAX_UDP_LENGTH]; | ||||
| 		int count = readSocket.read(buf); | ||||
| 		if (count>0) { | ||||
| 			COUT("read: " << buf); | ||||
| 			rc++; | ||||
| 		} else { | ||||
| 			sleep(2); | ||||
| 		} | ||||
| 	} | ||||
| 	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); | ||||
| 		if (count>0) { | ||||
| 			COUT("read: " << buf); | ||||
| 			rc++; | ||||
| 		} else { | ||||
| 			sleep(2); | ||||
| 		} | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| int main(int argc, char * argv[] ) | ||||
| { | ||||
|  | ||||
|   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()); | ||||
|  | ||||
|   // 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); | ||||
|   } | ||||
|  | ||||
|   readerThreadIP.join(); | ||||
|   readerThreadUnix.join(); | ||||
| } | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
| @@ -1,7 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -25,17 +24,11 @@ | ||||
| */ | ||||
|  | ||||
|  | ||||
| #include <string.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
|  | ||||
|  | ||||
| #include "Threads.h" | ||||
| #include "Timeval.h" | ||||
| #include "Logger.h" | ||||
|  | ||||
| #ifndef gettid | ||||
| #include <sys/syscall.h> | ||||
| #define gettid() syscall(SYS_gettid) | ||||
| #endif | ||||
|  | ||||
|  | ||||
| using namespace std; | ||||
| @@ -109,25 +102,6 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const | ||||
| 	pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); | ||||
| } | ||||
|  | ||||
| void set_selfthread_name(const char *name) | ||||
| { | ||||
| 	pthread_t selfid = pthread_self(); | ||||
| 	pid_t tid = gettid(); | ||||
| 	if (pthread_setname_np(selfid, name) == 0) { | ||||
| 		LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name; | ||||
| 	} else { | ||||
| 		char buf[256]; | ||||
| 		int err = errno; | ||||
| 		char* err_str = strerror_r(err, buf, sizeof(buf)); | ||||
| 		LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void thread_enable_cancel(bool cancel) | ||||
| { | ||||
| 	cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : | ||||
| 		 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); | ||||
| } | ||||
|  | ||||
| void Thread::start(void *(*task)(void*), void *arg) | ||||
| { | ||||
| @@ -136,10 +110,8 @@ void Thread::start(void *(*task)(void*), void *arg) | ||||
| 	// (pat) Moved initialization to constructor to avoid crash in destructor. | ||||
| 	//res = pthread_attr_init(&mAttrib); | ||||
| 	//assert(!res); | ||||
| 	if (mStackSize != 0) { | ||||
| 		res = pthread_attr_setstacksize(&mAttrib, mStackSize); | ||||
| 		assert(!res); | ||||
| 	} | ||||
| 	res = pthread_attr_setstacksize(&mAttrib, mStackSize); | ||||
| 	assert(!res); | ||||
| 	res = pthread_create(&mThread, &mAttrib, task, arg); | ||||
| 	assert(!res); | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008, 2011 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -57,7 +55,7 @@ void unlockCout();		///< call after writing cout | ||||
| #define OBJDCOUT(text) {} | ||||
| #else | ||||
| #define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); } | ||||
| #define OBJDCOUT(text) { DCOUT(this << " " << text); } | ||||
| #define OBJDCOUT(text) { DCOUT(this << " " << text); }  | ||||
| #endif | ||||
| //@} | ||||
| //@} | ||||
| @@ -143,9 +141,6 @@ class Signal { | ||||
| #define START_THREAD(thread,function,argument) \ | ||||
| 	thread.start((void *(*)(void*))function, (void*)argument); | ||||
|  | ||||
| void set_selfthread_name(const char *name); | ||||
| void thread_enable_cancel(bool cancel); | ||||
|  | ||||
| /** A C++ wrapper for pthread threads.  */ | ||||
| class Thread { | ||||
|  | ||||
| @@ -155,12 +150,12 @@ class Thread { | ||||
| 	pthread_attr_t mAttrib; | ||||
| 	// FIXME -- Can this be reduced now? | ||||
| 	size_t mStackSize; | ||||
|  | ||||
| 	 | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Create a thread in a non-running state. */ | ||||
| 	Thread(size_t wStackSize = 0):mThread((pthread_t)0) { | ||||
| 	Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { | ||||
| 		pthread_attr_init(&mAttrib);	// (pat) moved this here. | ||||
| 		mStackSize=wStackSize; | ||||
| 	} | ||||
| @@ -177,15 +172,8 @@ class Thread { | ||||
| 	void start(void *(*task)(void*), void *arg); | ||||
|  | ||||
| 	/** Join a thread that will stop on its own. */ | ||||
| 	void join() { | ||||
| 		if (mThread) { | ||||
| 			int s = pthread_join(mThread, NULL); | ||||
| 			assert(!s); | ||||
| 		} | ||||
| 	} | ||||
| 	void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; } | ||||
|  | ||||
| 	/** Send cancelation to thread */ | ||||
| 	void cancel() { pthread_cancel(mThread); } | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -28,49 +27,43 @@ | ||||
|  | ||||
| #include "Timeval.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/timer.h> | ||||
| } | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| void Timeval::now() | ||||
| { | ||||
| 	osmo_clock_gettime(CLOCK_REALTIME, &mTimespec); | ||||
| } | ||||
|  | ||||
| void Timeval::future(unsigned offset) | ||||
| { | ||||
| 	now(); | ||||
| 	unsigned sec = offset/1000; | ||||
| 	unsigned msec = offset%1000; | ||||
| 	mTimespec.tv_nsec += msec*1000*1000; | ||||
| 	mTimespec.tv_sec += sec; | ||||
| 	if (mTimespec.tv_nsec > 1000*1000*1000) { | ||||
| 		mTimespec.tv_nsec -= 1000*1000*1000; | ||||
| 		mTimespec.tv_sec += 1; | ||||
| 	mTimeval.tv_usec += msec*1000; | ||||
| 	mTimeval.tv_sec += sec; | ||||
| 	if (mTimeval.tv_usec>1000000) { | ||||
| 		mTimeval.tv_usec -= 1000000; | ||||
| 		mTimeval.tv_sec += 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| struct timespec Timeval::timespec() const | ||||
| { | ||||
| 	return mTimespec; | ||||
| 	struct timespec retVal; | ||||
| 	retVal.tv_sec = mTimeval.tv_sec; | ||||
| 	retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec; | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool Timeval::passed() const | ||||
| { | ||||
| 	Timeval nowTime; | ||||
| 	if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false; | ||||
| 	if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true; | ||||
| 	if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true; | ||||
| 	if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false; | ||||
| 	if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true; | ||||
| 	if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| double Timeval::seconds() const | ||||
| { | ||||
| 	return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec); | ||||
| 	return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -79,25 +72,24 @@ long Timeval::delta(const Timeval& other) const | ||||
| { | ||||
| 	// 2^31 milliseconds is just over 4 years. | ||||
| 	int32_t deltaS = other.sec() - sec(); | ||||
| 	int32_t deltaNs = other.nsec() - nsec(); | ||||
| 	return 1000*deltaS + deltaNs/1000000; | ||||
| 	int32_t deltaUs = other.usec() - usec(); | ||||
| 	return 1000*deltaS + deltaUs/1000; | ||||
| } | ||||
|  | ||||
| 	 | ||||
|  | ||||
|  | ||||
|  | ||||
| ostream& operator<<(ostream& os, const Timeval& tv) | ||||
| { | ||||
| 	ios_base::fmtflags flags_backup = os.setf( ios::fixed, ios::floatfield ); | ||||
| 	os.setf( ios::fixed, ios::floatfield ); | ||||
| 	os << tv.seconds(); | ||||
| 	os.flags( flags_backup ); | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
|  | ||||
| ostream& operator<<(ostream& os, const struct timespec& ts) | ||||
| { | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec/1000; | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec; | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -44,12 +42,12 @@ class Timeval { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	struct timespec mTimespec; | ||||
| 	struct timeval mTimeval; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Set the value to current time. */ | ||||
| 	void now(); | ||||
| 	/** Set the value to gettimeofday. */ | ||||
| 	void now() { gettimeofday(&mTimeval,NULL); } | ||||
|  | ||||
| 	/** Set the value to gettimeofday plus an offset. */ | ||||
| 	void future(unsigned ms); | ||||
| @@ -57,18 +55,16 @@ class Timeval { | ||||
| 	//@{ | ||||
| 	Timeval(unsigned sec, unsigned usec) | ||||
| 	{ | ||||
| 		mTimespec.tv_sec = sec; | ||||
| 		mTimespec.tv_nsec = usec*1000; | ||||
| 		mTimeval.tv_sec = sec; | ||||
| 		mTimeval.tv_usec = usec; | ||||
| 	} | ||||
|  | ||||
| 	Timeval(const struct timeval& wTimeval) | ||||
| 	{ | ||||
| 		mTimespec.tv_sec = wTimeval.tv_sec; | ||||
| 		mTimespec.tv_nsec = wTimeval.tv_sec*1000; | ||||
| 	} | ||||
| 		:mTimeval(wTimeval) | ||||
| 	{} | ||||
|  | ||||
| 	/** | ||||
| 		Create a Timespec offset into the future. | ||||
| 		Create a Timeval offset into the future. | ||||
| 		@param offset milliseconds | ||||
| 	*/ | ||||
| 	Timeval(unsigned offset=0) { future(offset); } | ||||
| @@ -80,9 +76,8 @@ class Timeval { | ||||
| 	/** Return total seconds. */ | ||||
| 	double seconds() const; | ||||
|  | ||||
| 	uint32_t sec() const { return mTimespec.tv_sec; } | ||||
| 	uint32_t usec() const { return mTimespec.tv_nsec / 1000; } | ||||
| 	uint32_t nsec() const { return mTimespec.tv_nsec; } | ||||
| 	uint32_t sec() const { return mTimeval.tv_sec; } | ||||
| 	uint32_t usec() const { return mTimeval.tv_usec; } | ||||
|  | ||||
| 	/** Return differnce from other (other-self), in ms. */ | ||||
| 	long delta(const Timeval& other) const; | ||||
| @@ -93,11 +88,11 @@ class Timeval { | ||||
| 	/** Remaining time in ms. */ | ||||
| 	long remaining() const { return -elapsed(); } | ||||
|  | ||||
| 	/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */ | ||||
| 	/** Return true if the time has passed, as per gettimeofday. */ | ||||
| 	bool passed() const; | ||||
|  | ||||
| 	/** Add a given number of minutes to the time. */ | ||||
| 	void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; } | ||||
| 	void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										45
									
								
								CommonLibs/TimevalTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								CommonLibs/TimevalTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
| * 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> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										28
									
								
								CommonLibs/URLEncode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								CommonLibs/URLEncode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* Copyright 2011, Range Networks, Inc. */ | ||||
|  | ||||
| #include <URLEncode.h> | ||||
| #include <string> | ||||
| #include <string.h> | ||||
| #include <ctype.h> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| //based on javascript encodeURIComponent() | ||||
| string URLEncode(const string &c) | ||||
| { | ||||
| 	static const char *digits = "01234567890ABCDEF"; | ||||
| 	string retVal=""; | ||||
| 	for (size_t i=0; i<c.length(); i++) | ||||
| 	{ | ||||
| 		const char ch = c[i]; | ||||
| 		if (isalnum(ch) || strchr("-_.!~'()",ch)) { | ||||
| 			retVal += ch; | ||||
| 		} else { | ||||
| 			retVal += '%'; | ||||
| 			retVal += digits[(ch>>4) & 0x0f]; | ||||
| 			retVal += digits[ch & 0x0f]; | ||||
| 		} | ||||
| 	} | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
							
								
								
									
										30
									
								
								CommonLibs/URLEncode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								CommonLibs/URLEncode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
| * Copyright 2011 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 <string> | ||||
|  | ||||
| std::string URLEncode(const std::string&); | ||||
							
								
								
									
										17
									
								
								CommonLibs/URLEncodeTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								CommonLibs/URLEncodeTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
|  | ||||
| #include "URLEncode.h" | ||||
| #include <string> | ||||
| #include <iostream> | ||||
|  | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
| 	string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__); | ||||
| 	cout << test << endl; | ||||
| 	cout << URLEncode(test) << endl; | ||||
| } | ||||
|  | ||||
| @@ -1,38 +1,211 @@ | ||||
| /* | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
| * Copyright 2011 Range Networks, Inc. | ||||
| * All Rights Reserved. | ||||
| * | ||||
| * This software is distributed under multiple licenses; | ||||
| * see the COPYING file in the main directory for licensing | ||||
| * information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
|     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. | ||||
| */ | ||||
|  | ||||
| std::vector<std::string> comma_delimited_to_vector(const char* opt) | ||||
| #include <unistd.h>		// For usleep | ||||
| #include <sys/time.h>	// For gettimeofday | ||||
| #include <stdio.h>		// For vsnprintf | ||||
| #include <ostream>		// For ostream | ||||
| #include <sstream>		// For ostringstream | ||||
| #include <string.h>		// For strcpy | ||||
| //#include "GSMCommon.h" | ||||
| #include "Utils.h" | ||||
| #include "MemoryLeak.h" | ||||
|  | ||||
| namespace Utils { | ||||
|  | ||||
| MemStats gMemStats; | ||||
| int gMemLeakDebug = 0; | ||||
|  | ||||
| MemStats::MemStats() | ||||
| { | ||||
| 	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; | ||||
| 	memset(mMemNow,0,sizeof(mMemNow)); | ||||
| 	memset(mMemTotal,0,sizeof(mMemTotal)); | ||||
| 	memset(mMemName,0,sizeof(mMemName)); | ||||
| } | ||||
|  | ||||
| void MemStats::text(std::ostream &os) | ||||
| { | ||||
| 	os << "Structs current total:\n"; | ||||
| 	for (int i = 0; i < mMax; i++) { | ||||
| 		os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MemStats::memChkNew(MemoryNames memIndex, const char *id) | ||||
| { | ||||
| 	/*std::cout << "new " #type "\n";*/ | ||||
| 	mMemNow[memIndex]++; | ||||
| 	mMemTotal[memIndex]++; | ||||
| 	mMemName[memIndex] = id; | ||||
| } | ||||
|  | ||||
| void MemStats::memChkDel(MemoryNames memIndex, const char *id) | ||||
| { | ||||
| 	/*std::cout << "del " #type "\n";*/ | ||||
| 	mMemNow[memIndex]--; | ||||
| 	if (mMemNow[memIndex] < 0) { | ||||
| 		LOG(ERR) << "Memory underflow on type "<<id; | ||||
| 		if (gMemLeakDebug) assert(0); | ||||
| 		mMemNow[memIndex] += 100;	// Prevent another message for a while. | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
| { | ||||
| 	return os << ss.str(); | ||||
| } | ||||
|  | ||||
| std::ostream &osprintf(std::ostream &os, const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	char buf[300]; | ||||
| 	va_start(ap,fmt); | ||||
| 	int n = vsnprintf(buf,300,fmt,ap); | ||||
| 	va_end(ap); | ||||
| 	if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); } | ||||
| 	os << buf; | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
| std::string format(const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	char buf[300]; | ||||
| 	va_start(ap,fmt); | ||||
| 	int n = vsnprintf(buf,300,fmt,ap); | ||||
| 	va_end(ap); | ||||
| 	if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); } | ||||
| 	return std::string(buf); | ||||
| } | ||||
|  | ||||
| // Return time in seconds with high resolution. | ||||
| // Note: In the past I found this to be a surprisingly expensive system call in linux. | ||||
| double timef() | ||||
| { | ||||
| 	struct timeval tv; | ||||
| 	gettimeofday(&tv,NULL); | ||||
| 	return tv.tv_usec / 1000000.0 + tv.tv_sec; | ||||
| } | ||||
|  | ||||
| const std::string timestr() | ||||
| { | ||||
| 	struct timeval tv; | ||||
| 	struct tm tm; | ||||
| 	gettimeofday(&tv,NULL); | ||||
| 	localtime_r(&tv.tv_sec,&tm); | ||||
| 	unsigned tenths = tv.tv_usec / 100000;	// Rounding down is ok. | ||||
| 	return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths); | ||||
| } | ||||
|  | ||||
| // High resolution sleep for the specified time. | ||||
| // Return FALSE if time is already past. | ||||
| void sleepf(double howlong) | ||||
| { | ||||
| 	if (howlong <= 0.00001) return;		// Less than 10 usecs, forget it. | ||||
| 	usleep((useconds_t) (1000000.0 * howlong)); | ||||
| } | ||||
|  | ||||
| //bool sleepuntil(double untilwhen) | ||||
| //{ | ||||
| 	//double now = timef(); | ||||
| 	//double howlong = untilwhen - now;		// Fractional time in seconds. | ||||
| 	// We are not worrying about overflow because all times should be in the near future. | ||||
| 	//if (howlong <= 0.00001) return false;		// Less than 10 usecs, forget it. | ||||
| 	//sleepf(sleeptime); | ||||
| //} | ||||
|  | ||||
| std::string Text2Str::str() const | ||||
| { | ||||
| 	std::ostringstream ss; | ||||
| 	text(ss); | ||||
| 	return ss.str(); | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, const Text2Str *val) | ||||
| { | ||||
| 	std::ostringstream ss; | ||||
| 	if (val) { | ||||
| 		val->text(ss); | ||||
| 		os << ss.str();  | ||||
| 	} else { | ||||
| 		os << "(null)"; | ||||
| 	} | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
| // Greatest Common Denominator. | ||||
| // This is by Doug Brown. | ||||
| int gcd(int x, int y) | ||||
| { | ||||
| 	if (x > y) { | ||||
| 		return x % y == 0 ? y : gcd(y, x % y); | ||||
| 	} else { | ||||
| 		return y % x == 0 ? x : gcd(x, y % x); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| // Split a C string into an argc,argv array in place; the input string is modified. | ||||
| // Returns argc, and places results in argv, up to maxargc elements. | ||||
| // The final argv receives the rest of the input string from maxargc on, | ||||
| // even if it contains additional splitchars. | ||||
| // The correct idiom for use is to make a copy of your string, like this: | ||||
| // char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str()); | ||||
| // char *argv[2]; | ||||
| // int argc = cstrSplit(copy,argv,2,NULL); | ||||
| // If you want to detect the error of too many arguments, add 1 to argv, like this: | ||||
| // char *argv[3]; | ||||
| // int argc = cstrSplit(copy,argv,3,NULL); | ||||
| // if (argc == 3) { error("too many arguments"; } | ||||
| int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars) | ||||
| { | ||||
| 	if (splitchars == NULL) { splitchars = " \t\r\n"; }	// Default is any space. | ||||
| 	int argc = 0; | ||||
| 	while (argc < maxargc) { | ||||
| 		while (*in && strchr(splitchars,*in)) {in++;}	// scan past any splitchars | ||||
| 		if (! *in) return argc;					// return if finished. | ||||
| 		pargv[argc++] = in;						// save ptr to start of arg. | ||||
| 		in = strpbrk(in,splitchars);			// go to end of arg. | ||||
| 		if (!in) return	argc;					// return if finished. | ||||
| 		*in++ = 0;								// zero terminate this arg. | ||||
| 	} | ||||
| 	return argc; | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; } | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; } | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; } | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; } | ||||
|  | ||||
| std::string replaceAll(const std::string input, const std::string search, const std::string replace) | ||||
| { | ||||
| 	std::string output = input; | ||||
|  	int index = 0; | ||||
|  | ||||
| 	while (true) { | ||||
| 		index = output.find(search, index); | ||||
| 		if (index == std::string::npos) { | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		output.replace(index, replace.length(), replace); | ||||
| 		index += replace.length(); | ||||
| 	} | ||||
|  | ||||
| 	return output; | ||||
| } | ||||
|  | ||||
| }; | ||||
|   | ||||
| @@ -1,26 +1,148 @@ | ||||
| /* | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
| * Copyright 2011 Range Networks, Inc. | ||||
| * All Rights Reserved. | ||||
| * | ||||
| * This software is distributed under multiple licenses; | ||||
| * see the COPYING file in the main directory for licensing | ||||
| * information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| #pragma once | ||||
|     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. | ||||
| */ | ||||
|  | ||||
| #include <vector> | ||||
| #ifndef GPRSUTILS_H | ||||
| #define GPRSUTILS_H | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <string> | ||||
| #include <string.h> | ||||
| #include <math.h>		// for sqrtf | ||||
| #include "Logger.h" | ||||
|  | ||||
| std::vector<std::string> comma_delimited_to_vector(const char* opt); | ||||
|  | ||||
| namespace Utils { | ||||
|  | ||||
| extern double timef();					// high resolution time | ||||
| extern const std::string timestr();		// A timestamp to print in messages. | ||||
| extern void sleepf(double howlong);	// high resolution sleep | ||||
| extern int gcd(int x, int y); | ||||
|  | ||||
| // It is irritating to create a string just to interface to the brain-damaged | ||||
| // C++ stream class, but this is only used for debug messages. | ||||
| std::string format(const char *fmt, ...) __attribute__((format (printf,1,2))); | ||||
|  | ||||
| int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL); | ||||
|  | ||||
| // For classes with a text() function, provide a function to return a String, | ||||
| // and also a standard << stream function that takes a pointer to the object. | ||||
| // We dont provide the function that takes a reference to the object | ||||
| // because it is too highly overloaded and generally doesnt work. | ||||
| class Text2Str { | ||||
| 	public: | ||||
| 	virtual void text(std::ostream &os) const = 0; | ||||
| 	std::string str() const; | ||||
| }; | ||||
| std::ostream& operator<<(std::ostream& os, const Text2Str *val); | ||||
|  | ||||
| #if 0 | ||||
| // Generic Activity Timer.  Lots of controls to make everybody happy. | ||||
| class ATimer { | ||||
| 	double mStart; | ||||
| 	//bool mActive; | ||||
| 	double mLimitTime; | ||||
| 	public: | ||||
| 	ATimer() : mStart(0), mLimitTime(0) { } | ||||
| 	ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { } | ||||
| 	void start() { mStart=timef(); } | ||||
| 	void stop() { mStart=0; } | ||||
| 	bool active() { return !!mStart; } | ||||
| 	double elapsed() { return timef() - mStart; } | ||||
| 	bool expired() { return elapsed() > mLimitTime; } | ||||
| }; | ||||
| #endif | ||||
|  | ||||
|  | ||||
| struct BitSet { | ||||
| 	unsigned mBits; | ||||
| 	void setBit(unsigned whichbit) { mBits |= 1<<whichbit; } | ||||
| 	void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); } | ||||
| 	unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); } | ||||
| 	bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); } | ||||
| 	unsigned bits() const { return mBits; } | ||||
| 	operator int(void) const { return mBits; } | ||||
| 	BitSet() { mBits = 0; } | ||||
| }; | ||||
|  | ||||
| // Store current, min, max and compute running average and standard deviation. | ||||
| template<class Type> struct Statistic { | ||||
| 	Type mCurrent, mMin, mMax;		// min,max optional initialization so you can print before adding any values. | ||||
| 	unsigned mCnt; | ||||
| 	double mSum; | ||||
| 	//double mSum2;	// sum of squares. | ||||
| 	// (Type) cast needed in case Type is an enum, stupid language. | ||||
| 	Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {} | ||||
| 	// Set the current value and add a statisical point. | ||||
| 	void addPoint(Type val) { | ||||
| 		mCurrent = val; | ||||
| 		if (mCnt == 0 || val < mMin) {mMin = val;} | ||||
| 		if (mCnt == 0 || val > mMax) {mMax = val;} | ||||
| 		mCnt++; | ||||
| 		mSum += val; | ||||
| 		//mSum2 += val * val; | ||||
| 	} | ||||
| 	Type getCurrent() const {	// Return current value. | ||||
| 		return mCnt ? mCurrent : 0; | ||||
| 	} | ||||
| 	double getAvg() const { 			// Return average. | ||||
| 		return mCnt==0 ? 0 : mSum/mCnt;  | ||||
| 	}; | ||||
| 	//float getSD() const { 	// Return standard deviation.  Use low precision square root function. | ||||
| 	//	return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt; | ||||
| 	//} | ||||
|  | ||||
| 	void text(std::ostream &os) const {	// Print everything in parens. | ||||
| 		os << "("<<mCurrent; | ||||
| 		if (mMin != mMax) {	// Not point in printing all this stuff if min == max. | ||||
| 			os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg()); | ||||
| 			if (mCnt <= 999999) { | ||||
| 				os <<LOGVAR2("N",mCnt); | ||||
| 			} else { // Shorten this up: | ||||
| 				char buf[10], *ep; | ||||
| 				sprintf(buf,"%.3g",round(mCnt)); | ||||
| 				if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); } | ||||
| 				os << LOGVAR2("N",buf); | ||||
| 			} | ||||
| 			// os<<LOGVAR2("sd",getSD())  standard deviation not interesting | ||||
| 		} | ||||
| 		os << ")"; | ||||
| 		// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD()); | ||||
| 	} | ||||
| 	// Not sure if this works: | ||||
| 	//std::string statStr() const { | ||||
| 	//	return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD()); | ||||
| 	//} | ||||
| }; | ||||
|  | ||||
| // This I/O mechanism is so dumb: | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat); | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat); | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat); | ||||
| std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat); | ||||
|  | ||||
|  | ||||
| // Yes, they botched and left this out: | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); | ||||
|  | ||||
| std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3))); | ||||
|  | ||||
| std::string replaceAll(const std::string input, const std::string search, const std::string replace); | ||||
|  | ||||
| };	// namespace | ||||
|  | ||||
| using namespace Utils; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -34,14 +32,11 @@ | ||||
| #include <string.h> | ||||
| #include <iostream> | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| // We cant use Logger.h in this file... | ||||
| extern int gVectorDebug; | ||||
| #define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;} | ||||
|  | ||||
| typedef void (*vector_free_func)(void* wData); | ||||
| typedef void *(*vector_alloc_func)(size_t newSize); | ||||
|  | ||||
|  | ||||
| /** | ||||
| 	A simplified Vector template with aliases. | ||||
| @@ -65,8 +60,6 @@ template <class T> class Vector { | ||||
| 	T* mData;		///< allocated data block, if any | ||||
| 	T* mStart;		///< start of useful data | ||||
| 	T* mEnd;		///< end of useful data + 1 | ||||
| 	vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize. | ||||
| 	vector_free_func mFreeFunc; ///< function used to free mData. | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| @@ -92,30 +85,13 @@ template <class T> class Vector { | ||||
| 	/** Change the size of the Vector, discarding content. */ | ||||
| 	void resize(size_t newSize) | ||||
| 	{ | ||||
| 		if (mData!=NULL) { | ||||
| 			if (mFreeFunc) | ||||
| 				mFreeFunc(mData); | ||||
| 			else | ||||
| 				delete[] mData; | ||||
| 		} | ||||
| 		if (mData!=NULL) delete[] mData; | ||||
| 		if (newSize==0) mData=NULL; | ||||
| 		else { | ||||
| 			if (mAllocFunc) | ||||
| 				mData = (T*) mAllocFunc(newSize); | ||||
| 			else | ||||
| 				mData = new T[newSize]; | ||||
| 		} | ||||
| 		else mData = new T[newSize]; | ||||
| 		mStart = mData; | ||||
| 		mEnd = mStart + newSize; | ||||
| 	} | ||||
|  | ||||
| 	/** Reduce addressable size of the Vector, keeping content. */ | ||||
| 	void shrink(size_t newSize) | ||||
| 	{ | ||||
| 		assert(newSize <= mEnd - mStart); | ||||
| 		mEnd = mStart + newSize; | ||||
| 	} | ||||
|  | ||||
| 	/** Release memory and clear pointers. */ | ||||
| 	void clear() { resize(0); } | ||||
|  | ||||
| @@ -124,7 +100,7 @@ template <class T> class Vector { | ||||
| 	void clone(const Vector<T>& other) | ||||
| 	{ | ||||
| 		resize(other.size()); | ||||
| 		other.copyTo(*this); | ||||
| 		memcpy(mData,other.mStart,other.bytes()); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @@ -133,31 +109,29 @@ template <class T> class Vector { | ||||
| 	//@{ | ||||
|  | ||||
| 	/** Build an empty Vector of a given size. */ | ||||
| 	Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ resize(wSize); } | ||||
| 	Vector(size_t wSize=0):mData(NULL) { resize(wSize); } | ||||
|  | ||||
| 	/** Build a Vector by moving another. */ | ||||
| 	Vector(Vector<T>&& other) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc),  mFreeFunc(other.mFreeFunc) | ||||
| 	/** Build a Vector by shifting the data block. */ | ||||
| 	Vector(Vector<T>& other) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) | ||||
| 	{ other.mData=NULL; } | ||||
|  | ||||
| 	/** Build a Vector by copying another. */ | ||||
| 	Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); } | ||||
| 	Vector(const Vector<T>& other):mData(NULL) { clone(other); } | ||||
|  | ||||
| 	/** Build a Vector with explicit values. */ | ||||
| 	Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	Vector(T* wData, T* wStart, T* wEnd) | ||||
| 		:mData(wData),mStart(wStart),mEnd(wEnd) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a vector from an existing block, NOT to be deleted upon destruction. */ | ||||
| 	Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	Vector(T* wStart, size_t span) | ||||
| 		:mData(NULL),mStart(wStart),mEnd(wStart+span) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a Vector by concatenation. */ | ||||
| 	Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	Vector(const Vector<T>& other1, const Vector<T>& other2) | ||||
| 		:mData(NULL) | ||||
| 	{ | ||||
| 		resize(other1.size()+other2.size()); | ||||
| 		memcpy(mStart, other1.mStart, other1.bytes()); | ||||
| @@ -181,8 +155,6 @@ template <class T> class Vector { | ||||
| 		mData=other.mData; | ||||
| 		mStart=other.mStart; | ||||
| 		mEnd=other.mEnd; | ||||
| 		mAllocFunc=other.mAllocFunc; | ||||
| 		mFreeFunc=other.mFreeFunc; | ||||
| 		other.mData=NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -225,15 +197,10 @@ template <class T> class Vector { | ||||
| 	*/ | ||||
| 	void copyToSegment(Vector<T>& other, size_t start, size_t span) const | ||||
| 	{ | ||||
| 		unsigned int i; | ||||
| 		T* dst = other.mStart + start; | ||||
| 		T* src = mStart; | ||||
| 		assert(dst+span<=other.mEnd); | ||||
| 		T* base = other.mStart + start; | ||||
| 		assert(base+span<=other.mEnd); | ||||
| 		assert(mStart+span<=mEnd); | ||||
| 		for (i = 0; i < span; i++, src++, dst++) | ||||
| 			*dst = *src; | ||||
| 		/*TODO if not non-trivially copyable type class, optimize: | ||||
| 		memcpy(dst,mStart,span*sizeof(T)); */ | ||||
| 		memcpy(base,mStart,span*sizeof(T)); | ||||
| 	} | ||||
|  | ||||
| 	/** Copy all of this Vector to a segment of another Vector. */ | ||||
| @@ -255,21 +222,6 @@ template <class T> class Vector { | ||||
| 		memcpy(other.mStart,base,span*sizeof(T)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Move (copy) a segment of this vector into a different position in the vector | ||||
| 		@param from Start point from which to copy. | ||||
| 		@param to   Start point to which to copy. | ||||
| 		@param span The number of elements to copy. | ||||
| 	*/ | ||||
| 	void segmentMove(size_t from, size_t to, size_t span) | ||||
| 	{ | ||||
| 		const T* baseFrom = mStart + from; | ||||
| 		T* baseTo = mStart + to; | ||||
| 		assert(baseFrom+span<=mEnd); | ||||
| 		assert(baseTo+span<=mEnd); | ||||
| 		memmove(baseTo,baseFrom,span*sizeof(T)); | ||||
| 	} | ||||
|  | ||||
| 	void fill(const T& val) | ||||
| 	{ | ||||
| 		T* dp=mStart; | ||||
| @@ -308,7 +260,7 @@ template <class T> class Vector { | ||||
| 	T* end() { return mEnd; } | ||||
| 	bool isOwner() { return !!mData; }	// Do we own any memory ourselves? | ||||
| 	//@} | ||||
|  | ||||
| 	 | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| /*
 | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| @@ -29,6 +28,10 @@ | ||||
| #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; | ||||
| @@ -1,20 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* | ||||
|  * This file contains structures used by both VTY (C, dir CommonLibs) and | ||||
|  * osmo-trx (CXX, dir Transceiver52) | ||||
|  */ | ||||
|  | ||||
| enum FillerType { | ||||
|   FILLER_DUMMY, | ||||
|   FILLER_ZERO, | ||||
|   FILLER_NORM_RAND, | ||||
|   FILLER_EDGE_RAND, | ||||
|   FILLER_ACCESS_RAND, | ||||
| }; | ||||
|  | ||||
| enum ReferenceType { | ||||
|   REF_INTERNAL, | ||||
|   REF_EXTERNAL, | ||||
|   REF_GPS, | ||||
| }; | ||||
| @@ -1,107 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol <pespin@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include "debug.h" | ||||
|  | ||||
| /* default categories */ | ||||
| static const struct log_info_cat default_categories[] = { | ||||
| 	[DMAIN] = { | ||||
| 		.name = "DMAIN", | ||||
| 		.description = "Main generic category", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DTRXCTRL] = { | ||||
| 			.name = "DTRXCTRL", | ||||
| 			.description = "TRX CTRL interface", | ||||
| 			.color = "\033[1;33m", | ||||
| 			.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DDEV] = { | ||||
| 		.name = "DDEV", | ||||
| 		.description = "Device/Driver specific code", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_INFO, | ||||
| 	}, | ||||
| 	[DLMS] = { | ||||
| 		.name = "DLMS", | ||||
| 		.description = "Logging from within LimeSuite itself", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| const struct log_info log_info = { | ||||
| 	.cat = default_categories, | ||||
| 	.num_cat = ARRAY_SIZE(default_categories), | ||||
| }; | ||||
|  | ||||
| pthread_mutex_t log_mutex; | ||||
|  | ||||
| bool log_mutex_init() { | ||||
| 	int rc; | ||||
| 	pthread_mutexattr_t attr; | ||||
|  | ||||
| 	if ((rc = pthread_mutexattr_init(&attr))) { | ||||
| 		fprintf(stderr, "pthread_mutexattr_init() failed: %d\n", rc); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))) { | ||||
| 		fprintf(stderr, "pthread_mutexattr_settype() failed: %d\n", rc); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((rc = pthread_mutex_init(&log_mutex, &attr))) { | ||||
| 		fprintf(stderr, "pthread_mutex_init() failed: %d\n", rc); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((rc = pthread_mutexattr_destroy(&attr))) { | ||||
| 		fprintf(stderr, "pthread_mutexattr_destroy() failed: %d\n", rc); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| 	/* FIXME: do we need to call pthread_mutex_destroy() during process exit? */ | ||||
| } | ||||
|  | ||||
| /* If called inside a C++ destructor, use log_mutex_(un)lock_canceldisable() APIs instead. | ||||
|    See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */ | ||||
| void log_mutex_lock() { | ||||
| 	OSMO_ASSERT(!pthread_mutex_lock(&log_mutex)); | ||||
| } | ||||
|  | ||||
| void log_mutex_unlock() { | ||||
| 	OSMO_ASSERT(!pthread_mutex_unlock(&log_mutex)); | ||||
| } | ||||
|  | ||||
| void log_mutex_lock_canceldisable(int *st) { | ||||
| 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, st); | ||||
| 	log_mutex_lock(); | ||||
| } | ||||
|  | ||||
| void log_mutex_unlock_canceldisable(int st) { | ||||
| 	log_mutex_unlock(); | ||||
| 	pthread_setcancelstate(st, NULL); | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include <osmocom/core/logging.h> | ||||
|  | ||||
| extern const struct log_info log_info; | ||||
|  | ||||
| /* Debug Areas of the code */ | ||||
| enum { | ||||
| 	DMAIN, | ||||
| 	DTRXCTRL, | ||||
| 	DDEV, | ||||
| 	DLMS, | ||||
| }; | ||||
|  | ||||
|  | ||||
| bool log_mutex_init(); | ||||
| void log_mutex_lock(); | ||||
| void log_mutex_unlock(); | ||||
| void log_mutex_lock_canceldisable(int *st); | ||||
| void log_mutex_unlock_canceldisable(int st); | ||||
|  | ||||
| #define CLOGC(category, level, fmt, args...) do { \ | ||||
| 	log_mutex_lock(); \ | ||||
| 	LOGP(category, level, "[tid=%lu] " fmt, pthread_self(), ##args);  \ | ||||
| 	log_mutex_unlock(); \ | ||||
| } while(0) | ||||
|  | ||||
| #define CLOGCHAN(chan, category, level, fmt, args...) do { \ | ||||
| 	log_mutex_lock(); \ | ||||
| 	LOGP(category, level, "[tid=%lu][chan=%lu] " fmt, pthread_self(), chan, ##args);  \ | ||||
| 	log_mutex_unlock(); \ | ||||
| } while(0) | ||||
| @@ -1,57 +0,0 @@ | ||||
| /* Generic signalling/notification infrastructure */ | ||||
| /* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol <pespin@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * 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/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/signal.h> | ||||
|  | ||||
| /* Signalling subsystems */ | ||||
| enum signal_subsystems { | ||||
| 	SS_MAIN, | ||||
| 	SS_DEVICE, | ||||
| }; | ||||
|  | ||||
| /* SS_MAIN signals */ | ||||
| enum SS_MAIN { | ||||
| 	S_MAIN_STOP_REQUIRED, /* TRX fatal error, it should be stopped */ | ||||
| }; | ||||
|  | ||||
| /* SS_DEVICE signals */ | ||||
| enum SS_DEVICE { | ||||
| 	/* Device internal counters changed. Counters are provided as cb data | ||||
| 	   (struct device_counters). Must be sent with PTHREAD_CANCEL_DISABLE | ||||
| 	   to avoid deadlocks in case osmo-trx process is asked to exit. */ | ||||
| 	S_DEVICE_COUNTER_CHANGE, | ||||
| }; | ||||
|  | ||||
| /* signal cb for signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> */ | ||||
| struct device_counters { | ||||
| 	size_t chan; | ||||
| 	unsigned int rx_overruns; | ||||
| 	unsigned int tx_underruns; | ||||
| 	unsigned int rx_dropped_events; | ||||
| 	unsigned int rx_dropped_samples; | ||||
| 	unsigned int tx_dropped_events; | ||||
| 	unsigned int tx_dropped_samples; | ||||
| }; | ||||
							
								
								
									
										154
									
								
								CommonLibs/sqlite3util.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								CommonLibs/sqlite3util.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| /* | ||||
| * 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; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										29
									
								
								CommonLibs/sqlite3util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								CommonLibs/sqlite3util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #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 | ||||
| @@ -1,336 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2019 sysmocom - s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol <pespin@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * rate_ctr API uses several osmocom select loop features, and as a result, | ||||
|  * calls to it must be done through the main thread (the one running the osmocom | ||||
|  * loop in osmo-trx). | ||||
|  * Since read/write from/to SDR is done in separate threads (even read and write | ||||
|  * each use a different thread), we must use some sort of message passing system | ||||
|  * between main thread feeding rate_ctr structures and the Rx/Tx threads | ||||
|  * generating the events. | ||||
|  * The idea is that upon read/write issues, lower layers (SDR APIs) provide us with | ||||
|  * underrun/overrun/droppedPackets information, and in that case we pass that up | ||||
|  * the stack through signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> with signal_cb | ||||
|  * being a pointer to a "struct device_counters" structure, which contains | ||||
|  * device (implementation agnostic) statful counters for different kind of | ||||
|  * statistics. | ||||
|  * That signal is processed here in device_sig_cb, where a copy of the "struct | ||||
|  * device_counters" structure is held and the main thread is instructed through | ||||
|  * a timerfd to update rate_ctr APIs against this copy. All this is done inside | ||||
|  * a mutex to avoid different race conditons (between Rx andTx threads, and | ||||
|  * between Rx/Tx and main thread). For the same reason, callers of signal | ||||
|  * <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads, | ||||
|  * must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks | ||||
|  * in case the main thread decides to cancel other threads due to a shutdown | ||||
|  * operation (fi SIGKILL received) | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdint.h> | ||||
| #include <inttypes.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/core/select.h> | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/core/timer.h> | ||||
|  | ||||
| #include "osmo_signal.h" | ||||
| #include "trx_vty.h" | ||||
| #include "trx_rate_ctr.h" | ||||
| } | ||||
| #include "Threads.h" | ||||
| #include "Logger.h" | ||||
|  | ||||
| /* Used in ctrs_pending, when set it means that channel slot contains unused | ||||
|    (non-pending) counter data */ | ||||
| #define PENDING_CHAN_NONE SIZE_MAX | ||||
|  | ||||
| static void *trx_rate_ctr_ctx; | ||||
|  | ||||
| static struct rate_ctr_group** rate_ctrs; | ||||
| static struct device_counters* ctrs_pending; | ||||
| static size_t chan_len; | ||||
| static struct osmo_fd rate_ctr_timerfd; | ||||
| static Mutex rate_ctr_mutex; | ||||
|  | ||||
| struct osmo_timer_list threshold_timer; | ||||
| static LLIST_HEAD(threshold_list); | ||||
| static int threshold_timer_sched_secs; | ||||
| static bool threshold_initied; | ||||
|  | ||||
| const struct value_string rate_ctr_intv[] = { | ||||
| 	{ RATE_CTR_INTV_SEC,	"per-second" }, | ||||
| 	{ RATE_CTR_INTV_MIN,	"per-minute" }, | ||||
| 	{ RATE_CTR_INTV_HOUR,	"per-hour" }, | ||||
| 	{ RATE_CTR_INTV_DAY, 	"per-day" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| const struct value_string trx_chan_ctr_names[] = { | ||||
| 	{ TRX_CTR_RX_OVERRUNS,	"rx_overruns" }, | ||||
| 	{ TRX_CTR_TX_UNDERRUNS,	"tx_underruns" }, | ||||
| 	{ TRX_CTR_RX_DROP_EV,	"rx_drop_events" }, | ||||
| 	{ TRX_CTR_RX_DROP_SMPL,	"rx_drop_samples" }, | ||||
| 	{ TRX_CTR_TX_DROP_EV,	"tx_drop_events" }, | ||||
| 	{ TRX_CTR_TX_DROP_SMPL,	"tx_drop_samples" }, | ||||
| 	{ 0, NULL } | ||||
| }; | ||||
|  | ||||
| static const struct rate_ctr_desc trx_chan_ctr_desc[] = { | ||||
| 	[TRX_CTR_RX_OVERRUNS]		= { "device:rx_overruns",	"Number of Rx overruns in FIFO queue" }, | ||||
| 	[TRX_CTR_TX_UNDERRUNS]		= { "device:tx_underruns",	"Number of Tx underruns in FIFO queue" }, | ||||
| 	[TRX_CTR_RX_DROP_EV]		= { "device:rx_drop_events",	"Number of times Rx samples were dropped by HW" }, | ||||
| 	[TRX_CTR_RX_DROP_SMPL]		= { "device:rx_drop_samples",	"Number of Rx samples dropped by HW" }, | ||||
| 	[TRX_CTR_TX_DROP_EV]		= { "device:tx_drop_events",	"Number of times Tx samples were dropped by HW" }, | ||||
| 	[TRX_CTR_TX_DROP_SMPL]		= { "device:tx_drop_samples",	"Number of Tx samples dropped by HW" } | ||||
| }; | ||||
|  | ||||
| static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = { | ||||
| 	.group_name_prefix		= "trx:chan", | ||||
| 	.group_description		= "osmo-trx statistics", | ||||
| 	.class_id			= OSMO_STATS_CLASS_GLOBAL, | ||||
| 	.num_ctr			= ARRAY_SIZE(trx_chan_ctr_desc), | ||||
| 	.ctr_desc			= trx_chan_ctr_desc, | ||||
| }; | ||||
|  | ||||
| static int rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) { | ||||
| 	size_t chan; | ||||
| 	struct rate_ctr *ctr; | ||||
| 	LOGC(DMAIN, NOTICE) << "Main thread is updating counters"; | ||||
| 	rate_ctr_mutex.lock(); | ||||
| 	for (chan = 0; chan < chan_len; chan++) { | ||||
| 		if (ctrs_pending[chan].chan == PENDING_CHAN_NONE) | ||||
| 			continue; | ||||
| 		LOGCHAN(chan, DMAIN, INFO) << "rate_ctr update"; | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_OVERRUNS]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].rx_overruns - ctr->current); | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_UNDERRUNS]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].tx_underruns - ctr->current); | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_EV]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_events - ctr->current); | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_SMPL]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_samples - ctr->current); | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_EV]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_events - ctr->current); | ||||
| 		ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_SMPL]; | ||||
| 		rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_samples - ctr->current); | ||||
|  | ||||
| 		/* Mark as done */ | ||||
| 		ctrs_pending[chan].chan = PENDING_CHAN_NONE; | ||||
| 	} | ||||
| 	if (osmo_timerfd_disable(&rate_ctr_timerfd) < 0) | ||||
| 		LOGC(DMAIN, ERROR) << "Failed to disable timerfd"; | ||||
| 	rate_ctr_mutex.unlock(); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Callback function to be called every time we receive a signal from DEVICE */ | ||||
| static int device_sig_cb(unsigned int subsys, unsigned int signal, | ||||
| 			 void *handler_data, void *signal_data) | ||||
| { | ||||
| 	struct device_counters *ctr; | ||||
| 	/* Delay sched around 20 ms, in case we receive several calls from several | ||||
| 	 * channels batched */ | ||||
| 	struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000}; | ||||
| 	/* no automatic re-trigger */ | ||||
| 	struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0}; | ||||
|  | ||||
| 	switch (signal) { | ||||
| 	case S_DEVICE_COUNTER_CHANGE: | ||||
| 		ctr = (struct device_counters *)signal_data; | ||||
| 		LOGCHAN(ctr->chan, DMAIN, NOTICE) << "Received counter change from radioDevice"; | ||||
| 		rate_ctr_mutex.lock(); | ||||
| 		ctrs_pending[ctr->chan] = *ctr; | ||||
| 		if (osmo_timerfd_schedule(&rate_ctr_timerfd, &next_sched, &intv_sched) < 0) { | ||||
| 			LOGC(DMAIN, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno); | ||||
| 		} | ||||
| 		rate_ctr_mutex.unlock(); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /************************************ | ||||
|  * ctr_threshold  APIs | ||||
|  ************************************/ | ||||
| static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr) | ||||
| { | ||||
| 	static char buf[256]; | ||||
| 	int rc = 0; | ||||
| 	rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id)); | ||||
| 	rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv)); | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static void threshold_timer_cb(void *data) | ||||
| { | ||||
| 	struct ctr_threshold *ctr_thr; | ||||
| 	struct rate_ctr *rate_ctr; | ||||
| 	size_t chan; | ||||
| 	LOGC(DMAIN, DEBUG) << "threshold_timer_cb fired!"; | ||||
|  | ||||
| 	llist_for_each_entry(ctr_thr, &threshold_list, list) { | ||||
| 		for (chan = 0; chan < chan_len; chan++) { | ||||
| 			rate_ctr = &rate_ctrs[chan]->ctr[ctr_thr->ctr_id]; | ||||
| 			LOGCHAN(chan, DMAIN, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr) | ||||
| 						   << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")"; | ||||
| 			if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) { | ||||
| 				LOGCHAN(chan, DMAIN, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr) | ||||
| 							   << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")"; | ||||
| 				osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0); | ||||
| } | ||||
|  | ||||
| static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr) | ||||
| { | ||||
| 	size_t mult = 0; | ||||
| 	switch (ctr->intv) { | ||||
| 	case RATE_CTR_INTV_SEC: | ||||
| 		mult = 1; | ||||
| 		break; | ||||
| 	case RATE_CTR_INTV_MIN: | ||||
| 		mult = 60; | ||||
| 		break; | ||||
| 	case RATE_CTR_INTV_HOUR: | ||||
| 		mult = 60*60; | ||||
| 		break; | ||||
| 	case RATE_CTR_INTV_DAY: | ||||
| 		mult = 60*60*24; | ||||
| 		break; | ||||
| 	default: | ||||
| 		OSMO_ASSERT(false); | ||||
| 	} | ||||
| 	return mult; | ||||
| } | ||||
|  | ||||
| static void threshold_timer_update_intv() { | ||||
| 	struct ctr_threshold *ctr, *min_ctr; | ||||
| 	size_t secs, min_secs; | ||||
|  | ||||
| 	/* Avoid scheduling timer until itself and other structures are prepared | ||||
| 	   by trx_rate_ctr_init */ | ||||
| 	if (!threshold_initied) | ||||
| 		return; | ||||
|  | ||||
| 	if (llist_empty(&threshold_list)) { | ||||
| 		if (osmo_timer_pending(&threshold_timer)) | ||||
| 			osmo_timer_del(&threshold_timer); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list); | ||||
| 	min_secs = ctr_threshold_2_seconds(min_ctr); | ||||
|  | ||||
| 	llist_for_each_entry(ctr, &threshold_list, list) { | ||||
| 		secs = ctr_threshold_2_seconds(ctr); | ||||
| 		if( min_secs > secs) | ||||
| 			min_secs = secs; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	threshold_timer_sched_secs = OSMO_MAX(min_secs / 2 - 1, 1); | ||||
| 	LOGC(DMAIN, INFO) << "New ctr-error-threshold check interval: " | ||||
| 			  << threshold_timer_sched_secs << " seconds"; | ||||
| 	osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0); | ||||
| } | ||||
|  | ||||
| /* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */ | ||||
| void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx) | ||||
| { | ||||
| 	size_t  i; | ||||
| 	trx_rate_ctr_ctx = ctx; | ||||
| 	chan_len = trx_ctx->cfg.num_chans; | ||||
| 	ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters)); | ||||
| 	rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*)); | ||||
|  | ||||
| 	for (i = 0; i < chan_len; i++) { | ||||
| 		ctrs_pending[i].chan = PENDING_CHAN_NONE; | ||||
| 		rate_ctrs[i] = rate_ctr_group_alloc(ctx, &trx_chan_ctr_group_desc, i); | ||||
| 		if (!rate_ctrs[i]) { | ||||
| 			LOGCHAN(i, DMAIN, ERROR) << "Failed to allocate rate ctr"; | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
| 	rate_ctr_timerfd.fd = -1; | ||||
| 	if (osmo_timerfd_setup(&rate_ctr_timerfd, rate_ctr_timerfd_cb, NULL) < 0) { | ||||
| 		LOGC(DMAIN, ERROR) << "Failed to setup timerfd"; | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL); | ||||
|  | ||||
| 	/* Now set up threshold checks */ | ||||
| 	threshold_initied = true; | ||||
| 	osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL); | ||||
| 	threshold_timer_update_intv(); | ||||
| } | ||||
|  | ||||
| void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr) | ||||
| { | ||||
| 	struct ctr_threshold *new_ctr; | ||||
|  | ||||
| 	new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold); | ||||
| 	*new_ctr = *ctr; | ||||
| 	LOGC(DMAIN, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr); | ||||
| 	llist_add(&new_ctr->list, &threshold_list); | ||||
| 	threshold_timer_update_intv(); | ||||
| } | ||||
|  | ||||
| int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr) | ||||
| { | ||||
| 	struct ctr_threshold *ctr; | ||||
|  | ||||
| 	llist_for_each_entry(ctr, &threshold_list, list) { | ||||
| 		if (ctr->intv != del_ctr->intv || | ||||
| 		    ctr->ctr_id != del_ctr->ctr_id || | ||||
| 		    ctr->val != del_ctr->val) | ||||
| 			continue; | ||||
|  | ||||
| 		LOGC(DMAIN, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr); | ||||
| 		llist_del(&ctr->list); | ||||
| 		talloc_free(ctr); | ||||
| 		threshold_timer_update_intv(); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix) | ||||
| { | ||||
| 	struct ctr_threshold *ctr; | ||||
|  | ||||
| 	llist_for_each_entry(ctr, &threshold_list, list) { | ||||
| 		vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE); | ||||
| 	} | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| enum TrxCtr { | ||||
| 	TRX_CTR_RX_OVERRUNS, | ||||
| 	TRX_CTR_TX_UNDERRUNS, | ||||
| 	TRX_CTR_RX_DROP_EV, | ||||
| 	TRX_CTR_RX_DROP_SMPL, | ||||
| 	TRX_CTR_TX_DROP_EV, | ||||
| 	TRX_CTR_TX_DROP_SMPL, | ||||
| }; | ||||
|  | ||||
| struct ctr_threshold { | ||||
| 	/*! Linked list of all counter groups in the system */ | ||||
| 	struct llist_head list; | ||||
| 	enum rate_ctr_intv intv; | ||||
| 	enum TrxCtr ctr_id; | ||||
| 	uint32_t val; | ||||
| }; | ||||
|  | ||||
| extern const struct value_string rate_ctr_intv[]; | ||||
| extern const struct value_string trx_chan_ctr_names[]; | ||||
|  | ||||
| struct trx_ctx; | ||||
| void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx); | ||||
| void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr); | ||||
| int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr); | ||||
| void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix); | ||||
| @@ -1,729 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol <pespin@sysmocom.de> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdint.h> | ||||
| #include <inttypes.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
|  | ||||
| #include "trx_rate_ctr.h" | ||||
| #include "trx_vty.h" | ||||
| #include "../config.h" | ||||
|  | ||||
| static struct trx_ctx* g_trx_ctx; | ||||
|  | ||||
| static const struct value_string clock_ref_names[] = { | ||||
| 	{ REF_INTERNAL,	"internal" }, | ||||
| 	{ REF_EXTERNAL,	"external" }, | ||||
| 	{ REF_GPS,	"gpsdo" }, | ||||
| 	{ 0,		NULL } | ||||
| }; | ||||
|  | ||||
| static const struct value_string filler_names[] = { | ||||
| 	{ FILLER_DUMMY,		"Dummy bursts" }, | ||||
| 	{ FILLER_ZERO,		"Disabled" }, | ||||
| 	{ FILLER_NORM_RAND,	"Normal bursts with random payload" }, | ||||
| 	{ FILLER_EDGE_RAND,	"EDGE bursts with random payload" }, | ||||
| 	{ FILLER_ACCESS_RAND,	"Access bursts with random payload" }, | ||||
| 	{ 0,			NULL } | ||||
| }; | ||||
|  | ||||
| struct trx_ctx *trx_from_vty(struct vty *v) | ||||
| { | ||||
|         /* It can't hurt to force callers to continue to pass the vty instance | ||||
|          * to this function, in case we'd like to retrieve the global | ||||
|          * trx instance from the vty at some point in the future. But | ||||
|          * until then, just return the global pointer, which should have been | ||||
|          * initialized by trx_vty_init(). | ||||
|          */ | ||||
|         OSMO_ASSERT(g_trx_ctx); | ||||
|         return g_trx_ctx; | ||||
| } | ||||
|  | ||||
| enum trx_vty_node { | ||||
| 	TRX_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	CHAN_NODE, | ||||
| }; | ||||
|  | ||||
| static struct cmd_node trx_node = { | ||||
| 	TRX_NODE, | ||||
| 	"%s(config-trx)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| static struct cmd_node chan_node = { | ||||
| 	CHAN_NODE, | ||||
| 	"%s(config-trx-chan)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| DEFUN(cfg_trx, cfg_trx_cmd, | ||||
| 	"trx", | ||||
| 	"Configure the TRX\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (!trx) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	vty->node = TRX_NODE; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_bind_ip, cfg_bind_ip_cmd, | ||||
| 	"bind-ip A.B.C.D", | ||||
| 	"Set the IP address for the local bind\n" | ||||
| 	"IPv4 Address\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_remote_ip, cfg_remote_ip_cmd, | ||||
| 	"remote-ip A.B.C.D", | ||||
| 	"Set the IP address for the remote BTS\n" | ||||
| 	"IPv4 Address\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_base_port, cfg_base_port_cmd, | ||||
| 	"base-port <1-65535>", | ||||
| 	"Set the TRX Base Port\n" | ||||
| 	"TRX Base Port\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.base_port = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_dev_args, cfg_dev_args_cmd, | ||||
| 	"dev-args DESC", | ||||
| 	"Set the device-specific arguments to pass to the device\n" | ||||
| 	"Device-specific arguments\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_tx_sps, cfg_tx_sps_cmd, | ||||
| 	"tx-sps (1|4)", | ||||
| 	"Set the Tx Samples-per-Symbol\n" | ||||
| 	"Tx Samples-per-Symbol\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.tx_sps = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rx_sps, cfg_rx_sps_cmd, | ||||
| 	"rx-sps (1|4)", | ||||
| 	"Set the Rx Samples-per-Symbol\n" | ||||
| 	"Rx Samples-per-Symbol\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.rx_sps = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd, | ||||
| 	"test rtsc <0-7>", | ||||
| 	"Set the Random Normal Burst test mode with TSC\n" | ||||
| 	"TSC\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (trx->cfg.rach_delay_set) { | ||||
| 		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	trx->cfg.rtsc_set = true; | ||||
| 	trx->cfg.rtsc = atoi(argv[0]); | ||||
| 	if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ | ||||
| 		trx->cfg.filler = FILLER_NORM_RAND; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd, | ||||
| 	"test rach-delay <0-68>", | ||||
| 	"Set the Random Access Burst test mode with delay\n" | ||||
| 	"RACH delay\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (trx->cfg.rtsc_set) { | ||||
| 		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (trx->cfg.egprs) { | ||||
| 		vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	trx->cfg.rach_delay_set = true; | ||||
| 	trx->cfg.rach_delay = atoi(argv[0]); | ||||
| 	trx->cfg.filler = FILLER_ACCESS_RAND; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_clock_ref, cfg_clock_ref_cmd, | ||||
| 	"clock-ref (internal|external|gpsdo)", | ||||
| 	"Set the Reference Clock\n" | ||||
| 	"Enable internal referece (default)\n" | ||||
| 	"Enable external 10 MHz reference\n" | ||||
| 	"Enable GPSDO reference\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd, | ||||
| 	"multi-arfcn (disable|enable)", | ||||
| 	"Enable multi-ARFCN transceiver (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.multi_arfcn = false; | ||||
| 		return CMD_SUCCESS; | ||||
| 	} | ||||
|  | ||||
| 	if (trx->cfg.num_chans > TRX_MCHAN_MAX) { | ||||
| 		vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s", | ||||
| 			TRX_MCHAN_MAX, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	trx->cfg.multi_arfcn = true; | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_offset, cfg_offset_cmd, | ||||
| 	"offset FLOAT", | ||||
| 	"Set the baseband frequency offset (default=0, auto)\n" | ||||
| 	"Baseband Frequency Offset\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.offset = atof(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd, | ||||
| 	"rssi-offset FLOAT", | ||||
| 	"Set the RSSI to dBm offset in dB (default=0)\n" | ||||
| 	"RSSI to dBm offset in dB\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.rssi_offset = atof(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_swap_channels, cfg_swap_channels_cmd, | ||||
| 	"swap-channels (disable|enable)", | ||||
| 	"Swap channels (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.swap_channels = false; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.swap_channels = true; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_egprs, cfg_egprs_cmd, | ||||
| 	"egprs (disable|enable)", | ||||
| 	"Enable EDGE receiver (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.egprs = false; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.egprs = true; | ||||
| 		trx->cfg.filler = FILLER_EDGE_RAND; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_ext_rach, cfg_ext_rach_cmd, | ||||
| 	"ext-rach (disable|enable)", | ||||
| 	"Enable extended (11-bit) RACH (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) | ||||
| 		trx->cfg.ext_rach = false; | ||||
|  | ||||
| 	if (strcmp("enable", argv[0]) == 0) | ||||
| 		trx->cfg.ext_rach = true; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rt_prio, cfg_rt_prio_cmd, | ||||
| 	"rt-prio <1-32>", | ||||
| 	"Set the SCHED_RR real-time priority\n" | ||||
| 	"Real time priority\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.sched_rr = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_stack_size, cfg_stack_size_cmd, | ||||
| 	"stack-size <0-2147483647>", | ||||
| 	"Set the stack size per thread in BYTE, 0 = OS default\n" | ||||
| 	"Stack size per thread in BYTE\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.stack_size = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_filler, cfg_filler_cmd, | ||||
| 	"filler dummy", | ||||
| 	"Enable C0 filler table\n" | ||||
| 	"Dummy method\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.filler = FILLER_DUMMY; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int vty_ctr_name_2_id(const char* str) { | ||||
| 	size_t i; | ||||
| 	for (i = 0; trx_chan_ctr_names[i].str; i++) { | ||||
| 		if (strstr(trx_chan_ctr_names[i].str, str)) { | ||||
| 			return i; | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static int vty_intv_name_2_id(const char* str) { | ||||
| 	size_t i; | ||||
| 	for (i = 0; rate_ctr_intv[i].str; i++) { | ||||
| 		if (strcmp(rate_ctr_intv[i].str, str) == 0) { | ||||
| 			return i; | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| #define THRESHOLD_ARGS "(rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples)" | ||||
| #define THRESHOLD_STR_VAL(s) "Set threshold value for rate_ctr device:" OSMO_STRINGIFY_VAL(s) "\n" | ||||
| #define THRESHOLD_STRS \ | ||||
| 	THRESHOLD_STR_VAL(rx_overruns) \ | ||||
| 	THRESHOLD_STR_VAL(tx_underruns) \ | ||||
| 	THRESHOLD_STR_VAL(rx_drop_events) \ | ||||
| 	THRESHOLD_STR_VAL(rx_drop_samples) \ | ||||
| 	THRESHOLD_STR_VAL(tx_drop_events) \ | ||||
| 	THRESHOLD_STR_VAL(tx_drop_samples) | ||||
| #define INTV_ARGS "(per-second|per-minute|per-hour|per-day)" | ||||
| #define INTV_STR_VAL(s) "Threshold value sampled " OSMO_STRINGIFY_VAL(s) "\n" | ||||
| #define INTV_STRS \ | ||||
| 	INTV_STR_VAL(per-second) \ | ||||
| 	INTV_STR_VAL(per-minute) \ | ||||
| 	INTV_STR_VAL(per-hour) \ | ||||
| 	INTV_STR_VAL(per-day) | ||||
|  | ||||
| DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd, | ||||
| 	"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS, | ||||
| 	"Threshold rate for error counter\n" | ||||
| 	THRESHOLD_STRS | ||||
| 	"Value to set for threshold\n" | ||||
| 	INTV_STRS) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct ctr_threshold ctr; | ||||
|  | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
| 	rc = vty_ctr_name_2_id(argv[0]); | ||||
| 	if (rc < 0) { | ||||
| 		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s", | ||||
| 			argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ctr.ctr_id = (enum TrxCtr)rc; | ||||
| 	ctr.val = atoi(argv[1]); | ||||
| 	rc = vty_intv_name_2_id(argv[2]); | ||||
| 	if (rc < 0) { | ||||
| 		vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s", | ||||
| 			argv[0], ctr.val, argv[2], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ctr.intv = (enum rate_ctr_intv) rc; | ||||
| 	trx_rate_ctr_threshold_add(&ctr); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd, | ||||
| 	"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS, | ||||
| 	NO_STR "Threshold rate for error counter\n" | ||||
| 	THRESHOLD_STRS | ||||
| 	"Value to set for threshold\n" | ||||
| 	INTV_STRS) | ||||
| { | ||||
| 	int rc; | ||||
| 	struct ctr_threshold ctr; | ||||
|  | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
| 	rc = vty_ctr_name_2_id(argv[0]); | ||||
| 	if (rc < 0) { | ||||
| 		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s", | ||||
| 			argv[0], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ctr.ctr_id = (enum TrxCtr)rc; | ||||
| 	ctr.val = atoi(argv[1]); | ||||
| 	rc = vty_intv_name_2_id(argv[2]); | ||||
| 	if (rc < 0) { | ||||
| 		vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s", | ||||
| 			argv[0], ctr.val, argv[2], VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	ctr.intv = (enum rate_ctr_intv) rc; | ||||
| 	if (trx_rate_ctr_threshold_del(&ctr) < 0) { | ||||
| 		vty_out(vty, "no ctr-error-threshold: Entry to delete not found%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan, cfg_chan_cmd, | ||||
| 	"chan <0-100>", | ||||
| 	"Select a channel to configure\n" | ||||
| 	"Channel index\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
| 	int idx = atoi(argv[0]); | ||||
|  | ||||
| 	if (idx >= TRX_CHAN_MAX) { | ||||
| 		vty_out(vty, "Chan list full.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} else if (trx->cfg.multi_arfcn && trx->cfg.num_chans >= TRX_MCHAN_MAX) { | ||||
| 		vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s", | ||||
| 			TRX_MCHAN_MAX, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */ | ||||
| 		vty_out(vty, "Non-existent or non-consecutive chan %d.%s", | ||||
| 				idx, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} else if (trx->cfg.num_chans == idx)  { /* creating it */ | ||||
| 		trx->cfg.num_chans++; | ||||
| 		trx->cfg.chans[idx].trx = trx; | ||||
| 		trx->cfg.chans[idx].idx = idx; | ||||
| 	} | ||||
|  | ||||
| 	vty->node = CHAN_NODE; | ||||
| 	vty->index = &trx->cfg.chans[idx]; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd, | ||||
| 	"rx-path NAME", | ||||
| 	"Set the Rx Path\n" | ||||
| 	"Rx Path name\n") | ||||
| { | ||||
| 	struct trx_chan *chan = vty->index; | ||||
|  | ||||
| 	osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd, | ||||
| 	"tx-path NAME", | ||||
| 	"Set the Tx Path\n" | ||||
| 	"Tx Path name\n") | ||||
| { | ||||
| 	struct trx_chan *chan = vty->index; | ||||
|  | ||||
| 	osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int dummy_config_write(struct vty *v) | ||||
| { | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int config_write_trx(struct vty *vty) | ||||
| { | ||||
| 	struct trx_chan *chan; | ||||
| 	int i; | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	vty_out(vty, "trx%s", VTY_NEWLINE); | ||||
| 	if (trx->cfg.bind_addr) | ||||
| 		vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE); | ||||
| 	if (trx->cfg.remote_addr) | ||||
| 		vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE); | ||||
| 	if (trx->cfg.base_port != DEFAULT_TRX_PORT) | ||||
| 		vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE); | ||||
| 	if (trx->cfg.dev_args) | ||||
| 		vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE); | ||||
| 	if (trx->cfg.tx_sps != DEFAULT_TX_SPS) | ||||
| 		vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rx_sps != DEFAULT_RX_SPS) | ||||
| 		vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rtsc_set) | ||||
| 		vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rach_delay_set) | ||||
| 		vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE); | ||||
| 	if (trx->cfg.clock_ref != REF_INTERNAL) | ||||
| 		vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); | ||||
| 	vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	if (trx->cfg.offset != 0) | ||||
| 		vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rssi_offset != 0) | ||||
| 		vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	if (trx->cfg.sched_rr != 0) | ||||
| 		vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE); | ||||
| 	if (trx->cfg.stack_size != 0) | ||||
| 		vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE); | ||||
| 	trx_rate_ctr_threshold_write_config(vty, " "); | ||||
|  | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		chan = &trx->cfg.chans[i]; | ||||
| 		vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE); | ||||
| 		if (chan->rx_path) | ||||
| 			vty_out(vty, "  rx-path %s%s", chan->rx_path, VTY_NEWLINE); | ||||
| 		if (chan->tx_path) | ||||
| 			vty_out(vty, "  tx-path %s%s", chan->tx_path, VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx) | ||||
| { | ||||
| 	struct trx_chan *chan; | ||||
| 	int i; | ||||
| 	vty_out(vty, "TRX Config:%s", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE); | ||||
| 	vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc, | ||||
| 		trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay, | ||||
| 		trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE); | ||||
| 	vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); | ||||
| 	vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr, | ||||
| 		trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Stack size per Thread in BYTE (0 = OS default): %u%s", trx->cfg.stack_size, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE); | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		chan = &trx->cfg.chans[i]; | ||||
| 		vty_out(vty, "  Channel %u:%s", chan->idx, VTY_NEWLINE); | ||||
| 		if (chan->rx_path) | ||||
| 			vty_out(vty, "   Rx Path: %s%s", chan->rx_path, VTY_NEWLINE); | ||||
| 		if (chan->tx_path) | ||||
| 			vty_out(vty, "   Tx Path: %s%s", chan->tx_path, VTY_NEWLINE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| DEFUN(show_trx, show_trx_cmd, | ||||
| 	"show trx", | ||||
| 	SHOW_STR "Display information on the TRX\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx_dump_vty(vty, trx); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int trx_vty_is_config_node(struct vty *vty, int node) | ||||
| { | ||||
| 	switch (node) { | ||||
| 	case TRX_NODE: | ||||
| 	case CHAN_NODE: | ||||
| 		return 1; | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int trx_vty_go_parent(struct vty *vty) | ||||
| { | ||||
| 	switch (vty->node) { | ||||
| 	case TRX_NODE: | ||||
| 		vty->node = CONFIG_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 		break; | ||||
| 	case CHAN_NODE: | ||||
| 		vty->node = TRX_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 		break; | ||||
| 	default: | ||||
| 		vty->node = CONFIG_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 	} | ||||
|  | ||||
| 	return vty->node; | ||||
| } | ||||
|  | ||||
| static const char trx_copyright[] = | ||||
| 	"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n" | ||||
| 	"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n" | ||||
| 	"Copyright (C) 2015 Ettus Research LLC\r\n" | ||||
| 	"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" | ||||
| 	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" | ||||
| 	"This is free software: you are free to change and redistribute it.\r\n" | ||||
| 	"There is NO WARRANTY, to the extent permitted by law.\r\n"; | ||||
|  | ||||
| struct vty_app_info g_vty_info = { | ||||
| 	.name		= "OsmoTRX", | ||||
| 	.version	= PACKAGE_VERSION, | ||||
| 	.copyright	= trx_copyright, | ||||
| 	.go_parent_cb	= trx_vty_go_parent, | ||||
| 	.is_config_node	= trx_vty_is_config_node, | ||||
| }; | ||||
|  | ||||
| struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx) | ||||
| { | ||||
| 	struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx); | ||||
|  | ||||
| 	trx->cfg.bind_addr =  talloc_strdup(trx, DEFAULT_TRX_IP); | ||||
| 	trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP); | ||||
| 	trx->cfg.base_port = DEFAULT_TRX_PORT; | ||||
| 	trx->cfg.tx_sps = DEFAULT_TX_SPS; | ||||
| 	trx->cfg.rx_sps = DEFAULT_RX_SPS; | ||||
| 	trx->cfg.filler = FILLER_ZERO; | ||||
|  | ||||
| 	return trx; | ||||
| } | ||||
|  | ||||
| int trx_vty_init(struct trx_ctx* trx) | ||||
| { | ||||
| 	g_trx_ctx = trx; | ||||
| 	install_element_ve(&show_trx_cmd); | ||||
|  | ||||
| 	install_element(CONFIG_NODE, &cfg_trx_cmd); | ||||
|  | ||||
| 	install_node(&trx_node, config_write_trx); | ||||
| 	install_element(TRX_NODE, &cfg_bind_ip_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_remote_ip_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_base_port_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_dev_args_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_tx_sps_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rx_sps_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_test_rtsc_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_test_rach_delay_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_clock_ref_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_multi_arfcn_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_offset_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rssi_offset_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_swap_channels_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_egprs_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_ext_rach_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rt_prio_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_filler_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_stack_size_cmd); | ||||
|  | ||||
| 	install_element(TRX_NODE, &cfg_chan_cmd); | ||||
| 	install_node(&chan_node, dummy_config_write); | ||||
| 	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd); | ||||
| 	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1,73 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| #include "config_defs.h" | ||||
|  | ||||
| extern struct vty_app_info g_vty_info; | ||||
|  | ||||
| /* Maximum number of physical RF channels */ | ||||
| #define TRX_CHAN_MAX 8 | ||||
| /* Maximum number of carriers in multi-ARFCN mode */ | ||||
| #define TRX_MCHAN_MAX 3 | ||||
|  | ||||
| /* Samples-per-symbol for downlink path | ||||
|  *     4 - Uses precision modulator (more computation, less distortion) | ||||
|  *     1 - Uses minimized modulator (less computation, more distortion) | ||||
|  * | ||||
|  *     Other values are invalid. Receive path (uplink) is always | ||||
|  *     downsampled to 1 sps. Default to 4 sps for all cases. | ||||
|  */ | ||||
| #define DEFAULT_TX_SPS		4 | ||||
|  | ||||
| /* | ||||
|  * Samples-per-symbol for uplink (receiver) path | ||||
|  *     Do not modify this value. EDGE configures 4 sps automatically on | ||||
|  *     B200/B210 devices only. Use of 4 sps on the receive path for other | ||||
|  *     configurations is not supported. | ||||
|  */ | ||||
| #define DEFAULT_RX_SPS		1 | ||||
|  | ||||
| /* Default configuration parameters */ | ||||
| #define DEFAULT_TRX_PORT	5700 | ||||
| #define DEFAULT_TRX_IP		"127.0.0.1" | ||||
| #define DEFAULT_CHANS		1 | ||||
|  | ||||
| struct trx_ctx; | ||||
|  | ||||
| struct trx_chan { | ||||
| 	struct trx_ctx *trx; /* backpointer */ | ||||
| 	unsigned int idx; /* channel index */ | ||||
| 	char *rx_path; | ||||
| 	char *tx_path; | ||||
| }; | ||||
|  | ||||
| struct trx_ctx { | ||||
| 	struct { | ||||
| 		char *bind_addr; | ||||
| 		char *remote_addr; | ||||
| 		char *dev_args; | ||||
| 		unsigned int base_port; | ||||
| 		unsigned int tx_sps; | ||||
| 		unsigned int rx_sps; | ||||
| 		unsigned int rtsc; | ||||
| 		bool rtsc_set; | ||||
| 		unsigned int rach_delay; | ||||
| 		bool rach_delay_set; | ||||
| 		enum ReferenceType clock_ref; | ||||
| 		enum FillerType filler; | ||||
| 		bool multi_arfcn; | ||||
| 		double offset; | ||||
| 		double rssi_offset; | ||||
| 		bool swap_channels; | ||||
| 		bool ext_rach; | ||||
| 		bool egprs; | ||||
| 		unsigned int sched_rr; | ||||
| 		unsigned int stack_size; | ||||
| 		unsigned int num_chans; | ||||
| 		struct trx_chan chans[TRX_CHAN_MAX]; | ||||
| 	} cfg; | ||||
| }; | ||||
|  | ||||
| int trx_vty_init(struct trx_ctx* trx); | ||||
| struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx); | ||||
| @@ -2,8 +2,6 @@ | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * Copyright 2011 Range Networks, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -43,26 +41,11 @@ const BitVector GSM::gTrainingSequence[] = { | ||||
|     BitVector("11101111000100101110111100"), | ||||
| }; | ||||
|  | ||||
| const BitVector GSM::gEdgeTrainingSequence[] = { | ||||
|     BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"), | ||||
|     BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"), | ||||
|     BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"), | ||||
|     BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"), | ||||
|     BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"), | ||||
|     BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"), | ||||
|     BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"), | ||||
|     BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"), | ||||
| }; | ||||
|  | ||||
| const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000"); | ||||
|  | ||||
| /* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000");  /* GSM, GMSK (default) */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101");  /* EGPRS, 8-PSK */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111");  /* EGPRS, GMSK */ | ||||
| const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000"); | ||||
|  | ||||
| //                               |-head-||---------midamble----------------------||--------------data----------------||t| | ||||
| const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000"); | ||||
| const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011"); | ||||
|  | ||||
|  | ||||
| int32_t GSM::FNDelta(int32_t v1, int32_t v2) | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
| /* | ||||
| * Copyright 2008-2011 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -48,18 +46,15 @@ namespace GSM { | ||||
|  | ||||
| /** GSM Training sequences from GSM 05.02 5.2.3. */ | ||||
| extern const BitVector gTrainingSequence[]; | ||||
| extern const BitVector gEdgeTrainingSequence[]; | ||||
|  | ||||
| /** C0T0 filler burst, GSM 05.02, 5.2.6 */ | ||||
| extern const BitVector gDummyBurst; | ||||
|  | ||||
| /** Random access burst synch. sequence */ | ||||
| extern const BitVector gRACHSynchSequenceTS0; | ||||
| extern const BitVector gRACHSynchSequenceTS1; | ||||
| extern const BitVector gRACHSynchSequenceTS2; | ||||
| /** Random access burst synch. sequence, GSM 05.02 5.2.7 */ | ||||
| extern const BitVector gRACHBurst; | ||||
| extern const BitVector gRACHSynchSequence; | ||||
|  | ||||
| /** Synchronization burst sync sequence */ | ||||
| extern const BitVector gSCHSynchSequence; | ||||
|  | ||||
| /**@name Modulus operations for frame numbers. */ | ||||
| //@{ | ||||
| @@ -168,7 +163,7 @@ class Time { | ||||
|         unsigned newTN = (mTN + other.mTN) % 8; | ||||
| 		uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe; | ||||
|         return Time(newFN,newTN); | ||||
|     } | ||||
|     }  | ||||
|  | ||||
| 	int operator-(const Time& other) const | ||||
| 	{ | ||||
|   | ||||
							
								
								
									
										33
									
								
								INSTALLATION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								INSTALLATION
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| Installation Requirements | ||||
|  | ||||
|  | ||||
|  | ||||
| OpenBTS 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). | ||||
| 	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 | ||||
| information. | ||||
|  | ||||
							
								
								
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							| @@ -1,8 +1,5 @@ | ||||
| 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. | ||||
| @@ -15,9 +12,17 @@ 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 OsmoTRX is the provision of telecommunications service | ||||
| The primary function of OpenBTS 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. | ||||
| @@ -34,7 +39,7 @@ The legal restrictions listed here are not necessarily exhaustive. | ||||
|  | ||||
| Note to US Government Users | ||||
|  | ||||
| The OsmoTRX software applications and associated documentation are "Commercial | ||||
| The OpenBTS 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 | ||||
| @@ -49,12 +54,13 @@ and AGPLv3. | ||||
| Note to US Government Contractors | ||||
|  | ||||
| GPL is not compatible with "government purpose rights" (GPR).  If you receive | ||||
| OsmoTRX software under a GPL and deliver it under GPR, you will be in violation | ||||
| OpenBTS 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 | ||||
|  | ||||
| The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file | ||||
| 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 | ||||
| for more information on the license for this distribution. | ||||
|   | ||||
							
								
								
									
										22
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								Makefile.am
									
									
									
									
									
								
							| @@ -22,30 +22,24 @@ include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| ACLOCAL_AMFLAGS = -I config | ||||
| 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 | ||||
| AM_CXXFLAGS = -Wall -pthread -ldl | ||||
| #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
| #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
|  | ||||
| # Order must be preserved | ||||
| SUBDIRS = \ | ||||
| 	doc \ | ||||
| 	sqlite3 \ | ||||
| 	CommonLibs \ | ||||
| 	GSM \ | ||||
| 	Transceiver52M \ | ||||
| 	contrib \ | ||||
| 	tests | ||||
| 	Transceiver52M | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	autogen.sh \ | ||||
| 	INSTALLATION \ | ||||
| 	LEGAL \ | ||||
| 	COPYING \ | ||||
| 	README.md | ||||
| 	README | ||||
|  | ||||
| AM_DISTCHECK_CONFIGURE_FLAGS = \ | ||||
| 	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) | ||||
|  | ||||
| .PHONY: release | ||||
|  | ||||
| @RELMAKE@ | ||||
|  | ||||
| dox: FORCE | ||||
| 	doxygen doxconfig | ||||
|   | ||||
| @@ -18,21 +18,21 @@ | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
|  | ||||
| top_srcdir = $(abs_top_srcdir) | ||||
| top_builddir = $(abs_top_builddir) | ||||
|  | ||||
| COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs | ||||
| GSM_INCLUDEDIR = $(top_srcdir)/GSM | ||||
| SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3 | ||||
|  | ||||
| STD_DEFINES_AND_INCLUDES = \ | ||||
| 	$(SVNDEV) \ | ||||
| 	-I$(COMMON_INCLUDEDIR) \ | ||||
| 	-I$(GSM_INCLUDEDIR) | ||||
| 	-I$(GSM_INCLUDEDIR) \ | ||||
| 	-I$(SQLITE_INCLUDEDIR) | ||||
|  | ||||
| COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la | ||||
| GSM_LA = $(top_builddir)/GSM/libGSM.la | ||||
|  | ||||
| if ARCH_ARM | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la | ||||
| else | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la | ||||
| endif | ||||
| SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl | ||||
|  | ||||
| MOSTLYCLEANFILES = *~ | ||||
|   | ||||
							
								
								
									
										168
									
								
								README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| Welcome to the OpenBTS source code. | ||||
|  | ||||
|  | ||||
| For free support, please subscribe to openbts-discuss@lists.sourceforge.net. | ||||
| See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss | ||||
| and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details. | ||||
|  | ||||
| For additional information, refer to http://openbts.org. | ||||
|  | ||||
|  | ||||
| These are the directories: | ||||
|  | ||||
| AsteriskConfig	Asterisk configuration files for use with OpenBTS. | ||||
| CommonLib	Common-use libraries, mostly C++ wrappers for basic facilities. | ||||
| Control		Control-layer functions for the protocols of GSM 04.08 and SIP. | ||||
| GSM		The GSM stack. | ||||
| SIP		Components of the SIP state machines ued by the control layer. | ||||
| SMS		The SMS stack. | ||||
| SR		The subscriber registry. | ||||
| TRXManager	The interface between the GSM stack and the radio. | ||||
| Transceiver	The software transceiver and specific installation tests. | ||||
| apps		OpenBTS application binaries. | ||||
| doc		Project documentation. | ||||
| tests		Test fixtures for subsets of OpenBTS components. | ||||
| smqueue		RFC-3428 store-and-forward server for SMS | ||||
|  | ||||
|  | ||||
|  | ||||
| By default, OpenBTS assumes the following UDP port assignments: | ||||
|  | ||||
| 5060 -- Asterisk SIP interface | ||||
| 5061 -- local SIP softphone | ||||
| 5062 -- OpenBTS SIP interface | ||||
| 5063 -- smqueue SIP interface | ||||
| 5064 -- subscriber registry SIP interface | ||||
| 5700-range -- OpenBTS-transceiver interface | ||||
|  | ||||
| These can be controlled in the CONFIG table in /etc/OpenBTS.db. | ||||
|  | ||||
| Standrd paths: | ||||
| /OpenBTS -- Binary installation. | ||||
| /etc/OpenBTS -- Configuration databases. | ||||
| /var/run/OpenBTS -- Real-time reporting databases. | ||||
|  | ||||
| The script apps/setUpFiles.sh will create these directories and install the | ||||
| correct files in them. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| Release history: | ||||
|  | ||||
| Release	Name		SVN Reposiory	SVN Rev	Comments | ||||
|  | ||||
| 1.0	(none)		SF.net		??		completed L1, L2 | ||||
|  | ||||
| 1.1	Arnaudville	GNU Radio	r10019 (trunk) | ||||
|  | ||||
| 1.2	Breaux Bridge	GNU Radio	r10088 (trunk)	GNU Build, very early assignment | ||||
|  | ||||
| 1.3	Carencro	KSP		r1 (trunk)	first post-injunction release | ||||
|  | ||||
| 1.4	Donaldsonville	KSP		r23 (trunk)	fixed Ubuntu build error | ||||
|  | ||||
| 1.5	Eunice		KSP		r39 (trunk)	fixed L2 bugs related to segmentation | ||||
| 							removed incomplete SMS directory | ||||
| 							moved "abort" calls into L3 subclasses | ||||
|  | ||||
| 1.6	New Iberia	KSP		r130 (trunk)	import of all 2.2 improvements to non-SMS release | ||||
|  | ||||
|  | ||||
| 2.0	St. Francisville KSP		r54 (smswork)	SMS support | ||||
| 							file-based configuration | ||||
|  | ||||
| 2.1	Grand Coteau	KSP		r70 (smswork)	DTMF support | ||||
| 							fixed more Linux-related build errors | ||||
| 								-lpthread | ||||
| 								TLMessage constructor | ||||
| 							expanded stack to prevent overflows in Linux | ||||
| 							moved gSIPInterface to main app | ||||
| 							fixed iterator bug in Pager | ||||
|  | ||||
| 2.2	Houma		KSP		r122 (smswork)	added LEGAL notice | ||||
| 							removed Assert classes | ||||
| 							stop paging on page response | ||||
| 							fixed Pager-spin bug | ||||
| 							fixed Transceiver spin bugs | ||||
| 							fixed 2^32 microsecond rollover bug | ||||
| 							reduced stack footprints in Transceiver | ||||
| 							fixed SMS timestamps | ||||
| 							check LAI before using TMSI in LUR | ||||
| 							reduced memory requirement by 75% | ||||
| 							removed PagerTest | ||||
| 							fixed stale-transaction bug in paging handler | ||||
| 							fixed USRP clock rollover bug | ||||
| 							faster call connection | ||||
| 							new USRPDevice design | ||||
|  | ||||
| 2.3	Jean Lafitte	KSP		r190? (trunk)	check for out-of-date RACH bursts | ||||
| 							better TRX-GSM clock sync | ||||
| 							formal logging system | ||||
| 							command line interface | ||||
| 							emergency call setup | ||||
|  | ||||
| 2.4	Kinder		KSP		r208? (trunk)	fixed BCCH neighbor list bug | ||||
| 							support for neighbor lists | ||||
| 							fixed support for non-local Asterisk servers | ||||
| 							cleaner configuration management | ||||
| 							more realtime control of BCCH parameters | ||||
| 							proper rejection of Hold messages | ||||
| 							fixed L3 hanging bug in MTDCheckBYE | ||||
|  | ||||
| 2.4.1	Kinder		KSP		r462		fixed lots of valgrind errors | ||||
|  | ||||
| 2.4.2	Kinder		KSP		r482		zero-length calling party number bug | ||||
| 							g++ 4.4 #includes | ||||
|  | ||||
| 2.5	Lacassine	KSP		r551		imported Joshua Lackey patches | ||||
| 							SIP fixes from Anne Kwong | ||||
| 							SIP fixes from testing with SMS server | ||||
| 							L3 TI handling fixes | ||||
| 							SMS server support | ||||
| 							GNU Radio 3.2 compatibility | ||||
| 							configurable max range and LU-reject cause | ||||
| 							"page" & "testcall" CLI features | ||||
|  | ||||
| 2.5.1	Lacassine	KSP		r595		fixed some build bugs for some Linux distros | ||||
|  | ||||
| 2.5.2	Lacassine	KSP		r630		fixed channel assignment bug for Nokia DCT4+ handsets | ||||
|  | ||||
| 2.5.3	Lacassine	KSP		r756		merged fix for transceiver startup crash | ||||
| 								due to use of uninitialized variables (r646) | ||||
| 							merged fix for fusb bug from trunk (r582) | ||||
|  | ||||
| 2.5.4	Lacassine	KSP		r812		merged fixes to build under latest Fedora and | ||||
| 								to build with git GnuRadio (r814) | ||||
|  | ||||
| 2.6	Mamou		KSP		r886		fixed infamous fusb bug (r582) | ||||
| 							fixed idle-filling table size bug | ||||
| 							smoother uplink power control | ||||
| 							load-limiting downlink power control | ||||
| 							new "config" features (optional, static) | ||||
| 							IMEI interrogation | ||||
| 							fixed MOD "missing FIFO" bug | ||||
| 							configurable short code features | ||||
| 							fixed transceiver startup crash (r646) | ||||
| 							readline support is back | ||||
| 							fixed timing advance bug (r844) | ||||
| 							added CLI "chans" command | ||||
| 							track time-of-use in TMSI table (r844) | ||||
| 							added CLI "noise" command (r844) | ||||
| 							added CLI "rxpower" command (r844) | ||||
| 							added CLI "unconfig" command | ||||
|  | ||||
| 2.7	Natchitoches	Range	rxxx			(never released publicly) | ||||
| 							converted TMSITable to sqlite3 (r902) | ||||
| 							sqlite3-based configuration (r???) | ||||
| 							converted Logger to syslogd (r903) | ||||
| 							added support for rest octets (r1022) | ||||
| 							external database for transaction reporting (r1184) | ||||
| 							external database for channel status reporting (r1203) | ||||
| 							in-call delivery and submission of text messages (r1231) | ||||
| 							RFC-2833 DMTF (r1249) | ||||
|  | ||||
| 2.8	Opelousas	Range	rxxx			move databases to /etc and /var | ||||
| 							RRLP aiding support | ||||
|  | ||||
|  | ||||
							
								
								
									
										66
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,66 +0,0 @@ | ||||
| About OsmTRX | ||||
| ============ | ||||
|  | ||||
| OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
| physical layer of a BTS comprising the following 3GPP specifications: | ||||
|  | ||||
| * TS 05.01 "Physical layer on the radio path" | ||||
| * TS 05.02 "Multiplexing and Multiple Access on the Radio Path" | ||||
| * TS 05.04 "Modulation" | ||||
| * TS 05.10 "Radio subsystem synchronization" | ||||
|  | ||||
| OsmoTRX is based on the transceiver code from the | ||||
| [OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup | ||||
| to operate independently with the purpose of using with non-OpenBTS software and | ||||
| projects, while still maintaining backwards compatibility with OpenBTS when | ||||
| possible. Currently there are numerous features contained in OsmoTRX that extend | ||||
| the functionality of the OpenBTS transceiver. These features include enhanced | ||||
| support for various embedded platforms - notably ARM - and dual channel | ||||
| diversity support for the Fairwaves umtrx. | ||||
|  | ||||
| Homepage | ||||
| -------- | ||||
|  | ||||
| The official homepage of the project is | ||||
| <https://osmocom.org/projects/osmotrx/wiki/OsmoTRX> | ||||
|  | ||||
| GIT Repository | ||||
| -------------- | ||||
|  | ||||
| You can clone from the official osmo-trx.git repository using | ||||
|  | ||||
|         git clone git://git.osmocom.org/osmo-trx.git | ||||
|  | ||||
| There is a cgit interface at <http://git.osmocom.org/osmo-trx/> | ||||
|  | ||||
| Documentation | ||||
| ------------- | ||||
|  | ||||
| Doxygen-generated API documentation is generated during the build process, but | ||||
| also available online for each of the sub-libraries at User Manual for OsmoTRX | ||||
| can be generated during the build process, and is also available online at | ||||
| <http://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>. | ||||
|  | ||||
| Mailing List | ||||
| ------------ | ||||
|  | ||||
| Discussions related to OsmoTRX are happening on the openbsc@lists.osmocom.org | ||||
| mailing list, please see <https://lists.osmocom.org/mailman/listinfo/openbsc> | ||||
| for subscription options and the list archive. | ||||
|  | ||||
| Please observe the [Osmocom Mailing List | ||||
| Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules) | ||||
| when posting. | ||||
|  | ||||
| Contributing | ||||
| ------------ | ||||
|  | ||||
| Our coding standards are described at | ||||
| <https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards> | ||||
|  | ||||
| We us a gerrit based patch submission/review process for managing contributions. | ||||
| Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> | ||||
| for more details | ||||
|  | ||||
| The current patch queue for OsmoTRX can be seen at | ||||
| <https://gerrit.osmocom.org/q/project:osmo-trx+status:open> | ||||
| @@ -1,109 +0,0 @@ | ||||
| /* | ||||
|  * Polyphase channelizer | ||||
|  * | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <math.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| #include <cstdio> | ||||
|  | ||||
| #include "Channelizer.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "fft.h" | ||||
| #include "convolve.h" | ||||
| } | ||||
|  | ||||
| static void deinterleave(const float *in, size_t ilen, | ||||
| 			 float **out, size_t olen, size_t m) | ||||
| { | ||||
| 	size_t i, n; | ||||
|  | ||||
| 	for (i = 0; i < olen; i++) { | ||||
| 		for (n = 0; n < m; n++) { | ||||
| 			out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0]; | ||||
| 			out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| size_t Channelizer::inputLen() const | ||||
| { | ||||
| 	return blockLen * m; | ||||
| } | ||||
|  | ||||
| size_t Channelizer::outputLen() const | ||||
| { | ||||
| 	return blockLen; | ||||
| } | ||||
|  | ||||
| float *Channelizer::outputBuffer(size_t chan) const | ||||
| { | ||||
| 	if (chan >= m) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return hInputs[chan]; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Implementation based on material found in: | ||||
|  * | ||||
|  * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, | ||||
|  *     Prentice Hall, 2006." | ||||
|  */ | ||||
| bool Channelizer::rotate(const float *in, size_t len) | ||||
| { | ||||
| 	size_t hSize = 2 * hLen * sizeof(float); | ||||
|  | ||||
| 	if (!checkLen(blockLen, len)) | ||||
| 		return false; | ||||
|  | ||||
| 	deinterleave(in, len, hInputs, blockLen, m); | ||||
|  | ||||
| 	/* | ||||
| 	 * Convolve through filterbank while applying and saving sample history | ||||
| 	 */ | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		memcpy(&hInputs[i][2 * -hLen], hist[i], hSize); | ||||
| 		memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize); | ||||
|  | ||||
| 		convolve_real(hInputs[i], blockLen, | ||||
| 			      subFilters[i], hLen, | ||||
| 			      hOutputs[i], blockLen, | ||||
| 			      0, blockLen); | ||||
| 	} | ||||
|  | ||||
| 	cxvec_fft(fftHandle); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* Setup channelizer paramaters */ | ||||
| Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen) | ||||
| 	: ChannelizerBase(m, blockLen, hLen) | ||||
| { | ||||
| } | ||||
|  | ||||
| Channelizer::~Channelizer() | ||||
| { | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| #ifndef _CHANNELIZER_RX_H_ | ||||
| #define _CHANNELIZER_RX_H_ | ||||
|  | ||||
| #include "ChannelizerBase.h" | ||||
|  | ||||
| class Channelizer : public ChannelizerBase { | ||||
| public: | ||||
| 	/** Constructor for channelizing filter bank | ||||
| 	    @param m number of physical channels | ||||
| 	    @param blockLen number of samples per output of each iteration | ||||
| 	    @param hLen number of taps in each constituent filter path | ||||
| 	*/ | ||||
| 	Channelizer(size_t m, size_t blockLen, size_t hLen = 16); | ||||
| 	~Channelizer(); | ||||
|  | ||||
| 	/* Return required input and output buffer lengths */ | ||||
| 	size_t inputLen() const; | ||||
| 	size_t outputLen() const; | ||||
|  | ||||
| 	/** Rotate "input commutator" and drive samples through filterbank | ||||
| 	    @param in complex input vector  | ||||
| 	    @param iLen number of samples in buffer (must match block length) | ||||
| 	    @return false on error and true otherwise | ||||
| 	*/ | ||||
| 	bool rotate(const float *in, size_t iLen); | ||||
|  | ||||
| 	/** Get buffer for an output path | ||||
| 	    @param chan channel number of filterbank | ||||
|             @return NULL on error and pointer to buffer otherwise | ||||
| 	*/ | ||||
| 	float *outputBuffer(size_t chan) const; | ||||
| }; | ||||
|  | ||||
| #endif /* _CHANNELIZER_RX_H_ */ | ||||
| @@ -1,254 +0,0 @@ | ||||
| /* | ||||
|  * Polyphase channelizer | ||||
|  * | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <malloc.h> | ||||
| #include <math.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| #include <cstdio> | ||||
|  | ||||
| #include "Logger.h" | ||||
| #include "ChannelizerBase.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "fft.h" | ||||
| } | ||||
|  | ||||
| static float sinc(float x) | ||||
| { | ||||
| 	if (x == 0.0f) | ||||
| 		return 0.999999999999f; | ||||
|  | ||||
| 	return sin(M_PI * x) / (M_PI * x); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * There are more efficient reversal algorithms, but we only reverse at | ||||
|  * initialization so we don't care. | ||||
|  */ | ||||
| static void reverse(float *buf, size_t len) | ||||
| { | ||||
| 	float tmp[2 * len]; | ||||
| 	memcpy(tmp, buf, 2 * len * sizeof(float)); | ||||
|  | ||||
| 	for (size_t i = 0; i < len; i++) { | ||||
| 		buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0]; | ||||
| 		buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1]; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Create polyphase filterbank | ||||
|  * | ||||
|  * Implementation based material found in, | ||||
|  * | ||||
|  * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, | ||||
|  *     Prentice Hall, 2006." | ||||
|  */ | ||||
| bool ChannelizerBase::initFilters() | ||||
| { | ||||
| 	size_t protoLen = m * hLen; | ||||
| 	float *proto; | ||||
| 	float sum = 0.0f, scale = 0.0f; | ||||
| 	float midpt = (float) (protoLen - 1.0) / 2.0; | ||||
|  | ||||
| 	/* | ||||
| 	 * Allocate 'M' partition filters and the temporary prototype | ||||
| 	 * filter. Coefficients are real only and must be 16-byte memory | ||||
| 	 * aligned for SSE usage. | ||||
| 	 */ | ||||
| 	proto = new float[protoLen]; | ||||
| 	if (!proto) | ||||
| 		return false; | ||||
|  | ||||
| 	subFilters = (float **) malloc(sizeof(float *) * m); | ||||
| 	if (!subFilters) { | ||||
| 		delete[] proto; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		subFilters[i] = (float *) | ||||
| 				memalign(16, hLen * 2 * sizeof(float)); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Generate the prototype filter with a Blackman-harris window. | ||||
| 	 * Scale coefficients with DC filter gain set to unity divided | ||||
| 	 * by the number of channels. | ||||
| 	 */ | ||||
| 	float a0 = 0.35875; | ||||
| 	float a1 = 0.48829; | ||||
| 	float a2 = 0.14128; | ||||
| 	float a3 = 0.01168; | ||||
|  | ||||
| 	for (size_t i = 0; i < protoLen; i++) { | ||||
| 		proto[i] = sinc(((float) i - midpt) / (float) m); | ||||
| 		proto[i] *= a0 - | ||||
| 			    a1 * cos(2 * M_PI * i / (protoLen - 1)) + | ||||
| 			    a2 * cos(4 * M_PI * i / (protoLen - 1)) - | ||||
| 			    a3 * cos(6 * M_PI * i / (protoLen - 1)); | ||||
| 		sum += proto[i]; | ||||
| 	} | ||||
| 	scale = (float) m / sum; | ||||
|  | ||||
| 	/* | ||||
| 	 * Populate partition filters and reverse the coefficients per | ||||
| 	 * convolution requirements. | ||||
| 	 */ | ||||
| 	for (size_t i = 0; i < hLen; i++) { | ||||
| 		for (size_t n = 0; n < m; n++) { | ||||
| 			subFilters[n][2 * i + 0] = proto[i * m + n] * scale; | ||||
| 			subFilters[n][2 * i + 1] = 0.0f; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < m; i++) | ||||
| 		reverse(subFilters[i], hLen); | ||||
|  | ||||
| 	delete[] proto; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ChannelizerBase::initFFT() | ||||
| { | ||||
| 	size_t size; | ||||
|  | ||||
| 	if (fftInput || fftOutput || fftHandle) | ||||
| 		return false; | ||||
|  | ||||
| 	size = blockLen * m * 2 * sizeof(float); | ||||
| 	fftInput = (float *) fft_malloc(size); | ||||
| 	memset(fftInput, 0, size); | ||||
|  | ||||
| 	size = (blockLen + hLen) * m * 2 * sizeof(float); | ||||
| 	fftOutput = (float *) fft_malloc(size); | ||||
| 	memset(fftOutput, 0, size); | ||||
|  | ||||
| 	if (!fftInput | !fftOutput) { | ||||
| 		LOG(ALERT) << "Memory allocation error"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	fftHandle = init_fft(0, m, blockLen, blockLen + hLen, | ||||
| 			     fftInput, fftOutput, hLen); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ChannelizerBase::mapBuffers() | ||||
| { | ||||
| 	if (!fftHandle) { | ||||
| 		LOG(ALERT) << "FFT buffers not initialized"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	hInputs = (float **) malloc(sizeof(float *) * m); | ||||
| 	hOutputs = (float **) malloc(sizeof(float *) * m); | ||||
| 	if (!hInputs | !hOutputs) | ||||
| 		return false; | ||||
|  | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)]; | ||||
| 		hOutputs[i] = &fftInput[2 * (i * blockLen)]; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Setup filterbank internals | ||||
|  */ | ||||
| bool ChannelizerBase::init() | ||||
| { | ||||
| 	/* | ||||
| 	 * Filterbank coefficients, fft plan, history, and output sample | ||||
| 	 * rate conversion blocks | ||||
| 	 */ | ||||
| 	if (!initFilters()) { | ||||
| 		LOG(ALERT) << "Failed to initialize channelizing filter"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	hist = (float **) malloc(sizeof(float *) * m); | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		hist[i] = new float[2 * hLen]; | ||||
| 		memset(hist[i], 0, 2 * hLen * sizeof(float)); | ||||
| 	} | ||||
|  | ||||
| 	if (!initFFT()) { | ||||
| 		LOG(ALERT) << "Failed to initialize FFT"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	mapBuffers(); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* Check vector length validity */ | ||||
| bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen) | ||||
| { | ||||
| 	if (outerLen != innerLen * m) { | ||||
| 		LOG(ALERT) << "Invalid outer length " << innerLen | ||||
| 			   <<  " is not multiple of " << blockLen; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (innerLen != blockLen) { | ||||
| 		LOG(ALERT) << "Invalid inner length " << outerLen | ||||
| 			   <<  " does not equal " << blockLen; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Setup channelizer paramaters | ||||
|  */ | ||||
| ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen) | ||||
| 	: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL), | ||||
| 	  fftInput(NULL), fftOutput(NULL), fftHandle(NULL) | ||||
| { | ||||
| 	this->m = m; | ||||
| 	this->hLen = hLen; | ||||
| 	this->blockLen = blockLen; | ||||
| } | ||||
|  | ||||
| ChannelizerBase::~ChannelizerBase() | ||||
| { | ||||
| 	free_fft(fftHandle); | ||||
|  | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		free(subFilters[i]); | ||||
| 		delete[] hist[i]; | ||||
| 	} | ||||
|  | ||||
| 	fft_free(fftInput); | ||||
| 	fft_free(fftOutput); | ||||
|  | ||||
| 	free(hInputs); | ||||
| 	free(hOutputs); | ||||
| 	free(hist); | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| #ifndef _CHANNELIZER_BASE_H_ | ||||
| #define _CHANNELIZER_BASE_H_ | ||||
|  | ||||
| class ChannelizerBase { | ||||
| protected: | ||||
| 	ChannelizerBase(size_t m, size_t blockLen, size_t hLen); | ||||
| 	~ChannelizerBase(); | ||||
|  | ||||
| 	/* Channelizer parameters */ | ||||
| 	size_t m; | ||||
| 	size_t hLen; | ||||
| 	size_t blockLen; | ||||
|  | ||||
| 	/* Channelizer filterbank sub-filters */ | ||||
| 	float **subFilters; | ||||
|  | ||||
| 	/* Input/Output buffers */ | ||||
| 	float **hInputs, **hOutputs, **hist; | ||||
| 	float *fftInput, *fftOutput; | ||||
|  | ||||
| 	/* Pointer to opaque FFT instance */ | ||||
| 	struct fft_hdl *fftHandle; | ||||
|  | ||||
| 	/* Initializer internals */ | ||||
| 	bool initFilters(); | ||||
| 	bool initFFT(); | ||||
| 	void releaseFilters(); | ||||
|  | ||||
| 	/* Map overlapped FFT and filter I/O buffers */ | ||||
| 	bool mapBuffers(); | ||||
|  | ||||
| 	/* Buffer length validity checking */ | ||||
| 	bool checkLen(size_t innerLen, size_t outerLen); | ||||
| public: | ||||
| 	/* Initilize channelizer/synthesis filter internals */ | ||||
| 	bool init(); | ||||
| }; | ||||
|  | ||||
| #endif /* _CHANNELIZER_BASE_H_ */ | ||||
| @@ -21,86 +21,81 @@ | ||||
|  | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| SUBDIRS = arch device | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common | ||||
| AM_CXXFLAGS = -ldl -lpthread | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| SUBDIRS = arm x86 | ||||
|  | ||||
| noinst_LTLIBRARIES = libtransceiver_common.la | ||||
| if ARCH_ARM | ||||
| ARCH_LA = arm/libarch.la | ||||
| else | ||||
| ARCH_LA = x86/libarch.la | ||||
| endif | ||||
|  | ||||
| if USRP1  | ||||
| AM_CPPFLAGS += $(USRP_CFLAGS) | ||||
| else | ||||
| AM_CPPFLAGS += $(UHD_CFLAGS) | ||||
| endif | ||||
|  | ||||
| rev2dir = $(datadir)/usrp/rev2 | ||||
| rev4dir = $(datadir)/usrp/rev4 | ||||
|  | ||||
| dist_rev2_DATA = std_inband.rbf | ||||
| dist_rev4_DATA = std_inband.rbf | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	README \ | ||||
| 	README.Talgorithm | ||||
|  | ||||
| noinst_LTLIBRARIES = libtransceiver.la | ||||
|  | ||||
| COMMON_SOURCES = \ | ||||
| 	radioInterface.cpp \ | ||||
| 	radioVector.cpp \ | ||||
| 	radioClock.cpp \ | ||||
| 	radioBuffer.cpp \ | ||||
| 	sigProcLib.cpp \ | ||||
| 	signalVector.cpp \ | ||||
| 	Transceiver.cpp \ | ||||
| 	ChannelizerBase.cpp \ | ||||
| 	Channelizer.cpp \ | ||||
| 	Synthesis.cpp \ | ||||
| 	proto_trxd.c | ||||
| 	sch.c | ||||
|  | ||||
| libtransceiver_common_la_SOURCES = \ | ||||
| libtransceiver_la_SOURCES = \ | ||||
| 	$(COMMON_SOURCES) \ | ||||
| 	Resampler.cpp \ | ||||
| 	radioInterfaceResamp.cpp \ | ||||
| 	radioInterfaceMulti.cpp | ||||
| 	radioInterfaceDiversity.cpp | ||||
|  | ||||
| bin_PROGRAMS = osmo-trx | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	Complex.h \ | ||||
| 	radioInterface.h \ | ||||
| 	radioVector.h \ | ||||
| 	radioClock.h \ | ||||
| 	radioBuffer.h \ | ||||
| 	radioDevice.h \ | ||||
| 	sigProcLib.h \ | ||||
| 	signalVector.h \ | ||||
| 	Transceiver.h \ | ||||
| 	USRPDevice.h \ | ||||
| 	Resampler.h \ | ||||
| 	ChannelizerBase.h \ | ||||
| 	Channelizer.h \ | ||||
| 	Synthesis.h \ | ||||
| 	proto_trxd.h | ||||
| 	common/convolve.h \ | ||||
| 	common/convert.h \ | ||||
| 	common/scale.h \ | ||||
| 	common/mult.h | ||||
|  | ||||
| COMMON_LDADD = \ | ||||
| 	libtransceiver_common.la \ | ||||
| osmo_trx_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_LDADD = \ | ||||
| 	libtransceiver.la \ | ||||
| 	$(ARCH_LA) \ | ||||
| 	$(GSM_LA) \ | ||||
| 	$(COMMON_LA) \ | ||||
| 	$(FFTWF_LIBS) \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOCTRL_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) | ||||
| 	$(SQLITE_LA) \ | ||||
| 	$(LIBOSMOCORE_LIBS) | ||||
|  | ||||
| bin_PROGRAMS = | ||||
|  | ||||
| if DEVICE_UHD | ||||
| bin_PROGRAMS += osmo-trx-uhd | ||||
| osmo_trx_uhd_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_uhd_LDADD = \ | ||||
| 	$(builddir)/device/uhd/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(UHD_LIBS) | ||||
| osmo_trx_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) | ||||
| endif | ||||
|  | ||||
| if DEVICE_USRP1 | ||||
| bin_PROGRAMS += osmo-trx-usrp1 | ||||
| osmo_trx_usrp1_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_usrp1_LDADD = \ | ||||
| 	$(builddir)/device/usrp1/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(USRP_LIBS) | ||||
| osmo_trx_usrp1_CPPFLAGS  = $(AM_CPPFLAGS) $(USRP_CFLAGS) | ||||
| endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| bin_PROGRAMS += osmo-trx-lms | ||||
| osmo_trx_lms_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_lms_LDADD = \ | ||||
| 	$(builddir)/device/lms/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(LMS_LIBS) | ||||
| osmo_trx_lms_CPPFLAGS  = $(AM_CPPFLAGS) $(LMS_CFLAGS) | ||||
| if USRP1  | ||||
| libtransceiver_la_SOURCES += USRPDevice.cpp | ||||
| osmo_trx_LDADD += $(USRP_LIBS) | ||||
| else | ||||
| libtransceiver_la_SOURCES += UHDDevice.cpp | ||||
| osmo_trx_LDADD += $(UHD_LIBS) | ||||
| endif | ||||
|   | ||||
							
								
								
									
										35
									
								
								Transceiver52M/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Transceiver52M/README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| The Transceiver | ||||
|  | ||||
| The transceiver consists of three modules: | ||||
|    --- transceiver | ||||
|    --- radioInterface | ||||
|    --- USRPDevice | ||||
|  | ||||
| The USRPDevice module is basically a driver that reads/writes | ||||
| packets to a USRP with two RFX900 daughterboards, board  | ||||
| A is the Tx chain and board B is the Rx chain.   | ||||
|  | ||||
| The radioInterface module is basically an interface b/w the | ||||
| transceiver and the USRP.   It operates the basestation clock | ||||
| based upon the sample count of received USRP samples.  Packets  | ||||
| from the USRP are queued and segmented into GSM bursts that are | ||||
| passed up to the transceiver; bursts from the transceiver are | ||||
| passed down to the USRP.  | ||||
|  | ||||
| The transceiver basically operates "layer 0" of the GSM stack, | ||||
| performing the modulation, detection, and demodulation of GSM  | ||||
| bursts.  It communicates with the GSM stack via three UDP sockets, | ||||
| one socket for data, one for control messages, and one socket to | ||||
| pass clocking information.  The transceiver contains a priority | ||||
| queue to sort to-be-transmitted bursts, and a filler table to fill | ||||
| in timeslots that do not have bursts in the priority queue.  The | ||||
| transceiver tries to stay ahead of the basestation clock, adapting  | ||||
| its latency when underruns are reported by the radioInterface/USRP. | ||||
| Received bursts (from the radioInterface) pass through a simple  | ||||
| energy detector, a RACH or midamble correlator, and a DFE-based demodulator. | ||||
|  | ||||
| NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced | ||||
| with a memory buffer.  In this mode, data written to the USRP is actually stored  | ||||
| in a buffer, and read commands to the USRP simply pull data from this buffer. | ||||
| This was very useful in early testing, and still may be useful in testing basic | ||||
| Transceiver and radioInterface functionality.  | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * Rational Sample Rate Conversion | ||||
|  * Copyright (C) 2012, 2013  Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
| @@ -24,7 +22,6 @@ | ||||
| #include <string.h> | ||||
| #include <malloc.h> | ||||
| #include <iostream> | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "Resampler.h" | ||||
|  | ||||
| @@ -38,8 +35,6 @@ extern "C" { | ||||
|  | ||||
| #define MAX_OUTPUT_LEN		4096 | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| static float sinc(float x) | ||||
| { | ||||
| 	if (x == 0.0) | ||||
| @@ -48,24 +43,37 @@ static float sinc(float x) | ||||
| 	return sin(M_PI * x) / (M_PI * x); | ||||
| } | ||||
|  | ||||
| void Resampler::initFilters(float bw) | ||||
| bool Resampler::initFilters(float bw) | ||||
| { | ||||
| 	float cutoff; | ||||
| 	size_t proto_len = p * filt_len; | ||||
| 	float *proto, val, 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. | ||||
| 	 */ | ||||
| 	auto proto = vector<float>(p * filt_len); | ||||
| 	for (auto &part : partitions) | ||||
| 		part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>)); | ||||
| 	proto = new float[proto_len]; | ||||
| 	if (!proto) | ||||
| 		return false; | ||||
|  | ||||
| 	/* | ||||
| 	partitions = (float **) malloc(sizeof(float *) * p); | ||||
| 	if (!partitions) { | ||||
| 		free(proto); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < p; i++) { | ||||
| 		partitions[i] = (float *) | ||||
| 				memalign(16, filt_len * 2 * sizeof(float)); | ||||
| 	} | ||||
|  | ||||
| 	/*  | ||||
| 	 * Generate the prototype filter with a Blackman-harris window. | ||||
| 	 * Scale coefficients with DC filter gain set to unity divided | ||||
| 	 * by the number of filter partitions. | ||||
| 	 * by the number of filter partitions.  | ||||
| 	 */ | ||||
| 	float a0 = 0.35875; | ||||
| 	float a1 = 0.48829; | ||||
| @@ -77,26 +85,47 @@ void Resampler::initFilters(float bw) | ||||
| 	else | ||||
| 		cutoff = (float) q; | ||||
|  | ||||
| 	float midpt = (proto.size() - 1) / 2.0; | ||||
| 	for (size_t i = 0; i < proto.size(); i++) { | ||||
| 	for (size_t i = 0; i < proto_len; i++) { | ||||
| 		proto[i] = sinc(((float) i - midpt) / cutoff * bw); | ||||
| 		proto[i] *= a0 - | ||||
| 			    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)); | ||||
| 			    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)); | ||||
| 		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][i] = complex<float>(proto[i * p + n] * scale); | ||||
| 		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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Store filter taps in reverse */ | ||||
| 	for (auto &part : partitions) | ||||
| 		reverse(&part[0], &part[filt_len]); | ||||
| 	/* 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; | ||||
| } | ||||
|  | ||||
| static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| @@ -130,41 +159,59 @@ static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len) | ||||
| 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(float *in, size_t in_len, float *out, size_t out_len) | ||||
| { | ||||
| 	int n, path; | ||||
| 	int hist_len = filt_len - 1; | ||||
|  | ||||
| 	if (!check_vec_len(in_len, out_len, p, q)) | ||||
| 		return -1; | ||||
| 		return -1;  | ||||
|  | ||||
| 	/* Insert history */ | ||||
| 	memcpy(&in[-2 * hist_len], history, hist_len * 2 * sizeof(float)); | ||||
|  | ||||
| 	/* Generate output from precomputed input/output paths */ | ||||
| 	for (size_t i = 0; i < out_len; i++) { | ||||
| 		n = in_index[i]; | ||||
| 		path = out_path[i]; | ||||
| 		n = in_index[i];  | ||||
| 		path = out_path[i];  | ||||
|  | ||||
| 		convolve_real(in, in_len, | ||||
| 			      reinterpret_cast<float *>(partitions[path]), | ||||
| 			      filt_len, &out[2 * i], out_len - i, | ||||
| 			      n, 1); | ||||
| 			      partitions[path], filt_len, | ||||
| 			      &out[2 * i], out_len - i, | ||||
| 			      n, 1, 1, 0); | ||||
| 	} | ||||
|  | ||||
| 	/* Save history */ | ||||
| 	memcpy(history, &in[2 * (in_len - hist_len)], | ||||
| 	       hist_len * 2 * sizeof(float)); | ||||
|  | ||||
| 	return out_len; | ||||
| } | ||||
|  | ||||
| bool Resampler::init(float bw) | ||||
| { | ||||
| 	if (p == 0 || q == 0 || filt_len == 0) return false; | ||||
| 	size_t hist_len = filt_len - 1; | ||||
|  | ||||
| 	/* Filterbank filter internals */ | ||||
| 	initFilters(bw); | ||||
| 	if (initFilters(bw) < 0) | ||||
| 		return false; | ||||
|  | ||||
| 	/* History buffer */ | ||||
| 	history = new float[2 * hist_len]; | ||||
| 	memset(history, 0, 2 * hist_len * sizeof(float)); | ||||
|  | ||||
| 	/* Precompute filterbank paths */ | ||||
| 	int i = 0; | ||||
| 	for (auto &index : in_index) | ||||
| 		index = (q * i++) / p; | ||||
| 	i = 0; | ||||
| 	for (auto &path : out_path) | ||||
| 		path = (q * i++) % p; | ||||
| 	in_index = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	out_path = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	computePath(); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| @@ -175,7 +222,7 @@ size_t Resampler::len() | ||||
| } | ||||
|  | ||||
| Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
| 	: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p) | ||||
| 	: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL) | ||||
| { | ||||
| 	this->p = p; | ||||
| 	this->q = q; | ||||
| @@ -184,6 +231,9 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
|  | ||||
| Resampler::~Resampler() | ||||
| { | ||||
| 	for (auto &part : partitions) | ||||
| 		free(part); | ||||
| 	releaseFilters(); | ||||
|  | ||||
| 	delete history; | ||||
| 	delete in_index; | ||||
| 	delete out_path; | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * Rational Sample Rate Conversion | ||||
|  * Copyright (C) 2012, 2013  Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
| @@ -22,15 +20,12 @@ | ||||
| #ifndef _RESAMPLER_H_ | ||||
| #define _RESAMPLER_H_ | ||||
|  | ||||
| #include <vector> | ||||
| #include <complex> | ||||
|  | ||||
| class Resampler { | ||||
| public: | ||||
| 	/* Constructor for rational sample rate conversion | ||||
| 	 *   @param p numerator of resampling ratio | ||||
| 	 *   @param q denominator of resampling ratio | ||||
| 	 *   @param filt_len length of each polyphase subfilter | ||||
| 	 *   @param filt_len length of each polyphase subfilter  | ||||
| 	 */ | ||||
| 	Resampler(size_t p, size_t q, size_t filt_len = 16); | ||||
| 	~Resampler(); | ||||
| @@ -57,10 +52,10 @@ public: | ||||
| 	 * Input and output vector lengths must of be equal multiples of the | ||||
| 	 * rational conversion rate denominator and numerator respectively. | ||||
| 	 */ | ||||
| 	int rotate(const float *in, size_t in_len, float *out, size_t out_len); | ||||
| 	int rotate(float *in, size_t in_len, float *out, size_t out_len); | ||||
|  | ||||
| 	/* Get filter length | ||||
| 	 *   @return number of taps in each filter partition | ||||
| 	 *   @return number of taps in each filter partition  | ||||
| 	 */ | ||||
| 	size_t len(); | ||||
|  | ||||
| @@ -68,11 +63,15 @@ private: | ||||
| 	size_t p; | ||||
| 	size_t q; | ||||
| 	size_t filt_len; | ||||
| 	std::vector<size_t> in_index; | ||||
| 	std::vector<size_t> out_path; | ||||
| 	std::vector<std::complex<float> *> partitions; | ||||
| 	size_t *in_index; | ||||
| 	size_t *out_path; | ||||
|  | ||||
| 	void initFilters(float bw); | ||||
| 	float **partitions; | ||||
| 	float *history; | ||||
|  | ||||
| 	bool initFilters(float bw); | ||||
| 	void releaseFilters(); | ||||
| 	void computePath(); | ||||
| }; | ||||
|  | ||||
| #endif /* _RESAMPLER_H_ */ | ||||
|   | ||||
| @@ -1,123 +0,0 @@ | ||||
| /* | ||||
|  * Polyphase synthesis filter | ||||
|  * | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
|  * | ||||
|  * SPDX-License-Identifier: AGPL-3.0+ | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <math.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
| #include <cstdio> | ||||
| #include <iostream> | ||||
|  | ||||
| #include "Synthesis.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "fft.h" | ||||
| #include "convolve.h" | ||||
| } | ||||
|  | ||||
| static void interleave(float **in, size_t ilen, | ||||
| 		       float *out, size_t m) | ||||
| { | ||||
| 	size_t i, n; | ||||
|  | ||||
| 	for (i = 0; i < ilen; i++) { | ||||
| 		for (n = 0; n < m; n++) { | ||||
| 			out[2 * (i * m + n) + 0] = in[n][2 * i + 0]; | ||||
| 			out[2 * (i * m + n) + 1] = in[n][2 * i + 1]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| size_t Synthesis::inputLen() const | ||||
| { | ||||
| 	return blockLen; | ||||
| } | ||||
|  | ||||
| size_t Synthesis::outputLen() const | ||||
| { | ||||
| 	return blockLen * m; | ||||
| } | ||||
|  | ||||
| float *Synthesis::inputBuffer(size_t chan) const | ||||
| { | ||||
| 	if (chan >= m) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return hOutputs[chan]; | ||||
| } | ||||
|  | ||||
| bool Synthesis::resetBuffer(size_t chan) | ||||
| { | ||||
| 	if (chan >= m) | ||||
| 		return false; | ||||
|  | ||||
| 	memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float)); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Implementation based on material found in: | ||||
|  * | ||||
|  * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, | ||||
|  *     Prentice Hall, 2006." | ||||
|  */ | ||||
| bool Synthesis::rotate(float *out, size_t len) | ||||
| { | ||||
| 	size_t hSize = 2 * hLen * sizeof(float); | ||||
|  | ||||
| 	if (!checkLen(blockLen, len)) { | ||||
| 		std::cout << "Length fail" << std::endl; | ||||
| 		exit(1); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	cxvec_fft(fftHandle); | ||||
|  | ||||
| 	/* | ||||
| 	 * Convolve through filterbank while applying and saving sample history | ||||
| 	 */ | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		memcpy(&hInputs[i][2 * -hLen], hist[i], hSize); | ||||
| 		memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize); | ||||
|  | ||||
| 		convolve_real(hInputs[i], blockLen, | ||||
| 			      subFilters[i], hLen, | ||||
| 			      hOutputs[i], blockLen, | ||||
| 			      0, blockLen); | ||||
| 	} | ||||
|  | ||||
| 	/* Interleave into output vector */ | ||||
| 	interleave(hOutputs, blockLen, out, m); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen) | ||||
| 	: ChannelizerBase(m, blockLen, hLen) | ||||
| { | ||||
| } | ||||
|  | ||||
| Synthesis::~Synthesis() | ||||
| { | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| #ifndef _SYNTHESIS_H_ | ||||
| #define _SYNTHESIS_H_ | ||||
|  | ||||
| #include "ChannelizerBase.h" | ||||
|  | ||||
| class Synthesis : public ChannelizerBase { | ||||
| public: | ||||
| 	/** Constructor for synthesis filterbank | ||||
| 	    @param m number of physical channels | ||||
| 	    @param blockLen number of samples per output of each iteration | ||||
| 	    @param hLen number of taps in each constituent filter path | ||||
| 	*/ | ||||
| 	Synthesis(size_t m, size_t blockLen, size_t hLen = 16); | ||||
| 	~Synthesis(); | ||||
|  | ||||
| 	/* Return required input and output buffer lengths */ | ||||
| 	size_t inputLen() const; | ||||
| 	size_t outputLen() const; | ||||
|  | ||||
| 	/** Rotate "output commutator" and drive samples through filterbank | ||||
| 	    @param out complex output vector  | ||||
| 	    @param oLen number of samples in buffer (must match block length * m) | ||||
| 	    @return false on error and true otherwise | ||||
| 	*/ | ||||
| 	bool rotate(float *out, size_t oLen); | ||||
|  | ||||
| 	/** Get buffer for an input path | ||||
| 	    @param chan channel number of filterbank | ||||
|             @return NULL on error and pointer to buffer otherwise | ||||
| 	*/ | ||||
| 	float *inputBuffer(size_t chan) const; | ||||
| 	bool resetBuffer(size_t chan); | ||||
| }; | ||||
|  | ||||
| #endif /* _SYNTHESIS_H_ */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,8 +1,6 @@ | ||||
| /* | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: GPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -27,21 +25,27 @@ | ||||
| #include "radioInterface.h" | ||||
| #include "Interthread.h" | ||||
| #include "GSMCommon.h" | ||||
| #include "Sockets.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/signal.h> | ||||
| #include "config_defs.h" | ||||
| } | ||||
|  | ||||
| class Transceiver; | ||||
|  | ||||
| /** Channel descriptor for transceiver object and channel number pair */ | ||||
| struct TrxChanThParams { | ||||
| 	Transceiver *trx; | ||||
| 	size_t num; | ||||
| struct TransceiverChannel { | ||||
|   TransceiverChannel(Transceiver *trx, int num) | ||||
|   { | ||||
|     this->trx = trx; | ||||
|     this->num = num; | ||||
|   } | ||||
|  | ||||
|   ~TransceiverChannel() | ||||
|   { | ||||
|   } | ||||
|  | ||||
|   Transceiver *trx; | ||||
|   size_t num; | ||||
| }; | ||||
|  | ||||
| /** Internal transceiver state variables */ | ||||
| @@ -50,7 +54,7 @@ struct TransceiverState { | ||||
|   ~TransceiverState(); | ||||
|  | ||||
|   /* Initialize a multiframe slot in the filler table */ | ||||
|   bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); | ||||
|   void init(size_t slot, signalVector *burst, bool fill); | ||||
|  | ||||
|   int chanType[8]; | ||||
|  | ||||
| @@ -76,37 +80,134 @@ struct TransceiverState { | ||||
|  | ||||
|   /* Received noise energy levels */ | ||||
|   float mNoiseLev; | ||||
|   noiseVector mNoises; | ||||
|   avgVector mNoises; | ||||
|   avgVector mFreqOffsets; | ||||
|  | ||||
|   /* Shadowed downlink attenuation */ | ||||
|   int mPower; | ||||
|   /* Store pointers to previous frame */ | ||||
|   radioVector *prevFrame[8]; | ||||
|  | ||||
|   /* Transceiver mode */ | ||||
|   int mode; | ||||
| }; | ||||
|  | ||||
| /** The Transceiver class, responsible for physical layer of basestation */ | ||||
| class Transceiver { | ||||
| private: | ||||
|   int mBasePort; | ||||
|   std::string mAddr; | ||||
|   GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock | ||||
|   GSM::Time mLatencyUpdateTime;   ///< last time latency was updated | ||||
|  | ||||
|   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 | ||||
|   UDPSocket *mClockSocket;                ///< socket for writing clock updates to GSM core | ||||
|  | ||||
|   std::vector<VectorQueue> mTxPriorityQueues;   ///< priority queue of transmit bursts received from GSM core | ||||
|   std::vector<VectorFIFO *>  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts | ||||
|  | ||||
|   std::vector<Thread *> mRxServiceLoopThreads;  ///< thread to pull bursts into receive FIFO | ||||
|   Thread *mLowerLoopThread;                   ///< thread to pull bursts into receive FIFO | ||||
|   std::vector<Thread *> mControlServiceLoopThreads;         ///< thread to process control messages from GSM core | ||||
|   std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core | ||||
|  | ||||
|   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 | ||||
|   double txFullScale;                     ///< full scale input to radio | ||||
|   double rxFullScale;                     ///< full scale output to radio | ||||
|  | ||||
|   /** Codes for burst types of received bursts*/ | ||||
|   typedef enum { | ||||
|     OFF,               ///< timeslot is off | ||||
|     TSC,	       ///< timeslot should contain a normal burst | ||||
|     RACH,	       ///< timeslot should contain an access burst | ||||
|     SCH,	       ///< timeslot should contain a SCH burst | ||||
|     IDLE	       ///< timeslot is an idle (or dummy) burst | ||||
|   } CorrType; | ||||
|  | ||||
|   /** modulate and add a burst to the transmit queue */ | ||||
|   void addRadioVector(size_t chan, BitVector &bits, | ||||
|                       int RSSI, GSM::Time &wTime); | ||||
|  | ||||
|   /** Update filler table */ | ||||
|   void updateFillerTable(size_t chan, radioVector *burst); | ||||
|  | ||||
|   /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */ | ||||
|   void pushRadioVector(GSM::Time &nowTime); | ||||
|  | ||||
|   /** Pull and demodulate a burst from the receive FIFO */ | ||||
|   SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, | ||||
|                               int &timingOffset, size_t chan = 0); | ||||
|  | ||||
|   /** Set modulus for specific timeslot */ | ||||
|   void setModulus(size_t timeslot, size_t chan); | ||||
|  | ||||
|   /** return the expected burst type for the specified timestamp */ | ||||
|   CorrType expectedCorrType(GSM::Time currTime, size_t chan); | ||||
|  | ||||
|   /** send messages over the clock socket */ | ||||
|   void writeClockInterface(void); | ||||
|  | ||||
|   /** Detect RACH bursts */ | ||||
|   bool detectRACH(TransceiverState *state, | ||||
|                   signalVector &burst, | ||||
|                   complex &, float &toa); | ||||
|  | ||||
|   bool detectSCH(TransceiverState *state, | ||||
|                   signalVector &burst, | ||||
|                   complex &, float &toa); | ||||
|  | ||||
|   bool decodeSCH(SoftVector *burst, GSM::Time *time); | ||||
|   bool correctFCCH(TransceiverState *state, signalVector *burst); | ||||
|  | ||||
|   /** Detect normal bursts */ | ||||
|   bool detectTSC(TransceiverState *state, | ||||
|                  signalVector &burst, | ||||
|                  complex &, float &toa, GSM::Time &time); | ||||
|  | ||||
|   /** Demodulat burst and output soft bits */ | ||||
|   SoftVector *demodulate(TransceiverState *state, | ||||
|                          signalVector &burst, complex amp, | ||||
|                          float toa, size_t tn, bool equalize); | ||||
|  | ||||
|  | ||||
|   int mSPSTx;                          ///< number of samples per Tx symbol | ||||
|   int mSPSRx;                          ///< number of samples per Rx symbol | ||||
|   size_t mChans; | ||||
|  | ||||
|   bool mOn;			       ///< flag to indicate that transceiver is powered on | ||||
|   double mTxFreq;                      ///< the transmit frequency | ||||
|   double mRxFreq;                      ///< the receive frequency | ||||
|   int mPower;                          ///< the transmit power in dB | ||||
|   unsigned mTSC;                       ///< the midamble sequence code | ||||
|   unsigned mMaxExpectedDelay;          ///< maximum TOA offset in GSM symbols | ||||
|   unsigned long long mRxSlotMask[8];   ///< MS - enabled multiframe slot mask | ||||
|   int mBSIC;                           ///< MS - detected BSIC | ||||
|  | ||||
|   std::vector<TransceiverState> mStates; | ||||
|  | ||||
| public: | ||||
|   /** Transceiver constructor | ||||
|  | ||||
|   /** Transceiver constructor  | ||||
|       @param wBasePort base port number of UDP sockets | ||||
|       @param TRXAddress IP address of the TRX, as a string | ||||
|       @param GSMcoreAddress IP address of the GSM core, as a string | ||||
|       @param TRXAddress IP address of the TRX manager, 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, | ||||
|               double wRssiOffset, int stackSize); | ||||
| 	      const char *TRXAddress, | ||||
| 	      size_t wSPS, size_t chans, | ||||
| 	      GSM::Time wTransmitLatency, | ||||
| 	      RadioInterface *wRadioInterface); | ||||
|  | ||||
|   /** Destructor */ | ||||
|   ~Transceiver(); | ||||
|  | ||||
|   /** Start the control loop */ | ||||
|   bool init(FillerType filler, size_t rtsc, unsigned rach_delay, | ||||
|             bool edge, bool ext_rach); | ||||
|   /** start the Transceiver */ | ||||
|   void start(); | ||||
|   bool init(bool filler); | ||||
|  | ||||
|   /** attach the radioInterface receive FIFO */ | ||||
|   bool receiveFIFO(VectorFIFO *wFIFO, size_t chan) | ||||
| @@ -141,83 +242,12 @@ public: | ||||
|     LOOPBACK            ///< similar go VII, used in loopback testing | ||||
|   } ChannelCombination; | ||||
|  | ||||
| private: | ||||
|   int mBasePort; | ||||
|   std::string mLocalAddr; | ||||
|   std::string mRemoteAddr; | ||||
|  | ||||
|   std::vector<int> mDataSockets;  ///< socket for writing to/reading from GSM core | ||||
|   std::vector<int> mCtrlSockets;  ///< socket for writing/reading control commands from GSM core | ||||
|   int mClockSocket;               ///< socket for writing clock updates to GSM core | ||||
|  | ||||
|   std::vector<VectorQueue> mTxPriorityQueues;   ///< priority queue of transmit bursts received from GSM core | ||||
|   std::vector<VectorFIFO *>  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts | ||||
|  | ||||
|   std::vector<Thread *> mRxServiceLoopThreads;  ///< thread to pull bursts into receive FIFO | ||||
|   Thread *mRxLowerLoopThread;                   ///< thread to pull bursts into receive FIFO | ||||
|   Thread *mTxLowerLoopThread;                   ///< thread to push bursts into transmit FIFO | ||||
|   std::vector<Thread *> mControlServiceLoopThreads;         ///< thread to process control messages from GSM core | ||||
|   std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core | ||||
|  | ||||
|   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 mLastClockUpdateTime;         ///< last time clock update was sent up to core | ||||
|  | ||||
|   RadioInterface *mRadioInterface;	  ///< associated radioInterface object | ||||
|   double txFullScale;                     ///< full scale input to radio | ||||
|   double rxFullScale;                     ///< full scale output to radio | ||||
|  | ||||
|   double rssiOffset;                      ///< RSSI to dBm conversion offset | ||||
|   int stackSize;                      ///< stack size for threads, 0 = OS default | ||||
|  | ||||
|   /** modulate and add a burst to the transmit queue */ | ||||
|   void addRadioVector(size_t chan, BitVector &bits, | ||||
|                       int RSSI, GSM::Time &wTime); | ||||
|  | ||||
|   /** Update filler table */ | ||||
|   void updateFillerTable(size_t chan, radioVector *burst); | ||||
|  | ||||
|   /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */ | ||||
|   void pushRadioVector(GSM::Time &nowTime); | ||||
|  | ||||
|   /** Pull and demodulate a burst from the receive FIFO */ | ||||
|   bool pullRadioVector(size_t chan, struct trx_ul_burst_ind *ind); | ||||
|  | ||||
|   /** Set modulus for specific timeslot */ | ||||
|   void setModulus(size_t timeslot, size_t chan); | ||||
|  | ||||
|   /** return the expected burst type for the specified timestamp */ | ||||
|   CorrType expectedCorrType(GSM::Time currTime, size_t chan); | ||||
|  | ||||
|   /** send messages over the clock socket */ | ||||
|   void writeClockInterface(void); | ||||
|  | ||||
|   int mSPSTx;                          ///< number of samples per Tx symbol | ||||
|   int mSPSRx;                          ///< number of samples per Rx symbol | ||||
|   size_t mChans; | ||||
|  | ||||
|   bool mExtRACH; | ||||
|   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 | ||||
|   unsigned mTSC;                       ///< the midamble sequence code | ||||
|   unsigned mMaxExpectedDelayAB;        ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH) | ||||
|   unsigned mMaxExpectedDelayNB;        ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts | ||||
|   unsigned mWriteBurstToDiskMask;      ///< debug: bitmask to indicate which timeslots to dump to disk | ||||
|  | ||||
|   std::vector<unsigned> mVersionTRXD;  ///< Format version to use for TRXD protocol communication, per channel | ||||
|   std::vector<TransceiverState> mStates; | ||||
|  | ||||
|   /** Start and stop I/O threads through the control socket API */ | ||||
|   bool start(); | ||||
|   void stop(); | ||||
|  | ||||
|   /** Protect destructor accessable stop call */ | ||||
|   Mutex mLock; | ||||
|   enum { | ||||
|     TRX_MODE_OFF, | ||||
|     TRX_MODE_BTS, | ||||
|     TRX_MODE_MS_ACQUIRE, | ||||
|     TRX_MODE_MS_TRACK, | ||||
|   }; | ||||
|  | ||||
| protected: | ||||
|   /** drive lower receive I/O and burst generation */ | ||||
| @@ -238,11 +268,13 @@ protected: | ||||
|   */ | ||||
|   bool driveTxPriorityQueue(size_t chan); | ||||
|  | ||||
|   friend void *RxUpperLoopAdapter(TrxChanThParams *params); | ||||
|   friend void *TxUpperLoopAdapter(TrxChanThParams *params); | ||||
|   friend void *RxLowerLoopAdapter(Transceiver *transceiver); | ||||
|   friend void *TxLowerLoopAdapter(Transceiver *transceiver); | ||||
|   friend void *ControlServiceLoopAdapter(TrxChanThParams *params); | ||||
|   friend void *RxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|   friend void *TxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|   friend void *LowerLoopAdapter(Transceiver *); | ||||
|  | ||||
|   friend void *ControlServiceLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|  | ||||
|   void reset(); | ||||
| @@ -250,17 +282,16 @@ protected: | ||||
|   /** set priority on current thread */ | ||||
|   void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); } | ||||
|  | ||||
|   void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi); | ||||
| }; | ||||
|  | ||||
| void *RxUpperLoopAdapter(TrxChanThParams *params); | ||||
| void *RxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
| /** Main drive threads */ | ||||
| void *RxLowerLoopAdapter(Transceiver *transceiver); | ||||
| void *TxLowerLoopAdapter(Transceiver *transceiver); | ||||
| void *LowerLoopAdapter(Transceiver *); | ||||
|  | ||||
| /** control message handler thread loop */ | ||||
| void *ControlServiceLoopAdapter(TrxChanThParams *params); | ||||
| void *ControlServiceLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
| /** transmit queueing thread loop */ | ||||
| void *TxUpperLoopAdapter(TrxChanThParams *params); | ||||
| void *TxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|   | ||||
							
								
								
									
										1388
									
								
								Transceiver52M/UHDDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1388
									
								
								Transceiver52M/UHDDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,8 +1,6 @@ | ||||
| /*
 | ||||
| * Copyright 2008, 2009 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| @@ -29,16 +27,17 @@ | ||||
| 	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 | ||||
| @@ -60,15 +59,12 @@ const dboardConfigType dboardConfig = TXA_RXB; | ||||
| 
 | ||||
| const double USRPDevice::masterClockRate = 52.0e6; | ||||
| 
 | ||||
| USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, | ||||
| 		       size_t chans, double lo_offset, | ||||
| 		       const std::vector<std::string>& tx_paths, | ||||
| 		       const std::vector<std::string>& rx_paths): | ||||
| 		RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths) | ||||
| USRPDevice::USRPDevice(size_t sps, size_t, bool) | ||||
| { | ||||
|   LOGC(DDEV, INFO) << "creating USRP device..."; | ||||
|   LOG(INFO) << "creating USRP device..."; | ||||
| 
 | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps)); | ||||
|   this->sps = sps; | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); | ||||
|   actualSampleRate = masterClockRate/decimRate; | ||||
|   rxGain = 0; | ||||
| 
 | ||||
| @@ -78,14 +74,14 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, | ||||
|    * split sample rate Tx/Rx - 4/1 sps we need to need to | ||||
|    * compensate for advance rather than delay. | ||||
|    */ | ||||
|   if (tx_sps == 1) | ||||
|   if (sps == 1) | ||||
|     pingOffset = 272; | ||||
|   else if (tx_sps == 4) | ||||
|   else if (sps == 4) | ||||
|     pingOffset = 269 - 7500; | ||||
|   else | ||||
|     pingOffset = 0; | ||||
| 
 | ||||
| #ifdef SWLOOPBACK | ||||
| #ifdef SWLOOPBACK  | ||||
|   samplePeriod = 1.0e6/actualSampleRate; | ||||
|   loopbackBufferSize = 0; | ||||
|   gettimeofday(&lastReadTime,NULL); | ||||
| @@ -93,36 +89,38 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| int USRPDevice::open(const std::string &, int, bool) | ||||
| int USRPDevice::open(const std::string &, bool) | ||||
| { | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
|   LOGC(DDEV, INFO) << "opening USRP device.."; | ||||
| #ifndef SWLOOPBACK | ||||
|   LOG(INFO) << "opening USRP device.."; | ||||
| #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 { | ||||
|     m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make( | ||||
|                                         0, decimRate * tx_sps, 1, -1, | ||||
|                                         0, decimRate * sps, 1, -1, | ||||
|                                         usrp_standard_rx::FPGA_MODE_NORMAL, | ||||
|                                         1024, 16 * 8, rbf)); | ||||
|     m_uRx->set_fpga_master_clock_freq(masterClockRate); | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOGC(DDEV, ALERT) << "make failed on Rx"; | ||||
|     LOG(ALERT) << "make failed on Rx"; | ||||
|     m_uRx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() | ||||
|                << ", desired clock freq = " << masterClockRate; | ||||
|     m_uRx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
|   } | ||||
| 
 | ||||
|   try { | ||||
|     m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make( | ||||
| @@ -132,22 +130,22 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOGC(DDEV, ALERT) << "make failed on Tx"; | ||||
|     LOG(ALERT) << "make failed on Tx"; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|                << ", desired clock freq = " << masterClockRate; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   m_uRx->stop(); | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
| 
 | ||||
|    | ||||
| #endif | ||||
| 
 | ||||
|   switch (dboardConfig) { | ||||
| @@ -175,21 +173,23 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|   m_dbTx = m_uTx->selected_subdev(txSubdevSpec); | ||||
|   m_dbRx = m_uRx->selected_subdev(rxSubdevSpec); | ||||
| 
 | ||||
|   samplesRead = 0; | ||||
|   samplesWritten = 0; | ||||
|   started = false; | ||||
| 
 | ||||
|    | ||||
|   return NORMAL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| bool USRPDevice::start() | ||||
| bool USRPDevice::start()  | ||||
| { | ||||
|   LOGC(DDEV, INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx) return false; | ||||
|   LOG(INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK  | ||||
|   if (!m_uRx && !skipRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
| 
 | ||||
|   m_uRx->stop(); | ||||
|    | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
| 
 | ||||
|   writeLock.lock(); | ||||
| @@ -218,8 +218,11 @@ bool USRPDevice::start() | ||||
|   hi32Timestamp = 0; | ||||
|   isAligned = false; | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
|   if (!skipRx)  | ||||
|   started = (m_uRx->start() && m_uTx->start()); | ||||
|   else | ||||
|   started = m_uTx->start(); | ||||
|   return started; | ||||
| #else | ||||
|   gettimeofday(&lastReadTime,NULL); | ||||
| @@ -227,14 +230,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 | ||||
| @@ -255,7 +258,7 @@ double USRPDevice::minTxGain() | ||||
| double USRPDevice::maxRxGain() | ||||
| { | ||||
|   return m_dbRx->gain_max(); | ||||
| } | ||||
| }  | ||||
| 
 | ||||
| double USRPDevice::minRxGain() | ||||
| { | ||||
| @@ -265,7 +268,7 @@ double USRPDevice::minRxGain() | ||||
| double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -275,10 +278,10 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
|   if (dB < minTxGain()) | ||||
|     dB = minTxGain(); | ||||
| 
 | ||||
|   LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
|   LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbTx->set_gain(dB)) | ||||
|     LOGC(DDEV, ERR) << "Error setting TX gain"; | ||||
|     LOG(ERR) << "Error setting TX gain"; | ||||
| 
 | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
| @@ -289,7 +292,7 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| double USRPDevice::setRxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -301,86 +304,38 @@ double USRPDevice::setRxGain(double dB, size_t chan) | ||||
|   if (dB < minRxGain()) | ||||
|     dB = minRxGain(); | ||||
| 
 | ||||
|   LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
|   LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbRx->set_gain(dB)) | ||||
|     LOGC(DDEV, ERR) << "Error setting RX gain"; | ||||
|     LOG(ERR) << "Error setting RX gain"; | ||||
| 
 | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
|   return dB; | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| std::string USRPDevice::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| std::string USRPDevice::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::requiresRadioAlign() | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| GSM::Time USRPDevice::minLatency() { | ||||
| 	return GSM::Time(1,1); | ||||
| } | ||||
| 
 | ||||
| // 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; | ||||
|   } | ||||
| 
 | ||||
|   *underrun = false; | ||||
| 
 | ||||
|   if (underrun) *underrun = false; | ||||
|   | ||||
|   uint32_t readBuf[2000]; | ||||
| 
 | ||||
|   | ||||
|   while (1) { | ||||
|     //guestimate USB read size
 | ||||
|     int readLen=0; | ||||
| @@ -390,21 +345,21 @@ 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++) { | ||||
|     for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { | ||||
|       // tmpBuf points to start of a USB packet
 | ||||
|       uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4); | ||||
|       TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]); | ||||
|       uint32_t word0 = usrp_to_host_u32(tmpBuf[0]); | ||||
|       uint32_t chan = (word0 >> 16) & 0x1f; | ||||
|       unsigned payloadSz = word0 & 0x1ff; | ||||
|       LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
|       LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
| 
 | ||||
|       bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); | ||||
|       if (incrementHi32 && (timeStart!=0)) { | ||||
|            LOGC(DDEV, DEBUG) << "high 32 increment!!!"; | ||||
|            LOG(DEBUG) << "high 32 increment!!!"; | ||||
|            hi32Timestamp++; | ||||
|       } | ||||
|       pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; | ||||
| @@ -416,24 +371,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { | ||||
|           timestamp -= timestampOffset; | ||||
| 	  timestampOffset = pktTimestamp - pingTimestamp + pingOffset; | ||||
| 	  LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
| 	  LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
|           timestamp += timestampOffset; | ||||
| 	  isAligned = true; | ||||
| 	} | ||||
| 	continue; | ||||
|       } | ||||
|       if (chan != 0) { | ||||
| 	LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	continue; | ||||
|       } | ||||
|       if ((word0 >> 28) & 0x04) { | ||||
| 	*underrun = true; | ||||
| 	LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
| 	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; | ||||
| @@ -446,25 +401,25 @@ 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); | ||||
| 
 | ||||
|       LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
|       LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
| 
 | ||||
|     }	 | ||||
|   }      | ||||
|   | ||||
|   // copy desired data to buf
 | ||||
|   unsigned bufStart = dataStart+(timestamp-timeStart); | ||||
|   if (bufStart + len < currDataSize/2) { | ||||
|     LOGC(DDEV, DEBUG) << "bufStart: " << bufStart; | ||||
|   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)); | ||||
|   } | ||||
|   else { | ||||
|     LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     unsigned firstLength = (currDataSize/2-bufStart); | ||||
|     LOGC(DDEV, DEBUG) << "firstLength: " << firstLength; | ||||
|     LOG(DEBUG) << "firstLength: " << firstLength; | ||||
|     memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short)); | ||||
|     memset(data+bufStart*2,0,firstLength*2*sizeof(short)); | ||||
|     memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short)); | ||||
| @@ -474,21 +429,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); | ||||
| @@ -505,7 +460,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|     gettimeofday(&lastReadTime,NULL); | ||||
|     firstRead = true; | ||||
|   } | ||||
| 
 | ||||
|   samplesRead += numSamples; | ||||
|    | ||||
|   return numSamples; | ||||
| #endif | ||||
| } | ||||
| @@ -516,7 +472,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
| { | ||||
|   writeLock.lock(); | ||||
| 
 | ||||
| #ifndef SWLOOPBACK | ||||
| #ifndef SWLOOPBACK  | ||||
|   if (!m_uTx) | ||||
|     return 0; | ||||
| 
 | ||||
| @@ -554,21 +510,23 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
|   } | ||||
|   m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL); | ||||
| 
 | ||||
|   samplesWritten += len/2/sizeof(short); | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
|   return len/2/sizeof(short); | ||||
| #else | ||||
|   int retVal = 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); | ||||
| @@ -585,25 +543,25 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| #ifndef SWLOOPBACK | ||||
| #ifndef SWLOOPBACK  | ||||
| bool USRPDevice::setTxFreq(double wFreq, size_t chan) | ||||
| { | ||||
|   usrp_tune_result result; | ||||
| 
 | ||||
|   if (chan) { | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { | ||||
|     LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl | ||||
|     LOG(INFO) << "set TX: " << wFreq << std::endl | ||||
|               << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|               << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|               << "    residual freq: " << result.residual_freq; | ||||
|     return true; | ||||
|   } | ||||
|   else { | ||||
|     LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl | ||||
|     LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl | ||||
|                << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|                << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|                << "    residual freq: " << result.residual_freq; | ||||
| @@ -616,19 +574,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan) | ||||
|   usrp_tune_result result; | ||||
| 
 | ||||
|   if (chan) { | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { | ||||
|     LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl | ||||
|     LOG(INFO) << "set RX: " << wFreq << std::endl | ||||
|               << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|               << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|               << "    residual freq: " << result.residual_freq; | ||||
|     return true; | ||||
|   } | ||||
|   else { | ||||
|     LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl | ||||
|     LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl | ||||
|                << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|                << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|                << "    residual freq: " << result.residual_freq; | ||||
| @@ -642,22 +600,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;}; | ||||
| bool USRPDevice::setRxFreq(double wFreq) { return true;}; | ||||
| #endif | ||||
| 
 | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       InterfaceType iface, size_t chans, double lo_offset, | ||||
| 			       const std::vector<std::string>& tx_paths, | ||||
| 			       const std::vector<std::string>& rx_paths) | ||||
| RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity) | ||||
| { | ||||
| 	if (tx_sps != rx_sps) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (chans != 1) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (lo_offset != 0.0) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); | ||||
| 	return new USRPDevice(sps, chans, diversity); | ||||
| } | ||||
| @@ -1,10 +1,7 @@ | ||||
| /*
 | ||||
| * Copyright 2008 Free Software Foundation, Inc. | ||||
| * | ||||
| * SPDX-License-Identifier: AGPL-3.0+ | ||||
| * | ||||
| * This software is distributed under multiple licenses; see the COPYING file in | ||||
| * the main directory for licensing information for this specific distribuion. | ||||
| * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
| @@ -51,10 +48,15 @@ private: | ||||
|   usrp_subdev_spec rxSubdevSpec; | ||||
|   usrp_subdev_spec txSubdevSpec; | ||||
| 
 | ||||
|   int sps; | ||||
|   double actualSampleRate;	///< the actual USRP sampling rate
 | ||||
|   unsigned int decimRate;	///< the USRP decimation rate
 | ||||
| 
 | ||||
|   unsigned long long samplesRead;	///< number of samples read from USRP
 | ||||
|   unsigned long long samplesWritten;	///< number of samples sent to USRP
 | ||||
| 
 | ||||
|   bool started;			///< flag indicates USRP has started
 | ||||
|   bool skipRx;			///< set if USRP is transmit-only.
 | ||||
| 
 | ||||
|   static const unsigned int currDataSize_log2 = 21; | ||||
|   static const unsigned long currDataSize = (1 << currDataSize_log2); | ||||
| @@ -81,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; | ||||
| @@ -94,12 +96,10 @@ private: | ||||
|  public: | ||||
| 
 | ||||
|   /** Object constructor */ | ||||
|   USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset, | ||||
| 		const std::vector<std::string>& tx_paths, | ||||
| 		const std::vector<std::string>& rx_paths); | ||||
|   USRPDevice(size_t sps, size_t chans = 1, bool diversity = false); | ||||
| 
 | ||||
|   /** Instantiate the USRP */ | ||||
|   int open(const std::string &, int, bool); | ||||
|   int open(const std::string &, bool); | ||||
| 
 | ||||
|   /** Start the USRP */ | ||||
|   bool start(); | ||||
| @@ -179,28 +179,14 @@ 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 whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   bool requiresRadioAlign(); | ||||
| 
 | ||||
|   /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   virtual GSM::Time minLatency(); | ||||
| 
 | ||||
|   /** Return internal status values */ | ||||
|   inline double getTxFreq(size_t chan = 0) { return 0; } | ||||
|   inline double getRxFreq(size_t chan = 0) { return 0; } | ||||
|   inline double getSampleRate() { return actualSampleRate; } | ||||
|   inline double numberRead() { return samplesRead; } | ||||
|   inline double numberWritten() { return samplesWritten; } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif // _USRP_DEVICE_H_
 | ||||
| 
 | ||||
| @@ -1,8 +0,0 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| SUBDIRS = common | ||||
| if ARCH_ARM | ||||
| SUBDIRS += arm | ||||
| else | ||||
| SUBDIRS += x86 | ||||
| endif | ||||
| @@ -1,15 +0,0 @@ | ||||
| AM_CFLAGS = -Wall -std=gnu99 | ||||
|  | ||||
| noinst_LTLIBRARIES = libarch_common.la | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
|         convolve.h \ | ||||
|         convert.h \ | ||||
|         scale.h \ | ||||
|         mult.h \ | ||||
|         fft.h | ||||
|  | ||||
| libarch_common_la_SOURCES = \ | ||||
|         convolve_base.c \ | ||||
|         convert_base.c \ | ||||
|         fft.c | ||||
| @@ -1,15 +0,0 @@ | ||||
| #ifndef _CONVERT_H_ | ||||
| #define _CONVERT_H_ | ||||
|  | ||||
| void convert_float_short(short *out, const float *in, float scale, int len); | ||||
|  | ||||
| void convert_short_float(float *out, const short *in, int len); | ||||
|  | ||||
| void base_convert_float_short(short *out, const float *in, | ||||
| 			      float scale, int len); | ||||
|  | ||||
| void base_convert_short_float(float *out, const short *in, int len); | ||||
|  | ||||
| void convert_init(void); | ||||
|  | ||||
| #endif /* _CONVERT_H_ */ | ||||
| @@ -1,35 +0,0 @@ | ||||
| /* | ||||
|  * Conversion | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 "convert.h" | ||||
|  | ||||
| void base_convert_float_short(short *out, const float *in, | ||||
| 			      float scale, int len) | ||||
| { | ||||
| 	for (int i = 0; i < len; i++) | ||||
| 		out[i] = in[i] * scale; | ||||
| } | ||||
|  | ||||
| void base_convert_short_float(float *out, const short *in, int len) | ||||
| { | ||||
| 	for (int i = 0; i < len; i++) | ||||
| 		out[i] = in[i]; | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| #ifndef _CONVOLVE_H_ | ||||
| #define _CONVOLVE_H_ | ||||
|  | ||||
| void *convolve_h_alloc(size_t num); | ||||
|  | ||||
| int convolve_real(const float *x, int x_len, | ||||
| 		  const float *h, int h_len, | ||||
| 		  float *y, int y_len, | ||||
| 		  int start, int len); | ||||
|  | ||||
| int convolve_complex(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len); | ||||
|  | ||||
| int base_convolve_real(const float *x, int x_len, | ||||
| 		       const float *h, int h_len, | ||||
| 		       float *y, int y_len, | ||||
| 		       int start, int len); | ||||
|  | ||||
| int base_convolve_complex(const float *x, int x_len, | ||||
| 			  const float *h, int h_len, | ||||
| 			  float *y, int y_len, | ||||
| 			  int start, int len); | ||||
|  | ||||
| void convolve_init(void); | ||||
|  | ||||
| #endif /* _CONVOLVE_H_ */ | ||||
| @@ -1,114 +0,0 @@ | ||||
| /* | ||||
|  * Fast Fourier transform | ||||
|  * | ||||
|  * Copyright (C) 2012 Tom Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| #include <fftw3.h> | ||||
|  | ||||
| #include "fft.h" | ||||
|  | ||||
| struct fft_hdl { | ||||
| 	float *fft_in; | ||||
| 	float *fft_out; | ||||
| 	int len; | ||||
| 	fftwf_plan fft_plan; | ||||
| }; | ||||
|  | ||||
| /*! \brief Initialize FFT backend | ||||
|  *  \param[in] reverse FFT direction | ||||
|  *  \param[in] m FFT length | ||||
|  *  \param[in] istride input stride count | ||||
|  *  \param[in] ostride output stride count | ||||
|  *  \param[in] in input buffer (FFTW aligned) | ||||
|  *  \param[in] out output buffer (FFTW aligned) | ||||
|  *  \param[in] ooffset initial offset into output buffer | ||||
|  * | ||||
|  * If the reverse is non-NULL, then an inverse FFT will be used. This is a | ||||
|  * wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for | ||||
|  * further details. | ||||
|  * | ||||
|  *   http://www.fftw.org/doc/Advanced-Complex-DFTs.html | ||||
|  * | ||||
|  * It is currently unknown how the offset of the output buffer affects FFTW | ||||
|  * memory alignment. | ||||
|  */ | ||||
| struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride, | ||||
| 			 float *in, float *out, int ooffset) | ||||
| { | ||||
| 	int rank = 1; | ||||
| 	int n[] = { m }; | ||||
| 	int howmany = istride; | ||||
| 	int idist = 1; | ||||
| 	int odist = 1; | ||||
| 	int *inembed = n; | ||||
| 	int *onembed = n; | ||||
| 	fftwf_complex *obuffer, *ibuffer; | ||||
|  | ||||
| 	struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl)); | ||||
| 	if (!hdl) | ||||
| 		return NULL; | ||||
|  | ||||
| 	int direction = FFTW_FORWARD; | ||||
| 	if (reverse) | ||||
| 		direction = FFTW_BACKWARD; | ||||
|  | ||||
| 	ibuffer = (fftwf_complex *) in; | ||||
| 	obuffer = (fftwf_complex *) out + ooffset; | ||||
|  | ||||
| 	hdl->fft_in = in; | ||||
| 	hdl->fft_out = out; | ||||
| 	hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany, | ||||
| 					    ibuffer, inembed, istride, idist, | ||||
| 					    obuffer, onembed, ostride, odist, | ||||
| 					    direction, FFTW_MEASURE); | ||||
| 	return hdl; | ||||
| } | ||||
|  | ||||
| void *fft_malloc(size_t size) | ||||
| { | ||||
| 	return fftwf_malloc(size); | ||||
| } | ||||
|  | ||||
| void fft_free(void *ptr) | ||||
| { | ||||
| 	free(ptr); | ||||
| } | ||||
|  | ||||
| /*! \brief Free FFT backend resources | ||||
|  */ | ||||
| void free_fft(struct fft_hdl *hdl) | ||||
| { | ||||
| 	fftwf_destroy_plan(hdl->fft_plan); | ||||
| 	free(hdl); | ||||
| } | ||||
|  | ||||
| /*! \brief Run multiple DFT operations with the initialized plan | ||||
|  *  \param[in] hdl handle to an intitialized fft struct | ||||
|  * | ||||
|  * Input and output buffers are configured with init_fft(). | ||||
|  */ | ||||
| int cxvec_fft(struct fft_hdl *hdl) | ||||
| { | ||||
| 	fftwf_execute(hdl->fft_plan); | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| #ifndef _FFT_H_ | ||||
| #define _FFT_H_ | ||||
|  | ||||
| struct fft_hdl; | ||||
|  | ||||
| struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride, | ||||
| 			 float *in, float *out, int ooffset); | ||||
| void *fft_malloc(size_t size); | ||||
| void fft_free(void *ptr); | ||||
| void free_fft(struct fft_hdl *hdl); | ||||
| int cxvec_fft(struct fft_hdl *hdl); | ||||
|  | ||||
| #endif /* _FFT_H_ */ | ||||
| @@ -1,33 +0,0 @@ | ||||
| AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common | ||||
|  | ||||
| noinst_LTLIBRARIES = libarch.la | ||||
| noinst_LTLIBRARIES += libarch_sse_3.la | ||||
| noinst_LTLIBRARIES += libarch_sse_4_1.la | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	convert_sse_3.h \ | ||||
| 	convert_sse_4_1.h \ | ||||
| 	convolve_sse_3.h | ||||
|  | ||||
| libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la | ||||
|  | ||||
| # SSE 3 specific code | ||||
| if HAVE_SSE3 | ||||
| libarch_sse_3_la_SOURCES = \ | ||||
| 	convert_sse_3.c \ | ||||
| 	convolve_sse_3.c | ||||
| libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3 | ||||
| libarch_la_LIBADD += libarch_sse_3.la | ||||
| endif | ||||
|  | ||||
| # SSE 4.1 specific code | ||||
| if HAVE_SSE4_1 | ||||
| libarch_sse_4_1_la_SOURCES = \ | ||||
| 	convert_sse_4_1.c | ||||
| libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1 | ||||
| libarch_la_LIBADD += libarch_sse_4_1.la | ||||
| endif | ||||
|  | ||||
| libarch_la_SOURCES = \ | ||||
| 	convert.c \ | ||||
| 	convolve.c | ||||
| @@ -1,83 +0,0 @@ | ||||
| /* | ||||
|  * SSE type conversions | ||||
|  * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 <malloc.h> | ||||
| #include <string.h> | ||||
| #include "convert.h" | ||||
| #include "convert_sse_3.h" | ||||
| #include "convert_sse_4_1.h" | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| /* Architecture dependant function pointers */ | ||||
| struct convert_cpu_context { | ||||
| 	void (*convert_si16_ps_16n) (float *, const short *, int); | ||||
| 	void (*convert_si16_ps) (float *, const short *, int); | ||||
| 	void (*convert_scale_ps_si16_16n)(short *, const float *, float, int); | ||||
| 	void (*convert_scale_ps_si16_8n)(short *, const float *, float, int); | ||||
| 	void (*convert_scale_ps_si16)(short *, const float *, float, int); | ||||
| }; | ||||
|  | ||||
| static struct convert_cpu_context c; | ||||
|  | ||||
| void convert_init(void) | ||||
| { | ||||
| 	c.convert_scale_ps_si16_16n = base_convert_float_short; | ||||
| 	c.convert_scale_ps_si16_8n = base_convert_float_short; | ||||
| 	c.convert_scale_ps_si16 = base_convert_float_short; | ||||
| 	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; | ||||
| 		c.convert_si16_ps = &_sse_convert_si16_ps; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| 	if (__builtin_cpu_supports("sse3")) { | ||||
| 		c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n; | ||||
| 		c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n; | ||||
| 		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) | ||||
| { | ||||
| 	if (!(len % 16)) | ||||
| 		c.convert_scale_ps_si16_16n(out, in, scale, len); | ||||
| 	else if (!(len % 8)) | ||||
| 		c.convert_scale_ps_si16_8n(out, in, scale, len); | ||||
| 	else | ||||
| 		c.convert_scale_ps_si16(out, in, scale, len); | ||||
| } | ||||
|  | ||||
| void convert_short_float(float *out, const short *in, int len) | ||||
| { | ||||
| 	if (!(len % 16)) | ||||
| 		c.convert_si16_ps_16n(out, in, len); | ||||
| 	else | ||||
| 		c.convert_si16_ps(out, in, len); | ||||
| } | ||||
| @@ -1,107 +0,0 @@ | ||||
| /* | ||||
|  * SSE type conversions | ||||
|  * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 <malloc.h> | ||||
| #include <string.h> | ||||
| #include "convert_sse_3.h" | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| #include <xmmintrin.h> | ||||
| #include <emmintrin.h> | ||||
|  | ||||
| /* 8*N single precision floats scaled and converted to 16-bit signed integer */ | ||||
| void _sse_convert_scale_ps_si16_8n(short *restrict out, | ||||
| 				   const float *restrict in, | ||||
| 				   float scale, int len) | ||||
| { | ||||
| 	__m128 m0, m1, m2; | ||||
| 	__m128i m4, m5; | ||||
|  | ||||
| 	for (int i = 0; i < len / 8; i++) { | ||||
| 		/* Load (unaligned) packed floats */ | ||||
| 		m0 = _mm_loadu_ps(&in[8 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&in[8 * i + 4]); | ||||
| 		m2 = _mm_load1_ps(&scale); | ||||
|  | ||||
| 		/* Scale */ | ||||
| 		m0 = _mm_mul_ps(m0, m2); | ||||
| 		m1 = _mm_mul_ps(m1, m2); | ||||
|  | ||||
| 		/* Convert */ | ||||
| 		m4 = _mm_cvtps_epi32(m0); | ||||
| 		m5 = _mm_cvtps_epi32(m1); | ||||
|  | ||||
| 		/* Pack and store */ | ||||
| 		m5 = _mm_packs_epi32(m4, m5); | ||||
| 		_mm_storeu_si128((__m128i *) & out[8 * i], m5); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* 8*N single precision floats scaled and converted with remainder */ | ||||
| void _sse_convert_scale_ps_si16(short *restrict out, | ||||
| 				const float *restrict in, float scale, int len) | ||||
| { | ||||
| 	int start = len / 8 * 8; | ||||
|  | ||||
| 	_sse_convert_scale_ps_si16_8n(out, in, scale, len); | ||||
|  | ||||
| 	for (int i = 0; i < len % 8; i++) | ||||
| 		out[start + i] = in[start + i] * scale; | ||||
| } | ||||
|  | ||||
| /* 16*N single precision floats scaled and converted to 16-bit signed integer */ | ||||
| void _sse_convert_scale_ps_si16_16n(short *restrict out, | ||||
| 				    const float *restrict in, | ||||
| 				    float scale, int len) | ||||
| { | ||||
| 	__m128 m0, m1, m2, m3, m4; | ||||
| 	__m128i m5, m6, m7, m8; | ||||
|  | ||||
| 	for (int i = 0; i < len / 16; i++) { | ||||
| 		/* Load (unaligned) packed floats */ | ||||
| 		m0 = _mm_loadu_ps(&in[16 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&in[16 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&in[16 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&in[16 * i + 12]); | ||||
| 		m4 = _mm_load1_ps(&scale); | ||||
|  | ||||
| 		/* Scale */ | ||||
| 		m0 = _mm_mul_ps(m0, m4); | ||||
| 		m1 = _mm_mul_ps(m1, m4); | ||||
| 		m2 = _mm_mul_ps(m2, m4); | ||||
| 		m3 = _mm_mul_ps(m3, m4); | ||||
|  | ||||
| 		/* Convert */ | ||||
| 		m5 = _mm_cvtps_epi32(m0); | ||||
| 		m6 = _mm_cvtps_epi32(m1); | ||||
| 		m7 = _mm_cvtps_epi32(m2); | ||||
| 		m8 = _mm_cvtps_epi32(m3); | ||||
|  | ||||
| 		/* Pack and store */ | ||||
| 		m5 = _mm_packs_epi32(m5, m6); | ||||
| 		m7 = _mm_packs_epi32(m7, m8); | ||||
| 		_mm_storeu_si128((__m128i *) & out[16 * i + 0], m5); | ||||
| 		_mm_storeu_si128((__m128i *) & out[16 * i + 8], m7); | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
| @@ -1,34 +0,0 @@ | ||||
| /* | ||||
|  * SSE type conversions | ||||
|  * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /* 8*N single precision floats scaled and converted to 16-bit signed integer */ | ||||
| void _sse_convert_scale_ps_si16_8n(short *restrict out, | ||||
| 				   const float *restrict in, | ||||
| 				   float scale, int len); | ||||
|  | ||||
| /* 8*N single precision floats scaled and converted with remainder */ | ||||
| void _sse_convert_scale_ps_si16(short *restrict out, | ||||
| 				const float *restrict in, float scale, int len); | ||||
|  | ||||
| /* 16*N single precision floats scaled and converted to 16-bit signed integer */ | ||||
| void _sse_convert_scale_ps_si16_16n(short *restrict out, | ||||
| 				    const float *restrict in, | ||||
| 				    float scale, int len); | ||||
| @@ -1,77 +0,0 @@ | ||||
| /* | ||||
|  * SSE type conversions | ||||
|  * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 <malloc.h> | ||||
| #include <string.h> | ||||
| #include "convert_sse_4_1.h" | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_SSE4_1 | ||||
| #include <smmintrin.h> | ||||
|  | ||||
| /* 16*N 16-bit signed integer converted to single precision floats */ | ||||
| void _sse_convert_si16_ps_16n(float *restrict out, | ||||
| 			      const short *restrict in, int len) | ||||
| { | ||||
| 	__m128i m0, m1, m2, m3, m4, m5; | ||||
| 	__m128 m6, m7, m8, m9; | ||||
|  | ||||
| 	for (int i = 0; i < len / 16; i++) { | ||||
| 		/* Load (unaligned) packed floats */ | ||||
| 		m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]); | ||||
| 		m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]); | ||||
|  | ||||
| 		/* Unpack */ | ||||
| 		m2 = _mm_cvtepi16_epi32(m0); | ||||
| 		m4 = _mm_cvtepi16_epi32(m1); | ||||
| 		m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2)); | ||||
| 		m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2)); | ||||
| 		m3 = _mm_cvtepi16_epi32(m0); | ||||
| 		m5 = _mm_cvtepi16_epi32(m1); | ||||
|  | ||||
| 		/* Convert */ | ||||
| 		m6 = _mm_cvtepi32_ps(m2); | ||||
| 		m7 = _mm_cvtepi32_ps(m3); | ||||
| 		m8 = _mm_cvtepi32_ps(m4); | ||||
| 		m9 = _mm_cvtepi32_ps(m5); | ||||
|  | ||||
| 		/* Store */ | ||||
| 		_mm_storeu_ps(&out[16 * i + 0], m6); | ||||
| 		_mm_storeu_ps(&out[16 * i + 4], m7); | ||||
| 		_mm_storeu_ps(&out[16 * i + 8], m8); | ||||
| 		_mm_storeu_ps(&out[16 * i + 12], m9); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* 16*N 16-bit signed integer conversion with remainder */ | ||||
| void _sse_convert_si16_ps(float *restrict out, | ||||
| 			  const short *restrict in, int len) | ||||
| { | ||||
| 	int start = len / 16 * 16; | ||||
|  | ||||
| 	_sse_convert_si16_ps_16n(out, in, len); | ||||
|  | ||||
| 	for (int i = 0; i < len % 16; i++) | ||||
| 		out[start + i] = in[start + i]; | ||||
| } | ||||
|  | ||||
| #endif | ||||
| @@ -1,28 +0,0 @@ | ||||
| /* | ||||
|  * SSE type conversions | ||||
|  * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /* 16*N 16-bit signed integer converted to single precision floats */ | ||||
| void _sse_convert_si16_ps_16n(float *restrict out, | ||||
| 			      const short *restrict in, int len); | ||||
|  | ||||
| /* 16*N 16-bit signed integer conversion with remainder */ | ||||
| void _sse_convert_si16_ps(float *restrict out, | ||||
| 			  const short *restrict in, int len); | ||||
| @@ -1,154 +0,0 @@ | ||||
| /* | ||||
|  * SSE Convolution | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 <malloc.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include "convolve.h" | ||||
| #include "convolve_sse_3.h" | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| /* Architecture dependant function pointers */ | ||||
| struct convolve_cpu_context { | ||||
| 	void (*conv_cmplx_4n) (const float *, int, const float *, int, float *, | ||||
| 			       int, int, int); | ||||
| 	void (*conv_cmplx_8n) (const float *, int, const float *, int, float *, | ||||
| 			       int, int, int); | ||||
| 	void (*conv_cmplx) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int); | ||||
| 	void (*conv_real4) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int); | ||||
| 	void (*conv_real8) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int); | ||||
| 	void (*conv_real12) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int); | ||||
| 	void (*conv_real16) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int); | ||||
| 	void (*conv_real20) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int); | ||||
| 	void (*conv_real4n) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int); | ||||
| 	void (*conv_real) (const float *, int, const float *, int, float *, int, | ||||
| 			   int, int); | ||||
| }; | ||||
| static struct convolve_cpu_context c; | ||||
|  | ||||
| /* Forward declarations from base implementation */ | ||||
| int _base_convolve_real(const float *x, int x_len, | ||||
| 			const float *h, int h_len, | ||||
| 			float *y, int y_len, | ||||
| 			int start, int len); | ||||
|  | ||||
| int _base_convolve_complex(const float *x, int x_len, | ||||
| 			   const float *h, int h_len, | ||||
| 			   float *y, int y_len, | ||||
| 			   int start, int len); | ||||
|  | ||||
| int bounds_check(int x_len, int h_len, int y_len, | ||||
| 		 int start, int len); | ||||
|  | ||||
| /* API: Initalize convolve module */ | ||||
| void convolve_init(void) | ||||
| { | ||||
| 	c.conv_cmplx_4n = (void *)_base_convolve_complex; | ||||
| 	c.conv_cmplx_8n = (void *)_base_convolve_complex; | ||||
| 	c.conv_cmplx = (void *)_base_convolve_complex; | ||||
| 	c.conv_real4 = (void *)_base_convolve_real; | ||||
| 	c.conv_real8 = (void *)_base_convolve_real; | ||||
| 	c.conv_real12 = (void *)_base_convolve_real; | ||||
| 	c.conv_real16 = (void *)_base_convolve_real; | ||||
| 	c.conv_real20 = (void *)_base_convolve_real; | ||||
| 	c.conv_real4n = (void *)_base_convolve_real; | ||||
| 	c.conv_real = (void *)_base_convolve_real; | ||||
|  | ||||
| #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; | ||||
| 		c.conv_real4 = sse_conv_real4; | ||||
| 		c.conv_real8 = sse_conv_real8; | ||||
| 		c.conv_real12 = sse_conv_real12; | ||||
| 		c.conv_real16 = sse_conv_real16; | ||||
| 		c.conv_real20 = sse_conv_real20; | ||||
| 		c.conv_real4n = sse_conv_real4n; | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* API: Aligned complex-real */ | ||||
| int convolve_real(const float *x, int x_len, | ||||
| 		  const float *h, int h_len, | ||||
| 		  float *y, int y_len, int start, int len) | ||||
| { | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len) < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
|  | ||||
| 	switch (h_len) { | ||||
| 	case 4: | ||||
| 		c.conv_real4(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 		break; | ||||
| 	case 8: | ||||
| 		c.conv_real8(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 		break; | ||||
| 	case 12: | ||||
| 		c.conv_real12(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 		break; | ||||
| 	case 16: | ||||
| 		c.conv_real16(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 		break; | ||||
| 	case 20: | ||||
| 		c.conv_real20(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 		break; | ||||
| 	default: | ||||
| 		if (!(h_len % 4)) | ||||
| 			c.conv_real4n(x, x_len, h, h_len, y, y_len, | ||||
| 				      start, len); | ||||
| 		else | ||||
| 			c.conv_real(x, x_len, h, h_len, y, y_len, start, | ||||
| 				    len); | ||||
| 	} | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| /* API: Aligned complex-complex */ | ||||
| int convolve_complex(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len) | ||||
| { | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len) < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
|  | ||||
| 	if (!(h_len % 8)) | ||||
| 		c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 	else if (!(h_len % 4)) | ||||
| 		c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len); | ||||
| 	else | ||||
| 		c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len); | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| /* | ||||
|  * SSE Convolution | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /* 4-tap SSE complex-real convolution */ | ||||
| void sse_conv_real4(const float *x, int x_len, | ||||
| 		    const float *h, int h_len, | ||||
| 		    float *y, int y_len, | ||||
| 		    int start, int len); | ||||
|  | ||||
| /* 8-tap SSE complex-real convolution */ | ||||
| void sse_conv_real8(const float *x, int x_len, | ||||
| 		    const float *h, int h_len, | ||||
| 		    float *y, int y_len, | ||||
| 		    int start, int len); | ||||
|  | ||||
| /* 12-tap SSE complex-real convolution */ | ||||
| void sse_conv_real12(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len); | ||||
|  | ||||
| /* 16-tap SSE complex-real convolution */ | ||||
| void sse_conv_real16(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len); | ||||
|  | ||||
| /* 20-tap SSE complex-real convolution */ | ||||
| void sse_conv_real20(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len); | ||||
|  | ||||
| /* 4*N-tap SSE complex-real convolution */ | ||||
| void sse_conv_real4n(const float *x, int x_len, | ||||
| 		     const float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len); | ||||
|  | ||||
| /* 4*N-tap SSE complex-complex convolution */ | ||||
| void sse_conv_cmplx_4n(const float *x, int x_len, | ||||
| 		       const float *h, int h_len, | ||||
| 		       float *y, int y_len, | ||||
| 		       int start, int len); | ||||
|  | ||||
| /* 8*N-tap SSE complex-complex convolution */ | ||||
| void sse_conv_cmplx_8n(const float *x, int x_len, | ||||
| 		       const float *h, int h_len, | ||||
| 		       float *y, int y_len, | ||||
| 		       int start, int len); | ||||
| @@ -1,17 +1,17 @@ | ||||
| if ARCH_ARM | ||||
| if ARCH_ARM_A15 | ||||
| ARCH_FLAGS = -mfpu=neon-vfpv4 | ||||
| else | ||||
| ARCH_FLAGS = -mfpu=neon | ||||
| endif | ||||
| 
 | ||||
| AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common | ||||
| AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common | ||||
| AM_CCASFLAGS = $(ARCH_FLAGS) | ||||
| 
 | ||||
| noinst_LTLIBRARIES = libarch.la | ||||
| 
 | ||||
| libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la | ||||
| 
 | ||||
| libarch_la_SOURCES = \
 | ||||
| 	../common/convolve_base.c \
 | ||||
| 	convert.c \
 | ||||
| 	convert_neon.S \
 | ||||
| 	convolve.c \
 | ||||
| @@ -20,3 +20,4 @@ libarch_la_SOURCES = \ | ||||
| 	scale_neon.S \
 | ||||
| 	mult.c \
 | ||||
| 	mult_neon.S | ||||
| endif | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * NEON type conversions | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
| @@ -27,15 +25,25 @@ | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| void neon_convert_ps_si16_4n(short *, const float *, const float *, int); | ||||
| void neon_convert_si16_ps_4n(float *, const short *, int); | ||||
| void neon_convert_ps_si16_4n(short *, float *, float *, int); | ||||
| void neon_convert_si16_ps_4n(float *, short *, int); | ||||
| 
 | ||||
| void convert_init(void) { | ||||
| #ifndef HAVE_NEON | ||||
| static void convert_si16_ps(float *out, short *in, int len) | ||||
| { | ||||
| 	for (int i = 0; i < len; i++) | ||||
| 		out[i] = in[i]; | ||||
| } | ||||
| 
 | ||||
| static void convert_ps_si16(short *out, float *in, float scale, int len) | ||||
| { | ||||
| 	for (int i = 0; i < len; i++) | ||||
| 		out[i] = in[i] * scale; | ||||
| } | ||||
| #else | ||||
| /* 4*N 16-bit signed integer conversion with remainder */ | ||||
| static void neon_convert_si16_ps(float *out, | ||||
| 				 const short *in, | ||||
| static void neon_convert_si16_ps(float *restrict out, | ||||
| 				 short *restrict in, | ||||
| 				 int len) | ||||
| { | ||||
| 	int start = len / 4 * 4; | ||||
| @@ -47,9 +55,9 @@ static void neon_convert_si16_ps(float *out, | ||||
| } | ||||
| 
 | ||||
| /* 4*N 16-bit signed integer conversion with remainder */ | ||||
| static void neon_convert_ps_si16(short *out, | ||||
| 				 const float *in, | ||||
| 				 const float *scale, | ||||
| static void neon_convert_ps_si16(short *restrict out, | ||||
| 				 float *restrict in, | ||||
| 				 float *restrict scale, | ||||
| 				 int len) | ||||
| { | ||||
| 	int start = len / 4 * 4; | ||||
| @@ -59,8 +67,9 @@ 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) | ||||
| void convert_float_short(short *out, float *in, float scale, int len) | ||||
| { | ||||
| #ifdef HAVE_NEON | ||||
|         float q[4] = { scale, scale, scale, scale }; | ||||
| @@ -70,11 +79,11 @@ void convert_float_short(short *out, const float *in, float scale, int len) | ||||
| 	else | ||||
| 		neon_convert_ps_si16_4n(out, in, q, len >> 2); | ||||
| #else | ||||
| 	base_convert_float_short(out, in, scale, len); | ||||
| 	convert_ps_si16(out, in, scale, len); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void convert_short_float(float *out, const short *in, int len) | ||||
| void convert_short_float(float *out, short *in, int len) | ||||
| { | ||||
| #ifdef HAVE_NEON | ||||
| 	if (len % 4) | ||||
| @@ -82,6 +91,6 @@ void convert_short_float(float *out, const short *in, int len) | ||||
| 	else | ||||
| 		neon_convert_si16_ps_4n(out, in, len >> 2); | ||||
| #else | ||||
| 	base_convert_short_float(out, in, len); | ||||
| 	convert_si16_ps(out, in, len); | ||||
| #endif | ||||
| } | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * NEON type conversions | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
 | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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
 | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * NEON Convolution | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
| @@ -31,15 +29,17 @@ | ||||
| int _base_convolve_real(float *x, int x_len, | ||||
| 			float *h, int h_len, | ||||
| 			float *y, int y_len, | ||||
| 			int start, int len); | ||||
| 			int start, int len, | ||||
| 			int step, int offset); | ||||
| 
 | ||||
| int _base_convolve_complex(float *x, int x_len, | ||||
| 			   float *h, int h_len, | ||||
| 			   float *y, int y_len, | ||||
| 			   int start, int len); | ||||
| 			   int start, int len, | ||||
| 			   int step, int offset); | ||||
| 
 | ||||
| int bounds_check(int x_len, int h_len, int y_len, | ||||
| 		 int start, int len); | ||||
| 		 int start, int len, int step); | ||||
| 
 | ||||
| #ifdef HAVE_NEON | ||||
| /* Calls into NEON assembler */ | ||||
| @@ -58,43 +58,39 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len) | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /* API: Initalize convolve module */ | ||||
| void convolve_init(void) | ||||
| { | ||||
| 	/* Stub */ | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| /* API: Aligned complex-real */ | ||||
| int convolve_real(float *x, int x_len, | ||||
| 		  float *h, int h_len, | ||||
| 		  float *y, int y_len, | ||||
| 		  int start, int len) | ||||
| 		  int start, int len, | ||||
| 		  int step, int offset) | ||||
| { | ||||
| 	void (*conv_func)(float *, float *, float *, int) = NULL; | ||||
| 
 | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len) < 0) | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
| 
 | ||||
| #ifdef HAVE_NEON | ||||
| 	switch (h_len) { | ||||
| 	case 4: | ||||
| 		conv_func = neon_conv_real4; | ||||
| 		break; | ||||
| 	case 8: | ||||
| 		conv_func = neon_conv_real8; | ||||
| 		break; | ||||
| 	case 12: | ||||
| 		conv_func = neon_conv_real12; | ||||
| 		break; | ||||
| 	case 16: | ||||
| 		conv_func = neon_conv_real16; | ||||
| 		break; | ||||
| 	case 20: | ||||
| 		conv_func = neon_conv_real20; | ||||
| 		break; | ||||
| 	if (step <= 4) { | ||||
| 		switch (h_len) { | ||||
| 		case 4: | ||||
| 			conv_func = neon_conv_real4; | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			conv_func = neon_conv_real8; | ||||
| 			break; | ||||
| 		case 12: | ||||
| 			conv_func = neon_conv_real12; | ||||
| 			break; | ||||
| 		case 16: | ||||
| 			conv_func = neon_conv_real16; | ||||
| 			break; | ||||
| 		case 20: | ||||
| 			conv_func = neon_conv_real20; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 	if (conv_func) { | ||||
| @@ -104,7 +100,7 @@ int convolve_real(float *x, int x_len, | ||||
| 		_base_convolve_real(x, x_len, | ||||
| 				    h, h_len, | ||||
| 				    y, y_len, | ||||
| 				    start, len); | ||||
| 				    start, len, step, offset); | ||||
| 	} | ||||
| 
 | ||||
| 	return len; | ||||
| @@ -115,17 +111,18 @@ int convolve_real(float *x, int x_len, | ||||
| int convolve_complex(float *x, int x_len, | ||||
| 		     float *h, int h_len, | ||||
| 		     float *y, int y_len, | ||||
| 		     int start, int len) | ||||
| 		     int start, int len, | ||||
| 		     int step, int offset) | ||||
| { | ||||
| 	void (*conv_func)(float *, float *, float *, int, int) = NULL; | ||||
| 
 | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len) < 0) | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
| 
 | ||||
| #ifdef HAVE_NEON | ||||
| 	if (!(h_len % 4)) | ||||
| 	if (step <= 4 && !(h_len % 4)) | ||||
| 		conv_func = neon_conv_cmplx_4n; | ||||
| #endif | ||||
| 	if (conv_func) { | ||||
| @@ -135,7 +132,7 @@ int convolve_complex(float *x, int x_len, | ||||
| 		_base_convolve_complex(x, x_len, | ||||
| 				       h, h_len, | ||||
| 				       y, y_len, | ||||
| 				       start, len); | ||||
| 				       start, len, step, offset); | ||||
| 	} | ||||
| 
 | ||||
| 	return len; | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * NEON Convolution | ||||
|  * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
 | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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
 | ||||
| @@ -94,8 +92,8 @@ neon_conv_real12: | ||||
| 	vld2.32     {q8-q9}, [r4], r6 | ||||
| 	vld2.32   {q10-q11}, [r5], r6 | ||||
| #ifdef HAVE_NEON_FMA | ||||
| 	vmul.f32         q1,   q6, q0 | ||||
| 	vmul.f32         q3,   q7, q0 | ||||
| 	vfma.f32         q1,   q6, q0 | ||||
| 	vfma.f32         q3,   q7, q0 | ||||
| 	vfma.f32         q1,   q8, q2 | ||||
| 	vfma.f32         q3,   q9, q2 | ||||
| 	vfma.f32         q1,  q10, q4 | ||||
| @@ -2,8 +2,6 @@ | ||||
|  * NEON scaling | ||||
|  * Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc> | ||||
|  * | ||||
|  * SPDX-License-Identifier: LGPL-2.1+ | ||||
|  * | ||||
|  * 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 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user