Front Haul Interface Library update to third seed code contribution
[o-du/phy.git] / fhi_lib / test / master.py
old mode 100644 (file)
new mode 100755 (executable)
index ce1a286..c6517f6
-#!/usr/bin/python\r
-#######################################################################\r
-#\r
-# <COPYRIGHT_TAG>\r
-#\r
-#######################################################################\r
-\r
-"""This script run test cases with O-DU and O-RU\r
-"""\r
-import logging\r
-import sys\r
-import argparse\r
-import re\r
-import subprocess\r
-import os\r
-import shutil\r
-from itertools import dropwhile\r
-from datetime import datetime\r
-import json\r
-\r
-nNumRbsPerSymF1 = [\r
-    #  5MHz    10MHz   15MHz   20 MHz  25 MHz  30 MHz  40 MHz  50MHz   60 MHz  70 MHz  80 MHz   90 MHz  100 MHz\r
-        [25,    52,     79,     106,    133,    160,    216,    270,    0,         0,      0,      0,      0],         # Numerology 0 (15KHz)\r
-        [11,    24,     38,     51,     65,     78,     106,    133,    162,       0,    217,    245,    273],         # Numerology 1 (30KHz)\r
-        [0,     11,     18,     24,     31,     38,     51,     65,     79,        0,    107,    121,    135]          # Numerology 2 (60KHz)\r
-]\r
-\r
-nNumRbsPerSymF2 = [\r
-    # 50Mhz  100MHz  200MHz   400MHz\r
-    [66,    132,    264,     0],       # Numerology 2 (60KHz)\r
-    [32,    66,     132,     264]      # Numerology 3 (120KHz)\r
-]\r
-\r
-\r
-nRChBwOptions_keys = ['5','10','15','20', '25', '30', '40', '50', '60','70', '80', '90', '100', '200', '400']\r
-nRChBwOptions_values = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]\r
-nRChBwOptions = dict(zip(nRChBwOptions_keys, nRChBwOptions_values))\r
-\r
-nRChBwOptions_keys_mu2and3 = ['50', '100', '200', '400']\r
-nRChBwOptions_values_mu2and3 = [0,1,2,3]\r
-nRChBwOptions_mu2and3 = dict(zip(nRChBwOptions_keys_mu2and3, nRChBwOptions_values_mu2and3))\r
-\r
-# table of all test cases\r
-#                 (cat, mu, bw, test case)\r
-all_test_cases =   [(0,   0,  5,   0),\r
-                  (0,   0,  10,  0),\r
-                  (0,   0,  20,  0),\r
-                  (0,   1,  100, 0),\r
-                  (0,   3,  100, 0),\r
-                  (1,   1,  100, 0)]\r
-#Cat B\r
-""" all_test_cases  =  [(1,   1,  100, 0),\r
-                    (1,   1,  100, 1),\r
-                    (1,   1,  100, 101),\r
-                    (1,   1,  100, 102),\r
-                    (1,   1,  100, 103),\r
-                    (1,   1,  100, 104),\r
-                    (1,   1,  100, 105),\r
-                    #(1,   1,  100, 106), 25G not enough\r
-                    (1,   1,  100, 107),\r
-                    (1,   1,  100, 108),\r
-                    #(1,   1,  100, 109), 25G not enough\r
-                    (1,   1,  100, 201),\r
-                    #(1,   1,  100, 202), 25G not enough\r
-                    (1,   1,  100, 203),\r
-                    (1,   1,  100, 204),\r
-                    (1,   1,  100, 205),\r
-                    (1,   1,  100, 206),\r
-                    (1,   1,  100, 211),\r
-                    #(1,   1,  100, 212), 25G not enough\r
-                    (1,   1,  100, 213),\r
-                    (1,   1,  100, 214),\r
-                    (1,   1,  100, 215),\r
-                    (1,   1,  100, 216)\r
-]\r
- """\r
-dic_dir = dict({0:'DL', 1:'UL'})\r
-dic_xu  = dict({0:'o-du', 1:'o-ru'})\r
-\r
-def init_logger(console_level, logfile_level):\r
-    """Initializes console and logfile logger with given logging levels"""\r
-    # File logger\r
-    logging.basicConfig(filename="runtests.log",\r
-                        filemode='w',\r
-                        format="%(asctime)s: %(levelname)s: %(message)s",\r
-                        level=logfile_level)\r
-    # Console logger\r
-    logger = logging.getLogger()\r
-    handler = logging.StreamHandler()\r
-    handler.setLevel(console_level)\r
-    formatter = logging.Formatter("%(levelname)s: %(message)s")\r
-    handler.setFormatter(formatter)\r
-    logger.addHandler(handler)\r
-\r
-def parse_args(args):\r
-    """Configures parser and parses command line configuration"""\r
-    # Parser configuration\r
-    parser = argparse.ArgumentParser(description="Run test cases: category numerology bandwidth test_num")\r
-\r
-    parser.add_argument("--cat", type=int, default=0, help="Category: 0 (A) or 1 (B)", metavar="cat", dest="category")\r
-    parser.add_argument("--mu", type=int, default=0, help="numerology [0,1,3]", metavar="num", dest="numerology")\r
-    parser.add_argument("--bw",  type=int, default=20, help="bandwidth [5,10,20,100]", metavar="bw", dest="bandwidth")\r
-    parser.add_argument("--testcase", type=int, default=0, help="test case number", metavar="testcase", dest="testcase")\r
-\r
-    # Parse arguments\r
-    options = parser.parse_args(args)\r
-    #parser.print_help()\r
-    logging.debug("Options: category=%d num=%d bw=%d testcase=%d",\r
-                  options.category, options.numerology, options.bandwidth, options.testcase)\r
-    return options\r
-\r
-def is_comment(s):\r
-    """ function to check if a line\r
-         starts with some character.\r
-         Here # for comment\r
-    """\r
-    # return true if a line starts with #\r
-    return s.startswith('#')\r
-\r
-class GetOutOfLoops( Exception ):\r
-    pass\r
-\r
-def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction):\r
-    res = 0\r
-\r
-    if mu < 3:\r
-        nDlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nDLBandwidth))]\r
-        nUlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nULBandwidth))]\r
-    elif (mu >=2) & (mu <= 3):\r
-        nDlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nDLBandwidth))]\r
-        nUlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nULBandwidth))]\r
-        print(nDlRB, nUlRB)\r
-    else:\r
-        print("Incorrect arguments\n")\r
-        res = -1\r
-        return res\r
-\r
-    if 'compression' in globals():\r
-        comp = compression\r
-    else:\r
-        comp = 0\r
-\r
-    print("compare results: {} [compression {}]\n".format(dic_dir.get(direction), comp))\r
-\r
-    #if cat == 1:\r
-    #    print("WARNING: Skip checking IQs and BF Weights for CAT B for now\n");\r
-    #    return res\r
-\r
-    #get slot config\r
-    if nFrameDuplexType == 1:\r
-        SlotConfig = []\r
-        for i in range(nTddPeriod):\r
-            if i == 0:\r
-                SlotConfig.insert(i, sSlotConfig0)\r
-            elif i == 1:\r
-                SlotConfig.insert(i, sSlotConfig1)\r
-            elif i == 2:\r
-                SlotConfig.insert(i, sSlotConfig2)\r
-            elif i == 3:\r
-                SlotConfig.insert(i, sSlotConfig3)\r
-            elif i == 4:\r
-                SlotConfig.insert(i, sSlotConfig4)\r
-            elif i == 5:\r
-                SlotConfig.insert(i, sSlotConfig5)\r
-            elif i == 6:\r
-                SlotConfig.insert(i, sSlotConfig6)\r
-            elif i == 7:\r
-                SlotConfig.insert(i, sSlotConfig7)\r
-            elif i == 8:\r
-                SlotConfig.insert(i, sSlotConfig8)\r
-            elif i == 9:\r
-                SlotConfig.insert(i, sSlotConfig9)\r
-            else :\r
-                raise Exception('i should not exceed nTddPeriod %d. The value of i was: {}'.format(nTddPeriod, i))\r
-        #print(SlotConfig, type(sSlotConfig0))\r
-    try:\r
-\r
-        if (direction == 1) & (cat == 1): #UL\r
-            flowId = ccNum*antNumUL\r
-        else:\r
-            flowId = ccNum*antNum\r
-\r
-        for i in range(0, flowId):\r
-            #read ref and test files\r
-            tst = []\r
-            ref = []\r
-            if direction == 0:\r
-                # DL\r
-                nRB = nDlRB\r
-                file_tst = xran_path+"/app/logs/"+"o-ru-rx_log_ant"+str(i)+".txt"\r
-                file_ref = xran_path+"/app/logs/"+"o-du-play_ant"+str(i)+".txt"\r
-            elif direction == 1:\r
-                # UL\r
-                nRB = nUlRB\r
-                file_tst = xran_path+"/app/logs/"+"o-du-rx_log_ant"+str(i)+".txt"\r
-                file_ref = xran_path+"/app/logs/"+"o-ru-play_ant"+str(i)+".txt"\r
-            else:\r
-                raise Exception('Direction is not supported %d'.format(direction))\r
-\r
-            print("test result   :", file_tst)\r
-            print("test reference:", file_ref)\r
-            if os.path.exists(file_tst):\r
-                try:\r
-                    file_tst = open(file_tst, 'r')\r
-                except OSError:\r
-                    print ("Could not open/read file:", file_tst)\r
-                    sys.exit()\r
-            else:\r
-                print(file_tst, "doesn't exist")\r
-                res = -1\r
-                return res\r
-            if os.path.exists(file_ref):\r
-                try:\r
-                    file_ref = open(file_ref, 'r')\r
-                except OSError:\r
-                    print ("Could not open/read file:", file_ref)\r
-                    sys.exit()\r
-            else:\r
-                print(file_tst, "doesn't exist")\r
-                res = -1\r
-                return res\r
-\r
-            tst = file_tst.readlines()\r
-            ref = file_ref.readlines()\r
-\r
-            print(len(tst))\r
-            print(len(ref))\r
-\r
-            file_tst.close();\r
-            file_ref.close();\r
-\r
-            print(numSlots)\r
-\r
-            for slot_idx in range(0, numSlots):\r
-                for sym_idx in range(0, 14):\r
-                    if nFrameDuplexType==1:\r
-                        #skip sym if TDD\r
-                        if direction == 0:\r
-                            #DL\r
-                            sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]\r
-                            if(sym_dir != 0):\r
-                                continue\r
-                        elif direction == 1:\r
-                            #UL\r
-                            sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]\r
-                            if(sym_dir != 1):\r
-                                continue\r
-\r
-                    #print("Check:","[",i,"]", slot_idx, sym_idx)\r
-                    for line_idx in range(0, nRB*12):\r
-                        offset = (slot_idx*nRB*12*14) + sym_idx*nRB*12 + line_idx\r
-                        line_tst = tst[offset].rstrip()\r
-                        line_ref = ref[offset].rstrip()\r
-                        if comp == 1:\r
-                            # discard LSB bits as BFP compression is not Bit Exact\r
-                            tst_i_value = int(line_tst.split(" ")[0]) & 0xFF80\r
-                            tst_q_value = int(line_tst.split(" ")[1]) & 0xFF80\r
-                            ref_i_value = int(line_ref.split(" ")[0]) & 0xFF80\r
-                            ref_q_value = int(line_ref.split(" ")[1]) & 0xFF80\r
-\r
-                            print("check:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")\r
-                            if (tst_i_value != ref_i_value) or  (tst_q_value != ref_q_value) :\r
-                                print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")\r
-                                res = -1\r
-                                raise GetOutOfLoops\r
-                        else:\r
-                            #if line_idx == 0:\r
-                                #print("Check:", offset,"[",i,"]", slot_idx, sym_idx,":",line_tst, line_ref)\r
-                            if line_ref != line_tst:\r
-                                print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst:", line_tst, "ref:", line_ref)\r
-                                res = -1\r
-                                raise GetOutOfLoops\r
-    except GetOutOfLoops:\r
-        pass\r
-\r
-    return res\r
-\r
-def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg):\r
-    #parse config files\r
-    logging.info("parse config files %s\n", test_cfg[0])\r
-    lineList = list()\r
-    sep = '#'\r
-    with open(test_cfg[0],'r') as fh:\r
-        for curline in dropwhile(is_comment, fh):\r
-            my_line = curline.rstrip().split(sep, 1)[0].strip()\r
-            if my_line:\r
-                lineList.append(my_line)\r
-    global_env = {}\r
-    local_env = {}\r
-\r
-    for line in lineList:\r
-        exe_line = line.replace(":", ",")\r
-        if exe_line.find("/") > 0 :\r
-            exe_line = exe_line.replace('./', "'")\r
-            exe_line = exe_line+"'"\r
-\r
-        code = compile(str(exe_line), '<string>', 'exec')\r
-        exec (code, global_env, local_env)\r
-\r
-    for k, v in local_env.items():\r
-        globals()[k] = v\r
-        print(k, v)\r
-\r
-    return local_env\r
-\r
-def make_copy_mlog(cat, mu, bw, tcase, xran_path):\r
-    res = 0\r
-\r
-    src_bin = xran_path+"/app/mlog-o-du-c0.bin"\r
-    src_csv = xran_path+"/app/mlog-o-du-hist.csv"\r
-    dst_bin = xran_path+"/app/mlog-o-du-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"\r
-    dst_csv = xran_path+"/app/mlog-o-du-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"\r
-\r
-    try:\r
-        d_bin  = shutil.copyfile(src_bin, dst_bin)\r
-        d_csv  = shutil.copyfile(src_csv, dst_csv)\r
-    except IOError:\r
-        logging.info("MLog is not present\n")\r
-        res = 1\r
-    else:\r
-        logging.info("Mlog was copied\n")\r
-\r
-    print("Destination path:", d_bin)\r
-    print("Destination path:", d_csv)\r
-\r
-    d_bin  = shutil.copyfile(src_bin, dst_bin)\r
-    d_csv  = shutil.copyfile(src_csv, dst_csv)\r
-\r
-    #print("After copying file:")\r
-    #print(os.listdir(xran_path+"/app/"))\r
-\r
-    #print("Destination path:", d_bin)\r
-    #print("Destination path:", d_csv)\r
-\r
-    src_bin = xran_path+"/app/mlog-o-ru-c0.bin"\r
-    src_csv = xran_path+"/app/mlog-o-ru-hist.csv"\r
-    dst_bin = xran_path+"/app/mlog-o-ru-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"\r
-    dst_csv = xran_path+"/app/mlog-o-ru-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"\r
-\r
-    d_bin  = shutil.copyfile(src_bin, dst_bin)\r
-    d_csv  = shutil.copyfile(src_csv, dst_csv)\r
-\r
-    #print("After copying file:")\r
-    #print(os.listdir(xran_path+"/app/"))\r
-\r
-    #print("Destination path:", d_bin)\r
-    #print("Destination path:", d_csv)\r
-\r
-    try:\r
-        d_bin  = shutil.copyfile(src_bin, dst_bin)\r
-        d_csv  = shutil.copyfile(src_csv, dst_csv)\r
-    except IOError:\r
-        logging.info("MLog is not present\n")\r
-        res = 1\r
-    else:\r
-        logging.info("Mlog was copied\n")\r
-\r
-    #print("After copying file:")\r
-    #print(os.listdir(xran_path+"/app/"))\r
-\r
-    #print("Destination path:", d_bin)\r
-    #print("Destination path:", d_csv)\r
-\r
-    return res\r
-\r
-\r
-def run_tcase(cat, mu, bw, tcase, xran_path):\r
-    if cat == 1:\r
-        test_config = xran_path+"/app/usecase/cat_b/mu{0:d}_{1:d}mhz".format(mu, bw)\r
-    elif cat == 0 :\r
-        test_config = xran_path+"/app/usecase/mu{0:d}_{1:d}mhz".format(mu, bw)\r
-    else:\r
-        print("Incorrect arguments\n")\r
-\r
-    if(tcase > 0) :\r
-        test_config = test_config+"/"+str(tcase)\r
-\r
-    app = xran_path+"/app/build/sample-app"\r
-\r
-    logging.debug("run: %s %s", app, test_config)\r
-    logging.debug("Started script: master.py, XRAN path %s", xran_path)\r
-\r
-    test_cfg = []\r
-    #TODO: add detection of ETH ports\r
-    eth_cp_dev = ["0000:22:02.1", "0000:22:0a.1"]\r
-    eth_up_dev = ["0000:22:02.0", "0000:22:0a.0"]\r
-\r
-    test_cfg.append(test_config+"/config_file_o_du.dat")\r
-    test_cfg.append(test_config+"/config_file_o_ru.dat")\r
-\r
-    wd = os.getcwd()\r
-    os.chdir(xran_path+"/app/")\r
-\r
-    processes     = []\r
-    logfile_xu    = []\r
-    log_file_name = []\r
-\r
-    os.system('rm -rf ./logs')\r
-\r
-    for i in range(2):\r
-        log_file_name.append("sampleapp_log_{}_cat_{}_mu{}_{}mhz_tst_{}.log".format(dic_xu.get(i),cat, mu, bw, tcase))\r
-        with open(log_file_name[i], "w") as f:\r
-            #, stdout=f, stderr=f\r
-            p = subprocess.Popen([app, test_cfg[i], eth_up_dev[i], eth_cp_dev[i]], stdout=f, stderr=f)\r
-            logfile_xu.insert(i, f)\r
-        processes.append((p, logfile_xu[i]))\r
-\r
-    logging.info("Running O-DU and O-RU see output in: %s %s\n", logfile_xu[0].name, logfile_xu[1].name)\r
-    for p, f in processes:\r
-        p.wait()\r
-        p.communicate()[0]\r
-        if p.returncode != 0:\r
-            print("Application {} failed p.returncode:{}".format(dic_xu.get(i), p.returncode))\r
-            print("FAIL")\r
-            logging.info("FAIL\n")\r
-            logging.shutdown()\r
-            sys.exit(p.returncode)\r
-\r
-        f.close()\r
-\r
-    logging.info("O-DU and O-RU are done\n")\r
-\r
-    make_copy_mlog(cat, mu, bw, tcase, xran_path)\r
-\r
-    usecase_cfg = parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)\r
-\r
-    res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 0)\r
-    if res != 0:\r
-        os.chdir(wd)\r
-        print("FAIL")\r
-        return res\r
-\r
-    res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 1)\r
-    if res != 0:\r
-        os.chdir(wd)\r
-        print("FAIL")\r
-        return res\r
-\r
-    os.chdir(wd)\r
-    print("PASS")\r
-    return res\r
-\r
-def main():\r
-    test_results = []\r
-    test_executed_total = 0\r
-    run_total = 0\r
-    cat   = 0\r
-    mu    = 0\r
-    bw    = 0\r
-    tcase = 0\r
-    """Processes input files to produce IACA files"""\r
-    # Find path to XRAN\r
-    xran_path = os.getenv("XRAN_DIR")\r
-\r
-    # Set up logging with given level (DEBUG, INFO, ERROR) for console end logfile\r
-    init_logger(logging.INFO, logging.DEBUG)\r
-    logging.info("Started script: master.py, XRAN path %s", xran_path)\r
-\r
-    # Parse input arguments\r
-    if len(sys.argv) == 1 :\r
-        run_total = len(all_test_cases)\r
-        print(run_total)\r
-        print("Run All test cases {}\n".format(run_total))\r
-    else:\r
-        options = parse_args(sys.argv[1:])\r
-        cat     = options.category\r
-        mu      = options.numerology\r
-        bw      = options.bandwidth\r
-        tcase   = options.testcase\r
-\r
-\r
-    if (run_total):\r
-        for test_run_ix in range(0, run_total):\r
-            cat     = all_test_cases[test_run_ix][0]\r
-            mu      = all_test_cases[test_run_ix][1]\r
-            bw      = all_test_cases[test_run_ix][2]\r
-            tcase   = all_test_cases[test_run_ix][3]\r
-\r
-            res = run_tcase(cat, mu, bw, tcase, xran_path)\r
-            if (res != 0):\r
-                test_results.append((cat, mu, bw, tcase,'FAIL'))\r
-                break;\r
-\r
-            test_results.append((cat, mu, bw, tcase,'PASS'))\r
-    else:\r
-        res = run_tcase(cat, mu, bw, tcase, xran_path)\r
-        if (res != 0):\r
-            test_results.append((cat, mu, bw, tcase,'FAIL'))\r
-        test_results.append((cat, mu, bw, tcase,'PASS'))\r
-\r
-    with open('testresult.txt', 'w') as reshandle:\r
-        json.dump(test_results, reshandle)\r
-\r
-    return res\r
-\r
-if __name__ == '__main__':\r
-    START_TIME = datetime.now()\r
-    res = main()\r
-    END_TIME = datetime.now()\r
-    logging.debug("Start time: %s, end time: %s", START_TIME, END_TIME)\r
-    logging.info("Execution time: %s", END_TIME - START_TIME)\r
-    logging.shutdown()\r
-    sys.exit(res)\r
+#!/usr/bin/python
+#******************************************************************************
+#
+#   Copyright (c) 2019 Intel.
+#
+#   Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+#
+#   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.
+#
+#******************************************************************************/
+
+"""This script run test cases with O-DU and O-RU
+"""
+import logging
+import sys
+import argparse
+import re
+import subprocess
+import os
+import shutil
+from itertools import dropwhile
+from datetime import datetime
+import json
+
+nNumRbsPerSymF1 = [
+    #  5MHz    10MHz   15MHz   20 MHz  25 MHz  30 MHz  40 MHz  50MHz   60 MHz  70 MHz  80 MHz   90 MHz  100 MHz
+        [25,    52,     79,     106,    133,    160,    216,    270,    0,         0,      0,      0,      0],         # Numerology 0 (15KHz)
+        [11,    24,     38,     51,     65,     78,     106,    133,    162,       0,    217,    245,    273],         # Numerology 1 (30KHz)
+        [0,     11,     18,     24,     31,     38,     51,     65,     79,        0,    107,    121,    135]          # Numerology 2 (60KHz)
+]
+
+nNumRbsPerSymF2 = [
+    # 50Mhz  100MHz  200MHz   400MHz
+    [66,    132,    264,     0],       # Numerology 2 (60KHz)
+    [32,    66,     132,     264]      # Numerology 3 (120KHz)
+]
+
+
+nRChBwOptions_keys = ['5','10','15','20', '25', '30', '40', '50', '60','70', '80', '90', '100', '200', '400']
+nRChBwOptions_values = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
+nRChBwOptions = dict(zip(nRChBwOptions_keys, nRChBwOptions_values))
+
+nRChBwOptions_keys_mu2and3 = ['50', '100', '200', '400']
+nRChBwOptions_values_mu2and3 = [0,1,2,3]
+nRChBwOptions_mu2and3 = dict(zip(nRChBwOptions_keys_mu2and3, nRChBwOptions_values_mu2and3))
+
+# table of all test cases
+#                 (cat, mu, bw, test case)
+all_test_cases =   [(0,   0,  5,   0),
+                  (0,   0,  10,  0),
+                  (0,   0,  20,  0),
+                  (0,   1,  100, 0),
+                  (0,   3,  100, 0),
+                  (1,   1,  100, 0)]
+#Cat B
+""" all_test_cases  =  [(1,   1,  100, 0),
+                    (1,   1,  100, 1),
+                    (1,   1,  100, 101),
+                    (1,   1,  100, 102),
+                    (1,   1,  100, 103),
+                    (1,   1,  100, 104),
+                    (1,   1,  100, 105),
+                    #(1,   1,  100, 106), 25G not enough
+                    (1,   1,  100, 107),
+                    (1,   1,  100, 108),
+                    #(1,   1,  100, 109), 25G not enough
+                    (1,   1,  100, 201),
+                    #(1,   1,  100, 202), 25G not enough
+                    (1,   1,  100, 203),
+                    (1,   1,  100, 204),
+                    (1,   1,  100, 205),
+                    (1,   1,  100, 206),
+                    (1,   1,  100, 211),
+                    #(1,   1,  100, 212), 25G not enough
+                    (1,   1,  100, 213),
+                    (1,   1,  100, 214),
+                    (1,   1,  100, 215),
+                    (1,   1,  100, 216)
+]
+ """
+dic_dir = dict({0:'DL', 1:'UL'})
+dic_xu  = dict({0:'o-du', 1:'o-ru'})
+
+def init_logger(console_level, logfile_level):
+    """Initializes console and logfile logger with given logging levels"""
+    # File logger
+    logging.basicConfig(filename="runtests.log",
+                        filemode='w',
+                        format="%(asctime)s: %(levelname)s: %(message)s",
+                        level=logfile_level)
+    # Console logger
+    logger = logging.getLogger()
+    handler = logging.StreamHandler()
+    handler.setLevel(console_level)
+    formatter = logging.Formatter("%(levelname)s: %(message)s")
+    handler.setFormatter(formatter)
+    logger.addHandler(handler)
+
+def parse_args(args):
+    """Configures parser and parses command line configuration"""
+    # Parser configuration
+    parser = argparse.ArgumentParser(description="Run test cases: category numerology bandwidth test_num")
+
+    parser.add_argument("--cat", type=int, default=0, help="Category: 0 (A) or 1 (B)", metavar="cat", dest="category")
+    parser.add_argument("--mu", type=int, default=0, help="numerology [0,1,3]", metavar="num", dest="numerology")
+    parser.add_argument("--bw",  type=int, default=20, help="bandwidth [5,10,20,100]", metavar="bw", dest="bandwidth")
+    parser.add_argument("--testcase", type=int, default=0, help="test case number", metavar="testcase", dest="testcase")
+
+    # Parse arguments
+    options = parser.parse_args(args)
+    #parser.print_help()
+    logging.debug("Options: category=%d num=%d bw=%d testcase=%d",
+                  options.category, options.numerology, options.bandwidth, options.testcase)
+    return options
+
+def is_comment(s):
+    """ function to check if a line
+         starts with some character.
+         Here # for comment
+    """
+    # return true if a line starts with #
+    return s.startswith('#')
+
+class GetOutOfLoops( Exception ):
+    pass
+
+def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction):
+    res = 0
+
+    if mu < 3:
+        nDlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nDLBandwidth))]
+        nUlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nULBandwidth))]
+    elif (mu >=2) & (mu <= 3):
+        nDlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nDLBandwidth))]
+        nUlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nULBandwidth))]
+        print(nDlRB, nUlRB)
+    else:
+        print("Incorrect arguments\n")
+        res = -1
+        return res
+
+    if 'compression' in globals():
+        comp = compression
+    else:
+        comp = 0
+
+    print("compare results: {} [compression {}]\n".format(dic_dir.get(direction), comp))
+
+    #if cat == 1:
+    #    print("WARNING: Skip checking IQs and BF Weights for CAT B for now\n");
+    #    return res
+
+    #get slot config
+    if nFrameDuplexType == 1:
+        SlotConfig = []
+        for i in range(nTddPeriod):
+            if i == 0:
+                SlotConfig.insert(i, sSlotConfig0)
+            elif i == 1:
+                SlotConfig.insert(i, sSlotConfig1)
+            elif i == 2:
+                SlotConfig.insert(i, sSlotConfig2)
+            elif i == 3:
+                SlotConfig.insert(i, sSlotConfig3)
+            elif i == 4:
+                SlotConfig.insert(i, sSlotConfig4)
+            elif i == 5:
+                SlotConfig.insert(i, sSlotConfig5)
+            elif i == 6:
+                SlotConfig.insert(i, sSlotConfig6)
+            elif i == 7:
+                SlotConfig.insert(i, sSlotConfig7)
+            elif i == 8:
+                SlotConfig.insert(i, sSlotConfig8)
+            elif i == 9:
+                SlotConfig.insert(i, sSlotConfig9)
+            else :
+                raise Exception('i should not exceed nTddPeriod %d. The value of i was: {}'.format(nTddPeriod, i))
+        #print(SlotConfig, type(sSlotConfig0))
+    try:
+
+        if (direction == 1) & (cat == 1): #UL
+            flowId = ccNum*antNumUL
+        else:
+            flowId = ccNum*antNum
+
+        for i in range(0, flowId):
+            #read ref and test files
+            tst = []
+            ref = []
+            if direction == 0:
+                # DL
+                nRB = nDlRB
+                file_tst = xran_path+"/app/logs/"+"o-ru-rx_log_ant"+str(i)+".txt"
+                file_ref = xran_path+"/app/logs/"+"o-du-play_ant"+str(i)+".txt"
+            elif direction == 1:
+                # UL
+                nRB = nUlRB
+                file_tst = xran_path+"/app/logs/"+"o-du-rx_log_ant"+str(i)+".txt"
+                file_ref = xran_path+"/app/logs/"+"o-ru-play_ant"+str(i)+".txt"
+            else:
+                raise Exception('Direction is not supported %d'.format(direction))
+
+            print("test result   :", file_tst)
+            print("test reference:", file_ref)
+            if os.path.exists(file_tst):
+                try:
+                    file_tst = open(file_tst, 'r')
+                except OSError:
+                    print ("Could not open/read file:", file_tst)
+                    sys.exit()
+            else:
+                print(file_tst, "doesn't exist")
+                res = -1
+                return res
+            if os.path.exists(file_ref):
+                try:
+                    file_ref = open(file_ref, 'r')
+                except OSError:
+                    print ("Could not open/read file:", file_ref)
+                    sys.exit()
+            else:
+                print(file_tst, "doesn't exist")
+                res = -1
+                return res
+
+            tst = file_tst.readlines()
+            ref = file_ref.readlines()
+
+            print(len(tst))
+            print(len(ref))
+
+            file_tst.close();
+            file_ref.close();
+
+            print(numSlots)
+
+            for slot_idx in range(0, numSlots):
+                for sym_idx in range(0, 14):
+                    if nFrameDuplexType==1:
+                        #skip sym if TDD
+                        if direction == 0:
+                            #DL
+                            sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
+                            if(sym_dir != 0):
+                                continue
+                        elif direction == 1:
+                            #UL
+                            sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
+                            if(sym_dir != 1):
+                                continue
+
+                    #print("Check:","[",i,"]", slot_idx, sym_idx)
+                    for line_idx in range(0, nRB*12):
+                        offset = (slot_idx*nRB*12*14) + sym_idx*nRB*12 + line_idx
+                        line_tst = tst[offset].rstrip()
+                        line_ref = ref[offset].rstrip()
+                        if comp == 1:
+                            # discard LSB bits as BFP compression is not Bit Exact
+                            tst_i_value = int(line_tst.split(" ")[0]) & 0xFF80
+                            tst_q_value = int(line_tst.split(" ")[1]) & 0xFF80
+                            ref_i_value = int(line_ref.split(" ")[0]) & 0xFF80
+                            ref_q_value = int(line_ref.split(" ")[1]) & 0xFF80
+
+                            print("check:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
+                            if (tst_i_value != ref_i_value) or  (tst_q_value != ref_q_value) :
+                                print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
+                                res = -1
+                                raise GetOutOfLoops
+                        else:
+                            #if line_idx == 0:
+                                #print("Check:", offset,"[",i,"]", slot_idx, sym_idx,":",line_tst, line_ref)
+                            if line_ref != line_tst:
+                                print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst:", line_tst, "ref:", line_ref)
+                                res = -1
+                                raise GetOutOfLoops
+    except GetOutOfLoops:
+        pass
+
+    return res
+
+def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg):
+    #parse config files
+    logging.info("parse config files %s\n", test_cfg[0])
+    lineList = list()
+    sep = '#'
+    with open(test_cfg[0],'r') as fh:
+        for curline in dropwhile(is_comment, fh):
+            my_line = curline.rstrip().split(sep, 1)[0].strip()
+            if my_line:
+                lineList.append(my_line)
+    global_env = {}
+    local_env = {}
+
+    for line in lineList:
+        exe_line = line.replace(":", ",")
+        if exe_line.find("/") > 0 :
+            exe_line = exe_line.replace('./', "'")
+            exe_line = exe_line+"'"
+
+        code = compile(str(exe_line), '<string>', 'exec')
+        exec (code, global_env, local_env)
+
+    for k, v in local_env.items():
+        globals()[k] = v
+        print(k, v)
+
+    return local_env
+
+def make_copy_mlog(cat, mu, bw, tcase, xran_path):
+    res = 0
+
+    src_bin = xran_path+"/app/mlog-o-du-c0.bin"
+    src_csv = xran_path+"/app/mlog-o-du-hist.csv"
+    dst_bin = xran_path+"/app/mlog-o-du-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
+    dst_csv = xran_path+"/app/mlog-o-du-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
+
+    try:
+        d_bin  = shutil.copyfile(src_bin, dst_bin)
+        d_csv  = shutil.copyfile(src_csv, dst_csv)
+    except IOError:
+        logging.info("MLog is not present\n")
+        res = 1
+    else:
+        logging.info("Mlog was copied\n")
+
+
+    src_bin = xran_path+"/app/mlog-o-ru-c0.bin"
+    src_csv = xran_path+"/app/mlog-o-ru-hist.csv"
+    dst_bin = xran_path+"/app/mlog-o-ru-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
+    dst_csv = xran_path+"/app/mlog-o-ru-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
+
+
+    try:
+        d_bin  = shutil.copyfile(src_bin, dst_bin)
+        d_csv  = shutil.copyfile(src_csv, dst_csv)
+    except IOError:
+        logging.info("MLog is not present\n")
+        res = 1
+    else:
+        logging.info("Mlog was copied\n")
+
+
+    return res
+
+
+def run_tcase(cat, mu, bw, tcase, xran_path):
+    if cat == 1:
+        test_config = xran_path+"/app/usecase/cat_b/mu{0:d}_{1:d}mhz".format(mu, bw)
+    elif cat == 0 :
+        test_config = xran_path+"/app/usecase/mu{0:d}_{1:d}mhz".format(mu, bw)
+    else:
+        print("Incorrect arguments\n")
+
+    if(tcase > 0) :
+        test_config = test_config+"/"+str(tcase)
+
+    app = xran_path+"/app/build/sample-app"
+
+    logging.debug("run: %s %s", app, test_config)
+    logging.debug("Started script: master.py, XRAN path %s", xran_path)
+
+    test_cfg = []
+    #TODO: add detection of ETH ports
+    eth_cp_dev = ["0000:22:02.1", "0000:22:0a.1"]
+    eth_up_dev = ["0000:22:02.0", "0000:22:0a.0"]
+
+    test_cfg.append(test_config+"/config_file_o_du.dat")
+    test_cfg.append(test_config+"/config_file_o_ru.dat")
+
+    wd = os.getcwd()
+    os.chdir(xran_path+"/app/")
+
+    processes     = []
+    logfile_xu    = []
+    log_file_name = []
+
+    os.system('rm -rf ./logs')
+
+    for i in range(2):
+        log_file_name.append("sampleapp_log_{}_cat_{}_mu{}_{}mhz_tst_{}.log".format(dic_xu.get(i),cat, mu, bw, tcase))
+        with open(log_file_name[i], "w") as f:
+            #, stdout=f, stderr=f
+            p = subprocess.Popen([app, test_cfg[i], eth_up_dev[i], eth_cp_dev[i]], stdout=f, stderr=f)
+            logfile_xu.insert(i, f)
+        processes.append((p, logfile_xu[i]))
+
+    logging.info("Running O-DU and O-RU see output in: %s %s\n", logfile_xu[0].name, logfile_xu[1].name)
+    for p, f in processes:
+        p.wait()
+        p.communicate()[0]
+        if p.returncode != 0:
+            print("Application {} failed p.returncode:{}".format(dic_xu.get(i), p.returncode))
+            print("FAIL")
+            logging.info("FAIL\n")
+            logging.shutdown()
+            sys.exit(p.returncode)
+
+        f.close()
+
+    logging.info("O-DU and O-RU are done\n")
+
+    make_copy_mlog(cat, mu, bw, tcase, xran_path)
+
+    usecase_cfg = parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)
+
+    res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 0)
+    if res != 0:
+        os.chdir(wd)
+        print("FAIL")
+        return res
+
+    res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 1)
+    if res != 0:
+        os.chdir(wd)
+        print("FAIL")
+        return res
+
+    os.chdir(wd)
+    print("PASS")
+    return res
+
+def main():
+    test_results = []
+    test_executed_total = 0
+    run_total = 0
+    cat   = 0
+    mu    = 0
+    bw    = 0
+    tcase = 0
+    """Processes input files to produce IACA files"""
+    # Find path to XRAN
+    xran_path = os.getenv("XRAN_DIR")
+
+    # Set up logging with given level (DEBUG, INFO, ERROR) for console end logfile
+    init_logger(logging.INFO, logging.DEBUG)
+    logging.info("Started script: master.py, XRAN path %s", xran_path)
+
+    # Parse input arguments
+    if len(sys.argv) == 1 :
+        run_total = len(all_test_cases)
+        print(run_total)
+        print("Run All test cases {}\n".format(run_total))
+    else:
+        options = parse_args(sys.argv[1:])
+        cat     = options.category
+        mu      = options.numerology
+        bw      = options.bandwidth
+        tcase   = options.testcase
+
+
+    if (run_total):
+        for test_run_ix in range(0, run_total):
+            cat     = all_test_cases[test_run_ix][0]
+            mu      = all_test_cases[test_run_ix][1]
+            bw      = all_test_cases[test_run_ix][2]
+            tcase   = all_test_cases[test_run_ix][3]
+
+            res = run_tcase(cat, mu, bw, tcase, xran_path)
+            if (res != 0):
+                test_results.append((cat, mu, bw, tcase,'FAIL'))
+                break;
+
+            test_results.append((cat, mu, bw, tcase,'PASS'))
+    else:
+        res = run_tcase(cat, mu, bw, tcase, xran_path)
+        if (res != 0):
+            test_results.append((cat, mu, bw, tcase,'FAIL'))
+        test_results.append((cat, mu, bw, tcase,'PASS'))
+
+    with open('testresult.txt', 'w') as reshandle:
+        json.dump(test_results, reshandle)
+
+    return res
+
+if __name__ == '__main__':
+    START_TIME = datetime.now()
+    res = main()
+    END_TIME = datetime.now()
+    logging.debug("Start time: %s, end time: %s", START_TIME, END_TIME)
+    logging.info("Execution time: %s", END_TIME - START_TIME)
+    logging.shutdown()
+    sys.exit(res)