void startTransceiver() { //if local kill the process currently listening on this port char killCmd[32]; if (gConfig.getStr("TRX.IP") == "127.0.0.1"){ sprintf(killCmd,"fuser -k -n udp %d",(int)gConfig.getNum("TRX.Port")); if (system(killCmd)) {} } // Start the transceiver binary, if the path is defined. // If the path is not defined, the transceiver must be started by some other process. char TRXnumARFCN[4]; sprintf(TRXnumARFCN,"%1d",(int)gConfig.getNum("GSM.Radio.ARFCNs")); std::string extra_args = gConfig.getStr("TRX.Args"); LOG(NOTICE) << "starting transceiver " << transceiverPath << " w/ " << TRXnumARFCN << " ARFCNs and Args:" << extra_args; gTransceiverPid = vfork(); LOG_ASSERT(gTransceiverPid>=0); if (gTransceiverPid==0) { // Pid==0 means this is the process that starts the transceiver. execlp(transceiverPath,transceiverPath,TRXnumARFCN,extra_args.c_str(),(void*)NULL); LOG(EMERG) << "cannot find " << transceiverPath; _exit(1); } else { int status; waitpid(gTransceiverPid, &status,0); LOG(EMERG) << "Transceiver quit with status " << status << ". Exiting."; exit(2); } }
/** 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(); }
// Add an alarm to the alarm list, and send it out via udp // // On the first call we read the ip and port from the configuration // TODO - is there any global setup function where this should be done? -- Alon void addAlarm(const string& s) { // Socket open and close on every alarm - wise? // Probably. That way we are sure to pick up changes in the target address. // Alarms should not happen often. if (gConfig.defines("Log.Alarms.TargetIP")) { UDPSocket alarmsocket(0, gConfig.getStr("Log.Alarms.TargetIP"), gConfig.getNum("Log.Alarms.TargetPort")); alarmsocket.write(s.c_str()); } // append to list and reduce list to max alarm count alarmsLock.lock(); alarmsList.push_back(s); unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max"); while (alarmsList.size() > maxAlarms) alarmsList.pop_front(); alarmsLock.unlock(); }
int SubscriberRegistry::init() { string ldb = gConfig.getStr("SubscriberRegistry.db"); size_t p = ldb.find_last_of('/'); if (p == string::npos) { LOG(EMERG) << "SubscriberRegistry.db not in a directory?"; mDB = NULL; return 1; } string dir = ldb.substr(0, p); struct stat buf; if (stat(dir.c_str(), &buf)) { LOG(EMERG) << dir << " does not exist"; mDB = NULL; return 1; } mNumSQLTries=gConfig.getNum("Control.NumSQLTries"); int rc = sqlite3_open(ldb.c_str(),&mDB); if (rc) { LOG(EMERG) << "Cannot open SubscriberRegistry database: " << ldb << " error: " << sqlite3_errmsg(mDB); sqlite3_close(mDB); mDB = NULL; return 1; } if (!sqlite3_command(mDB,createRRLPTable,mNumSQLTries)) { LOG(EMERG) << "Cannot create RRLP table"; return 1; } if (!sqlite3_command(mDB,createDDTable,mNumSQLTries)) { LOG(EMERG) << "Cannot create DIALDATA_TABLE table"; return 1; } if (!sqlite3_command(mDB,createRateTable,mNumSQLTries)) { LOG(EMERG) << "Cannot create rate table"; return 1; } if (!sqlite3_command(mDB,createSBTable,mNumSQLTries)) { LOG(EMERG) << "Cannot create SIP_BUDDIES table"; return 1; } // Set high-concurrency WAL mode. if (!sqlite3_command(mDB,enableWAL,mNumSQLTries)) { LOG(EMERG) << "Cannot enable WAL mode on database at " << ldb << ", error message: " << sqlite3_errmsg(mDB); } if (!getCLIDLocal("IMSI001010000000000")) { // This is a test SIM provided with the BTS. if (addUser("IMSI001010000000000", "2100") != SUCCESS) { LOG(EMERG) << "Cannot insert test SIM"; } } return 0; }
void startTransceiver() { // kill any stray transceiver process system("killall transceiver"); // Start the transceiver binary, if the path is defined. // If the path is not defined, the transceiver must be started by some other process. char TRXnumARFCN[16]; sprintf(TRXnumARFCN,"%1d", static_cast<int>(gConfig.getNum("GSM.Radio.ARFCNs"))); LOG(NOTICE) << "starting transceiver " << transceiverPath << " " << TRXnumARFCN; gTransceiverPid = vfork(); LOG_ASSERT(gTransceiverPid>=0); if (gTransceiverPid==0) { // Pid==0 means this is the process that starts the transceiver. execlp(transceiverPath,transceiverPath,TRXnumARFCN,NULL); LOG(EMERG) << "cannot find " << transceiverPath; _exit(1); } else { int status; waitpid(gTransceiverPid, &status,0); LOG(EMERG) << "Transceiver quit with status " << status << ". Exiting."; exit(2); } }
int main(int argc, char **argv) { // TODO: Properly parse and handle any arguments if (argc > 1) { for (int argi = 0; argi < argc; argi++) { if (!strcmp(argv[argi], "--version") || !strcmp(argv[argi], "-v")) { cout << gVersionString << endl; } if (!strcmp(argv[argi], "--gensql")) { cout << gConfig.getDefaultSQL(string(argv[0]), gVersionString) << endl; } if (!strcmp(argv[argi], "--gentex")) { cout << gConfig.getTeX(string(argv[0]), gVersionString) << endl; } } return 0; } sockaddr_in si_me; sockaddr_in si_other; int aSocket; char buf[BUFLEN]; LOG(ALERT) << argv[0] << " (re)starting"; srand ( time(NULL) + (int)getpid() ); my_udp_port = gConfig.getNum("SubscriberRegistry.Port"); gSubscriberRegistry.init(); gNodeManager.setAppLogicHandler(&nmHandler); gNodeManager.start(45064); // init osip lib osip_t *osip; int i=osip_init(&osip); if (i!=0) { LOG(ALERT) << "cannot init sip lib"; exit(1); } if ((aSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { LOG(ALERT) << "can't initialize socket"; exit(1); } memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(my_udp_port); si_me.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(aSocket, (sockaddr*)&si_me, sizeof(si_me)) == -1) { LOG(ALERT) << "can't bind socket on port " << my_udp_port; exit(1); } LOG(NOTICE) << "binding on port " << my_udp_port; while (true) { gConfig.purge(); socklen_t slen = sizeof(si_other); memset(buf, 0, BUFLEN); if (recvfrom(aSocket, buf, BUFLEN, 0, (sockaddr*)&si_other, &slen) == -1) { LOG(ERR) << "recvfrom problem"; continue; } LOG(INFO) << " receiving " << buf; char *dest = processBuffer(buf); if (dest == NULL) { continue; } if (sendto(aSocket, dest, strlen(dest), 0, (sockaddr*)&si_other, sizeof(si_other)) == -1) { LOG(ERR) << "sendto problem"; continue; } osip_free(dest); } close(aSocket); return 0; }
int main(int argc, char *argv[]) { try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); gLogInit("openbts",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7); LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__; COUT("\n\n" << gOpenBTSWelcome << "\n"); gTMSITable.open(gConfig.getStr("Control.Reporting.TMSITable").c_str()); gTransactionTable.init(); gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gSubscriberRegistry.init(); gParser.addCommands(); COUT("\nStarting the system..."); Thread transceiverThread; transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // Start the SIP interface. gSIPInterface.start(); // // Configure the radio. // // Start the transceiver interface. // Sleep long enough for the USRP to bootload. sleep(5); gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(); // Tuning. // Make sure its off for tuning. C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); // Tune the radio. LOG(INFO) << "tuning TRX to ARFCN " << C0; ARFCNManager* radio = gTRX.ARFCN(); radio->tune(C0); // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,gSDCCH_4_0), SDCCHLogicalChannel(0,gSDCCH_4_1), SDCCHLogicalChannel(0,gSDCCH_4_2), SDCCHLogicalChannel(0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; for (int i=0; i<4; i++) { C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount); sCount++; } if (!gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount); sCount++; } } // Set up idle filling on C0 as needed. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. LOG_ASSERT(gConfig.getNum("GSM.CCCH.PCH.Reserve")<(int)gBTS.numAGCHs()); // OK, now it is safe to start the BTS. gBTS.start(); #ifdef HAVE_LIBREADLINE // [ // start console using_history(); static const char * const history_file_name = "/.openbts_history"; char *history_name = 0; char *home_dir = getenv("HOME"); if(home_dir) { size_t home_dir_len = strlen(home_dir); size_t history_file_len = strlen(history_file_name); size_t history_len = home_dir_len + history_file_len + 1; if(history_len > home_dir_len) { if(!(history_name = (char *)malloc(history_len))) { LOG(ERR) << "malloc failed: " << strerror(errno); exit(2); } memcpy(history_name, home_dir, home_dir_len); memcpy(history_name + home_dir_len, history_file_name, history_file_len + 1); read_history(history_name); } } #endif // HAVE_LIBREADLINE ] LOG(INFO) << "system ready"; COUT("\n\nWelcome to OpenBTS. Type \"help\" to see available commands."); // FIXME: We want to catch control-d (emacs keybinding for exit()) // The logging parts were removed from this loop. // If we want them back, they will need to go into their own thread. while (1) { #ifdef HAVE_LIBREADLINE // [ char *inbuf = readline(gConfig.getStr("CLI.Prompt").c_str()); if (!inbuf) break; if (*inbuf) { add_history(inbuf); // The parser returns -1 on exit. if (gParser.process(inbuf, cout, cin)<0) { free(inbuf); break; } } free(inbuf); #else // HAVE_LIBREADLINE ][ cout << endl << gConfig.getStr("CLI.Prompt"); cout.flush(); char inbuf[1024]; cin.getline(inbuf,1024,'\n'); // The parser returns -1 on exit. if (gParser.process(inbuf,cout,cin)<0) break; #endif // !HAVE_LIBREADLINE ] } #ifdef HAVE_LIBREADLINE // [ if(history_name) { int e = write_history(history_name); if(e) { fprintf(stderr, "error: history: %s\n", strerror(e)); } free(history_name); history_name = 0; } #endif // HAVE_LIBREADLINE ] if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); } catch (ConfigurationTableKeyNotFound e) { LOG(ALERT) << "configuration key " << e.key() << " not defined"; exit(2); } }
// The transaction table. Control::TransactionTable gTransactionTable; // Physical status reporting GSM::PhysicalStatus gPhysStatus; // The global SIPInterface object. SIP::SIPInterface gSIPInterface; // Configure the BTS object based on the config file. // So don't create this until AFTER loading the config file. GSMConfig gBTS; // Our interface to the software-defined radio. TransceiverManager gTRX(gConfig.getStr("TRX.IP").c_str(), gConfig.getNum("TRX.Port")); // Subscriber registry SubscriberRegistry gSubscriberRegistry; // Create a Global Handover Decision Class GSM::GSMHandover gHandover; /** Define a function to call any time the configuration database changes. */ void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) { LOG(INFO) << "purging configuration cache"; gConfig.purge(); gBTS.regenerateBeacon(); }
int main(int argc, char *argv[]) { srandom(time(NULL)); COUT("\n\n" << gOpenBTSWelcome << "\n"); COUT("\nStarting the system..."); gSetLogLevel(gConfig.getStr("LogLevel")); if (gConfig.defines("LogFileName")) { gSetLogFile(gConfig.getStr("LogFileName")); } // Start the transceiver binary, if the path is defined. // If the path is not defined, the transceiver must be started by some other process. const char *TRXPath = NULL; if (gConfig.defines("TRX.Path")) TRXPath=gConfig.getStr("TRX.Path"); pid_t transceiverPid = 0; if (TRXPath) { const char *TRXLogLevel = gConfig.getStr("TRX.LogLevel"); const char *TRXLogFileName = NULL; if (gConfig.defines("TRX.LogFileName")) TRXLogFileName=gConfig.getStr("TRX.LogFileName"); transceiverPid = vfork(); assert(transceiverPid>=0); if (transceiverPid==0) { execl(TRXPath,"transceiver",TRXLogLevel,TRXLogFileName,NULL); LOG(ERROR) << "cannot start transceiver"; _exit(0); } } // Start the SIP interface. gSIPInterface.start(); // Start the transceiver interface. gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. radio->powerOff(); // Set TSC same as BSC everywhere. radio->setTSC(gBTS.BCC()); // Tune. radio->tune(gConfig.getNum("GSM.ARFCN")); // C-V on C0T0 radio->setSlot(0,5); // Turn on and power up. radio->powerOn(); radio->setPower(gConfig.getNum("GSM.PowerAttenDB")); // set up a combination V beacon set // SCH SCHL1FEC SCH; SCH.downstream(radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel SDCCH[4] = { SDCCHLogicalChannel(0,gSDCCH_4_0), SDCCHLogicalChannel(0,gSDCCH_4_1), SDCCHLogicalChannel(0,gSDCCH_4_2), SDCCHLogicalChannel(0,gSDCCH_4_3) }; Thread SDCCHControlThread[4]; for (int i=0; i<4; i++) { SDCCH[i].downstream(radio); SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&SDCCH[i]); SDCCH[i].open(); gBTS.addSDCCH(&SDCCH[i]); } // Count configured slots. unsigned sCount = 1; // Create C-VII slots on C0Tn for (unsigned i=0; i<gConfig.getNum("GSM.NumC7s"); i++) { radio->setSlot(sCount,7); for (unsigned sub=0; sub<8; sub++) { SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(sCount,gSDCCH8[sub]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addSDCCH(chan); } sCount++; } // Create C-I slots on C0Tn for (unsigned i=0; i<gConfig.getNum("GSM.NumC1s"); i++) { radio->setSlot(sCount,1); TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(sCount,gTCHF_T[sCount]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addTCH(chan); sCount++; } assert(sCount<=8); /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. gBTS.addPCH(&CCCH2); // Start the paging generator // Don't start the pager until some PCHs exist!! gBTS.pager().start(); LOG(INFO) << "system ready"; COUT("\n\nWelcome to OpenBTS. Type \"help\" to see available commands."); // FIXME: We want to catch control-d (emacs keybinding for exit()) while (1) { char inbuf[1024]; cout << "\nOpenBTS> "; cin.getline(inbuf,1024,'\n'); if (strcmp(inbuf,"exit")==0) break; gParser.process(inbuf,cout,cin); } if (transceiverPid) kill(transceiverPid,SIGKILL); }
/** Return warning strings about a potential conflicting value */ vector<string> configurationCrossCheck(const string& key) { vector<string> warnings; ostringstream warning; // GSM.Timer.T3113 should equal SIP.Timer.B if (key.compare("GSM.Timer.T3113") == 0 || key.compare("SIP.Timer.B") == 0) { string gsm = gConfig.getStr("GSM.Timer.T3113"); string sip = gConfig.getStr("SIP.Timer.B"); if (gsm.compare(sip) != 0) { warning << "GSM.Timer.T3113 (" << gsm << ") and SIP.Timer.B (" << sip << ") should usually have the same value"; warnings.push_back(warning.str()); warning.str(std::string()); } // Control.VEA depends on GSM.CellSelection.NECI } else if (key.compare("Control.VEA") == 0 || key.compare("GSM.CellSelection.NECI") == 0) { if (gConfig.getBool("Control.VEA") && gConfig.getStr("GSM.CellSelection.NECI").compare("1") != 0) { warning << "Control.VEA is enabled but will not be functional until GSM.CellSelection.NECI is set to \"1\""; warnings.push_back(warning.str()); warning.str(std::string()); } // GSM.Timer.T3212 should be a factor of six and shorter than SIP.RegistrationPeriod } else if (key.compare("GSM.Timer.T3212") == 0 || key.compare("SIP.RegistrationPeriod") == 0) { int gsm = gConfig.getNum("GSM.Timer.T3212"); int sip = gConfig.getNum("SIP.RegistrationPeriod"); if (key.compare("GSM.Timer.T3212") == 0 && gsm % 6) { warning << "GSM.Timer.T3212 should be a factor of 6"; warnings.push_back(warning.str()); warning.str(std::string()); } if (gsm >= sip) { warning << "GSM.Timer.T3212 (" << gsm << ") should be shorter than SIP.RegistrationPeriod (" << sip << ")"; warnings.push_back(warning.str()); warning.str(std::string()); } // GPRS.ChannelCodingControl.RSSI should normally be 10db more than GSM.Radio.RSSITarget } else if (key.compare("GPRS.ChannelCodingControl.RSSI") == 0 || key.compare("GSM.Radio.RSSITarget") == 0) { int gprs = gConfig.getNum("GPRS.ChannelCodingControl.RSSI"); int gsm = gConfig.getNum("GSM.Radio.RSSITarget"); if ((gprs - gsm) != 10) { warning << "GPRS.ChannelCodingControl.RSSI (" << gprs << ") should normally be 10db greater than GSM.Radio.RSSITarget (" << gsm << ")"; warnings.push_back(warning.str()); warning.str(std::string()); } // TODO : This NEEDS to be an error not a warning. OpenBTS will fail to start because of an assert if an invalid value is used. // GSM.Radio.C0 needs to be inside the valid range of ARFCNs for GSM.Radio.Band } else if (key.compare("GSM.Radio.C0") == 0 || key.compare("GSM.Radio.Band") == 0) { int c0 = gConfig.getNum("GSM.Radio.C0"); string band = gConfig.getStr("GSM.Radio.Band"); string range; if (band.compare("850") == 0 && (c0 < 128 || 251 < c0)) { range = "128-251"; } else if (band.compare("900") == 0 && (c0 < 1 || 124 < c0)) { range = "1-124"; } else if (band.compare("1800") == 0 && (c0 < 512 || 885 < c0)) { range = "512-885"; } else if (band.compare("1900") == 0 && (c0 < 512 || 810 < c0)) { range = "512-810"; } if (range.length()) { warning << "GSM.Radio.C0 (" << c0 << ") falls outside the valid range of ARFCNs " << range << " for GSM.Radio.Band (" << band << ")"; warnings.push_back(warning.str()); warning.str(std::string()); } // SGSN.Timer.ImplicitDetach should be at least 240 seconds greater than SGSN.Timer.RAUpdate" } else if (key.compare("SGSN.Timer.ImplicitDetach") == 0 || key.compare("SGSN.Timer.RAUpdate") == 0) { int detach = gConfig.getNum("SGSN.Timer.ImplicitDetach"); int update = gConfig.getNum("SGSN.Timer.RAUpdate"); if ((detach - update) < 240) { warning << "SGSN.Timer.ImplicitDetach (" << detach << ") should be at least 240 seconds greater than SGSN.Timer.RAUpdate (" << update << ")"; warnings.push_back(warning.str()); warning.str(std::string()); } // Control.LUR.FailedRegistration.Message depends on Control.LUR.FailedRegistration.ShortCode } else if (key.compare("Control.LUR.FailedRegistration.Message") == 0 || key.compare("Control.LUR.FailedRegistration.ShortCode") == 0) { if (gConfig.getStr("Control.LUR.FailedRegistration.Message").length() && !gConfig.getStr("Control.LUR.FailedRegistration.ShortCode").length()) { warning << "Control.LUR.FailedRegistration.Message is enabled but will not be functional until Control.LUR.FailedRegistration.ShortCode is set"; warnings.push_back(warning.str()); warning.str(std::string()); } // Control.LUR.NormalRegistration.Message depends on Control.LUR.NormalRegistration.ShortCode } else if (key.compare("Control.LUR.NormalRegistration.Message") == 0 || key.compare("Control.LUR.NormalRegistration.ShortCode") == 0) { if (gConfig.getStr("Control.LUR.NormalRegistration.Message").length() && !gConfig.getStr("Control.LUR.NormalRegistration.ShortCode").length()) { warning << "Control.LUR.NormalRegistration.Message is enabled but will not be functional until Control.LUR.NormalRegistration.ShortCode is set"; warnings.push_back(warning.str()); warning.str(std::string()); } // Control.LUR.OpenRegistration depends on Control.LUR.OpenRegistration.ShortCode } else if (key.compare("Control.LUR.OpenRegistration") == 0 || key.compare("Control.LUR.OpenRegistration.ShortCode") == 0) { if (gConfig.getStr("Control.LUR.OpenRegistration").length() && !gConfig.getStr("Control.LUR.OpenRegistration.ShortCode").length()) { warning << "Control.LUR.OpenRegistration is enabled but will not be functional until Control.LUR.OpenRegistration.ShortCode is set"; warnings.push_back(warning.str()); warning.str(std::string()); } // TODO : SIP.SMSC is actually broken with the verification bits, no way to set value as null // SIP.SMSC should normally be NULL if SMS.MIMIEType is "text/plain" and "smsc" if SMS.MIMEType is "application/vnd.3gpp". } else if (key.compare("SMS.MIMEType") == 0 || key.compare("SIP.SMSC") == 0) { string sms = gConfig.getStr("SMS.MIMEType"); string sip = gConfig.getStr("SIP.SMSC"); if (sms.compare("application/vnd.3gpp.sms") == 0 && sip.compare("smsc") != 0) { warning << "SMS.MIMEType is set to \"application/vnc.3gpp.sms\", SIP.SMSC should usually be set to \"smsc\""; warnings.push_back(warning.str()); warning.str(std::string()); } else if (sms.compare("text/plain") == 0 && sip.compare("") != 0) { warning << "SMS.MIMEType is set to \"text/plain\", SIP.SMSC should usually be empty (use unconfig to clear)"; warnings.push_back(warning.str()); warning.str(std::string()); } // SIP.Local.IP cannot be 127.0.0.1 when any of the SIP.Proxy.* settings are non-localhost } else if (key.compare("SIP.Local.IP") == 0 || key.compare("SIP.Proxy.Registration") == 0 || key.compare("SIP.Proxy.SMS") == 0 || key.compare("SIP.Proxy.Speech") == 0 || key.compare("SIP.Proxy.USSD") == 0) { string loopback = "127.0.0.1"; string local = gConfig.getStr("SIP.Local.IP"); if (local.compare(loopback) == 0) { string registration = gConfig.getStr("SIP.Proxy.Registration"); string sms = gConfig.getStr("SIP.Proxy.SMS"); string speech = gConfig.getStr("SIP.Proxy.Speech"); string ussd = gConfig.getStr("SIP.Proxy.USSD"); if (registration.find(loopback) == std::string::npos || sms.find(loopback) == std::string::npos || speech.find(loopback) == std::string::npos || (ussd.length() && ussd.find(loopback) == std::string::npos)) { warning << "A non-local IP is being used for one or more SIP.Proxy.* settings but SIP.Local.IP is still set to 127.0.0.1. "; warning << "Set SIP.Local.IP to the IP address of this machine as seen by the proxies."; warnings.push_back(warning.str()); warning.str(std::string()); } } // GSM.MS.Power.Min cannot be higher than GSM.MS.Power.Max } else if (key.compare("GSM.MS.Power.Min") == 0 || key.compare("GSM.MS.Power.Max") == 0) { if (gConfig.getNum("GSM.MS.Power.Min") > gConfig.getNum("GSM.MS.Power.Max")) { warning << "GSM.MS.Power.Min is set higher than GSM.MS.Power.Max. Swap the values or set a new minimum."; warnings.push_back(warning.str()); warning.str(std::string()); } // GSM.Channels.NumC1s + GSM.Channels.NumC1s must fall within 8 * GSM.Radio.ARFCNs } else if (key.compare("GSM.Radio.ARFCNs") == 0 || key.compare("GSM.Channels.NumC1s") == 0 || key.compare("GSM.Channels.NumC7s") == 0) { int max = (8 * gConfig.getNum("GSM.Radio.ARFCNs")) - 1; int current = gConfig.getNum("GSM.Channels.NumC1s") + gConfig.getNum("GSM.Channels.NumC7s"); if (max < current) { warning << "There are only " << max << " channels available but " << current << " are configured. "; warning << "Reduce GSM.Channels.NumC1s and/or GSM.Channels.NumC7s accordingly."; warnings.push_back(warning.str()); warning.str(std::string()); } else if (max > current) { int avail = max-current; if (avail == 1) { warning << "There is still " << avail << " channel available for additional capacity. "; } else { warning << "There are still " << avail << " channels available for additional capacity. "; } warning << "Increase GSM.Channels.NumC1s and/or GSM.Channels.NumC7s accordingly."; warnings.push_back(warning.str()); warning.str(std::string()); } } return warnings; }
int main(int argc, char **argv) { sockaddr_in si_me; sockaddr_in si_other; int aSocket; char buf[BUFLEN]; LOG(ALERT) << argv[0] << " (re)starting"; srand ( time(NULL) + (int)getpid() ); my_udp_port = gConfig.getNum("SubscriberRegistry.Port"); gSubscriberRegistry.init(); // init osip lib osip_t *osip; int i=osip_init(&osip); if (i!=0) { LOG(ALERT) << "cannot init sip lib"; return NULL; } if ((aSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { LOG(ALERT) << "can't initialize socket"; exit(1); } memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(my_udp_port); si_me.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(aSocket, (sockaddr*)&si_me, sizeof(si_me)) == -1) { LOG(ALERT) << "can't bind socket on port " << my_udp_port; exit(1); } LOG(NOTICE) << "binding on port " << my_udp_port; while (true) { gConfig.purge(); socklen_t slen = sizeof(si_other); memset(buf, 0, BUFLEN); if (recvfrom(aSocket, buf, BUFLEN, 0, (sockaddr*)&si_other, &slen) == -1) { LOG(ERR) << "recvfrom problem"; continue; } LOG(INFO) << " receiving " << buf; char *dest = processBuffer(buf); if (dest == NULL) { continue; } if (sendto(aSocket, dest, strlen(dest), 0, (sockaddr*)&si_other, sizeof(si_other)) == -1) { LOG(ERR) << "sendto problem"; continue; } osip_free(dest); } close(aSocket); return 0; }
// Physical status reporting GSM::PhysicalStatus gPhysStatus; // Configure the BTS object based on the config file. // So don't create this until AFTER loading the config file. GSMConfig gBTS; // Note to all from pat: // It is inadvisable to statically initialize any non-trivial entity here because // the underlying dependencies may not yet have undergone their static initialization. // For example, if any of these throw an alarm, the system will crash because // the Logger may not have been initialized yet. // Our interface to the software-defined radio. TransceiverManager gTRX(gConfig.getNum("GSM.Radio.ARFCNs"), gConfig.getStr("TRX.IP").c_str(), gConfig.getNum("TRX.Port")); /** The global peering interface. */ Peering::PeerInterface gPeerInterface; /** The global neighbor table. */ Peering::NeighborTable gNeighborTable; /** Define a function to call any time the configuration database changes. */ void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) { // (pat) NO NO NO. Do not call LOG from here - it may result in infinite recursion. // LOG(INFO) << "purging configuration cache"; gConfig.purge(); gBTS.regenerateBeacon();
int main(int argc, char *argv[]) { // TODO: Properly parse and handle any arguments if (argc > 1) { for (int argi = 0; argi < argc; argi++) { if (!strcmp(argv[argi], "--version") || !strcmp(argv[argi], "-v")) { cout << gVersionString << endl; } } return 0; } createStats(); gReports.incr("OpenBTS.Starts"); int sock = socket(AF_UNIX,SOCK_DGRAM,0); if (sock<0) { perror("creating CLI datagram socket"); LOG(ALERT) << "cannot create socket for CLI"; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); gLogInit("openbts",gConfig.getStr("Log.Level").c_str()); LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__; COUT("\n\n" << gOpenBTSWelcome << "\n"); gTMSITable.open(gConfig.getStr("Control.Reporting.TMSITable").c_str()); gTransactionTable.init(gConfig.getStr("Control.Reporting.TransactionTable").c_str()); gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gSubscriberRegistry.init(); gParser.addCommands(); COUT("\nStarting the system..."); // is the radio running? // Start the transceiver interface. LOG(INFO) << "checking transceiver"; //gTRX.ARFCN(0)->powerOn(); //sleep(gConfig.getNum("TRX.Timeout.Start",2)); bool haveTRX = gTRX.ARFCN(0)->powerOn(false); Thread transceiverThread; if (!haveTRX) { transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // sleep to let the FPGA code load // TODO: we should be "pinging" the radio instead of sleeping sleep(5); } else { LOG(NOTICE) << "transceiver already running"; } // Start the SIP interface. gSIPInterface.start(); // // Configure the radio. // gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. //C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); unsigned numARFCNs = gConfig.getNum("GSM.Radio.ARFCNs"); for (unsigned i=0; i<numARFCNs; i++) { // Tune the radios. unsigned ARFCN = C0 + i*2; LOG(INFO) << "tuning TRX " << i << " to ARFCN " << ARFCN; ARFCNManager* radio = gTRX.ARFCN(i); radio->tune(ARFCN); } // Send either TSC or full BSIC depending on radio need if (gConfig.getBool("GSM.Radio.NeedBSIC")) { // Send BSIC to C0radio->setBSIC(gBTS.BSIC()); } else { // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); } // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(true); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,0,gSDCCH_4_0), SDCCHLogicalChannel(0,0,gSDCCH_4_1), SDCCHLogicalChannel(0,0,gSDCCH_4_2), SDCCHLogicalChannel(0,0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; for (int i=0; i<4; i++) { C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount/8,sCount%8); sCount++; } if (!gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Set up idle filling on C0 as needed. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. if (gConfig.getNum("GSM.Channels.SDCCHReserve")>=(int)gBTS.SDCCHTotal()) { unsigned val = gBTS.SDCCHTotal() - 1; LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val; gConfig.set("GSM.Channels.SDCCHReserve",val); } // OK, now it is safe to start the BTS. gBTS.start(); cout << "\nsystem ready\n"; cout << "\nuse the OpenBTSCLI utility to access CLI\n"; LOG(INFO) << "system ready"; struct sockaddr_un cmdSockName; cmdSockName.sun_family = AF_UNIX; const char* sockpath = gConfig.getStr("CLI.SocketPath").c_str(); char rmcmd[strlen(sockpath)+5]; sprintf(rmcmd,"rm %s",sockpath); system(rmcmd); strcpy(cmdSockName.sun_path,sockpath); if (bind(sock, (struct sockaddr *) &cmdSockName, sizeof(struct sockaddr_un))) { perror("binding name to cmd datagram socket"); LOG(ALERT) << "cannot bind socket for CLI at " << sockpath; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } while (1) { char cmdbuf[1000]; struct sockaddr_un source; socklen_t sourceSize = sizeof(source); int nread = recvfrom(sock,cmdbuf,sizeof(cmdbuf)-1,0,(struct sockaddr*)&source,&sourceSize); gReports.incr("OpenBTS.CLI.Command"); cmdbuf[nread]='\0'; LOG(INFO) << "received command \"" << cmdbuf << "\" from " << source.sun_path; std::ostringstream sout; int res = gParser.process(cmdbuf,sout); const std::string rspString= sout.str(); const char* rsp = rspString.c_str(); LOG(INFO) << "sending " << strlen(rsp) << "-char result to " << source.sun_path; if (sendto(sock,rsp,strlen(rsp)+1,0,(struct sockaddr*)&source,sourceSize)<0) { LOG(ERR) << "can't send CLI response to " << source.sun_path; gReports.incr("OpenBTS.CLI.Command.ResponseFailure"); } // res<0 means to exit the application if (res<0) break; gReports.incr("OpenBTS.Exit.Normal.CLI"); } } // try catch (ConfigurationTableKeyNotFound e) { LOG(EMERG) << "required configuration parameter " << e.key() << " not defined, aborting"; gReports.incr("OpenBTS.Exit.Error.ConfigurationParamterNotFound"); } //if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); close(sock); }
int main(int argc, char *argv[]) { gConfig.setUpdateHook(purgeConfig); const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"}; for (int i=0; i<5; i++) { gConfig.set(keys[i],i); } for (int i=0; i<5; i++) { cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl; cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl; } for (int i=0; i<5; i++) { cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl; } gConfig.set("key5","100 200 300 400 "); std::vector<unsigned> vect = gConfig.getVector("key5"); cout << "vect length " << vect.size() << ": "; for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i]; cout << endl; std::vector<string> svect = gConfig.getVectorOfStrings("key5"); cout << "vect length " << svect.size() << ": "; for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":"; cout << endl; cout << "bool " << gConfig.getBool("booltest") << endl; gConfig.set("booltest",1); cout << "bool " << gConfig.getBool("booltest") << endl; gConfig.set("booltest",0); cout << "bool " << gConfig.getBool("booltest") << endl; gConfig.getStr("newstring"); gConfig.getNum("numnumber"); SimpleKeyValue pairs; pairs.addItems(" a=1 b=34 dd=143 "); cout<< pairs.get("a") << endl; cout<< pairs.get("b") << endl; cout<< pairs.get("dd") << endl; gConfig.set("fkey","123.456"); float fval = gConfig.getFloat("fkey"); cout << "fkey " << fval << endl; cout << "search fkey:" << endl; gConfig.find("fkey",cout); cout << "search fkey:" << endl; gConfig.find("fkey",cout); gConfig.remove("fkey"); cout << "search fkey:" << endl; gConfig.find("fkey",cout); try { gConfig.getNum("supposedtoabort"); } catch (ConfigurationTableKeyNotFound) { cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl; } }
int main(int argc, char *argv[]) { if ( signal( SIGINT, ctrlCHandler ) == SIG_ERR ) { cerr << "Couldn't install signal handler for SIGINT" << endl; exit(1); } if ( signal( SIGTERM, ctrlCHandler ) == SIG_ERR ) { cerr << "Couldn't install signal handler for SIGTERM" << endl; exit(1); } // Configure logger. gLogInit("transceiver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7); if (gLogConn.valid()) Log::gHook = Connection::LogConnection::hook; // Device specific global initialization RadioDevice::staticInit(); int numARFCN=1; if (argc>1) numARFCN = atoi(argv[1]); std::string deviceArgs = ""; if (argc>2) deviceArgs = argv[2]; srandom(time(NULL)); int mOversamplingRate = 1; switch(numARFCN) { // DAVID COMMENT: I have no way to test this, but I would bet that you can // just change these numbers to get different oversampling rates for single-ARFCN // operation.. case 1: mOversamplingRate = 1; break; case 2: mOversamplingRate = 6; break; case 3: mOversamplingRate = 8; break; case 4: mOversamplingRate = 12; break; case 5: mOversamplingRate = 16; break; default: break; } int minOver = gConfig.getNum("TRX.MinOversampling"); if (mOversamplingRate < minOver) mOversamplingRate = minOver; //int mOversamplingRate = numARFCN/2 + numARFCN; //mOversamplingRate = 15; //mOversamplingRate*2; //if ((numARFCN > 1) && (mOversamplingRate % 2)) mOversamplingRate++; /* RAD1Device *usrp = new RAD1Device(mOversamplingRate*1625.0e3/6.0); //DummyLoad *usrp = new DummyLoad(mOversamplingRate*1625.0e3/6.0); usrp->make(false, deviceID); */ RadioDevice* usrp = RadioDevice::make(mOversamplingRate); if (!usrp->open(deviceArgs)) { LOG(ALERT) << "Transceiver exiting..." << std::endl; return EXIT_FAILURE; } RadioInterface* radio = new RadioInterface(usrp,3,SAMPSPERSYM,SAMPSPERSYM*mOversamplingRate,false,numARFCN); Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(2,0),radio, numARFCN,mOversamplingRate,false); trx->receiveFIFO(radio->receiveFIFO()); /* signalVector *gsmPulse = generateGSMPulse(2,1); BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000"; BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[0]),normalBurstSeg); signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,8,1); signalVector *modBurst9 = modulateBurst(normalBurst,*gsmPulse,9,1); signalVector *interpolationFilter = createLPF(0.6/mOversamplingRate,6*mOversamplingRate,1); signalVector totalBurst1(*modBurst,*modBurst9); signalVector totalBurst2(*modBurst,*modBurst); signalVector totalBurst(totalBurst1,totalBurst2); scaleVector(totalBurst,usrp->fullScaleInputValue()); double beaconFreq = -1.0*(numARFCN-1)*200e3; signalVector finalVec(625*mOversamplingRate); for (int j = 0; j < numARFCN; j++) { signalVector *frequencyShifter = new signalVector(625*mOversamplingRate); frequencyShifter->fill(1.0); frequencyShift(frequencyShifter,frequencyShifter,2.0*M_PI*(beaconFreq+j*400e3)/(1625.0e3/6.0*mOversamplingRate)); signalVector *interpVec = polyphaseResampleVector(totalBurst,mOversamplingRate,1,interpolationFilter); multVector(*interpVec,*frequencyShifter); addVector(finalVec,*interpVec); } signalVector::iterator itr = finalVec.begin(); short finalVecShort[2*finalVec.size()]; short *shortItr = finalVecShort; while (itr < finalVec.end()) { *shortItr++ = (short) (itr->real()); *shortItr++ = (short) (itr->imag()); itr++; } usrp->loadBurst(finalVecShort,finalVec.size()); */ trx->start(); gLogConn.write("Starting transceiver"); //int i = 0; while(!gbShutdown) { sleep(1); } //i++; if (i==60) exit(1);} cout << "Shutting down transceiver..." << endl; // trx->stop(); delete trx; // delete radio; }
int main(int argc, char *argv[]) { if ( signal( SIGINT, ctrlCHandler ) == SIG_ERR ) { cerr << "Couldn't install signal handler for SIGINT" << endl; exit(1); } if ( signal( SIGTERM, ctrlCHandler ) == SIG_ERR ) { cerr << "Couldn't install signal handler for SIGTERM" << endl; exit(1); } // Configure logger. gLogInit("transceiver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7); int numARFCN=1; if (argc>1) numARFCN = atoi(argv[1]); int deviceID = 0; if (argc>2) deviceID = atoi(argv[2]); gFactoryCalibration.readEEPROM(deviceID); srandom(time(NULL)); int mOversamplingRate = 1; switch(numARFCN) { case 1: mOversamplingRate = 1; break; case 2: mOversamplingRate = 6; break; case 3: mOversamplingRate = 8; break; case 4: mOversamplingRate = 12; break; case 5: mOversamplingRate = 16; break; default: break; } //int mOversamplingRate = numARFCN/2 + numARFCN; //mOversamplingRate = 15; //mOversamplingRate*2; //if ((numARFCN > 1) && (mOversamplingRate % 2)) mOversamplingRate++; RAD1Device *usrp = new RAD1Device(mOversamplingRate*1625.0e3/6.0); //DummyLoad *usrp = new DummyLoad(mOversamplingRate*1625.0e3/6.0); usrp->make(false, deviceID); RadioInterface* radio = new RadioInterface(usrp,3,SAMPSPERSYM,mOversamplingRate,false,numARFCN); Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(2,0),radio, numARFCN,mOversamplingRate,false); trx->receiveFIFO(radio->receiveFIFO()); /* signalVector *gsmPulse = generateGSMPulse(2,1); BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000"; BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[0]),normalBurstSeg); signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,8,1); signalVector *modBurst9 = modulateBurst(normalBurst,*gsmPulse,9,1); signalVector *interpolationFilter = createLPF(0.6/mOversamplingRate,6*mOversamplingRate,1); signalVector totalBurst1(*modBurst,*modBurst9); signalVector totalBurst2(*modBurst,*modBurst); signalVector totalBurst(totalBurst1,totalBurst2); scaleVector(totalBurst,usrp->fullScaleInputValue()); double beaconFreq = -1.0*(numARFCN-1)*200e3; signalVector finalVec(625*mOversamplingRate); for (int j = 0; j < numARFCN; j++) { signalVector *frequencyShifter = new signalVector(625*mOversamplingRate); frequencyShifter->fill(1.0); frequencyShift(frequencyShifter,frequencyShifter,2.0*M_PI*(beaconFreq+j*400e3)/(1625.0e3/6.0*mOversamplingRate)); signalVector *interpVec = polyphaseResampleVector(totalBurst,mOversamplingRate,1,interpolationFilter); multVector(*interpVec,*frequencyShifter); addVector(finalVec,*interpVec); } signalVector::iterator itr = finalVec.begin(); short finalVecShort[2*finalVec.size()]; short *shortItr = finalVecShort; while (itr < finalVec.end()) { *shortItr++ = (short) (itr->real()); *shortItr++ = (short) (itr->imag()); itr++; } usrp->loadBurst(finalVecShort,finalVec.size()); */ trx->start(); //int i = 0; while(!gbShutdown) { sleep(1); } //i++; if (i==60) exit(1);} cout << "Shutting down transceiver..." << endl; // trx->stop(); delete trx; // delete radio; }
int main(int argc, char *argv[]) { //mtrace(); // (pat) Enable memory leak detection. Unfortunately, huge amounts of code have been started in the constructors above. gLogGroup.setAll(); // TODO: Properly parse and handle any arguments if (argc > 1) { bool testflag = false; for (int argi = 1; argi < argc; argi++) { // Skip argv[0] which is the program name. if (!strcmp(argv[argi], "--version") || !strcmp(argv[argi], "-v")) { // Print the version number and exit immediately. cout << gVersionString << endl; return 0; } if (!strcmp(argv[argi], "--test")) { testflag = true; continue; } if (!strcmp(argv[argi], "--gensql")) { cout << gConfig.getDefaultSQL(string(argv[0]), gVersionString) << endl; return 0; } if (!strcmp(argv[argi], "--gentex")) { cout << gConfig.getTeX(string(argv[0]), gVersionString) << endl; return 0; } // (pat) Adding support for specified sql file. // Unfortunately, the Config table was inited quite some time ago, // so stick this arg in the environment, whence the ConfigurationTable can find it, and then reboot. if (!strcmp(argv[argi],"--config")) { if (++argi == argc) { LOG(ALERT) <<"Missing argument to --config option"; exit(2); } setenv(cOpenBTSConfigEnv,argv[argi],1); execl(argv[0],"OpenBTS",NULL); LOG(ALERT) <<"execl failed? Exiting..."; exit(0); } if (!strcmp(argv[argi],"--help")) { printf("OpenBTS [--version --gensql --gentex] [--config file.db]\n"); printf("OpenBTS exiting...\n"); exit(0); } printf("OpenBTS: unrecognized argument: %s\nexiting...\n",argv[argi]); } if (testflag) { GSM::TestTCHL1FEC(); return 0; } } createStats(); gConfig.setCrossCheckHook(&configurationCrossCheck); gReports.incr("OpenBTS.Starts"); gNeighborTable.NeighborTableInit( gConfig.getStr("Peering.NeighborTable.Path").c_str()); int sock = socket(AF_UNIX,SOCK_DGRAM,0); if (sock<0) { perror("creating CLI datagram socket"); LOG(ALERT) << "cannot create socket for CLI"; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); LOG(ALERT) << "OpenBTS (re)starting, ver " << VERSION << " build date " << __DATE__; LOG(ALERT) << "OpenBTS reading config file "<<cOpenBTSConfigFile; COUT("\n\n" << gOpenBTSWelcome << "\n"); Control::controlInit(); // init Layer3: TMSITable, TransactionTable. gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gParser.addCommands(); COUT("\nStarting the system..."); // is the radio running? // Start the transceiver interface. LOG(INFO) << "checking transceiver"; //gTRX.ARFCN(0)->powerOn(); //sleep(gConfig.getNum("TRX.Timeout.Start")); //bool haveTRX = gTRX.ARFCN(0)->powerOn(false); This prints an inapplicable warning message. bool haveTRX = gTRX.ARFCN(0)->trxRunning(); // This does not print an inapplicable warning message. Thread transceiverThread; if (!haveTRX) { LOG(ALERT) << "starting the transceiver"; transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // sleep to let the FPGA code load // TODO: we should be "pinging" the radio instead of sleeping sleep(5); } else { LOG(NOTICE) << "transceiver already running"; } // Start the SIP interface. SIP::SIPInterfaceStart(); // Start the peer interface gPeerInterface.start(); // Sync factory calibration as defaults from radio EEPROM signed sdrsn = gTRX.ARFCN(0)->getFactoryCalibration("sdrsn"); if (sdrsn != 0 && sdrsn != 65535) { signed val; val = gTRX.ARFCN(0)->getFactoryCalibration("band"); if (gConfig.isValidValue("GSM.Radio.Band", val)) { gConfig.mSchema["GSM.Radio.Band"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("freq"); if (gConfig.isValidValue("TRX.RadioFrequencyOffset", val)) { gConfig.mSchema["TRX.RadioFrequencyOffset"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("rxgain"); if (gConfig.isValidValue("GSM.Radio.RxGain", val)) { gConfig.mSchema["GSM.Radio.RxGain"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("txgain"); if (gConfig.isValidValue("TRX.TxAttenOffset", val)) { gConfig.mSchema["TRX.TxAttenOffset"].updateDefaultValue(val); } } // Limit valid ARFCNs to current band gConfig.mSchema["GSM.Radio.C0"].updateValidValues(getARFCNsString(gConfig.getNum("GSM.Radio.Band"))); // // Configure the radio. // gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. //C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); unsigned numARFCNs = gConfig.getNum("GSM.Radio.ARFCNs"); for (unsigned i=0; i<numARFCNs; i++) { // Tune the radios. unsigned ARFCN = C0 + i*2; LOG(INFO) << "tuning TRX " << i << " to ARFCN " << ARFCN; ARFCNManager* radio = gTRX.ARFCN(i); radio->tune(ARFCN); } // Send either TSC or full BSIC depending on radio need if (gConfig.getBool("GSM.Radio.NeedBSIC")) { // Send BSIC to C0radio->setBSIC(gBTS.BSIC()); } else { // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); } // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(true); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs // (pat) I thought config 'GSM.CCCH.CCCH-CONF' was supposed to control the number of SDCCH allocated? SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,0,gSDCCH_4_0), SDCCHLogicalChannel(0,0,gSDCCH_4_1), SDCCHLogicalChannel(0,0,gSDCCH_4_2), SDCCHLogicalChannel(0,0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; // Subchannel 2 used for CBCH if SMSCB enabled. bool SMSCB = (gConfig.getStr("Control.SMSCB.Table").length() != 0); CBCHLogicalChannel CBCH(gSDCCH_4_2); Thread CBCHControlThread; for (int i=0; i<4; i++) { if (SMSCB && (i==2)) continue; C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // Install CBCH if used. if (SMSCB) { LOG(INFO) << "creating CBCH for SMSCB"; CBCH.downstream(C0radio); CBCH.open(); gBTS.addCBCH(&CBCH); CBCHControlThread.start((void*(*)(void*))Control::SMSCBSender,NULL); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (!gConfig.defines("GSM.Channels.NumC1s")) { int numChan = numARFCNs*7; LOG(CRIT) << "GSM.Channels.NumC1s not defined. Defaulting to " << numChan << "."; gConfig.set("GSM.Channels.NumC1s",numChan); } if (!gConfig.defines("GSM.Channels.NumC7s")) { int numChan = numARFCNs-1; LOG(CRIT) << "GSM.Channels.NumC7s not defined. Defaulting to " << numChan << "."; gConfig.set("GSM.Channels.NumC7s",numChan); } // sanity check on channel counts // the clamp here could be improved to take the customer's current ratio of C1:C7 and scale it back to fit in the window if (((numARFCNs * 8) - 1) < (gConfig.getNum("GSM.Channels.NumC1s") + gConfig.getNum("GSM.Channels.NumC7s"))) { LOG(CRIT) << "scaling back GSM.Channels.NumC1s and GSM.Channels.NumC7s to fit inside number of available timeslots"; gConfig.set("GSM.Channels.NumC1s",numARFCNs*7); gConfig.set("GSM.Channels.NumC7s",numARFCNs-1); } if (gConfig.getBool("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount/8,sCount%8); sCount++; } if (!gConfig.getBool("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } if (sCount<(numARFCNs*8)) { LOG(CRIT) << "Only " << sCount << " timeslots configured in an " << numARFCNs << "-ARFCN system."; } // Set up idle filling on C0 as needed for unconfigured slots.. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* (pat) See GSM 05.02 6.5.2 and 3.3.2.3 Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. if (gConfig.getNum("GSM.Channels.SDCCHReserve")>=(int)gBTS.SDCCHTotal()) { unsigned val = gBTS.SDCCHTotal() - 1; LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val; gConfig.set("GSM.Channels.SDCCHReserve",val); } // OK, now it is safe to start the BTS. gBTS.start(); struct sockaddr_un cmdSockName; cmdSockName.sun_family = AF_UNIX; const char* sockpath = gConfig.getStr("CLI.SocketPath").c_str(); char rmcmd[strlen(sockpath)+5]; sprintf(rmcmd,"rm -f %s",sockpath); if (system(rmcmd)) {} // The 'if' shuts up gcc warnings. strcpy(cmdSockName.sun_path,sockpath); LOG(INFO) "binding CLI datagram socket at " << sockpath; if (bind(sock, (struct sockaddr *) &cmdSockName, sizeof(struct sockaddr_un))) { perror("binding name to cmd datagram socket"); LOG(ALERT) << "cannot bind socket for CLI at " << sockpath; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } COUT("\nsystem ready\n"); if (chmod(sockpath, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) { perror("sockpath"); // don't exit, at this point, we must run CLI as root COUT("\nuse the OpenBTSCLI utility to access CLI as root\n"); } else { COUT("\nuse the OpenBTSCLI utility to access CLI\n"); } LOG(INFO) << "system ready"; gParser.startCommandLine(); while (1) { char cmdbuf[1000]; struct sockaddr_un source; socklen_t sourceSize = sizeof(source); int nread = recvfrom(sock,cmdbuf,sizeof(cmdbuf)-1,0,(struct sockaddr*)&source,&sourceSize); gReports.incr("OpenBTS.CLI.Command"); cmdbuf[nread]='\0'; LOG(INFO) << "received command \"" << cmdbuf << "\" from " << source.sun_path; std::ostringstream sout; int res = gParser.process(cmdbuf,sout); const std::string rspString= sout.str(); const char* rsp = rspString.c_str(); LOG(INFO) << "sending " << strlen(rsp) << "-char result to " << source.sun_path; if (sendto(sock,rsp,strlen(rsp)+1,0,(struct sockaddr*)&source,sourceSize)<0) { LOG(ERR) << "can't send CLI response to " << source.sun_path; gReports.incr("OpenBTS.CLI.Command.ResponseFailure"); } // res<0 means to exit the application if (res<0) break; } } // try catch (ConfigurationTableKeyNotFound e) { LOG(EMERG) << "required configuration parameter " << e.key() << " not defined, aborting"; gReports.incr("OpenBTS.Exit.Error.ConfigurationParamterNotFound"); } LOG(ALERT) << "exiting OpenBTS as directed by command line..."; //if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); close(sock); }
/* 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; 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; 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_RAND: fillstr = "Normal 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 << " Samples-per-Symbol...... " << config->sps << 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; }