Merge remote-tracking branch 'origin/develop' into RU-RAU-split
Conflicts: openair1/PHY/INIT/lte_init.c openair1/PHY/LTE_ESTIMATION/defs.h openair1/PHY/LTE_ESTIMATION/lte_ue_measurements.c openair1/PHY/LTE_TRANSPORT/dci.c openair1/PHY/LTE_TRANSPORT/dci_tools.c openair1/PHY/LTE_TRANSPORT/defs.h openair1/PHY/LTE_TRANSPORT/dlsch_decoding.c openair1/PHY/LTE_TRANSPORT/dlsch_demodulation.c openair1/PHY/LTE_TRANSPORT/dlsch_llr_computation.c openair1/PHY/LTE_TRANSPORT/if5_tools.c openair1/PHY/LTE_TRANSPORT/phich.c openair1/PHY/LTE_TRANSPORT/proto.h openair1/PHY/defs.h openair1/SCHED/phy_procedures_lte_eNb.c openair1/SCHED/phy_procedures_lte_ue.c openair1/SCHED/pusch_pc.c openair2/ENB_APP/enb_config.c openair2/LAYER2/MAC/defs.h openair2/LAYER2/MAC/eNB_scheduler.c openair2/LAYER2/MAC/eNB_scheduler_dlsch.c openair2/LAYER2/MAC/eNB_scheduler_primitives.c openair2/LAYER2/MAC/eNB_scheduler_ulsch.c openair2/LAYER2/MAC/pre_processor.c openair2/LAYER2/MAC/ue_procedures.c targets/RT/USER/lte-enb.c targets/RT/USER/lte-softmodem.c targets/RT/USER/lte-softmodem.h targets/RT/USER/lte-ue.c targets/SIMU/USER/init_lte.c targets/SIMU/USER/oaisim_functions.c
This commit is contained in:
@@ -82,6 +82,48 @@ openair0_timestamp trx_get_timestamp(openair0_device *device, bladerf_module mod
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int trx_brf_start(openair0_device *device) {
|
||||
brf_state_t *brf = (brf_state_t*)device->priv;
|
||||
int status;
|
||||
|
||||
brf->meta_tx.flags = 0;
|
||||
|
||||
if ((status = bladerf_sync_config(brf->dev,
|
||||
BLADERF_MODULE_TX,
|
||||
BLADERF_FORMAT_SC16_Q11_META,
|
||||
brf->num_buffers,
|
||||
brf->buffer_size,
|
||||
brf->num_transfers,
|
||||
100/*brf->tx_timeout_ms*/)) != 0 ) {
|
||||
fprintf(stderr,"Failed to configure TX sync interface: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
if ((status = bladerf_sync_config(brf->dev,
|
||||
BLADERF_MODULE_RX,
|
||||
BLADERF_FORMAT_SC16_Q11_META,
|
||||
brf->num_buffers,
|
||||
brf->buffer_size,
|
||||
brf->num_transfers,
|
||||
100/*brf->rx_timeout_ms*/)) != 0 ) {
|
||||
fprintf(stderr,"Failed to configure RX sync interface: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, true)) != 0) {
|
||||
fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, true)) != 0) {
|
||||
fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
|
||||
if ((status=bladerf_calibrate_dc(brf->dev, BLADERF_MODULE_TX)) != 0) {
|
||||
fprintf(stderr,"Failed to calibrate TX DC: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
if ((status=bladerf_calibrate_dc(brf->dev, BLADERF_MODULE_RX)) != 0) {
|
||||
fprintf(stderr,"Failed to calibrate RX DC: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -133,7 +175,7 @@ static int trx_brf_write(openair0_device *device,openair0_timestamp ptimestamp,
|
||||
brf->tx_count++;
|
||||
|
||||
|
||||
return(0);
|
||||
return nsamps; //brf->meta_tx.actual_count;
|
||||
}
|
||||
|
||||
/*! \brief Receive samples from hardware.
|
||||
@@ -155,6 +197,7 @@ static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp,
|
||||
// BRF has only one rx/tx chain
|
||||
int16_t *samples = (int16_t*)buff[0];
|
||||
|
||||
brf->meta_rx.actual_count = 0;
|
||||
brf->meta_rx.flags = BLADERF_META_FLAG_RX_NOW;
|
||||
status = bladerf_sync_rx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_rx, 2*brf->rx_timeout_ms);
|
||||
|
||||
@@ -170,6 +213,10 @@ static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp,
|
||||
brf->num_overflows,brf->meta_rx.timestamp, brf->meta_rx.actual_count, nsamps);
|
||||
}
|
||||
|
||||
if (brf->meta_rx.actual_count != nsamps) {
|
||||
printf("RX bad samples count, wanted %d, got %d\n", nsamps, brf->meta_rx.actual_count);
|
||||
}
|
||||
|
||||
//printf("Current RX timestampe %u\n", brf->meta_rx.timestamp);
|
||||
//printf("[BRF] (buff %p) ts=0x%"PRIu64" %s\n",samples, brf->meta_rx.timestamp,bladerf_strerror(status));
|
||||
brf->rx_current_ts=brf->meta_rx.timestamp;
|
||||
@@ -180,7 +227,7 @@ static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp,
|
||||
|
||||
*ptimestamp = brf->meta_rx.timestamp;
|
||||
|
||||
return brf->meta_rx.actual_count;
|
||||
return nsamps; //brf->meta_rx.actual_count;
|
||||
|
||||
}
|
||||
|
||||
@@ -188,6 +235,7 @@ static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp,
|
||||
* \param device the hardware to use
|
||||
*/
|
||||
void trx_brf_end(openair0_device *device) {
|
||||
abort();
|
||||
|
||||
int status;
|
||||
brf_state_t *brf = (brf_state_t*)device->priv;
|
||||
@@ -1081,6 +1129,15 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
|
||||
// memcpy((void*)&device->openair0_cfg,(void*)&openair0_cfg[0],sizeof(openair0_config_t));
|
||||
|
||||
if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, false)) != 0) {
|
||||
fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, false)) != 0) {
|
||||
fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status));
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,9 @@ typedef enum {
|
||||
//! This tells the underlying hardware to use the internal reference
|
||||
internal=0,
|
||||
//! This tells the underlying hardware to use the external reference
|
||||
external=1
|
||||
external=1,
|
||||
//! This tells the underlying hardware to use the gpsdo reference
|
||||
gpsdo=2
|
||||
} clock_source_t;
|
||||
|
||||
/*! \brief RF frontend parameters set by application */
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <uhd/version.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
#include <complex>
|
||||
#include <fstream>
|
||||
@@ -92,12 +94,181 @@ typedef struct {
|
||||
int num_seq_errors;
|
||||
int64_t tx_count;
|
||||
int64_t rx_count;
|
||||
int wait_for_first_pps;
|
||||
int use_gps;
|
||||
//! timestamp of RX packet
|
||||
openair0_timestamp rx_timestamp;
|
||||
|
||||
} usrp_state_t;
|
||||
|
||||
//void print_notes(void)
|
||||
//{
|
||||
// Helpful notes
|
||||
// std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n");
|
||||
// std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n");
|
||||
// std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n");
|
||||
// std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n");
|
||||
// std::cout << boost::format("****************************************************************************************************************\n");
|
||||
//}
|
||||
|
||||
static int sync_to_gps(openair0_device *device)
|
||||
{
|
||||
uhd::set_thread_priority_safe();
|
||||
|
||||
//std::string args;
|
||||
|
||||
//Set up program options
|
||||
//po::options_description desc("Allowed options");
|
||||
//desc.add_options()
|
||||
//("help", "help message")
|
||||
//("args", po::value<std::string>(&args)->default_value(""), "USRP device arguments")
|
||||
//;
|
||||
//po::variables_map vm;
|
||||
//po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
//po::notify(vm);
|
||||
|
||||
//Print the help message
|
||||
//if (vm.count("help"))
|
||||
//{
|
||||
// std::cout << boost::format("Synchronize USRP to GPS %s") % desc << std::endl;
|
||||
// return EXIT_FAILURE;
|
||||
//}
|
||||
|
||||
//Create a USRP device
|
||||
//std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args;
|
||||
//uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
|
||||
//std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string();
|
||||
|
||||
usrp_state_t *s = (usrp_state_t*)device->priv;
|
||||
|
||||
try
|
||||
{
|
||||
size_t num_mboards = s->usrp->get_num_mboards();
|
||||
size_t num_gps_locked = 0;
|
||||
for (size_t mboard = 0; mboard < num_mboards; mboard++)
|
||||
{
|
||||
std::cout << "Synchronizing mboard " << mboard << ": " << s->usrp->get_mboard_name(mboard) << std::endl;
|
||||
|
||||
//Set references to GPSDO
|
||||
s->usrp->set_clock_source("gpsdo", mboard);
|
||||
s->usrp->set_time_source("gpsdo", mboard);
|
||||
|
||||
//std::cout << std::endl;
|
||||
//print_notes();
|
||||
//std::cout << std::endl;
|
||||
|
||||
//Check for 10 MHz lock
|
||||
std::vector<std::string> sensor_names = s->usrp->get_mboard_sensor_names(mboard);
|
||||
if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())
|
||||
{
|
||||
std::cout << "Waiting for reference lock..." << std::flush;
|
||||
bool ref_locked = false;
|
||||
for (int i = 0; i < 30 and not ref_locked; i++)
|
||||
{
|
||||
ref_locked = s->usrp->get_mboard_sensor("ref_locked", mboard).to_bool();
|
||||
if (not ref_locked)
|
||||
{
|
||||
std::cout << "." << std::flush;
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(1));
|
||||
}
|
||||
}
|
||||
if(ref_locked)
|
||||
{
|
||||
std::cout << "LOCKED" << std::endl;
|
||||
} else {
|
||||
std::cout << "FAILED" << std::endl;
|
||||
std::cout << "Failed to lock to GPSDO 10 MHz Reference. Exiting." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << boost::format("ref_locked sensor not present on this board.\n");
|
||||
}
|
||||
|
||||
//Wait for GPS lock
|
||||
bool gps_locked = s->usrp->get_mboard_sensor("gps_locked", mboard).to_bool();
|
||||
if(gps_locked)
|
||||
{
|
||||
num_gps_locked++;
|
||||
std::cout << boost::format("GPS Locked\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "WARNING: GPS not locked - time will not be accurate until locked" << std::endl;
|
||||
}
|
||||
|
||||
//Set to GPS time
|
||||
uhd::time_spec_t gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
|
||||
//s->usrp->set_time_next_pps(gps_time+1.0, mboard);
|
||||
s->usrp->set_time_next_pps(uhd::time_spec_t(0.0));
|
||||
|
||||
//Wait for it to apply
|
||||
//The wait is 2 seconds because N-Series has a known issue where
|
||||
//the time at the last PPS does not properly update at the PPS edge
|
||||
//when the time is actually set.
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(2));
|
||||
|
||||
//Check times
|
||||
gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
|
||||
uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps(mboard);
|
||||
std::cout << "USRP time: " << (boost::format("%0.9f") % time_last_pps.get_real_secs()) << std::endl;
|
||||
std::cout << "GPSDO time: " << (boost::format("%0.9f") % gps_time.get_real_secs()) << std::endl;
|
||||
//if (gps_time.get_real_secs() == time_last_pps.get_real_secs())
|
||||
// std::cout << std::endl << "SUCCESS: USRP time synchronized to GPS time" << std::endl << std::endl;
|
||||
//else
|
||||
// std::cerr << std::endl << "ERROR: Failed to synchronize USRP time to GPS time" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
if (num_gps_locked == num_mboards and num_mboards > 1)
|
||||
{
|
||||
//Check to see if all USRP times are aligned
|
||||
//First, wait for PPS.
|
||||
uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps();
|
||||
while (time_last_pps == s->usrp->get_time_last_pps())
|
||||
{
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
|
||||
}
|
||||
|
||||
//Sleep a little to make sure all devices have seen a PPS edge
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
||||
|
||||
//Compare times across all mboards
|
||||
bool all_matched = true;
|
||||
uhd::time_spec_t mboard0_time = s->usrp->get_time_last_pps(0);
|
||||
for (size_t mboard = 1; mboard < num_mboards; mboard++)
|
||||
{
|
||||
uhd::time_spec_t mboard_time = s->usrp->get_time_last_pps(mboard);
|
||||
if (mboard_time != mboard0_time)
|
||||
{
|
||||
all_matched = false;
|
||||
std::cerr << (boost::format("ERROR: Times are not aligned: USRP 0=%0.9f, USRP %d=%0.9f")
|
||||
% mboard0_time.get_real_secs()
|
||||
% mboard
|
||||
% mboard_time.get_real_secs()) << std::endl;
|
||||
}
|
||||
}
|
||||
if (all_matched)
|
||||
{
|
||||
std::cout << "SUCCESS: USRP times aligned" << std::endl << std::endl;
|
||||
} else {
|
||||
std::cout << "ERROR: USRP times are not aligned" << std::endl << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << boost::format("\nError: %s") % e.what();
|
||||
std::cout << boost::format("This could mean that you have not installed the GPSDO correctly.\n\n");
|
||||
std::cout << boost::format("Visit one of these pages if the problem persists:\n");
|
||||
std::cout << boost::format(" * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html");
|
||||
std::cout << boost::format(" * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n");
|
||||
std::cout << boost::format(" * E3X0: http://files.ettus.com/manual/page_usrp_e3x0.html#e3x0_hw_gps\n\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*! \brief Called to start the USRP transceiver. Return 0 if OK, < 0 if error
|
||||
@param device pointer to the device structure specific to the RF hardware target
|
||||
@@ -106,9 +277,31 @@ static int trx_usrp_start(openair0_device *device) {
|
||||
|
||||
usrp_state_t *s = (usrp_state_t*)device->priv;
|
||||
|
||||
|
||||
// setup GPIO for TDD, GPIO(4) = ATR_RX
|
||||
//set data direction register (DDR) to output
|
||||
s->usrp->set_gpio_attr("FP0", "DDR", 0x1f, 0x1f);
|
||||
|
||||
//set control register to ATR
|
||||
s->usrp->set_gpio_attr("FP0", "CTRL", 0x1f,0x1f);
|
||||
|
||||
//set ATR register
|
||||
s->usrp->set_gpio_attr("FP0", "ATR_RX", 1<<4, 0x1f);
|
||||
|
||||
// init recv and send streaming
|
||||
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
|
||||
cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
|
||||
LOG_I(PHY,"Time in secs now: %llu \n", s->usrp->get_time_now().to_ticks(s->sample_rate));
|
||||
LOG_I(PHY,"Time in secs last pps: %llu \n", s->usrp->get_time_last_pps().to_ticks(s->sample_rate));
|
||||
|
||||
if (s->use_gps == 1) {
|
||||
s->wait_for_first_pps = 1;
|
||||
cmd.time_spec = s->usrp->get_time_last_pps() + uhd::time_spec_t(1.0);
|
||||
}
|
||||
else {
|
||||
s->wait_for_first_pps = 0;
|
||||
cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
|
||||
}
|
||||
|
||||
cmd.stream_now = false; // start at constant delay
|
||||
s->rx_stream->issue_stream_cmd(cmd);
|
||||
|
||||
@@ -256,9 +449,11 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
|
||||
while (samples_received != nsamps) {
|
||||
samples_received += s->rx_stream->recv(buff_tmp[0]+samples_received,
|
||||
nsamps-samples_received, s->rx_md);
|
||||
if (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE)
|
||||
if ((s->wait_for_first_pps == 0) && (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE))
|
||||
break;
|
||||
if ((s->wait_for_first_pps == 1) && (samples_received != nsamps)) { printf("sleep...\n");} //usleep(100);
|
||||
}
|
||||
if (samples_received == nsamps) s->wait_for_first_pps=0;
|
||||
}
|
||||
// bring RX data into 12 LSBs for softmodem RX
|
||||
for (int i=0; i<cc; i++) {
|
||||
@@ -497,6 +692,10 @@ extern "C" {
|
||||
int device_init(openair0_device* device, openair0_config_t *openair0_cfg) {
|
||||
uhd::set_thread_priority_safe(1.0);
|
||||
usrp_state_t *s = (usrp_state_t*)calloc(sizeof(usrp_state_t),1);
|
||||
|
||||
if (openair0_cfg[0].clock_source==gpsdo)
|
||||
s->use_gps =1;
|
||||
|
||||
// Initialize USRP device
|
||||
device->openair0_cfg = openair0_cfg;
|
||||
|
||||
@@ -590,10 +789,13 @@ extern "C" {
|
||||
// set master clock rate and sample rate for tx & rx for streaming
|
||||
|
||||
// lock mboard clocks
|
||||
if (openair0_cfg[0].clock_source == internal)
|
||||
s->usrp->set_clock_source("internal");
|
||||
else
|
||||
if (openair0_cfg[0].clock_source == internal){
|
||||
s->usrp->set_clock_source("internal");
|
||||
}
|
||||
else{
|
||||
s->usrp->set_clock_source("external");
|
||||
s->usrp->set_time_source("external");
|
||||
}
|
||||
|
||||
device->type = USRP_B200_DEV;
|
||||
if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
|
||||
@@ -714,8 +916,6 @@ extern "C" {
|
||||
for(int i=0; i<s->usrp->get_rx_num_channels() && i<openair0_cfg[0].rx_num_channels; i++)
|
||||
s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
|
||||
|
||||
s->usrp->set_time_now(uhd::time_spec_t(0.0));
|
||||
|
||||
for (int i=0; i<openair0_cfg[0].rx_num_channels; i++) {
|
||||
LOG_I(PHY,"RX Channel %d\n",i);
|
||||
LOG_I(PHY," Actual RX sample rate: %fMSps...\n",s->usrp->get_rx_rate(i)/1e6);
|
||||
@@ -757,6 +957,14 @@ extern "C" {
|
||||
s->tx_forward_nsamps = 90;
|
||||
if(is_equal(s->sample_rate, (double)7.68e6))
|
||||
s->tx_forward_nsamps = 50;
|
||||
|
||||
if (s->use_gps == 1) {
|
||||
if (sync_to_gps(device)) {
|
||||
LOG_I(PHY,"USRP fails to sync with GPS...\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "common_lib.h"
|
||||
#include "ethernet_lib.h"
|
||||
|
||||
#include "mobipass.h"
|
||||
#include "queues.h"
|
||||
|
||||
struct mobipass_header {
|
||||
uint16_t flags;
|
||||
uint16_t fifo_status;
|
||||
unsigned char seqno;
|
||||
unsigned char ack;
|
||||
uint32_t word0;
|
||||
uint32_t timestamp;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
int mobipass_start(openair0_device *device) { init_mobipass(device->priv); return 0; }
|
||||
int mobipass_request(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
|
||||
int mobipass_reply(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
|
||||
int mobipass_get_stats(openair0_device* device) { return 0; }
|
||||
int mobipass_reset_stats(openair0_device* device) { return 0; }
|
||||
void mobipass_end(openair0_device *device) {}
|
||||
int mobipass_stop(openair0_device *device) { return 0; }
|
||||
int mobipass_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) { return 0; }
|
||||
int mobipass_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) { return 0; }
|
||||
|
||||
int mobipass_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
||||
mobipass_state_t *mobi = device->priv;
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(((char *)buff[0]) + 14);
|
||||
mobi->mobipass_write_last_timestamp += 640;
|
||||
mobi->mobipass_write_last_timestamp %= mobi->samples_per_1024_frames;
|
||||
mh->timestamp = htonl(ntohl(mh->timestamp) % mobi->samples_per_1024_frames);
|
||||
if (mobi->mobipass_write_last_timestamp != ntohl(mh->timestamp))
|
||||
{ printf("mobipass: ERROR: bad timestamp wanted %d got %d\n", mobi->mobipass_write_last_timestamp, ntohl(mh->timestamp)); exit(1); }
|
||||
//printf("__write nsamps %d timestamps %ld seqno %d (packet timestamp %d)\n", nsamps, timestamp, mh->seqno, ntohl(mh->timestamp));
|
||||
if (nsamps != 640) abort();
|
||||
enqueue_to_mobipass(mobi->qstate, buff[0]);
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
int mobipass_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
mobipass_state_t *mobi = device->priv;
|
||||
//printf("__read nsamps %d return timestamp %d\n", nsamps, ts);
|
||||
*timestamp = htonl(mobi->mobipass_read_ts);
|
||||
mobi->mobipass_read_ts += nsamps;
|
||||
mobi->mobipass_read_ts %= mobi->samples_per_1024_frames;
|
||||
if (nsamps != 640) { printf("mobipass: ERROR: bad nsamps %d, should be 640\n", nsamps); fflush(stdout); abort(); }
|
||||
|
||||
dequeue_from_mobipass(mobi->qstate, ntohl(*timestamp), buff[0]);
|
||||
|
||||
#if 1
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(((char *)buff[0]) + 14);
|
||||
mh->flags = 0;
|
||||
mh->fifo_status = 0;
|
||||
mh->seqno = mobi->mobipass_read_seqno++;
|
||||
mh->ack = 0;
|
||||
mh->word0 = 0;
|
||||
mh->timestamp = htonl(mobi->mobipass_read_ts);
|
||||
#endif
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
/* this is the only function in the library that is visible from outside
|
||||
* because in CMakeLists.txt we use -fvisibility=hidden
|
||||
*/
|
||||
__attribute__((__visibility__("default")))
|
||||
int transport_init(openair0_device *device, openair0_config_t *openair0_cfg,
|
||||
eth_params_t * eth_params )
|
||||
{
|
||||
//init_mobipass();
|
||||
|
||||
mobipass_state_t *mobi = (mobipass_state_t*)malloc(sizeof(mobipass_state_t));
|
||||
memset(mobi, 0, sizeof(mobipass_state_t));
|
||||
|
||||
if (eth_params->transp_preference != 4) goto err;
|
||||
if (eth_params->if_compress != 0) goto err;
|
||||
|
||||
/* only 50 PRBs handled for the moment */
|
||||
if (openair0_cfg[0].sample_rate != 15360000) {
|
||||
printf("mobipass: ERROR: only 50 PRBs supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mobi->eth.flags = ETH_RAW_IF5_MOBIPASS;
|
||||
mobi->eth.compression = NO_COMPRESS;
|
||||
device->Mod_id = 0;//num_devices_eth++;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
|
||||
device->trx_start_func = mobipass_start;
|
||||
device->trx_request_func = mobipass_request;
|
||||
device->trx_reply_func = mobipass_reply;
|
||||
device->trx_get_stats_func = mobipass_get_stats;
|
||||
device->trx_reset_stats_func = mobipass_reset_stats;
|
||||
device->trx_end_func = mobipass_end;
|
||||
device->trx_stop_func = mobipass_stop;
|
||||
device->trx_set_freq_func = mobipass_set_freq;
|
||||
device->trx_set_gains_func = mobipass_set_gains;
|
||||
device->trx_write_func = mobipass_write;
|
||||
device->trx_read_func = mobipass_read;
|
||||
|
||||
device->priv = mobi;
|
||||
|
||||
mobi->eth.if_name = strdup(eth_params->local_if_name);
|
||||
if (mobi->eth.if_name == NULL) abort();
|
||||
|
||||
if (sscanf(eth_params->my_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&mobi->eth_local[0],
|
||||
&mobi->eth_local[1],
|
||||
&mobi->eth_local[2],
|
||||
&mobi->eth_local[3],
|
||||
&mobi->eth_local[4],
|
||||
&mobi->eth_local[5]) != 6) {
|
||||
printf("mobipass: ERROR: bad local ethernet address '%s', check configuration file\n",
|
||||
eth_params->my_addr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sscanf(eth_params->remote_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&mobi->eth_remote[0],
|
||||
&mobi->eth_remote[1],
|
||||
&mobi->eth_remote[2],
|
||||
&mobi->eth_remote[3],
|
||||
&mobi->eth_remote[4],
|
||||
&mobi->eth_remote[5]) != 6) {
|
||||
printf("mobipass: ERROR: bad remote ethernet address '%s', check configuration file\n",
|
||||
eth_params->remote_addr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* note: this only works for 50 PRBs */
|
||||
mobi->samples_per_1024_frames = 7680*2*10*1024;
|
||||
|
||||
/* TX starts at subframe 4, let's pretend we are at the right position */
|
||||
/* note: this only works for 50 PRBs */
|
||||
mobi->mobipass_write_last_timestamp = 4*7680*2-640;
|
||||
|
||||
/* device specific */
|
||||
openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
||||
openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
|
||||
openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
|
||||
|
||||
/* this is useless, I think */
|
||||
if (device->host_type == BBU_HOST) {
|
||||
/*Note scheduling advance values valid only for case 7680000 */
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
openair0_cfg[0].samples_per_packet = 3840;
|
||||
break;
|
||||
case 23040000:
|
||||
openair0_cfg[0].samples_per_packet = 2880;
|
||||
break;
|
||||
case 15360000:
|
||||
openair0_cfg[0].samples_per_packet = 1920;
|
||||
break;
|
||||
case 7680000:
|
||||
openair0_cfg[0].samples_per_packet = 960;
|
||||
break;
|
||||
case 1920000:
|
||||
openair0_cfg[0].samples_per_packet = 240;
|
||||
break;
|
||||
default:
|
||||
printf("mobipass: ERROR: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
printf("mobipass: ERROR: bad configuration file?\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "queues.h"
|
||||
#include "mobipass.h"
|
||||
|
||||
/******************************************************************/
|
||||
/* time begin */
|
||||
/******************************************************************/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static void init_time(mobipass_state_t *mobi)
|
||||
{
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &now)) abort();
|
||||
mobi->t0 = (uint64_t)now.tv_sec * (uint64_t)1000000000 + (uint64_t)now.tv_nsec;
|
||||
}
|
||||
|
||||
/* called before sending data to mobipass
|
||||
* waits if called too early with respect to system clock
|
||||
* does not wait more than 1ms in any case
|
||||
*/
|
||||
static void synch_time(mobipass_state_t *mobi, uint32_t ts)
|
||||
{
|
||||
if (ts < mobi->synch_time_last_ts) mobi->synch_time_mega_ts++;
|
||||
mobi->synch_time_last_ts = ts;
|
||||
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &now)) abort();
|
||||
|
||||
uint64_t tnow;
|
||||
tnow = (uint64_t)now.tv_sec * (uint64_t)1000000000 + (uint64_t)now.tv_nsec;
|
||||
|
||||
uint64_t cur = tnow - mobi->t0;
|
||||
|
||||
/* 15360000 samples/second, in nanoseconds:
|
||||
* = 15360000 / 1000000000 = 1536 / 100000 = 48 / 3125*/
|
||||
|
||||
uint64_t ts_ns = ((uint64_t)ts + mobi->synch_time_mega_ts * (uint64_t)mobi->samples_per_1024_frames) * (uint64_t)3125 / (uint64_t)48;
|
||||
|
||||
/* TODO: if cur is way higher than ts_ns, we are very late, log something? */
|
||||
if (cur >= ts_ns) return;
|
||||
|
||||
uint64_t delta = ts_ns - cur;
|
||||
/* don't sleep more than 1 ms */
|
||||
if (delta > 1000*1000) delta = 1000*1000;
|
||||
delta = delta/1000;
|
||||
if (delta) usleep(delta);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* time end */
|
||||
/******************************************************************/
|
||||
|
||||
struct ethernet_header {
|
||||
unsigned char dst[6];
|
||||
unsigned char src[6];
|
||||
uint16_t packet_type;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mobipass_header {
|
||||
uint16_t flags;
|
||||
uint16_t fifo_status;
|
||||
unsigned char seqno;
|
||||
unsigned char ack;
|
||||
uint32_t word0;
|
||||
uint32_t timestamp;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
static void do_receive(mobipass_state_t *mobi, unsigned char *b)
|
||||
{
|
||||
if (recv(mobi->sock, b, 14+14+1280, 0) != 14+14+1280) { perror("recv"); exit(1); }
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(b+14);
|
||||
mh->timestamp = htonl((ntohl(mh->timestamp)-45378/*40120*/) % mobi->samples_per_1024_frames);
|
||||
//printf("recv timestamp %u\n", ntohl(mh->timestamp));
|
||||
}
|
||||
|
||||
/* receiver thread */
|
||||
static void *receiver(void *_mobi)
|
||||
{
|
||||
mobipass_state_t *mobi = _mobi;
|
||||
unsigned char receive_packet[14 + 14 + 1280];
|
||||
while (1) {
|
||||
do_receive(mobi, receive_packet);
|
||||
enqueue_from_mobipass(mobi->qstate, receive_packet);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_send(mobipass_state_t *mobi, int seqno, uint32_t ts,
|
||||
unsigned char *packet)
|
||||
{
|
||||
struct ethernet_header *eh = (struct ethernet_header *)packet;
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(packet+14);
|
||||
|
||||
ts %= mobi->samples_per_1024_frames;
|
||||
//printf("SEND seqno %d ts %d\n", seqno, ts);
|
||||
|
||||
memcpy(eh->dst, mobi->eth_remote, 6);
|
||||
memcpy(eh->src, mobi->eth_local, 6);
|
||||
|
||||
eh->packet_type = htons(0xbffe);
|
||||
|
||||
mh->flags = 0;
|
||||
mh->fifo_status = 0;
|
||||
mh->seqno = seqno;
|
||||
mh->ack = 0;
|
||||
mh->word0 = 0;
|
||||
mh->timestamp = htonl(ts);
|
||||
|
||||
synch_time(mobi, ts);
|
||||
|
||||
if (send(mobi->sock, packet, 14+14+1280, 0) != 14+14+1280) { perror("send"); exit(1); }
|
||||
}
|
||||
|
||||
/* sender thread */
|
||||
static void *sender(void *_mobi)
|
||||
{
|
||||
mobipass_state_t *mobi = _mobi;
|
||||
unsigned char packet[14 + 14 + 1280];
|
||||
uint32_t ts = 0;
|
||||
unsigned char seqno = 0;
|
||||
while (1) {
|
||||
dequeue_to_mobipass(mobi->qstate, ts, packet);
|
||||
do_send(mobi, seqno, ts, packet);
|
||||
seqno++;
|
||||
ts += 640;
|
||||
ts %= mobi->samples_per_1024_frames;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void new_thread(void *(*f)(void *), void *data)
|
||||
{
|
||||
pthread_t t;
|
||||
pthread_attr_t att;
|
||||
|
||||
if (pthread_attr_init(&att))
|
||||
{ fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
|
||||
if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
|
||||
{ fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
|
||||
if (pthread_attr_setstacksize(&att, 10000000))
|
||||
{ fprintf(stderr, "pthread_attr_setstacksize err\n"); exit(1); }
|
||||
if (pthread_create(&t, &att, f, data))
|
||||
{ fprintf(stderr, "pthread_create err\n"); exit(1); }
|
||||
if (pthread_attr_destroy(&att))
|
||||
{ fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
|
||||
}
|
||||
|
||||
void init_mobipass(mobipass_state_t *mobi)
|
||||
{
|
||||
int i;
|
||||
unsigned char data[14+14+640];
|
||||
memset(data, 0, 14+14+640);
|
||||
|
||||
init_time(mobi);
|
||||
|
||||
mobi->qstate = init_queues(mobi->samples_per_1024_frames);
|
||||
|
||||
for (i = 0; i < 24*4; i++) {
|
||||
uint32_t timestamp = i*640;
|
||||
unsigned char seqno = i;
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(data+14);
|
||||
mh->seqno = seqno;
|
||||
mh->timestamp = htonl(timestamp);
|
||||
enqueue_to_mobipass(mobi->qstate, data);
|
||||
}
|
||||
|
||||
mobi->sock = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (mobi->sock == -1) { perror("socket"); exit(1); }
|
||||
|
||||
/* get if index */
|
||||
struct ifreq if_index;
|
||||
memset(&if_index, 0, sizeof(struct ifreq));
|
||||
strcpy(if_index.ifr_name, mobi->eth.if_name);
|
||||
if (ioctl(mobi->sock, SIOCGIFINDEX, &if_index)<0) {perror("SIOCGIFINDEX");exit(1);}
|
||||
|
||||
struct sockaddr_ll local_addr;
|
||||
local_addr.sll_family = AF_PACKET;
|
||||
local_addr.sll_ifindex = if_index.ifr_ifindex;
|
||||
local_addr.sll_protocol = htons(0xbffe);
|
||||
local_addr.sll_halen = ETH_ALEN;
|
||||
local_addr.sll_pkttype = PACKET_OTHERHOST;
|
||||
|
||||
if (bind(mobi->sock, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_ll))<0)
|
||||
{ perror("bind"); exit(1); }
|
||||
|
||||
new_thread(receiver, mobi);
|
||||
new_thread(sender, mobi);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _MOBIPASS_H_
|
||||
#define _MOBIPASS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ethernet_lib.h"
|
||||
|
||||
typedef struct {
|
||||
/* this has to come first */
|
||||
eth_state_t eth;
|
||||
|
||||
void *qstate;
|
||||
|
||||
uint8_t eth_local[6];
|
||||
uint8_t eth_remote[6];
|
||||
|
||||
int samples_per_1024_frames;
|
||||
|
||||
/* variables used by the function interface.c:mobipass_read */
|
||||
uint32_t mobipass_read_ts;
|
||||
unsigned char mobipass_read_seqno;
|
||||
|
||||
/* variables used by the function interface.c:mobipass_write */
|
||||
uint32_t mobipass_write_last_timestamp;
|
||||
|
||||
/* variables used by the function mobipass.c:[init_time|synch_time] */
|
||||
uint64_t t0;
|
||||
|
||||
/* variables used by the function mobipass.c:synch_time */
|
||||
uint32_t synch_time_last_ts;
|
||||
uint64_t synch_time_mega_ts;
|
||||
|
||||
/* sock is used in mobipass.c */
|
||||
int sock;
|
||||
} mobipass_state_t;
|
||||
|
||||
void init_mobipass(mobipass_state_t *mobi);
|
||||
|
||||
#endif /* _MOBIPASS_H_ */
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "queues.h"
|
||||
#include "mobipass.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define QSIZE 10000
|
||||
|
||||
struct mobipass_header {
|
||||
uint16_t flags;
|
||||
uint16_t fifo_status;
|
||||
unsigned char seqno;
|
||||
unsigned char ack;
|
||||
uint32_t word0;
|
||||
uint32_t timestamp;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct queue {
|
||||
unsigned char buf[QSIZE][14+14+640*2];
|
||||
volatile int start;
|
||||
volatile int len;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct queue to_mobipass;
|
||||
struct queue from_mobipass;
|
||||
int samples_per_1024_frames;
|
||||
|
||||
/* variables used by dequeue_from_mobipass */
|
||||
int dequeue_from_mobipass_seqno;
|
||||
|
||||
/* variables used to manage logging of "missing samples"
|
||||
* coming from mobipass. (Yes, this can happen, mostly
|
||||
* at startup.)
|
||||
* This idea is to print some logs, but not too much to
|
||||
* flood stdout. (This is already a bad idea to call
|
||||
* printf in a 'realtime' thread.)
|
||||
*/
|
||||
int no_sample_log_running;
|
||||
uint32_t no_sample_log_start_sample;
|
||||
uint32_t no_sample_log_next_sample;
|
||||
} queue_state_t;
|
||||
|
||||
static void enqueue(void *data, struct queue *q)
|
||||
{
|
||||
int pos;
|
||||
|
||||
if (pthread_mutex_lock(&q->mutex)) abort();
|
||||
if (q->len == QSIZE) {
|
||||
printf("mobipass: WARNING: enqueue: full\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pos = (q->start + q->len) % QSIZE;
|
||||
memcpy(q->buf[pos], data, 14+14+640*2);
|
||||
q->len++;
|
||||
|
||||
done:
|
||||
if (pthread_cond_signal(&q->cond)) abort();
|
||||
if (pthread_mutex_unlock(&q->mutex)) abort();
|
||||
}
|
||||
|
||||
void enqueue_to_mobipass(void *_qstate, void *data)
|
||||
{
|
||||
queue_state_t *qstate = _qstate;
|
||||
enqueue(data, &qstate->to_mobipass);
|
||||
}
|
||||
|
||||
void dequeue_to_mobipass(void *_qstate, uint32_t timestamp, void *data)
|
||||
{
|
||||
queue_state_t *qstate = _qstate;
|
||||
if (pthread_mutex_lock(&qstate->to_mobipass.mutex)) abort();
|
||||
while (qstate->to_mobipass.len == 0) {
|
||||
if (pthread_cond_wait(&qstate->to_mobipass.cond, &qstate->to_mobipass.mutex)) abort();
|
||||
}
|
||||
|
||||
memcpy(data, qstate->to_mobipass.buf[qstate->to_mobipass.start], 14+14+640*2);
|
||||
qstate->to_mobipass.len--;
|
||||
qstate->to_mobipass.start = (qstate->to_mobipass.start + 1) % QSIZE;
|
||||
|
||||
if (pthread_mutex_unlock(&qstate->to_mobipass.mutex)) abort();
|
||||
}
|
||||
|
||||
void enqueue_from_mobipass(void *_qstate, void *data)
|
||||
{
|
||||
queue_state_t *qstate = _qstate;
|
||||
struct mobipass_header *mh = (struct mobipass_header *)((char*)data+14);
|
||||
mh->timestamp = htonl(ntohl(mh->timestamp) % qstate->samples_per_1024_frames);
|
||||
//printf("from mobipass! timestamp %u seqno %d\n", ntohl(mh->timestamp), mh->seqno);
|
||||
enqueue(data, &qstate->from_mobipass);
|
||||
}
|
||||
|
||||
static int cmp_timestamps(uint32_t a, uint32_t b, int samples_per_1024_frames)
|
||||
{
|
||||
if (a == b) return 0;
|
||||
if (a < b) {
|
||||
if (b-a > samples_per_1024_frames/2) return 1;
|
||||
return -1;
|
||||
}
|
||||
if (a-b > samples_per_1024_frames/2) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*************************************************/
|
||||
/* missing samples logging management begin */
|
||||
/*************************************************/
|
||||
|
||||
static void log_flush(queue_state_t *qstate)
|
||||
{
|
||||
/* print now if there is something to print */
|
||||
if (qstate->no_sample_log_running == 0)
|
||||
return;
|
||||
qstate->no_sample_log_running = 0;
|
||||
printf("mobipass: WARNING: missing samples [%u-%u]\n",
|
||||
qstate->no_sample_log_start_sample,
|
||||
(uint32_t)(qstate->no_sample_log_next_sample-1));
|
||||
}
|
||||
|
||||
static void log_missed_sample(queue_state_t *qstate, uint32_t timestamp)
|
||||
{
|
||||
/* collect data, print if there is a discontinuity */
|
||||
if (qstate->no_sample_log_running == 0 ||
|
||||
timestamp != qstate->no_sample_log_next_sample) {
|
||||
log_flush(qstate);
|
||||
qstate->no_sample_log_start_sample = timestamp;
|
||||
}
|
||||
|
||||
qstate->no_sample_log_next_sample = timestamp+1;
|
||||
qstate->no_sample_log_running = 1;
|
||||
}
|
||||
|
||||
static void log_flush_if_old(queue_state_t *qstate, uint32_t timestamp)
|
||||
{
|
||||
/* log every second (more or less), if we have to */
|
||||
/* note that if mobipass stopped, it may take much more
|
||||
* than one second to log, due to the sleeps done while
|
||||
* waiting for samples (that never come)
|
||||
*/
|
||||
if (qstate->no_sample_log_running == 1 &&
|
||||
labs(timestamp-qstate->no_sample_log_start_sample) > qstate->samples_per_1024_frames/10)
|
||||
log_flush(qstate);
|
||||
}
|
||||
|
||||
/*************************************************/
|
||||
/* missing samples logging management end */
|
||||
/*************************************************/
|
||||
|
||||
/* to be called with lock on */
|
||||
static void get_sample_from_mobipass(queue_state_t *qstate, char *I, char *Q, uint32_t timestamp)
|
||||
{
|
||||
unsigned char *b = NULL;
|
||||
unsigned char *data = NULL;
|
||||
struct mobipass_header *mh = NULL;
|
||||
uint32_t packet_timestamp = 0;
|
||||
|
||||
#if 0
|
||||
uint32_t old_start = qstate->from_mobipass.start;
|
||||
uint32_t old_len = qstate->from_mobipass.len;
|
||||
b = qstate->from_mobipass.buf[qstate->from_mobipass.start];
|
||||
mh = (struct mobipass_header *)(b+14);
|
||||
uint32_t old_pts = qstate->from_mobipass.len ? ntohl(mh->timestamp) : -1;
|
||||
b=NULL;
|
||||
mh=NULL;
|
||||
#endif
|
||||
|
||||
while (qstate->from_mobipass.len) {
|
||||
b = qstate->from_mobipass.buf[qstate->from_mobipass.start];
|
||||
mh = (struct mobipass_header *)(b+14);
|
||||
data = b + 14*2;
|
||||
packet_timestamp = ntohl(mh->timestamp);
|
||||
if (cmp_timestamps(timestamp, packet_timestamp, qstate->samples_per_1024_frames) < 0) goto nodata;
|
||||
if (cmp_timestamps(timestamp, (packet_timestamp+640) % qstate->samples_per_1024_frames, qstate->samples_per_1024_frames) < 0) break;
|
||||
qstate->from_mobipass.len--;
|
||||
qstate->from_mobipass.start = (qstate->from_mobipass.start+1) % QSIZE;
|
||||
}
|
||||
|
||||
if (qstate->from_mobipass.len == 0) goto nodata;
|
||||
|
||||
if (timestamp == (packet_timestamp + 639) % qstate->samples_per_1024_frames) {
|
||||
qstate->from_mobipass.len--;
|
||||
qstate->from_mobipass.start = (qstate->from_mobipass.start+1) % QSIZE;
|
||||
}
|
||||
|
||||
if (timestamp < packet_timestamp) timestamp += qstate->samples_per_1024_frames;
|
||||
|
||||
*I = data[(timestamp - packet_timestamp) * 2];
|
||||
*Q = data[(timestamp - packet_timestamp) * 2 + 1];
|
||||
|
||||
return;
|
||||
|
||||
nodata:
|
||||
*I = 0;
|
||||
*Q = 0;
|
||||
|
||||
log_missed_sample(qstate, timestamp);
|
||||
|
||||
#if 0
|
||||
printf("no sample timestamp %u pt %u start %d old_start %d old_pt %u len %d old len %d\n", timestamp, packet_timestamp, qstate->from_mobipass.start, old_start, old_pts, qstate->from_mobipass.len, old_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* doesn't work with delay more than 1s */
|
||||
static void wait_for_data(pthread_cond_t *cond, pthread_mutex_t *mutex, int delay_us)
|
||||
{
|
||||
struct timeval now;
|
||||
struct timespec target;
|
||||
gettimeofday(&now, NULL);
|
||||
target.tv_sec = now.tv_sec;
|
||||
target.tv_nsec = (now.tv_usec + delay_us) * 1000;
|
||||
if (target.tv_nsec >= 1000 * 1000 * 1000) { target.tv_nsec -= 1000 * 1000 * 1000; target.tv_sec++; }
|
||||
int err = pthread_cond_timedwait(cond, mutex, &target);
|
||||
if (err != 0 && err != ETIMEDOUT) { printf("mobipass: ERROR: pthread_cond_timedwait: err (%d) %s\n", err, strerror(err)); abort(); }
|
||||
}
|
||||
|
||||
/* don't block infinitely when waiting for data
|
||||
* if waiting for too long, just return some zeros
|
||||
*/
|
||||
void dequeue_from_mobipass(void *_qstate, uint32_t timestamp, void *data)
|
||||
{
|
||||
queue_state_t *qstate = _qstate;
|
||||
int i;
|
||||
// int ts = timestamp;
|
||||
int waiting_allowed;
|
||||
|
||||
if (pthread_mutex_lock(&qstate->from_mobipass.mutex)) abort();
|
||||
|
||||
if (qstate->from_mobipass.len == 0) {
|
||||
//printf("sleep 1\n");
|
||||
wait_for_data(&qstate->from_mobipass.cond, &qstate->from_mobipass.mutex, 2000); //1000/3);
|
||||
}
|
||||
|
||||
waiting_allowed = qstate->from_mobipass.len != 0;
|
||||
|
||||
for (i = 0; i < 640*2; i+=2) {
|
||||
if (qstate->from_mobipass.len == 0 && waiting_allowed) {
|
||||
//printf("sleep 2\n");
|
||||
wait_for_data(&qstate->from_mobipass.cond, &qstate->from_mobipass.mutex, 2000); //1000/3);
|
||||
waiting_allowed = qstate->from_mobipass.len != 0;
|
||||
}
|
||||
|
||||
get_sample_from_mobipass(qstate, (char*)data + 14*2 + i, (char*)data + 14*2 + i+1, timestamp % qstate->samples_per_1024_frames);
|
||||
timestamp++;
|
||||
}
|
||||
|
||||
log_flush_if_old(qstate, timestamp);
|
||||
|
||||
if (pthread_mutex_unlock(&qstate->from_mobipass.mutex)) abort();
|
||||
|
||||
struct mobipass_header *mh = (struct mobipass_header *)(((char *)data) + 14);
|
||||
mh->flags = 0;
|
||||
mh->fifo_status = 0;
|
||||
mh->seqno = qstate->dequeue_from_mobipass_seqno++;
|
||||
mh->ack = 0;
|
||||
mh->word0 = 0;
|
||||
mh->timestamp = htonl(timestamp);
|
||||
}
|
||||
|
||||
void *init_queues(int samples_per_1024_frames)
|
||||
{
|
||||
queue_state_t *q;
|
||||
q = malloc(sizeof(queue_state_t));
|
||||
if (q == NULL) abort();
|
||||
memset(q, 0, sizeof(queue_state_t));
|
||||
|
||||
if (pthread_mutex_init(&q->to_mobipass.mutex, NULL)) abort();
|
||||
if (pthread_mutex_init(&q->from_mobipass.mutex, NULL)) abort();
|
||||
if (pthread_cond_init(&q->to_mobipass.cond, NULL)) abort();
|
||||
if (pthread_cond_init(&q->from_mobipass.cond, NULL)) abort();
|
||||
|
||||
q->samples_per_1024_frames = samples_per_1024_frames;
|
||||
|
||||
return q;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _QUEUES_H_
|
||||
#define _QUEUES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void enqueue_to_mobipass(void *qstate, void *data);
|
||||
void dequeue_to_mobipass(void *qstate, uint32_t timestamp, void *data);
|
||||
|
||||
void enqueue_from_mobipass(void *qstate, void *receive_packet);
|
||||
void dequeue_from_mobipass(void *qstate, uint32_t timestamp, void *data);
|
||||
|
||||
/* returns a queue state type, as opaque data structure */
|
||||
void *init_queues(int samples_per_1024_frames);
|
||||
|
||||
#endif /* _QUEUES_H_ */
|
||||
Reference in New Issue
Block a user