mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-10-31 12:13:34 +00:00 
			
		
		
		
	Compare commits
	
		
			351 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d20b7fa579 | ||
|  | 77e18352fb | ||
|  | f97d75b355 | ||
|  | a944001873 | ||
|  | 42c165605a | ||
|  | affd351787 | ||
|  | 03b11620d9 | ||
|  | 25185886f0 | ||
|  | 47031405f5 | ||
|  | 0646b3ce75 | ||
|  | 46cf9efc8e | ||
|  | 871713bf6a | ||
|  | 8d9a05ce5b | ||
|  | fe865f45d7 | ||
|  | e5b6664419 | ||
|  | 1595ddaa5f | ||
|  | 441d82add9 | ||
|  | 867cea575b | ||
|  | 2f53ea4cf2 | ||
|  | 138caaf09d | ||
|  | 3fadcad33e | ||
|  | eaa0144dcb | ||
|  | f7331764ac | ||
|  | 800c029c70 | ||
|  | 522cbe91d3 | ||
|  | ed361f9912 | ||
|  | 2d085e290b | ||
|  | 5cea18e5cb | ||
|  | 4adc4eb8d0 | ||
|  | 41c68aa41c | ||
|  | 39e0bbacca | ||
|  | b4ea7b5211 | ||
|  | 69869bd58f | ||
|  | ebb37693a5 | ||
|  | 55928f23cb | ||
|  | 0277b58f6d | ||
|  | be8a83f681 | ||
|  | d9a460dce9 | ||
|  | f570b4af62 | ||
|  | 46560ea254 | ||
|  | 038ab45137 | ||
|  | 6b50b62b12 | ||
|  | 3984256273 | ||
|  | d06172ce32 | ||
|  | 762c951faa | ||
|  | e47d7e964a | ||
|  | 3cee25ac68 | ||
|  | 0bd0782a39 | ||
|  | 8072650406 | ||
|  | c6541e6c26 | ||
|  | 1e2c0105e2 | ||
|  | 32b3c2e4b2 | ||
|  | 1c4bbadda6 | ||
|  | 08dfe237c8 | ||
|  | 444ff34396 | ||
|  | a79bc70737 | ||
|  | a439fed166 | ||
|  | 8fb0c3dce4 | ||
|  | f5e1cf8790 | ||
|  | e7d267f178 | ||
|  | 06f3b622b8 | ||
|  | 74bcc562a9 | ||
|  | 55dd2aa5ab | ||
|  | 5b60c98769 | ||
|  | 207d8a2624 | ||
|  | 92fc186450 | ||
|  | 4d179abfd0 | ||
|  | 6fdfb68b9e | ||
|  | 5ac2cb3662 | ||
|  | f4ee021b8c | ||
|  | e62555370e | ||
|  | 1f15152968 | ||
|  | 21ce05c54f | ||
|  | 5e68cde779 | ||
|  | 1f4a009c67 | ||
|  | 544ce0d6b7 | ||
|  | b148d05542 | ||
|  | 970096932e | ||
|  | db936b9b55 | ||
|  | 49ad759072 | ||
|  | 8e498bfd35 | ||
|  | 46444637c6 | ||
|  | 86be40b4eb | ||
|  | 288d8af070 | ||
|  | aae403f0c9 | ||
|  | 5cc8858d8f | ||
|  | 70d0344b31 | ||
|  | 62002fb8ac | ||
|  | 03b3c30533 | ||
|  | a4b569d936 | ||
|  | eb0945d85d | ||
|  | f968507912 | ||
|  | 58c89fb8d6 | ||
|  | 16e7e20f85 | ||
|  | c7756e73b7 | ||
|  | 7ed686b223 | ||
|  | 2ab6ddb0de | ||
|  | b229439b31 | ||
|  | ffb3301bd8 | ||
|  | 62b7900fd7 | ||
|  | 61707e8b23 | ||
|  | ce70ba529c | ||
|  | cfb9dac70d | ||
|  | 105a61e689 | ||
|  | 2407314f2e | ||
|  | ff4418539c | ||
|  | 0494d05916 | ||
|  | fd268b6f5a | ||
|  | 43f3678b40 | ||
|  | 6493dc838b | ||
|  | f7905ac548 | ||
|  | 0c27938d44 | ||
|  | 587916e810 | ||
|  | 79024867de | ||
|  | 4aec1f1cf5 | ||
|  | 68f054169b | ||
|  | ea8be22e50 | ||
|  | a5054b398b | ||
|  | a438114173 | ||
|  | 9cb4f27112 | ||
|  | c38e45e9dc | ||
|  | 1f50fedb5f | ||
|  | c7a0bf1ffc | ||
|  | 940738e86a | ||
|  | 8c1e2bddff | ||
|  | 01eea0aa42 | ||
|  | 55df1e43e3 | ||
|  | e9424e241f | ||
|  | d0ac926b56 | ||
|  | 00d5114717 | ||
|  | 3a496f3b8a | ||
|  | fad2e09840 | ||
|  | e09e80f5ee | ||
|  | 2e276e7edd | ||
|  | dffc21725c | ||
|  | 96f0f2cf7e | ||
|  | 225b16d48e | ||
|  | 0ebbb2ed2e | ||
|  | 2fea950644 | ||
|  | d0a97a5f73 | ||
|  | 295b938d51 | ||
|  | e8605202ab | ||
|  | 2a8183bdf0 | ||
|  | 478f82f47e | ||
|  | f37b0ad652 | ||
|  | 3b78cbfdc1 | ||
|  | f3d7f443a0 | ||
|  | e564f0fd84 | ||
|  | 0fc20d14b3 | ||
|  | a4316ee4c5 | ||
|  | 2128a308eb | ||
|  | 43fedb656b | ||
|  | 53bdb7f82a | ||
|  | 6462dd3963 | ||
|  | e1977fcd22 | ||
|  | f97296e0ce | ||
|  | 20259cb307 | ||
|  | ffa4e5938c | ||
|  | c0c6d70fe9 | ||
|  | 8c6c5d2bcd | ||
|  | a62fcf786a | ||
|  | 4d9b59c3ef | ||
|  | bd0efb0bea | ||
|  | 8fbbd656c7 | ||
|  | b35cba613a | ||
|  | 8dffadb8da | ||
|  | 408f25081e | ||
|  | 2001550f7d | ||
|  | a3ab8c263d | ||
|  | efac20b6bb | ||
|  | 0bbd8922ea | ||
|  | 28b8cc6283 | ||
|  | 3f52f0e6c5 | ||
|  | 3da1f8352e | ||
|  | 5ea1817dc2 | ||
|  | 49d42e979e | ||
|  | 3a3b220751 | ||
|  | ab22f4c421 | ||
|  | 8b843e5bed | ||
|  | c92dad32dd | ||
|  | 61837c0420 | ||
|  | f83e11fefd | ||
|  | 01aff88ce9 | ||
|  | 11d50d950c | ||
|  | 8bd111c942 | ||
|  | 3808e479aa | ||
|  | bd45a979f8 | ||
|  | b7095c7bc5 | ||
|  | 77ce99ac67 | ||
|  | f58cd8ac83 | ||
|  | 99eb07e232 | ||
|  | 89be118a3b | ||
|  | 6fafd33b13 | ||
|  | 6e55d51747 | ||
|  | 6cae1d7b4b | ||
|  | 28ce315a32 | ||
|  | 10d76b6863 | ||
|  | 708b8b44ae | ||
|  | cb0fc9b21a | ||
|  | 8639fee504 | ||
|  | ca46896cfe | ||
|  | 4a25d6b8f6 | ||
|  | 79baee3a8f | ||
|  | c2ba427b52 | ||
|  | 611212676b | ||
|  | 4ebb289c90 | ||
|  | 2f376a3edf | ||
|  | 2edbe4d366 | ||
|  | a3694bd303 | ||
|  | 2652f2bc39 | ||
|  | 93d9b114b7 | ||
|  | 2ac788b2c3 | ||
|  | d36ef2f57b | ||
|  | caf2abc58f | ||
|  | de1685f6d7 | ||
|  | f3837d26f9 | ||
|  | ddf4743306 | ||
|  | 82f83ced73 | ||
|  | cff4ed9b4c | ||
|  | 6ec26bb788 | ||
|  | a1ff991402 | ||
|  | d09843c692 | ||
|  | e5448ff972 | ||
|  | e48c1367dc | ||
|  | aa60dda99a | ||
|  | 1468a5c3dc | ||
|  | b0e1bd8c22 | ||
|  | 78e1cd20e2 | ||
|  | db9c1b54cb | ||
|  | 099a44abfb | ||
|  | 8c80095017 | ||
|  | d49a6aa136 | ||
|  | 81486e053c | ||
|  | 28d8081e25 | ||
|  | 87ed77b937 | ||
|  | f9d996813d | ||
|  | aa5acc953c | ||
|  | 934da48618 | ||
|  | 7c405a0c1f | ||
|  | 4cafb0fa15 | ||
|  | f611569018 | ||
|  | 354741326c | ||
|  | d2e5c5694e | ||
|  | a3dce85ffc | ||
|  | bb0c68ae61 | ||
|  | 87d158cc2d | ||
|  | 7278a87767 | ||
|  | 63eef9faf2 | ||
|  | d67bd603e9 | ||
|  | 988a464d5d | ||
|  | 1b6ab7d7ee | ||
|  | 980525c8a9 | ||
|  | 70134a01eb | ||
|  | 1fb0ce67d8 | ||
|  | 8ca237b5c2 | ||
|  | 082bbbf8fe | ||
|  | 3bd763d2a1 | ||
|  | ee57357682 | ||
|  | 8537b90dbe | ||
|  | 038fd7fd70 | ||
|  | 0cd246c27a | ||
|  | 61fbf2ec95 | ||
|  | 15f9d95f5f | ||
|  | 73dbccda78 | ||
|  | 5e65b531e0 | ||
|  | b992d0a515 | ||
|  | d6ae8648ff | ||
|  | e51a8f029e | ||
|  | e8ae9fcf38 | ||
|  | f5bf33b287 | ||
|  | fe9769833f | ||
|  | 7e07cf2346 | ||
|  | dfe0aef184 | ||
|  | 131f82bfac | ||
|  | 78b5627fa1 | ||
|  | de116e90c0 | ||
|  | 15da7e1f7e | ||
|  | 6031734f44 | ||
|  | 5d2a36a113 | ||
|  | 2af14407a8 | ||
|  | 92bdfb86ac | ||
|  | ae91f13ecb | ||
|  | 9d53ecf666 | ||
|  | e0c12189d4 | ||
|  | 1470fcdb5a | ||
|  | 6e1dffd486 | ||
|  | 0229d22d2e | ||
|  | f7717acd0c | ||
|  | b34e60c105 | ||
|  | 4e6c938024 | ||
|  | 4aa548f0c2 | ||
|  | f9e78beea5 | ||
|  | f0189c47be | ||
|  | c708816be1 | ||
|  | e56bf3a0e5 | ||
|  | 38b69871ae | ||
|  | 7db522b6d9 | ||
|  | ae09b04e26 | ||
|  | b61c610cd9 | ||
|  | 132fb247b1 | ||
|  | 1c0b8b355c | ||
|  | 1dd05cf35a | ||
|  | 14d13b67dc | ||
|  | 89bca9b2de | ||
|  | 9270a5aa2e | ||
|  | 4793f4679b | ||
|  | 802b86502d | ||
|  | a93f789e50 | ||
|  | 72bf762b42 | ||
|  | 2dee3e996e | ||
|  | 1f1cebb2e5 | ||
|  | d1b28bd766 | ||
|  | 833e97e9ba | ||
|  | e6d059f0c9 | ||
|  | 012a1b345b | ||
|  | 80cb08071b | ||
|  | 44c7f41d75 | ||
|  | 9436fbbf3c | ||
|  | 93ca09ea61 | ||
|  | 365bc38bee | ||
|  | 43242efc85 | ||
|  | 76b98cf236 | ||
|  | aa15d62a8c | ||
|  | 2e5e2c537b | ||
|  | 8f0ccf618d | ||
|  | 06676ead63 | ||
|  | 4609f3285c | ||
|  | 7c741ec6a6 | ||
|  | 2f3e60bc1f | ||
|  | cbfef6e40a | ||
|  | b577ef014f | ||
|  | c37594f3b9 | ||
|  | ffee30d190 | ||
|  | 24575a6530 | ||
|  | 1e9801411b | ||
|  | 64464e6c34 | ||
|  | e88710881b | ||
|  | a84e162672 | ||
|  | 7676427816 | ||
|  | 35222296fe | ||
|  | 28670fb5da | ||
|  | 05c6feb71d | ||
|  | 2e4ed10722 | ||
|  | c8c4eac55e | ||
|  | 37c52c79cf | ||
|  | 58e9591f9e | ||
|  | 19174f581b | ||
|  | 1ba69e7762 | ||
|  | f931cf226b | ||
|  | e476231deb | ||
|  | e90c24c8d5 | 
							
								
								
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| # 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 | ||||
							
								
								
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| [gerrit] | ||||
| host=gerrit.osmocom.org | ||||
| project=osmo-trx | ||||
							
								
								
									
										83
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -1,18 +1,18 @@ | ||||
| # | ||||
| # Copyright 2008, 2009 Free Software Foundation, Inc. | ||||
| #  | ||||
| # | ||||
| # This file is part of GNU Radio | ||||
| #  | ||||
| # | ||||
| # GNU Radio is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 3, or (at your option) | ||||
| # any later version. | ||||
| #  | ||||
| # | ||||
| # GNU Radio is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| #  | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License along | ||||
| # with this program; if not, write to the Free Software Foundation, Inc., | ||||
| # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| @@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com: | ||||
|     CLI/CLI.h | ||||
|     CommonLibs/Assert.h | ||||
|     CommonLibs/BitVector.cpp | ||||
|     CommonLibs/BitVectorTest.cpp | ||||
|     CommonLibs/Configuration.cpp | ||||
|     CommonLibs/Configuration.h | ||||
|     CommonLibs/ConfigurationTest.cpp | ||||
|     CommonLibs/Interthread.h | ||||
|     CommonLibs/InterthreadTest.cpp | ||||
|     CommonLibs/LinkedLists.cpp | ||||
|     CommonLibs/LinkedLists.h | ||||
|     CommonLibs/Regexp.h | ||||
|     CommonLibs/RegexpTest.cpp | ||||
|     CommonLibs/Sockets.cpp | ||||
|     CommonLibs/Sockets.h | ||||
|     CommonLibs/SocketsTest.cpp | ||||
|     CommonLibs/Threads.cpp | ||||
|     CommonLibs/Threads.h | ||||
|     CommonLibs/Timeval.cpp | ||||
|     CommonLibs/Timeval.h | ||||
|     CommonLibs/TimevalTest.cpp | ||||
|     CommonLibs/Vector.h | ||||
|     CommonLibs/VectorTest.cpp | ||||
|     Control/CallControl.cpp | ||||
|     Control/ControlCommon.cpp | ||||
|     Control/ControlCommon.h | ||||
|     Control/FACCHDispatch.cpp | ||||
|     Control/MobilityManagement.cpp | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     Control/SDCCHDispatch.cpp | ||||
|     GSM/GSM610Tables.cpp | ||||
|     GSM/GSM610Tables.h | ||||
|     GSM/GSMCommon.cpp | ||||
| @@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com: | ||||
|     GSM/GSMTransfer.cpp | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
|     SIP/SIPEngine.h | ||||
|     SIP/SIPInterface.h | ||||
|     SMS/SMSMessages.cpp | ||||
|     SMS/SMSMessages.h | ||||
|     SMS/SMSTransfer.cpp | ||||
|     SMS/SMSTransfer.h | ||||
|     TRXManager/TRXManager.cpp | ||||
|     Transceiver/Complex.h | ||||
|     apps/OpenBTS900.cpp | ||||
|     apps/OpenBTS850.cpp | ||||
|     apps/OpenBTS25c3.cpp | ||||
|     tests/AGCHTest.cpp | ||||
|     tests/BeaconTest.cpp | ||||
|     tests/CallTest.cpp | ||||
|     tests/CallTest2.cpp | ||||
|     tests/LAPDmTest.cpp | ||||
|     tests/LoopbackTest.cpp | ||||
|     tests/RegistrationTest.cpp | ||||
|     tests/TRXSimulator.cpp | ||||
|     tests/CommonLibs/BitVectorTest.cpp | ||||
|     tests/CommonLibs/InterthreadTest.cpp | ||||
|     tests/CommonLibs/SocketsTest.cpp | ||||
|     tests/CommonLibs/TimevalTest.cpp | ||||
|     tests/CommonLibs/VectorTest.cpp | ||||
|  | ||||
| Harvind S. Samra, hssamra@kestrelsp.com: | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
| @@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com: | ||||
|     Transceiver/testRadio.cpp | ||||
|  | ||||
| Raffi Sevlian, raffisev@gmail.com: | ||||
|     Control/CallControl.cpp | ||||
|     Control/ControlCommon.cpp | ||||
|     Control/ControlCommon.h | ||||
|     Control/FACCHDispatch.cpp | ||||
|     Control/MobilityManagement.cpp | ||||
|     Control/PagerTest.cpp | ||||
|     Control/RadioResource.cpp | ||||
|     GSM/GSMCommon.h | ||||
|     GSM/GSMConfig.h | ||||
|     GSM/GSML1FEC.h | ||||
| @@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com: | ||||
|     GSM/GSMSAPMux.h | ||||
|     GSM/GSMTransfer.h | ||||
|     LICENSEBLOCK | ||||
|     SIP/SIPEngine.cpp | ||||
|     SIP/SIPInterface.cpp | ||||
|     SIP/SIPInterface.h | ||||
|     SIP/SIPMessage.cpp | ||||
|     SIP/SIPMessage.h | ||||
|     SIP/SIPUtility.cpp | ||||
|     SIP/SIPUtility.h | ||||
|     SMS/CMMessage.cpp | ||||
|     SMS/CMMessage.h | ||||
|     SMS/CMProcessor.cpp | ||||
|     SMS/CMProcessor.h | ||||
|     SMS/CMTest.cpp | ||||
|     SMS/RLMessage.cpp | ||||
|     SMS/RLMessage.h | ||||
|     SMS/RLProcessor.cpp | ||||
|     SMS/RLProcessor.h | ||||
|     SMS/SMSMessages.cpp | ||||
|     SMS/SMSMessages.h | ||||
|     SMS/SMSProcessors.cpp | ||||
|     SMS/SMSProcessors.h | ||||
|     SMS/SMSTransfer.cpp | ||||
|     SMS/SMSTransfer.h | ||||
|     SMS/TLMessage.cpp | ||||
|     SMS/TLMessage.h | ||||
|     SMS/TLProcessor.cpp | ||||
|     SMS/TLProcessor.h | ||||
|     TRXManager/TRXManager.h | ||||
|  | ||||
| Alon Levy, alonlevy1@gmail.com | ||||
|     RRLPMessages.cpp | ||||
|     RRLPMessages.h | ||||
|     RRLPTest.cpp | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								COPYING
									
									
									
									
									
								
							| @@ -673,16 +673,16 @@ on the AGPLv3 text. | ||||
| ========================================================================= | ||||
|  | ||||
|  | ||||
| ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS | ||||
| ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX | ||||
|  | ||||
|  | ||||
| Permissive Terms Supplementing the License | ||||
|  | ||||
| 1. Remote Interaction Through IP Networks. | ||||
|  | ||||
| OpenBTS includes an implementation of the GSM network cellular air interface, | ||||
| OsmoTRX is an implementation of the GSM network cellular air interface, | ||||
| as well as other interfaces to IP networks.  The interaction of cellular | ||||
| handsets with the OpenBTS software is considered "remote network interaction" | ||||
| handsets with the OsmoTRX software is considered "remote network interaction" | ||||
| for the purposes of the Affero General Public License and cellular users are | ||||
| subject to the source code access requirements of Section 13 of AGPLv3 ("Remote | ||||
| Network Interaction; Use with the GNU General Public License"). | ||||
| @@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13 | ||||
| is an additional permission granted to you.   | ||||
|  | ||||
|  | ||||
| Non-Permissive Terms Supplementing The License | ||||
|  | ||||
| 1. Trademarks. | ||||
|  | ||||
| "OpenBTS" is a trademark of Range Networks, Inc., registered with | ||||
| the US Patent and Trademark Office.  Your use of OpenBTS software under a GPL | ||||
| license does not include the right to use the OpenBTS trademark in commerce. | ||||
| This additional non-permissive term is consistent with Section 7 of the AGPLv3 | ||||
| license. | ||||
|  | ||||
|  | ||||
| END OF ADDITIONAL TERMS | ||||
|  | ||||
|  | ||||
| @@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS | ||||
| How to comply with Section 13 of the AGPLv3 license. | ||||
|  | ||||
| The recommended method for compliance with Section 13 of the AGPLv3 license is | ||||
| to deliver a text message to each handset that attaches to the OpenBTS cellular | ||||
| network.  At a minimum, that text message should include the string "OpenBTS | ||||
| AGPLv3" and a URL that can be used to access the OpenBTS source code.  This | ||||
| to deliver a text message to each handset that attaches to the cellular | ||||
| network which uses OsmoTRX.  At a minimum, that text message should include the string | ||||
| "OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code.  This | ||||
| message need not be delivered to handsets that are denied registration with the | ||||
| network, since those handsets have been denied service. | ||||
|  | ||||
| In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message" | ||||
| feature.  See the OpenBTS.config.example file for more information on the use of | ||||
| this feature for AGPLv3 compliance. | ||||
|  | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #include <iostream> | ||||
| #include <stdio.h> | ||||
| #include <sstream> | ||||
| #include <math.h> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| @@ -199,49 +200,6 @@ 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; | ||||
| @@ -287,148 +245,12 @@ 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]=0.0F; | ||||
| 		else mStart[i]=-1.0F; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -438,102 +260,20 @@ BitVector SoftVector::sliced() const | ||||
| 	size_t sz = size(); | ||||
| 	BitVector newSig(sz); | ||||
| 	for (size_t i=0; i<sz; i++) { | ||||
| 		if (mStart[i]>0.5F) newSig[i]=1; | ||||
| 		if (mStart[i]>0.0F) 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 bit = vec[i]; | ||||
| 		float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5)); | ||||
| 		float energy = fabsf(vec[i]); | ||||
| 		if (energy < low) low = energy; | ||||
| 		avg += energy/len; | ||||
| 	} | ||||
| @@ -545,8 +285,12 @@ 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.25) os << "0"; | ||||
| 		else if (sv[i]>0.75) os << "1"; | ||||
| 		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 << "'"; | ||||
| 		else os << "-"; | ||||
| 	} | ||||
| 	return os; | ||||
|   | ||||
| @@ -30,201 +30,6 @@ | ||||
| #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> { | ||||
|  | ||||
|  | ||||
| @@ -282,16 +87,6 @@ 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(); | ||||
| @@ -427,23 +222,20 @@ class SoftVector: public Vector<float> { | ||||
| 	const SoftVector tail(size_t start) const { return segment(start,size()-start); } | ||||
| 	//@} | ||||
|  | ||||
| 	/** 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 | ||||
| 	// 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 | ||||
| 	// 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.5F); } | ||||
| 	void unknown() { fill(0.0F); } | ||||
|  | ||||
| 	/** 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.5F; | ||||
| 		return (*dp)>0.0F; | ||||
| 	} | ||||
|  | ||||
| 	/** Slice the whole signal into bits. */ | ||||
|   | ||||
| @@ -1,88 +0,0 @@ | ||||
| /* | ||||
| * 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; | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,422 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * Copyright 2011, 2012 Range Networks, Inc. | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	This program is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU Affero General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
|  | ||||
| 	This program is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU Affero General Public License for more details. | ||||
|  | ||||
| 	You should have received a copy of the GNU Affero General Public License | ||||
| 	along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef CONFIGURATION_H | ||||
| #define CONFIGURATION_H | ||||
|  | ||||
|  | ||||
| #include "sqlite3util.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <regex.h> | ||||
|  | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
| #include <iostream> | ||||
|  | ||||
| #include <Threads.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
|  | ||||
| /** A class for configuration file errors. */ | ||||
| class ConfigurationTableError {}; | ||||
| extern char gCmdName[];	// Gotta be global, gotta be char*, gotta love it. | ||||
|  | ||||
| /** An exception thrown when a given config key isn't found. */ | ||||
| class ConfigurationTableKeyNotFound : public ConfigurationTableError { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mKey; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationTableKeyNotFound(const std::string& wKey) | ||||
| 		:mKey(wKey) | ||||
| 	{ } | ||||
|  | ||||
| 	const std::string& key() const { return mKey; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| class ConfigurationRecord { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mValue; | ||||
| 	long mNumber; | ||||
| 	bool mDefined; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationRecord(bool wDefined=true): | ||||
| 		mDefined(wDefined) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationRecord(const std::string& wValue): | ||||
| 		mValue(wValue), | ||||
| 		mNumber(strtol(wValue.c_str(),NULL,0)), | ||||
| 		mDefined(true) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationRecord(const char* wValue): | ||||
| 		mValue(std::string(wValue)), | ||||
| 		mNumber(strtol(wValue,NULL,0)), | ||||
| 		mDefined(true) | ||||
| 	{ } | ||||
|  | ||||
|  | ||||
| 	const std::string& value() const { return mValue; } | ||||
| 	long number() const { return mNumber; } | ||||
| 	bool defined() const { return mDefined; } | ||||
|  | ||||
| 	float floatNumber() const; | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** A string class that uses a hash function for comparison. */ | ||||
| class HashString : public std::string { | ||||
|  | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	uint64_t mHash; | ||||
|  | ||||
| 	void computeHash(); | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	HashString(const char* src) | ||||
| 		:std::string(src) | ||||
| 	{ | ||||
| 		computeHash(); | ||||
| 	} | ||||
|  | ||||
| 	HashString(const std::string& src) | ||||
| 		:std::string(src) | ||||
| 	{ | ||||
| 		computeHash(); | ||||
| 	} | ||||
|  | ||||
| 	HashString() | ||||
| 	{ | ||||
| 		mHash=0; | ||||
| 	} | ||||
|  | ||||
| 	HashString& operator=(std::string& src) | ||||
| 	{ | ||||
| 		std::string::operator=(src); | ||||
| 		computeHash(); | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	HashString& operator=(const char* src) | ||||
| 	{ | ||||
| 		std::string::operator=(src); | ||||
| 		computeHash(); | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	bool operator==(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash==other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	bool operator<(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash<other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	bool operator>(const HashString& other) | ||||
| 	{ | ||||
| 		return mHash<other.mHash; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t hash() const { return mHash; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap; | ||||
| typedef std::map<HashString, ConfigurationRecord> ConfigurationMap; | ||||
| class ConfigurationKey; | ||||
| typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap; | ||||
|  | ||||
| /** | ||||
| 	A class for maintaining a configuration key-value table, | ||||
| 	based on sqlite3 and a local map-based cache. | ||||
| 	Thread-safe, too. | ||||
| */ | ||||
| class ConfigurationTable { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	sqlite3* mDB;				///< database connection | ||||
| 	ConfigurationMap mCache;	///< cache of recently access configuration values | ||||
| 	mutable Mutex mLock;		///< control for multithreaded access to the cache | ||||
| 	std::vector<std::string> (*mCrossCheck)(const std::string&);	///< cross check callback pointer | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic | ||||
|  | ||||
| 	ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap()); | ||||
|  | ||||
| 	/** Generate an up-to-date example sql file for new installs. */ | ||||
| 	std::string getDefaultSQL(const std::string& program, const std::string& version); | ||||
|  | ||||
| 	/** Generate an up-to-date TeX snippet. */ | ||||
| 	std::string getTeX(const std::string& program, const std::string& version); | ||||
|  | ||||
| 	/** Return true if the key is used in the table.  */ | ||||
| 	bool defines(const std::string& key); | ||||
|  | ||||
| 	/** Return true if the application's schema knows about this key. */ | ||||
| 	bool keyDefinedInSchema(const std::string& name); | ||||
|  | ||||
| 	/** Return true if the provided value validates correctly against the defined schema. */ | ||||
| 	bool isValidValue(const std::string& name, const std::string& val); | ||||
|  | ||||
| 	/** Return true if the provided value validates correctly against the defined schema. */ | ||||
| 	bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); } | ||||
|  | ||||
| 	/** Return a map of all similar keys in the defined schema. */ | ||||
| 	ConfigurationKeyMap getSimilarKeys(const std::string& snippet); | ||||
|  | ||||
| 	/** Return true if this key is identified as static. */ | ||||
| 	bool isStatic(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a string parameter from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	std::string getStr(const std::string& key); | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 		Get a boolean from the table. | ||||
| 		Return false if NULL or 0, true otherwise. | ||||
| 	*/ | ||||
| 	bool getBool(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a numeric parameter from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	long getNum(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a vector of strings from the table. | ||||
| 	*/ | ||||
| 	std::vector<std::string> getVectorOfStrings(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a float from the table. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 	*/ | ||||
| 	float getFloat(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Get a numeric vector from the table. | ||||
| 	*/ | ||||
| 	std::vector<unsigned> getVector(const std::string& key); | ||||
|  | ||||
| 	/** Get length of a vector */ | ||||
| 	unsigned getVectorLength(const std::string &key)  | ||||
| 		{ return getVector(key).size(); } | ||||
|  | ||||
| 	/** Set or change a value in the table.  */ | ||||
| 	bool set(const std::string& key, const std::string& value); | ||||
|  | ||||
| 	/** Set or change a value in the table.  */ | ||||
| 	bool set(const std::string& key, long value); | ||||
|  | ||||
| 	/** Create an entry in the table, no value though. */ | ||||
| 	bool set(const std::string& key); | ||||
|  | ||||
| 	/** | ||||
| 		Remove an entry from the table. | ||||
| 		Will not alter required values. | ||||
| 		@param key The key of the item to be removed. | ||||
| 		@return true if anything was actually removed. | ||||
| 	*/ | ||||
| 	bool remove(const std::string& key); | ||||
|  | ||||
| 	/** Search the table, dumping to a stream. */ | ||||
| 	void find(const std::string& pattern, std::ostream&) const; | ||||
|  | ||||
| 	/** Return all key/value pairs stored in the ConfigurationTable */ | ||||
| 	ConfigurationRecordMap getAllPairs() const; | ||||
|  | ||||
| 	/** Define the callback to purge the cache whenever the database changes. */ | ||||
| 	void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64)); | ||||
|  | ||||
| 	/** Define the callback for cross checking. */ | ||||
| 	void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&)); | ||||
|  | ||||
| 	/** Execute the application specific value cross checking logic. */ | ||||
| 	std::vector<std::string> crossCheck(const std::string& key); | ||||
|  | ||||
| 	/** purege cache if it exceeds a certain age */ | ||||
| 	void checkCacheAge(); | ||||
|  | ||||
| 	/** Delete all records from the cache. */ | ||||
| 	void purge(); | ||||
|  | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	/** | ||||
| 		Attempt to lookup a record, cache if needed. | ||||
| 		Throw ConfigurationTableKeyNotFound if not found. | ||||
| 		Caller should hold mLock because the returned reference points into the cache. | ||||
| 	*/ | ||||
| 	const ConfigurationRecord& lookup(const std::string& key); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| typedef std::map<HashString, std::string> HashStringMap; | ||||
|  | ||||
| class SimpleKeyValue { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	HashStringMap mMap; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Take a C string "A=B" and set map["A"]="B". */ | ||||
| 	void addItem(const char*); | ||||
|  | ||||
| 	/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */ | ||||
| 	void addItems(const char*s); | ||||
|  | ||||
| 	/** Return a reference to the string at map["key"]. */ | ||||
| 	const char* get(const char*) const; | ||||
| }; | ||||
|  | ||||
|  | ||||
| class ConfigurationKey { | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	enum VisibilityLevel | ||||
| 	{ | ||||
| 		CUSTOMER, | ||||
| 		CUSTOMERSITE, | ||||
| 		CUSTOMERTUNE, | ||||
| 		CUSTOMERWARN, | ||||
| 		DEVELOPER, | ||||
| 		FACTORY | ||||
| 	}; | ||||
|  | ||||
| 	enum Type | ||||
| 	{ | ||||
| 		BOOLEAN, | ||||
| 		CHOICE_OPT, | ||||
| 		CHOICE, | ||||
| 		CIDR_OPT, | ||||
| 		CIDR, | ||||
| 		FILEPATH_OPT, | ||||
| 		FILEPATH, | ||||
| 		IPADDRESS_OPT, | ||||
| 		IPADDRESS, | ||||
| 		IPANDPORT, | ||||
| 		MIPADDRESS_OPT, | ||||
| 		MIPADDRESS, | ||||
| 		PORT_OPT, | ||||
| 		PORT, | ||||
| 		REGEX_OPT, | ||||
| 		REGEX, | ||||
| 		STRING_OPT, | ||||
| 		STRING, | ||||
| 		VALRANGE | ||||
| 	}; | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	std::string mName; | ||||
| 	std::string mDefaultValue; | ||||
| 	std::string mUnits; | ||||
| 	VisibilityLevel mVisibility; | ||||
| 	Type mType; | ||||
| 	std::string mValidValues; | ||||
| 	bool mIsStatic; | ||||
| 	std::string mDescription; | ||||
|  | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription): | ||||
| 		mName(wName), | ||||
| 		mDefaultValue(wDefaultValue), | ||||
| 		mUnits(wUnits), | ||||
| 		mVisibility(wVisibility), | ||||
| 		mType(wType), | ||||
| 		mValidValues(wValidValues), | ||||
| 		mIsStatic(wIsStatic), | ||||
| 		mDescription(wDescription) | ||||
| 	{ } | ||||
|  | ||||
| 	ConfigurationKey() | ||||
| 	{ } | ||||
|  | ||||
| 	const std::string& getName() const { return mName; } | ||||
| 	const std::string& getDefaultValue() const { return mDefaultValue; } | ||||
| 	void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; } | ||||
| 	void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); } | ||||
| 	const std::string& getUnits() const { return mUnits; } | ||||
| 	const VisibilityLevel& getVisibility() const { return mVisibility; } | ||||
| 	const Type& getType() const { return mType; } | ||||
| 	const std::string& getValidValues() const { return mValidValues; } | ||||
| 	bool isStatic() const { return mIsStatic; } | ||||
| 	const std::string& getDescription() const { return mDescription; } | ||||
|  | ||||
| 	static bool isValidIP(const std::string& ip); | ||||
| 	static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping); | ||||
| 	template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger); | ||||
| 	static const std::string visibilityLevelToString(const VisibilityLevel& visibility); | ||||
| 	static const std::string typeToString(const ConfigurationKey::Type& type); | ||||
| 	static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os); | ||||
| 	static void printDescription(const ConfigurationKey &key, std::ostream& os); | ||||
| 	static const std::string getARFCNsString(); | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
| @@ -1,149 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| * See the COPYING file in the main directory for details. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
| 	This program is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU Affero General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
|  | ||||
| 	This program is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU Affero General Public License for more details. | ||||
|  | ||||
| 	You should have received a copy of the GNU Affero General Public License | ||||
| 	along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
| #include "Configuration.h" | ||||
| #include <iostream> | ||||
| #include <string> | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| ConfigurationKeyMap getConfigurationKeys(); | ||||
| ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys()); | ||||
|  | ||||
| void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) | ||||
| { | ||||
| 	//cout << "update hook" << endl; | ||||
| 	gConfig.purge(); | ||||
| } | ||||
|  | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
| 	gConfig.setUpdateHook(purgeConfig); | ||||
|  | ||||
| 	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
									
									
									
									
									
								
							
							
						
						
									
										210
									
								
								CommonLibs/F16.h
									
									
									
									
									
								
							| @@ -1,210 +0,0 @@ | ||||
| /* | ||||
| * 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 | ||||
|  | ||||
| @@ -1,55 +0,0 @@ | ||||
| /* | ||||
| * 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; | ||||
| } | ||||
| @@ -29,6 +29,25 @@ | ||||
| #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. | ||||
|   | ||||
| @@ -70,6 +70,7 @@ 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. | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * Copyright 2011, 2012 Range Networks, Inc. | ||||
| * Copyright (C) 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| @@ -30,276 +28,37 @@ | ||||
| #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; | ||||
|  | ||||
| // Switches to enable/disable logging targets | ||||
| // MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY | ||||
| bool gLogToConsole = true; | ||||
| bool gLogToSyslog = false; | ||||
| FILE *gLogToFile = NULL; | ||||
| Mutex gLogToLock; | ||||
|  | ||||
|  | ||||
| // Reference to a global config table, used all over the system. | ||||
| extern ConfigurationTable gConfig; | ||||
|  | ||||
|  | ||||
| /**@ The global alarms table. */ | ||||
| //@{ | ||||
| Mutex           alarmsLock; | ||||
| list<string>    alarmsList; | ||||
| void            addAlarm(const string&); | ||||
| //@} | ||||
|  | ||||
|  | ||||
|  | ||||
| // (pat) If Log messages are printed before the classes in this module are inited | ||||
| // (which happens when static classes have constructors that do work) | ||||
| // the OpenBTS just crashes. | ||||
| // Prevent that by setting sLoggerInited to true when this module is inited. | ||||
| static bool sLoggerInited = 0; | ||||
| static struct CheckLoggerInitStatus { | ||||
| 	CheckLoggerInitStatus() { sLoggerInited = 1; } | ||||
| } sCheckloggerInitStatus; | ||||
|  | ||||
|  | ||||
|  | ||||
| /** Names of the logging levels. */ | ||||
| const char *levelNames[] = { | ||||
| 	"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" | ||||
| }; | ||||
| int numLevels = 8; | ||||
|  | ||||
|  | ||||
| int levelStringToInt(const string& name) | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
| { | ||||
| 	// 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; | ||||
| 	return os << ss.str(); | ||||
| } | ||||
|  | ||||
| /** 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() | ||||
| { | ||||
| 	if (mDummyInit) return; | ||||
| 	// Anything at or above LOG_CRIT is an "alarm". | ||||
| 	// Save alarms in the local list and echo them to stderr. | ||||
| 	if (mPriority <= LOG_ERR) { | ||||
| 		if (sLoggerInited) addAlarm(mStream.str().c_str()); | ||||
| 		cerr << mStream.str() << endl; | ||||
| 	} | ||||
| 	// Current logging level was already checked by the macro. So just log. | ||||
| 	// Log to syslog | ||||
| 	if (gLogToSyslog) { | ||||
| 		syslog(mPriority, "%s", mStream.str().c_str()); | ||||
| 	} | ||||
| 	// Log to file and console | ||||
| 	if (gLogToConsole||gLogToFile) { | ||||
| 		int mlen = mStream.str().size(); | ||||
| 		int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 		ScopedLock lock(gLogToLock); | ||||
| 		if (gLogToConsole) { | ||||
| 			// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, | ||||
| 			// so just use std::cout. | ||||
| 			std::cout << mStream.str(); | ||||
| 			if (neednl) std::cout<<"\n"; | ||||
| 		} | ||||
| 		if (gLogToFile) { | ||||
| 			fputs(mStream.str().c_str(),gLogToFile); | ||||
| 			if (neednl) {fputc('\n',gLogToFile);} | ||||
| 			fflush(gLogToFile); | ||||
| 		} | ||||
| 	} | ||||
| 	int old_state; | ||||
| 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); | ||||
| 	int mlen = mStream.str().size(); | ||||
| 	int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 	const char *fmt = neednl ? "%s\n" : "%s"; | ||||
| 	ScopedLock lock(gLogToLock); | ||||
| 	// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, | ||||
| 	// so just use std::cout. | ||||
| 	LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str()); | ||||
| 	pthread_setcancelstate(old_state, NULL); | ||||
| } | ||||
|  | ||||
|  | ||||
| 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); | ||||
| 	} | ||||
|  | ||||
| 	// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file: | ||||
| 	string str = gConfig.getStr("Log.File"); | ||||
| 	if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) { | ||||
| 		const char *fn = str.c_str(); | ||||
| 		if (fn && *fn && strlen(fn)>3) {	// strlen because a garbage char is getting in sometimes. | ||||
| 			gLogToFile = fopen(fn,"w"); // New log file each time we start. | ||||
| 			if (gLogToFile) { | ||||
| 				time_t now; | ||||
| 				time(&now); | ||||
| 				fprintf(gLogToFile,"Starting at %s",ctime(&now)); | ||||
| 				fflush(gLogToFile); | ||||
| 				std::cout << "Logging to file: " << fn << "\n"; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Open the log connection. | ||||
| 	openlog(name,0,facility); | ||||
| } | ||||
|  | ||||
|  | ||||
| void gLogEarly(int level, const char *fmt, ...) | ||||
| { | ||||
| 	va_list args; | ||||
|  | ||||
| 	va_start(args, fmt); | ||||
|  | ||||
| 	if (gLogToSyslog) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vsyslog(level | LOG_USER, fmt, args_copy); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	if (gLogToConsole) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vprintf(fmt, args_copy); | ||||
| 		printf("\n"); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	if (gLogToFile) { | ||||
| 		va_list args_copy; | ||||
| 		va_copy(args_copy, args); | ||||
| 		vfprintf(gLogToFile, fmt, args_copy); | ||||
| 		fprintf(gLogToFile, "\n"); | ||||
| 		va_end(args_copy); | ||||
| 	} | ||||
|  | ||||
| 	va_end(args); | ||||
| } | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
|   | ||||
| @@ -23,72 +23,41 @@ | ||||
|  | ||||
| */ | ||||
|  | ||||
| // (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> | ||||
|  | ||||
| #define _LOG(level) \ | ||||
| 	Log(LOG_##level).get() << pthread_self() \ | ||||
| 	<< timestr() << " " __FILE__  ":"  << __LINE__ << ":" << __FUNCTION__ << ": " | ||||
| extern "C" { | ||||
| #include <osmocom/core/logging.h> | ||||
| #include "debug.h" | ||||
| } | ||||
|  | ||||
| #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) | ||||
| /* 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 | ||||
| #endif | ||||
|  | ||||
| // pat: And for your edification here are the 'levels' as defined in syslog.h: | ||||
| // LOG_EMERG   0  system is unusable | ||||
| // LOG_ALERT   1  action must be taken immediately | ||||
| // LOG_CRIT    2  critical conditions | ||||
| // LOG_ERR     3  error conditions | ||||
| // LOG_WARNING 4  warning conditions | ||||
| // LOG_NOTICE  5  normal, but significant, condition | ||||
| // LOG_INFO    6  informational message | ||||
| // LOG_DEBUG   7  debug-level message | ||||
| #define LOG(level) \ | ||||
| 	Log(DMAIN, 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 LOGC(category, level) \ | ||||
| 	Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
|  | ||||
| #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" | ||||
| #define LOGLV(category, level) \ | ||||
| 	Log(category, level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
| /** | ||||
| 	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. | ||||
| */ | ||||
| @@ -98,42 +67,27 @@ class Log { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	std::ostringstream mStream;		///< This is where we buffer up the log entry. | ||||
| 	int mPriority;					///< Priority of current report. | ||||
| 	bool mDummyInit; | ||||
| 	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. | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Log(int wPriority) | ||||
| 		:mPriority(wPriority), mDummyInit(false) | ||||
| 	Log(int wCategory, int wPriority, const char* filename, int line) | ||||
| 		: mCategory(wCategory), mPriority(wPriority), | ||||
| 		  filename(filename), line(line) | ||||
| 	{ } | ||||
|  | ||||
| 	Log(const char* name, const char* level=NULL, int facility=LOG_USER); | ||||
|  | ||||
| 	// Most of the work is in the destructor. | ||||
| 	/** The destructor actually generates the log entry. */ | ||||
| 	~Log(); | ||||
|  | ||||
| 	std::ostringstream& get(); | ||||
| }; | ||||
| extern bool gLogToConsole;	// Output log messages to stdout | ||||
| extern bool gLogToSyslog;	// Output log messages to syslog | ||||
|  | ||||
|  | ||||
|  | ||||
| std::list<std::string> gGetLoggerAlarms();		///< Get a copy of the recent alarm list. | ||||
|  | ||||
|  | ||||
| /**@ 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,11 +22,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	example.config \ | ||||
| 	README.common | ||||
| AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
|  | ||||
| noinst_LTLIBRARIES = libcommon.la | ||||
|  | ||||
| @@ -36,78 +33,24 @@ libcommon_la_SOURCES = \ | ||||
| 	Sockets.cpp \ | ||||
| 	Threads.cpp \ | ||||
| 	Timeval.cpp \ | ||||
| 	Reporting.cpp \ | ||||
| 	Logger.cpp \ | ||||
| 	Configuration.cpp \ | ||||
| 	sqlite3util.cpp \ | ||||
| 	URLEncode.cpp \ | ||||
| 	Utils.cpp | ||||
|  | ||||
| noinst_PROGRAMS = \ | ||||
| 	BitVectorTest \ | ||||
| 	InterthreadTest \ | ||||
| 	SocketsTest \ | ||||
| 	TimevalTest \ | ||||
| 	RegexpTest \ | ||||
| 	VectorTest \ | ||||
| 	ConfigurationTest \ | ||||
| 	LogTest \ | ||||
| 	URLEncodeTest \ | ||||
| 	F16Test | ||||
|  | ||||
| #	ReportingTest  | ||||
| 	Utils.cpp \ | ||||
| 	trx_vty.c \ | ||||
| 	debug.c | ||||
| libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	BitVector.h \ | ||||
| 	PRBS.h \ | ||||
| 	Interthread.h \ | ||||
| 	LinkedLists.h \ | ||||
| 	Sockets.h \ | ||||
| 	Threads.h \ | ||||
| 	Timeval.h \ | ||||
| 	Regexp.h \ | ||||
| 	Vector.h \ | ||||
| 	Configuration.h \ | ||||
| 	Reporting.h \ | ||||
| 	F16.h \ | ||||
| 	URLEncode.h \ | ||||
| 	Utils.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 | ||||
|  | ||||
|  | ||||
| 	Utils.h \ | ||||
| 	trx_vty.h \ | ||||
| 	debug.h \ | ||||
| 	osmo_signal.h \ | ||||
| 	config_defs.h | ||||
|   | ||||
| @@ -1,111 +0,0 @@ | ||||
| /* | ||||
| * 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 | ||||
							
								
								
									
										110
									
								
								CommonLibs/PRBS.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								CommonLibs/PRBS.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| /* | ||||
|  * Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #ifndef PRBS_H | ||||
| #define PRBS_H | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| /** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */ | ||||
| class PRBS { | ||||
| public: | ||||
|  | ||||
|   PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01) | ||||
|     : mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen) | ||||
|   { assert(wLen<=64); } | ||||
|  | ||||
|   /**@name Accessors */ | ||||
|   //@{ | ||||
|   uint64_t coeff() const { return mCoeff; } | ||||
|   uint64_t state() const { return mState; } | ||||
|   void state(uint64_t state) { mState = state & mask(); } | ||||
|   unsigned size() const { return mLen; } | ||||
|   //@} | ||||
|  | ||||
|   /** | ||||
|     Calculate one bit of a PRBS | ||||
|   */ | ||||
|   unsigned generateBit() | ||||
|   { | ||||
|     const unsigned result = mState & 0x01; | ||||
|     processBit(result); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     Update the generator state by one bit. | ||||
|     If you want to synchronize your PRBS to a known state, call this function | ||||
|     size() times passing your PRBS to it bit by bit. | ||||
|   */ | ||||
|   void processBit(unsigned inBit) | ||||
|   { | ||||
|     mState >>= 1; | ||||
|     if (inBit) mState ^= mCoeff; | ||||
|   } | ||||
|  | ||||
|   /** Return true when PRBS is wrapping through initial state */ | ||||
|   bool isFinished() const { return mStartState == mState; } | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   uint64_t mCoeff;      ///< polynomial coefficients. LSB is zero exponent. | ||||
|   uint64_t mStartState; ///< initial shift register state. | ||||
|   uint64_t mState;      ///< shift register state. | ||||
|   unsigned mLen;        ///< number of bits used in shift register | ||||
|  | ||||
|   /** Return mask for the state register */ | ||||
|   uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); } | ||||
|  | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 9-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^9 + x^5 + 1 | ||||
| */ | ||||
| class PRBS9 : public PRBS { | ||||
|   public: | ||||
|   PRBS9(uint64_t wState = 0x01) | ||||
|   : PRBS(9, 0x0110, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 15-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^15 + x^14 + 1 | ||||
| */ | ||||
| class PRBS15 : public PRBS { | ||||
| public: | ||||
|   PRBS15(uint64_t wState = 0x01) | ||||
|   : PRBS(15, 0x6000, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   A standard 64-bit based pseudorandom binary sequence (PRBS) generator. | ||||
|   Polynomial: x^64 + x^63 + x^61 + x^60 + 1 | ||||
| */ | ||||
| class PRBS64 : public PRBS { | ||||
| public: | ||||
|   PRBS64(uint64_t wState = 0x01) | ||||
|   : PRBS(64, 0xD800000000000000ULL, wState) | ||||
|   {} | ||||
| }; | ||||
|  | ||||
| #endif // PRBS_H | ||||
| @@ -1,64 +0,0 @@ | ||||
| /* | ||||
| * 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,145 +0,0 @@ | ||||
| /**@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); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,86 +0,0 @@ | ||||
| /**@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 | ||||
| @@ -1,136 +0,0 @@ | ||||
| /* | ||||
| * 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 | ||||
| @@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout) | ||||
|  | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort) | ||||
| UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| 	open(wSrcPort, wSrcIP); | ||||
| } | ||||
|  | ||||
|  | ||||
| UDPSocket::UDPSocket(unsigned short wSrcPort, | ||||
|           	 const char * wDestIP, unsigned short wDestPort ) | ||||
| UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort, | ||||
| 		     const char *wDestIP, unsigned short wDestPort) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	open(wSrcPort); | ||||
| 	open(wSrcPort, wSrcIP); | ||||
| 	destination(wDestPort, wDestIP); | ||||
| } | ||||
|  | ||||
| @@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) | ||||
| } | ||||
|  | ||||
|  | ||||
| void UDPSocket::open(unsigned short localPort) | ||||
| void UDPSocket::open(unsigned short localPort, const char *wlocalIP) | ||||
| { | ||||
| 	// create | ||||
| 	mSocketFD = socket(AF_INET,SOCK_DGRAM,0); | ||||
| @@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort) | ||||
| 	size_t length = sizeof(address); | ||||
| 	bzero(&address,length); | ||||
| 	address.sin_family = AF_INET; | ||||
| 	address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||||
| 	address.sin_addr.s_addr = inet_addr(wlocalIP); | ||||
| 	address.sin_port = htons(localPort); | ||||
| 	if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { | ||||
| 		perror("bind() failed"); | ||||
| @@ -284,50 +284,4 @@ unsigned short UDPSocket::port() const | ||||
| 	return ntohs(name.sin_port); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| UDDSocket::UDDSocket(const char* localPath, const char* remotePath) | ||||
| 	:DatagramSocket() | ||||
| { | ||||
| 	if (localPath!=NULL) open(localPath); | ||||
| 	if (remotePath!=NULL) destination(remotePath); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void UDDSocket::open(const char* localPath) | ||||
| { | ||||
| 	// create | ||||
| 	mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0); | ||||
| 	if (mSocketFD<0) { | ||||
| 		perror("socket() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
|  | ||||
| 	// bind | ||||
| 	struct sockaddr_un address; | ||||
| 	size_t length = sizeof(address); | ||||
| 	bzero(&address,length); | ||||
| 	address.sun_family = AF_UNIX; | ||||
| 	strcpy(address.sun_path,localPath); | ||||
| 	unlink(localPath); | ||||
| 	if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { | ||||
| 		perror("bind() failed"); | ||||
| 		throw SocketError(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void UDDSocket::destination(const char* remotePath) | ||||
| { | ||||
| 	struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination; | ||||
| 	strcpy(unAddr->sun_path,remotePath); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| // vim:ts=4:sw=4 | ||||
|   | ||||
| @@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket { | ||||
| public: | ||||
|  | ||||
| 	/** Open a USP socket with an OS-assigned port and no default destination. */ | ||||
| 	UDPSocket( unsigned short localPort=0); | ||||
| 	UDPSocket(const char *localIP, unsigned short localPort); | ||||
|  | ||||
| 	/** Given a full specification, open the socket and set the dest address. */ | ||||
| 	UDPSocket( 	unsigned short localPort,  | ||||
| 			const char * remoteIP, unsigned short remotePort); | ||||
| 	UDPSocket(const char *localIP, unsigned short localPort, | ||||
| 		  const char *remoteIP, unsigned short remotePort); | ||||
|  | ||||
| 	/** Set the destination port. */ | ||||
| 	void destination( unsigned short wDestPort, const char * wDestIP ); | ||||
| @@ -157,7 +157,7 @@ public: | ||||
| 	unsigned short port() const; | ||||
|  | ||||
| 	/** Open and bind the UDP socket to a local port. */ | ||||
| 	void open(unsigned short localPort=0); | ||||
| 	void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1"); | ||||
|  | ||||
| 	/** Give the return address of the most recently received packet. */ | ||||
| 	const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; } | ||||
| @@ -166,26 +166,6 @@ public: | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** Unix Domain Datagram Socket */ | ||||
| class UDDSocket : public DatagramSocket { | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	UDDSocket(const char* localPath=NULL, const char* remotePath=NULL); | ||||
|  | ||||
| 	void destination(const char* remotePath); | ||||
|  | ||||
| 	void open(const char* localPath); | ||||
|  | ||||
| 	/** Give the return address of the most recently received packet. */ | ||||
| 	const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; } | ||||
|  | ||||
| 	size_t addressSize() const { return sizeof(struct sockaddr_un); } | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -24,11 +24,17 @@ | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #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; | ||||
| @@ -102,6 +108,19 @@ 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::start(void *(*task)(void*), void *arg) | ||||
| { | ||||
|   | ||||
| @@ -141,6 +141,8 @@ class Signal { | ||||
| #define START_THREAD(thread,function,argument) \ | ||||
| 	thread.start((void *(*)(void*))function, (void*)argument); | ||||
|  | ||||
| void set_selfthread_name(const char *name); | ||||
|  | ||||
| /** A C++ wrapper for pthread threads.  */ | ||||
| class Thread { | ||||
|  | ||||
|   | ||||
| @@ -27,43 +27,49 @@ | ||||
|  | ||||
| #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; | ||||
| 	mTimeval.tv_usec += msec*1000; | ||||
| 	mTimeval.tv_sec += sec; | ||||
| 	if (mTimeval.tv_usec>1000000) { | ||||
| 		mTimeval.tv_usec -= 1000000; | ||||
| 		mTimeval.tv_sec += 1; | ||||
| 	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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| struct timespec Timeval::timespec() const | ||||
| { | ||||
| 	struct timespec retVal; | ||||
| 	retVal.tv_sec = mTimeval.tv_sec; | ||||
| 	retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec; | ||||
| 	return retVal; | ||||
| 	return mTimespec; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool Timeval::passed() const | ||||
| { | ||||
| 	Timeval nowTime; | ||||
| 	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; | ||||
| 	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; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| double Timeval::seconds() const | ||||
| { | ||||
| 	return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec); | ||||
| 	return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -72,8 +78,8 @@ long Timeval::delta(const Timeval& other) const | ||||
| { | ||||
| 	// 2^31 milliseconds is just over 4 years. | ||||
| 	int32_t deltaS = other.sec() - sec(); | ||||
| 	int32_t deltaUs = other.usec() - usec(); | ||||
| 	return 1000*deltaS + deltaUs/1000; | ||||
| 	int32_t deltaNs = other.nsec() - nsec(); | ||||
| 	return 1000*deltaS + deltaNs/1000000; | ||||
| } | ||||
| 	 | ||||
|  | ||||
| @@ -89,7 +95,7 @@ ostream& operator<<(ostream& os, const Timeval& tv) | ||||
|  | ||||
| ostream& operator<<(ostream& os, const struct timespec& ts) | ||||
| { | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec; | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec/1000; | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -42,12 +42,12 @@ class Timeval { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	struct timeval mTimeval; | ||||
| 	struct timespec mTimespec; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Set the value to gettimeofday. */ | ||||
| 	void now() { gettimeofday(&mTimeval,NULL); } | ||||
| 	/** Set the value to current time. */ | ||||
| 	void now(); | ||||
|  | ||||
| 	/** Set the value to gettimeofday plus an offset. */ | ||||
| 	void future(unsigned ms); | ||||
| @@ -55,16 +55,18 @@ class Timeval { | ||||
| 	//@{ | ||||
| 	Timeval(unsigned sec, unsigned usec) | ||||
| 	{ | ||||
| 		mTimeval.tv_sec = sec; | ||||
| 		mTimeval.tv_usec = usec; | ||||
| 		mTimespec.tv_sec = sec; | ||||
| 		mTimespec.tv_nsec = usec*1000; | ||||
| 	} | ||||
|  | ||||
| 	Timeval(const struct timeval& wTimeval) | ||||
| 		:mTimeval(wTimeval) | ||||
| 	{} | ||||
| 	{ | ||||
| 		mTimespec.tv_sec = wTimeval.tv_sec; | ||||
| 		mTimespec.tv_nsec = wTimeval.tv_sec*1000; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Create a Timeval offset into the future. | ||||
| 		Create a Timespec offset into the future. | ||||
| 		@param offset milliseconds | ||||
| 	*/ | ||||
| 	Timeval(unsigned offset=0) { future(offset); } | ||||
| @@ -76,8 +78,9 @@ class Timeval { | ||||
| 	/** Return total seconds. */ | ||||
| 	double seconds() const; | ||||
|  | ||||
| 	uint32_t sec() const { return mTimeval.tv_sec; } | ||||
| 	uint32_t usec() const { return mTimeval.tv_usec; } | ||||
| 	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; } | ||||
|  | ||||
| 	/** Return differnce from other (other-self), in ms. */ | ||||
| 	long delta(const Timeval& other) const; | ||||
| @@ -88,11 +91,11 @@ class Timeval { | ||||
| 	/** Remaining time in ms. */ | ||||
| 	long remaining() const { return -elapsed(); } | ||||
|  | ||||
| 	/** Return true if the time has passed, as per gettimeofday. */ | ||||
| 	/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */ | ||||
| 	bool passed() const; | ||||
|  | ||||
| 	/** Add a given number of minutes to the time. */ | ||||
| 	void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; } | ||||
| 	void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| /* | ||||
| * 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; | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| /* 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; | ||||
| } | ||||
|  | ||||
| @@ -1,30 +0,0 @@ | ||||
| /* | ||||
| * 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&); | ||||
| @@ -1,17 +0,0 @@ | ||||
|  | ||||
| #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,211 +1,36 @@ | ||||
| /* | ||||
| * 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. | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
|     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> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
|  | ||||
| #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::vector<std::string> comma_delimited_to_vector(const char* opt) | ||||
| { | ||||
| 	memset(mMemNow,0,sizeof(mMemNow)); | ||||
| 	memset(mMemTotal,0,sizeof(mMemTotal)); | ||||
| 	memset(mMemName,0,sizeof(mMemName)); | ||||
| } | ||||
| 	std::string str = std::string(opt); | ||||
| 	std::vector<std::string> result; | ||||
| 	std::stringstream ss(str); | ||||
|  | ||||
| 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"; | ||||
| 	while( ss.good() ) | ||||
| 	{ | ||||
| 	    std::string substr; | ||||
| 	    getline(ss, substr, ','); | ||||
| 	    result.push_back(substr); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| 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,148 +1,24 @@ | ||||
| /* | ||||
| * 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. | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
|     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. | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #ifndef GPRSUTILS_H | ||||
| #define GPRSUTILS_H | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <string.h> | ||||
| #include <math.h>		// for sqrtf | ||||
| #include "Logger.h" | ||||
|  | ||||
|  | ||||
| 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 | ||||
| std::vector<std::string> comma_delimited_to_vector(const char* opt); | ||||
|   | ||||
| @@ -32,11 +32,14 @@ | ||||
| #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. | ||||
| @@ -60,6 +63,8 @@ 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: | ||||
|  | ||||
| @@ -85,13 +90,30 @@ template <class T> class Vector { | ||||
| 	/** Change the size of the Vector, discarding content. */ | ||||
| 	void resize(size_t newSize) | ||||
| 	{ | ||||
| 		if (mData!=NULL) delete[] mData; | ||||
| 		if (mData!=NULL) { | ||||
| 			if (mFreeFunc) | ||||
| 				mFreeFunc(mData); | ||||
| 			else | ||||
| 				delete[] mData; | ||||
| 		} | ||||
| 		if (newSize==0) mData=NULL; | ||||
| 		else mData = new T[newSize]; | ||||
| 		else { | ||||
| 			if (mAllocFunc) | ||||
| 				mData = (T*) mAllocFunc(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); } | ||||
|  | ||||
| @@ -100,7 +122,7 @@ template <class T> class Vector { | ||||
| 	void clone(const Vector<T>& other) | ||||
| 	{ | ||||
| 		resize(other.size()); | ||||
| 		memcpy(mData,other.mStart,other.bytes()); | ||||
| 		other.copyTo(*this); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @@ -109,29 +131,31 @@ template <class T> class Vector { | ||||
| 	//@{ | ||||
|  | ||||
| 	/** Build an empty Vector of a given size. */ | ||||
| 	Vector(size_t wSize=0):mData(NULL) { resize(wSize); } | ||||
| 	Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ resize(wSize); } | ||||
|  | ||||
| 	/** Build a Vector by shifting the data block. */ | ||||
| 	Vector(Vector<T>& other) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) | ||||
| 	/** 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) | ||||
| 	{ other.mData=NULL; } | ||||
|  | ||||
| 	/** Build a Vector by copying another. */ | ||||
| 	Vector(const Vector<T>& other):mData(NULL) { clone(other); } | ||||
| 	Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); } | ||||
|  | ||||
| 	/** Build a Vector with explicit values. */ | ||||
| 	Vector(T* wData, T* wStart, T* wEnd) | ||||
| 		:mData(wData),mStart(wStart),mEnd(wEnd) | ||||
| 	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) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a vector from an existing block, NOT to be deleted upon destruction. */ | ||||
| 	Vector(T* wStart, size_t span) | ||||
| 		:mData(NULL),mStart(wStart),mEnd(wStart+span) | ||||
| 	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) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a Vector by concatenation. */ | ||||
| 	Vector(const Vector<T>& other1, const Vector<T>& other2) | ||||
| 		:mData(NULL) | ||||
| 	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) | ||||
| 	{ | ||||
| 		resize(other1.size()+other2.size()); | ||||
| 		memcpy(mStart, other1.mStart, other1.bytes()); | ||||
| @@ -155,6 +179,8 @@ template <class T> class Vector { | ||||
| 		mData=other.mData; | ||||
| 		mStart=other.mStart; | ||||
| 		mEnd=other.mEnd; | ||||
| 		mAllocFunc=other.mAllocFunc; | ||||
| 		mFreeFunc=other.mFreeFunc; | ||||
| 		other.mData=NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -197,10 +223,15 @@ template <class T> class Vector { | ||||
| 	*/ | ||||
| 	void copyToSegment(Vector<T>& other, size_t start, size_t span) const | ||||
| 	{ | ||||
| 		T* base = other.mStart + start; | ||||
| 		assert(base+span<=other.mEnd); | ||||
| 		unsigned int i; | ||||
| 		T* dst = other.mStart + start; | ||||
| 		T* src = mStart; | ||||
| 		assert(dst+span<=other.mEnd); | ||||
| 		assert(mStart+span<=mEnd); | ||||
| 		memcpy(base,mStart,span*sizeof(T)); | ||||
| 		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)); */ | ||||
| 	} | ||||
|  | ||||
| 	/** Copy all of this Vector to a segment of another Vector. */ | ||||
| @@ -222,6 +253,21 @@ 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; | ||||
| @@ -260,7 +306,7 @@ template <class T> class Vector { | ||||
| 	T* end() { return mEnd; } | ||||
| 	bool isOwner() { return !!mData; }	// Do we own any memory ourselves? | ||||
| 	//@} | ||||
| 	 | ||||
|  | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								CommonLibs/config_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								CommonLibs/config_defs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #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, | ||||
| }; | ||||
							
								
								
									
										36
									
								
								CommonLibs/debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								CommonLibs/debug.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| #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), | ||||
| }; | ||||
							
								
								
									
										11
									
								
								CommonLibs/debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								CommonLibs/debug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| extern const struct log_info log_info; | ||||
|  | ||||
| /* Debug Areas of the code */ | ||||
| enum { | ||||
| 	DMAIN, | ||||
| 	DTRXCTRL, | ||||
| 	DDEV, | ||||
| 	DLMS, | ||||
| }; | ||||
							
								
								
									
										35
									
								
								CommonLibs/osmo_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								CommonLibs/osmo_signal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* 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 | ||||
|  * | ||||
|  * 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_TRANSC, | ||||
| }; | ||||
|  | ||||
| /* SS_TRANSC signals */ | ||||
| enum SS_TRANSC { | ||||
| 	S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */ | ||||
| }; | ||||
| @@ -1,154 +0,0 @@ | ||||
| /* | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * All rights reserved. | ||||
| */ | ||||
|  | ||||
|  | ||||
| #include "sqlite3.h" | ||||
| #include "sqlite3util.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| // Wrappers to sqlite operations. | ||||
| // These will eventually get moved to commonlibs. | ||||
|  | ||||
| int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query) | ||||
| { | ||||
|         int src = SQLITE_BUSY; | ||||
|         while (src==SQLITE_BUSY) { | ||||
|                 src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL); | ||||
|                 if (src==SQLITE_BUSY) { | ||||
|                         usleep(100000); | ||||
|                 } | ||||
|         } | ||||
|         if (src) { | ||||
|                 fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB)); | ||||
|                 sqlite3_finalize(*stmt); | ||||
|         } | ||||
|         return src; | ||||
| } | ||||
|  | ||||
| int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt) | ||||
| { | ||||
| 	int src = SQLITE_BUSY; | ||||
| 	while (src==SQLITE_BUSY) { | ||||
| 		src = sqlite3_step(stmt); | ||||
| 		if (src==SQLITE_BUSY) { | ||||
| 			usleep(100000); | ||||
| 		} | ||||
| 	} | ||||
| 	if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) { | ||||
| 		fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB)); | ||||
| 	} | ||||
| 	return src; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool sqlite3_exists(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData) | ||||
| { | ||||
| 	size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	// Anything there? | ||||
| 	return (src == SQLITE_ROW); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, unsigned &valueData) | ||||
| { | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		valueData = (unsigned)sqlite3_column_int64(stmt,0); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by the caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, char* &valueData) | ||||
| { | ||||
| 	valueData=NULL; | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData); | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		const char* ptr = (const char*)sqlite3_column_text(stmt,0); | ||||
| 		if (ptr) valueData = strdup(ptr); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by tha caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, unsigned keyData, | ||||
| 		const char* valueName, char* &valueData) | ||||
| { | ||||
| 	valueData=NULL; | ||||
| 	size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20; | ||||
| 	char query[stringSize]; | ||||
| 	sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData); | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Read the result. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	bool retVal = false; | ||||
| 	if (src == SQLITE_ROW) { | ||||
| 		const char* ptr = (const char*)sqlite3_column_text(stmt,0); | ||||
| 		if (ptr) valueData = strdup(ptr); | ||||
| 		retVal = true; | ||||
| 	} | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return retVal; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| bool sqlite3_command(sqlite3* DB, const char* query) | ||||
| { | ||||
| 	// Prepare the statement. | ||||
| 	sqlite3_stmt *stmt; | ||||
| 	if (sqlite3_prepare_statement(DB,&stmt,query)) return false; | ||||
| 	// Run the query. | ||||
| 	int src = sqlite3_run_query(DB,stmt); | ||||
| 	sqlite3_finalize(stmt); | ||||
| 	return src==SQLITE_DONE; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,29 +0,0 @@ | ||||
| #ifndef SQLITE3UTIL_H | ||||
| #define SQLITE3UTIL_H | ||||
|  | ||||
| #include <sqlite3.h> | ||||
|  | ||||
| int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query); | ||||
|  | ||||
| int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt); | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char *tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, unsigned &valueData); | ||||
|  | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData, | ||||
| 		const char* valueName, char* &valueData); | ||||
|  | ||||
| // This function returns an allocated string that must be free'd by the caller. | ||||
| bool sqlite3_single_lookup(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, unsigned keyData, | ||||
| 		const char* valueName, char* &valueData); | ||||
|  | ||||
| bool sqlite3_exists(sqlite3* DB, const char* tableName, | ||||
| 		const char* keyName, const char* keyData); | ||||
|  | ||||
| /** Run a query, ignoring the result; return true on success. */ | ||||
| bool sqlite3_command(sqlite3* DB, const char* query); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										576
									
								
								CommonLibs/trx_vty.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								CommonLibs/trx_vty.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,576 @@ | ||||
| /* | ||||
|  * Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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_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; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.multi_arfcn = true; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	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_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_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; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	} | ||||
| 	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); | ||||
| 	if (trx->cfg.sched_rr != 0) | ||||
| 		vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE); | ||||
|  | ||||
| 	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, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr, | ||||
| 		trx->cfg.sched_rr ? "Enabled" : "Disabled", 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_rt_prio_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_filler_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; | ||||
| } | ||||
							
								
								
									
										68
									
								
								CommonLibs/trx_vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								CommonLibs/trx_vty.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| #include "config_defs.h" | ||||
|  | ||||
| extern struct vty_app_info g_vty_info; | ||||
|  | ||||
| #define TRX_CHAN_MAX 8 | ||||
|  | ||||
| /* 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 egprs; | ||||
| 		unsigned int sched_rr; | ||||
| 		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); | ||||
| @@ -54,7 +54,10 @@ const BitVector GSM::gEdgeTrainingSequence[] = { | ||||
|  | ||||
| const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000"); | ||||
|  | ||||
| const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000"); | ||||
| /* 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 */ | ||||
|  | ||||
| //                               |-head-||---------midamble----------------------||--------------data----------------||t| | ||||
| const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000"); | ||||
|   | ||||
| @@ -52,7 +52,9 @@ extern const BitVector gEdgeTrainingSequence[]; | ||||
| extern const BitVector gDummyBurst; | ||||
|  | ||||
| /** Random access burst synch. sequence */ | ||||
| extern const BitVector gRACHSynchSequence; | ||||
| 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; | ||||
|  | ||||
|   | ||||
							
								
								
									
										22
									
								
								INSTALLATION
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								INSTALLATION
									
									
									
									
									
								
							| @@ -2,32 +2,18 @@ Installation Requirements | ||||
|  | ||||
|  | ||||
|  | ||||
| OpenBTS compiles to a simple Unix binary and does not require special | ||||
| osmo-trx compiles to a simple Unix binary and does not require special | ||||
| installation. | ||||
|  | ||||
| One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to | ||||
| running configure. | ||||
|  | ||||
| To run OpenBTS, the following should be installed: | ||||
|  | ||||
| 	Asterisk (http://www.asterisk.org), running SIP on port 5060. | ||||
|  | ||||
| 	libosip2 (http://www.gnu.org/software/osip/) | ||||
|  | ||||
| 	libortp (http://freshmeat.net/projects/ortp/) | ||||
|  | ||||
| 	libusrp (http://gnuradio.org). | ||||
| To run osmo-trx, the following should be installed: | ||||
| 	libuhd (https://gnuradio.org). | ||||
| 	This is part of the GNURadio installation. | ||||
| 	It is the only part used by OpenBTS. | ||||
|  | ||||
|  | ||||
| OpenBTS logs to syslogd as facility LOG_LOCAL7.  Please set your /etc/syslog.conf | ||||
| accordingly. | ||||
|  | ||||
|  | ||||
| For information on specific executables, see tests/README.tests and | ||||
| apps/README.apps. | ||||
|  | ||||
| See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more | ||||
| See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more | ||||
| information. | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								LEGAL
									
									
									
									
									
								
							| @@ -1,5 +1,8 @@ | ||||
| OpenBTS | ||||
|  | ||||
| The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details. | ||||
|  | ||||
| The related copyrights: | ||||
| Most parts copyright 2008-2011 Free Software Foundation. | ||||
| Some parts copyright 2010 Kestrel Signal Processing, Inc. | ||||
| Some parts copyright 2011 Range Networks, Inc. | ||||
| @@ -12,17 +15,9 @@ patented technologies.  The user of this software is required to take whatever | ||||
| actions are necessary to avoid patent infringement. | ||||
|  | ||||
|  | ||||
| Trademark | ||||
|  | ||||
| "OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a | ||||
| California corporation.  Range reserves the right to control the use of this  | ||||
| trademark.  Do not use this trademark in commerce without permission and do not | ||||
| rebrand OpenBTS under a different trademark. | ||||
|  | ||||
|  | ||||
| Telecom and Radio Spectrum Laws | ||||
|  | ||||
| The primary function of OpenBTS is the provision of telecommunications service | ||||
| The primary function of OsmoTRX is the provision of telecommunications service | ||||
| over a radio link.  This activity is heavily regulated nearly everywhere in | ||||
| the world.  Users of this software are expected to comply with local and national | ||||
| regulations in the jurisdictions where this sortware is used with radio equipment. | ||||
| @@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive. | ||||
|  | ||||
| Note to US Government Users | ||||
|  | ||||
| The OpenBTS software applications and associated documentation are "Commercial | ||||
| The OsmoTRX software applications and associated documentation are "Commercial | ||||
| Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of | ||||
| "Commercial Computer Software" and "Commercial Computer Software Documentation," | ||||
| as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as | ||||
| @@ -54,13 +49,12 @@ and AGPLv3. | ||||
| Note to US Government Contractors | ||||
|  | ||||
| GPL is not compatible with "government purpose rights" (GPR).  If you receive | ||||
| OpenBTS software under a GPL and deliver it under GPR, you will be in violation | ||||
| OsmoTRX software under a GPL and deliver it under GPR, you will be in violation | ||||
| of GPL and possibly subject to enforcement actions by the original authors and | ||||
| copyright holders, including the Free Software Foundation, Inc. | ||||
|  | ||||
|  | ||||
| Software Licensing and Distribution | ||||
|  | ||||
| A subset of OpenBTS is distributed publicly under AGPLv3.  Range reserves the right to | ||||
| distribute most of this source code other licenses as well.  See the COPYING file | ||||
| The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file | ||||
| for more information on the license for this distribution. | ||||
|   | ||||
							
								
								
									
										18
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile.am
									
									
									
									
									
								
							| @@ -22,16 +22,18 @@ include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| ACLOCAL_AMFLAGS = -I config | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -pthread -ldl | ||||
| #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
| #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl | ||||
| AM_CXXFLAGS = -Wall -pthread | ||||
| #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread | ||||
| #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread | ||||
|  | ||||
| # Order must be preserved | ||||
| SUBDIRS = \ | ||||
| 	sqlite3 \ | ||||
| 	doc \ | ||||
| 	CommonLibs \ | ||||
| 	GSM \ | ||||
| 	Transceiver52M | ||||
| 	Transceiver52M \ | ||||
| 	contrib \ | ||||
| 	tests | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	autogen.sh \ | ||||
| @@ -40,6 +42,12 @@ EXTRA_DIST = \ | ||||
| 	COPYING \ | ||||
| 	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$(SQLITE_INCLUDEDIR) | ||||
| 	-I$(GSM_INCLUDEDIR) | ||||
|  | ||||
| COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la | ||||
| GSM_LA = $(top_builddir)/GSM/libGSM.la | ||||
| SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl | ||||
|  | ||||
| if ARCH_ARM | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la | ||||
| else | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la | ||||
| endif | ||||
|  | ||||
| MOSTLYCLEANFILES = *~ | ||||
|   | ||||
							
								
								
									
										107
									
								
								Transceiver52M/Channelizer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								Transceiver52M/Channelizer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /* | ||||
|  * Polyphase channelizer | ||||
|  *  | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #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, 1, 0); | ||||
| 	} | ||||
|  | ||||
| 	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() | ||||
| { | ||||
| } | ||||
							
								
								
									
										34
									
								
								Transceiver52M/Channelizer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Transceiver52M/Channelizer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #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_ */ | ||||
							
								
								
									
										251
									
								
								Transceiver52M/ChannelizerBase.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								Transceiver52M/ChannelizerBase.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| /* | ||||
|  * Polyphase channelizer | ||||
|  *  | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC  | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #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) | ||||
| 	: 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); | ||||
| } | ||||
							
								
								
									
										39
									
								
								Transceiver52M/ChannelizerBase.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Transceiver52M/ChannelizerBase.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #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,22 +21,10 @@ | ||||
|  | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common | ||||
| AM_CXXFLAGS = -ldl -lpthread | ||||
| SUBDIRS = arch device | ||||
|  | ||||
| SUBDIRS = arm x86 | ||||
|  | ||||
| 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 | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
|  | ||||
| rev2dir = $(datadir)/usrp/rev2 | ||||
| rev4dir = $(datadir)/usrp/rev4 | ||||
| @@ -44,55 +32,80 @@ rev4dir = $(datadir)/usrp/rev4 | ||||
| dist_rev2_DATA = std_inband.rbf | ||||
| dist_rev4_DATA = std_inband.rbf | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	README \ | ||||
| 	README.Talgorithm | ||||
| EXTRA_DIST = README | ||||
|  | ||||
| noinst_LTLIBRARIES = libtransceiver.la | ||||
| noinst_LTLIBRARIES = libtransceiver_common.la | ||||
|  | ||||
| COMMON_SOURCES = \ | ||||
| 	radioInterface.cpp \ | ||||
| 	radioVector.cpp \ | ||||
| 	radioClock.cpp \ | ||||
| 	radioBuffer.cpp \ | ||||
| 	sigProcLib.cpp \ | ||||
| 	signalVector.cpp \ | ||||
| 	Transceiver.cpp | ||||
| 	Transceiver.cpp \ | ||||
| 	ChannelizerBase.cpp \ | ||||
| 	Channelizer.cpp \ | ||||
| 	Synthesis.cpp | ||||
|  | ||||
| libtransceiver_la_SOURCES = \ | ||||
| libtransceiver_common_la_SOURCES = \ | ||||
| 	$(COMMON_SOURCES) \ | ||||
| 	Resampler.cpp \ | ||||
| 	radioInterfaceResamp.cpp \ | ||||
| 	radioInterfaceDiversity.cpp | ||||
|  | ||||
| bin_PROGRAMS = osmo-trx | ||||
| 	radioInterfaceMulti.cpp | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	Complex.h \ | ||||
| 	radioInterface.h \ | ||||
| 	radioVector.h \ | ||||
| 	radioClock.h \ | ||||
| 	radioDevice.h \ | ||||
| 	radioBuffer.h \ | ||||
| 	sigProcLib.h \ | ||||
| 	signalVector.h \ | ||||
| 	Transceiver.h \ | ||||
| 	USRPDevice.h \ | ||||
| 	Resampler.h \ | ||||
| 	common/convolve.h \ | ||||
| 	common/convert.h \ | ||||
| 	common/scale.h \ | ||||
| 	common/mult.h | ||||
| 	ChannelizerBase.h \ | ||||
| 	Channelizer.h \ | ||||
| 	Synthesis.h | ||||
|  | ||||
| osmo_trx_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_LDADD = \ | ||||
| 	libtransceiver.la \ | ||||
| COMMON_LDADD = \ | ||||
| 	libtransceiver_common.la \ | ||||
| 	$(ARCH_LA) \ | ||||
| 	$(GSM_LA) \ | ||||
| 	$(COMMON_LA) $(SQLITE_LA) | ||||
| 	$(COMMON_LA) \ | ||||
| 	$(FFTWF_LIBS) \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOCTRL_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) | ||||
|  | ||||
| if USRP1  | ||||
| libtransceiver_la_SOURCES += USRPDevice.cpp | ||||
| osmo_trx_LDADD += $(USRP_LIBS) | ||||
| else | ||||
| libtransceiver_la_SOURCES += UHDDevice.cpp | ||||
| osmo_trx_LDADD += $(UHD_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) | ||||
| endif | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include <string.h> | ||||
| #include <malloc.h> | ||||
| #include <iostream> | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "Resampler.h" | ||||
|  | ||||
| @@ -35,6 +36,8 @@ extern "C" { | ||||
|  | ||||
| #define MAX_OUTPUT_LEN		4096 | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| static float sinc(float x) | ||||
| { | ||||
| 	if (x == 0.0) | ||||
| @@ -43,32 +46,19 @@ static float sinc(float x) | ||||
| 	return sin(M_PI * x) / (M_PI * x); | ||||
| } | ||||
|  | ||||
| bool Resampler::initFilters(float bw) | ||||
| void Resampler::initFilters(float bw) | ||||
| { | ||||
| 	size_t proto_len = p * filt_len; | ||||
| 	float *proto, val, cutoff; | ||||
| 	float cutoff; | ||||
| 	float sum = 0.0f, scale = 0.0f; | ||||
| 	float midpt = (float) (proto_len - 1.0) / 2.0; | ||||
|  | ||||
| 	/*  | ||||
| 	 * Allocate partition filters and the temporary prototype filter | ||||
| 	 * according to numerator of the rational rate. Coefficients are | ||||
| 	 * real only and must be 16-byte memory aligned for SSE usage. | ||||
| 	 */ | ||||
| 	proto = new float[proto_len]; | ||||
| 	if (!proto) | ||||
| 		return false; | ||||
|  | ||||
| 	partitions = (float **) malloc(sizeof(float *) * p); | ||||
| 	if (!partitions) { | ||||
| 		free(proto); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < p; i++) { | ||||
| 		partitions[i] = (float *) | ||||
| 				memalign(16, filt_len * 2 * sizeof(float)); | ||||
| 	} | ||||
| 	auto proto = vector<float>(p * filt_len); | ||||
| 	for (auto &part : partitions) | ||||
| 		part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>)); | ||||
|  | ||||
| 	/*  | ||||
| 	 * Generate the prototype filter with a Blackman-harris window. | ||||
| @@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw) | ||||
| 	else | ||||
| 		cutoff = (float) q; | ||||
|  | ||||
| 	for (size_t i = 0; i < proto_len; i++) { | ||||
| 	float midpt = (proto.size() - 1) / 2.0; | ||||
| 	for (size_t i = 0; i < proto.size(); i++) { | ||||
| 		proto[i] = sinc(((float) i - midpt) / cutoff * bw); | ||||
| 		proto[i] *= a0 - | ||||
| 			    a1 * cos(2 * M_PI * i / (proto_len - 1)) + | ||||
| 			    a2 * cos(4 * M_PI * i / (proto_len - 1)) - | ||||
| 			    a3 * cos(6 * M_PI * i / (proto_len - 1)); | ||||
| 			    a1 * cos(2 * M_PI * i / (proto.size() - 1)) + | ||||
| 			    a2 * cos(4 * M_PI * i / (proto.size() - 1)) - | ||||
| 			    a3 * cos(6 * M_PI * i / (proto.size() - 1)); | ||||
| 		sum += proto[i]; | ||||
| 	} | ||||
| 	scale = p / sum; | ||||
|  | ||||
| 	/* Populate filter partitions from the prototype filter */ | ||||
| 	for (size_t i = 0; i < filt_len; i++) { | ||||
| 		for (size_t n = 0; n < p; n++) { | ||||
| 			partitions[n][2 * i + 0] = proto[i * p + n] * scale; | ||||
| 			partitions[n][2 * i + 1] = 0.0f; | ||||
| 		} | ||||
| 		for (size_t n = 0; n < p; n++) | ||||
| 			partitions[n][i] = complex<float>(proto[i * p + n] * scale); | ||||
| 	} | ||||
|  | ||||
| 	/* For convolution, we store the filter taps in reverse */  | ||||
| 	for (size_t n = 0; n < p; n++) { | ||||
| 		for (size_t i = 0; i < filt_len / 2; i++) { | ||||
| 			val = partitions[n][2 * i]; | ||||
| 			partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)]; | ||||
| 			partitions[n][2 * (filt_len - 1 - i)] = val; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	delete proto; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void Resampler::releaseFilters() | ||||
| { | ||||
| 	if (partitions) { | ||||
| 		for (size_t i = 0; i < p; i++) | ||||
| 			free(partitions[i]); | ||||
| 	} | ||||
|  | ||||
| 	free(partitions); | ||||
| 	partitions = NULL; | ||||
| 	/* Store filter taps in reverse */ | ||||
| 	for (auto &part : partitions) | ||||
| 		reverse(&part[0], &part[filt_len]); | ||||
| } | ||||
|  | ||||
| static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| @@ -159,66 +128,41 @@ static bool check_vec_len(int in_len, int out_len, int p, int q) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void Resampler::computePath() | ||||
| { | ||||
| 	for (int i = 0; i < MAX_OUTPUT_LEN; i++) { | ||||
| 		in_index[i] = (q * i) / p; | ||||
| 		out_path[i] = (q * i) % p; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len) | ||||
| int Resampler::rotate(const 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; | ||||
|  | ||||
| 	if (history_on) { | ||||
| 		memcpy(&in[-2 * hist_len], | ||||
| 		       history, hist_len * 2 * sizeof(float)); | ||||
| 	} else { | ||||
| 		memset(&in[-2 * hist_len], 0, | ||||
| 		       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];  | ||||
|  | ||||
| 		convolve_real(in, in_len, | ||||
| 			      partitions[path], filt_len, | ||||
| 			      &out[2 * i], out_len - i, | ||||
| 			      reinterpret_cast<float *>(partitions[path]), | ||||
| 			      filt_len, &out[2 * i], out_len - i, | ||||
| 			      n, 1, 1, 0); | ||||
| 	} | ||||
|  | ||||
| 	/* Save history */ | ||||
| 	if (history_on) { | ||||
| 		memcpy(history, &in[2 * (in_len - hist_len)], | ||||
| 		       hist_len * 2 * sizeof(float)); | ||||
| 	} | ||||
|  | ||||
| 	return out_len; | ||||
| } | ||||
|  | ||||
| bool Resampler::init(float bw) | ||||
| { | ||||
| 	size_t hist_len = filt_len - 1; | ||||
| 	if (p == 0 || q == 0 || filt_len == 0) return false; | ||||
|  | ||||
| 	/* Filterbank filter internals */ | ||||
| 	if (initFilters(bw) < 0) | ||||
| 		return false; | ||||
|  | ||||
| 	/* History buffer */ | ||||
| 	history = new float[2 * hist_len]; | ||||
| 	memset(history, 0, 2 * hist_len * sizeof(float)); | ||||
| 	initFilters(bw); | ||||
|  | ||||
| 	/* Precompute filterbank paths */ | ||||
| 	in_index = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	out_path = new size_t[MAX_OUTPUT_LEN]; | ||||
| 	computePath(); | ||||
| 	int i = 0; | ||||
| 	for (auto &index : in_index) | ||||
| 		index = (q * i++) / p; | ||||
| 	i = 0; | ||||
| 	for (auto &path : out_path) | ||||
| 		path = (q * i++) % p; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| @@ -228,14 +172,8 @@ size_t Resampler::len() | ||||
| 	return filt_len; | ||||
| } | ||||
|  | ||||
| void Resampler::enableHistory(bool on) | ||||
| { | ||||
| 	history_on = on; | ||||
| } | ||||
|  | ||||
| Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
| 	: in_index(NULL), out_path(NULL), partitions(NULL), | ||||
| 	  history(NULL), history_on(true) | ||||
| 	: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p) | ||||
| { | ||||
| 	this->p = p; | ||||
| 	this->q = q; | ||||
| @@ -244,9 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len) | ||||
|  | ||||
| Resampler::~Resampler() | ||||
| { | ||||
| 	releaseFilters(); | ||||
|  | ||||
| 	delete history; | ||||
| 	delete in_index; | ||||
| 	delete out_path; | ||||
| 	for (auto &part : partitions) | ||||
| 		free(part); | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,9 @@ | ||||
| #ifndef _RESAMPLER_H_ | ||||
| #define _RESAMPLER_H_ | ||||
|  | ||||
| #include <vector> | ||||
| #include <complex> | ||||
|  | ||||
| class Resampler { | ||||
| public: | ||||
| 	/* Constructor for rational sample rate conversion | ||||
| @@ -52,32 +55,22 @@ public: | ||||
| 	 * Input and output vector lengths must of be equal multiples of the | ||||
| 	 * rational conversion rate denominator and numerator respectively. | ||||
| 	 */ | ||||
| 	int rotate(float *in, size_t in_len, float *out, size_t out_len); | ||||
| 	int rotate(const float *in, size_t in_len, float *out, size_t out_len); | ||||
|  | ||||
| 	/* Get filter length | ||||
| 	 *   @return number of taps in each filter partition  | ||||
| 	 */ | ||||
| 	size_t len(); | ||||
|  | ||||
| 	/* | ||||
| 	 * Enable/disable history  | ||||
| 	 */ | ||||
| 	void enableHistory(bool on); | ||||
|  | ||||
| private: | ||||
| 	size_t p; | ||||
| 	size_t q; | ||||
| 	size_t filt_len; | ||||
| 	size_t *in_index; | ||||
| 	size_t *out_path; | ||||
| 	std::vector<size_t> in_index; | ||||
| 	std::vector<size_t> out_path; | ||||
| 	std::vector<std::complex<float> *> partitions; | ||||
|  | ||||
| 	float **partitions; | ||||
| 	float *history; | ||||
| 	bool history_on; | ||||
|  | ||||
| 	bool initFilters(float bw); | ||||
| 	void releaseFilters(); | ||||
| 	void computePath(); | ||||
| 	void initFilters(float bw); | ||||
| }; | ||||
|  | ||||
| #endif /* _RESAMPLER_H_ */ | ||||
|   | ||||
							
								
								
									
										121
									
								
								Transceiver52M/Synthesis.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								Transceiver52M/Synthesis.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
|  * Polyphase synthesis filter | ||||
|  *  | ||||
|  * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc> | ||||
|  * Copyright (C) 2015 Ettus Research LLC | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * See the COPYING file in the main directory for details. | ||||
|  */ | ||||
|  | ||||
| #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, 1, 0); | ||||
| 	} | ||||
|  | ||||
| 	/* 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() | ||||
| { | ||||
| } | ||||
							
								
								
									
										35
									
								
								Transceiver52M/Synthesis.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Transceiver52M/Synthesis.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #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_ */ | ||||
| @@ -27,6 +27,10 @@ | ||||
| #include "Transceiver.h" | ||||
| #include <Logger.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include "osmo_signal.h" | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| @@ -35,12 +39,6 @@ using namespace GSM; | ||||
|  | ||||
| #define USB_LATENCY_INTRVL		10,0 | ||||
|  | ||||
| #if USE_UHD | ||||
| #  define USB_LATENCY_MIN		6,7 | ||||
| #else | ||||
| #  define USB_LATENCY_MIN		1,1 | ||||
| #endif | ||||
|  | ||||
| /* Number of running values use in noise average */ | ||||
| #define NOISE_CNT			20 | ||||
|  | ||||
| @@ -71,7 +69,7 @@ TransceiverState::~TransceiverState() | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc) | ||||
| bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay) | ||||
| { | ||||
|   signalVector *burst; | ||||
|  | ||||
| @@ -81,19 +79,19 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc) | ||||
|   for (size_t n = 0; n < 8; n++) { | ||||
|     for (size_t i = 0; i < 102; i++) { | ||||
|       switch (filler) { | ||||
|       case Transceiver::FILLER_DUMMY: | ||||
|       case FILLER_DUMMY: | ||||
|         burst = generateDummyBurst(sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_NORM_RAND: | ||||
|       case FILLER_NORM_RAND: | ||||
|         burst = genRandNormalBurst(rtsc, sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_EDGE_RAND: | ||||
|       case FILLER_EDGE_RAND: | ||||
|         burst = generateEdgeBurst(rtsc); | ||||
|         break; | ||||
|       case Transceiver::FILLER_ACCESS_RAND: | ||||
|         burst = genRandAccessBurst(sps, n); | ||||
|       case FILLER_ACCESS_RAND: | ||||
|         burst = genRandAccessBurst(rach_delay, sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_ZERO: | ||||
|       case FILLER_ZERO: | ||||
|       default: | ||||
|         burst = generateEmptyBurst(sps, n); | ||||
|       } | ||||
| @@ -102,9 +100,9 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc) | ||||
|       fillerTable[i][n] = burst; | ||||
|     } | ||||
|  | ||||
|     if ((filler == Transceiver::FILLER_NORM_RAND) || | ||||
|         (filler == Transceiver::FILLER_EDGE_RAND)) { | ||||
|         chanType[n] = Transceiver::TSC; | ||||
|     if ((filler == FILLER_NORM_RAND) || | ||||
|         (filler == FILLER_EDGE_RAND)) { | ||||
|         chanType[n] = TSC; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -112,16 +110,17 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc) | ||||
| } | ||||
|  | ||||
| Transceiver::Transceiver(int wBasePort, | ||||
|                          const char *wTRXAddress, | ||||
|                          const char *TRXAddress, | ||||
|                          const char *GSMcoreAddress, | ||||
|                          size_t tx_sps, size_t rx_sps, size_t chans, | ||||
|                          GSM::Time wTransmitLatency, | ||||
|                          RadioInterface *wRadioInterface, | ||||
|                          double wRssiOffset) | ||||
|   : mBasePort(wBasePort), mAddr(wTRXAddress), | ||||
|     mClockSocket(wBasePort, wTRXAddress, mBasePort + 100), | ||||
|   : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress), | ||||
|     mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100), | ||||
|     mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface), | ||||
|     rssiOffset(wRssiOffset), | ||||
|     mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mOn(false), | ||||
|     rssiOffset(wRssiOffset), sig_cbfn(NULL), | ||||
|     mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false), | ||||
|     mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0), | ||||
|     mWriteBurstToDiskMask(0) | ||||
| { | ||||
| @@ -160,7 +159,7 @@ Transceiver::~Transceiver() | ||||
|  * are still expected to report clock indications through control channel | ||||
|  * activity. | ||||
|  */ | ||||
| bool Transceiver::init(int filler, size_t rtsc) | ||||
| bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge) | ||||
| { | ||||
|   int d_srcport, d_dstport, c_srcport, c_dstport; | ||||
|  | ||||
| @@ -174,6 +173,8 @@ bool Transceiver::init(int filler, size_t rtsc) | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   mEdge = edge; | ||||
|  | ||||
|   mDataSockets.resize(mChans); | ||||
|   mCtrlSockets.resize(mChans); | ||||
|   mControlServiceLoopThreads.resize(mChans); | ||||
| @@ -195,8 +196,8 @@ bool Transceiver::init(int filler, size_t rtsc) | ||||
|     d_srcport = mBasePort + 2 * i + 2; | ||||
|     d_dstport = mBasePort + 2 * i + 102; | ||||
|  | ||||
|     mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport); | ||||
|     mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport); | ||||
|     mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport); | ||||
|     mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport); | ||||
|   } | ||||
|  | ||||
|   /* Randomize the central clock */ | ||||
| @@ -216,12 +217,23 @@ bool Transceiver::init(int filler, size_t rtsc) | ||||
|     if (i && filler == FILLER_DUMMY) | ||||
|       filler = FILLER_ZERO; | ||||
|  | ||||
|     mStates[i].init(filler, mSPSTx, txFullScale, rtsc); | ||||
|     mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay); | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn) | ||||
| { | ||||
|   if (this->sig_cbfn) | ||||
|     osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL); | ||||
|  | ||||
|   if (cbfn) { | ||||
|     this->sig_cbfn = cbfn; | ||||
|     osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Start the transceiver | ||||
|  * | ||||
| @@ -271,7 +283,7 @@ bool Transceiver::start() | ||||
|                             TxUpperLoopAdapter, (void*) chan); | ||||
|   } | ||||
|  | ||||
|   writeClockInterface(); | ||||
|   mForceClockInterface = true; | ||||
|   mOn = true; | ||||
|   return true; | ||||
| } | ||||
| @@ -295,6 +307,10 @@ void Transceiver::stop() | ||||
|   LOG(NOTICE) << "Stopping the transceiver"; | ||||
|   mTxLowerLoopThread->cancel(); | ||||
|   mRxLowerLoopThread->cancel(); | ||||
|   mTxLowerLoopThread->join(); | ||||
|   mRxLowerLoopThread->join(); | ||||
|   delete mTxLowerLoopThread; | ||||
|   delete mRxLowerLoopThread; | ||||
|  | ||||
|   for (size_t i = 0; i < mChans; i++) { | ||||
|     mRxServiceLoopThreads[i]->cancel(); | ||||
| @@ -313,11 +329,6 @@ void Transceiver::stop() | ||||
|     mTxPriorityQueues[i].clear(); | ||||
|   } | ||||
|  | ||||
|   mTxLowerLoopThread->join(); | ||||
|   mRxLowerLoopThread->join(); | ||||
|   delete mTxLowerLoopThread; | ||||
|   delete mRxLowerLoopThread; | ||||
|  | ||||
|   mOn = false; | ||||
|   LOG(NOTICE) << "Transceiver stopped"; | ||||
| } | ||||
| @@ -377,7 +388,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime) | ||||
|     state = &mStates[i]; | ||||
|  | ||||
|     while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) { | ||||
|       LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface"; | ||||
|       LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->SDR interface (" | ||||
|                   << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans; | ||||
|       if (state->mRetrans) | ||||
|         updateFillerTable(i, burst); | ||||
|       delete burst; | ||||
| @@ -428,7 +440,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan) | ||||
|   case V: | ||||
|     state->fillerModulus[timeslot] = 51; | ||||
|     break; | ||||
|     //case V:  | ||||
|     //case V: | ||||
|   case VII: | ||||
|     state->fillerModulus[timeslot] = 102; | ||||
|     break; | ||||
| @@ -441,8 +453,8 @@ void Transceiver::setModulus(size_t timeslot, size_t chan) | ||||
| } | ||||
|  | ||||
|  | ||||
| Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime, | ||||
|                                                     size_t chan) | ||||
| CorrType Transceiver::expectedCorrType(GSM::Time currTime, | ||||
|                                        size_t chan) | ||||
| { | ||||
|   static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 }; | ||||
|   static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2, | ||||
| @@ -531,51 +543,6 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime, | ||||
|   } | ||||
| } | ||||
|  | ||||
| int Transceiver::detectBurst(signalVector &burst, | ||||
|                              complex &, float &toa, CorrType type) | ||||
| { | ||||
|   float threshold = 5.0, rc = 0; | ||||
|  | ||||
|   switch (type) { | ||||
|   case EDGE: | ||||
|     rc = detectEdgeBurst(burst, mTSC, threshold, mSPSRx, | ||||
|                          amp, toa, mMaxExpectedDelayNB); | ||||
|     if (rc > 0) | ||||
|       break; | ||||
|     else | ||||
|       type = TSC; | ||||
|   case TSC: | ||||
|     rc = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, | ||||
|                              amp, toa, mMaxExpectedDelayNB); | ||||
|     break; | ||||
|   case RACH: | ||||
|     threshold = 6.0; | ||||
|     rc = detectRACHBurst(burst, threshold, mSPSRx, amp, toa, | ||||
|                          mMaxExpectedDelayAB); | ||||
|     break; | ||||
|   default: | ||||
|     LOG(ERR) << "Invalid correlation type"; | ||||
|   } | ||||
|  | ||||
|   if (rc > 0) | ||||
|     return type; | ||||
|  | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Demodulate GMSK by direct rotation and soft slicing. | ||||
|  */ | ||||
| SoftVector *Transceiver::demodulate(signalVector &burst, complex amp, | ||||
|                                     float toa, CorrType type) | ||||
| { | ||||
|   if (type == EDGE) | ||||
| 	  return demodEdgeBurst(burst, mSPSRx, amp, toa); | ||||
|  | ||||
|   return demodulateBurst(burst, mSPSRx, amp, toa); | ||||
| } | ||||
|  | ||||
| void writeToFile(radioVector *radio_burst, size_t chan) | ||||
| { | ||||
|   GSM::Time time = radio_burst->getTime(); | ||||
| @@ -588,7 +555,7 @@ void writeToFile(radioVector *radio_burst, size_t chan) | ||||
|  | ||||
| /* | ||||
|  * Pull bursts from the FIFO and handle according to the slot | ||||
|  * and burst correlation type. Equalzation is currently disabled.  | ||||
|  * and burst correlation type. Equalzation is currently disabled. | ||||
|  */ | ||||
| SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid, | ||||
|                                          double &timingOffset, double &noise, | ||||
| @@ -596,7 +563,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i | ||||
| { | ||||
|   int rc; | ||||
|   complex amp; | ||||
|   float toa, pow, max = -1.0, avg = 0.0; | ||||
|   float toa, max = -1.0, avg = 0.0; | ||||
|   int max_i = -1; | ||||
|   signalVector *burst; | ||||
|   SoftVector *bits = NULL; | ||||
| @@ -612,6 +579,10 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i | ||||
|   GSM::Time time = radio_burst->getTime(); | ||||
|   CorrType type = expectedCorrType(time, chan); | ||||
|  | ||||
|   /* Enable 8-PSK burst detection if EDGE is enabled */ | ||||
|   if (mEdge && (type == TSC)) | ||||
|     type = EDGE; | ||||
|  | ||||
|   /* Debug: dump bursts to disk */ | ||||
|   /* bits 0-7  - chan 0 timeslots | ||||
|    * bits 8-15 - chan 1 timeslots */ | ||||
| @@ -627,7 +598,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i | ||||
|  | ||||
|   /* Select the diversity channel with highest energy */ | ||||
|   for (size_t i = 0; i < radio_burst->chans(); i++) { | ||||
|     energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow); | ||||
|     float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx); | ||||
|     if (pow > max) { | ||||
|       max = pow; | ||||
|       max_i = i; | ||||
| @@ -665,7 +636,8 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i | ||||
|   } | ||||
|  | ||||
|   /* Detect normal or RACH bursts */ | ||||
|   rc = detectBurst(*burst, amp, toa, type); | ||||
|   rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, | ||||
|                       (type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB); | ||||
|  | ||||
|   if (rc > 0) { | ||||
|     type = (CorrType) rc; | ||||
| @@ -680,9 +652,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   timingOffset = toa / mSPSRx; | ||||
|   timingOffset = toa; | ||||
|  | ||||
|   bits = demodulate(*burst, amp, toa, type); | ||||
|   bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type); | ||||
|  | ||||
|   delete radio_burst; | ||||
|   return bits; | ||||
| @@ -694,87 +666,113 @@ void Transceiver::reset() | ||||
|     mTxPriorityQueues[i].clear(); | ||||
| } | ||||
|  | ||||
|    | ||||
|  | ||||
| #define MAX_PACKET_LENGTH 100 | ||||
|  | ||||
| /** | ||||
|  * Matches a buffer with a command. | ||||
|  * @param  buf    a buffer to look command in | ||||
|  * @param  cmd    a command to look in buffer | ||||
|  * @param  params pointer to arguments, or NULL | ||||
|  * @return        true if command matches, otherwise false | ||||
|  */ | ||||
| static bool match_cmd(char *buf, | ||||
|   const char *cmd, char **params) | ||||
| { | ||||
|   size_t cmd_len = strlen(cmd); | ||||
|  | ||||
|   /* Check a command itself */ | ||||
|   if (strncmp(buf, cmd, cmd_len)) | ||||
|     return false; | ||||
|  | ||||
|   /* A command has arguments */ | ||||
|   if (params != NULL) { | ||||
|     /* Make sure there is a space */ | ||||
|     if (buf[cmd_len] != ' ') | ||||
|       return false; | ||||
|  | ||||
|     /* Update external pointer */ | ||||
|     *params = buf + cmd_len + 1; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Transceiver::driveControl(size_t chan) | ||||
| { | ||||
|   int MAX_PACKET_LENGTH = 100; | ||||
|   char buffer[MAX_PACKET_LENGTH + 1]; | ||||
|   char response[MAX_PACKET_LENGTH + 1]; | ||||
|   char *command, *params; | ||||
|   int msgLen; | ||||
|  | ||||
|   // check control socket | ||||
|   char buffer[MAX_PACKET_LENGTH]; | ||||
|   int msgLen = -1; | ||||
|   buffer[0] = '\0'; | ||||
|   /* Attempt to read from control socket */ | ||||
|   msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH); | ||||
|   if (msgLen < 1) | ||||
|     return; | ||||
|  | ||||
|   msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer)); | ||||
|   /* Zero-terminate received string */ | ||||
|   buffer[msgLen] = '\0'; | ||||
|  | ||||
|   if (msgLen < 1) { | ||||
|   /* Verify a command signature */ | ||||
|   if (strncmp(buffer, "CMD ", 4)) { | ||||
|     LOGC(DTRXCTRL, WARNING) << "bogus message on control interface"; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   char cmdcheck[4]; | ||||
|   char command[MAX_PACKET_LENGTH]; | ||||
|   char response[MAX_PACKET_LENGTH]; | ||||
|   /* Set command pointer */ | ||||
|   command = buffer + 4; | ||||
|   LOGC(DTRXCTRL, INFO) << "chan " << chan << ": command is '" << command << "'"; | ||||
|  | ||||
|   sscanf(buffer,"%3s %s",cmdcheck,command); | ||||
|  | ||||
|   if (!chan) | ||||
|     writeClockInterface(); | ||||
|  | ||||
|   if (strcmp(cmdcheck,"CMD")!=0) { | ||||
|     LOG(WARNING) << "bogus message on control interface"; | ||||
|     return; | ||||
|   } | ||||
|   LOG(INFO) << "command is " << buffer; | ||||
|  | ||||
|   if (strcmp(command,"POWEROFF")==0) { | ||||
|   if (match_cmd(command, "POWEROFF", NULL)) { | ||||
|     stop(); | ||||
|     sprintf(response,"RSP POWEROFF 0"); | ||||
|   } | ||||
|   else if (strcmp(command,"POWERON")==0) { | ||||
|     if (!start()) | ||||
|   } else if (match_cmd(command, "POWERON", NULL)) { | ||||
|     if (!start()) { | ||||
|       sprintf(response,"RSP POWERON 1"); | ||||
|     else | ||||
|     } else { | ||||
|       sprintf(response,"RSP POWERON 0"); | ||||
|       for (int i = 0; i < 8; i++) { | ||||
|         for (int j = 0; j < 8; j++) | ||||
|           mHandover[i][j] = false; | ||||
|       } | ||||
|   } | ||||
|   else if (strcmp(command,"HANDOVER")==0){ | ||||
|     int ts=0,ss=0; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss); | ||||
|     mHandover[ts][ss] = true; | ||||
|     LOG(WARNING) << "HANDOVER RACH at timeslot " << ts << " subslot " << ss; | ||||
|     sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss); | ||||
|   } | ||||
|   else if (strcmp(command,"NOHANDOVER")==0){ | ||||
|     int ts=0,ss=0; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss); | ||||
|     mHandover[ts][ss] = false; | ||||
|     LOG(WARNING) << "NOHANDOVER at timeslot " << ts << " subslot " << ss; | ||||
|     sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss); | ||||
|   } | ||||
|   else if (strcmp(command,"SETMAXDLY")==0) { | ||||
|     } | ||||
|   } else if (match_cmd(command, "HANDOVER", ¶ms)) { | ||||
|     unsigned ts = 0, ss = 0; | ||||
|     sscanf(params, "%u %u", &ts, &ss); | ||||
|     if (ts > 7 || ss > 7) { | ||||
|       sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); | ||||
|     } else { | ||||
|       mHandover[ts][ss] = true; | ||||
|       sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss); | ||||
|     } | ||||
|   } else if (match_cmd(command, "NOHANDOVER", ¶ms)) { | ||||
|     unsigned ts = 0, ss = 0; | ||||
|     sscanf(params, "%u %u", &ts, &ss); | ||||
|     if (ts > 7 || ss > 7) { | ||||
|       sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); | ||||
|     } else { | ||||
|       mHandover[ts][ss] = false; | ||||
|       sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss); | ||||
|     } | ||||
|   } else if (match_cmd(command, "SETMAXDLY", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int maxDelay; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay); | ||||
|     sscanf(params, "%d", &maxDelay); | ||||
|     mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km | ||||
|     sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay); | ||||
|   } | ||||
|   else if (strcmp(command,"SETMAXDLYNB")==0) { | ||||
|   } else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int maxDelay; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay); | ||||
|     sscanf(params, "%d", &maxDelay); | ||||
|     mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km | ||||
|     sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay); | ||||
|   } | ||||
|   else if (strcmp(command,"SETRXGAIN")==0) { | ||||
|   } else if (match_cmd(command, "SETRXGAIN", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int newGain; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain); | ||||
|     sscanf(params, "%d", &newGain); | ||||
|     newGain = mRadioInterface->setRxGain(newGain, chan); | ||||
|     sprintf(response,"RSP SETRXGAIN 0 %d",newGain); | ||||
|   } | ||||
|   else if (strcmp(command,"NOISELEV")==0) { | ||||
|   } else if (match_cmd(command, "NOISELEV", NULL)) { | ||||
|     if (mOn) { | ||||
|       float lev = mStates[chan].mNoiseLev; | ||||
|       sprintf(response,"RSP NOISELEV 0 %d", | ||||
| @@ -783,98 +781,97 @@ void Transceiver::driveControl(size_t chan) | ||||
|     else { | ||||
|       sprintf(response,"RSP NOISELEV 1  0"); | ||||
|     } | ||||
|   } | ||||
|   else if (!strcmp(command, "SETPOWER")) { | ||||
|   } else if (match_cmd(command, "SETPOWER", ¶ms)) { | ||||
|     int power; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &power); | ||||
|     sscanf(params, "%d", &power); | ||||
|     power = mRadioInterface->setPowerAttenuation(power, chan); | ||||
|     mStates[chan].mPower = power; | ||||
|     sprintf(response, "RSP SETPOWER 0 %d", power); | ||||
|   } | ||||
|   else if (!strcmp(command,"ADJPOWER")) { | ||||
|   } else if (match_cmd(command, "ADJPOWER", ¶ms)) { | ||||
|     int power, step; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &step); | ||||
|     sscanf(params, "%d", &step); | ||||
|     power = mStates[chan].mPower + step; | ||||
|     power = mRadioInterface->setPowerAttenuation(power, chan); | ||||
|     mStates[chan].mPower = power; | ||||
|     sprintf(response, "RSP ADJPOWER 0 %d", power); | ||||
|   } | ||||
|   else if (strcmp(command,"RXTUNE")==0) { | ||||
|   } else if (match_cmd(command, "RXTUNE", ¶ms)) { | ||||
|     // tune receiver | ||||
|     int freqKhz; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); | ||||
|     sscanf(params, "%d", &freqKhz); | ||||
|     mRxFreq = freqKhz * 1e3; | ||||
|     if (!mRadioInterface->tuneRx(mRxFreq, chan)) { | ||||
|        LOG(ALERT) << "RX failed to tune"; | ||||
|        LOGC(DTRXCTRL, ALERT) << "RX failed to tune"; | ||||
|        sprintf(response,"RSP RXTUNE 1 %d",freqKhz); | ||||
|     } | ||||
|     else | ||||
|        sprintf(response,"RSP RXTUNE 0 %d",freqKhz); | ||||
|   } | ||||
|   else if (strcmp(command,"TXTUNE")==0) { | ||||
|   } else if (match_cmd(command, "TXTUNE", ¶ms)) { | ||||
|     // tune txmtr | ||||
|     int freqKhz; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); | ||||
|     sscanf(params, "%d", &freqKhz); | ||||
|     mTxFreq = freqKhz * 1e3; | ||||
|     if (!mRadioInterface->tuneTx(mTxFreq, chan)) { | ||||
|        LOG(ALERT) << "TX failed to tune"; | ||||
|        LOGC(DTRXCTRL, ALERT) << "TX failed to tune"; | ||||
|        sprintf(response,"RSP TXTUNE 1 %d",freqKhz); | ||||
|     } | ||||
|     else | ||||
|        sprintf(response,"RSP TXTUNE 0 %d",freqKhz); | ||||
|   } | ||||
|   else if (!strcmp(command,"SETTSC")) { | ||||
|   } else if (match_cmd(command, "SETTSC", ¶ms)) { | ||||
|     // set TSC | ||||
|     unsigned TSC; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC); | ||||
|     if (mOn || (TSC < 0) || (TSC > 7)) | ||||
|     sscanf(params, "%u", &TSC); | ||||
|     if (TSC > 7) { | ||||
|       sprintf(response, "RSP SETTSC 1 %d", TSC); | ||||
|     else if (chan && (TSC != mTSC)) | ||||
|       sprintf(response, "RSP SETTSC 1 %d", TSC); | ||||
|     else { | ||||
|     } else { | ||||
|       LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC; | ||||
|       mTSC = TSC; | ||||
|       sprintf(response,"RSP SETTSC 0 %d", TSC); | ||||
|     } | ||||
|   } | ||||
|   else if (strcmp(command,"SETSLOT")==0) { | ||||
|   } else if (match_cmd(command, "SETSLOT", ¶ms)) { | ||||
|     // set slot type | ||||
|     int  corrCode; | ||||
|     int  timeslot; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode); | ||||
|     sscanf(params, "%d %d", ×lot, &corrCode); | ||||
|     if ((timeslot < 0) || (timeslot > 7)) { | ||||
|       LOG(WARNING) << "bogus message on control interface"; | ||||
|       LOGC(DTRXCTRL, WARNING) << "bogus message on control interface"; | ||||
|       sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode); | ||||
|       return; | ||||
|     }      | ||||
|     } | ||||
|     mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode; | ||||
|     setModulus(timeslot, chan); | ||||
|     sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode); | ||||
|  | ||||
|   } | ||||
|   else if (strcmp(command,"_SETBURSTTODISKMASK")==0) { | ||||
|   } else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) { | ||||
|     // debug command! may change or disapear without notice | ||||
|     // set a mask which bursts to dump to disk | ||||
|     int mask; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask); | ||||
|     sscanf(params, "%d", &mask); | ||||
|     mWriteBurstToDiskMask = mask; | ||||
|     sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask); | ||||
|   } | ||||
|   else { | ||||
|     LOG(WARNING) << "bogus command " << command << " on control interface."; | ||||
|   } else { | ||||
|     LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface."; | ||||
|     sprintf(response,"RSP ERR 1"); | ||||
|   } | ||||
|  | ||||
|   LOGC(DTRXCTRL, INFO) << "chan " << chan << ": response is '" << response << "'"; | ||||
|   mCtrlSockets[chan]->write(response, strlen(response) + 1); | ||||
| } | ||||
|  | ||||
| bool Transceiver::driveTxPriorityQueue(size_t chan) | ||||
| { | ||||
|   char buffer[gSlotLen+50]; | ||||
|   int burstLen; | ||||
|   char buffer[EDGE_BURST_NBITS + 50]; | ||||
|  | ||||
|   // check data socket | ||||
|   size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer)); | ||||
|  | ||||
|   if (msgLen!=gSlotLen+1+4+1) { | ||||
|   if (msgLen == gSlotLen + 1 + 4 + 1) { | ||||
|     burstLen = gSlotLen; | ||||
|   } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) { | ||||
|     if (mSPSTx != 4) | ||||
|       return false; | ||||
|  | ||||
|     burstLen = EDGE_BURST_NBITS; | ||||
|   } else { | ||||
|     LOG(ERR) << "badly formatted packet on GSM->TRX interface"; | ||||
|     return false; | ||||
|   } | ||||
| @@ -885,14 +882,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan) | ||||
|     frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]); | ||||
|  | ||||
|   LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot); | ||||
|    | ||||
|  | ||||
|   int RSSI = (int) buffer[5]; | ||||
|   static BitVector newBurst(gSlotLen); | ||||
|   BitVector newBurst(burstLen); | ||||
|   BitVector::iterator itr = newBurst.begin(); | ||||
|   char *bufferItr = buffer+6; | ||||
|   while (itr < newBurst.end())  | ||||
|   while (itr < newBurst.end()) | ||||
|     *itr++ = *bufferItr++; | ||||
|    | ||||
|  | ||||
|   GSM::Time currTime = GSM::Time(frameNum,timeSlot); | ||||
|  | ||||
|   addRadioVector(chan, newBurst, RSSI, currTime); | ||||
| @@ -904,18 +901,23 @@ bool Transceiver::driveTxPriorityQueue(size_t chan) | ||||
|  | ||||
| void Transceiver::driveReceiveRadio() | ||||
| { | ||||
|   if (!mRadioInterface->driveReceiveRadio()) { | ||||
|   int rc = mRadioInterface->driveReceiveRadio(); | ||||
|   if (rc == 0) { | ||||
|     usleep(100000); | ||||
|   } else { | ||||
|     if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) | ||||
|       writeClockInterface(); | ||||
|   } else if (rc < 0) { | ||||
|     LOG(FATAL) << "radio Interface receive failed, requesting stop."; | ||||
|     osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this); | ||||
|   } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) { | ||||
|     mForceClockInterface = false; | ||||
|     writeClockInterface(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void Transceiver::logRxBurst(SoftVector *burst, GSM::Time time, double dbm, | ||||
| void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm, | ||||
|                              double rssi, double noise, double toa) | ||||
| { | ||||
|   LOG(DEBUG) << std::fixed << std::right | ||||
|     << " chan: "   << chan | ||||
|     << " time: "   << time | ||||
|     << " RSSI: "   << std::setw(5) << std::setprecision(1) << rssi | ||||
|                    << "dBFS/" << std::setw(6) << -dbm << "dBm" | ||||
| @@ -941,6 +943,9 @@ void Transceiver::driveReceiveFIFO(size_t chan) | ||||
|   if (!rxBurst) | ||||
|     return; | ||||
|  | ||||
|   // Convert -1..+1 soft bits to 0..1 soft bits | ||||
|   vectorSlicer(rxBurst); | ||||
|  | ||||
|   /* | ||||
|    * EDGE demodulator returns 444 (148 * 3) bits | ||||
|    */ | ||||
| @@ -948,7 +953,7 @@ void Transceiver::driveReceiveFIFO(size_t chan) | ||||
|     nbits = gSlotLen * 3; | ||||
|  | ||||
|   dBm = RSSI + rssiOffset; | ||||
|   logRxBurst(rxBurst, burstTime, dBm, RSSI, noise, TOA); | ||||
|   logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA); | ||||
|  | ||||
|   TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer | ||||
|  | ||||
| @@ -974,7 +979,7 @@ void Transceiver::driveTxFIFO() | ||||
| { | ||||
|  | ||||
|   /** | ||||
|       Features a carefully controlled latency mechanism, to  | ||||
|       Features a carefully controlled latency mechanism, to | ||||
|       assure that transmit packets arrive at the radio/USRP | ||||
|       before they need to be transmitted. | ||||
|  | ||||
| @@ -985,7 +990,7 @@ void Transceiver::driveTxFIFO() | ||||
|  | ||||
|  | ||||
|   RadioClock *radioClock = (mRadioInterface->getClock()); | ||||
|    | ||||
|  | ||||
|   if (mOn) { | ||||
|     //radioClock->wait(); // wait until clock updates | ||||
|     LOG(DEBUG) << "radio clock " << radioClock->get(); | ||||
| @@ -997,14 +1002,15 @@ void Transceiver::driveTxFIFO() | ||||
|           // only update latency at the defined frame interval | ||||
|           if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) { | ||||
|             mTransmitLatency = mTransmitLatency + GSM::Time(1,0); | ||||
|             LOG(INFO) << "new latency: " << mTransmitLatency; | ||||
|             LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun " | ||||
|                       << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")"; | ||||
|             mLatencyUpdateTime = radioClock->get(); | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           // if underrun hasn't occurred in the last sec (216 frames) drop | ||||
|           //    transmit latency by a timeslot | ||||
|           if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) { | ||||
|           if (mTransmitLatency > mRadioInterface->minLatency()) { | ||||
|               if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) { | ||||
|               mTransmitLatency.decTN(); | ||||
|               LOG(INFO) << "reduced latency: " << mTransmitLatency; | ||||
| @@ -1040,11 +1046,15 @@ void Transceiver::writeClockInterface() | ||||
|  | ||||
| void *RxUpperLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "RxUpper%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   trx->setPriority(0.42); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1056,6 +1066,8 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan) | ||||
|  | ||||
| void *RxLowerLoopAdapter(Transceiver *transceiver) | ||||
| { | ||||
|   set_selfthread_name("RxLower"); | ||||
|  | ||||
|   transceiver->setPriority(0.45); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1067,6 +1079,8 @@ void *RxLowerLoopAdapter(Transceiver *transceiver) | ||||
|  | ||||
| void *TxLowerLoopAdapter(Transceiver *transceiver) | ||||
| { | ||||
|   set_selfthread_name("TxLower"); | ||||
|  | ||||
|   transceiver->setPriority(0.44); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1078,11 +1092,15 @@ void *TxLowerLoopAdapter(Transceiver *transceiver) | ||||
|  | ||||
| void *ControlServiceLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "CtrlService%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   while (1) { | ||||
|     trx->driveControl(num); | ||||
|     pthread_testcancel(); | ||||
| @@ -1092,11 +1110,15 @@ void *ControlServiceLoopAdapter(TransceiverChannel *chan) | ||||
|  | ||||
| void *TxUpperLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "TxUpper%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   trx->setPriority(0.40); | ||||
|  | ||||
|   while (1) { | ||||
|   | ||||
| @@ -30,6 +30,11 @@ | ||||
| #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 */ | ||||
| @@ -54,7 +59,7 @@ struct TransceiverState { | ||||
|   ~TransceiverState(); | ||||
|  | ||||
|   /* Initialize a multiframe slot in the filler table */ | ||||
|   bool init(int filler, size_t sps, float scale, size_t rtsc); | ||||
|   bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); | ||||
|  | ||||
|   int chanType[8]; | ||||
|  | ||||
| @@ -89,15 +94,17 @@ struct TransceiverState { | ||||
| /** The Transceiver class, responsible for physical layer of basestation */ | ||||
| class Transceiver { | ||||
| public: | ||||
|   /** Transceiver constructor  | ||||
|   /** Transceiver constructor | ||||
|       @param wBasePort base port number of UDP sockets | ||||
|       @param TRXAddress IP address of the TRX manager, as a string | ||||
|       @param TRXAddress IP address of the TRX, as a string | ||||
|       @param GSMcoreAddress IP address of the GSM core, as a string | ||||
|       @param wSPS number of samples per GSM symbol | ||||
|       @param wTransmitLatency initial setting of transmit latency | ||||
|       @param radioInterface associated radioInterface object | ||||
|   */ | ||||
|   Transceiver(int wBasePort, | ||||
|               const char *TRXAddress, | ||||
|               const char *GSMcoreAddress, | ||||
|               size_t tx_sps, size_t rx_sps, size_t chans, | ||||
|               GSM::Time wTransmitLatency, | ||||
|               RadioInterface *wRadioInterface, | ||||
| @@ -107,7 +114,7 @@ public: | ||||
|   ~Transceiver(); | ||||
|  | ||||
|   /** Start the control loop */ | ||||
|   bool init(int filler, size_t rtsc); | ||||
|   bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge); | ||||
|  | ||||
|   /** attach the radioInterface receive FIFO */ | ||||
|   bool receiveFIFO(VectorFIFO *wFIFO, size_t chan) | ||||
| @@ -122,6 +129,8 @@ public: | ||||
|   /** accessor for number of channels */ | ||||
|   size_t numChans() const { return mChans; }; | ||||
|  | ||||
|   void setSignalHandler(osmo_signal_cbfn cbfn); | ||||
|  | ||||
|   /** Codes for channel combinations */ | ||||
|   typedef enum { | ||||
|     FILL,               ///< Channel is transmitted, but unused | ||||
| @@ -142,26 +151,10 @@ public: | ||||
|     LOOPBACK            ///< similar go VII, used in loopback testing | ||||
|   } ChannelCombination; | ||||
|  | ||||
|   /** 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 | ||||
|     EDGE,	       ///< timeslot should contain an EDGE burst | ||||
|     IDLE	       ///< timeslot is an idle (or dummy) burst | ||||
|   } CorrType; | ||||
|  | ||||
|   enum FillerType { | ||||
|     FILLER_DUMMY, | ||||
|     FILLER_ZERO, | ||||
|     FILLER_NORM_RAND, | ||||
|     FILLER_EDGE_RAND, | ||||
|     FILLER_ACCESS_RAND, | ||||
|   }; | ||||
|  | ||||
| private: | ||||
|   int mBasePort; | ||||
|   std::string mAddr; | ||||
|   std::string mLocalAddr; | ||||
|   std::string mRemoteAddr; | ||||
|  | ||||
|   std::vector<UDPSocket *> mDataSockets;  ///< socket for writing to/reading from GSM core | ||||
|   std::vector<UDPSocket *> mCtrlSockets;  ///< socket for writing/reading control commands from GSM core | ||||
| @@ -178,7 +171,7 @@ private: | ||||
|  | ||||
|   GSM::Time mTransmitLatency;             ///< latency between basestation clock and transmit deadline clock | ||||
|   GSM::Time mLatencyUpdateTime;           ///< last time latency was updated | ||||
|   GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO  | ||||
|   GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO | ||||
|   GSM::Time mLastClockUpdateTime;         ///< last time clock update was sent up to core | ||||
|  | ||||
|   RadioInterface *mRadioInterface;	  ///< associated radioInterface object | ||||
| @@ -187,6 +180,8 @@ private: | ||||
|  | ||||
|   double rssiOffset;                      ///< RSSI to dBm conversion offset | ||||
|  | ||||
|   osmo_signal_cbfn *sig_cbfn;              ///< Registered Signal Handler to announce events. | ||||
|  | ||||
|   /** modulate and add a burst to the transmit queue */ | ||||
|   void addRadioVector(size_t chan, BitVector &bits, | ||||
|                       int RSSI, GSM::Time &wTime); | ||||
| @@ -211,19 +206,13 @@ private: | ||||
|   /** send messages over the clock socket */ | ||||
|   void writeClockInterface(void); | ||||
|  | ||||
|   /** Detectbursts */ | ||||
|   int detectBurst(signalVector &burst, | ||||
|                   complex &, float &toa, CorrType type); | ||||
|  | ||||
|   /** Demodulate burst and output soft bits */ | ||||
|   SoftVector *demodulate(signalVector &burst, | ||||
|                          complex amp, float toa, CorrType type); | ||||
|  | ||||
|   int mSPSTx;                          ///< number of samples per Tx symbol | ||||
|   int mSPSRx;                          ///< number of samples per Rx symbol | ||||
|   size_t mChans; | ||||
|  | ||||
|   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 | ||||
| @@ -276,7 +265,7 @@ protected: | ||||
|   /** set priority on current thread */ | ||||
|   void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); } | ||||
|  | ||||
|   void logRxBurst(SoftVector *burst, GSM::Time time, double dbm, | ||||
|   void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm, | ||||
|                   double rssi, double noise, double toa); | ||||
| }; | ||||
|  | ||||
| @@ -291,4 +280,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
| /** transmit queueing thread loop */ | ||||
| void *TxUpperLoopAdapter(TransceiverChannel *); | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								Transceiver52M/arch/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Transceiver52M/arch/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| SUBDIRS = common | ||||
| if ARCH_ARM | ||||
| SUBDIRS += arm | ||||
| else | ||||
| SUBDIRS += x86 | ||||
| endif | ||||
| @@ -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../common | ||||
| AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../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,4 +20,3 @@ libarch_la_SOURCES = \ | ||||
| 	scale_neon.S \
 | ||||
| 	mult.c \
 | ||||
| 	mult_neon.S | ||||
| endif | ||||
| @@ -28,19 +28,9 @@ | ||||
| void neon_convert_ps_si16_4n(short *, const float *, const float *, int); | ||||
| void neon_convert_si16_ps_4n(float *, const short *, int); | ||||
| 
 | ||||
| #ifndef HAVE_NEON | ||||
| static void convert_si16_ps(float *out, const short *in, int len) | ||||
| { | ||||
| 	for (int i = 0; i < len; i++) | ||||
| 		out[i] = in[i]; | ||||
| void convert_init(void) { | ||||
| } | ||||
| 
 | ||||
| static void convert_ps_si16(short *out, const 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, | ||||
| @@ -67,7 +57,6 @@ static void neon_convert_ps_si16(short *out, | ||||
| 	for (int i = 0; i < len % 4; i++) | ||||
| 		out[start + i] = (short) (in[start + i] * (*scale)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void convert_float_short(short *out, const float *in, float scale, int len) | ||||
| { | ||||
| @@ -79,7 +68,7 @@ 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 | ||||
| 	convert_ps_si16(out, in, scale, len); | ||||
| 	base_convert_float_short(out, in, scale, len); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| @@ -91,6 +80,6 @@ void convert_short_float(float *out, const short *in, int len) | ||||
| 	else | ||||
| 		neon_convert_si16_ps_4n(out, in, len >> 2); | ||||
| #else | ||||
| 	convert_si16_ps(out, in, len); | ||||
| 	base_convert_short_float(out, in, len); | ||||
| #endif | ||||
| } | ||||
| @@ -58,6 +58,13 @@ 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, | ||||
							
								
								
									
										15
									
								
								Transceiver52M/arch/common/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Transceiver52M/arch/common/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| 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 | ||||
| @@ -2,6 +2,14 @@ | ||||
| #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_ */ | ||||
							
								
								
									
										34
									
								
								Transceiver52M/arch/common/convert_base.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Transceiver52M/arch/common/convert_base.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  * Conversion | ||||
|  * 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 "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,7 +1,7 @@ | ||||
| #ifndef _CONVOLVE_H_ | ||||
| #define _CONVOLVE_H_ | ||||
| 
 | ||||
| void *convolve_h_alloc(int num); | ||||
| void *convolve_h_alloc(size_t num); | ||||
| 
 | ||||
| int convolve_real(const float *x, int x_len, | ||||
| 		  const float *h, int h_len, | ||||
| @@ -27,4 +27,6 @@ int base_convolve_complex(const float *x, int x_len, | ||||
| 			  int start, int len, | ||||
| 			  int step, int offset); | ||||
| 
 | ||||
| void convolve_init(void); | ||||
| 
 | ||||
| #endif /* _CONVOLVE_H_ */ | ||||
| @@ -146,7 +146,7 @@ int base_convolve_complex(const float *x, int x_len, | ||||
| } | ||||
| 
 | ||||
| /* Aligned filter tap allocation */ | ||||
| void *convolve_h_alloc(int len) | ||||
| void *convolve_h_alloc(size_t len) | ||||
| { | ||||
| #ifdef HAVE_SSE3 | ||||
| 	return memalign(16, len * 2 * sizeof(float)); | ||||
							
								
								
									
										112
									
								
								Transceiver52M/arch/common/fft.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								Transceiver52M/arch/common/fft.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| /* | ||||
|  * Fast Fourier transform  | ||||
|  * | ||||
|  * Copyright (C) 2012 Tom Tsou <tom@tsou.cc> | ||||
|  *  | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										13
									
								
								Transceiver52M/arch/common/fft.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Transceiver52M/arch/common/fft.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #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_ */ | ||||
							
								
								
									
										33
									
								
								Transceiver52M/arch/x86/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Transceiver52M/arch/x86/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| 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 | ||||
							
								
								
									
										83
									
								
								Transceiver52M/arch/x86/convert.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								Transceiver52M/arch/x86/convert.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| /* | ||||
|  * 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); | ||||
| } | ||||
							
								
								
									
										107
									
								
								Transceiver52M/arch/x86/convert_sse_3.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								Transceiver52M/arch/x86/convert_sse_3.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										34
									
								
								Transceiver52M/arch/x86/convert_sse_3.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Transceiver52M/arch/x86/convert_sse_3.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  * 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); | ||||
							
								
								
									
										77
									
								
								Transceiver52M/arch/x86/convert_sse_4_1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								Transceiver52M/arch/x86/convert_sse_4_1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
|  * 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 | ||||
							
								
								
									
										28
									
								
								Transceiver52M/arch/x86/convert_sse_4_1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Transceiver52M/arch/x86/convert_sse_4_1.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * 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); | ||||
							
								
								
									
										172
									
								
								Transceiver52M/arch/x86/convolve.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								Transceiver52M/arch/x86/convolve.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| /* | ||||
|  * 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, int, int); | ||||
| 	void (*conv_cmplx_8n) (const float *, int, const float *, int, float *, | ||||
| 			       int, int, int, int, int); | ||||
| 	void (*conv_cmplx) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int, int, int); | ||||
| 	void (*conv_real4) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int, int, int); | ||||
| 	void (*conv_real8) (const float *, int, const float *, int, float *, | ||||
| 			    int, int, int, int, int); | ||||
| 	void (*conv_real12) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int, int, int); | ||||
| 	void (*conv_real16) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int, int, int); | ||||
| 	void (*conv_real20) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int, int, int); | ||||
| 	void (*conv_real4n) (const float *, int, const float *, int, float *, | ||||
| 			     int, int, int, int, int); | ||||
| 	void (*conv_real) (const float *, int, const float *, int, float *, int, | ||||
| 			   int, 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 step, int offset); | ||||
|  | ||||
| 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 step, int offset); | ||||
|  | ||||
| int bounds_check(int x_len, int h_len, int y_len, | ||||
| 		 int start, int len, int step); | ||||
|  | ||||
| /* 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, int step, int offset) | ||||
| { | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
|  | ||||
| 	if (step <= 4) { | ||||
| 		switch (h_len) { | ||||
| 		case 4: | ||||
| 			c.conv_real4(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				     step, offset); | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			c.conv_real8(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				     step, offset); | ||||
| 			break; | ||||
| 		case 12: | ||||
| 			c.conv_real12(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				      step, offset); | ||||
| 			break; | ||||
| 		case 16: | ||||
| 			c.conv_real16(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				      step, offset); | ||||
| 			break; | ||||
| 		case 20: | ||||
| 			c.conv_real20(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				      step, offset); | ||||
| 			break; | ||||
| 		default: | ||||
| 			if (!(h_len % 4)) | ||||
| 				c.conv_real4n(x, x_len, h, h_len, y, y_len, | ||||
| 					      start, len, step, offset); | ||||
| 			else | ||||
| 				c.conv_real(x, x_len, h, h_len, y, y_len, start, | ||||
| 					    len, step, offset); | ||||
| 		} | ||||
| 	} else | ||||
| 		c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step, | ||||
| 			    offset); | ||||
|  | ||||
| 	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, int step, int offset) | ||||
| { | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
|  | ||||
| 	if (step <= 4) { | ||||
| 		if (!(h_len % 8)) | ||||
| 			c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, | ||||
| 					len, step, offset); | ||||
| 		else if (!(h_len % 4)) | ||||
| 			c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, | ||||
| 					len, step, offset); | ||||
| 		else | ||||
| 			c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, | ||||
| 				     step, offset); | ||||
| 	} else | ||||
| 		c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step, | ||||
| 			     offset); | ||||
|  | ||||
| 	return len; | ||||
| } | ||||
| @@ -20,40 +20,31 @@ | ||||
| #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 | ||||
| 
 | ||||
| /* 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 step, int offset); | ||||
| 
 | ||||
| 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 step, int offset); | ||||
| 
 | ||||
| int bounds_check(int x_len, int h_len, int y_len, | ||||
| 		 int start, int len, int step); | ||||
| 
 | ||||
| #ifdef HAVE_SSE3 | ||||
| #include <xmmintrin.h> | ||||
| #include <pmmintrin.h> | ||||
| 
 | ||||
| /* 4-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real4(const float *restrict x, | ||||
| 			   const float *restrict h, | ||||
| 			   float *restrict y, | ||||
| 			   int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* NOTE: The parameter list of this function has to match the parameter
 | ||||
| 	 * list of _base_convolve_real() in convolve_base.c. This specific | ||||
| 	 * implementation, ignores some of the parameters of | ||||
| 	 * _base_convolve_complex(), which are: x_len, y_len, offset, step */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	/* Load (aligned) filter taps */ | ||||
| 	m0 = _mm_load_ps(&h[0]); | ||||
| 	m1 = _mm_load_ps(&h[4]); | ||||
| @@ -61,8 +52,8 @@ static void sse_conv_real4(const float *restrict x, | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Load (unaligned) input data */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 4]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 4]); | ||||
| 		m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| @@ -81,13 +72,17 @@ static void sse_conv_real4(const float *restrict x, | ||||
| } | ||||
| 
 | ||||
| /* 8-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real8(const float *restrict x, | ||||
| 			   const float *restrict h, | ||||
| 			   float *restrict y, | ||||
| 			   int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_real4() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	/* Load (aligned) filter taps */ | ||||
| 	m0 = _mm_load_ps(&h[0]); | ||||
| 	m1 = _mm_load_ps(&h[4]); | ||||
| @@ -99,10 +94,10 @@ static void sse_conv_real8(const float *restrict x, | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Load (unaligned) input data */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 12]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 12]); | ||||
| 
 | ||||
| 		m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| @@ -128,14 +123,18 @@ static void sse_conv_real8(const float *restrict x, | ||||
| } | ||||
| 
 | ||||
| /* 12-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real12(const float *restrict x, | ||||
| 			    const float *restrict h, | ||||
| 			    float *restrict y, | ||||
| 			    int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_real4() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 	__m128 m8, m9, m10, m11, m12, m13, m14; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	/* Load (aligned) filter taps */ | ||||
| 	m0 = _mm_load_ps(&h[0]); | ||||
| 	m1 = _mm_load_ps(&h[4]); | ||||
| @@ -150,18 +149,18 @@ static void sse_conv_real12(const float *restrict x, | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Load (unaligned) input data */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 12]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 12]); | ||||
| 
 | ||||
| 		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 16]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 20]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 16]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 20]); | ||||
| 
 | ||||
| 		m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| @@ -175,8 +174,8 @@ static void sse_conv_real12(const float *restrict x, | ||||
| 		m5 = _mm_mul_ps(m9, m14); | ||||
| 
 | ||||
| 		/* Sum and store */ | ||||
| 		m8  = _mm_add_ps(m0, m2); | ||||
| 		m9  = _mm_add_ps(m1, m3); | ||||
| 		m8 = _mm_add_ps(m0, m2); | ||||
| 		m9 = _mm_add_ps(m1, m3); | ||||
| 		m10 = _mm_add_ps(m8, m4); | ||||
| 		m11 = _mm_add_ps(m9, m5); | ||||
| 
 | ||||
| @@ -190,14 +189,18 @@ static void sse_conv_real12(const float *restrict x, | ||||
| } | ||||
| 
 | ||||
| /* 16-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real16(const float *restrict x, | ||||
| 			    const float *restrict h, | ||||
| 			    float *restrict y, | ||||
| 			    int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_real4() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 	__m128 m8, m9, m10, m11, m12, m13, m14, m15; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	/* Load (aligned) filter taps */ | ||||
| 	m0 = _mm_load_ps(&h[0]); | ||||
| 	m1 = _mm_load_ps(&h[4]); | ||||
| @@ -216,23 +219,23 @@ static void sse_conv_real16(const float *restrict x, | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Load (unaligned) input data */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 12]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 12]); | ||||
| 
 | ||||
| 		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 16]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 20]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 24]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 28]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 16]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 20]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 24]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 28]); | ||||
| 
 | ||||
| 		m8  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m9  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| @@ -248,8 +251,8 @@ static void sse_conv_real16(const float *restrict x, | ||||
| 		m7 = _mm_mul_ps(m11, m15); | ||||
| 
 | ||||
| 		/* Sum and store */ | ||||
| 		m8  = _mm_add_ps(m0, m2); | ||||
| 		m9  = _mm_add_ps(m1, m3); | ||||
| 		m8 = _mm_add_ps(m0, m2); | ||||
| 		m9 = _mm_add_ps(m1, m3); | ||||
| 		m10 = _mm_add_ps(m4, m6); | ||||
| 		m11 = _mm_add_ps(m5, m7); | ||||
| 
 | ||||
| @@ -265,14 +268,18 @@ static void sse_conv_real16(const float *restrict x, | ||||
| } | ||||
| 
 | ||||
| /* 20-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real20(const float *restrict x, | ||||
| 			    const float *restrict h, | ||||
| 			    float *restrict y, | ||||
| 			    int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_real4() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 	__m128 m8, m9, m11, m12, m13, m14, m15; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	/* Load (aligned) filter taps */ | ||||
| 	m0 = _mm_load_ps(&h[0]); | ||||
| 	m1 = _mm_load_ps(&h[4]); | ||||
| @@ -293,19 +300,19 @@ static void sse_conv_real20(const float *restrict x, | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Multiply-accumulate first 12 taps */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 12]); | ||||
| 		m4 = _mm_loadu_ps(&x[2 * i + 16]); | ||||
| 		m5 = _mm_loadu_ps(&x[2 * i + 20]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 0]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 4]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 8]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 12]); | ||||
| 		m4 = _mm_loadu_ps(&_x[2 * i + 16]); | ||||
| 		m5 = _mm_loadu_ps(&_x[2 * i + 20]); | ||||
| 
 | ||||
| 		m6  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m7  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m8  = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m9  = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m0  = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m1  = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 		m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| 		m2 = _mm_mul_ps(m6, m11); | ||||
| 		m3 = _mm_mul_ps(m7, m11); | ||||
| @@ -314,16 +321,16 @@ static void sse_conv_real20(const float *restrict x, | ||||
| 		m6 = _mm_mul_ps(m0, m13); | ||||
| 		m7 = _mm_mul_ps(m1, m13); | ||||
| 
 | ||||
| 		m0  = _mm_add_ps(m2, m4); | ||||
| 		m1  = _mm_add_ps(m3, m5); | ||||
| 		m8  = _mm_add_ps(m0, m6); | ||||
| 		m9  = _mm_add_ps(m1, m7); | ||||
| 		m0 = _mm_add_ps(m2, m4); | ||||
| 		m1 = _mm_add_ps(m3, m5); | ||||
| 		m8 = _mm_add_ps(m0, m6); | ||||
| 		m9 = _mm_add_ps(m1, m7); | ||||
| 
 | ||||
| 		/* Multiply-accumulate last 8 taps */ | ||||
| 		m0 = _mm_loadu_ps(&x[2 * i + 24]); | ||||
| 		m1 = _mm_loadu_ps(&x[2 * i + 28]); | ||||
| 		m2 = _mm_loadu_ps(&x[2 * i + 32]); | ||||
| 		m3 = _mm_loadu_ps(&x[2 * i + 36]); | ||||
| 		m0 = _mm_loadu_ps(&_x[2 * i + 24]); | ||||
| 		m1 = _mm_loadu_ps(&_x[2 * i + 28]); | ||||
| 		m2 = _mm_loadu_ps(&_x[2 * i + 32]); | ||||
| 		m3 = _mm_loadu_ps(&_x[2 * i + 36]); | ||||
| 
 | ||||
| 		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| @@ -335,8 +342,8 @@ static void sse_conv_real20(const float *restrict x, | ||||
| 		m2 = _mm_mul_ps(m6, m15); | ||||
| 		m3 = _mm_mul_ps(m7, m15); | ||||
| 
 | ||||
| 		m4  = _mm_add_ps(m0, m2); | ||||
| 		m5  = _mm_add_ps(m1, m3); | ||||
| 		m4 = _mm_add_ps(m0, m2); | ||||
| 		m5 = _mm_add_ps(m1, m3); | ||||
| 
 | ||||
| 		/* Final sum and store */ | ||||
| 		m0 = _mm_add_ps(m8, m4); | ||||
| @@ -351,13 +358,17 @@ static void sse_conv_real20(const float *restrict x, | ||||
| } | ||||
| 
 | ||||
| /* 4*N-tap SSE complex-real convolution */ | ||||
| static void sse_conv_real4n(const float *x, | ||||
| 			    const float *h, | ||||
| 			    float *y, | ||||
| 			    int h_len, int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_real4() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m4, m5, m6, m7; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Zero */ | ||||
| 		m6 = _mm_setzero_ps(); | ||||
| @@ -370,8 +381,8 @@ static void sse_conv_real4n(const float *x, | ||||
| 			m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 
 | ||||
| 			/* Load (unaligned) input data */ | ||||
| 			m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]); | ||||
| 			m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]); | ||||
| 			m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 			m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| @@ -394,13 +405,20 @@ static void sse_conv_real4n(const float *x, | ||||
| } | ||||
| 
 | ||||
| /* 4*N-tap SSE complex-complex convolution */ | ||||
| static void sse_conv_cmplx_4n(const float *x, | ||||
| 			      const float *h, | ||||
| 			      float *y, | ||||
| 			      int h_len, int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* NOTE: The parameter list of this function has to match the parameter
 | ||||
| 	 * list of _base_convolve_complex() in convolve_base.c. This specific | ||||
| 	 * implementation, ignores some of the parameters of | ||||
| 	 * _base_convolve_complex(), which are: x_len, y_len, offset, step. */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Zero */ | ||||
| 		m6 = _mm_setzero_ps(); | ||||
| @@ -414,8 +432,8 @@ static void sse_conv_cmplx_4n(const float *x, | ||||
| 			m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| 			/* Load (unaligned) input data */ | ||||
| 			m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]); | ||||
| 			m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]); | ||||
| 			m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 			m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| @@ -445,14 +463,18 @@ static void sse_conv_cmplx_4n(const float *x, | ||||
| } | ||||
| 
 | ||||
| /* 8*N-tap SSE complex-complex convolution */ | ||||
| static void sse_conv_cmplx_8n(const float *x, | ||||
| 			      const float *h, | ||||
| 			      float *y, | ||||
| 			      int h_len, int len) | ||||
| 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, int step, int offset) | ||||
| { | ||||
| 	/* See NOTE in sse_conv_cmplx_4n() */ | ||||
| 
 | ||||
| 	__m128 m0, m1, m2, m3, m4, m5, m6, m7; | ||||
| 	__m128 m8, m9, m10, m11, m12, m13, m14, m15; | ||||
| 
 | ||||
| 	const float *_x = &x[2 * (-(h_len - 1) + start)]; | ||||
| 
 | ||||
| 	for (int i = 0; i < len; i++) { | ||||
| 		/* Zero */ | ||||
| 		m12 = _mm_setzero_ps(); | ||||
| @@ -473,13 +495,13 @@ static void sse_conv_cmplx_8n(const float *x, | ||||
| 			m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| 			/* Load (unaligned) input data */ | ||||
| 			m0 = _mm_loadu_ps(&x[2 * i + 16 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&x[2 * i + 16 * n + 4]); | ||||
| 			m2 = _mm_loadu_ps(&x[2 * i + 16 * n + 8]); | ||||
| 			m3 = _mm_loadu_ps(&x[2 * i + 16 * n + 12]); | ||||
| 			m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]); | ||||
| 			m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]); | ||||
| 			m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]); | ||||
| 			m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]); | ||||
| 
 | ||||
| 			m8  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 			m9  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 			m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 			m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 			m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); | ||||
| 			m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); | ||||
| 
 | ||||
| @@ -518,96 +540,3 @@ static void sse_conv_cmplx_8n(const float *x, | ||||
| 	} | ||||
| } | ||||
| #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, | ||||
| 		  int step, int offset) | ||||
| { | ||||
| 	void (*conv_func)(const float *, const float *, | ||||
| 			  float *, int) = NULL; | ||||
| 	void (*conv_func_n)(const float *, const float *, | ||||
| 			    float *, int, int) = NULL; | ||||
| 
 | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
| 
 | ||||
| #ifdef HAVE_SSE3 | ||||
| 	if (step <= 4) { | ||||
| 		switch (h_len) { | ||||
| 		case 4: | ||||
| 			conv_func = sse_conv_real4; | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			conv_func = sse_conv_real8; | ||||
| 			break; | ||||
| 		case 12: | ||||
| 			conv_func = sse_conv_real12; | ||||
| 			break; | ||||
| 		case 16: | ||||
| 			conv_func = sse_conv_real16; | ||||
| 			break; | ||||
| 		case 20: | ||||
| 			conv_func = sse_conv_real20; | ||||
| 			break; | ||||
| 		default: | ||||
| 			if (!(h_len % 4)) | ||||
| 				conv_func_n = sse_conv_real4n; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 	if (conv_func) { | ||||
| 		conv_func(&x[2 * (-(h_len - 1) + start)], | ||||
| 			  h, y, len); | ||||
| 	} else if (conv_func_n) { | ||||
| 		conv_func_n(&x[2 * (-(h_len - 1) + start)], | ||||
| 			    h, y, h_len, len); | ||||
| 	} else { | ||||
| 		_base_convolve_real(x, x_len, | ||||
| 				    h, h_len, | ||||
| 				    y, y_len, | ||||
| 				    start, len, step, offset); | ||||
| 	} | ||||
| 
 | ||||
| 	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, | ||||
| 		     int step, int offset) | ||||
| { | ||||
| 	void (*conv_func)(const float *, const float *, | ||||
| 			  float *, int, int) = NULL; | ||||
| 
 | ||||
| 	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(y, 0, len * 2 * sizeof(float)); | ||||
| 
 | ||||
| #ifdef HAVE_SSE3 | ||||
| 	if (step <= 4) { | ||||
| 		if (!(h_len % 8)) | ||||
| 			conv_func = sse_conv_cmplx_8n; | ||||
| 		else if (!(h_len % 4)) | ||||
| 			conv_func = sse_conv_cmplx_4n; | ||||
| 	} | ||||
| #endif | ||||
| 	if (conv_func) { | ||||
| 		conv_func(&x[2 * (-(h_len - 1) + start)], | ||||
| 			  h, y, h_len, len); | ||||
| 	} else { | ||||
| 		_base_convolve_complex(x, x_len, | ||||
| 				       h, h_len, | ||||
| 				       y, y_len, | ||||
| 				       start, len, step, offset); | ||||
| 	} | ||||
| 
 | ||||
| 	return len; | ||||
| } | ||||
							
								
								
									
										68
									
								
								Transceiver52M/arch/x86/convolve_sse_3.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Transceiver52M/arch/x86/convolve_sse_3.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
|  | ||||
| /* 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, int step, int offset); | ||||
							
								
								
									
										17
									
								
								Transceiver52M/device/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Transceiver52M/device/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| noinst_HEADERS = radioDevice.h | ||||
|  | ||||
| SUBDIRS = | ||||
|  | ||||
| if DEVICE_USRP1 | ||||
| SUBDIRS += usrp1 | ||||
| endif | ||||
|  | ||||
| if DEVICE_UHD | ||||
| SUBDIRS += uhd | ||||
| endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| SUBDIRS += lms | ||||
| endif | ||||
							
								
								
									
										700
									
								
								Transceiver52M/device/lms/LMSDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										700
									
								
								Transceiver52M/device/lms/LMSDevice.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,700 @@ | ||||
| /* | ||||
| * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| 	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 <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include "Logger.h" | ||||
| #include "Threads.h" | ||||
| #include "LMSDevice.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include <lime/LimeSuite.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| constexpr double LMSDevice::masterClockRate; | ||||
|  | ||||
| #define MAX_ANTENNA_LIST_SIZE 10 | ||||
| #define LMS_SAMPLE_RATE GSMRATE*32 | ||||
| #define GSM_CARRIER_BW 270000.0 /* 270kHz */ | ||||
| #define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */ | ||||
| #define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED) | ||||
|  | ||||
| static int compat_LMS_VCTCXORead(lms_device_t *dev, uint16_t *val, bool memory) | ||||
| { | ||||
| #if HAVE_LMS_VCTCXO_EEPROM_SAVING | ||||
| 	return LMS_VCTCXORead(dev, val, memory); | ||||
| #else | ||||
| 	return LMS_VCTCXORead(dev, val); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int compat_LMS_VCTCXOWrite(lms_device_t *dev, uint16_t val, bool memory) | ||||
| { | ||||
| #if HAVE_LMS_VCTCXO_EEPROM_SAVING | ||||
| 	return LMS_VCTCXOWrite(dev, val, memory); | ||||
| #else | ||||
| 	return LMS_VCTCXOWrite(dev, val); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| LMSDevice::LMSDevice(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), | ||||
| 	m_lms_dev(NULL) | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "creating LMS device..."; | ||||
|  | ||||
| 	m_lms_stream_rx.resize(chans); | ||||
| 	m_lms_stream_tx.resize(chans); | ||||
|  | ||||
| 	m_last_rx_underruns.resize(chans, 0); | ||||
| 	m_last_rx_overruns.resize(chans, 0); | ||||
| 	m_last_tx_underruns.resize(chans, 0); | ||||
| 	m_last_tx_overruns.resize(chans, 0); | ||||
| } | ||||
|  | ||||
| LMSDevice::~LMSDevice() | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "Closing LMS device"; | ||||
| 	if (m_lms_dev) { | ||||
| 		LMS_Close(m_lms_dev); | ||||
| 		m_lms_dev = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void lms_log_callback(int lvl, const char *msg) | ||||
| { | ||||
| 	/* map lime specific log levels */ | ||||
| 	static const int lvl_map[5] = { | ||||
| 		[0] = LOGL_FATAL, | ||||
| 		[LMS_LOG_ERROR] = LOGL_ERROR, | ||||
| 		[LMS_LOG_WARNING] = LOGL_NOTICE, | ||||
| 		[LMS_LOG_INFO] = LOGL_INFO, | ||||
| 		[LMS_LOG_DEBUG] = LOGL_DEBUG, | ||||
| 	}; | ||||
| 	/* protect against future higher log level values (lower importance) */ | ||||
| 	if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map)) | ||||
| 		lvl = ARRAY_SIZE(lvl_map)-1; | ||||
|  | ||||
| 	LOGLV(DLMS, lvl_map[lvl]) << msg; | ||||
| } | ||||
|  | ||||
| static void thread_enable_cancel(bool cancel) | ||||
| { | ||||
| 	cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : | ||||
| 		 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); | ||||
| } | ||||
|  | ||||
| static void print_range(const char* name, lms_range_t *range) | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max | ||||
| 		   << " Step=" << range->step; | ||||
| } | ||||
|  | ||||
| /*! Find the device string that matches all filters from \a args. | ||||
|  *  \param[in] info_list device addresses found by LMS_GetDeviceList() | ||||
|  *  \param[in] count length of info_list | ||||
|  *  \param[in] args dev-args value from osmo-trx.cfg, containing comma separated key=value pairs | ||||
|  *  \return index of first matching device or -1 (no match) */ | ||||
| int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args) | ||||
| { | ||||
| 	unsigned int i, j; | ||||
| 	vector<string> filters; | ||||
|  | ||||
| 	filters = comma_delimited_to_vector(args.c_str()); | ||||
|  | ||||
| 	/* iterate over device addresses */ | ||||
| 	for (i=0; i < count; i++) { | ||||
| 		/* check if all filters match */ | ||||
| 		bool match = true; | ||||
| 		for (j=0; j < filters.size(); j++) { | ||||
| 			if (!strstr(info_list[i], filters[j].c_str())) { | ||||
| 				match = false; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (match) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int LMSDevice::open(const std::string &args, int ref, bool swap_channels) | ||||
| { | ||||
| 	//lms_info_str_t dev_str; | ||||
| 	lms_info_str_t* info_list; | ||||
| 	lms_range_t range_lpfbw_rx, range_lpfbw_tx, range_sr; | ||||
| 	float_type sr_host, sr_rf, lpfbw_rx, lpfbw_tx; | ||||
| 	uint16_t dac_val; | ||||
| 	unsigned int i, n; | ||||
| 	int rc, dev_id; | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Opening LMS device.."; | ||||
|  | ||||
| 	LMS_RegisterLogHandler(&lms_log_callback); | ||||
|  | ||||
| 	if ((n = LMS_GetDeviceList(NULL)) < 0) | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed"; | ||||
| 	LOGC(DDEV, INFO) << "Devices found: " << n; | ||||
| 	if (n < 1) | ||||
| 	    return -1; | ||||
|  | ||||
| 	info_list = new lms_info_str_t[n]; | ||||
|  | ||||
| 	if (LMS_GetDeviceList(info_list) < 0) | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList(info_list) failed"; | ||||
|  | ||||
| 	for (i = 0; i < n; i++) | ||||
| 		LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i]; | ||||
|  | ||||
| 	dev_id = info_list_find(info_list, n, args); | ||||
| 	if (dev_id == -1) { | ||||
| 		LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'"; | ||||
| 		delete[] info_list; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Using device[" << dev_id << "]"; | ||||
| 	rc = LMS_Open(&m_lms_dev, info_list[dev_id], NULL); | ||||
| 	if (rc != 0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)"; | ||||
| 		delete [] info_list; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	delete [] info_list; | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Init LMS device"; | ||||
| 	if (LMS_Init(m_lms_dev) != 0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS_Init() failed"; | ||||
| 		goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr)) | ||||
| 		goto out_close; | ||||
| 	print_range("Sample Rate", &range_sr); | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps; | ||||
| 	if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0) | ||||
| 		goto out_close; | ||||
|  | ||||
| 	if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf)) | ||||
| 		goto out_close; | ||||
| 	LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf; | ||||
|  | ||||
| 	/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */ | ||||
| 	ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */ | ||||
|  | ||||
| 	switch (ref) { | ||||
| 	case REF_INTERNAL: | ||||
| 		LOGC(DDEV, INFO) << "Setting Internal clock reference"; | ||||
| 		/* Ugly API: Selecting clock source implicit by writing to VCTCXO DAC ?!? */ | ||||
| 		if (compat_LMS_VCTCXORead(m_lms_dev, &dac_val, false) < 0) | ||||
| 			goto out_close; | ||||
| 		LOGC(DDEV, INFO) << "Setting VCTCXO to " << dac_val; | ||||
| 		if (compat_LMS_VCTCXOWrite(m_lms_dev, dac_val, false) < 0) | ||||
| 			goto out_close; | ||||
| 		break; | ||||
| 	case REF_EXTERNAL: | ||||
| 		LOGC(DDEV, INFO) << "Setting External clock reference to " << 10000000.0; | ||||
| 		/* Assume an external 10 MHz reference clock */ | ||||
| 		if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0) | ||||
| 			goto out_close; | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGC(DDEV, ALERT) << "Invalid reference type"; | ||||
| 		goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx)) | ||||
| 		goto out_close; | ||||
| 	print_range("LPFBWRange Rx", &range_lpfbw_rx); | ||||
| 	if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx)) | ||||
| 		goto out_close; | ||||
| 	print_range("LPFBWRange Tx", &range_lpfbw_tx); | ||||
| 	lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max); | ||||
| 	lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max); | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx; | ||||
|  | ||||
| 	if (!set_antennas()) { | ||||
| 		LOGC(DDEV, ALERT) << "LMS antenna setting failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Perform Rx and Tx calibration */ | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LOGC(DDEV, INFO) << "Setting LPFBW chan " << i; | ||||
| 		if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, i, lpfbw_rx) < 0) | ||||
| 			goto out_close; | ||||
| 		if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, i, lpfbw_tx) < 0) | ||||
| 			goto out_close; | ||||
| 		LOGC(DDEV, INFO) << "Calibrating chan " << i; | ||||
| 		if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, i, LMS_CALIBRATE_BW_HZ, 0) < 0) | ||||
| 			goto out_close; | ||||
| 		if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, i, LMS_CALIBRATE_BW_HZ, 0) < 0) | ||||
| 			goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	samplesRead = 0; | ||||
| 	samplesWritten = 0; | ||||
| 	started = false; | ||||
|  | ||||
| 	return NORMAL; | ||||
|  | ||||
| out_close: | ||||
| 	LOGC(DDEV, ALERT) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage(); | ||||
| 	LMS_Close(m_lms_dev); | ||||
| 	m_lms_dev = NULL; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::start() | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "starting LMS..."; | ||||
|  | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (started) { | ||||
| 		LOGC(DDEV, ERR) << "Device already started"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/* configure the channels/streams */ | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		// Set gains to midpoint | ||||
| 		setTxGain((minTxGain() + maxTxGain()) / 2, i); | ||||
| 		setRxGain((minRxGain() + maxRxGain()) / 2, i); | ||||
|  | ||||
| 		m_lms_stream_rx[i] = {}; | ||||
| 		m_lms_stream_rx[i].isTx = false; | ||||
| 		m_lms_stream_rx[i].channel = i; | ||||
| 		m_lms_stream_rx[i].fifoSize = 1024 * 1024; | ||||
| 		m_lms_stream_rx[i].throughputVsLatency = 0.3; | ||||
| 		m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16; | ||||
|  | ||||
| 		m_lms_stream_tx[i] = {}; | ||||
| 		m_lms_stream_tx[i].isTx = true; | ||||
| 		m_lms_stream_tx[i].channel = i; | ||||
| 		m_lms_stream_tx[i].fifoSize = 1024 * 1024; | ||||
| 		m_lms_stream_tx[i].throughputVsLatency = 0.3; | ||||
| 		m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16; | ||||
|  | ||||
| 		if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	/* now start the streams in a second loop, as we can no longer call | ||||
| 	 * LMS_SetupStream() after LMS_StartStream() of the first stream */ | ||||
| 	for (i = 0; i < chans; i++) { | ||||
| 		if (LMS_StartStream(&m_lms_stream_rx[i]) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_StartStream(&m_lms_stream_tx[i]) < 0) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	flush_recv(10); | ||||
|  | ||||
| 	started = true; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::stop() | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (!started) | ||||
| 		return true; | ||||
|  | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LMS_StopStream(&m_lms_stream_tx[i]); | ||||
| 		LMS_StopStream(&m_lms_stream_rx[i]); | ||||
| 	} | ||||
|  | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LMS_DestroyStream(m_lms_dev, &m_lms_stream_tx[i]); | ||||
| 		LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]); | ||||
| 		LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false); | ||||
| 		LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false); | ||||
| 	} | ||||
|  | ||||
| 	started = false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| double LMSDevice::maxTxGain() | ||||
| { | ||||
| 	return 73.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::minTxGain() | ||||
| { | ||||
| 	return 0.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::maxRxGain() | ||||
| { | ||||
| 	return 73.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::minRxGain() | ||||
| { | ||||
| 	return 0.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::setTxGain(double dB, size_t chan) | ||||
| { | ||||
| 	if (dB > maxTxGain()) | ||||
| 		dB = maxTxGain(); | ||||
| 	if (dB < minTxGain()) | ||||
| 		dB = minTxGain(); | ||||
|  | ||||
| 	LOGC(DDEV, NOTICE) << "chan " << chan <<": Setting TX gain to " << dB << " dB"; | ||||
|  | ||||
| 	if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0) | ||||
| 		LOGC(DDEV, ERR) << "chan " << chan <<": Error setting TX gain to " << dB << " dB"; | ||||
|  | ||||
| 	return dB; | ||||
| } | ||||
|  | ||||
| double LMSDevice::setRxGain(double dB, size_t chan) | ||||
| { | ||||
| 	if (dB > maxRxGain()) | ||||
| 		dB = maxRxGain(); | ||||
| 	if (dB < minRxGain()) | ||||
| 		dB = minRxGain(); | ||||
|  | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting RX gain to " << dB << " dB"; | ||||
|  | ||||
| 	if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0) | ||||
| 		LOGC(DDEV, ERR) << "chan "<< chan << ": Error setting RX gain to " << dB << " dB"; | ||||
|  | ||||
| 	return dB; | ||||
| } | ||||
|  | ||||
| int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	const char* c_name = name.c_str(); | ||||
| 	int num_names; | ||||
| 	int i; | ||||
|  | ||||
| 	num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list); | ||||
| 	for (i = 0; i < num_names; i++) { | ||||
| 		if (!strcmp(c_name, name_list[i])) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::flush_recv(size_t num_pkts) | ||||
| { | ||||
| 	#define CHUNK 625 | ||||
| 	int len = CHUNK * tx_sps; | ||||
| 	short *buffer = new short[len * 2]; | ||||
| 	int rc; | ||||
| 	lms_stream_meta_t rx_metadata = {}; | ||||
| 	rx_metadata.flushPartialPacket = false; | ||||
| 	rx_metadata.waitForTimestamp = false; | ||||
|  | ||||
| 	ts_initial = 0; | ||||
|  | ||||
| 	while (!ts_initial || (num_pkts-- > 0)) { | ||||
| 		rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100); | ||||
| 		LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp; | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device receive timed out"; | ||||
| 			delete[] buffer; | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		ts_initial = rx_metadata.timestamp + len; | ||||
| 	} | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl; | ||||
| 	delete[] buffer; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan) | ||||
| { | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	idx = get_ant_idx(ant, LMS_CH_RX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid Rx Antenna"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Unable to set Rx Antenna"; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string LMSDevice::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Rx Antenna"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Rx Antenna List"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	return name_list[idx]; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan) | ||||
| { | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	idx = get_ant_idx(ant, LMS_CH_TX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid Rx Antenna"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Unable to set Rx Antenna"; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string LMSDevice::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Tx Antenna"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Tx Antenna List"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	return name_list[idx]; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::requiresRadioAlign() | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| GSM::Time LMSDevice::minLatency() { | ||||
| 	/* Empirical data from a handful of | ||||
| 	relatively recent machines shows that the B100 will underrun when | ||||
| 	the transmit threshold is reduced to a time of 6 and a half frames, | ||||
| 	so we set a minimum 7 frame threshold. */ | ||||
| 	return GSM::Time(6,7); | ||||
| } | ||||
|  | ||||
| void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun) | ||||
| { | ||||
| 	lms_stream_status_t status; | ||||
| 	if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) { | ||||
| 		if (status.underrun > m_last_rx_underruns[chan]) | ||||
| 			*underrun = true; | ||||
| 		m_last_rx_underruns[chan] = status.underrun; | ||||
|  | ||||
| 		if (status.overrun > m_last_rx_overruns[chan]) | ||||
| 			*overrun = true; | ||||
| 		m_last_rx_overruns[chan] = status.overrun; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NOTE: Assumes sequential reads | ||||
| int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun, | ||||
| 			   TIMESTAMP timestamp, bool * underrun, unsigned *RSSI) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	unsigned int i; | ||||
| 	lms_stream_meta_t rx_metadata = {}; | ||||
| 	rx_metadata.flushPartialPacket = false; | ||||
| 	rx_metadata.waitForTimestamp = false; | ||||
| 	rx_metadata.timestamp = 0; | ||||
|  | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	*overrun = false; | ||||
| 	*underrun = false; | ||||
| 	for (i = 0; i<chans; i++) { | ||||
| 		thread_enable_cancel(false); | ||||
| 		rc = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len, &rx_metadata, 100); | ||||
| 		update_stream_stats(i, underrun, overrun); | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device receive timed out (" << rc << " vs exp " << len << ")."; | ||||
| 			thread_enable_cancel(true); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (timestamp != (TIMESTAMP)rx_metadata.timestamp) | ||||
| 			LOGC(DDEV, ALERT) << "chan "<< i << " recv buffer of len " << rc << " expect " << std::hex << timestamp << " got " << std::hex << (TIMESTAMP)rx_metadata.timestamp << " (" << std::hex << rx_metadata.timestamp <<") diff=" << rx_metadata.timestamp - timestamp; | ||||
| 		thread_enable_cancel(true); | ||||
| 	} | ||||
|  | ||||
| 	samplesRead += rc; | ||||
|  | ||||
| 	if (((TIMESTAMP) rx_metadata.timestamp) < timestamp) | ||||
| 		rc = 0; | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| int LMSDevice::writeSamples(std::vector < short *>&bufs, int len, | ||||
| 			    bool * underrun, unsigned long long timestamp, | ||||
| 			    bool isControl) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	unsigned int i; | ||||
| 	lms_stream_status_t status; | ||||
| 	lms_stream_meta_t tx_metadata = {}; | ||||
| 	tx_metadata.flushPartialPacket = false; | ||||
| 	tx_metadata.waitForTimestamp = true; | ||||
| 	tx_metadata.timestamp = timestamp - ts_offset;	/* Shift Tx time by offset */ | ||||
|  | ||||
| 	if (isControl) { | ||||
| 		LOGC(DDEV, ERR) << "Control packets not supported"; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	*underrun = false; | ||||
|  | ||||
| 	for (i = 0; i<chans; i++) { | ||||
| 		LOGC(DDEV, DEBUG) << "chan "<< i << " send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp; | ||||
| 		thread_enable_cancel(false); | ||||
| 		rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100); | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device send timed out"; | ||||
| 		} | ||||
|  | ||||
| 		if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) { | ||||
| 			if (status.underrun > m_last_tx_underruns[i]) | ||||
| 				*underrun = true; | ||||
| 			m_last_tx_underruns[i] = status.underrun; | ||||
| 		} | ||||
| 		thread_enable_cancel(true); | ||||
| 	} | ||||
|  | ||||
| 	samplesWritten += rc; | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setTxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Tx Freq to " << wFreq << " Hz"; | ||||
|  | ||||
| 	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) { | ||||
| 		LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Tx Freq to " << wFreq << " Hz"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setRxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Rx Freq to " << wFreq << " Hz"; | ||||
|  | ||||
| 	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) { | ||||
| 		LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Rx Freq to " << wFreq << " Hz"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	if (tx_sps != rx_sps) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (lo_offset != 0.0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); | ||||
| } | ||||
							
								
								
									
										211
									
								
								Transceiver52M/device/lms/LMSDevice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								Transceiver52M/device/lms/LMSDevice.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
| /* | ||||
| * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| * 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 _LMS_DEVICE_H_ | ||||
| #define _LMS_DEVICE_H_ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include "radioDevice.h" | ||||
|  | ||||
| #include <sys/time.h> | ||||
| #include <math.h> | ||||
| #include <limits.h> | ||||
| #include <string> | ||||
| #include <iostream> | ||||
| #include <lime/LimeSuite.h> | ||||
|  | ||||
| /* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q | ||||
|  * channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) = | ||||
|  * 0.7071.... to get an amplitude of 1 of the complex signal: | ||||
|  * 	A^2 = I^2 + Q^2 | ||||
|  * 	A^2 = (1/sqrt(2))^2 + (1/sqrt(2))^2 | ||||
|  * 	A^2 = 1/2 + 1/2 | ||||
|  * 	A^2 = 1 */ | ||||
| #define LIMESDR_TX_AMPL  0.707 | ||||
|  | ||||
| /** A class to handle a LimeSuite supported device */ | ||||
| class LMSDevice:public RadioDevice { | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	static constexpr double masterClockRate = 52.0e6; | ||||
|  | ||||
| 	lms_device_t *m_lms_dev; | ||||
| 	std::vector<lms_stream_t> m_lms_stream_rx; | ||||
| 	std::vector<lms_stream_t> m_lms_stream_tx; | ||||
|  | ||||
| 	std::vector<uint32_t> m_last_rx_underruns; | ||||
| 	std::vector<uint32_t> m_last_rx_overruns; | ||||
| 	std::vector<uint32_t> m_last_tx_underruns; | ||||
| 	std::vector<uint32_t> m_last_tx_overruns; | ||||
|  | ||||
| 	double actualSampleRate;	///< the actual USRP sampling rate | ||||
|  | ||||
| 	unsigned long long samplesRead;	///< number of samples read from LMS | ||||
| 	unsigned long long samplesWritten;	///< number of samples sent to LMS | ||||
|  | ||||
| 	bool started;		///< flag indicates LMS has started | ||||
| 	bool skipRx;		///< set if LMS is transmit-only. | ||||
|  | ||||
| 	TIMESTAMP ts_initial, ts_offset; | ||||
|  | ||||
| 	double rxGain; | ||||
|  | ||||
| 	int get_ant_idx(const std::string & name, bool dir_tx, size_t chan); | ||||
| 	bool flush_recv(size_t num_pkts); | ||||
| 	void update_stream_stats(size_t chan, bool * underrun, bool * overrun); | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/** Object constructor */ | ||||
| 	LMSDevice(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); | ||||
| 	~LMSDevice(); | ||||
|  | ||||
| 	/** Instantiate the LMS */ | ||||
| 	int open(const std::string &args, int ref, bool swap_channels); | ||||
|  | ||||
| 	/** Start the LMS */ | ||||
| 	bool start(); | ||||
|  | ||||
| 	/** Stop the LMS */ | ||||
| 	bool stop(); | ||||
|  | ||||
| 	/** Set priority not supported */ | ||||
| 	void setPriority(float prio = 0.5) { | ||||
| 	} | ||||
|  | ||||
| 	enum TxWindowType getWindowType() { | ||||
| 		return TX_WINDOW_LMS1; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	Read samples from the LMS. | ||||
| 	@param buf preallocated buf to contain read result | ||||
| 	@param len number of samples desired | ||||
| 	@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough | ||||
| 	@param timestamp The timestamp of the first samples to be read | ||||
| 	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough | ||||
| 	@param RSSI The received signal strength of the read result | ||||
| 	@return The number of samples actually read | ||||
| 	*/ | ||||
| 	int readSamples(std::vector < short *>&buf, int len, bool * overrun, | ||||
| 			TIMESTAMP timestamp = 0xffffffff, bool * underrun = | ||||
| 			NULL, unsigned *RSSI = NULL); | ||||
| 	/** | ||||
| 	Write samples to the LMS. | ||||
| 	@param buf Contains the data to be written. | ||||
| 	@param len number of samples to write. | ||||
| 	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough | ||||
| 	@param timestamp The timestamp of the first sample of the data buffer. | ||||
| 	@param isControl Set if data is a control packet, e.g. a ping command | ||||
| 	@return The number of samples actually written | ||||
| 	*/ | ||||
| 	int writeSamples(std::vector < short *>&bufs, int len, bool * underrun, | ||||
| 			 TIMESTAMP timestamp = 0xffffffff, bool isControl = | ||||
| 			 false); | ||||
|  | ||||
| 	/** Update the alignment between the read and write timestamps */ | ||||
| 	bool updateAlignment(TIMESTAMP timestamp); | ||||
|  | ||||
| 	/** Set the transmitter frequency */ | ||||
| 	bool setTxFreq(double wFreq, size_t chan = 0); | ||||
|  | ||||
| 	/** Set the receiver frequency */ | ||||
| 	bool setRxFreq(double wFreq, size_t chan = 0); | ||||
|  | ||||
| 	/** Returns the starting write Timestamp*/ | ||||
| 	TIMESTAMP initialWriteTimestamp(void) { | ||||
| 		return ts_initial; | ||||
| 	} | ||||
|  | ||||
| 	/** Returns the starting read Timestamp*/ | ||||
| 	TIMESTAMP initialReadTimestamp(void) { | ||||
| 		return ts_initial; | ||||
| 	} | ||||
|  | ||||
| 	/** returns the full-scale transmit amplitude **/ | ||||
| 	double fullScaleInputValue() { | ||||
| 		return(double) SHRT_MAX * LIMESDR_TX_AMPL; | ||||
| 	} | ||||
|  | ||||
| 	/** returns the full-scale receive amplitude **/ | ||||
| 	double fullScaleOutputValue() { | ||||
| 		return (double) SHRT_MAX; | ||||
| 	} | ||||
|  | ||||
| 	/** sets the receive chan gain, returns the gain setting **/ | ||||
| 	double setRxGain(double dB, size_t chan = 0); | ||||
|  | ||||
| 	/** get the current receive gain */ | ||||
| 	double getRxGain(size_t chan = 0) { | ||||
| 		return rxGain; | ||||
| 	} | ||||
|  | ||||
| 	/** return maximum Rx Gain **/ | ||||
| 	double maxRxGain(void); | ||||
|  | ||||
| 	/** return minimum Rx Gain **/ | ||||
| 	double minRxGain(void); | ||||
|  | ||||
| 	/** sets the transmit chan gain, returns the gain setting **/ | ||||
| 	double setTxGain(double dB, size_t chan = 0); | ||||
|  | ||||
| 	/** return maximum Tx Gain **/ | ||||
| 	double maxTxGain(void); | ||||
|  | ||||
| 	/** 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 // _LMS_DEVICE_H_ | ||||
							
								
								
									
										10
									
								
								Transceiver52M/device/lms/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Transceiver52M/device/lms/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS) | ||||
|  | ||||
| noinst_HEADERS = LMSDevice.h | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = LMSDevice.cpp | ||||
| @@ -18,11 +18,19 @@ | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "GSMCommon.h" | ||||
| #include "Logger.h" | ||||
| 
 | ||||
| extern "C" { | ||||
| #include "config_defs.h" | ||||
| } | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| 
 | ||||
| #define GSMRATE       1625e3/6 | ||||
| #define GSMRATE       (1625e3/6) | ||||
| #define MCBTS_SPACING  800000.0 | ||||
| 
 | ||||
| /** a 64-bit virtual timestamp for radio data */ | ||||
| typedef unsigned long long TIMESTAMP; | ||||
| @@ -32,16 +40,23 @@ class RadioDevice { | ||||
| 
 | ||||
|   public: | ||||
|   /* Available transport bus types */ | ||||
|   enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; | ||||
|   enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 }; | ||||
| 
 | ||||
|   /* Radio interface types */ | ||||
|   enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY }; | ||||
|   enum InterfaceType { | ||||
|     NORMAL, | ||||
|     RESAMP_64M, | ||||
|     RESAMP_100M, | ||||
|     MULTI_ARFCN, | ||||
|   }; | ||||
| 
 | ||||
|   static RadioDevice *make(size_t tx_sps, size_t rx_sps = 1, size_t chans = 1, | ||||
|                            bool diversity = false, double offset = 0.0); | ||||
|   static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, | ||||
|                            size_t chans = 1, double offset = 0.0, | ||||
|                            const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""), | ||||
|                            const std::vector<std::string>& rx_paths = std::vector<std::string>(1, "")); | ||||
| 
 | ||||
|   /** Initialize the USRP */ | ||||
|   virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0; | ||||
|   virtual int open(const std::string &args, int ref, bool swap_channels)=0; | ||||
| 
 | ||||
|   virtual ~RadioDevice() { } | ||||
| 
 | ||||
| @@ -124,6 +139,24 @@ class RadioDevice { | ||||
|   /** return minimum Tx Gain **/ | ||||
|   virtual double minTxGain(void) = 0; | ||||
| 
 | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0; | ||||
| 
 | ||||
|   /** return the used RX path */ | ||||
|   virtual std::string getRxAntenna(size_t chan = 0) = 0; | ||||
| 
 | ||||
|   /** sets the RX path to use, returns true if successful and false otherwise */ | ||||
|   virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0; | ||||
| 
 | ||||
|   /** return the used RX path */ | ||||
|   virtual std::string getTxAntenna(size_t chan = 0) = 0; | ||||
| 
 | ||||
|   /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   virtual bool requiresRadioAlign() = 0; | ||||
| 
 | ||||
|   /** Minimum latency that the device can achieve */ | ||||
|   virtual GSM::Time minLatency() = 0; | ||||
| 
 | ||||
|   /** Return internal status values */ | ||||
|   virtual double getTxFreq(size_t chan = 0) = 0; | ||||
|   virtual double getRxFreq(size_t chan = 0) = 0; | ||||
| @@ -131,6 +164,47 @@ class RadioDevice { | ||||
|   virtual double numberRead()=0; | ||||
|   virtual double numberWritten()=0; | ||||
| 
 | ||||
|   protected: | ||||
|   size_t tx_sps, rx_sps; | ||||
|   InterfaceType iface; | ||||
|   size_t chans; | ||||
|   double lo_offset; | ||||
|   std::vector<std::string> tx_paths, rx_paths; | ||||
| 
 | ||||
|   RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset, | ||||
|               const std::vector<std::string>& tx_paths, | ||||
|               const std::vector<std::string>& rx_paths): | ||||
| 		tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset), | ||||
| 		tx_paths(tx_paths), rx_paths(rx_paths) | ||||
| 	{ } | ||||
| 
 | ||||
|   bool set_antennas() { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < tx_paths.size(); i++) { | ||||
| 		if (tx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 		if (!setTxAntenna(tx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < rx_paths.size(); i++) { | ||||
| 		if (rx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 		if (!setRxAntenna(rx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	LOG(INFO) << "Antennas configured successfully"; | ||||
| 	return true; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										8
									
								
								Transceiver52M/device/uhd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Transceiver52M/device/uhd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS) | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = UHDDevice.cpp | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								Transceiver52M/device/usrp1/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Transceiver52M/device/usrp1/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS) | ||||
|  | ||||
| noinst_HEADERS = USRPDevice.h | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = USRPDevice.cpp | ||||
| @@ -27,17 +27,16 @@ | ||||
| 	Compilation Flags | ||||
| 
 | ||||
| 	SWLOOPBACK	compile for software loopback testing | ||||
| */  | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include "Logger.h" | ||||
| #include "Threads.h" | ||||
| #include "USRPDevice.h" | ||||
| 
 | ||||
| #include <Logger.h> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| @@ -59,12 +58,15 @@ const dboardConfigType dboardConfig = TXA_RXB; | ||||
| 
 | ||||
| const double USRPDevice::masterClockRate = 52.0e6; | ||||
| 
 | ||||
| USRPDevice::USRPDevice(size_t sps) | ||||
| 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) | ||||
| { | ||||
|   LOG(INFO) << "creating USRP device..."; | ||||
|   LOGC(DDEV, INFO) << "creating USRP device..."; | ||||
| 
 | ||||
|   this->sps = sps; | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps)); | ||||
|   actualSampleRate = masterClockRate/decimRate; | ||||
|   rxGain = 0; | ||||
| 
 | ||||
| @@ -74,14 +76,14 @@ USRPDevice::USRPDevice(size_t sps) | ||||
|    * split sample rate Tx/Rx - 4/1 sps we need to need to | ||||
|    * compensate for advance rather than delay. | ||||
|    */ | ||||
|   if (sps == 1) | ||||
|   if (tx_sps == 1) | ||||
|     pingOffset = 272; | ||||
|   else if (sps == 4) | ||||
|   else if (tx_sps == 4) | ||||
|     pingOffset = 269 - 7500; | ||||
|   else | ||||
|     pingOffset = 0; | ||||
| 
 | ||||
| #ifdef SWLOOPBACK  | ||||
| #ifdef SWLOOPBACK | ||||
|   samplePeriod = 1.0e6/actualSampleRate; | ||||
|   loopbackBufferSize = 0; | ||||
|   gettimeofday(&lastReadTime,NULL); | ||||
| @@ -89,38 +91,36 @@ USRPDevice::USRPDevice(size_t sps) | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| int USRPDevice::open(const std::string &, bool, bool) | ||||
| int USRPDevice::open(const std::string &, int, bool) | ||||
| { | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
|   LOG(INFO) << "opening USRP device.."; | ||||
| #ifndef SWLOOPBACK  | ||||
|   LOGC(DDEV, 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 * sps, 1, -1, | ||||
|                                         0, decimRate * tx_sps, 1, -1, | ||||
|                                         usrp_standard_rx::FPGA_MODE_NORMAL, | ||||
|                                         1024, 16 * 8, rbf)); | ||||
|     m_uRx->set_fpga_master_clock_freq(masterClockRate); | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOG(ALERT) << "make failed on Rx"; | ||||
|     LOGC(DDEV, ALERT) << "make failed on Rx"; | ||||
|     m_uRx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() | ||||
|     LOGC(DDEV, 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( | ||||
| @@ -130,22 +130,22 @@ int USRPDevice::open(const std::string &, bool, bool) | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOG(ALERT) << "make failed on Tx"; | ||||
|     LOGC(DDEV, ALERT) << "make failed on Tx"; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|     LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|                << ", desired clock freq = " << masterClockRate; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
|    | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|   switch (dboardConfig) { | ||||
| @@ -176,20 +176,20 @@ int USRPDevice::open(const std::string &, bool, bool) | ||||
|   samplesRead = 0; | ||||
|   samplesWritten = 0; | ||||
|   started = false; | ||||
|    | ||||
| 
 | ||||
|   return NORMAL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| bool USRPDevice::start()  | ||||
| bool USRPDevice::start() | ||||
| { | ||||
|   LOG(INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK  | ||||
|   if (!m_uRx && !skipRx) return false; | ||||
|   LOGC(DDEV, INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
|    | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
| 
 | ||||
|   m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
| 
 | ||||
|   writeLock.lock(); | ||||
| @@ -218,11 +218,8 @@ 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); | ||||
| @@ -230,14 +227,14 @@ bool USRPDevice::start() | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::stop()  | ||||
| bool USRPDevice::stop() | ||||
| { | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
|    | ||||
| 
 | ||||
|   delete[] currData; | ||||
|    | ||||
| 
 | ||||
|   started = !(m_uRx->stop() && m_uTx->stop()); | ||||
|   return !started; | ||||
| #else | ||||
| @@ -258,7 +255,7 @@ double USRPDevice::minTxGain() | ||||
| double USRPDevice::maxRxGain() | ||||
| { | ||||
|   return m_dbRx->gain_max(); | ||||
| }  | ||||
| } | ||||
| 
 | ||||
| double USRPDevice::minRxGain() | ||||
| { | ||||
| @@ -268,7 +265,7 @@ double USRPDevice::minRxGain() | ||||
| double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -278,10 +275,10 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
|   if (dB < minTxGain()) | ||||
|     dB = minTxGain(); | ||||
| 
 | ||||
|   LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
|   LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbTx->set_gain(dB)) | ||||
|     LOG(ERR) << "Error setting TX gain"; | ||||
|     LOGC(DDEV, ERR) << "Error setting TX gain"; | ||||
| 
 | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
| @@ -292,7 +289,7 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| double USRPDevice::setRxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -304,38 +301,86 @@ double USRPDevice::setRxGain(double dB, size_t chan) | ||||
|   if (dB < minRxGain()) | ||||
|     dB = minRxGain(); | ||||
| 
 | ||||
|   LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
|   LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbRx->set_gain(dB)) | ||||
|     LOG(ERR) << "Error setting RX gain"; | ||||
|     LOGC(DDEV, 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; | ||||
|   } | ||||
| 
 | ||||
|   if (underrun) *underrun = false; | ||||
|   | ||||
| 
 | ||||
|   uint32_t readBuf[2000]; | ||||
|   | ||||
| 
 | ||||
|   while (1) { | ||||
|     //guestimate USB read size
 | ||||
|     int readLen=0; | ||||
| @@ -345,21 +390,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; | ||||
|       LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
|       LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
| 
 | ||||
|       bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); | ||||
|       if (incrementHi32 && (timeStart!=0)) { | ||||
|            LOG(DEBUG) << "high 32 increment!!!"; | ||||
|            LOGC(DDEV, DEBUG) << "high 32 increment!!!"; | ||||
|            hi32Timestamp++; | ||||
|       } | ||||
|       pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; | ||||
| @@ -371,24 +416,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { | ||||
|           timestamp -= timestampOffset; | ||||
| 	  timestampOffset = pktTimestamp - pingTimestamp + pingOffset; | ||||
| 	  LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
| 	  LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
|           timestamp += timestampOffset; | ||||
| 	  isAligned = true; | ||||
| 	} | ||||
| 	continue; | ||||
|       } | ||||
|       if (chan != 0) { | ||||
| 	LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	continue; | ||||
|       } | ||||
|       if ((word0 >> 28) & 0x04) { | ||||
| 	if (underrun) *underrun = true;  | ||||
| 	LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
| 	if (underrun) *underrun = true; | ||||
| 	LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
|       } | ||||
|       if (RSSI) *RSSI = (word0 >> 21) & 0x3f; | ||||
|        | ||||
| 
 | ||||
|       if (!isAligned) continue; | ||||
|        | ||||
| 
 | ||||
|       unsigned cursorStart = pktTimestamp - timeStart + dataStart; | ||||
|       while (cursorStart*2 > currDataSize) { | ||||
| 	cursorStart -= currDataSize/2; | ||||
| @@ -401,25 +446,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); | ||||
| 
 | ||||
|       LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
|       LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|     }	 | ||||
|   }      | ||||
|   | ||||
|   // copy desired data to buf
 | ||||
|   unsigned bufStart = dataStart+(timestamp-timeStart); | ||||
|   if (bufStart + len < currDataSize/2) {  | ||||
|     LOG(DEBUG) << "bufStart: " << bufStart; | ||||
|   if (bufStart + len < currDataSize/2) { | ||||
|     LOGC(DDEV, DEBUG) << "bufStart: " << bufStart; | ||||
|     memcpy(buf,data+bufStart*2,len*2*sizeof(short)); | ||||
|     memset(data+bufStart*2,0,len*2*sizeof(short)); | ||||
|   } | ||||
|   else { | ||||
|     LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     unsigned firstLength = (currDataSize/2-bufStart); | ||||
|     LOG(DEBUG) << "firstLength: " << firstLength; | ||||
|     LOGC(DDEV, 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)); | ||||
| @@ -429,21 +474,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|   timeStart = timestamp + len; | ||||
| 
 | ||||
|   return len; | ||||
|    | ||||
| 
 | ||||
| #else | ||||
|   if (loopbackBufferSize < 2) return 0; | ||||
|   int numSamples = 0; | ||||
|   struct timeval currTime; | ||||
|   gettimeofday(&currTime,NULL); | ||||
|   double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +  | ||||
|   double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 + | ||||
|     (currTime.tv_usec - lastReadTime.tv_usec); | ||||
|   if (timeElapsed < samplePeriod) {return 0;} | ||||
|   int numSamplesToRead = (int) floor(timeElapsed/samplePeriod); | ||||
|   if (numSamplesToRead < len) return 0; | ||||
|    | ||||
| 
 | ||||
|   if (numSamplesToRead > len) numSamplesToRead = len; | ||||
|   if (numSamplesToRead > loopbackBufferSize/2) { | ||||
|     firstRead =false;  | ||||
|     firstRead =false; | ||||
|     numSamplesToRead = loopbackBufferSize/2; | ||||
|   } | ||||
|   memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead); | ||||
| @@ -461,7 +506,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|     firstRead = true; | ||||
|   } | ||||
|   samplesRead += numSamples; | ||||
|    | ||||
| 
 | ||||
|   return numSamples; | ||||
| #endif | ||||
| } | ||||
| @@ -472,7 +517,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
| { | ||||
|   writeLock.lock(); | ||||
| 
 | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uTx) | ||||
|     return 0; | ||||
| 
 | ||||
| @@ -519,14 +564,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len, | ||||
|   memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len); | ||||
|   samplesWritten += retVal; | ||||
|   loopbackBufferSize += retVal*2; | ||||
|     | ||||
| 
 | ||||
|   return retVal; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::updateAlignment(TIMESTAMP timestamp)  | ||||
| bool USRPDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| { | ||||
| #ifndef SWLOOPBACK  | ||||
| #ifndef SWLOOPBACK | ||||
|   short data[] = {0x00,0x02,0x00,0x00}; | ||||
|   uint32_t *wordPtr = (uint32_t *) data; | ||||
|   *wordPtr = host_to_usrp_u32(*wordPtr); | ||||
| @@ -543,25 +588,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) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { | ||||
|     LOG(INFO) << "set TX: " << wFreq << std::endl | ||||
|     LOGC(DDEV, 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 { | ||||
|     LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl | ||||
|     LOGC(DDEV, 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; | ||||
| @@ -574,19 +619,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan) | ||||
|   usrp_tune_result result; | ||||
| 
 | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { | ||||
|     LOG(INFO) << "set RX: " << wFreq << std::endl | ||||
|     LOGC(DDEV, 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 { | ||||
|     LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl | ||||
|     LOGC(DDEV, 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; | ||||
| @@ -601,7 +646,21 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;}; | ||||
| #endif | ||||
| 
 | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       size_t chans, bool diversity, double) | ||||
| 			       InterfaceType iface, size_t chans, double lo_offset, | ||||
| 			       const std::vector<std::string>& tx_paths, | ||||
| 			       const std::vector<std::string>& rx_paths) | ||||
| { | ||||
| 	return new USRPDevice(tx_sps); | ||||
| 	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); | ||||
| } | ||||
| @@ -48,7 +48,6 @@ 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
 | ||||
| 
 | ||||
| @@ -56,7 +55,6 @@ private: | ||||
|   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); | ||||
| @@ -83,10 +81,10 @@ private: | ||||
| 
 | ||||
|   double rxGain; | ||||
| 
 | ||||
| #ifdef SWLOOPBACK  | ||||
| #ifdef SWLOOPBACK | ||||
|   short loopbackBuffer[1000000]; | ||||
|   int loopbackBufferSize; | ||||
|   double samplePeriod;  | ||||
|   double samplePeriod; | ||||
| 
 | ||||
|   struct timeval startTime; | ||||
|   struct timeval lastReadTime; | ||||
| @@ -96,10 +94,12 @@ private: | ||||
|  public: | ||||
| 
 | ||||
|   /** Object constructor */ | ||||
|   USRPDevice(size_t sps); | ||||
|   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); | ||||
| 
 | ||||
|   /** Instantiate the USRP */ | ||||
|   int open(const std::string &, bool, bool); | ||||
|   int open(const std::string &, int, bool); | ||||
| 
 | ||||
|   /** Start the USRP */ | ||||
|   bool start(); | ||||
| @@ -179,14 +179,30 @@ 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_
 | ||||
| 
 | ||||
| @@ -22,191 +22,59 @@ | ||||
|  | ||||
| #include "Transceiver.h" | ||||
| #include "radioDevice.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include <time.h> | ||||
| #include <signal.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <getopt.h> | ||||
| #include <sched.h> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
| #include <iostream> | ||||
|  | ||||
| #include <GSMCommon.h> | ||||
| #include <Logger.h> | ||||
| #include <Configuration.h> | ||||
|  | ||||
| /* 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 | ||||
| extern "C" { | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/ports.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| #include <osmocom/ctrl/control_vty.h> | ||||
| #include <osmocom/ctrl/ports.h> | ||||
| #include <osmocom/ctrl/control_if.h> | ||||
| #include <osmocom/vty/stats.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| /* | ||||
|  * 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 | ||||
| #include "convolve.h" | ||||
| #include "convert.h" | ||||
| #include "trx_vty.h" | ||||
| #include "debug.h" | ||||
| #include "osmo_signal.h" | ||||
| } | ||||
|  | ||||
| /* Default configuration parameters | ||||
|  *     Note that these values are only used if the particular key does not | ||||
|  *     exist in the configuration database. IP port and address values will | ||||
|  *     typically be overwritten by the OpenBTS.db values. Other values will | ||||
|  *     not be in the database by default. | ||||
|  */ | ||||
| #define DEFAULT_TRX_PORT	5700 | ||||
| #define DEFAULT_TRX_IP		"127.0.0.1" | ||||
| #define DEFAULT_EXTREF		false | ||||
| #define DEFAULT_DIVERSITY	false | ||||
| #define DEFAULT_CHANS		1 | ||||
| #define DEFAULT_CONFIG_FILE	"osmo-trx.cfg" | ||||
|  | ||||
| struct trx_config { | ||||
| 	std::string log_level; | ||||
| 	std::string addr; | ||||
| 	std::string dev_args; | ||||
| 	unsigned port; | ||||
| 	unsigned tx_sps; | ||||
| 	unsigned rx_sps; | ||||
| 	unsigned chans; | ||||
| 	unsigned rtsc; | ||||
| 	bool extref; | ||||
| 	Transceiver::FillerType filler; | ||||
| 	bool diversity; | ||||
| 	double offset; | ||||
| 	double rssi_offset; | ||||
| 	bool swap_channels; | ||||
| 	bool edge; | ||||
| }; | ||||
| #define charp2str(a) ((a) ? std::string(a) : std::string("")) | ||||
|  | ||||
| ConfigurationTable gConfig; | ||||
| static char* config_file = (char*)DEFAULT_CONFIG_FILE; | ||||
|  | ||||
| volatile bool gshutdown = false; | ||||
|  | ||||
| /* Run sanity check on configuration table | ||||
|  *     The global table constructor cannot provide notification in the | ||||
|  *     event of failure. Make sure that we can access the database, | ||||
|  *     write to it, and that it contains the bare minimum required keys. | ||||
|  */ | ||||
| bool testConfig() | ||||
| { | ||||
| 	int val = 9999; | ||||
| 	std::string test = "asldfkjsaldkf"; | ||||
| 	const char *key = "Log.Level"; | ||||
| static void *tall_trx_ctx; | ||||
| static struct trx_ctx *g_trx_ctx; | ||||
| static struct ctrl_handle *g_ctrlh; | ||||
|  | ||||
| 	/* Attempt to query */ | ||||
| 	try { | ||||
| 		gConfig.getStr(key); | ||||
| 	} catch (...) { | ||||
| 		std::cerr << std::endl; | ||||
| 		std::cerr << "Config: Failed query required key " << key | ||||
| 			  << std::endl; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/* Attempt to set a test value in the global config */ | ||||
| 	if (!gConfig.set(test, val)) { | ||||
| 		std::cerr << std::endl; | ||||
| 		std::cerr << "Config: Failed to set test key" << std::endl; | ||||
| 		return false; | ||||
| 	} else { | ||||
| 		gConfig.remove(test); | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Setup configuration values | ||||
|  *     Don't query the existence of the Log.Level because it's a | ||||
|  *     mandatory value. That is, if it doesn't exist, the configuration | ||||
|  *     table will crash or will have already crashed. Everything else we | ||||
|  *     can survive without and use default values if the database entries | ||||
|  *     are empty. | ||||
|  */ | ||||
| bool trx_setup_config(struct trx_config *config) | ||||
| { | ||||
| 	std::string refstr, fillstr, divstr, edgestr; | ||||
|  | ||||
| 	if (!testConfig()) | ||||
| 		return false; | ||||
|  | ||||
| 	if (config->log_level == "") | ||||
| 		config->log_level = gConfig.getStr("Log.Level"); | ||||
|  | ||||
| 	if (!config->port) { | ||||
| 		if (gConfig.defines("TRX.Port")) | ||||
| 			config->port = gConfig.getNum("TRX.Port"); | ||||
| 		else | ||||
| 			config->port = DEFAULT_TRX_PORT; | ||||
| 	} | ||||
|  | ||||
| 	if (config->addr == "") { | ||||
| 		if (gConfig.defines("TRX.IP")) | ||||
| 			config->addr = gConfig.getStr("TRX.IP"); | ||||
| 		else | ||||
| 			config->addr = DEFAULT_TRX_IP; | ||||
| 	} | ||||
|  | ||||
| 	if (!config->extref) { | ||||
| 		if (gConfig.defines("TRX.Reference")) | ||||
| 			config->extref = gConfig.getNum("TRX.Reference"); | ||||
| 		else | ||||
| 			config->extref = DEFAULT_EXTREF; | ||||
| 	} | ||||
|  | ||||
| 	if (!config->diversity) { | ||||
| 		if (gConfig.defines("TRX.Diversity")) | ||||
| 			config->diversity = gConfig.getNum("TRX.Diversity"); | ||||
| 		else | ||||
| 			config->diversity = DEFAULT_DIVERSITY; | ||||
| 	} | ||||
|  | ||||
| 	/* Diversity only supported on 2 channels */ | ||||
| 	if (config->diversity) | ||||
| 		config->chans = 2; | ||||
|  | ||||
| 	edgestr = config->edge ? "Enabled" : "Disabled"; | ||||
| 	refstr = config->extref ? "Enabled" : "Disabled"; | ||||
| 	divstr = config->diversity ? "Enabled" : "Disabled"; | ||||
| 	switch (config->filler) { | ||||
| 	case Transceiver::FILLER_DUMMY: | ||||
| 		fillstr = "Dummy bursts"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_ZERO: | ||||
| 		fillstr = "Disabled"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_NORM_RAND: | ||||
| 		fillstr = "Normal busrts with random payload"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_EDGE_RAND: | ||||
| 		fillstr = "EDGE busrts with random payload"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_ACCESS_RAND: | ||||
| 		fillstr = "Access busrts with random payload"; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	std::ostringstream ost(""); | ||||
| 	ost << "Config Settings" << std::endl; | ||||
| 	ost << "   Log Level............... " << config->log_level << std::endl; | ||||
| 	ost << "   Device args............. " << config->dev_args << std::endl; | ||||
| 	ost << "   TRX Base Port........... " << config->port << std::endl; | ||||
| 	ost << "   TRX Address............. " << config->addr << std::endl; | ||||
| 	ost << "   Channels................ " << config->chans << std::endl; | ||||
| 	ost << "   Tx Samples-per-Symbol... " << config->tx_sps << std::endl; | ||||
| 	ost << "   Rx Samples-per-Symbol... " << config->rx_sps << std::endl; | ||||
| 	ost << "   EDGE support............ " << edgestr << std::endl; | ||||
| 	ost << "   External Reference...... " << refstr << std::endl; | ||||
| 	ost << "   C0 Filler Table......... " << fillstr << std::endl; | ||||
| 	ost << "   Diversity............... " << divstr << std::endl; | ||||
| 	ost << "   Tuning offset........... " << config->offset << std::endl; | ||||
| 	ost << "   RSSI to dBm offset...... " << config->rssi_offset << std::endl; | ||||
| 	ost << "   Swap channels........... " << config->swap_channels << std::endl; | ||||
| 	std::cout << ost << std::endl; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| static RadioDevice *usrp; | ||||
| static RadioInterface *radio; | ||||
| static Transceiver *transceiver; | ||||
|  | ||||
| /* Create radio interface | ||||
|  *     The interface consists of sample rate changes, frequency shifts, | ||||
| @@ -215,28 +83,24 @@ bool trx_setup_config(struct trx_config *config) | ||||
|  *     The radio interface connects the main transceiver with the device | ||||
|  *     object, which may be operating some other rate. | ||||
|  */ | ||||
| RadioInterface *makeRadioInterface(struct trx_config *config, | ||||
| RadioInterface *makeRadioInterface(struct trx_ctx *trx, | ||||
|                                    RadioDevice *usrp, int type) | ||||
| { | ||||
| 	RadioInterface *radio = NULL; | ||||
|  | ||||
| 	if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) { | ||||
| 		LOG(ALERT) << "Unsupported radio interface configuration"; | ||||
| 	} | ||||
|  | ||||
| 	switch (type) { | ||||
| 	case RadioDevice::NORMAL: | ||||
| 		radio = new RadioInterface(usrp, config->tx_sps, | ||||
| 					   config->rx_sps, config->chans); | ||||
| 		radio = new RadioInterface(usrp, trx->cfg.tx_sps, | ||||
| 					   trx->cfg.rx_sps, trx->cfg.num_chans); | ||||
| 		break; | ||||
| 	case RadioDevice::RESAMP_64M: | ||||
| 	case RadioDevice::RESAMP_100M: | ||||
| 		radio = new RadioInterfaceResamp(usrp, config->tx_sps, | ||||
| 						 config->chans); | ||||
| 		radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps, | ||||
| 						 trx->cfg.rx_sps); | ||||
| 		break; | ||||
| 	case RadioDevice::DIVERSITY: | ||||
| 		radio = new RadioInterfaceDiversity(usrp, config->tx_sps, | ||||
| 						    config->chans); | ||||
| 	case RadioDevice::MULTI_ARFCN: | ||||
| 		radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps, | ||||
| 						trx->cfg.rx_sps, trx->cfg.num_chans); | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOG(ALERT) << "Unsupported radio interface configuration"; | ||||
| @@ -251,231 +115,477 @@ RadioInterface *makeRadioInterface(struct trx_config *config, | ||||
| 	return radio; | ||||
| } | ||||
|  | ||||
| /* Callback function to be called every time we receive a signal from TRANSC */ | ||||
| static int transc_sig_cb(unsigned int subsys, unsigned int signal, | ||||
| 		     void *handler_data, void *signal_data) | ||||
| { | ||||
| 	switch (signal) { | ||||
| 	case S_TRANSC_STOP_REQUIRED: | ||||
| 		gshutdown = true; | ||||
|                 break; | ||||
| 	default: | ||||
|                 break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Create transceiver core | ||||
|  *     The multi-threaded modem core operates at multiples of the GSM rate of | ||||
|  *     270.8333 ksps and consists of GSM specific modulation, demodulation, | ||||
|  *     and decoding schemes. Also included are the socket interfaces for | ||||
|  *     connecting to the upper layer stack. | ||||
|  */ | ||||
| Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio) | ||||
| int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio) | ||||
| { | ||||
| 	Transceiver *trx; | ||||
| 	VectorFIFO *fifo; | ||||
|  | ||||
| 	trx = new Transceiver(config->port, config->addr.c_str(), | ||||
| 			      config->tx_sps, config->rx_sps, config->chans, | ||||
| 			      GSM::Time(3,0), radio, config->rssi_offset); | ||||
| 	if (!trx->init(config->filler, config->rtsc)) { | ||||
| 	transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr, | ||||
| 			      trx->cfg.remote_addr, trx->cfg.tx_sps, | ||||
| 			      trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0), | ||||
| 			      radio, trx->cfg.rssi_offset); | ||||
| 	if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc, | ||||
| 		       trx->cfg.rach_delay, trx->cfg.egprs)) { | ||||
| 		LOG(ALERT) << "Failed to initialize transceiver"; | ||||
| 		delete trx; | ||||
| 		return NULL; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < config->chans; i++) { | ||||
|         transceiver->setSignalHandler(transc_sig_cb); | ||||
|  | ||||
| 	for (size_t i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		fifo = radio->receiveFIFO(i); | ||||
| 		if (fifo && trx->receiveFIFO(fifo, i)) | ||||
| 		if (fifo && transceiver->receiveFIFO(fifo, i)) | ||||
| 			continue; | ||||
|  | ||||
| 		LOG(ALERT) << "Could not attach FIFO to channel " << i; | ||||
| 		delete trx; | ||||
| 		return NULL; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return trx; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void sig_handler(int signo) | ||||
| { | ||||
| 	fprintf(stdout, "Received shutdown signal"); | ||||
| 	gshutdown = true; | ||||
| 	fprintf(stdout, "signal %d received\n", signo); | ||||
| 	switch (signo) { | ||||
| 	case SIGINT: | ||||
| 	case SIGTERM: | ||||
| 		fprintf(stdout, "shutting down\n"); | ||||
| 		gshutdown = true; | ||||
| 		break; | ||||
| 	case SIGABRT: | ||||
| 	case SIGUSR1: | ||||
| 		talloc_report(tall_trx_ctx, stderr); | ||||
| 		talloc_report_full(tall_trx_ctx, stderr); | ||||
| 		break; | ||||
| 	case SIGUSR2: | ||||
| 		talloc_report_full(tall_trx_ctx, stderr); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void setup_signal_handlers() | ||||
| { | ||||
| 	if (signal(SIGINT, sig_handler) == SIG_ERR) { | ||||
| 		fprintf(stderr, "Failed to install SIGINT signal handler\n"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| 	if (signal(SIGTERM, sig_handler) == SIG_ERR) { | ||||
| 		fprintf(stderr, "Couldn't install SIGTERM signal handler\n"); | ||||
| 		exit( EXIT_FAILURE); | ||||
| 	} | ||||
| 	/* Handle keyboard interrupt SIGINT */ | ||||
| 	signal(SIGINT, &sig_handler); | ||||
| 	signal(SIGTERM, &sig_handler); | ||||
| 	signal(SIGABRT, &sig_handler); | ||||
| 	signal(SIGUSR1, &sig_handler); | ||||
| 	signal(SIGUSR2, &sig_handler); | ||||
| 	osmo_init_ignore_signals(); | ||||
| } | ||||
|  | ||||
| static void print_help() | ||||
| { | ||||
| 	fprintf(stdout, "Options:\n" | ||||
| 		"  -h    This text\n" | ||||
| 		"  -a    UHD device args\n" | ||||
| 		"  -l    Logging level (%s)\n" | ||||
| 		"  -i    IP address of GSM core\n" | ||||
| 		"  -p    Base port number\n" | ||||
| 		"  -e    Enable EDGE receiver\n" | ||||
| 		"  -d    Enable dual channel diversity receiver\n" | ||||
| 		"  -x    Enable external 10 MHz reference\n" | ||||
| 		"  -s    Samples-per-symbol (1 or 4)\n" | ||||
| 		"  -c    Number of ARFCN channels (default=1)\n" | ||||
| 		"  -f    Enable C0 filler table\n" | ||||
| 		"  -o    Set baseband frequency offset (default=auto)\n" | ||||
| 		"  -r    Random burst test mode with TSC\n" | ||||
| 		"  -A    Random burst test mode with Access Bursts\n" | ||||
| 		"  -R    RSSI to dBm offset in dB (default=0)\n" | ||||
| 		"  -S    Swap channels (UmTRX only)\n", | ||||
| 		"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG"); | ||||
| 		"  -h, --help      This text\n" | ||||
| 		"  -C, --config    Filename The config file to use\n" | ||||
| 		"  -V, --version   Print the version of OsmoTRX\n" | ||||
| 		); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| static void print_deprecated(char opt) | ||||
| { | ||||
| 	LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed." | ||||
| 		<< " Please use VTY cfg option instead." | ||||
| 		<< " All cmd line options are already being overriden by VTY options if set."; | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv, struct trx_ctx* trx) | ||||
| { | ||||
| 	int option; | ||||
| 	unsigned int i; | ||||
| 	std::vector<std::string> rx_paths, tx_paths; | ||||
| 	bool rx_paths_set = false, tx_paths_set = false; | ||||
| 	static struct option long_options[] = { | ||||
| 		{"help", 0, 0, 'h'}, | ||||
| 		{"config", 1, 0, 'C'}, | ||||
| 		{"version", 0, 0, 'V'}, | ||||
| 		{NULL, 0, 0, 0} | ||||
| 	}; | ||||
|  | ||||
| 	config->port = 0; | ||||
| 	config->tx_sps = DEFAULT_TX_SPS; | ||||
| 	config->rx_sps = DEFAULT_RX_SPS; | ||||
| 	config->chans = DEFAULT_CHANS; | ||||
| 	config->rtsc = 0; | ||||
| 	config->extref = false; | ||||
| 	config->filler = Transceiver::FILLER_ZERO; | ||||
| 	config->diversity = false; | ||||
| 	config->offset = 0.0; | ||||
| 	config->rssi_offset = 0.0; | ||||
| 	config->swap_channels = false; | ||||
| 	config->edge = false; | ||||
|  | ||||
| 	while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:AR:Se")) != -1) { | ||||
| 	while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options, | ||||
| 		NULL)) != -1) { | ||||
| 		switch (option) { | ||||
| 		case 'h': | ||||
| 			print_help(); | ||||
| 			exit(0); | ||||
| 			break; | ||||
| 		case 'a': | ||||
| 			config->dev_args = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg); | ||||
| 			break; | ||||
| 		case 'l': | ||||
| 			config->log_level = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			log_set_log_level(osmo_stderr_target, atoi(optarg)); | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			config->addr = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg); | ||||
| 			break; | ||||
| 		case 'j': | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			config->port = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.base_port = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'c': | ||||
| 			config->chans = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.num_chans = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'd': | ||||
| 			config->diversity = true; | ||||
| 		case 'm': | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.multi_arfcn = true; | ||||
| 			break; | ||||
| 		case 'x': | ||||
| 			config->extref = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.clock_ref = REF_EXTERNAL; | ||||
| 			break; | ||||
| 		case 'g': | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.clock_ref = REF_GPS; | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			config->filler = Transceiver::FILLER_DUMMY; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.filler = FILLER_DUMMY; | ||||
| 			break; | ||||
| 		case 'o': | ||||
| 			config->offset = atof(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.offset = atof(optarg); | ||||
| 			break; | ||||
| 		case 's': | ||||
| 			config->tx_sps = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.tx_sps = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'b': | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rx_sps = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'r': | ||||
| 			config->rtsc = atoi(optarg); | ||||
| 			config->filler = Transceiver::FILLER_NORM_RAND; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rtsc_set = true; | ||||
| 			trx->cfg.rtsc = atoi(optarg); | ||||
| 			if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ | ||||
| 				trx->cfg.filler = FILLER_NORM_RAND; | ||||
| 			break; | ||||
| 		case 'A': | ||||
| 			config->filler = Transceiver::FILLER_ACCESS_RAND; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rach_delay_set = true; | ||||
| 			trx->cfg.rach_delay = atoi(optarg); | ||||
| 			trx->cfg.filler = FILLER_ACCESS_RAND; | ||||
| 			break; | ||||
| 		case 'R': | ||||
| 			config->rssi_offset = atof(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rssi_offset = atof(optarg); | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			config->swap_channels = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.swap_channels = true; | ||||
| 			break; | ||||
| 		case 'e': | ||||
| 			config->edge = true; | ||||
| 			config->rx_sps = 4; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.egprs = true; | ||||
| 			break; | ||||
| 		case 't': | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.sched_rr = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'y': | ||||
| 			print_deprecated(option); | ||||
| 			tx_paths = comma_delimited_to_vector(optarg); | ||||
| 			tx_paths_set = true; | ||||
| 			break; | ||||
| 		case 'z': | ||||
| 			print_deprecated(option); | ||||
| 			rx_paths = comma_delimited_to_vector(optarg); | ||||
| 			rx_paths_set = true; | ||||
| 			break; | ||||
| 		case 'C': | ||||
| 			config_file = optarg; | ||||
| 			break; | ||||
| 		case 'V': | ||||
| 			print_version(1); | ||||
| 			exit(0); | ||||
| 			break; | ||||
| 		default: | ||||
| 			print_help(); | ||||
| 			exit(0); | ||||
| 			goto bad_config; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND)) | ||||
| 		config->filler = Transceiver::FILLER_EDGE_RAND; | ||||
| 	/* Cmd line option specific validation & setup */ | ||||
|  | ||||
| 	if ((config->tx_sps != 1) && (config->tx_sps != 4)) { | ||||
| 		printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps); | ||||
| 		print_help(); | ||||
| 		exit(0); | ||||
| 	if (trx->cfg.num_chans > TRX_CHAN_MAX) { | ||||
| 		LOG(ERROR) << "Too many channels requested, maximum is " <<  TRX_CHAN_MAX; | ||||
| 		goto bad_config; | ||||
| 	} | ||||
| 	if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) || | ||||
| 	    (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) { | ||||
| 		LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match"; | ||||
| 		goto bad_config; | ||||
| 	} | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		trx->cfg.chans[i].trx = trx; | ||||
| 		trx->cfg.chans[i].idx = i; | ||||
| 		if (tx_paths_set) | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str()); | ||||
| 		if (rx_paths_set) | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str()); | ||||
| 	} | ||||
|  | ||||
| 	if (config->edge && (config->tx_sps != 4)) { | ||||
| 		printf("EDGE only supported at 4 samples per symbol\n\n"); | ||||
| 		print_help(); | ||||
| 		exit(0); | ||||
| 	} | ||||
| 	return; | ||||
|  | ||||
| 	if (config->rtsc > 7) { | ||||
| 		printf("Invalid training sequence %i\n\n", config->rtsc); | ||||
| 		print_help(); | ||||
| 		exit(0); | ||||
| 	} | ||||
| bad_config: | ||||
| 	print_help(); | ||||
| 	exit(0); | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| int trx_validate_config(struct trx_ctx *trx) | ||||
| { | ||||
| 	int type, chans; | ||||
| 	RadioDevice *usrp; | ||||
| 	RadioInterface *radio = NULL; | ||||
| 	Transceiver *trx = NULL; | ||||
| 	struct trx_config config; | ||||
|  | ||||
| 	handle_options(argc, argv, &config); | ||||
|  | ||||
| 	setup_signal_handlers(); | ||||
|  | ||||
| 	/* Check database sanity */ | ||||
| 	if (!trx_setup_config(&config)) { | ||||
| 		std::cerr << "Config: Database failure - exiting" << std::endl; | ||||
| 		return EXIT_FAILURE; | ||||
| 	if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) { | ||||
| 		LOG(ERROR) << "Unsupported number of channels"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7); | ||||
| 	/* Force 4 SPS for EDGE or multi-ARFCN configurations */ | ||||
| 	if ((trx->cfg.egprs || trx->cfg.multi_arfcn) && | ||||
| 	    (trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) { | ||||
| 		LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config."; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	srandom(time(NULL)); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int set_sched_rr(unsigned int prio) | ||||
| { | ||||
| 	struct sched_param param; | ||||
| 	int rc; | ||||
| 	memset(¶m, 0, sizeof(param)); | ||||
| 	param.sched_priority = prio; | ||||
| 	LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority; | ||||
| 	rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); | ||||
| 	if (rc != 0) { | ||||
| 		LOG(ERROR) << "Config: Setting SCHED_RR failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void print_config(struct trx_ctx *trx) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	std::ostringstream ost(""); | ||||
|  | ||||
| 	ost << "Config Settings" << std::endl; | ||||
| 	ost << "   Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl; | ||||
| 	ost << "   Device args............. " << charp2str(trx->cfg.dev_args) << std::endl; | ||||
| 	ost << "   TRX Base Port........... " << trx->cfg.base_port << std::endl; | ||||
| 	ost << "   TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl; | ||||
| 	ost << "   GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl; | ||||
| 	ost << "   Channels................ " << trx->cfg.num_chans << std::endl; | ||||
| 	ost << "   Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl; | ||||
| 	ost << "   Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl; | ||||
| 	ost << "   EDGE support............ " << trx->cfg.egprs << std::endl; | ||||
| 	ost << "   Reference............... " << trx->cfg.clock_ref << std::endl; | ||||
| 	ost << "   C0 Filler Table......... " << trx->cfg.filler << std::endl; | ||||
| 	ost << "   Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl; | ||||
| 	ost << "   Tuning offset........... " << trx->cfg.offset << std::endl; | ||||
| 	ost << "   RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl; | ||||
| 	ost << "   Swap channels........... " << trx->cfg.swap_channels << std::endl; | ||||
| 	ost << "   Tx Antennas............."; | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		std::string p = charp2str(trx->cfg.chans[i].tx_path); | ||||
| 		ost << " '" << ((p != "") ? p : "<default>") <<  "'"; | ||||
| 	} | ||||
| 	ost << std::endl; | ||||
| 	ost << "   Rx Antennas............."; | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		std::string p = charp2str(trx->cfg.chans[i].rx_path); | ||||
| 		ost << " '" << ((p != "") ? p : "<default>") <<  "'"; | ||||
| 	} | ||||
| 	ost << std::endl; | ||||
|  | ||||
| 	LOG(INFO) << ost << std::endl; | ||||
| } | ||||
|  | ||||
| static void trx_stop() | ||||
| { | ||||
| 	LOG(NOTICE) << "Shutting down transceiver..." << std::endl; | ||||
|  | ||||
| 	delete transceiver; | ||||
| 	delete radio; | ||||
| 	delete usrp; | ||||
| } | ||||
|  | ||||
| static int trx_start(struct trx_ctx *trx) | ||||
| { | ||||
| 	int type, chans; | ||||
| 	unsigned int i; | ||||
| 	std::vector<std::string> rx_paths, tx_paths; | ||||
| 	RadioDevice::InterfaceType iface = RadioDevice::NORMAL; | ||||
|  | ||||
| 	/* Create the low level device object */ | ||||
| 	usrp = RadioDevice::make(config.tx_sps, config.rx_sps, config.chans, | ||||
| 				 config.diversity, config.offset); | ||||
| 	type = usrp->open(config.dev_args, config.extref, config.swap_channels); | ||||
| 	if (trx->cfg.multi_arfcn) | ||||
| 		iface = RadioDevice::MULTI_ARFCN; | ||||
|  | ||||
| 	/* Generate vector of rx/tx_path: */ | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path)); | ||||
| 		tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path)); | ||||
| 	} | ||||
|  | ||||
| 	usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface, | ||||
| 				 trx->cfg.num_chans, trx->cfg.offset, | ||||
| 				 tx_paths, rx_paths); | ||||
| 	type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels); | ||||
| 	if (type < 0) { | ||||
| 		LOG(ALERT) << "Failed to create radio device" << std::endl; | ||||
| 		goto shutdown; | ||||
| 	} | ||||
|  | ||||
| 	/* Setup the appropriate device interface */ | ||||
| 	radio = makeRadioInterface(&config, usrp, type); | ||||
| 	radio = makeRadioInterface(trx, usrp, type); | ||||
| 	if (!radio) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	/* Create the transceiver core */ | ||||
| 	trx = makeTransceiver(&config, radio); | ||||
| 	if (!trx) | ||||
| 	if (makeTransceiver(trx, radio) < 0) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	chans = trx->numChans(); | ||||
| 	std::cout << "-- Transceiver active with " | ||||
| 	chans = transceiver->numChans(); | ||||
| 	LOG(NOTICE) << "-- Transceiver active with " | ||||
| 		  << chans << " channel(s)" << std::endl; | ||||
|  | ||||
| 	while (!gshutdown) | ||||
| 		sleep(1); | ||||
| 	return 0; | ||||
|  | ||||
| shutdown: | ||||
| 	std::cout << "Shutting down transceiver..." << std::endl; | ||||
| 	trx_stop(); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| 	delete trx; | ||||
| 	delete radio; | ||||
| 	delete usrp; | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX"); | ||||
| 	msgb_talloc_ctx_init(tall_trx_ctx, 0); | ||||
| 	g_vty_info.tall_ctx = tall_trx_ctx; | ||||
|  | ||||
| 	setup_signal_handlers(); | ||||
|  | ||||
| 	g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx); | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| 	printf("Info: SSE3 support compiled in"); | ||||
| #ifdef HAVE___BUILTIN_CPU_SUPPORTS | ||||
| 	if (__builtin_cpu_supports("sse3")) | ||||
| 		printf(" and supported by CPU\n"); | ||||
| 	else | ||||
| 		printf(", but not supported by CPU\n"); | ||||
| #else | ||||
| 	printf(", but runtime SIMD detection disabled\n"); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_SSE4_1 | ||||
| 	printf("Info: SSE4.1 support compiled in"); | ||||
| #ifdef HAVE___BUILTIN_CPU_SUPPORTS | ||||
| 	if (__builtin_cpu_supports("sse4.1")) | ||||
| 		printf(" and supported by CPU\n"); | ||||
| 	else | ||||
| 		printf(", but not supported by CPU\n"); | ||||
| #else | ||||
| 	printf(", but runtime SIMD detection disabled\n"); | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| 	convolve_init(); | ||||
| 	convert_init(); | ||||
|  | ||||
| 	osmo_init_logging2(tall_trx_ctx, &log_info); | ||||
| 	osmo_stats_init(tall_trx_ctx); | ||||
| 	vty_init(&g_vty_info); | ||||
| 	ctrl_vty_init(tall_trx_ctx); | ||||
| 	trx_vty_init(g_trx_ctx); | ||||
|  | ||||
| 	logging_vty_add_cmds(); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	osmo_stats_vty_add_cmds(); | ||||
|  | ||||
| 	handle_options(argc, argv, g_trx_ctx); | ||||
|  | ||||
| 	rate_ctr_init(tall_trx_ctx); | ||||
|  | ||||
| 	rc = vty_read_config_file(config_file, NULL); | ||||
| 	if (rc < 0) { | ||||
| 		fprintf(stderr, "Failed to open config file: '%s'\n", config_file); | ||||
| 		exit(2); | ||||
| 	} | ||||
|  | ||||
| 	rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX); | ||||
| 	if (rc < 0) | ||||
| 		exit(1); | ||||
|  | ||||
| 	g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL); | ||||
| 	if (!g_ctrlh) { | ||||
| 		LOG(ERROR) << "Failed to create CTRL interface.\n"; | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	/* Backward compatibility: Hack to have 1 channel allocated by default. | ||||
| 	 * Can be Dropped once we * get rid of "-c" cmdline param */ | ||||
| 	if (g_trx_ctx->cfg.num_chans == 0) { | ||||
| 		g_trx_ctx->cfg.num_chans = 1; | ||||
| 		g_trx_ctx->cfg.chans[0].trx = g_trx_ctx; | ||||
| 		g_trx_ctx->cfg.chans[0].idx = 0; | ||||
| 		LOG(ERROR) << "No explicit channel config found. Make sure you" \ | ||||
| 			" configure channels in VTY config. Using 1 channel as default," \ | ||||
| 			" but expect your config to break in the future."; | ||||
| 	} | ||||
|  | ||||
| 	print_config(g_trx_ctx); | ||||
|  | ||||
| 	if (trx_validate_config(g_trx_ctx) < 0) { | ||||
| 		LOG(ERROR) << "Config failure - exiting"; | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	if (g_trx_ctx->cfg.sched_rr) { | ||||
| 		if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0) | ||||
| 			return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	srandom(time(NULL)); | ||||
|  | ||||
| 	if(trx_start(g_trx_ctx) < 0) | ||||
| 		return EXIT_FAILURE; | ||||
|  | ||||
| 	while (!gshutdown) | ||||
| 		osmo_select_main(0); | ||||
|  | ||||
| 	trx_stop(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user