├── .gitignore ├── Documentation └── HDAQ_firmware_ver1.0.20201130.pdf ├── Firmware ├── BringUpInstructions.txt ├── _calibration │ ├── cable_ch0.s1p │ ├── cable_ch1.s1p │ ├── cable_ch2.s1p │ ├── cable_ch3.s1p │ └── cable_ch4.s1p ├── _daq_core │ ├── Makefile │ ├── NE10.h │ ├── NE10_dsp.h │ ├── NE10_imgproc.h │ ├── NE10_init.h │ ├── NE10_macros.h │ ├── NE10_math.h │ ├── NE10_physics.h │ ├── NE10_types.h │ ├── dac_controller.py │ ├── delay_sync.py │ ├── eth_server.h │ ├── fir_decimate.c │ ├── hw_controller.py │ ├── ini.c │ ├── ini.h │ ├── inter_module_messages.py │ ├── iq_eth_sink.py │ ├── iq_header.c │ ├── iq_header.h │ ├── iq_header.py │ ├── iq_server.c │ ├── log.c │ ├── log.h │ ├── rebuffer.c │ ├── rtl_daq.c │ ├── rtl_daq.h │ ├── serial_test │ ├── serial_test.c │ ├── sh_mem_util.c │ ├── sh_mem_util.h │ └── shmemIface.py ├── _data_control │ └── iq_track_lock ├── _logs │ └── logs ├── _testing │ ├── iq_analyzer.py │ ├── iq_recorder.py │ ├── test_data_synthesizer.py │ ├── test_logs │ │ └── logs │ └── unit_test │ │ ├── capture_shmem_stream.py │ │ ├── gen_burst.py │ │ ├── gen_cw.py │ │ ├── gen_ramp.py │ │ ├── gen_std_frame.py │ │ ├── test_decimator.py │ │ ├── test_delay_sync.py │ │ ├── test_iq_server.py │ │ ├── test_rebuffer.py │ │ ├── test_squelch.py │ │ └── test_sys.py ├── daq_chain_config.ini ├── daq_start_sm.sh ├── daq_stop.sh ├── daq_synthetic_start.sh ├── fir_filter_designer.py ├── ini_checker.py └── unit_test.sh ├── LICENSE ├── README.md ├── config_files ├── kerberos_default │ └── daq_chain_config.ini ├── kraken_default │ └── daq_chain_config.ini ├── kraken_development │ └── daq_chain_config.ini └── unit_test_k4 │ └── daq_chain_config.ini └── util ├── cfg_gen.py ├── install.sh └── kerberos_eeprom_init.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Firmware/_logs/*.log 2 | Firmware/_logs/*.html 3 | Firmware/_data_control/* 4 | !Firmware/_data_control/iq_track_lock 5 | Firmware/_test_vectors/*.iq 6 | Firmware/_daq_core/*.out 7 | Firmware/_daq_core/*.o 8 | Firmware/_daq_core/*.a 9 | Firmware/_daq_core/__pycache__/*.pyc 10 | Firmware/_daq_core/__pycache__/*.nbi 11 | Firmware/_daq_core/__pycache__/*.nbc 12 | Firmware/_daq_core/__pycache__/*.cpython-35.pyc 13 | Fimrware/_daq_core/__pycache__/* 14 | Firmware/_testing/unit_test/__pycache__/* 15 | Firmware/_testing/__pycache__/* 16 | Firmware/_testing/test_logs/*.log 17 | Firmware/_testing/test_logs/*.html 18 | Firmware/__pycache__/* -------------------------------------------------------------------------------- /Documentation/HDAQ_firmware_ver1.0.20201130.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krakenrf/heimdall_daq_fw/1efc252e24501a6bd091699c4f22f140ee71d901/Documentation/HDAQ_firmware_ver1.0.20201130.pdf -------------------------------------------------------------------------------- /Firmware/BringUpInstructions.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Build Instructions 3 | # Authors: Carl Laufer, Tamas Peto 4 | # 5 | # Required Python version: 3.8 6 | # 7 | #1. Install Dependencies 8 | apt install cmake 9 | apt install libusb-1.0-0-dev 10 | 11 | #2. Install RTl-SDR kernel driver 12 | 13 | Github page of the driver: 14 | git clone https://github.com/rtlsdrblog/rtl-sdr-kerberos 15 | 16 | cd rtl-sdr-kerberos 17 | mkdir build 18 | cd build 19 | cmake ../ -DINSTALL_UDEV_RULES=ON 20 | make 21 | sudo make install 22 | sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ 23 | sudo ldconfig 24 | 25 | #3. Disable builtin rtl-sdr drivers 26 | 27 | echo 'blacklist dvb_usb_rtl28xxu' | sudo tee --append /etc/modprobe.d/blacklist-dvb_usb_rtl28xxu.conf 28 | 29 | #4-ARM. Install the Ne10 library for ARM devices 30 | # More info on the Ne10 building: https://github.com/projectNe10/Ne10/blob/master/doc/building.md#building-ne10 31 | git clone https://github.com/projectNe10/Ne10 32 | cd Ne10 33 | mkdir build 34 | cd build 35 | export NE10_LINUX_TARGET_ARCH=armv7 # Set the target architecture (can also be "aarch64") 36 | cmake -DGNULINUX_PLATFORM=ON .. # Run CMake to generate the build files 37 | make 38 | # copy "NE10_PATH/build/modules/libNE10.a" to "Firmware/_daq_core/' 39 | # 4-X86. Install the KFR library 40 | #-> Config compiler 41 | sudo apt-get install clang 42 | sudo update-alternatives --config c++ 43 | #-> Select clang++ 44 | 45 | # More info on the KFR library building: https://github.com/kfrlib/kfr/blob/master/README.md#usage 46 | git clone https://github.com/kfrlib/kfr 47 | mkdir build 48 | cd build 49 | cmake -DENABLE_CAPI_BUILD=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release .. 50 | make 51 | sudo make install 52 | sudo ldconfig 53 | # In case of cmake error, remove the problematic section from the cmake file (kfr_capi install), make the library and copy the libbrary files (libkfr_capi.so) manually to /usr/local/lib 54 | 55 | #5. Install Python 3.8 to Raspberry Pi4 56 | sudo apt-get update 57 | sudo apt-get install -y build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev libffi-dev tar wget vim 58 | wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz 59 | sudo tar zxf Python-3.8.0.tgz 60 | cd Python-3.8.0 61 | sudo ./configure --enable-optimizations 62 | sudo make -j 4 63 | sudo make install 64 | 65 | #6. Install the required python packages 66 | sudo python3 -m pip install numpy 67 | sudo python3 -m pip install configparser 68 | # For testing 69 | sudo apt-get install libatlas-base-dev gfortran 70 | sudo python3 -m pip install scipy 71 | sudo python3 -m pip install plotly 72 | sudo python3 -m pip install pyzmq 73 | 74 | #7 Install ZMQ 75 | sudo apt install libzmq3-dev -y -------------------------------------------------------------------------------- /Firmware/_calibration/cable_ch0.s1p: -------------------------------------------------------------------------------- 1 | # HZ S RI R 50 2 | 10000000 0.2571108937263489 -0.9937942028045654 3 | 29900000 -0.6030716896057129 0.6120917201042175 4 | 49800000 0.9279214143753052 -0.18504084646701813 5 | 69700000 -0.7533499002456665 -0.33377549052238464 6 | 89600000 0.5309068560600281 0.7252815365791321 7 | 109500000 0.004096850752830505 -0.8605415225028992 8 | 129400000 -0.4391650855541229 0.7039307355880737 9 | 149300000 0.7964149713516235 -0.3726142942905426 10 | 169200000 -0.7697880864143372 -0.15505041182041168 11 | 189100000 0.6704474091529846 0.5541911125183105 12 | 209000000 -0.15922340750694275 -0.7904901504516602 13 | 228900000 -0.23912134766578674 0.7802208065986633 14 | 248800000 0.6716068983078003 -0.5104817152023315 15 | 268700000 -0.7743467092514038 0.031159603968262672 16 | 288600000 0.7599645256996155 0.3554557263851166 17 | 308500000 -0.3123413324356079 -0.7288392186164856 18 | 328400000 -0.016855280846357346 0.7915047407150269 19 | 348300000 0.5229469537734985 -0.6319707632064819 20 | 368200000 -0.7207397818565369 0.23076693713665009 21 | 388100000 0.7974130511283875 0.1433458924293518 22 | 408000000 -0.4642895758152008 -0.6254493594169617 23 | 427900000 0.17808285355567932 0.7366194128990173 24 | 447800000 0.349004328250885 -0.7223183512687683 25 | 467700000 -0.6084887385368347 0.3983462154865265 26 | 487600000 0.7872880697250366 -0.023656170815229416 27 | 507500000 -0.5736711621284485 -0.4827934801578522 28 | 527400000 0.3688512444496155 0.6217142343521118 29 | 547300000 0.17065194249153137 -0.770005464553833 30 | 567200000 -0.4653477072715759 0.4998227059841156 31 | 587100000 0.7363120913505554 -0.21979057788848877 32 | 607000000 -0.6426096558570862 -0.3189893662929535 33 | 626900000 0.46407654881477356 0.5116126537322998 34 | 646800000 0.0005598729476332664 -0.7628813982009888 35 | 666700000 -0.30103054642677307 0.5588890910148621 36 | 686600000 0.6335286498069763 -0.3987748622894287 37 | 706500000 -0.6557241082191467 -0.1138351783156395 38 | 726400000 0.5681406259536743 0.3577501177787781 39 | 746300000 -0.1778307557106018 -0.7022238969802856 40 | 766200000 -0.15544940531253815 0.5744487643241882 41 | 786100000 0.5278624296188354 -0.5019777417182922 42 | 806000000 -0.6098358035087585 0.00724167563021183 43 | 825900000 0.6135453581809998 0.23072364926338196 44 | 845800000 -0.2756125330924988 -0.6200761198997498 45 | 865700000 0.010730546899139881 0.5552720427513123 46 | 885600000 0.40126270055770874 -0.5929276943206787 47 | 905500000 -0.5800793170928955 0.017441298812627792 48 | 925400000 0.6208855509757996 0.04832146316766739 49 | 945300000 -0.38465651869773865 -0.510841965675354 50 | 965200000 0.09432884305715561 0.5247215032577515 51 | 985100000 0.1740681231021881 -0.6373183727264404 52 | 1005000000 -0.504652202129364 0.21186065673828125 53 | 1024900000 0.6157748699188232 -0.09218810498714447 54 | 1044800000 -0.4313729405403137 -0.4274328649044037 55 | 1064700000 0.22492310404777527 0.48090100288391113 56 | 1084600000 0.09493700414896011 -0.6433440446853638 57 | 1104500000 -0.46256688237190247 0.26055392622947693 58 | 1124400000 0.5596188306808472 -0.2437085062265396 59 | 1144300000 -0.49324244260787964 -0.3470834493637085 60 | 1164200000 0.32895004749298096 0.40024179220199585 61 | 1184100000 -0.02371126413345337 -0.6446571946144104 62 | 1204000000 -0.39184707403182983 0.3413742184638977 63 | 1223900000 0.4658934772014618 -0.3781225085258484 64 | 1243800000 -0.5579816102981567 -0.24580629169940948 65 | 1263700000 0.40321284532546997 0.2887834310531616 66 | 1283600000 -0.20199713110923767 -0.6069770455360413 67 | 1303500000 -0.288360059261322 0.43180954456329346 68 | 1323400000 0.37138307094573975 -0.4566882848739624 69 | 1343300000 -0.5903123617172241 -0.08937656879425049 70 | 1363200000 0.4736197292804718 0.15437892079353333 71 | 1383100000 -0.304165244102478 -0.5670814514160156 72 | 1403000000 -0.18026690185070038 0.46422603726387024 73 | 1422900000 0.24639767408370972 -0.5334051847457886 74 | 1442800000 -0.58859783411026 0.038864172995090485 75 | 1462700000 0.4808274805545807 0.06101563945412636 76 | 1482600000 -0.4163985252380371 -0.4818567633628845 77 | 1502500000 -0.06306713819503784 0.4790143668651581 78 | 1522400000 0.1457294374704361 -0.5652775168418884 79 | 1542300000 -0.546525239944458 0.16759873926639557 80 | 1562200000 0.47399312257766724 -0.06272739171981812 81 | 1582100000 -0.5153735876083374 -0.3475554287433624 82 | 1602000000 0.1161639392375946 0.43713051080703735 83 | 1621900000 -0.03364386409521103 -0.5964519381523132 84 | 1641800000 -0.4671706557273865 0.2866678535938263 85 | 1661700000 0.4450404644012451 -0.1807178258895874 86 | 1681600000 -0.5631083846092224 -0.2386472225189209 87 | 1701500000 0.19772428274154663 0.3739407956600189 88 | 1721400000 -0.18013118207454681 -0.5703877210617065 89 | 1741300000 -0.4118673801422119 0.3435651361942291 90 | 1761200000 0.3591221868991852 -0.2652107775211334 91 | 1781100000 -0.5762578248977661 -0.04000149294734001 92 | 1801000000 0.27122247219085693 0.3412686884403229 93 | 1820900000 -0.26558512449264526 -0.48829537630081177 94 | 1840800000 -0.25474897027015686 0.41935670375823975 95 | 1860700000 0.3007740378379822 -0.3514559864997864 96 | 1880600000 -0.5211795568466187 0.07656865566968918 97 | 1900500000 0.3692742884159088 0.2055569291114807 98 | 1920400000 -0.3451622724533081 -0.4229567348957062 99 | 1940300000 -0.1598425805568695 0.430191308259964 100 | 1960200000 0.1917809545993805 -0.42813611030578613 101 | 1980100000 -0.5087525248527527 0.11805570870637894 102 | 2000000000 0.3704017996788025 0.15374013781547546 103 | -------------------------------------------------------------------------------- /Firmware/_calibration/cable_ch1.s1p: -------------------------------------------------------------------------------- 1 | # HZ S RI R 50 2 | 10000000 0.2571108937263489 -0.9937942028045654 3 | 29900000 -0.6030716896057129 0.6120917201042175 4 | 49800000 0.9279214143753052 -0.18504084646701813 5 | 69700000 -0.7533499002456665 -0.33377549052238464 6 | 89600000 0.5309068560600281 0.7252815365791321 7 | 109500000 0.004096850752830505 -0.8605415225028992 8 | 129400000 -0.4391650855541229 0.7039307355880737 9 | 149300000 0.7964149713516235 -0.3726142942905426 10 | 169200000 -0.7697880864143372 -0.15505041182041168 11 | 189100000 0.6704474091529846 0.5541911125183105 12 | 209000000 -0.15922340750694275 -0.7904901504516602 13 | 228900000 -0.23912134766578674 0.7802208065986633 14 | 248800000 0.6716068983078003 -0.5104817152023315 15 | 268700000 -0.7743467092514038 0.031159603968262672 16 | 288600000 0.7599645256996155 0.3554557263851166 17 | 308500000 -0.3123413324356079 -0.7288392186164856 18 | 328400000 -0.016855280846357346 0.7915047407150269 19 | 348300000 0.5229469537734985 -0.6319707632064819 20 | 368200000 -0.7207397818565369 0.23076693713665009 21 | 388100000 0.7974130511283875 0.1433458924293518 22 | 408000000 -0.4642895758152008 -0.6254493594169617 23 | 427900000 0.17808285355567932 0.7366194128990173 24 | 447800000 0.349004328250885 -0.7223183512687683 25 | 467700000 -0.6084887385368347 0.3983462154865265 26 | 487600000 0.7872880697250366 -0.023656170815229416 27 | 507500000 -0.5736711621284485 -0.4827934801578522 28 | 527400000 0.3688512444496155 0.6217142343521118 29 | 547300000 0.17065194249153137 -0.770005464553833 30 | 567200000 -0.4653477072715759 0.4998227059841156 31 | 587100000 0.7363120913505554 -0.21979057788848877 32 | 607000000 -0.6426096558570862 -0.3189893662929535 33 | 626900000 0.46407654881477356 0.5116126537322998 34 | 646800000 0.0005598729476332664 -0.7628813982009888 35 | 666700000 -0.30103054642677307 0.5588890910148621 36 | 686600000 0.6335286498069763 -0.3987748622894287 37 | 706500000 -0.6557241082191467 -0.1138351783156395 38 | 726400000 0.5681406259536743 0.3577501177787781 39 | 746300000 -0.1778307557106018 -0.7022238969802856 40 | 766200000 -0.15544940531253815 0.5744487643241882 41 | 786100000 0.5278624296188354 -0.5019777417182922 42 | 806000000 -0.6098358035087585 0.00724167563021183 43 | 825900000 0.6135453581809998 0.23072364926338196 44 | 845800000 -0.2756125330924988 -0.6200761198997498 45 | 865700000 0.010730546899139881 0.5552720427513123 46 | 885600000 0.40126270055770874 -0.5929276943206787 47 | 905500000 -0.5800793170928955 0.017441298812627792 48 | 925400000 0.6208855509757996 0.04832146316766739 49 | 945300000 -0.38465651869773865 -0.510841965675354 50 | 965200000 0.09432884305715561 0.5247215032577515 51 | 985100000 0.1740681231021881 -0.6373183727264404 52 | 1005000000 -0.504652202129364 0.21186065673828125 53 | 1024900000 0.6157748699188232 -0.09218810498714447 54 | 1044800000 -0.4313729405403137 -0.4274328649044037 55 | 1064700000 0.22492310404777527 0.48090100288391113 56 | 1084600000 0.09493700414896011 -0.6433440446853638 57 | 1104500000 -0.46256688237190247 0.26055392622947693 58 | 1124400000 0.5596188306808472 -0.2437085062265396 59 | 1144300000 -0.49324244260787964 -0.3470834493637085 60 | 1164200000 0.32895004749298096 0.40024179220199585 61 | 1184100000 -0.02371126413345337 -0.6446571946144104 62 | 1204000000 -0.39184707403182983 0.3413742184638977 63 | 1223900000 0.4658934772014618 -0.3781225085258484 64 | 1243800000 -0.5579816102981567 -0.24580629169940948 65 | 1263700000 0.40321284532546997 0.2887834310531616 66 | 1283600000 -0.20199713110923767 -0.6069770455360413 67 | 1303500000 -0.288360059261322 0.43180954456329346 68 | 1323400000 0.37138307094573975 -0.4566882848739624 69 | 1343300000 -0.5903123617172241 -0.08937656879425049 70 | 1363200000 0.4736197292804718 0.15437892079353333 71 | 1383100000 -0.304165244102478 -0.5670814514160156 72 | 1403000000 -0.18026690185070038 0.46422603726387024 73 | 1422900000 0.24639767408370972 -0.5334051847457886 74 | 1442800000 -0.58859783411026 0.038864172995090485 75 | 1462700000 0.4808274805545807 0.06101563945412636 76 | 1482600000 -0.4163985252380371 -0.4818567633628845 77 | 1502500000 -0.06306713819503784 0.4790143668651581 78 | 1522400000 0.1457294374704361 -0.5652775168418884 79 | 1542300000 -0.546525239944458 0.16759873926639557 80 | 1562200000 0.47399312257766724 -0.06272739171981812 81 | 1582100000 -0.5153735876083374 -0.3475554287433624 82 | 1602000000 0.1161639392375946 0.43713051080703735 83 | 1621900000 -0.03364386409521103 -0.5964519381523132 84 | 1641800000 -0.4671706557273865 0.2866678535938263 85 | 1661700000 0.4450404644012451 -0.1807178258895874 86 | 1681600000 -0.5631083846092224 -0.2386472225189209 87 | 1701500000 0.19772428274154663 0.3739407956600189 88 | 1721400000 -0.18013118207454681 -0.5703877210617065 89 | 1741300000 -0.4118673801422119 0.3435651361942291 90 | 1761200000 0.3591221868991852 -0.2652107775211334 91 | 1781100000 -0.5762578248977661 -0.04000149294734001 92 | 1801000000 0.27122247219085693 0.3412686884403229 93 | 1820900000 -0.26558512449264526 -0.48829537630081177 94 | 1840800000 -0.25474897027015686 0.41935670375823975 95 | 1860700000 0.3007740378379822 -0.3514559864997864 96 | 1880600000 -0.5211795568466187 0.07656865566968918 97 | 1900500000 0.3692742884159088 0.2055569291114807 98 | 1920400000 -0.3451622724533081 -0.4229567348957062 99 | 1940300000 -0.1598425805568695 0.430191308259964 100 | 1960200000 0.1917809545993805 -0.42813611030578613 101 | 1980100000 -0.5087525248527527 0.11805570870637894 102 | 2000000000 0.3704017996788025 0.15374013781547546 103 | -------------------------------------------------------------------------------- /Firmware/_calibration/cable_ch2.s1p: -------------------------------------------------------------------------------- 1 | # HZ S RI R 50 2 | 10000000 0.2571108937263489 -0.9937942028045654 3 | 29900000 -0.6030716896057129 0.6120917201042175 4 | 49800000 0.9279214143753052 -0.18504084646701813 5 | 69700000 -0.7533499002456665 -0.33377549052238464 6 | 89600000 0.5309068560600281 0.7252815365791321 7 | 109500000 0.004096850752830505 -0.8605415225028992 8 | 129400000 -0.4391650855541229 0.7039307355880737 9 | 149300000 0.7964149713516235 -0.3726142942905426 10 | 169200000 -0.7697880864143372 -0.15505041182041168 11 | 189100000 0.6704474091529846 0.5541911125183105 12 | 209000000 -0.15922340750694275 -0.7904901504516602 13 | 228900000 -0.23912134766578674 0.7802208065986633 14 | 248800000 0.6716068983078003 -0.5104817152023315 15 | 268700000 -0.7743467092514038 0.031159603968262672 16 | 288600000 0.7599645256996155 0.3554557263851166 17 | 308500000 -0.3123413324356079 -0.7288392186164856 18 | 328400000 -0.016855280846357346 0.7915047407150269 19 | 348300000 0.5229469537734985 -0.6319707632064819 20 | 368200000 -0.7207397818565369 0.23076693713665009 21 | 388100000 0.7974130511283875 0.1433458924293518 22 | 408000000 -0.4642895758152008 -0.6254493594169617 23 | 427900000 0.17808285355567932 0.7366194128990173 24 | 447800000 0.349004328250885 -0.7223183512687683 25 | 467700000 -0.6084887385368347 0.3983462154865265 26 | 487600000 0.7872880697250366 -0.023656170815229416 27 | 507500000 -0.5736711621284485 -0.4827934801578522 28 | 527400000 0.3688512444496155 0.6217142343521118 29 | 547300000 0.17065194249153137 -0.770005464553833 30 | 567200000 -0.4653477072715759 0.4998227059841156 31 | 587100000 0.7363120913505554 -0.21979057788848877 32 | 607000000 -0.6426096558570862 -0.3189893662929535 33 | 626900000 0.46407654881477356 0.5116126537322998 34 | 646800000 0.0005598729476332664 -0.7628813982009888 35 | 666700000 -0.30103054642677307 0.5588890910148621 36 | 686600000 0.6335286498069763 -0.3987748622894287 37 | 706500000 -0.6557241082191467 -0.1138351783156395 38 | 726400000 0.5681406259536743 0.3577501177787781 39 | 746300000 -0.1778307557106018 -0.7022238969802856 40 | 766200000 -0.15544940531253815 0.5744487643241882 41 | 786100000 0.5278624296188354 -0.5019777417182922 42 | 806000000 -0.6098358035087585 0.00724167563021183 43 | 825900000 0.6135453581809998 0.23072364926338196 44 | 845800000 -0.2756125330924988 -0.6200761198997498 45 | 865700000 0.010730546899139881 0.5552720427513123 46 | 885600000 0.40126270055770874 -0.5929276943206787 47 | 905500000 -0.5800793170928955 0.017441298812627792 48 | 925400000 0.6208855509757996 0.04832146316766739 49 | 945300000 -0.38465651869773865 -0.510841965675354 50 | 965200000 0.09432884305715561 0.5247215032577515 51 | 985100000 0.1740681231021881 -0.6373183727264404 52 | 1005000000 -0.504652202129364 0.21186065673828125 53 | 1024900000 0.6157748699188232 -0.09218810498714447 54 | 1044800000 -0.4313729405403137 -0.4274328649044037 55 | 1064700000 0.22492310404777527 0.48090100288391113 56 | 1084600000 0.09493700414896011 -0.6433440446853638 57 | 1104500000 -0.46256688237190247 0.26055392622947693 58 | 1124400000 0.5596188306808472 -0.2437085062265396 59 | 1144300000 -0.49324244260787964 -0.3470834493637085 60 | 1164200000 0.32895004749298096 0.40024179220199585 61 | 1184100000 -0.02371126413345337 -0.6446571946144104 62 | 1204000000 -0.39184707403182983 0.3413742184638977 63 | 1223900000 0.4658934772014618 -0.3781225085258484 64 | 1243800000 -0.5579816102981567 -0.24580629169940948 65 | 1263700000 0.40321284532546997 0.2887834310531616 66 | 1283600000 -0.20199713110923767 -0.6069770455360413 67 | 1303500000 -0.288360059261322 0.43180954456329346 68 | 1323400000 0.37138307094573975 -0.4566882848739624 69 | 1343300000 -0.5903123617172241 -0.08937656879425049 70 | 1363200000 0.4736197292804718 0.15437892079353333 71 | 1383100000 -0.304165244102478 -0.5670814514160156 72 | 1403000000 -0.18026690185070038 0.46422603726387024 73 | 1422900000 0.24639767408370972 -0.5334051847457886 74 | 1442800000 -0.58859783411026 0.038864172995090485 75 | 1462700000 0.4808274805545807 0.06101563945412636 76 | 1482600000 -0.4163985252380371 -0.4818567633628845 77 | 1502500000 -0.06306713819503784 0.4790143668651581 78 | 1522400000 0.1457294374704361 -0.5652775168418884 79 | 1542300000 -0.546525239944458 0.16759873926639557 80 | 1562200000 0.47399312257766724 -0.06272739171981812 81 | 1582100000 -0.5153735876083374 -0.3475554287433624 82 | 1602000000 0.1161639392375946 0.43713051080703735 83 | 1621900000 -0.03364386409521103 -0.5964519381523132 84 | 1641800000 -0.4671706557273865 0.2866678535938263 85 | 1661700000 0.4450404644012451 -0.1807178258895874 86 | 1681600000 -0.5631083846092224 -0.2386472225189209 87 | 1701500000 0.19772428274154663 0.3739407956600189 88 | 1721400000 -0.18013118207454681 -0.5703877210617065 89 | 1741300000 -0.4118673801422119 0.3435651361942291 90 | 1761200000 0.3591221868991852 -0.2652107775211334 91 | 1781100000 -0.5762578248977661 -0.04000149294734001 92 | 1801000000 0.27122247219085693 0.3412686884403229 93 | 1820900000 -0.26558512449264526 -0.48829537630081177 94 | 1840800000 -0.25474897027015686 0.41935670375823975 95 | 1860700000 0.3007740378379822 -0.3514559864997864 96 | 1880600000 -0.5211795568466187 0.07656865566968918 97 | 1900500000 0.3692742884159088 0.2055569291114807 98 | 1920400000 -0.3451622724533081 -0.4229567348957062 99 | 1940300000 -0.1598425805568695 0.430191308259964 100 | 1960200000 0.1917809545993805 -0.42813611030578613 101 | 1980100000 -0.5087525248527527 0.11805570870637894 102 | 2000000000 0.3704017996788025 0.15374013781547546 103 | -------------------------------------------------------------------------------- /Firmware/_calibration/cable_ch3.s1p: -------------------------------------------------------------------------------- 1 | # HZ S RI R 50 2 | 10000000 0.2571108937263489 -0.9937942028045654 3 | 29900000 -0.6030716896057129 0.6120917201042175 4 | 49800000 0.9279214143753052 -0.18504084646701813 5 | 69700000 -0.7533499002456665 -0.33377549052238464 6 | 89600000 0.5309068560600281 0.7252815365791321 7 | 109500000 0.004096850752830505 -0.8605415225028992 8 | 129400000 -0.4391650855541229 0.7039307355880737 9 | 149300000 0.7964149713516235 -0.3726142942905426 10 | 169200000 -0.7697880864143372 -0.15505041182041168 11 | 189100000 0.6704474091529846 0.5541911125183105 12 | 209000000 -0.15922340750694275 -0.7904901504516602 13 | 228900000 -0.23912134766578674 0.7802208065986633 14 | 248800000 0.6716068983078003 -0.5104817152023315 15 | 268700000 -0.7743467092514038 0.031159603968262672 16 | 288600000 0.7599645256996155 0.3554557263851166 17 | 308500000 -0.3123413324356079 -0.7288392186164856 18 | 328400000 -0.016855280846357346 0.7915047407150269 19 | 348300000 0.5229469537734985 -0.6319707632064819 20 | 368200000 -0.7207397818565369 0.23076693713665009 21 | 388100000 0.7974130511283875 0.1433458924293518 22 | 408000000 -0.4642895758152008 -0.6254493594169617 23 | 427900000 0.17808285355567932 0.7366194128990173 24 | 447800000 0.349004328250885 -0.7223183512687683 25 | 467700000 -0.6084887385368347 0.3983462154865265 26 | 487600000 0.7872880697250366 -0.023656170815229416 27 | 507500000 -0.5736711621284485 -0.4827934801578522 28 | 527400000 0.3688512444496155 0.6217142343521118 29 | 547300000 0.17065194249153137 -0.770005464553833 30 | 567200000 -0.4653477072715759 0.4998227059841156 31 | 587100000 0.7363120913505554 -0.21979057788848877 32 | 607000000 -0.6426096558570862 -0.3189893662929535 33 | 626900000 0.46407654881477356 0.5116126537322998 34 | 646800000 0.0005598729476332664 -0.7628813982009888 35 | 666700000 -0.30103054642677307 0.5588890910148621 36 | 686600000 0.6335286498069763 -0.3987748622894287 37 | 706500000 -0.6557241082191467 -0.1138351783156395 38 | 726400000 0.5681406259536743 0.3577501177787781 39 | 746300000 -0.1778307557106018 -0.7022238969802856 40 | 766200000 -0.15544940531253815 0.5744487643241882 41 | 786100000 0.5278624296188354 -0.5019777417182922 42 | 806000000 -0.6098358035087585 0.00724167563021183 43 | 825900000 0.6135453581809998 0.23072364926338196 44 | 845800000 -0.2756125330924988 -0.6200761198997498 45 | 865700000 0.010730546899139881 0.5552720427513123 46 | 885600000 0.40126270055770874 -0.5929276943206787 47 | 905500000 -0.5800793170928955 0.017441298812627792 48 | 925400000 0.6208855509757996 0.04832146316766739 49 | 945300000 -0.38465651869773865 -0.510841965675354 50 | 965200000 0.09432884305715561 0.5247215032577515 51 | 985100000 0.1740681231021881 -0.6373183727264404 52 | 1005000000 -0.504652202129364 0.21186065673828125 53 | 1024900000 0.6157748699188232 -0.09218810498714447 54 | 1044800000 -0.4313729405403137 -0.4274328649044037 55 | 1064700000 0.22492310404777527 0.48090100288391113 56 | 1084600000 0.09493700414896011 -0.6433440446853638 57 | 1104500000 -0.46256688237190247 0.26055392622947693 58 | 1124400000 0.5596188306808472 -0.2437085062265396 59 | 1144300000 -0.49324244260787964 -0.3470834493637085 60 | 1164200000 0.32895004749298096 0.40024179220199585 61 | 1184100000 -0.02371126413345337 -0.6446571946144104 62 | 1204000000 -0.39184707403182983 0.3413742184638977 63 | 1223900000 0.4658934772014618 -0.3781225085258484 64 | 1243800000 -0.5579816102981567 -0.24580629169940948 65 | 1263700000 0.40321284532546997 0.2887834310531616 66 | 1283600000 -0.20199713110923767 -0.6069770455360413 67 | 1303500000 -0.288360059261322 0.43180954456329346 68 | 1323400000 0.37138307094573975 -0.4566882848739624 69 | 1343300000 -0.5903123617172241 -0.08937656879425049 70 | 1363200000 0.4736197292804718 0.15437892079353333 71 | 1383100000 -0.304165244102478 -0.5670814514160156 72 | 1403000000 -0.18026690185070038 0.46422603726387024 73 | 1422900000 0.24639767408370972 -0.5334051847457886 74 | 1442800000 -0.58859783411026 0.038864172995090485 75 | 1462700000 0.4808274805545807 0.06101563945412636 76 | 1482600000 -0.4163985252380371 -0.4818567633628845 77 | 1502500000 -0.06306713819503784 0.4790143668651581 78 | 1522400000 0.1457294374704361 -0.5652775168418884 79 | 1542300000 -0.546525239944458 0.16759873926639557 80 | 1562200000 0.47399312257766724 -0.06272739171981812 81 | 1582100000 -0.5153735876083374 -0.3475554287433624 82 | 1602000000 0.1161639392375946 0.43713051080703735 83 | 1621900000 -0.03364386409521103 -0.5964519381523132 84 | 1641800000 -0.4671706557273865 0.2866678535938263 85 | 1661700000 0.4450404644012451 -0.1807178258895874 86 | 1681600000 -0.5631083846092224 -0.2386472225189209 87 | 1701500000 0.19772428274154663 0.3739407956600189 88 | 1721400000 -0.18013118207454681 -0.5703877210617065 89 | 1741300000 -0.4118673801422119 0.3435651361942291 90 | 1761200000 0.3591221868991852 -0.2652107775211334 91 | 1781100000 -0.5762578248977661 -0.04000149294734001 92 | 1801000000 0.27122247219085693 0.3412686884403229 93 | 1820900000 -0.26558512449264526 -0.48829537630081177 94 | 1840800000 -0.25474897027015686 0.41935670375823975 95 | 1860700000 0.3007740378379822 -0.3514559864997864 96 | 1880600000 -0.5211795568466187 0.07656865566968918 97 | 1900500000 0.3692742884159088 0.2055569291114807 98 | 1920400000 -0.3451622724533081 -0.4229567348957062 99 | 1940300000 -0.1598425805568695 0.430191308259964 100 | 1960200000 0.1917809545993805 -0.42813611030578613 101 | 1980100000 -0.5087525248527527 0.11805570870637894 102 | 2000000000 0.3704017996788025 0.15374013781547546 103 | -------------------------------------------------------------------------------- /Firmware/_calibration/cable_ch4.s1p: -------------------------------------------------------------------------------- 1 | # HZ S RI R 50 2 | 10000000 0.2571108937263489 -0.9937942028045654 3 | 29900000 -0.6030716896057129 0.6120917201042175 4 | 49800000 0.9279214143753052 -0.18504084646701813 5 | 69700000 -0.7533499002456665 -0.33377549052238464 6 | 89600000 0.5309068560600281 0.7252815365791321 7 | 109500000 0.004096850752830505 -0.8605415225028992 8 | 129400000 -0.4391650855541229 0.7039307355880737 9 | 149300000 0.7964149713516235 -0.3726142942905426 10 | 169200000 -0.7697880864143372 -0.15505041182041168 11 | 189100000 0.6704474091529846 0.5541911125183105 12 | 209000000 -0.15922340750694275 -0.7904901504516602 13 | 228900000 -0.23912134766578674 0.7802208065986633 14 | 248800000 0.6716068983078003 -0.5104817152023315 15 | 268700000 -0.7743467092514038 0.031159603968262672 16 | 288600000 0.7599645256996155 0.3554557263851166 17 | 308500000 -0.3123413324356079 -0.7288392186164856 18 | 328400000 -0.016855280846357346 0.7915047407150269 19 | 348300000 0.5229469537734985 -0.6319707632064819 20 | 368200000 -0.7207397818565369 0.23076693713665009 21 | 388100000 0.7974130511283875 0.1433458924293518 22 | 408000000 -0.4642895758152008 -0.6254493594169617 23 | 427900000 0.17808285355567932 0.7366194128990173 24 | 447800000 0.349004328250885 -0.7223183512687683 25 | 467700000 -0.6084887385368347 0.3983462154865265 26 | 487600000 0.7872880697250366 -0.023656170815229416 27 | 507500000 -0.5736711621284485 -0.4827934801578522 28 | 527400000 0.3688512444496155 0.6217142343521118 29 | 547300000 0.17065194249153137 -0.770005464553833 30 | 567200000 -0.4653477072715759 0.4998227059841156 31 | 587100000 0.7363120913505554 -0.21979057788848877 32 | 607000000 -0.6426096558570862 -0.3189893662929535 33 | 626900000 0.46407654881477356 0.5116126537322998 34 | 646800000 0.0005598729476332664 -0.7628813982009888 35 | 666700000 -0.30103054642677307 0.5588890910148621 36 | 686600000 0.6335286498069763 -0.3987748622894287 37 | 706500000 -0.6557241082191467 -0.1138351783156395 38 | 726400000 0.5681406259536743 0.3577501177787781 39 | 746300000 -0.1778307557106018 -0.7022238969802856 40 | 766200000 -0.15544940531253815 0.5744487643241882 41 | 786100000 0.5278624296188354 -0.5019777417182922 42 | 806000000 -0.6098358035087585 0.00724167563021183 43 | 825900000 0.6135453581809998 0.23072364926338196 44 | 845800000 -0.2756125330924988 -0.6200761198997498 45 | 865700000 0.010730546899139881 0.5552720427513123 46 | 885600000 0.40126270055770874 -0.5929276943206787 47 | 905500000 -0.5800793170928955 0.017441298812627792 48 | 925400000 0.6208855509757996 0.04832146316766739 49 | 945300000 -0.38465651869773865 -0.510841965675354 50 | 965200000 0.09432884305715561 0.5247215032577515 51 | 985100000 0.1740681231021881 -0.6373183727264404 52 | 1005000000 -0.504652202129364 0.21186065673828125 53 | 1024900000 0.6157748699188232 -0.09218810498714447 54 | 1044800000 -0.4313729405403137 -0.4274328649044037 55 | 1064700000 0.22492310404777527 0.48090100288391113 56 | 1084600000 0.09493700414896011 -0.6433440446853638 57 | 1104500000 -0.46256688237190247 0.26055392622947693 58 | 1124400000 0.5596188306808472 -0.2437085062265396 59 | 1144300000 -0.49324244260787964 -0.3470834493637085 60 | 1164200000 0.32895004749298096 0.40024179220199585 61 | 1184100000 -0.02371126413345337 -0.6446571946144104 62 | 1204000000 -0.39184707403182983 0.3413742184638977 63 | 1223900000 0.4658934772014618 -0.3781225085258484 64 | 1243800000 -0.5579816102981567 -0.24580629169940948 65 | 1263700000 0.40321284532546997 0.2887834310531616 66 | 1283600000 -0.20199713110923767 -0.6069770455360413 67 | 1303500000 -0.288360059261322 0.43180954456329346 68 | 1323400000 0.37138307094573975 -0.4566882848739624 69 | 1343300000 -0.5903123617172241 -0.08937656879425049 70 | 1363200000 0.4736197292804718 0.15437892079353333 71 | 1383100000 -0.304165244102478 -0.5670814514160156 72 | 1403000000 -0.18026690185070038 0.46422603726387024 73 | 1422900000 0.24639767408370972 -0.5334051847457886 74 | 1442800000 -0.58859783411026 0.038864172995090485 75 | 1462700000 0.4808274805545807 0.06101563945412636 76 | 1482600000 -0.4163985252380371 -0.4818567633628845 77 | 1502500000 -0.06306713819503784 0.4790143668651581 78 | 1522400000 0.1457294374704361 -0.5652775168418884 79 | 1542300000 -0.546525239944458 0.16759873926639557 80 | 1562200000 0.47399312257766724 -0.06272739171981812 81 | 1582100000 -0.5153735876083374 -0.3475554287433624 82 | 1602000000 0.1161639392375946 0.43713051080703735 83 | 1621900000 -0.03364386409521103 -0.5964519381523132 84 | 1641800000 -0.4671706557273865 0.2866678535938263 85 | 1661700000 0.4450404644012451 -0.1807178258895874 86 | 1681600000 -0.5631083846092224 -0.2386472225189209 87 | 1701500000 0.19772428274154663 0.3739407956600189 88 | 1721400000 -0.18013118207454681 -0.5703877210617065 89 | 1741300000 -0.4118673801422119 0.3435651361942291 90 | 1761200000 0.3591221868991852 -0.2652107775211334 91 | 1781100000 -0.5762578248977661 -0.04000149294734001 92 | 1801000000 0.27122247219085693 0.3412686884403229 93 | 1820900000 -0.26558512449264526 -0.48829537630081177 94 | 1840800000 -0.25474897027015686 0.41935670375823975 95 | 1860700000 0.3007740378379822 -0.3514559864997864 96 | 1880600000 -0.5211795568466187 0.07656865566968918 97 | 1900500000 0.3692742884159088 0.2055569291114807 98 | 1920400000 -0.3451622724533081 -0.4229567348957062 99 | 1940300000 -0.1598425805568695 0.430191308259964 100 | 1960200000 0.1917809545993805 -0.42813611030578613 101 | 1980100000 -0.5087525248527527 0.11805570870637894 102 | 2000000000 0.3704017996788025 0.15374013781547546 103 | -------------------------------------------------------------------------------- /Firmware/_daq_core/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for compiling the DAQ firmware 2 | # Author: Tamás Pető, Sándor Bajusz 3 | CC=gcc 4 | CFLAGS=-Wall -std=gnu99 -march=native -O2 -I. 5 | 6 | # Optimized C-flags for Pi 4 7 | #CFLAGS=-Wall -std=gnu99 -mcpu=cortex-a72 -mtune=cortex-a72 -Ofast -funsafe-math-optimizations -funroll-loops 8 | # Uncomment if you are using third party switches from ckoval on a Rpi device and have pigpio installed 9 | #PIGPIO=-lpigpio -DUSEPIGPIO 10 | 11 | # Uncomment if you installed GCC 11.2 12 | #CC=/usr/local/bin/gcc 13 | RM= rm -f 14 | 15 | HOST_ARCH := $(shell uname -m) 16 | 17 | all: daq_util rtl_daq rebuffer iq_server decimator 18 | ifeq ($(HOST_ARCH), x86_64) 19 | decimator: decimate_x86 20 | else 21 | decimator: decimate_arm_neon 22 | endif 23 | 24 | daq_util: 25 | $(CC) $(CFLAGS) -c -o ini.o ini.c 26 | $(CC) $(CFLAGS) -c -o log.o log.c 27 | $(CC) $(CFLAGS) -c -o iq_header.o iq_header.c 28 | $(CC) $(CFLAGS) -c -o sh_mem_util.o sh_mem_util.c 29 | 30 | rtl_daq: iq_header.c log.c ini.c rtl_daq.c rtl_daq.h 31 | $(CC) $(CFLAGS) log.o ini.o iq_header.o -o rtl_daq.out rtl_daq.c -lpthread -lzmq $(PIGPIO) -L. -lrtlsdr -lusb-1.0 32 | 33 | rebuffer: sh_mem_util.c iq_header.c log.c ini.c rebuffer.c rtl_daq.h 34 | $(CC) $(CFLAGS) sh_mem_util.o log.o ini.o iq_header.o -o rebuffer.out rebuffer.c -lrt -lm 35 | 36 | decimate_x86: sh_mem_util.c iq_header.c log.c ini.c fir_decimate.c 37 | $(CC) $(CFLAGS) -c fir_decimate.c -o fir_decimate.o 38 | $(CC) $(CFLAGS) fir_decimate.o sh_mem_util.o log.o ini.o iq_header.o -o decimate.out -lrt -lkfr_capi 39 | 40 | decimate_arm_neon: sh_mem_util.c iq_header.c log.c ini.c fir_decimate.c 41 | $(CC) $(CFLAGS) -DARM_NEON -c fir_decimate.c -o fir_decimate.o 42 | $(CC) $(CFLAGS) fir_decimate.o sh_mem_util.o log.o ini.o iq_header.o -o decimate.out -lrt -L. -lNE10 -lm 43 | 44 | iq_server: sh_mem_util.c iq_header.c log.c ini.c iq_server.c 45 | $(CC) $(CFLAGS) sh_mem_util.o log.o ini.o iq_header.o -o iq_server.out iq_server.c -lrt 46 | 47 | clean: 48 | $(RM) ini.o log.o iq_header.o sh_mem_util.o fir_decimate.o decimate.o rtl_daq.out rebuffer.out decimate.out iq_server.out 49 | 50 | -------------------------------------------------------------------------------- /Firmware/_daq_core/NE10.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-16 ARM Limited and Contributors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of ARM Limited nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * NE10 Library : inc/NE10.h 30 | */ 31 | 32 | 33 | /** 34 | * @defgroup groupMaths Math Functions 35 | * 36 | * An assortment of vector/matrix math operations. 37 | */ 38 | 39 | /** 40 | * @defgroup groupDSPs Signal Processing Functions 41 | * 42 | * 43 | * An assortment of signal processing operations, including FFT/IFFT, FIR, 44 | * and IIR operations. 45 | */ 46 | 47 | /** 48 | * @defgroup groupIMGPROCs Image Processing Functions 49 | * 50 | * 51 | * An assortment of image processing operations, including image resize 52 | * and rotation operations. 53 | */ 54 | 55 | /** 56 | * @defgroup groupPhysics Physics Functions 57 | * 58 | * 59 | * An assortment of collision detection operations, including AABB, relative 60 | * velocity, and contact impulse calculations. 61 | */ 62 | 63 | #ifndef NE10_H 64 | #define NE10_H 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | #include "NE10_types.h" 71 | #include "NE10_macros.h" 72 | #include "NE10_init.h" 73 | #include "NE10_math.h" 74 | #include "NE10_dsp.h" 75 | #include "NE10_imgproc.h" 76 | #include "NE10_physics.h" 77 | 78 | #ifdef __cplusplus 79 | } 80 | #endif 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Firmware/_daq_core/NE10_imgproc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-16 ARM Limited and Contributors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of ARM Limited nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * NE10 Library : inc/NE10_imgproc.h 30 | */ 31 | 32 | 33 | #include "NE10_types.h" 34 | 35 | #ifndef NE10_IMGPROC_H 36 | #define NE10_IMGPROC_H 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | ne10_result_t ne10_init_imgproc (ne10_int32_t is_NEON_available); 42 | 43 | /** 44 | * @ingroup IMG_RESIZE 45 | * @brief Image resize of 8-bit data. 46 | * 47 | * @param[out] *dst point to the destination image 48 | * @param[in] dst_width width of destination image 49 | * @param[in] dst_height height of destination image 50 | * @param[in] *src point to the source image 51 | * @param[in] src_width width of source image 52 | * @param[in] src_height height of source image 53 | * @param[in] src_stride stride of source buffer 54 | * 55 | * The function implements image resize. 56 | * Points to @ref ne10_img_resize_bilinear_rgba_c or @ref ne10_img_resize_bilinear_rgba_neon. 57 | */ 58 | extern void (*ne10_img_resize_bilinear_rgba) (ne10_uint8_t* dst, 59 | ne10_uint32_t dst_width, 60 | ne10_uint32_t dst_height, 61 | ne10_uint8_t* src, 62 | ne10_uint32_t src_width, 63 | ne10_uint32_t src_height, 64 | ne10_uint32_t src_stride); 65 | extern void ne10_img_resize_bilinear_rgba_c (ne10_uint8_t* dst, 66 | ne10_uint32_t dst_width, 67 | ne10_uint32_t dst_height, 68 | ne10_uint8_t* src, 69 | ne10_uint32_t src_width, 70 | ne10_uint32_t src_height, 71 | ne10_uint32_t src_stride); 72 | extern void ne10_img_resize_bilinear_rgba_neon (ne10_uint8_t* dst, 73 | ne10_uint32_t dst_width, 74 | ne10_uint32_t dst_height, 75 | ne10_uint8_t* src, 76 | ne10_uint32_t src_width, 77 | ne10_uint32_t src_height, 78 | ne10_uint32_t src_stride) 79 | asm ("ne10_img_resize_bilinear_rgba_neon"); 80 | 81 | /** 82 | * @ingroup IMG_ROTATE 83 | * @brief Image rotate of 8-bit data. 84 | * 85 | * @param[out] *dst point to the destination image 86 | * @param[out] *dst_width width of destination image 87 | * @param[out] *dst_height height of destination image 88 | * @param[in] *src point to the source image 89 | * @param[in] src_width width of source image 90 | * @param[in] src_height height of source image 91 | * @param[in] angle angle of rotate 92 | * 93 | * The function extracts pixels from src at sub-pixel accuracy and stores them to dst. 94 | * Points to @ref ne10_img_rotate_rgba_c or @ref ne10_img_rotate_rgba_neon. 95 | */ 96 | extern void (*ne10_img_rotate_rgba) (ne10_uint8_t* dst, 97 | ne10_uint32_t* dst_width, 98 | ne10_uint32_t* dst_height, 99 | ne10_uint8_t* src, 100 | ne10_uint32_t src_width, 101 | ne10_uint32_t src_height, 102 | ne10_int32_t angle); 103 | extern void ne10_img_rotate_rgba_c (ne10_uint8_t* dst, 104 | ne10_uint32_t* dst_width, 105 | ne10_uint32_t* dst_height, 106 | ne10_uint8_t* src, 107 | ne10_uint32_t src_width, 108 | ne10_uint32_t src_height, 109 | ne10_int32_t angle); 110 | #ifdef ENABLE_NE10_IMG_ROTATE_RGBA_NEON 111 | extern void ne10_img_rotate_rgba_neon (ne10_uint8_t* dst, 112 | ne10_uint32_t* dst_width, 113 | ne10_uint32_t* dst_height, 114 | ne10_uint8_t* src, 115 | ne10_uint32_t src_width, 116 | ne10_uint32_t src_height, 117 | ne10_int32_t angle) 118 | asm ("ne10_img_rotate_rgba_neon"); 119 | #endif // ENABLE_NE10_IMG_ROTATE_RGBA_NEON 120 | 121 | /** 122 | * @ingroup IMG_BOXFILTER 123 | * @brief Box blur of RGBA8888 image data 124 | * 125 | * @param[out] *dst pointer to the output buffer 126 | * @param[in] *src pointer to the input buffer 127 | * @param[in] src_sz size of the source image 128 | * @param[in] src_stride source stride 129 | * @param[in] dst_stride destination stride 130 | * @param[in] kernel blurring kernel size (with sides between 1 and 65535 pixels wide) 131 | * 132 | * Applies a box filter (also known as a [box blur](https://en.wikipedia.org/wiki/Box_blur)) 133 | * to an input image in the RGBA8888 format, producing a blurred output 134 | * image of the same size and format. The blurring kernel is anchored at its 135 | * center. When values must be obtained from beyond the source image 136 | * boundaries, zero-valued pixels are assumed. 137 | * 138 | * Points to @ref ne10_img_boxfilter_rgba8888_c or 139 | * @ref ne10_img_boxfilter_rgba8888_neon. This is an out-of-place algorithm. 140 | */ 141 | extern void (*ne10_img_boxfilter_rgba8888) (const ne10_uint8_t *src, 142 | ne10_uint8_t *dst, 143 | ne10_size_t src_size, 144 | ne10_int32_t src_stride, 145 | ne10_int32_t dst_stride, 146 | ne10_size_t kernel_size); 147 | extern void ne10_img_boxfilter_rgba8888_c (const ne10_uint8_t *src, 148 | ne10_uint8_t *dst, 149 | ne10_size_t src_size, 150 | ne10_int32_t src_stride, 151 | ne10_int32_t dst_stride, 152 | ne10_size_t kernel_size); 153 | extern void ne10_img_boxfilter_rgba8888_neon (const ne10_uint8_t *src, 154 | ne10_uint8_t *dst, 155 | ne10_size_t src_size, 156 | ne10_int32_t src_stride, 157 | ne10_int32_t dst_stride, 158 | ne10_size_t kernel_size); 159 | 160 | #ifdef __cplusplus 161 | } 162 | #endif 163 | 164 | #endif 165 | -------------------------------------------------------------------------------- /Firmware/_daq_core/NE10_init.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-16 ARM Limited and Contributors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of ARM Limited nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "NE10.h" 29 | 30 | #ifndef NE10_init_H 31 | #define NE10_init_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /*! 38 | This routine returns NE10_OK if the running platform supports NEON, otherwise it returns NE10_ERR 39 | */ 40 | extern ne10_result_t ne10_HasNEON(void); 41 | 42 | /*! 43 | This routine initializes all the function pointers. 44 | */ 45 | extern ne10_result_t ne10_init(void); 46 | 47 | /*! 48 | This routine initializes all the math function pointers defined in "NE10_math.h" with pointers to ARM NEON or ARM VFP implementations. 49 | */ 50 | extern ne10_result_t ne10_init_math (ne10_int32_t is_NEON_available); 51 | extern ne10_result_t ne10_init_dsp (ne10_int32_t is_NEON_available); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /Firmware/_daq_core/NE10_macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-16 ARM Limited and Contributors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of ARM Limited nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * NE10 Library : inc/NE10_macros.h 30 | */ 31 | 32 | /** NE10 defines a number of macros for use in its function signatures. 33 | * The macros are defined within this header file. 34 | */ 35 | 36 | #ifndef NE10_MACROS_H 37 | #define NE10_MACROS_H 38 | 39 | #include 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | ///////////////////////////////////////////////////////// 45 | // constant values that are used across the library 46 | ///////////////////////////////////////////////////////// 47 | 48 | #define NE10_PI (ne10_float32_t)(3.1415926535897932384626433832795) 49 | 50 | ///////////////////////////////////////////////////////// 51 | // some external macro definitions to be exposed to the users 52 | ///////////////////////////////////////////////////////// 53 | 54 | #define NE10_MALLOC malloc 55 | #define NE10_FREE(p) \ 56 | do { \ 57 | free(p); \ 58 | p = 0; \ 59 | }while(0) 60 | 61 | #define NE10_MIN(a,b) ((a)>(b)?(b):(a)) 62 | #define NE10_MAX(a,b) ((a)<(b)?(b):(a)) 63 | 64 | #define NE10_BYTE_ALIGNMENT(address, alignment) \ 65 | do { \ 66 | (address) = (((address) + ((alignment) - 1)) & ~ ((alignment) - 1)); \ 67 | }while (0) 68 | 69 | ///////////////////////////////////////////////////////// 70 | // macro definitions for float to fixed point 71 | ///////////////////////////////////////////////////////// 72 | #define NE10_F2I16_MAX 32767 73 | #define NE10_F2I16_SHIFT 15 74 | #define NE10_F2I16_SAMPPROD ne10_int32_t 75 | #define NE10_F2I16_OP(x) (ne10_int16_t)((x)*NE10_F2I16_MAX + 0.5f) 76 | #define NE10_F2I16_SROUND(x) (ne10_int16_t)((((x)<<1)+(1<>16) 77 | #define NE10_F2I16_SMUL(a,b) ((NE10_F2I16_SAMPPROD)(a)*(b)) 78 | #define NE10_F2I16_FIXDIV(c,div) \ 79 | do { ((c).r) = ( ( ((c).r)/div) ); \ 80 | ((c).i) = ( ( ((c).i)/div) ); }while (0) 81 | 82 | #define NE10_F2I32_MAX 2147483647 83 | #define NE10_F2I32_SHIFT 31 84 | #define NE10_F2I32_SAMPPROD ne10_int64_t 85 | #define NE10_F2I32_OP(x) (ne10_int32_t)((x)*NE10_F2I32_MAX + 0.5f) 86 | #define NE10_F2I32_SROUND(x) (ne10_int32_t) ((x)>>NE10_F2I32_SHIFT) 87 | #define NE10_F2I32_SMUL(a,b) ((NE10_F2I32_SAMPPROD)(a)*(b)) 88 | #define NE10_F2I32_FIXDIV(c,div) \ 89 | do { ((c).r) = ( ( ((c).r)/div) ); \ 90 | ((c).i) = ( ( ((c).i)/div) ); }while (0) 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /Firmware/_daq_core/NE10_physics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-16 ARM Limited and Contributors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of ARM Limited nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * NE10 Library : inc/NE10_physics.h 30 | */ 31 | 32 | 33 | #include "NE10_types.h" 34 | 35 | #ifndef NE10_PHYSICS_H 36 | #define NE10_PHYSICS_H 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | ne10_result_t ne10_init_physics (ne10_int32_t is_NEON_available); 42 | 43 | /** 44 | * @ingroup COLLISION_DETECT 45 | * @brief Compute the AABB for a polygon. 46 | * 47 | * @param[out] *aabb return axis aligned box 48 | * @param[in] *vertices a convex polygon 49 | * @param[in] *xf the position and orientation of rigid 50 | * @param[in] radius the aligned bounding 51 | * @param[in] vertex_count vertices count of convex ploygen 52 | * 53 | * The function computes the AABB for a polygon. Points to @ref ne10_physics_compute_aabb_vec2f_c 54 | * or @ref ne10_physics_compute_aabb_vec2f_neon, the latter of which requires that vertex_count 55 | * is a multiple of 4. 56 | */ 57 | extern void (*ne10_physics_compute_aabb_vec2f) (ne10_mat2x2f_t *aabb, 58 | ne10_vec2f_t *vertices, 59 | ne10_mat2x2f_t *xf, 60 | ne10_vec2f_t *radius, 61 | ne10_uint32_t vertex_count); 62 | extern void ne10_physics_compute_aabb_vec2f_c (ne10_mat2x2f_t *aabb, 63 | ne10_vec2f_t *vertices, 64 | ne10_mat2x2f_t *xf, 65 | ne10_vec2f_t *radius, 66 | ne10_uint32_t vertex_count); 67 | #ifdef ENABLE_NE10_PHYSICS_COMPUTE_AABB_VEC2F_NEON 68 | extern void ne10_physics_compute_aabb_vec2f_neon (ne10_mat2x2f_t *aabb, 69 | ne10_vec2f_t *vertices, 70 | ne10_mat2x2f_t *xf, 71 | ne10_vec2f_t *radius, 72 | ne10_uint32_t vertex_count); 73 | #endif // ENABLE_NE10_PHYSICS_COMPUTE_AABB_VEC2F_NEON 74 | 75 | /** 76 | * @ingroup COLLISION_DETECT 77 | * @brief Calculate relative velocity at contact. 78 | * 79 | * @param[out] *dv return relative velocity 80 | * @param[in] *v_wa velocity and angular velocity of body a 81 | * @param[in] *ra distance vector from center of mass of body a to contact point 82 | * @param[in] *v_wb velocity and angular velocity of body b 83 | * @param[in] *rb distance vector from center of mass of body b to contact point 84 | * @param[in] count the number of items 85 | * 86 | * To improve performance, two items are processed in one loop. 87 | * Points to @ref ne10_physics_relative_v_vec2f_c or @ref ne10_physics_relative_v_vec2f_neon. 88 | */ 89 | extern void (*ne10_physics_relative_v_vec2f) (ne10_vec2f_t *dv, 90 | ne10_vec3f_t *v_wa, 91 | ne10_vec2f_t *ra, 92 | ne10_vec3f_t *v_wb, 93 | ne10_vec2f_t *rb, 94 | ne10_uint32_t count); 95 | extern void ne10_physics_relative_v_vec2f_c (ne10_vec2f_t *dv, 96 | ne10_vec3f_t *v_wa, 97 | ne10_vec2f_t *ra, 98 | ne10_vec3f_t *v_wb, 99 | ne10_vec2f_t *rb, 100 | ne10_uint32_t count); 101 | #ifdef ENABLE_NE10_PHYSICS_RELATIVE_V_VEC2F_NEON 102 | /** 103 | * @ingroup COLLISION_DETECT 104 | * Specific implementation of @ref ne10_physics_relative_v_vec2f using NEON SIMD capabilities. 105 | */ 106 | extern void ne10_physics_relative_v_vec2f_neon (ne10_vec2f_t *dv, 107 | ne10_vec3f_t *v_wa, 108 | ne10_vec2f_t *ra, 109 | ne10_vec3f_t *v_wb, 110 | ne10_vec2f_t *rb, 111 | ne10_uint32_t count) 112 | asm ("ne10_physics_relative_v_vec2f_neon"); 113 | #endif // ENABLE_NE10_PHYSICS_RELATIVE_V_VEC2F_NEON 114 | 115 | /** 116 | * @ingroup COLLISION_DETECT 117 | * @brief Apply contact impulse. 118 | * 119 | * @param[in,out] *v_wa return velocity and angular velocity of body a 120 | * @param[in,out] *v_wb return velocity and angular velocity of body b 121 | * @param[in] *ra distance vector from center of mass of body a to contact point 122 | * @param[in] *rb distance vector from center of mass of body b to contact point 123 | * @param[in] *ima constant of body a 124 | * @param[in] *imb constant of body b 125 | * @param[in] *p constant 126 | * @param[in] count the number of items 127 | * 128 | * To improve performance, two items are processed in one loop. 129 | * Points to @ref ne10_physics_apply_impulse_vec2f_c or @ref ne10_physics_apply_impulse_vec2f_neon. 130 | */ 131 | extern void (*ne10_physics_apply_impulse_vec2f) (ne10_vec3f_t *v_wa, 132 | ne10_vec3f_t *v_wb, 133 | ne10_vec2f_t *ra, 134 | ne10_vec2f_t *rb, 135 | ne10_vec2f_t *ima, 136 | ne10_vec2f_t *imb, 137 | ne10_vec2f_t *p, 138 | ne10_uint32_t count); 139 | extern void ne10_physics_apply_impulse_vec2f_c (ne10_vec3f_t *v_wa, 140 | ne10_vec3f_t *v_wb, 141 | ne10_vec2f_t *ra, 142 | ne10_vec2f_t *rb, 143 | ne10_vec2f_t *ima, 144 | ne10_vec2f_t *imb, 145 | ne10_vec2f_t *p, 146 | ne10_uint32_t count); 147 | #ifdef ENABLE_NE10_PHYSICS_APPLY_IMPULSE_VEC2F_NEON 148 | /** 149 | * @ingroup COLLISION_DETECT 150 | * Specific implementation of @ref ne10_physics_apply_impulse_vec2f using NEON SIMD capabilities. 151 | */ 152 | extern void ne10_physics_apply_impulse_vec2f_neon (ne10_vec3f_t *v_wa, 153 | ne10_vec3f_t *v_wb, 154 | ne10_vec2f_t *ra, 155 | ne10_vec2f_t *rb, 156 | ne10_vec2f_t *ima, 157 | ne10_vec2f_t *imb, 158 | ne10_vec2f_t *p, 159 | ne10_uint32_t count) 160 | asm ("ne10_physics_apply_impulse_vec2f_neon"); 161 | #endif // ENABLE_NE10_PHYSICS_APPLY_IMPULSE_VEC2F_NEON 162 | 163 | #ifdef __cplusplus 164 | } 165 | #endif 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /Firmware/_daq_core/dac_controller.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description: 3 | Implements driver functions to controll the RF-Front modul. The driver covers the DAC controll for the IQ 4 | modulators. 5 | TODO: Implement multiple channel operation 6 | TODO: Take care of bit width in case of multipple channel operation 7 | 8 | Project: HeIMDALL DAQ Firmware 9 | Python version: 3.6 10 | RF Front version: 2.0 11 | SBC: Asus Tinkerboard 12 | Author: Tamás Pető 13 | License: GNU GPL V3 14 | 15 | This program is free software: you can redistribute it and/or modify 16 | it under the terms of the GNU General Public License as published by 17 | the Free Software Foundation, either version 3 of the License, or 18 | any later version. 19 | 20 | This program is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | 25 | You should have received a copy of the GNU General Public License 26 | along with this program. If not, see . 27 | """ 28 | 29 | import time 30 | import logging 31 | class DACController(): 32 | def __init__(self, iface): 33 | """ 34 | iface: "SPI" or "I2C" 35 | SPI with direct control 36 | I2C with using an i2c to spi bridge 37 | """ 38 | self.logger = logging.getLogger(__name__) 39 | 40 | self.iface = iface 41 | self.channel_no = 6 # Used only when I2C interface is selected 42 | self.bit_widths = [8, 8, 8, 8 , 8, 8] # Should have "channel_no" items 43 | self.i2c_addresses = [40, 42, 43, 44, 45, 41] # Should have "channel_no" items 44 | # TODO: Load the addresses from the ini file 45 | 46 | self.init_status = False 47 | if self.iface == "SPI": 48 | self.logger.info("Initializing SPI device") 49 | 50 | import spidev 51 | 52 | spi_speed = 5000 53 | self.spi_i = spidev.SpiDev() 54 | self.spi_q = spidev.SpiDev() 55 | self.spi_i.open(0, 0) 56 | self.spi_q.open(0, 1) 57 | self.spi_i.max_speed_hz = spi_speed 58 | self.spi_q.max_speed_hz = spi_speed 59 | self.spi_i.mode = 0b01 60 | self.spi_q.mode = 0b01 61 | self.spi_i.bits_per_word=8 62 | self.spi_q.bits_per_word=8 63 | self.logger.info("SPI initialization finished") 64 | 65 | elif self.iface == "I2C": 66 | self.logger.info("Initializing I2C device") 67 | 68 | from smbus2 import SMBus 69 | 70 | self.i2cbus = SMBus(1) # To use the i2c1 device on the ASUS Tinkerboard SBC 71 | 72 | """ 73 | Configure spi on the i2c-spi device 74 | 75 | I DAC on SS0 76 | Q DAC on SS1 77 | According to HeIMDALL FM-RTL HW ver2.0 78 | spi_config = 7 # SPI clock - 58 kHz 79 | spi_config = 4 # SPI clock - 1843 kHz 80 | """ 81 | spi_config = 4 # SPI clock - 1843 kHz 82 | fid = 240 # F0h -> See SC18IS602B data sheet 83 | 84 | for address in self.i2c_addresses: 85 | self.logger.info("I2C address: {:d}".format(address)) 86 | try: 87 | self.i2cbus.write_byte_data(address , fid, spi_config) 88 | time.sleep(0.1) 89 | self.init_status = True 90 | self.logger.info(" I2C- SPI bridge has been configured") 91 | except: 92 | self.logger.critical("Error occured when writing to i2c device") 93 | self.i2cbus.close() 94 | self.init_status = False 95 | self.logger.critical("I2C device configuration failed, device closed") 96 | break 97 | else: 98 | self.logger.critical("Unidentified interface type") 99 | 100 | def close_interface(self): 101 | """ 102 | Close function 103 | TODO:Write header 104 | """ 105 | if self.iface == "SPI": 106 | self.spi_i.close() 107 | self.spi_q.close() 108 | self.logger.info("SPI interface closed") 109 | elif self.iface == "I2C": 110 | self.i2cbus.close() 111 | self.logger.info("I2C interface closed") 112 | 113 | def set_IQ_value(self, i_val, q_val, channel): 114 | """ 115 | Function to set the desired IQ value using DACs 116 | TODO: Write header 117 | TODO: Implement control for multichannel op. 118 | TODO: Verify operation with 8 bit DACs 119 | 120 | i_val: should be in the range of 0..1 121 | q_val: should be in the range of 0..1 122 | 123 | Return values: 124 | -------------- 125 | 126 | -1: Invalid I or Q value 127 | """ 128 | 129 | # Range check 130 | if 1 < i_val or i_val < 0: 131 | self.logger.error("Ivalid I value. It sholuld be in range 0..1:", i_val) 132 | return -1 133 | if 1 < q_val or q_val < 0: 134 | self.logger.error("Ivalid Q value. It sholuld be in range 0..1:", q_val) 135 | return -1 136 | 137 | i_val *= 2**self.bit_widths[0]-1 138 | q_val *= 2**self.bit_widths[0]-1 139 | 140 | i_val = int(round(i_val)) 141 | q_val = int(round(q_val)) 142 | 143 | # Covert integers to 16 bit byte arrays 144 | # Covert integers to 16 bit byte arrays 145 | i_val <<= 14-self.bit_widths[0] 146 | i_byte_array=[] 147 | for i in range(2): 148 | b = bytes([(i_val >> 8*(1-i)) & 0xFF]) 149 | i_byte_array.append(int.from_bytes(b,"big")) 150 | q_val <<= 14-self.bit_widths[0] 151 | q_byte_array=[] 152 | for q in range(2): 153 | b = bytes([(q_val >> 8*(1-q)) & 0xFF]) 154 | q_byte_array.append(int.from_bytes(b,"big")) 155 | 156 | # Display note: first channel is the reference channel, that is why we use (channel+1) 157 | self.logger.debug("Channel: {:d}, Address: {:d}".format(channel+1, self.i2c_addresses[channel])) 158 | self.logger.debug("Sending to I DAC: [{:d},{:d}]".format(i_byte_array[0], i_byte_array[1])) 159 | self.logger.debug("Sending to Q DAC: [{:d},{:d}]".format(q_byte_array[0], q_byte_array[1])) 160 | 161 | if self.iface == "SPI": 162 | 163 | self.spi_i.xfer2(i_byte_array) 164 | self.spi_q.xfer2(q_byte_array) 165 | 166 | elif self.iface == "I2C": 167 | try: 168 | self.i2cbus.write_i2c_block_data(self.i2c_addresses[channel], 1, i_byte_array) 169 | self.i2cbus.write_i2c_block_data(self.i2c_addresses[channel], 2, q_byte_array) 170 | except: 171 | self.logger.error("Error occured when writing to i2c device") 172 | return -2 173 | self.logger.debug("I2C transfer completed") 174 | return 0 175 | 176 | 177 | -------------------------------------------------------------------------------- /Firmware/_daq_core/eth_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Description: Manages the Ethernet server socket for IQ streaming 4 | * 5 | * Project: HeIMDALL DAQ Firmware 6 | * License: GNU GPL V3 7 | * Authors: Tamas Peto 8 | * 9 | * Copyright (C) 2018-2020 Tamás Pető 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program. If not, see . 23 | * 24 | */ 25 | #pragma once 26 | #include 27 | // Ethernet server libraries 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "log.h" 37 | 38 | #define IQ_SERVER_PORT 5000 39 | 40 | int iq_stream_con(int * sockets) 41 | { 42 | /* 43 | * Description: 44 | * This function opens an Ethernet server and then accepts connections 45 | * from IQ sample sinks, such as signal processing or data recorder modules. 46 | * Aftef successfull connection, the server waits for the "streaming" command. 47 | * When the command has arrived, the descriptor of the client is passed to 48 | * the calling process (generally a double buffered iq streaming process). 49 | */ 50 | int sock, connected, bytes_recieved, true_value = 1; 51 | socklen_t sin_size; 52 | char recv_data[1024]; 53 | 54 | struct sockaddr_in server_addr,client_addr; // This structure stores the addresses 55 | 56 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // Create server socket 57 | log_fatal("Create server socket failed "); 58 | return(-1); 59 | } 60 | /* Applying socket options*/ 61 | if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true_value,sizeof(int)) == -1) { 62 | log_fatal("Setting socket options failed"); 63 | return(-1); 64 | } 65 | // Set server address parameters 66 | server_addr.sin_family = AF_INET; // Address family 67 | server_addr.sin_port = htons(IQ_SERVER_PORT); 68 | server_addr.sin_addr.s_addr = INADDR_ANY; // Set the server IP address to own 69 | bzero(&(server_addr.sin_zero),8); 70 | 71 | if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) 72 | { 73 | log_fatal("Unable to bind the server socket"); 74 | return(-1); 75 | } 76 | if (listen(sock, 5) == -1) 77 | { 78 | log_fatal("Unable to start listenning"); 79 | return(-1); 80 | } 81 | 82 | log_info("IQ Data TCP Server waiting for client on port %d",IQ_SERVER_PORT); 83 | 84 | sin_size = sizeof(struct sockaddr_in); 85 | connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size); 86 | log_info("New connection from (%s , %d)", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); 87 | 88 | log_info("Waiting for client streaming request"); 89 | bytes_recieved = recv(connected,recv_data,1024,0); 90 | recv_data[bytes_recieved] = '\0'; 91 | 92 | /* User may close the socket unexpectedly */ 93 | if(bytes_recieved == -1) 94 | { 95 | log_error("Unexpected connection close - exiting.."); 96 | close(connected); 97 | close(sock); 98 | return -1; 99 | } 100 | /* Streaming start command */ 101 | if (strcmp(recv_data , "streaming") == 0) 102 | { 103 | log_info("Streaming request received"); 104 | sockets[0] = sock; 105 | sockets[1] = connected; 106 | return 0; 107 | } 108 | close(connected); 109 | close(sock); 110 | return -1; //Not expected signaling 111 | } 112 | int iq_stream_close(int* sockets) 113 | { 114 | /* 115 | * Closes the previously opened TCP Server and Client sockets 116 | */ 117 | close(sockets[1]); 118 | close(sockets[0]); 119 | return 0; 120 | } 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Firmware/_daq_core/ini.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 11 | #define _CRT_SECURE_NO_WARNINGS 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "ini.h" 19 | 20 | #if !INI_USE_STACK 21 | #include 22 | #endif 23 | 24 | #define MAX_SECTION 50 25 | #define MAX_NAME 50 26 | 27 | /* Used by ini_parse_string() to keep track of string parsing state. */ 28 | typedef struct { 29 | const char* ptr; 30 | size_t num_left; 31 | } ini_parse_string_ctx; 32 | 33 | /* Strip whitespace chars off end of given string, in place. Return s. */ 34 | static char* rstrip(char* s) 35 | { 36 | char* p = s + strlen(s); 37 | while (p > s && isspace((unsigned char)(*--p))) 38 | *p = '\0'; 39 | return s; 40 | } 41 | 42 | /* Return pointer to first non-whitespace char in given string. */ 43 | static char* lskip(const char* s) 44 | { 45 | while (*s && isspace((unsigned char)(*s))) 46 | s++; 47 | return (char*)s; 48 | } 49 | 50 | /* Return pointer to first char (of chars) or inline comment in given string, 51 | or pointer to null at end of string if neither found. Inline comment must 52 | be prefixed by a whitespace character to register as a comment. */ 53 | static char* find_chars_or_comment(const char* s, const char* chars) 54 | { 55 | #if INI_ALLOW_INLINE_COMMENTS 56 | int was_space = 0; 57 | while (*s && (!chars || !strchr(chars, *s)) && 58 | !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { 59 | was_space = isspace((unsigned char)(*s)); 60 | s++; 61 | } 62 | #else 63 | while (*s && (!chars || !strchr(chars, *s))) { 64 | s++; 65 | } 66 | #endif 67 | return (char*)s; 68 | } 69 | 70 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ 71 | static char* strncpy0(char* dest, const char* src, size_t size) 72 | { 73 | strncpy(dest, src, size - 1); 74 | dest[size - 1] = '\0'; 75 | return dest; 76 | } 77 | 78 | /* See documentation in header file. */ 79 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 80 | void* user) 81 | { 82 | /* Uses a fair bit of stack (use heap instead if you need to) */ 83 | #if INI_USE_STACK 84 | char line[INI_MAX_LINE]; 85 | int max_line = INI_MAX_LINE; 86 | #else 87 | char* line; 88 | int max_line = INI_INITIAL_ALLOC; 89 | #endif 90 | #if INI_ALLOW_REALLOC && !INI_USE_STACK 91 | char* new_line; 92 | int offset; 93 | #endif 94 | char section[MAX_SECTION] = ""; 95 | char prev_name[MAX_NAME] = ""; 96 | 97 | char* start; 98 | char* end; 99 | char* name; 100 | char* value; 101 | int lineno = 0; 102 | int error = 0; 103 | 104 | #if !INI_USE_STACK 105 | line = (char*)malloc(INI_INITIAL_ALLOC); 106 | if (!line) { 107 | return -2; 108 | } 109 | #endif 110 | 111 | #if INI_HANDLER_LINENO 112 | #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) 113 | #else 114 | #define HANDLER(u, s, n, v) handler(u, s, n, v) 115 | #endif 116 | 117 | /* Scan through stream line by line */ 118 | while (reader(line, max_line, stream) != NULL) { 119 | #if INI_ALLOW_REALLOC && !INI_USE_STACK 120 | offset = strlen(line); 121 | while (offset == max_line - 1 && line[offset - 1] != '\n') { 122 | max_line *= 2; 123 | if (max_line > INI_MAX_LINE) 124 | max_line = INI_MAX_LINE; 125 | new_line = realloc(line, max_line); 126 | if (!new_line) { 127 | free(line); 128 | return -2; 129 | } 130 | line = new_line; 131 | if (reader(line + offset, max_line - offset, stream) == NULL) 132 | break; 133 | if (max_line >= INI_MAX_LINE) 134 | break; 135 | offset += strlen(line + offset); 136 | } 137 | #endif 138 | 139 | lineno++; 140 | 141 | start = line; 142 | #if INI_ALLOW_BOM 143 | if (lineno == 1 && (unsigned char)start[0] == 0xEF && 144 | (unsigned char)start[1] == 0xBB && 145 | (unsigned char)start[2] == 0xBF) { 146 | start += 3; 147 | } 148 | #endif 149 | start = lskip(rstrip(start)); 150 | 151 | if (strchr(INI_START_COMMENT_PREFIXES, *start)) { 152 | /* Start-of-line comment */ 153 | } 154 | #if INI_ALLOW_MULTILINE 155 | else if (*prev_name && *start && start > line) { 156 | /* Non-blank line with leading whitespace, treat as continuation 157 | of previous name's value (as per Python configparser). */ 158 | if (!HANDLER(user, section, prev_name, start) && !error) 159 | error = lineno; 160 | } 161 | #endif 162 | else if (*start == '[') { 163 | /* A "[section]" line */ 164 | end = find_chars_or_comment(start + 1, "]"); 165 | if (*end == ']') { 166 | *end = '\0'; 167 | strncpy0(section, start + 1, sizeof(section)); 168 | *prev_name = '\0'; 169 | #if INI_CALL_HANDLER_ON_NEW_SECTION 170 | if (!HANDLER(user, section, NULL, NULL) && !error) 171 | error = lineno; 172 | #endif 173 | } 174 | else if (!error) { 175 | /* No ']' found on section line */ 176 | error = lineno; 177 | } 178 | } 179 | else if (*start) { 180 | /* Not a comment, must be a name[=:]value pair */ 181 | end = find_chars_or_comment(start, "=:"); 182 | if (*end == '=' || *end == ':') { 183 | *end = '\0'; 184 | name = rstrip(start); 185 | value = end + 1; 186 | #if INI_ALLOW_INLINE_COMMENTS 187 | end = find_chars_or_comment(value, NULL); 188 | if (*end) 189 | *end = '\0'; 190 | #endif 191 | value = lskip(value); 192 | rstrip(value); 193 | 194 | /* Valid name[=:]value pair found, call handler */ 195 | strncpy0(prev_name, name, sizeof(prev_name)); 196 | if (!HANDLER(user, section, name, value) && !error) 197 | error = lineno; 198 | } 199 | else if (!error) { 200 | /* No '=' or ':' found on name[=:]value line */ 201 | error = lineno; 202 | } 203 | } 204 | 205 | #if INI_STOP_ON_FIRST_ERROR 206 | if (error) 207 | break; 208 | #endif 209 | } 210 | 211 | #if !INI_USE_STACK 212 | free(line); 213 | #endif 214 | 215 | return error; 216 | } 217 | 218 | /* See documentation in header file. */ 219 | int ini_parse_file(FILE* file, ini_handler handler, void* user) 220 | { 221 | return ini_parse_stream((ini_reader)fgets, file, handler, user); 222 | } 223 | 224 | /* See documentation in header file. */ 225 | int ini_parse(const char* filename, ini_handler handler, void* user) 226 | { 227 | FILE* file; 228 | int error; 229 | 230 | file = fopen(filename, "r"); 231 | if (!file) 232 | return -1; 233 | error = ini_parse_file(file, handler, user); 234 | fclose(file); 235 | return error; 236 | } 237 | 238 | /* An ini_reader function to read the next line from a string buffer. This 239 | is the fgets() equivalent used by ini_parse_string(). */ 240 | static char* ini_reader_string(char* str, int num, void* stream) { 241 | ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; 242 | const char* ctx_ptr = ctx->ptr; 243 | size_t ctx_num_left = ctx->num_left; 244 | char* strp = str; 245 | char c; 246 | 247 | if (ctx_num_left == 0 || num < 2) 248 | return NULL; 249 | 250 | while (num > 1 && ctx_num_left != 0) { 251 | c = *ctx_ptr++; 252 | ctx_num_left--; 253 | *strp++ = c; 254 | if (c == '\n') 255 | break; 256 | num--; 257 | } 258 | 259 | *strp = '\0'; 260 | ctx->ptr = ctx_ptr; 261 | ctx->num_left = ctx_num_left; 262 | return str; 263 | } 264 | 265 | /* See documentation in header file. */ 266 | int ini_parse_string(const char* string, ini_handler handler, void* user) { 267 | ini_parse_string_ctx ctx; 268 | 269 | ctx.ptr = string; 270 | ctx.num_left = strlen(string); 271 | return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, 272 | user); 273 | } 274 | -------------------------------------------------------------------------------- /Firmware/_daq_core/ini.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #ifndef __INI_H__ 11 | #define __INI_H__ 12 | 13 | /* Make this header file easier to include in C++ code */ 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #include 19 | 20 | /* Nonzero if ini_handler callback should accept lineno parameter. */ 21 | #ifndef INI_HANDLER_LINENO 22 | #define INI_HANDLER_LINENO 0 23 | #endif 24 | 25 | /* Typedef for prototype of handler function. */ 26 | #if INI_HANDLER_LINENO 27 | typedef int (*ini_handler)(void* user, const char* section, 28 | const char* name, const char* value, 29 | int lineno); 30 | #else 31 | typedef int (*ini_handler)(void* user, const char* section, 32 | const char* name, const char* value); 33 | #endif 34 | 35 | /* Typedef for prototype of fgets-style reader function. */ 36 | typedef char* (*ini_reader)(char* str, int num, void* stream); 37 | 38 | /* Parse given INI-style file. May have [section]s, name=value pairs 39 | (whitespace stripped), and comments starting with ';' (semicolon). Section 40 | is "" if name=value pair parsed before any section heading. name:value 41 | pairs are also supported as a concession to Python's configparser. 42 | 43 | For each name=value pair parsed, call handler function with given user 44 | pointer as well as section, name, and value (data only valid for duration 45 | of handler call). Handler should return nonzero on success, zero on error. 46 | 47 | Returns 0 on success, line number of first error on parse error (doesn't 48 | stop on first error), -1 on file open error, or -2 on memory allocation 49 | error (only when INI_USE_STACK is zero). 50 | */ 51 | int ini_parse(const char* filename, ini_handler handler, void* user); 52 | 53 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 54 | close the file when it's finished -- the caller must do that. */ 55 | int ini_parse_file(FILE* file, ini_handler handler, void* user); 56 | 57 | /* Same as ini_parse(), but takes an ini_reader function pointer instead of 58 | filename. Used for implementing custom or string-based I/O (see also 59 | ini_parse_string). */ 60 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 61 | void* user); 62 | 63 | /* Same as ini_parse(), but takes a zero-terminated string with the INI data 64 | instead of a file. Useful for parsing INI data from a network socket or 65 | already in memory. */ 66 | int ini_parse_string(const char* string, ini_handler handler, void* user); 67 | 68 | /* Nonzero to allow multi-line value parsing, in the style of Python's 69 | configparser. If allowed, ini_parse() will call the handler with the same 70 | name for each subsequent line parsed. */ 71 | #ifndef INI_ALLOW_MULTILINE 72 | #define INI_ALLOW_MULTILINE 1 73 | #endif 74 | 75 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 76 | the file. See https://github.com/benhoyt/inih/issues/21 */ 77 | #ifndef INI_ALLOW_BOM 78 | #define INI_ALLOW_BOM 1 79 | #endif 80 | 81 | /* Chars that begin a start-of-line comment. Per Python configparser, allow 82 | both ; and # comments at the start of a line by default. */ 83 | #ifndef INI_START_COMMENT_PREFIXES 84 | #define INI_START_COMMENT_PREFIXES ";#" 85 | #endif 86 | 87 | /* Nonzero to allow inline comments (with valid inline comment characters 88 | specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 89 | Python 3.2+ configparser behaviour. */ 90 | #ifndef INI_ALLOW_INLINE_COMMENTS 91 | #define INI_ALLOW_INLINE_COMMENTS 1 92 | #endif 93 | #ifndef INI_INLINE_COMMENT_PREFIXES 94 | #define INI_INLINE_COMMENT_PREFIXES ";" 95 | #endif 96 | 97 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ 98 | #ifndef INI_USE_STACK 99 | #define INI_USE_STACK 1 100 | #endif 101 | 102 | /* Maximum line length for any line in INI file (stack or heap). Note that 103 | this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ 104 | #ifndef INI_MAX_LINE 105 | #define INI_MAX_LINE 200 106 | #endif 107 | 108 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a 109 | fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is 110 | zero. */ 111 | #ifndef INI_ALLOW_REALLOC 112 | #define INI_ALLOW_REALLOC 0 113 | #endif 114 | 115 | /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK 116 | is zero. */ 117 | #ifndef INI_INITIAL_ALLOC 118 | #define INI_INITIAL_ALLOC 200 119 | #endif 120 | 121 | /* Stop parsing on first error (default is to keep parsing). */ 122 | #ifndef INI_STOP_ON_FIRST_ERROR 123 | #define INI_STOP_ON_FIRST_ERROR 0 124 | #endif 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif /* __INI_H__ */ 131 | -------------------------------------------------------------------------------- /Firmware/_daq_core/inter_module_messages.py: -------------------------------------------------------------------------------- 1 | from struct import pack 2 | 3 | def pack_msg_reconfiguration(module_identifier,center_frequency, sample_rate, gain): 4 | """ 5 | Prepares the byte array of an inter-module ZMQ message for tunner reconfiguration. 6 | 7 | Parameters: 8 | ----------- 9 | :param: module_identifier: Source module id 10 | :param: center_frequency: New RF center frequency, specified in [Hz] 11 | :param: sample_rate: New ADC sampling frequency, specified in [Hz] 12 | :param: gain: New Gain value (Will be unique for all tuners) 13 | 14 | :type: module_identifier: int 15 | :type: center_frequency: int 16 | :type: sample_rate: int 17 | :type: gain: int 18 | 19 | Return: 20 | ------- 21 | Assembled message structure in byte array 22 | """ 23 | msg_length = 128 # Total message length 128 byte 24 | msg_byte_array = pack("b", module_identifier) # 1byte 25 | msg_byte_array += 'r'.encode('ascii') # 1 byte 26 | msg_byte_array += pack('III', center_frequency, sample_rate, gain) # 12 byte 27 | for m in range(msg_length-1-1-12): 28 | msg_byte_array +=pack('b',0) 29 | 30 | return msg_byte_array 31 | 32 | def pack_msg_rf_tune(module_identifier,center_frequency): 33 | """ 34 | Prepares the byte array of an inter-module ZMQ message for Radio Frequency 35 | tunning. 36 | 37 | Parameters: 38 | ----------- 39 | :param: module_identifier: Source module id 40 | :param: center_frequency: New RF center frequency, specified in [Hz] 41 | 42 | :type: module_identifier: int 43 | :type: center_frequency: int 44 | 45 | Return: 46 | ------- 47 | Assembled message structure in byte array 48 | """ 49 | msg_length = 128 # Total message length 128 byte 50 | msg_byte_array = pack("b", module_identifier) # 1byte 51 | msg_byte_array += 'c'.encode('ascii') # 1 byte 52 | msg_byte_array += pack('I', center_frequency) # 4 byte 53 | for m in range(msg_length-1-1-4): 54 | msg_byte_array +=pack('b',0) 55 | 56 | return msg_byte_array 57 | 58 | def pack_msg_set_gain(module_identifier, gains): 59 | """ 60 | Prepares the byte array of an inter-module ZMQ message for receiver gain change. 61 | 62 | Parameters: 63 | ----------- 64 | :param: module_identifier: Source module id 65 | :param: gain: New Gain values (Can be different for the individual receivers) 66 | 67 | :type: module_identifier: int 68 | :type: gains: list of ints values [gain_for_ch1, gain_for_ch2, ..] 69 | 70 | Return: 71 | ------- 72 | Assembled message structure in byte array 73 | """ 74 | msg_length = 128 # Total message length 128 byte 75 | msg_byte_array = pack("b", module_identifier) # 1byte 76 | msg_byte_array += 'g'.encode('ascii') # 1 byte 77 | for gain in gains: 78 | msg_byte_array += pack('I', gain) # 4 byte 79 | for m in range(msg_length-1-1-len(gains)*4): 80 | msg_byte_array +=pack('b',0) 81 | return msg_byte_array 82 | 83 | 84 | def pack_msg_enable_agc(module_identifier): 85 | """ 86 | Prepares the byte array of an inter-module ZMQ message for enabling 87 | automatic gain control. 88 | 89 | Parameters: 90 | ----------- 91 | :param: module_identifier: Source module id 92 | 93 | Return: 94 | ------- 95 | Assembled message structure in byte array 96 | """ 97 | msg_length = 128 # Total message length 128 byte 98 | msg_byte_array = pack("b", module_identifier) # 1byte 99 | msg_byte_array += 'a'.encode('ascii') # 1 byte 100 | for _ in range(msg_length-1-1): 101 | msg_byte_array +=pack('b',0) 102 | 103 | return msg_byte_array 104 | 105 | 106 | def pack_msg_noise_source_ctr(module_identifier, state): 107 | """ 108 | Prepares the byte array of an inter-module ZMQ message for internal noise source control 109 | 110 | Parameters: 111 | ----------- 112 | :param: module_identifier: Source module id 113 | :param: state: True will turn on the noise source, False will turn off the noise source 114 | 115 | :type: module_identifier: int 116 | :type: stae: Boolean 117 | 118 | Return: 119 | ------- 120 | Assembled message structure in byte array 121 | """ 122 | msg_length = 128 # Total message length 128 byte 123 | msg_byte_array = pack("b", module_identifier) # 1byte 124 | msg_byte_array += 'n'.encode('ascii') # 1 byte 125 | if state: 126 | msg_byte_array += pack('b',1) # 1 byte 127 | else: 128 | msg_byte_array += pack('b',0) # 1 byte 129 | for m in range(msg_length-1-1-1): 130 | msg_byte_array +=pack('b',0) 131 | return msg_byte_array 132 | 133 | def pack_msg_sample_freq_tune(module_identifier, fs_ppm_offsets): 134 | """ 135 | Prepares the byte array of an inter-module ZMQ message for sampling frequency ppm offset seting. 136 | 137 | Parameters: 138 | ----------- 139 | :param: module_identifier: Source module id 140 | :param: fs_ppm_offsets: List of sampling frequency offset for the individual receivers channels. 141 | 142 | :type: module_identifier: int 143 | :type: fs_ppm_offsets: list of float values [fs offset for ch1, fs offset for ch 2] 144 | 145 | Return: 146 | ------- 147 | Assembled message structure in byte array 148 | """ 149 | msg_length = 128 # Total message length 128 byte 150 | msg_byte_array = pack("b", module_identifier) # 1byte 151 | msg_byte_array += 's'.encode('ascii') # 1 byte 152 | for fs_offset in fs_ppm_offsets: 153 | msg_byte_array += pack('f', fs_offset) # 4 byte 154 | for m in range(msg_length-1-1-len(fs_ppm_offsets)*4): 155 | msg_byte_array +=pack('b',0) 156 | 157 | return msg_byte_array 158 | -------------------------------------------------------------------------------- /Firmware/_daq_core/iq_eth_sink.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : 3 | IQ Frame Ethernet receiver used for system level throughput testing 4 | 5 | Project : HeIMDALL DAQ Firmware 6 | License : GNU GPL V3 7 | Author : Tamas Peto 8 | 9 | Copyright (C) 2018-2020 Tamás Pető 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see . 23 | 24 | """ 25 | import os 26 | import sys 27 | import logging 28 | import socket 29 | import time 30 | # Import IQ header module 31 | currentPath = os.path.dirname(os.path.realpath(__file__)) 32 | rootPath = os.path.dirname(currentPath) 33 | sys.path.insert(0, os.path.join(rootPath, "_daq_core")) 34 | from iq_header import IQHeader 35 | 36 | class IQRecorder(): 37 | def __init__(self, frame_count = 10000): 38 | """ 39 | Initialzes the IQ Frame receiver module 40 | """ 41 | self.logger = logging.getLogger(__name__) 42 | 43 | self.receiver_connection_status = False 44 | self.port = 5000 45 | self.rec_ip_addr = "127.0.0.1" 46 | self.socket_inst = socket.socket() 47 | 48 | self.receiverBufferSize = 2 ** 18 # Size of the Ethernet receiver buffer measured in bytes 49 | self.channel_number = 4 50 | self.sample_number = 2**18 51 | self.iq_header = IQHeader() 52 | 53 | # Misc parameters 54 | self.first_frame=0 55 | self.max_received_frames = frame_count 56 | self.test_case = "phase_cont" 57 | self.dropped_frames = 0 58 | self.cpi_index_track = 0 59 | 60 | def connect_eth(self): 61 | """ 62 | Establish Ethernet connection to the IQ data interface 63 | 64 | Compatible only with DAQ firmwares that has the IQ streaming mode option. 65 | HeIMDALL-DAQ Firmware version: 0.2 or later 66 | """ 67 | try: 68 | self.socket_inst.connect((self.rec_ip_addr, self.port)) 69 | self.socket_inst.sendall(str.encode('streaming')) 70 | test_iq=self.receive_iq_frame() 71 | self.receiver_connection_status = True 72 | self.logger.info("Connection established") 73 | except: 74 | errorMsg = sys.exc_info()[0] 75 | self.logger.error("Error message: "+str(errorMsg)) 76 | self.receiver_connection_status = False 77 | self.logger.info("Failed to connect to the IQ server") 78 | 79 | 80 | def close_eth(self): 81 | """ 82 | Close the previously establed IQ data interface connection 83 | """ 84 | if self.receiver_connection_status: 85 | self.socket_inst.sendall(str.encode('q')) # Quit from the server 86 | try: 87 | self.socket_inst.close() # close connection 88 | self.receiver_connection_status = False 89 | self.socket_inst = socket.socket() 90 | except: 91 | errorMsg = sys.exc_info()[0] 92 | self.logger.error("Error message: "+str(errorMsg)) 93 | return -1 94 | self.logger.info("Ethernet connection closed") 95 | return 0 96 | 97 | def listen(self): 98 | """ 99 | Starts receiving IQ Frames through the IQ data interface 100 | """ 101 | while not self.receiver_connection_status: 102 | time.sleep(1) 103 | self.connect_eth() 104 | 105 | self.logger.info("Starting IQ acquisition loop") 106 | test_armed = False 107 | exit_flag = False 108 | while not exit_flag: 109 | 110 | # Check Rec control character 111 | try: 112 | self.socket_inst.sendall(str.encode("IQDownload")) # Send iq request command 113 | iq_frame_bytes = self.receive_iq_frame() 114 | if self.iq_header.check_sync_word(): 115 | self.logger.critical("Sync word error") 116 | return -1 117 | 118 | #self.logger.info("IQ Frame received: {:d}".format(self.iq_header.cpi_index)) 119 | 120 | if self.iq_header.delay_sync_flag == 1 and \ 121 | self.iq_header.iq_sync_flag == 1 and \ 122 | self.iq_header.frame_type == 0 and \ 123 | test_armed == False: 124 | 125 | test_armed = True 126 | self.first_frame = self.iq_header.cpi_index 127 | self.cpi_index_track = self.iq_header.cpi_index 128 | self.logger.info("-->Armed<--") 129 | self.logger.info("Start receiving: {:d} frames".format(self.max_received_frames)) 130 | 131 | if test_armed: 132 | 133 | if self.cpi_index_track != self.iq_header.cpi_index: 134 | frame_count_diff = self.iq_header.cpi_index-self.cpi_index_track 135 | self.logger.error("{:d} frame droped, Exp.<->Rec. [{:d}<->{:d}]".format(frame_count_diff,self.cpi_index_track, self.iq_header.cpi_index)) 136 | self.dropped_frames += frame_count_diff 137 | self.cpi_index_track = self.iq_header.cpi_index 138 | 139 | 140 | self.cpi_index_track +=1 141 | if self.first_frame + self.max_received_frames-1 <= self.iq_header.cpi_index: 142 | exit_flag = True 143 | self.logger.info("Total dropped frames: {:d}".format(self.dropped_frames)) 144 | self.logger.info("First received frame: {:d}".format(self.first_frame)) 145 | self.logger.info("Current frame index: {:d}".format(self.iq_header.cpi_index)) 146 | except: 147 | errorMsg = sys.exc_info()[0] 148 | self.logger.error("Error message: "+str(errorMsg)) 149 | self.logger.error("Unable to get new IQ samples, exiting..") 150 | return -1 151 | self.close_eth() 152 | return 0 153 | 154 | def receive_iq_frame(self): 155 | """ 156 | Receives IQ samples over Ethernet connection 157 | """ 158 | total_received_bytes = 0 159 | recv_bytes_count = 0 160 | iq_header_bytes = bytearray(self.iq_header.header_size) # allocate array 161 | view = memoryview(iq_header_bytes) # Get buffer 162 | 163 | self.logger.debug("Starting IQ header reception") 164 | 165 | while total_received_bytes < self.iq_header.header_size: 166 | # Receive into buffer 167 | recv_bytes_count = self.socket_inst.recv_into(view, self.iq_header.header_size-total_received_bytes) 168 | view = view[recv_bytes_count:] # reset memory region 169 | total_received_bytes += recv_bytes_count 170 | 171 | self.iq_header.decode_header(iq_header_bytes) 172 | self.logger.debug("IQ header received and decoded") 173 | 174 | # Calculate total bytes to receive from the iq header data 175 | total_bytes_to_receive = int((self.iq_header.cpi_length * self.iq_header.active_ant_chs * (2*self.iq_header.sample_bit_depth))/8) 176 | receiver_buffer_size = 2**18 177 | 178 | self.logger.debug("Total bytes to receive: {:d}".format(total_bytes_to_receive)) 179 | 180 | total_received_bytes = 0 181 | recv_bytes_count = 0 182 | iq_data_bytes = bytearray(total_bytes_to_receive + receiver_buffer_size) # allocate array 183 | view = memoryview(iq_data_bytes) # Get buffer 184 | 185 | self.logger.debug("Starting IQ reception") 186 | 187 | while total_received_bytes < total_bytes_to_receive: 188 | # Receive into buffer 189 | recv_bytes_count = self.socket_inst.recv_into(view, receiver_buffer_size) 190 | view = view[recv_bytes_count:] # reset memory region 191 | total_received_bytes += recv_bytes_count 192 | 193 | self.logger.debug("Succesfully received") 194 | 195 | # Dump IQ header 196 | #self.iq_header.dump_header() 197 | # Enable here to get access to the received data 198 | #iq_frame_bytes=bytearray()+iq_header_bytes+iq_data_bytes[:total_bytes_to_receive] 199 | return 0#iq_frame_bytes 200 | 201 | if __name__ == "__main__": 202 | logging.basicConfig(level=logging.INFO) 203 | IQ_rec_inst0 = IQRecorder() 204 | IQ_rec_inst0.listen() 205 | 206 | -------------------------------------------------------------------------------- /Firmware/_daq_core/iq_header.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Desctiption: IQ Frame header definition 3 | * For header field description check the corresponding documentation 4 | * Project: HeIMDALL DAQ Firmware 5 | * Author: Tamás Pető 6 | */ 7 | 8 | #include 9 | #include 10 | #include "iq_header.h" 11 | 12 | void dump_iq_header(struct iq_header_struct* iq_header){ 13 | fprintf(stderr, "Sync word: %u\n", iq_header->sync_word); 14 | fprintf(stderr, "Header version: %u\n", iq_header->header_version); 15 | fprintf(stderr, "Frame type ID: %u\n", iq_header->frame_type); 16 | fprintf(stderr, "Hardware ID: %s\n", iq_header->hardware_id); 17 | fprintf(stderr, "Unit ID: %u\n", iq_header->unit_id); 18 | fprintf(stderr, "Active antenna channels: %u\n", iq_header->active_ant_chs); 19 | fprintf(stderr, "IoO type: %u\n", iq_header->ioo_type); 20 | fprintf(stderr, "RF center frequency: %.2f MHz\n", (float) iq_header->rf_center_freq/1000000); 21 | fprintf(stderr, "ADC sampling frequency: %.2f MHz\n", (float) iq_header->adc_sampling_freq/1000000); 22 | fprintf(stderr, "IQ sampling frequency: %.2f MHz\n", (float) iq_header->sampling_freq/1000000); 23 | fprintf(stderr, "CPI length: %u \n", iq_header->cpi_length); 24 | fprintf(stderr, "Unix Epoch timestamp: %"PRIu64"\n", iq_header->time_stamp); 25 | fprintf(stderr, "DAQ block index %u\n", iq_header->daq_block_index); 26 | fprintf(stderr, "CPI index: %u \n", iq_header->cpi_index); 27 | fprintf(stderr, "Extended integration counter: %"PRIu64"\n", iq_header->ext_integration_cntr); 28 | fprintf(stderr, "Data type: %u \n", iq_header->data_type); 29 | fprintf(stderr, "Sample bit depth: %u \n", iq_header->sample_bit_depth); 30 | fprintf(stderr, "ADC overdrive flag: 0x%08X \n", iq_header->adc_overdrive_flags); 31 | for(int m=0;m<32;m++) 32 | { 33 | fprintf(stderr, "Ch: %u IF gain: %u \n",m, iq_header->if_gains[m]); 34 | } 35 | fprintf(stderr, "Delay sync flag: %u \n", iq_header->delay_sync_flag); 36 | fprintf(stderr, "IQ sync flag: %u \n", iq_header->iq_sync_flag); 37 | fprintf(stderr, "Sync state: %u \n", iq_header->sync_state); 38 | fprintf(stderr, "Noise source state: %u \n", iq_header->noise_source_state); 39 | } 40 | 41 | int check_sync_word(struct iq_header_struct* iq_header) 42 | { 43 | if (iq_header->sync_word != SYNC_WORD){return -1;} 44 | else{return 0;} 45 | } -------------------------------------------------------------------------------- /Firmware/_daq_core/iq_header.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Desctiption: IQ Frame header definition 3 | * For header field description check the corresponding documentation 4 | * Project: HeIMDALL DAQ Firmware 5 | * Author: Tamás Pető 6 | */ 7 | 8 | #define __STDC_FORMAT_MACROS 9 | #include 10 | #include 11 | 12 | #define FRAME_TYPE_DATA 0 13 | #define FRAME_TYPE_DUMMY 1 14 | #define FRAME_TYPE_RAMP 2 15 | #define FRAME_TYPE_CAL 3 16 | #define FRAME_TYPE_TRIGW 4 17 | 18 | #define SYNC_WORD 0x2bf7b95a 19 | 20 | #define IQ_HEADER_LENGTH 1024 21 | struct iq_frame_struct 22 | { 23 | struct iq_header_struct* header; 24 | uint8_t* payload; 25 | int payload_size; // Used when the channel buffer is not equal to the CPI size 26 | }; 27 | 28 | struct iq_frame_struct_32 // Complex float 32 compatible 29 | { 30 | struct iq_header_struct* header; 31 | float* payload; //TODO: Modifiy this to CF32 type 32 | int payload_size; // Used when the channel buffer is not equal to the CPI size 33 | }; 34 | 35 | struct iq_header_struct { 36 | uint32_t sync_word; //Updates: RTL-DAQ - Static 37 | uint32_t frame_type; //Updates: RTL-DAQ - Static 38 | char hardware_id [16]; //Updates: RTL-DAQ - Static 39 | uint32_t unit_id; //Updates: RTL-DAQ - Static 40 | uint32_t active_ant_chs; //Updates: RTL-DAQ - Static 41 | uint32_t ioo_type; //Updates: RTL-DAQ - Static 42 | uint64_t rf_center_freq; //Updates: RTL-DAQ - Static 43 | uint64_t adc_sampling_freq; //Updates: RTL-DAQ - Static 44 | uint64_t sampling_freq; //Updates: Decimator - Static 45 | uint32_t cpi_length; //Updates: Rebuffer / Decimator 46 | uint64_t time_stamp; //Updates: RTL-DAQ 47 | uint32_t daq_block_index; //Updates: RTL-DAQ 48 | uint32_t cpi_index; //Updates: Decimator 49 | uint64_t ext_integration_cntr; //Updates: RTL-DAQ 50 | uint32_t data_type; //Updates: Decimator - Static 51 | uint32_t sample_bit_depth; //Updates: RTL-DAQ->Decimator - Static 52 | uint32_t adc_overdrive_flags; //Updates: RTL-DAQ -> Rebuffer 53 | uint32_t if_gains[32]; //Updates: RTL-DAQ 54 | uint32_t delay_sync_flag; //Updates: Delay synchronizer 55 | uint32_t iq_sync_flag; //Updates: Delay synchronizer 56 | uint32_t sync_state; //Updates: Delay synchronizer 57 | uint32_t noise_source_state; //Updates: RTL-DAQ 58 | uint32_t reserved[192]; //Updates: RTL-DAQ - Static 59 | uint32_t header_version; //Updates: RTL-DAQ - Static 60 | }; 61 | void dump_iq_header(struct iq_header_struct* iq_header); 62 | int check_sync_word(struct iq_header_struct* iq_header); 63 | -------------------------------------------------------------------------------- /Firmware/_daq_core/iq_header.py: -------------------------------------------------------------------------------- 1 | from struct import pack,unpack 2 | import logging 3 | import sys 4 | """ 5 | Desctiption: IQ Frame header definition 6 | For header field description check the corresponding documentation 7 | Total length: 1024 byte 8 | Project: HeIMDALL DAQ Firmware 9 | Author: Tamás Pető 10 | """ 11 | class IQHeader(): 12 | 13 | FRAME_TYPE_DATA = 0 14 | FRAME_TYPE_DUMMY = 1 15 | FRAME_TYPE_RAMP = 2 16 | FRAME_TYPE_CAL = 3 17 | FRAME_TYPE_TRIGW = 4 18 | 19 | SYNC_WORD = 0x2bf7b95a 20 | 21 | def __init__(self): 22 | 23 | self.logger = logging.getLogger(__name__) 24 | self.header_size = 1024 # size in bytes 25 | self.reserved_bytes = 192 26 | 27 | self.sync_word=self.SYNC_WORD # uint32_t 28 | self.frame_type=0 # uint32_t 29 | self.hardware_id="" # char [16] 30 | self.unit_id=0 # uint32_t 31 | self.active_ant_chs=0 # uint32_t 32 | self.ioo_type=0 # uint32_t 33 | self.rf_center_freq=0 # uint64_t 34 | self.adc_sampling_freq=0 # uint64_t 35 | self.sampling_freq=0 # uint64_t 36 | self.cpi_length=0 # uint32_t 37 | self.time_stamp=0 # uint64_t 38 | self.daq_block_index=0 # uint32_t 39 | self.cpi_index=0 # uint32_t 40 | self.ext_integration_cntr=0 # uint64_t 41 | self.data_type=0 # uint32_t 42 | self.sample_bit_depth=0 # uint32_t 43 | self.adc_overdrive_flags=0 # uint32_t 44 | self.if_gains=[0]*32 # uint32_t x 32 45 | self.delay_sync_flag=0 # uint32_t 46 | self.iq_sync_flag=0 # uint32_t 47 | self.sync_state=0 # uint32_t 48 | self.noise_source_state=0 # uint32_t 49 | self.reserved=[0]*self.reserved_bytes# uint32_t x reserverd_bytes 50 | self.header_version=0 # uint32_t 51 | 52 | def decode_header(self, iq_header_byte_array): 53 | """ 54 | Unpack,decode and store the content of the iq header 55 | """ 56 | iq_header_list = unpack("II16sIIIQQQIQIIQIII"+"I"*32+"IIII"+"I"*self.reserved_bytes+"I", iq_header_byte_array) 57 | 58 | self.sync_word = iq_header_list[0] 59 | self.frame_type = iq_header_list[1] 60 | self.hardware_id = iq_header_list[2].decode() 61 | self.unit_id = iq_header_list[3] 62 | self.active_ant_chs = iq_header_list[4] 63 | self.ioo_type = iq_header_list[5] 64 | self.rf_center_freq = iq_header_list[6] 65 | self.adc_sampling_freq = iq_header_list[7] 66 | self.sampling_freq = iq_header_list[8] 67 | self.cpi_length = iq_header_list[9] 68 | self.time_stamp = iq_header_list[10] 69 | self.daq_block_index = iq_header_list[11] 70 | self.cpi_index = iq_header_list[12] 71 | self.ext_integration_cntr = iq_header_list[13] 72 | self.data_type = iq_header_list[14] 73 | self.sample_bit_depth = iq_header_list[15] 74 | self.adc_overdrive_flags = iq_header_list[16] 75 | self.if_gains = iq_header_list[17:49] 76 | self.delay_sync_flag = iq_header_list[49] 77 | self.iq_sync_flag = iq_header_list[50] 78 | self.sync_state = iq_header_list[51] 79 | self.noise_source_state = iq_header_list[52] 80 | self.header_version = iq_header_list[52+self.reserved_bytes+1] 81 | 82 | def encode_header(self): 83 | """ 84 | Pack the iq header information into a byte array 85 | """ 86 | iq_header_byte_array=pack("II", self.sync_word, self.frame_type) 87 | iq_header_byte_array+=self.hardware_id.encode()+bytearray(16-len(self.hardware_id.encode())) 88 | iq_header_byte_array+=pack("IIIQQQIQIIQIII", 89 | self.unit_id, self.active_ant_chs, self.ioo_type, self.rf_center_freq, self.adc_sampling_freq, 90 | self.sampling_freq, self.cpi_length, self.time_stamp, self.daq_block_index, self.cpi_index, 91 | self.ext_integration_cntr, self.data_type, self.sample_bit_depth, self.adc_overdrive_flags) 92 | for m in range(32): 93 | iq_header_byte_array+=pack("I", self.if_gains[m]) 94 | 95 | iq_header_byte_array+=pack("I", self.delay_sync_flag) 96 | iq_header_byte_array+=pack("I", self.iq_sync_flag) 97 | iq_header_byte_array+=pack("I", self.sync_state) 98 | iq_header_byte_array+=pack("I", self.noise_source_state) 99 | 100 | for m in range(self.reserved_bytes): 101 | iq_header_byte_array+=pack("I",0) 102 | 103 | iq_header_byte_array+=pack("I", self.header_version) 104 | return iq_header_byte_array 105 | 106 | def dump_header(self): 107 | """ 108 | Prints out the content of the header in human readable format 109 | """ 110 | self.logger.info("Sync word: {:d}".format(self.sync_word)) 111 | self.logger.info("Header version: {:d}".format(self.header_version)) 112 | self.logger.info("Frame type: {:d}".format(self.frame_type)) 113 | self.logger.info("Hardware ID: {:16}".format(self.hardware_id)) 114 | self.logger.info("Unit ID: {:d}".format(self.unit_id)) 115 | self.logger.info("Active antenna channels: {:d}".format(self.active_ant_chs)) 116 | self.logger.info("Illuminator type: {:d}".format(self.ioo_type)) 117 | self.logger.info("RF center frequency: {:.2f} MHz".format(self.rf_center_freq/10**6)) 118 | self.logger.info("ADC sampling frequency: {:.2f} MHz".format(self.adc_sampling_freq/10**6)) 119 | self.logger.info("IQ sampling frequency {:.2f} MHz".format(self.sampling_freq/10**6)) 120 | self.logger.info("CPI length: {:d}".format(self.cpi_length)) 121 | self.logger.info("Unix Epoch timestamp: {:d}".format(self.time_stamp)) 122 | self.logger.info("DAQ block index: {:d}".format(self.daq_block_index)) 123 | self.logger.info("CPI index: {:d}".format(self.cpi_index)) 124 | self.logger.info("Extended integration counter {:d}".format(self.ext_integration_cntr)) 125 | self.logger.info("Data type: {:d}".format(self.data_type)) 126 | self.logger.info("Sample bit depth: {:d}".format(self.sample_bit_depth)) 127 | self.logger.info("ADC overdrive flags: {:d}".format(self.adc_overdrive_flags)) 128 | for m in range(32): 129 | self.logger.info("Ch: {:d} IF gain: {:.1f} dB".format(m, self.if_gains[m]/10)) 130 | self.logger.info("Delay sync flag: {:d}".format(self.delay_sync_flag)) 131 | self.logger.info("IQ sync flag: {:d}".format(self.iq_sync_flag)) 132 | self.logger.info("Sync state: {:d}".format(self.sync_state)) 133 | self.logger.info("Noise source state: {:d}".format(self.noise_source_state)) 134 | 135 | def check_sync_word(self): 136 | """ 137 | Check the sync word of the header 138 | """ 139 | if self.sync_word != self.SYNC_WORD: 140 | return -1 141 | else: 142 | return 0 143 | -------------------------------------------------------------------------------- /Firmware/_daq_core/iq_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Description : 4 | * IQ frame Ethernet server 5 | * 6 | * 7 | * Project : HeIMDALL DAQ Firmware 8 | * License : GNU GPL V3 9 | * Author : Tamas Peto 10 | * 11 | * Copyright (C) 2018-2020 Tamás Pető 12 | * 13 | * This program is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation, either version 3 of the License, or 16 | * any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License 24 | * along with this program. If not, see . 25 | * 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "eth_server.h" 35 | #include "ini.h" 36 | #include "log.h" 37 | #include "sh_mem_util.h" 38 | #include "iq_header.h" 39 | #include "rtl_daq.h" 40 | #define INI_FNAME "daq_chain_config.ini" 41 | 42 | #define FATAL_ERR(l) log_fatal(l); return -1; 43 | 44 | /* 45 | * This structure stores the configuration parameters, 46 | * that are loaded from the ini file 47 | */ 48 | typedef struct 49 | { 50 | int num_ch; 51 | int cpi_size; 52 | int log_level; 53 | } configuration; 54 | 55 | /* 56 | * Ini configuration parser callback function 57 | */ 58 | static int handler(void* conf_struct, const char* section, const char* name, 59 | const char* value) 60 | 61 | { 62 | configuration* pconfig = (configuration*) conf_struct; 63 | 64 | #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 65 | if (MATCH("hw", "num_ch")) 66 | { 67 | pconfig->num_ch = atoi(value); 68 | } 69 | else if (MATCH("pre_processing", "cpi_size")) 70 | { 71 | pconfig->cpi_size = atoi(value); 72 | } 73 | else if (MATCH("daq", "log_level")) 74 | { 75 | pconfig->log_level = atoi(value); 76 | } 77 | else { 78 | return 0; /* unknown section/name, error */ 79 | } 80 | return 0; 81 | } 82 | 83 | int send_iq_frame(struct iq_frame_struct_32* iq_frame, int socket) 84 | { 85 | int transfer_size =iq_frame->payload_size*sizeof(*iq_frame->payload)*2+IQ_HEADER_LENGTH; 86 | 87 | // Sending header 88 | int size = send(socket, iq_frame->header, sizeof(struct iq_header_struct), 0); 89 | // Sending payload 90 | if (iq_frame->payload_size !=0){ 91 | size += send(socket, iq_frame->payload, transfer_size-IQ_HEADER_LENGTH, 0);} 92 | // Check transfer 93 | if(size != transfer_size){log_error("Ethernet transfer failed"); return -1;} 94 | //usleep(50000); // In some cases it is required to fully finish the sending from OS buffers 95 | return 0; 96 | } 97 | 98 | int main(int argc, char* argv[]) 99 | { 100 | log_set_level(LOG_TRACE); 101 | configuration config; 102 | int ret = 0; 103 | int active_buff_ind; 104 | char eth_cmd[1024]; // Ethernet command buffer 105 | 106 | /* Set parameters from the config file*/ 107 | if (ini_parse(INI_FNAME, handler, &config) < 0) 108 | {FATAL_ERR("Configuration could not be loaded, exiting ..")} 109 | 110 | log_set_level(config.log_level); 111 | struct iq_frame_struct_32* iq_frame =calloc(1, sizeof(struct iq_frame_struct_32)); 112 | 113 | /* Initializing input shared memory interface */ 114 | struct shmem_transfer_struct* input_sm_buff = calloc(1, sizeof(struct shmem_transfer_struct)); 115 | input_sm_buff->shared_memory_size = MAX_IQFRAME_PAYLOAD_SIZE*config.num_ch*4*2+IQ_HEADER_LENGTH; 116 | input_sm_buff->io_type = 1; // Input type 117 | strcpy(input_sm_buff->shared_memory_names[0], DELAY_SYNC_IQ_SM_NAME_A); 118 | strcpy(input_sm_buff->shared_memory_names[1], DELAY_SYNC_IQ_SM_NAME_B); 119 | strcpy(input_sm_buff->fw_ctr_fifo_name, DELAY_SYNC_IQ_FW_FIFO); 120 | strcpy(input_sm_buff->bw_ctr_fifo_name, DELAY_SYNC_IQ_BW_FIFO); 121 | 122 | ret= init_in_sm_buffer(input_sm_buff); 123 | if (ret !=0) {FATAL_ERR("Failed to init shared memory interface")} 124 | else{log_info("Shared memory interface succesfully initialized");} 125 | 126 | /* Starting IQ ethernet server */ 127 | int run_server=1; 128 | while(run_server) 129 | { 130 | 131 | /* This function blocks until a client connects to the server */ 132 | int * sockets = malloc(2*sizeof(int)); //[server, client] 133 | iq_stream_con(sockets); 134 | // TODO: Check and handle success 135 | 136 | int exit_flag =0; 137 | while(!exit_flag) 138 | { 139 | // Acquire data buffer on the shared memory interface 140 | active_buff_ind = wait_buff_ready(input_sm_buff); 141 | if (active_buff_ind < 0){exit_flag = active_buff_ind; break;} 142 | iq_frame->header = (struct iq_header_struct*) input_sm_buff->shm_ptr[active_buff_ind]; 143 | iq_frame->payload = ((float *) input_sm_buff->shm_ptr[active_buff_ind] )+ IQ_HEADER_LENGTH/sizeof(float); 144 | CHK_SYNC_WORD(check_sync_word(iq_frame->header)); 145 | iq_frame->payload_size=iq_frame->header->cpi_length * iq_frame->header->active_ant_chs; 146 | //dump_iq_header(iq_frame->header); 147 | 148 | ret=send_iq_frame(iq_frame, sockets[1]); 149 | send_ctr_buff_free(input_sm_buff, active_buff_ind); 150 | if(ret !=0){log_error("Closing connection"); break;} 151 | 152 | /* Waiting for further download commands on the Ethernet link*/ 153 | int bytes_recieved = recv(sockets[1],eth_cmd,1024,0); 154 | eth_cmd[bytes_recieved] = '\0'; 155 | if (strcmp(eth_cmd, "IQDownload") !=0){exit_flag=1;} 156 | } 157 | iq_stream_close(sockets); 158 | } 159 | destory_sm_buffer(input_sm_buff); 160 | log_info("DAQ chain IQ server has exited."); 161 | } 162 | -------------------------------------------------------------------------------- /Firmware/_daq_core/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "log.h" 30 | 31 | static struct { 32 | void *udata; 33 | log_LockFn lock; 34 | FILE *fp; 35 | int level; 36 | int quiet; 37 | } L; 38 | 39 | 40 | static const char *level_names[] = { 41 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 42 | }; 43 | 44 | #ifdef LOG_USE_COLOR 45 | static const char *level_colors[] = { 46 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 47 | }; 48 | #endif 49 | 50 | 51 | static void lock(void) { 52 | if (L.lock) { 53 | L.lock(L.udata, 1); 54 | } 55 | } 56 | 57 | 58 | static void unlock(void) { 59 | if (L.lock) { 60 | L.lock(L.udata, 0); 61 | } 62 | } 63 | 64 | 65 | void log_set_udata(void *udata) { 66 | L.udata = udata; 67 | } 68 | 69 | 70 | void log_set_lock(log_LockFn fn) { 71 | L.lock = fn; 72 | } 73 | 74 | 75 | void log_set_fp(FILE *fp) { 76 | L.fp = fp; 77 | } 78 | 79 | 80 | void log_set_level(int level) { 81 | L.level = level; 82 | } 83 | 84 | 85 | void log_set_quiet(int enable) { 86 | L.quiet = enable ? 1 : 0; 87 | } 88 | 89 | 90 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 91 | if (level < L.level) { 92 | return; 93 | } 94 | 95 | /* Acquire lock */ 96 | lock(); 97 | 98 | /* Get current time */ 99 | time_t t = time(NULL); 100 | struct tm *lt = localtime(&t); 101 | 102 | /* Log to stderr */ 103 | if (!L.quiet) { 104 | va_list args; 105 | char buf[16]; 106 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; 107 | #ifdef LOG_USE_COLOR 108 | fprintf( 109 | stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 110 | buf, level_colors[level], level_names[level], file, line); 111 | #else 112 | fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); 113 | #endif 114 | va_start(args, fmt); 115 | vfprintf(stderr, fmt, args); 116 | va_end(args); 117 | fprintf(stderr, "\n"); 118 | fflush(stderr); 119 | } 120 | 121 | /* Log to file */ 122 | if (L.fp) { 123 | va_list args; 124 | char buf[32]; 125 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; 126 | fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); 127 | va_start(args, fmt); 128 | vfprintf(L.fp, fmt, args); 129 | va_end(args); 130 | fprintf(L.fp, "\n"); 131 | fflush(L.fp); 132 | } 133 | 134 | /* Release lock */ 135 | unlock(); 136 | } 137 | -------------------------------------------------------------------------------- /Firmware/_daq_core/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | 14 | #define LOG_VERSION "0.1.0" 15 | 16 | typedef void (*log_LockFn)(void *udata, int lock); 17 | 18 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 19 | 20 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 21 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 22 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 23 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 24 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 25 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 26 | 27 | void log_set_udata(void *udata); 28 | void log_set_lock(log_LockFn fn); 29 | void log_set_fp(FILE *fp); 30 | void log_set_level(int level); 31 | void log_set_quiet(int enable); 32 | 33 | void log_log(int level, const char *file, int line, const char *fmt, ...); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /Firmware/_daq_core/rtl_daq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Description : 4 | * Various descriptor structures for the DAQ chain 5 | * 6 | * Project : HeIMDALL DAQ Firmware 7 | * License : GNU GPL V3 8 | * Author : Tamas Peto 9 | * 10 | * Copyright (C) 2018-2020 Tamás Pető 11 | * 12 | * This program is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program. If not, see . 24 | * 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include "log.h" 31 | 32 | 33 | #define ERR_IQFRAME_WRITE 10 34 | #define ERR_IQFRAME_READ 11 35 | #define ERR_CMD_READ 12 36 | #define ERR_IQFRAME_SYC_WORD 13 37 | #define ERR_DATA_PIPE_CLOSE 14 38 | #define ERR_CTR_THREAD_READ 15 39 | 40 | #define CHK_SYNC_WORD(r) if(r != 0) {exit_flag = ERR_IQFRAME_SYC_WORD; break;} 41 | #define CHK_FR_WRITE(r, e) if(r != e) {exit_flag = ERR_IQFRAME_WRITE; break;} 42 | #define CHK_FR_READ(r, e) if(r != e) {exit_flag = ERR_IQFRAME_READ; break;} 43 | #define CHK_CMD_READ(r, e) if(r != e) {exit_flag = ERR_CMD_READ; break;} 44 | #define CHK_DATA_PIPE(fd) if(feof(fd)) {exit_flag = ERR_DATA_PIPE_CLOSE; break;} 45 | #define CHK_CTR_READ(r, e) if(r != e) {exit_flag = ERR_CTR_THREAD_READ;} 46 | 47 | 48 | #define MAX_IQFRAME_PAYLOAD_SIZE 8388608 // 2^23[sample] per channel 49 | //Should be greather than the cpi_size in the daq_chain_config.ini 50 | void error_code_log(int exit_flag) 51 | /* 52 | * Dump out error codes 53 | * 54 | */ 55 | { 56 | switch (exit_flag) 57 | { 58 | case ERR_IQFRAME_WRITE: 59 | log_error("IQ frame sending failed"); 60 | break; 61 | case ERR_IQFRAME_READ: 62 | log_fatal("IQ header read error "); 63 | break; 64 | case ERR_IQFRAME_SYC_WORD: 65 | log_fatal("IQ frame sync word check failed"); 66 | break; 67 | case ERR_DATA_PIPE_CLOSE: 68 | log_fatal("Unexpected data pipe close"); 69 | break; 70 | case ERR_CMD_READ: 71 | log_fatal("Command read error"); 72 | break; 73 | default: 74 | break; 75 | } 76 | 77 | } 78 | 79 | // HeIMDALL DAQ inter-modul message structure 80 | struct hdaq_im_msg_struct { 81 | // Total length: 128 byte 82 | uint8_t source_module_identifier; 83 | char command_identifier; 84 | uint8_t parameters[126]; 85 | }; 86 | 87 | struct rtl_rec_struct { 88 | int dev_ind, gain, agc; 89 | rtlsdr_dev_t *dev; 90 | uint8_t *buffer; 91 | unsigned long long buff_ind; 92 | pthread_t async_read_thread; 93 | uint32_t center_freq, sample_rate; 94 | }; 95 | struct sync_buffer_struct { // Each channel has a circular buffer struct 96 | uint32_t delay; 97 | uint8_t *circ_buffer; // Circular buffer 98 | }; 99 | 100 | struct circ_buffer_struct { 101 | uint8_t *iq_circ_buffer; // Stores 8 bit int values (Complex int 8 format) 102 | }; 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /Firmware/_daq_core/serial_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krakenrf/heimdall_daq_fw/1efc252e24501a6bd091699c4f22f140ee71d901/Firmware/_daq_core/serial_test -------------------------------------------------------------------------------- /Firmware/_daq_core/serial_test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "log.h" 11 | #include "rtl-sdr.h" 12 | #include "rtl_daq.h" 13 | 14 | int main( int argc, char** argv ) 15 | { 16 | log_set_level(LOG_TRACE); 17 | 18 | 19 | log_info("Starting serial test"); 20 | // Get device index by serial number 21 | char dev_serial[16]; 22 | int i = 0; 23 | sprintf(dev_serial, "%d", 1000+i); 24 | int dev_index = rtlsdr_get_index_by_serial(dev_serial); 25 | log_info("Device serial:%s, index: %d",dev_serial, dev_index); 26 | if(dev_index==-3){log_fatal("Device with the requested serial number is not available");} 27 | 28 | // Open device and get serial number 29 | rtlsdr_dev_t *dev = NULL; 30 | if (rtlsdr_open(&dev, 0) !=0) 31 | { 32 | log_fatal("Failed to open RTL-SDR device: %s", strerror(errno)); 33 | return -1; 34 | } 35 | 36 | char manufact[128]; 37 | char product[128]; 38 | char serial[128]; 39 | rtlsdr_get_usb_strings(dev,manufact,product,serial); 40 | log_info("Serial number of the currently active device is: %s", serial); 41 | 42 | rtlsdr_close(dev); 43 | log_info("All the resources are free now"); 44 | 45 | return 0; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Firmware/_daq_core/sh_mem_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Description : 4 | * Util functions to handle shared memory based IQ frame transfer between processing blocks 5 | * 6 | * Project : HeIMDALL DAQ Firmware 7 | * License : GNU GPL V3 8 | * Author : Tamas Peto 9 | * 10 | * Copyright (C) 2018-2020 Tamás Pető 11 | * 12 | * This program is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program. If not, see . 24 | * 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "sh_mem_util.h" 36 | #include "log.h" 37 | 38 | #define CHK_SUCC(r, e) if(r != 0) {return e;} 39 | #define CHK_ZERO(r, e) if(r == 0) {return e;} 40 | #define CHK_READ(r, s ,e) if(r != s) {return e;} 41 | #define CHK_DATA_PIPE(fd, e) if(feof(fd)) {return e;} 42 | 43 | unsigned char char_init_ready[1]={INIT_READY}; 44 | unsigned char char_a_buff_ready[1]={A_BUFF_READY}; 45 | unsigned char char_b_buff_ready[1]={B_BUFF_READY}; 46 | unsigned char char_terminate[1]={TERMINATE}; 47 | 48 | uint8_t signal; 49 | 50 | void send_ctr_init_ready(struct shmem_transfer_struct* sm_buff) 51 | { 52 | fwrite(char_init_ready,1,1,sm_buff->fw_ctr_fifo); 53 | fflush(sm_buff->fw_ctr_fifo); 54 | } 55 | void send_ctr_terminate(struct shmem_transfer_struct* sm_buff) 56 | { 57 | fwrite(char_terminate,1,1,sm_buff->fw_ctr_fifo); 58 | fflush(sm_buff->fw_ctr_fifo); 59 | } 60 | void send_ctr_buff_ready(struct shmem_transfer_struct* sm_buff, int active_buff_index) 61 | { 62 | sm_buff->buffer_free[active_buff_index] = false; 63 | if (active_buff_index == 0){fwrite(char_a_buff_ready,1,1,sm_buff->fw_ctr_fifo);} 64 | else if (active_buff_index == 1){fwrite(char_b_buff_ready,1,1,sm_buff->fw_ctr_fifo);} 65 | fflush(sm_buff->fw_ctr_fifo); 66 | 67 | } 68 | void send_ctr_buff_free(struct shmem_transfer_struct* sm_buff, int active_buff_index) 69 | { 70 | if (active_buff_index == 0){fwrite(char_a_buff_ready,1,1,sm_buff->bw_ctr_fifo);} 71 | else if (active_buff_index == 1){fwrite(char_b_buff_ready,1,1,sm_buff->bw_ctr_fifo);} 72 | fflush(sm_buff->bw_ctr_fifo); 73 | } 74 | 75 | int wait_ctr_init_ready(struct shmem_transfer_struct* sm_buff) 76 | { 77 | int read_size=fread(&signal, sizeof(signal), 1, sm_buff->fw_ctr_fifo); 78 | CHK_READ(read_size, 1 ,-1) 79 | if(signal == INIT_READY) {return 0;} 80 | else {return -2;} 81 | } 82 | int wait_buff_free(struct shmem_transfer_struct* sm_buff) 83 | { 84 | if (sm_buff->buffer_free[0] == true) 85 | return 0; 86 | else if (sm_buff->buffer_free[1] == true) 87 | return 1; 88 | else 89 | { 90 | int read_size=fread(&signal, sizeof(signal), 1, sm_buff->bw_ctr_fifo); 91 | switch (read_size) 92 | { 93 | case 0: // PIPE empty and errono set EAGAIN 94 | if (errno == EAGAIN) 95 | { 96 | sm_buff->dropped_frame_cntr +=1; 97 | if (INGORE_FRAME_DROP_WARNINGS==0) 98 | log_warn("Dropping frame.. Total: [%d]",sm_buff->dropped_frame_cntr); 99 | return 3; 100 | } 101 | else 102 | { 103 | log_error("Backward control FIFO read error"); 104 | return -1; 105 | } 106 | break; 107 | case EOF: // EOF, Pipe closed 108 | log_error("Unhandled bw fifo close"); 109 | return -1; 110 | break; 111 | case 1: 112 | if(signal == A_BUFF_READY) 113 | { 114 | sm_buff->buffer_free[0] = true; 115 | return 0; 116 | } 117 | else if(signal == B_BUFF_READY) 118 | { 119 | sm_buff->buffer_free[1] = true; 120 | return 1; 121 | } 122 | else 123 | { 124 | log_error("Unidentified control signal: %d", signal); 125 | return -1; 126 | } 127 | break; 128 | } 129 | } 130 | return -2; 131 | } 132 | int wait_buff_ready(struct shmem_transfer_struct* sm_buff) 133 | { 134 | uint8_t signal; 135 | CHK_DATA_PIPE(sm_buff->fw_ctr_fifo, -1); 136 | int read_size=fread(&signal, sizeof(signal), 1, sm_buff->fw_ctr_fifo); 137 | CHK_READ(read_size, 1 ,-1) 138 | if(signal == A_BUFF_READY){return 0;} 139 | else if(signal == B_BUFF_READY){return 1;} 140 | else if (signal == TERMINATE){return TERMINATE;} 141 | 142 | return -2; 143 | } 144 | 145 | int init_out_sm_buffer(struct shmem_transfer_struct* sm_buff) 146 | { 147 | /* Create the shared memory object */ 148 | sm_buff->shm_fd[0] = shm_open(sm_buff->shared_memory_names[0], O_CREAT | O_RDWR, 0666); 149 | CHK_ZERO(sm_buff->shm_fd[0], -1) 150 | sm_buff->shm_fd[1] = shm_open(sm_buff->shared_memory_names[1], O_CREAT | O_RDWR, 0666); 151 | CHK_ZERO(sm_buff->shm_fd[1], -1) 152 | 153 | /* Configure the size of the shared memory object */ 154 | int ret = ftruncate(sm_buff->shm_fd[0], sm_buff->shared_memory_size); 155 | CHK_SUCC(ret, -2) 156 | ret = ftruncate(sm_buff->shm_fd[1], sm_buff->shared_memory_size); 157 | CHK_SUCC(ret, -2) 158 | 159 | /* Memory map the shared memory object */ 160 | sm_buff->shm_ptr[0] = mmap(0, sm_buff->shared_memory_size, PROT_WRITE, MAP_SHARED, sm_buff->shm_fd[0], 0); 161 | CHK_ZERO(sm_buff->shm_ptr[0], -3) 162 | sm_buff->shm_ptr[1] = mmap(0, sm_buff->shared_memory_size, PROT_WRITE, MAP_SHARED, sm_buff->shm_fd[1], 0); 163 | CHK_ZERO(sm_buff->shm_ptr[1], -3) 164 | 165 | /* Open forward control FIFO*/ 166 | sm_buff->fw_ctr_fifo = fopen(sm_buff->fw_ctr_fifo_name, "w"); 167 | CHK_ZERO(sm_buff->fw_ctr_fifo, -4); 168 | 169 | /* Open backward control FIFO*/ 170 | sm_buff->bw_ctr_fifo= fopen(sm_buff->bw_ctr_fifo_name, "r"); 171 | if (sm_buff->drop_mode) 172 | { 173 | int ret= fcntl(fileno(sm_buff->bw_ctr_fifo), F_SETFL, fcntl(fileno(sm_buff->bw_ctr_fifo), F_GETFL) | O_NONBLOCK); 174 | CHK_SUCC(ret, -4); 175 | } 176 | 177 | CHK_ZERO(sm_buff->bw_ctr_fifo, -5); 178 | 179 | sm_buff->buffer_free[0] = true; 180 | sm_buff->buffer_free[1] = true; 181 | 182 | sm_buff->dropped_frame_cntr = 0; 183 | 184 | send_ctr_init_ready(sm_buff); 185 | 186 | return 0; 187 | } 188 | int init_in_sm_buffer(struct shmem_transfer_struct* sm_buff) 189 | { 190 | /* Open forward control FIFO*/ 191 | sm_buff->fw_ctr_fifo = fopen(sm_buff->fw_ctr_fifo_name, "r"); 192 | CHK_ZERO(sm_buff->fw_ctr_fifo, -1) 193 | 194 | /* Open backward control FIFO*/ 195 | sm_buff->bw_ctr_fifo= fopen(sm_buff->bw_ctr_fifo_name, "w"); 196 | CHK_ZERO(sm_buff->bw_ctr_fifo, -2) 197 | 198 | /* Check init ready success on the generator side*/ 199 | int ret = wait_ctr_init_ready(sm_buff); 200 | CHK_SUCC(ret, -3) 201 | 202 | /* Create the shared memory object */ 203 | sm_buff->shm_fd[0] = shm_open(sm_buff->shared_memory_names[0], O_RDWR, 0666); 204 | CHK_ZERO(sm_buff->shm_fd[0], -4) 205 | sm_buff->shm_fd[1] = shm_open(sm_buff->shared_memory_names[1], O_RDWR, 0666); 206 | CHK_ZERO(sm_buff->shm_fd[1], -4) 207 | 208 | /* Memory map the shared memory object */ 209 | sm_buff->shm_ptr[0] = mmap(0, sm_buff->shared_memory_size, PROT_READ, MAP_SHARED, sm_buff->shm_fd[0], 0); 210 | CHK_ZERO(sm_buff->shm_ptr[0], -5) 211 | sm_buff->shm_ptr[1] = mmap(0, sm_buff->shared_memory_size, PROT_READ, MAP_SHARED, sm_buff->shm_fd[1], 0); 212 | CHK_ZERO(sm_buff->shm_ptr[1], -5) 213 | 214 | sm_buff->dropped_frame_cntr = 0; 215 | 216 | return 0; 217 | } 218 | 219 | int destory_sm_buffer(struct shmem_transfer_struct* sm_buff) 220 | { 221 | /* Unmap the shared memory object */ 222 | int ret = munmap(sm_buff->shm_ptr[0], sm_buff->shared_memory_size); 223 | CHK_SUCC(ret, -1) 224 | ret = munmap(sm_buff->shm_ptr[1], sm_buff->shared_memory_size); 225 | CHK_SUCC(ret, -1) 226 | 227 | if( sm_buff->io_type == 0) 228 | { 229 | /* Remove shared memory object */ 230 | ret = shm_unlink(sm_buff->shared_memory_names[0]); 231 | CHK_SUCC(ret, -2) 232 | ret = shm_unlink(sm_buff->shared_memory_names[1]); 233 | CHK_SUCC(ret, -2) 234 | } 235 | 236 | /* Close forward control FIFO*/ 237 | fclose(sm_buff->fw_ctr_fifo); 238 | 239 | /* Close backward control FIFO*/ 240 | fclose(sm_buff->bw_ctr_fifo); 241 | 242 | return 0; 243 | } 244 | -------------------------------------------------------------------------------- /Firmware/_daq_core/sh_mem_util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ERR_FCNTL -20 5 | 6 | /* 7 | *------------------------------------- 8 | * Control FIFO Parameters 9 | *------------------------------------- 10 | */ 11 | #define INGORE_FRAME_DROP_WARNINGS 1 12 | 13 | #define GEN_FRAME_SM_NAME_A "HEIMDALL_DAQ_FW_GEN_STD_FRAME_A" 14 | #define GEN_FRAME_SM_NAME_B "HEIMDALL_DAQ_FW_GEN_STD_FRAME_B" 15 | #define GEN_FRAME_FW_FIFO "_data_control/fw_ctr_gen_frame" 16 | #define GEN_FRAME_BW_FIFO "_data_control/bw_ctr_gen_frame" 17 | 18 | #define DECIMATOR_IN_SM_NAME_A "decimator_in_A" 19 | #define DECIMATOR_IN_SM_NAME_B "decimator_in_B" 20 | #define DECIMATOR_IN_FW_FIFO "_data_control/fw_decimator_in" 21 | #define DECIMATOR_IN_BW_FIFO "_data_control/bw_decimator_in" 22 | 23 | #define DECIMATOR_OUT_SM_NAME_A "decimator_out_A" 24 | #define DECIMATOR_OUT_SM_NAME_B "decimator_out_B" 25 | #define DECIMATOR_OUT_FW_FIFO "_data_control/fw_decimator_out" 26 | #define DECIMATOR_OUT_BW_FIFO "_data_control/bw_decimator_out" 27 | 28 | #define DELAY_SYNC_IQ_SM_NAME_A "delay_sync_iq_A" 29 | #define DELAY_SYNC_IQ_SM_NAME_B "delay_sync_iq_B" 30 | #define DELAY_SYNC_IQ_FW_FIFO "_data_control/fw_delay_sync_iq" 31 | #define DELAY_SYNC_IQ_BW_FIFO "_data_control/bw_delay_sync_iq" 32 | 33 | //const unsigned char fw_cmd_init_ready[1] = 0x0A; 34 | #define INIT_READY 10 35 | #define A_BUFF_READY 1 36 | #define B_BUFF_READY 2 37 | #define TERMINATE 255 38 | 39 | /* 40 | *------------------------------------- 41 | * Shared memory transfer 42 | *------------------------------------- 43 | */ 44 | 45 | struct shmem_transfer_struct { 46 | char shared_memory_names[2][512]; 47 | char fw_ctr_fifo_name[512]; 48 | char bw_ctr_fifo_name[512]; 49 | size_t shared_memory_size; 50 | bool buffer_free[2]; 51 | bool io_type; // 0-Output, 1-Input 52 | bool drop_mode; // If enabled, frames are dropped 53 | int dropped_frame_cntr; 54 | FILE* fw_ctr_fifo; 55 | FILE* bw_ctr_fifo; 56 | void* shm_ptr[2]; 57 | int shm_fd[2]; 58 | }; 59 | 60 | /* 61 | *------------------------------------- 62 | * Shared memory util functions 63 | *------------------------------------- 64 | */ 65 | 66 | int init_out_sm_buffer(struct shmem_transfer_struct*); 67 | int init_in_sm_buffer(struct shmem_transfer_struct*); 68 | 69 | int destory_sm_buffer(struct shmem_transfer_struct*); 70 | 71 | void send_ctr_init_ready(struct shmem_transfer_struct*); 72 | void send_ctr_buff_ready(struct shmem_transfer_struct*, int); 73 | void send_ctr_buff_free(struct shmem_transfer_struct*, int); 74 | void send_ctr_terminate(struct shmem_transfer_struct*); 75 | 76 | int wait_buff_free(struct shmem_transfer_struct*); 77 | int wait_buff_ready(struct shmem_transfer_struct*); 78 | int wait_ctr_init_read(struct shmem_transfer_struct*); 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Firmware/_daq_core/shmemIface.py: -------------------------------------------------------------------------------- 1 | """ 2 | HeIMDALL DAQ Firmware 3 | Python based shared memory interface implementations 4 | 5 | Author: Tamás Pető 6 | License: GNU GPL V3 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | """ 21 | import logging 22 | from struct import pack, unpack 23 | from multiprocessing import shared_memory 24 | import numpy as np 25 | import os 26 | 27 | A_BUFF_READY = 1 28 | B_BUFF_READY = 2 29 | INIT_READY = 10 30 | TERMINATE = 255 31 | class outShmemIface(): 32 | 33 | 34 | def __init__(self, shmem_name, shmem_size, drop_mode = False): 35 | 36 | self.init_ok = True 37 | self.logger = logging.getLogger(__name__) 38 | self.ignore_frame_drop_warning = True 39 | self.drop_mode = drop_mode 40 | self.dropped_frame_cntr = 0 41 | 42 | self.shmem_name = shmem_name 43 | self.buffer_free = [True, True] 44 | 45 | self.memories = [] 46 | self.buffers = [] 47 | 48 | # Try to remove shared memories if already exist 49 | try: 50 | shmem_A = shared_memory.SharedMemory(name=shmem_name+'_A',create=False, size=shmem_size) 51 | shmem_A.close() 52 | #shmem_A.unlink() 53 | except FileNotFoundError as err: 54 | self.logger.warning("Shared memory not exist") 55 | try: 56 | shmem_B = shared_memory.SharedMemory(name=shmem_name+'_B',create=False, size=shmem_size) 57 | shmem_B.close() 58 | #shmem_B.unlink() 59 | except FileNotFoundError as err: 60 | self.logger.warning("Shared memory not exist") 61 | 62 | # Create the shared memories 63 | self.memories.append(shared_memory.SharedMemory(name=shmem_name+'_A',create=True, size=shmem_size)) 64 | self.memories.append(shared_memory.SharedMemory(name=shmem_name+'_B',create=True, size=shmem_size)) 65 | self.buffers.append(np.ndarray((shmem_size,), dtype=np.uint8, buffer=self.memories[0].buf)) 66 | self.buffers.append(np.ndarray((shmem_size,), dtype=np.uint8, buffer=self.memories[1].buf)) 67 | 68 | # Opening control FIFOs 69 | if self.drop_mode: 70 | bw_fifo_flags = os.O_RDONLY | os.O_NONBLOCK 71 | else: 72 | bw_fifo_flags = os.O_RDONLY 73 | try: 74 | self.fw_ctr_fifo = os.open('_data_control/'+'fw_'+shmem_name, os.O_WRONLY) 75 | self.bw_ctr_fifo = os.open('_data_control/'+'bw_'+shmem_name, bw_fifo_flags) 76 | except OSError as err: 77 | self.logger.critical("OS error: {0}".format(err)) 78 | self.logger.critical("Failed to open control fifos") 79 | self.bw_ctr_fifo = None 80 | self.fw_ctr_fifo = None 81 | self.init_ok = False 82 | 83 | # Send init ready signal 84 | if self.init_ok: 85 | os.write(self.fw_ctr_fifo, pack('B',INIT_READY)) 86 | 87 | def send_ctr_buff_ready(self, active_buffer_index): 88 | # Send buffer ready signal on the forward FIFO 89 | if active_buffer_index == 0: 90 | os.write(self.fw_ctr_fifo, pack('B',A_BUFF_READY)) 91 | elif active_buffer_index == 1: 92 | os.write(self.fw_ctr_fifo, pack('B',B_BUFF_READY)) 93 | 94 | # Deassert buffer free flag 95 | self.buffer_free[active_buffer_index] = False 96 | 97 | def send_ctr_terminate(self): 98 | os.write(self.fw_ctr_fifo, pack('B',TERMINATE)) 99 | self.logger.info("Terminate signal sent") 100 | 101 | def destory_sm_buffer(self): 102 | for memory in self.memories: 103 | memory.close() 104 | #memory.unlink() 105 | 106 | if self.fw_ctr_fifo is not None: 107 | os.close(self.fw_ctr_fifo) 108 | 109 | if self.bw_ctr_fifo is not None: 110 | os.close(self.bw_ctr_fifo) 111 | 112 | def wait_buff_free(self): 113 | if self.buffer_free[0]: 114 | return 0 115 | elif self.buffer_free[1]: 116 | return 1 117 | else: 118 | try: 119 | buffer = os.read(self.bw_ctr_fifo, 1) 120 | signal = unpack('B', buffer )[0] 121 | 122 | if signal == A_BUFF_READY: 123 | self.buffer_free[0] = True 124 | return 0 125 | if signal == B_BUFF_READY: 126 | self.buffer_free[1] = True 127 | return 1 128 | except BlockingIOError as err: 129 | self.dropped_frame_cntr +=1 130 | if not self.ignore_frame_drop_warning: self.logger.warning("Dropping frame.. Total: [{:d}] ".format(self.dropped_frame_cntr)) 131 | return 3 132 | return -1 133 | 134 | 135 | class inShmemIface(): 136 | 137 | def __init__(self, shmem_name): 138 | 139 | self.init_ok = True 140 | self.logger = logging.getLogger(__name__) 141 | self.drop_mode = False 142 | 143 | self.shmem_name = shmem_name 144 | 145 | self.memories = [] 146 | self.buffers = [] 147 | 148 | try: 149 | self.fw_ctr_fifo = os.open('_data_control/'+'fw_'+shmem_name, os.O_RDONLY) 150 | self.bw_ctr_fifo = os.open('_data_control/'+'bw_'+shmem_name, os.O_WRONLY) 151 | except OSError as err: 152 | self.logger.critical("OS error: {0}".format(err)) 153 | self.logger.critical("Failed to open control fifos") 154 | self.bw_ctr_fifo = None 155 | self.fw_ctr_fifo = None 156 | self.init_ok = False 157 | 158 | if self.fw_ctr_fifo is not None: 159 | if unpack('B', os.read(self.fw_ctr_fifo, 1))[0] == INIT_READY: 160 | self.memories.append(shared_memory.SharedMemory(name=shmem_name+'_A')) 161 | self.memories.append(shared_memory.SharedMemory(name=shmem_name+'_B')) 162 | self.buffers.append(np.ndarray((self.memories[0].size,), 163 | dtype=np.uint8, 164 | buffer=self.memories[0].buf)) 165 | self.buffers.append(np.ndarray((self.memories[1].size,), 166 | dtype=np.uint8, 167 | buffer=self.memories[1].buf)) 168 | else: 169 | self.init_ok = False 170 | 171 | def send_ctr_buff_ready(self, active_buffer_index): 172 | if active_buffer_index == 0: 173 | os.write(self.bw_ctr_fifo, pack('B',A_BUFF_READY)) 174 | elif active_buffer_index == 1: 175 | os.write(self.bw_ctr_fifo, pack('B',B_BUFF_READY)) 176 | 177 | def destory_sm_buffer(self): 178 | for memory in self.memories: 179 | memory.close() 180 | 181 | if self.fw_ctr_fifo is not None: 182 | os.close(self.fw_ctr_fifo) 183 | 184 | if self.bw_ctr_fifo is not None: 185 | os.close(self.bw_ctr_fifo) 186 | 187 | def wait_buff_free(self): 188 | signal = unpack('B', os.read(self.fw_ctr_fifo, 1))[0] 189 | if signal == A_BUFF_READY: 190 | return 0 191 | elif signal == B_BUFF_READY: 192 | return 1 193 | elif signal == TERMINATE: 194 | return TERMINATE 195 | return -1 196 | -------------------------------------------------------------------------------- /Firmware/_data_control/iq_track_lock: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /Firmware/_logs/logs: -------------------------------------------------------------------------------- 1 | Realtek data acquistion module : rtl_daq.log 2 | Sample delay synchronizer : sync.log 3 | Sample rebuffer : rebuffer.log 4 | Squelch module : squelch.log 5 | IQ sample decimator : decimator.log 6 | Delay synchronizer module : delay_sync.log 7 | IQ Server : iq_server.log 8 | Hardware controller module : hwc.log 9 | -------------------------------------------------------------------------------- /Firmware/_testing/iq_analyzer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import os 4 | import logging 5 | import sys 6 | # Import IQ header module 7 | currentPath = os.path.dirname(os.path.realpath(__file__)) 8 | rootPath = os.path.dirname(currentPath) 9 | sys.path.insert(0, os.path.join(rootPath, "_daq_core")) 10 | from iq_header import IQHeader 11 | 12 | 13 | logging.basicConfig(level=logging.INFO) 14 | """ 15 | --------------------- 16 | P A R A M S 17 | --------------------- 18 | """ 19 | 20 | file_name = "VEGAM_2.iqf" 21 | file_descr = open(file_name, "rb") 22 | fs = 2.4*10**6 23 | std_ch_ind = 0 24 | 25 | en_td_plot = True 26 | en_fd_plot = False 27 | en_xcorr_plot = False 28 | en_iq_diff_plot = True 29 | 30 | td_plot_ch_ind = 0 31 | xcorr_channels = [0,1] 32 | """ 33 | --------------------- 34 | L O A D 35 | --------------------- 36 | """ 37 | 38 | iq_header_bytes = file_descr.read(1024) 39 | iq_header = IQHeader() 40 | iq_header.decode_header(iq_header_bytes) 41 | iq_header.dump_header() 42 | 43 | iq_data_length = int((iq_header.cpi_length * iq_header.active_ant_chs * (2*iq_header.sample_bit_depth))/8) 44 | iq_data_bytes = file_descr.read(iq_data_length) 45 | 46 | 47 | file_descr.close() 48 | 49 | iq_cf64 = np.frombuffer(iq_data_bytes, dtype=np.complex64).reshape(iq_header.active_ant_chs, iq_header.cpi_length) 50 | iq_cf64 = iq_cf64.copy() 51 | 52 | """ 53 | --------------------- 54 | P L O T S 55 | --------------------- 56 | """ 57 | N = iq_header.cpi_length 58 | M = iq_header.active_ant_chs 59 | 60 | # Remove DC 61 | for m in range(M): 62 | iq_cf64[m,:] -= np.average(iq_cf64[m,:]) 63 | 64 | if en_td_plot: 65 | x = iq_cf64[0,:] 66 | plt.figure(1) 67 | #plt.plot(x.real[0:200]) 68 | #plt.plot(x.imag[0:200]) 69 | plt.plot(iq_cf64[td_plot_ch_ind,0:200].real) 70 | plt.plot(iq_cf64[td_plot_ch_ind,0:200].imag) 71 | 72 | if en_fd_plot: 73 | plt.figure(2) 74 | freqs = np.fft.fftfreq(N, 1/fs) 75 | freqs = np.fft.fftshift(freqs) 76 | freqs /= 10**6 77 | for m in range(1): 78 | xw = np.fft.fft(iq_cf64[m, :]) 79 | xw = np.fft.fftshift(xw) 80 | xw = abs(xw) 81 | xw /= np.max(xw) 82 | plt.plot(freqs, 20*np.log10(xw)) 83 | 84 | if en_xcorr_plot: 85 | plt.figure(3) 86 | 87 | N_proc = 2**16 88 | np_zeros = np.zeros(N_proc, dtype=np.complex64) 89 | x_padd = np.concatenate([iq_cf64[std_ch_ind, 0:N_proc], np_zeros]) 90 | x_fft = np.fft.fft(x_padd) 91 | 92 | time_delay_indices = np.arange(0,2*N_proc)-N_proc 93 | for m in np.arange(1, M,1): 94 | y_padd = np.concatenate([np_zeros, iq_cf64[m, 0:N_proc]]) 95 | y_fft = np.fft.fft(y_padd) 96 | corr_function = np.fft.ifft(x_fft.conj() * y_fft) 97 | corr_function = abs(corr_function) 98 | corr_function_log = 20*np.log10(corr_function) 99 | #corr_function_log -= max(corr_function_log) 100 | 101 | plt.plot(time_delay_indices,corr_function_log) 102 | 103 | plt.xlim([-100, 100]) 104 | #plt.ylim([-60,0]) 105 | plt.xlabel("Time delay [sample]") 106 | plt.ylabel("Amplitude [dB]") 107 | 108 | if en_iq_diff_plot: 109 | plt.figure(4) 110 | xcorr = iq_cf64[xcorr_channels[0],:] * np.conjugate(iq_cf64[xcorr_channels[1],:]) 111 | xcorr /= np.max(np.abs(xcorr)) 112 | plt.scatter(xcorr.real, xcorr.imag) -------------------------------------------------------------------------------- /Firmware/_testing/test_logs/logs: -------------------------------------------------------------------------------- 1 | Unit test log placeholder -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/capture_shmem_stream.py: -------------------------------------------------------------------------------- 1 | """ 2 | HeIMDALL DAQ Firmware 3 | Description : Capture and record an IQ frame stream transfered via a shared memory interface 4 | Author : Tamas Peto 5 | License : GNU GPL V3 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | WARNING: Check the native size of the IQ header on the target device 21 | """ 22 | 23 | import logging 24 | import sys 25 | from os.path import join, dirname, realpath 26 | from struct import pack, unpack 27 | import numpy as np 28 | from configparser import ConfigParser 29 | import time 30 | import threading 31 | 32 | current_path = dirname(realpath(__file__)) 33 | root_path = dirname(dirname(current_path)) 34 | daq_core_path = join(root_path, "_daq_core") 35 | 36 | sys.path.insert(0, daq_core_path) 37 | from iq_header import IQHeader 38 | from shmemIface import inShmemIface 39 | 40 | class IQFrameRecorder(threading.Thread): 41 | 42 | def __init__(self, shmem_name, out_fname, en_write): 43 | threading.Thread.__init__(self) 44 | logging.basicConfig(level=logging.DEBUG) 45 | self.logger = logging.getLogger(__name__) 46 | 47 | self.fw_ctr_gen_frame = None 48 | self.bw_ctr_gen_frame = None 49 | 50 | self.iq_header = IQHeader() 51 | 52 | # Open record output file 53 | self.output_file = open(out_fname,"wb") 54 | 55 | self.en_write = int(en_write) 56 | 57 | self.start_time = 0 58 | self.elapsed_time = 0 59 | # Open shared memory interface to capture the module output 60 | self.in_shmem_iface = inShmemIface(shmem_name) 61 | if not self.in_shmem_iface.init_ok: 62 | self.logger.critical("Shared memory initialization failed") 63 | self.in_shmem_iface.destory_sm_buffer() 64 | 65 | def run(self): 66 | """ 67 | Start the main processing loop 68 | """ 69 | logging.info("Starting IQ frame capture on shared memory iface") 70 | while True: 71 | ############################################# 72 | # OBTAIN NEW DATA BLOCK # 73 | ############################################# 74 | 75 | # Read and convert header 76 | active_buff_index = self.in_shmem_iface.wait_buff_free() 77 | if self.start_time == 0: 78 | self.start_time = time.time() 79 | 80 | if active_buff_index < 0 or active_buff_index > 1: 81 | logging.info("Terminating.., signal: {:d}".format(active_buff_index)) 82 | break; 83 | 84 | buffer = self.in_shmem_iface.buffers[active_buff_index] 85 | 86 | iq_header_bytes = buffer[0:1024].tobytes() 87 | iq_frame_bytes = bytearray()+iq_header_bytes 88 | 89 | self.iq_header.decode_header(iq_header_bytes) 90 | if self.iq_header.check_sync_word(): 91 | logging.error("IQ header sync word check failed, exiting..") 92 | break 93 | #self.iq_header.dump_header() 94 | incoming_payload_size = self.iq_header.cpi_length*self.iq_header.active_ant_chs*2*int(self.iq_header.sample_bit_depth/8) 95 | if incoming_payload_size > 0: 96 | iq_samples = buffer[1024:1024 + incoming_payload_size].view(dtype=np.float32) 97 | iq_frame_bytes += iq_samples.tobytes() 98 | if self.en_write: 99 | self.output_file.write(iq_frame_bytes) 100 | 101 | self.logger.info("IQ frame received. [{:d}]".format(self.iq_header.daq_block_index)) 102 | self.in_shmem_iface.send_ctr_buff_ready(active_buff_index) 103 | #self.logger.info("Buff ready: {:d}".format(active_buff_index)) 104 | 105 | self.elapsed_time = time.time()-self.start_time 106 | self.in_shmem_iface.destory_sm_buffer() 107 | self.output_file.close() 108 | logging.info("Capture module exited") 109 | 110 | 111 | if __name__ == '__main__': 112 | recorder = IQFrameRecorder(sys.argv[1],sys.argv[2],sys.argv[3]) 113 | if recorder.in_shmem_iface.init_ok: 114 | recorder.start() 115 | else: 116 | exit() 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/gen_burst.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : 3 | Signal generator for testing the squelch module 4 | 5 | Project : HeIMDALL DAQ Firmware 6 | License : GNU GPL V3 7 | Author : Tamas Peto 8 | 9 | Command line arguments: 10 | 11 | Copyright (C) 2018-2020 Tamás Pető 12 | 13 | This program is free software: you can redistribute it and/or modify 14 | it under the terms of the GNU General Public License as published by 15 | the Free Software Foundation, either version 3 of the License, or 16 | any later version. 17 | 18 | This program is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU General Public License 24 | along with this program. If not, see . 25 | 26 | """ 27 | import os 28 | import sys 29 | import getopt 30 | import time 31 | import numpy as np 32 | from struct import pack, unpack 33 | import threading 34 | import logging 35 | 36 | # Import external modules 37 | current_path = os.path.dirname(os.path.realpath(__file__)) 38 | root_path = os.path.dirname(os.path.dirname(current_path)) 39 | test_logs_path = os.path.join(root_path, "_testing", "test_logs") 40 | sys.path.insert(0, os.path.join(root_path, "_daq_core")) 41 | from iq_header import IQHeader 42 | from shmemIface import outShmemIface 43 | 44 | from os.path import join 45 | from plotly import graph_objects as go 46 | 47 | class burstFrameGenator(threading.Thread): 48 | 49 | def __init__(self, test_case, shmem_name): 50 | threading.Thread.__init__(self) 51 | self.logger = logging.getLogger(__name__) 52 | 53 | self.init_ok = True 54 | self.M = 4 55 | self.frame_type = IQHeader.FRAME_TYPE_DATA 56 | self.test_case = test_case 57 | self.shmem_name = shmem_name 58 | 59 | if self.test_case == '3_5': 60 | self.blocks = 13 61 | self.pulse_start = 5 62 | self.pulse_end = 7 63 | self.N_cpi = 2**18 64 | elif self.test_case == '3_10': 65 | self.blocks = 5 66 | self.N_cpi = 256 67 | self.peak_1_pos = 100 68 | self.peak_2_pos = 130 69 | self.burst_1_len = 211 70 | self.burst_2_len = 197 71 | else: 72 | self.logger.fatal('Unidentified test case: {:s}'.format(test_case)) 73 | self.init_ok = False 74 | 75 | self.iq_header = IQHeader() 76 | self.iq_header.sync_word = IQHeader.SYNC_WORD 77 | self.iq_header.frame_type = self.frame_type 78 | self.iq_header.active_ant_chs = self.M 79 | self.iq_header.daq_block_index = 0 80 | self.iq_header.cpi_index = 0 81 | self.iq_header.cpi_length = self.N_cpi 82 | self.iq_header.sample_bit_depth = 32 83 | # Uninitialzed header fields are all zero! 84 | 85 | self.logger.info("Frame type: {:d}".format(self.frame_type)) 86 | self.logger.info("Number of frames: {:d}".format(self.blocks)) 87 | self.logger.info("Number of samples per channel: {:d}".format(self.N_cpi)) 88 | self.logger.info('Test case: {:s}'.format(self.test_case)) 89 | self.logger.info("Shared memory interface name: {:s}".format(shmem_name)) 90 | 91 | 92 | def run(self): 93 | self.out_shmem_iface = outShmemIface(self.shmem_name, 94 | int(1024+self.N_cpi*2*self.M*(self.iq_header.sample_bit_depth/8)), 95 | drop_mode = False) 96 | if not self.out_shmem_iface.init_ok: 97 | self.logger.critical("Shared memory initialization failed, exiting..") 98 | self.out_shmem_iface.destory_sm_buffer() 99 | else: 100 | self.logger.info("Shared memory interface succesfully initialized") 101 | 102 | self.logger.info("Starting test frame generation") 103 | 104 | fig = go.Figure() 105 | #################################### 106 | # Simulation 107 | #################################### 108 | 109 | # Allocation 110 | sig_out_ch1 = np.zeros((self.N_cpi*2), dtype=np.uint8) # This array stores the samples that are written to output 111 | sig_out_chm = np.zeros((self.M, self.N_cpi*2), dtype=np.uint8) 112 | time_index = 0 113 | for b in range(self.blocks): 114 | self.logger.info("Writing block: {:d}".format(b)) 115 | self.iq_header.daq_block_index = b 116 | self.iq_header.cpi_index = b 117 | ramp_max = 0 118 | 119 | payload_byte_array = bytearray() 120 | for m in range(self.M): 121 | #------------------ 122 | # Test case 3_5 123 | #------------------ 124 | if self.test_case == '3_5': 125 | if b < self.pulse_start: 126 | raw_sig_m = np.zeros((self.N_cpi), dtype=np.uint8) 127 | # -- > Pulse < -- 128 | elif b >=self.pulse_start and b <= self.pulse_end: 129 | raw_sig_m = np.ones((self.N_cpi), dtype=np.uint8) 130 | # -- > Pulse < -- 131 | elif b > self.pulse_end: 132 | raw_sig_m = np.zeros((self.N_cpi), dtype=np.uint8) 133 | #------------------ 134 | # Test case 3_10 135 | #------------------ 136 | elif self.test_case == '3_10': 137 | raw_sig_m = np.ones(self.N_cpi, dtype=np.uint8)*-0.1 138 | # -- > Block 0 < -- 139 | if b == 1: 140 | ramp_max = 29 141 | if m==0: 142 | raw_sig_m[self.peak_1_pos] = 1 143 | raw_sig_m[self.peak_1_pos+1:self.N_cpi] = np.arange(0, self.N_cpi-self.peak_1_pos-1,1, dtype=np.uint32)%ramp_max 144 | # -- > Block 1 < -- 145 | elif b == 2: 146 | ramp_max = 29 147 | burst_1_remain = self.burst_1_len -(self.N_cpi-self.peak_1_pos-1) 148 | raw_sig_m[0:burst_1_remain] = np.arange(self.burst_1_len-burst_1_remain, self.burst_1_len,1, dtype=np.uint32)%ramp_max 149 | if m==0: 150 | raw_sig_m[self.peak_2_pos] = 1 151 | raw_sig_m[self.peak_2_pos+1:self.N_cpi] = np.arange(0, self.N_cpi-self.peak_2_pos-1,1, dtype=np.uint32)%ramp_max 152 | # -- > Block 2 < -- 153 | elif b == 3: 154 | ramp_max = 29 155 | burst_2_remain = self.burst_2_len - (self.N_cpi-self.peak_2_pos-1) 156 | raw_sig_m[0:burst_2_remain] = np.arange(self.burst_2_len-burst_2_remain, self.burst_2_len,1, dtype=np.uint32)%ramp_max 157 | 158 | mask = (raw_sig_m[:] >= 0).astype(int) 159 | sig_out_ch1[0::2] = raw_sig_m[:] + mask*2*m*ramp_max 160 | sig_out_ch1[1::2] = raw_sig_m[:] + mask*(2*m+1)*ramp_max 161 | sig_out_chm[m,:] = sig_out_ch1[:] 162 | 163 | ####################################### 164 | # SEND DATA FRAME 165 | ####################################### 166 | fig.add_trace(go.Scatter(x=np.arange(self.N_cpi),y=sig_out_chm[0,0::2], name = "Block {:d}".format(b), line=dict(width=2, dash='solid'))) 167 | 168 | 169 | active_buffer_index = self.out_shmem_iface.wait_buff_free() 170 | self.logger.info("Buffer free: {:d}".format(active_buffer_index)) 171 | if active_buffer_index !=3: 172 | # Get the shared memory buffer 173 | iq_frame_buffer_out = (self.out_shmem_iface.buffers[active_buffer_index]).view(dtype=np.uint8) 174 | 175 | # Get the IQ sample array from the buffer 176 | iq_samples_out = (iq_frame_buffer_out[1024:1024 + self.iq_header.cpi_length*2*self.iq_header.sample_bit_depth//8*self.iq_header.active_ant_chs].view(dtype=np.complex64))\ 177 | .reshape(self.iq_header.active_ant_chs, self.iq_header.cpi_length) 178 | 179 | (self.out_shmem_iface.buffers[active_buffer_index])[0:1024] = np.frombuffer(self.iq_header.encode_header(), dtype=np.uint8) 180 | for m in range(self.iq_header.active_ant_chs): 181 | iq_samples_out[m,:].real = sig_out_chm[m,0::2] 182 | iq_samples_out[m,:].imag = sig_out_chm[m,1::2] 183 | self.out_shmem_iface.send_ctr_buff_ready(active_buffer_index) 184 | 185 | self.out_shmem_iface.send_ctr_terminate() 186 | time.sleep(2) 187 | self.out_shmem_iface.destory_sm_buffer() 188 | self.logger.info("Total dropped frames: {:d}".format(self.out_shmem_iface.dropped_frame_cntr)) 189 | self.logger.info("Burst frame generator exited") 190 | 191 | fig.write_html(join(test_logs_path,'TestCase-3_10.html')) 192 | 193 | 194 | if __name__ == "__main__": 195 | test_case = "3_5" 196 | # Parse command line arguments 197 | opts, args = getopt.getopt(sys.argv[1:],"c:") 198 | for opt, arg in opts: 199 | if opt == '-c': 200 | test_case = arg 201 | 202 | generator = burstFrameGenator(test_case) 203 | if generator.init_ok: 204 | generator.start() 205 | 206 | -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/gen_cw.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : 3 | Swept CW signal generator for testing the decimator 4 | 5 | Project : HeIMDALL DAQ Firmware 6 | License : GNU GPL V3 7 | Author : Tamas Peto 8 | 9 | Command line arguments: 10 | -t : Frame type (default:DATA) 11 | -b : Number of frames to send (default:10) 12 | -n : Number of samples per channel in the payload 13 | -r : decimation ratio 14 | -m : Shared memory name (if not specified, data is sent on std out) 15 | 16 | Copyright (C) 2018-2020 Tamás Pető 17 | 18 | This program is free software: you can redistribute it and/or modify 19 | it under the terms of the GNU General Public License as published by 20 | the Free Software Foundation, either version 3 of the License, or 21 | any later version. 22 | 23 | This program is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU General Public License for more details. 27 | 28 | You should have received a copy of the GNU General Public License 29 | along with this program. If not, see . 30 | 31 | """ 32 | import os 33 | import sys 34 | import getopt 35 | import time 36 | import numpy as np 37 | from struct import pack, unpack 38 | import logging 39 | 40 | # Import IQ header module 41 | currentPath = os.path.dirname(os.path.realpath(__file__)) 42 | rootPath = os.path.dirname(os.path.dirname(currentPath)) 43 | sys.path.insert(0, os.path.join(rootPath, "_daq_core")) 44 | from iq_header import IQHeader 45 | from shmemIface import outShmemIface 46 | 47 | #################################### 48 | # PARAMETERS 49 | #################################### 50 | logging.basicConfig(level=logging.INFO) 51 | logger = logging.getLogger(__name__) 52 | 53 | N_daq = 2**18 54 | M = 4 55 | R = 1 56 | blocks = 1000 57 | frame_type = IQHeader.FRAME_TYPE_DATA 58 | shmem_name = "decimator_in" 59 | 60 | # Synchronization related parameters 61 | block_size = N_daq*2 62 | multi_frames = 1 # For continuity testing 63 | 64 | # Generated excitation signal parameters 65 | rf_freq = 100 *10**6 # Simulated center frequency 66 | fs = 2.4 *10**6 # Sampling frequency [Hz] 67 | sig_type = "cw" # "cw" / "swept-cw" 68 | sig_freq = 0.024 * 10**6# Interpreted as the distance from the center freq [Hz] 69 | ps = -3 # Signal power [dB] 70 | pn = - 50 # Uncorrelated noise power [dB] 71 | 72 | # -> For swept-CW source type: 73 | sig_freq_sweep_start = -fs/2 74 | sig_freq_sweep_stop = fs/2 75 | sig_freq_sweep_step = fs/N_daq*2**10 76 | 77 | # Parse command line arguments 78 | opts, args = getopt.getopt(sys.argv[1:],"b:n:r:s:m:") 79 | for opt, arg in opts: 80 | if opt == '-b': 81 | blocks = int(arg) 82 | elif opt == '-n': 83 | N_daq = int(arg) 84 | elif opt == '-r': 85 | R = int(arg) 86 | elif opt == '-s': 87 | sig_type= arg 88 | elif opt == '-m': 89 | shmem_name = arg 90 | 91 | 92 | N_daq *= R 93 | if sig_type=="cw": 94 | multi_frames = blocks 95 | sig_freq_sweep_start = fs/256 96 | if sig_type=="swept-cw": 97 | blocks = int(fs / sig_freq_sweep_step) 98 | 99 | 100 | logging.info("Frame type: {:d}".format(frame_type)) 101 | logging.info("Number of frames: {:d}".format(blocks)) 102 | logging.info("Number of sample per channel: {:d}".format(N_daq)) 103 | logging.info("Decimation ratio: {:d}".format(R)) 104 | logging.info("Signal source type: {:s}".format(sig_type)) 105 | if shmem_name != "": 106 | logging.info("Frames will be transfered on a shared memory interface") 107 | logging.info("Shared memory interface name: {:s}".format(shmem_name)) 108 | 109 | 110 | #################################### 111 | # Initialization 112 | #################################### 113 | iq_header = IQHeader() 114 | 115 | iq_header.sync_word = IQHeader.SYNC_WORD 116 | iq_header.frame_type = frame_type 117 | iq_header.active_ant_chs = M 118 | iq_header.daq_block_index = 0 119 | iq_header.cpi_index = 0 120 | iq_header.cpi_length = N_daq 121 | iq_header.sample_bit_depth = 8 122 | iq_header.rf_center_freq = 0 123 | iq_header.adc_sampling_freq = int(fs) 124 | iq_header.sampling_freq = int(fs) 125 | # Uninitialzed header fields are all zero! 126 | 127 | if shmem_name != "": 128 | out_shmem_iface = outShmemIface(shmem_name, 129 | int(1024+N_daq*2*M*(iq_header.sample_bit_depth/8)), 130 | drop_mode = False) 131 | if not out_shmem_iface.init_ok: 132 | logging.critical("Shared memory initialization failed, exiting..") 133 | out_shmem_iface.destory_sm_buffer() 134 | exit() 135 | else: 136 | logging.info("Shared memory interface succesfully initialized") 137 | 138 | #################################### 139 | # Simulation 140 | #################################### 141 | 142 | # Allocation 143 | signal_out = np.zeros(N_daq*2, dtype=np.uint8) # This array stores the samples that are written to the output 144 | signal_iqcf = np.zeros(N_daq, dtype=np.complex64) # Stores the generate samples in complex float type 145 | packet_start_time = 0 146 | sig_pow = 10**(ps/10) 147 | sig_amp = np.sqrt(sig_pow) 148 | sig_freq = sig_freq_sweep_start 149 | multi_frame_cntr = 0 150 | try: 151 | for b in range(blocks): 152 | logger.info("Writing block: {:d}".format(b)) 153 | logging.info("Current CW frequency: {:.2f} MHz".format(sig_freq/10**6)) 154 | payload_byte_array = bytearray() 155 | 156 | # Generating time indexes 157 | t = np.arange(packet_start_time, packet_start_time+N_daq,1) 158 | packet_start_time += N_daq 159 | 160 | # Generating signal 161 | signal_iqcf = sig_amp * np.exp(1j*2*np.pi*(sig_freq/fs)*t) 162 | 163 | # Aditive noise to avoid spurs from quantization 164 | std_dev = np.sqrt(10**(pn/10)/2) 165 | noise = np.random.normal(0,std_dev,(N_daq))+1j*np.random.normal(0,std_dev,(N_daq)) 166 | signal_iqcf += noise 167 | 168 | # Scaling and leveling 169 | signal_iqcf += (1+1j) 170 | signal_iqcf *= (255/2) 171 | 172 | # Casting and packing 173 | signal_out[0::2] = signal_iqcf.real 174 | signal_out[1::2] = signal_iqcf.imag 175 | 176 | # Assembling payload 177 | for m in range(M): 178 | payload_byte_array += pack('B'*N_daq*2, *signal_out) 179 | 180 | # Fill up header 181 | iq_header.daq_block_index = b 182 | iq_header.rf_center_freq = int(rf_freq+sig_freq) 183 | 184 | multi_frame_cntr +=1 185 | if multi_frame_cntr == multi_frames: 186 | sig_freq += sig_freq_sweep_step 187 | multi_frame_cntr = 0 188 | packet_start_time = 0 189 | 190 | ####################################### 191 | # SEND DATA FRAME 192 | ####################################### 193 | if shmem_name != "": 194 | active_buffer_index = out_shmem_iface.wait_buff_free() 195 | logging.info("Buffer free: {:d}".format(active_buffer_index)) 196 | # Get the shared memory buffer 197 | iq_frame_buffer_out = (out_shmem_iface.buffers[active_buffer_index]).view(dtype=np.uint8) 198 | # Get the IQ sample array from the buffer 199 | iq_samples_out = iq_frame_buffer_out[1024:1024+iq_header.cpi_length*2*iq_header.active_ant_chs].reshape(iq_header.active_ant_chs, iq_header.cpi_length*2) 200 | if active_buffer_index !=3: 201 | (out_shmem_iface.buffers[active_buffer_index])[0:1024] = np.frombuffer(iq_header.encode_header(), dtype=np.uint8) 202 | for m in range(M): 203 | iq_samples_out[m,0::2] = signal_iqcf[:].real 204 | iq_samples_out[m,1::2] = signal_iqcf[:].imag 205 | out_shmem_iface.send_ctr_buff_ready(active_buffer_index) 206 | else: 207 | sys.stdout.buffer.write(iq_header.encode_header()) # Write the IQ header 208 | if (frame_type == IQHeader.FRAME_TYPE_DATA) | (frame_type == IQHeader.FRAME_TYPE_CAL): 209 | sys.stdout.buffer.write(payload_byte_array) # Write IQ data 210 | except: 211 | logging.error("Unexpected error: {:s}".format(sys.exc_info()[0])) 212 | if shmem_name != "": 213 | out_shmem_iface.send_ctr_terminate() 214 | time.sleep(2) 215 | out_shmem_iface.destory_sm_buffer() 216 | logging.info("Total dropped frames: {:d}".format(out_shmem_iface.dropped_frame_cntr)) 217 | logging.info("Standard frame generator exited") 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/gen_ramp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : 3 | Ramp signal generator for unit testing 4 | 5 | Project : HeIMDALL DAQ Firmware 6 | License : GNU GPL V3 7 | Author : Tamas Peto 8 | 9 | Command line arguments: 10 | -t : Frame type (default:DATA) 11 | -b : Number of frames to send (default:10) 12 | -n : Number of samples per channel in the payload 13 | 14 | Copyright (C) 2018-2020 Tamás Pető 15 | 16 | This program is free software: you can redistribute it and/or modify 17 | it under the terms of the GNU General Public License as published by 18 | the Free Software Foundation, either version 3 of the License, or 19 | any later version. 20 | 21 | This program is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | GNU General Public License for more details. 25 | 26 | You should have received a copy of the GNU General Public License 27 | along with this program. If not, see . 28 | 29 | """ 30 | import os 31 | import sys 32 | import getopt 33 | import time 34 | import numpy as np 35 | from struct import pack, unpack 36 | import logging 37 | import threading 38 | 39 | # Import external modules 40 | currentPath = os.path.dirname(os.path.realpath(__file__)) 41 | rootPath = os.path.dirname(os.path.dirname(currentPath)) 42 | sys.path.insert(0, os.path.join(rootPath, "_daq_core")) 43 | from iq_header import IQHeader 44 | from shmemIface import outShmemIface 45 | 46 | class rampFrameGenator(threading.Thread): 47 | 48 | def __init__(self, frame_type=0, 49 | blocks = 10, 50 | N_daq=2**18, 51 | data_type='CINT8', 52 | shmem_name=''): 53 | threading.Thread.__init__(self) 54 | logging.basicConfig(level=logging.INFO) 55 | self.logger = logging.getLogger(__name__) 56 | 57 | self.N_daq = N_daq 58 | self.M = 4 59 | self.blocks = blocks 60 | self.frame_type = frame_type 61 | self.data_type = data_type 62 | self.shmem_name = shmem_name 63 | 64 | # Synchronization related parameters 65 | self.delays = [0, 0, 0, 0] 66 | self.ramp_max = 29 67 | 68 | # Initialize IQ header 69 | self.iq_header = IQHeader() 70 | self.iq_header.sync_word = IQHeader.SYNC_WORD 71 | self.iq_header.frame_type = frame_type 72 | self.iq_header.active_ant_chs = self.M 73 | self.iq_header.daq_block_index = 0 74 | self.iq_header.cpi_index = 0 75 | 76 | if (frame_type == IQHeader.FRAME_TYPE_DUMMY) | (frame_type == IQHeader.FRAME_TYPE_TRIGW): 77 | self.iq_header.cpi_length = 0 78 | elif (frame_type == IQHeader.FRAME_TYPE_DATA) | (frame_type == IQHeader.FRAME_TYPE_CAL): 79 | self.iq_header.cpi_length = N_daq 80 | 81 | if data_type == "CINT8": 82 | self.iq_header.sample_bit_depth = 8 83 | elif data_type == "CF32": 84 | self.iq_header.sample_bit_depth = 32 85 | else: 86 | self.iq_header.sample_bit_depth = 0 87 | 88 | # Uninitialzed header fields are all zero! 89 | self.logger.info("Frame type: {:d}".format(self.frame_type)) 90 | self.logger.info("Number of frames: {:d}".format(self.blocks)) 91 | self.logger.info("Number of samples per channel: {:d}".format(self.N_daq)) 92 | self.logger.info("Frame data type: {:s}".format(data_type)) 93 | if shmem_name != "": 94 | self.logger.info("Frames will be transfered on a shared memory interface") 95 | self.logger.info("Shared memory interface name: {:s}".format(shmem_name)) 96 | 97 | def run(self): 98 | if self.shmem_name != "": 99 | self.out_shmem_iface = outShmemIface(self.shmem_name, 100 | int(1024+self.N_daq*2*self.M*(self.iq_header.sample_bit_depth/8)), 101 | drop_mode = False) 102 | if not self.out_shmem_iface.init_ok: 103 | self.logger.critical("Shared memory initialization failed, exiting..") 104 | self.out_shmem_iface.destory_sm_buffer() 105 | else: 106 | self.logger.info("Shared memory interface succesfully initialized") 107 | 108 | self.logger.info("Starting test frame generation") 109 | 110 | #################################### 111 | # Simulation 112 | #################################### 113 | 114 | # Allocation 115 | sig_out_ch1 = np.zeros((self.N_daq*2), dtype=np.uint8) # This array stores the samples that are written to output 116 | sig_out_chm = np.zeros((self.M, self.N_daq*2), dtype=np.uint8) 117 | 118 | raw_sig_ramp = np.zeros((self.N_daq+max(self.delays)), dtype = np.uint8) 119 | time_index = 0 120 | for b in range(self.blocks): 121 | self.logger.info("Writing block: {:d}".format(b)) 122 | self.iq_header.daq_block_index = b 123 | 124 | payload_byte_array = bytearray() 125 | for m in range(self.M): 126 | raw_sig_m = np.arange(time_index + self.delays[m], time_index + self.delays[m]+self.N_daq,1, dtype=np.uint32)%self.ramp_max 127 | sig_out_ch1[0::2] = raw_sig_m[:]+2*m*self.ramp_max 128 | sig_out_ch1[1::2] = raw_sig_m[:]+(2*m+1)*self.ramp_max 129 | payload_byte_array += pack('B'*self.N_daq*2, *sig_out_ch1) 130 | sig_out_chm[m,:] = sig_out_ch1[:] 131 | time_index+=self.N_daq 132 | ####################################### 133 | # SEND DATA FRAME 134 | ####################################### 135 | if self.shmem_name != "": 136 | active_buffer_index = self.out_shmem_iface.wait_buff_free() 137 | logging.info("Buffer free: {:d}".format(active_buffer_index)) 138 | if active_buffer_index !=3: 139 | # Get the shared memory buffer 140 | iq_frame_buffer_out = (self.out_shmem_iface.buffers[active_buffer_index]).view(dtype=np.uint8) 141 | 142 | # Get the IQ sample array from the buffer 143 | iq_samples_out = iq_frame_buffer_out[1024:1024+self.iq_header.cpi_length*2*self.iq_header.active_ant_chs]\ 144 | .reshape(self.iq_header.active_ant_chs, self.iq_header.cpi_length*2) 145 | 146 | (self.out_shmem_iface.buffers[active_buffer_index])[0:1024] = np.frombuffer(self.iq_header.encode_header(), dtype=np.uint8) 147 | for m in range(self.M): 148 | iq_samples_out[m,:] = sig_out_chm[m,:] 149 | self.out_shmem_iface.send_ctr_buff_ready(active_buffer_index) 150 | else: 151 | #self.iq_header.dump_header() 152 | sys.stdout.buffer.write(self.iq_header.encode_header()) # Write the IQ header 153 | sys.stdout.buffer.write(payload_byte_array) # Write IQ data 154 | if self.shmem_name != "": 155 | self.out_shmem_iface.send_ctr_terminate() 156 | time.sleep(2) 157 | self.out_shmem_iface.destory_sm_buffer() 158 | self.logger.info("Total dropped frames: {:d}".format(self.out_shmem_iface.dropped_frame_cntr)) 159 | self.logger.info("Standard frame generator exited") 160 | 161 | 162 | if __name__ == "__main__": 163 | logging.basicConfig(level=logging.INFO) 164 | N_daq = 2**18 165 | M = 4 166 | blocks = 10 167 | frame_type = IQHeader.FRAME_TYPE_DATA 168 | data_type = "CINT8" 169 | shmem_name = "" 170 | # Parse command line arguments 171 | opts, args = getopt.getopt(sys.argv[1:],"t:b:n:d:m:") 172 | for opt, arg in opts: 173 | if opt == '-t': 174 | frame_type = int(arg) 175 | elif opt == '-b': 176 | blocks = int(arg) 177 | elif opt == '-n': 178 | N_daq = int(arg) 179 | elif opt == '-d': 180 | data_type = arg 181 | elif opt == '-m': 182 | shmem_name = arg 183 | generator = rampFrameGenator(frame_type, blocks, N_daq, data_type, shmem_name) 184 | generator.start() -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/gen_std_frame.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : 3 | Standard frame generator for unit testing 4 | 5 | Project : HeIMDALL DAQ Firmware 6 | License : GNU GPL V3 7 | Author : Tamas Peto 8 | 9 | Command line arguments: 10 | -t : Frame type (default:DATA) 11 | -b : Number of frames to send (default:10) 12 | -n : Number of samples per channel in the payload 13 | -d : Data type "CINT16"/"CF64"(default:CINT16) 14 | -m : Shared memory name (if not specified, data is sent on std out) 15 | 16 | Copyright (C) 2018-2020 Tamás Pető 17 | 18 | This program is free software: you can redistribute it and/or modify 19 | it under the terms of the GNU General Public License as published by 20 | the Free Software Foundation, either version 3 of the License, or 21 | any later version. 22 | 23 | This program is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU General Public License for more details. 27 | 28 | You should have received a copy of the GNU General Public License 29 | along with this program. If not, see . 30 | 31 | """ 32 | import os 33 | import sys 34 | import getopt 35 | import time 36 | import numpy as np 37 | from struct import pack, unpack 38 | import logging 39 | import threading 40 | 41 | # Import IQ header module 42 | currentPath = os.path.dirname(os.path.realpath(__file__)) 43 | rootPath = os.path.dirname(os.path.dirname(currentPath)) 44 | sys.path.insert(0, os.path.join(rootPath, "_daq_core")) 45 | from iq_header import IQHeader 46 | from shmemIface import outShmemIface 47 | 48 | class stdFrameGenator(threading.Thread): 49 | 50 | def __init__(self, frame_type=0, 51 | blocks = 10, 52 | N_daq=2**18, 53 | data_type='CINT8', 54 | shmem_name=''): 55 | threading.Thread.__init__(self) 56 | self.logger = logging.getLogger(__name__) 57 | 58 | self.N_daq = N_daq 59 | self.M = 4 60 | self.blocks = blocks 61 | self.frame_type = frame_type 62 | self.data_type = data_type 63 | self.shmem_name = shmem_name 64 | 65 | self.logger.info("Frame type: {:d}".format(frame_type)) 66 | self.logger.info("Number of frames: {:d}".format(blocks)) 67 | self.logger.info("Number of sample per channel: {:d}".format(N_daq)) 68 | self.logger.info("Frame data type: {:s}".format(data_type)) 69 | if shmem_name != "": 70 | self.logger.info("Frames will be transfered on a shared memory interface") 71 | self.logger.info("Shared memory interface name: {:s}".format(shmem_name)) 72 | 73 | self.iq_header = IQHeader() 74 | 75 | self.iq_header.sync_word = IQHeader.SYNC_WORD 76 | self.iq_header.frame_type = frame_type 77 | self.iq_header.active_ant_chs = self.M 78 | self.iq_header.daq_block_index = 0 79 | self.iq_header.cpi_index = 0 80 | 81 | if (frame_type == IQHeader.FRAME_TYPE_DUMMY) | (frame_type == IQHeader.FRAME_TYPE_TRIGW): 82 | self.iq_header.cpi_length = 0 83 | elif (frame_type == IQHeader.FRAME_TYPE_DATA) | (frame_type == IQHeader.FRAME_TYPE_CAL): 84 | self.iq_header.cpi_length = N_daq 85 | 86 | if data_type == "CINT8": 87 | self.iq_header.sample_bit_depth = 8 88 | elif data_type == "CF32": 89 | self.iq_header.sample_bit_depth = 32 90 | else: 91 | self.logger.critical("Unidentified data type: {:s}".format(data_type)) 92 | self.iq_header.sample_bit_depth = 0 93 | 94 | # Uninitialzed header fields are all zero! 95 | 96 | def run(self): 97 | if self.shmem_name != "": 98 | self.out_shmem_iface = outShmemIface(self.shmem_name, 99 | int(1024+self.N_daq*2*self.M*(self.iq_header.sample_bit_depth/8)), 100 | drop_mode = False) 101 | if not self.out_shmem_iface.init_ok: 102 | self.logger.critical("Shared memory initialization failed, exiting..") 103 | self.out_shmem_iface.destory_sm_buffer() 104 | else: 105 | self.logger.info("Shared memory interface succesfully initialized") 106 | 107 | #################################### 108 | # Simulation 109 | #################################### 110 | 111 | self.logger.info("Starting test frame generation") 112 | # Allocation 113 | if self.data_type == "CINT8": 114 | signal = np.zeros((self.N_daq*2), dtype=np.uint8) # This array stores the samples that are written to output 115 | elif self.data_type == "CF32": 116 | signal = np.zeros((self.N_daq*2), dtype=np.float32) # This array stores the samples that are written to output 117 | 118 | payload_byte_array = bytearray() 119 | for m in range(self.M): 120 | if self.data_type == "CINT8": 121 | payload_byte_array += pack('B'*self.N_daq*2, *signal) 122 | elif self.data_type == "CF32": 123 | payload_byte_array += pack('f'*self.N_daq*2, *signal) 124 | try: 125 | for b in range(self.blocks): 126 | self.logger.info("Writing block: {:d}".format(b)) 127 | self.iq_header.daq_block_index = b 128 | self.iq_header.cpi_index = b 129 | ####################################### 130 | # SEND DATA FRAME 131 | ####################################### 132 | if self.shmem_name != "": 133 | active_buffer_index = self.out_shmem_iface.wait_buff_free() 134 | self.logger.info("Buffer free: {:d}".format(active_buffer_index)) 135 | if active_buffer_index !=3: 136 | (self.out_shmem_iface.buffers[active_buffer_index])[0:1024] = np.frombuffer(self.iq_header.encode_header(), dtype=np.uint8) 137 | self.out_shmem_iface.send_ctr_buff_ready(active_buffer_index) 138 | else: 139 | sys.stdout.buffer.write(self.iq_header.encode_header()) # Write the IQ header 140 | if (self.frame_type == IQHeader.FRAME_TYPE_DATA) | (self.frame_type == IQHeader.FRAME_TYPE_CAL): 141 | sys.stdout.buffer.write(payload_byte_array) # Write IQ data 142 | except: 143 | self.logger.error("Unexpected error: {:s}".format(sys.exc_info()[0])) 144 | 145 | if self.shmem_name != "": 146 | self.out_shmem_iface.send_ctr_terminate() 147 | time.sleep(2) 148 | self.out_shmem_iface.destory_sm_buffer() 149 | self.logger.info("Total dropped frames: {:d}".format(self.out_shmem_iface.dropped_frame_cntr)) 150 | self.logger.info("Standard frame generator exited") 151 | 152 | if __name__ == "__main__": 153 | logging.basicConfig(level=logging.DEBUG) 154 | N_daq = 2**18 155 | M = 4 156 | blocks = 10 157 | frame_type = IQHeader.FRAME_TYPE_DATA 158 | data_type = "CINT8" 159 | shmem_name = "" 160 | # Parse command line arguments 161 | opts, args = getopt.getopt(sys.argv[1:],"t:b:n:d:m:") 162 | for opt, arg in opts: 163 | if opt == '-t': 164 | frame_type = int(arg) 165 | elif opt == '-b': 166 | blocks = int(arg) 167 | elif opt == '-n': 168 | N_daq = int(arg) 169 | elif opt == '-d': 170 | data_type = arg 171 | elif opt == '-m': 172 | shmem_name = arg 173 | generator = stdFrameGenator(frame_type, blocks, N_daq, data_type, shmem_name) 174 | generator.start() -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/test_iq_server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : System level testing 3 | 4 | Project : HeIMDALL DAQ Firmware 5 | License : GNU GPL V3 6 | Author : Tamas Peto 7 | Copyright (C) 2018-2020 Tamás Pető 8 | 9 | This program is free Falsesoftware: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | 22 | """ 23 | import unittest 24 | from os.path import join, dirname, realpath 25 | import sys 26 | import subprocess 27 | import logging 28 | import numpy as np 29 | from struct import pack 30 | import time 31 | 32 | current_path = dirname(realpath(__file__)) 33 | root_path = dirname(dirname(current_path)) 34 | daq_core_path = join(root_path, "_daq_core") 35 | data_control_path = join(root_path, "_data_control") 36 | log_path = join(root_path, "_logs") 37 | unit_test_path = join(root_path, "_testing", "unit_test") 38 | test_logs_path = join(root_path, "_testing", "test_logs") 39 | 40 | 41 | # Import IQ header module 42 | sys.path.insert(0, daq_core_path) 43 | from iq_header import IQHeader 44 | 45 | # Import IQ frame sink module 46 | from iq_eth_sink import IQRecorder 47 | 48 | sys.path.insert(0, unit_test_path) 49 | from gen_std_frame import stdFrameGenator 50 | 51 | class TesterSystem(unittest.TestCase): 52 | 53 | @classmethod 54 | def setUpClass(cls): 55 | logging.basicConfig(filename=join(test_logs_path,'unit_test.log'), 56 | level=logging.INFO) 57 | logging.info("--> Starting IQ server test <--") 58 | 59 | def setUp(self): 60 | warnings.simplefilter("ignore", ResourceWarning) 61 | self.logger = logging.getLogger(__name__) 62 | 63 | # Open log files 64 | self.fd_log_gen_err = open(join(log_path,"gen.log"), "w") 65 | self.fd_log_mut_err = open(join(log_path,"mut.log"), "w") # Module under test 66 | 67 | def tearDown(self): 68 | 69 | # Close log files 70 | self.fd_log_gen_err.close() 71 | self.fd_log_mut_err.close() 72 | 73 | ############################################# 74 | # TEST FUNCTIONS # 75 | ############################################# 76 | @unittest.skip("Not implemented") 77 | def test_case_7_1(self): 78 | self.logger.info("-> Starting Test Case [7_1] : IQ server frame forward test") 79 | 80 | # -> Assume <- 81 | frame_type = IQHeader.FRAME_TYPE_DATA 82 | frame_count = 100 83 | sample_size = 2**18 84 | 85 | # -> Action 86 | # Start the frame generator 87 | generator = stdFrameGenator(frame_type, frame_count+1, sample_size, 'CF32', 'delay_sync_iq') 88 | 89 | generator.iq_header.delay_sync_flag=1 90 | generator.iq_header.iq_sync_flag=1 91 | 92 | # Start the iq server module 93 | iq_server_module = subprocess.Popen([join(daq_core_path,"iq_server.out")], 94 | stdout=subprocess.DEVNULL, 95 | stderr=self.fd_log_mut_err) 96 | 97 | # Start ethernet IQ frame receiver 98 | IQ_rec_inst0 = IQRecorder(frame_count) 99 | time.sleep(2) 100 | generator.start() 101 | IQ_rec_inst0.listen() 102 | self.logger.info("IQ frame reception interval finished") 103 | 104 | # -> Assert <- 105 | self.assertTrue(1) 106 | 107 | ############################################# 108 | # TEST RUN WRAPPERS # 109 | ############################################# 110 | 111 | ############################################# 112 | # RESULT CHECKER FUNCTIONS # 113 | ############################################# 114 | 115 | -------------------------------------------------------------------------------- /Firmware/_testing/unit_test/test_sys.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description : System level testing 3 | 4 | Project : HeIMDALL DAQ 5 | License : GNU GPL V3 6 | Author : Tamas Peto 7 | Copyright (C) 2018-2020 Tamás Pető 8 | 9 | This program is free Falsesoftware: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | 22 | """ 23 | import unittest 24 | from os.path import join, dirname, realpath 25 | import sys 26 | import subprocess 27 | import logging 28 | import numpy as np 29 | from struct import pack 30 | import time 31 | 32 | current_path = dirname(realpath(__file__)) 33 | root_path = dirname(dirname(current_path)) 34 | daq_core_path = join(root_path, "_daq_core") 35 | data_control_path = join(root_path, "_data_control") 36 | log_path = join(root_path, "_logs") 37 | unit_test_path = join(root_path, "_testing", "unit_test") 38 | test_logs_path = join(root_path, "_testing", "test_logs") 39 | 40 | 41 | # Import IQ header module 42 | sys.path.insert(0, daq_core_path) 43 | from iq_header import IQHeader 44 | 45 | 46 | # Import IQ frame sink module 47 | from iq_eth_sink import IQRecorder 48 | 49 | class TesterSystem(unittest.TestCase): 50 | 51 | @classmethod 52 | def setUpClass(cls): 53 | logging.basicConfig(filename=join(test_logs_path,'unit_test.log'), 54 | level=logging.INFO) 55 | logging.info("--> Starting System level testing <--") 56 | 57 | def setUp(self): 58 | self.logger = logging.getLogger(__name__) 59 | 60 | # Open log files 61 | self.fd_log_gen_err = open(join(log_path,"gen.log"), "w") 62 | 63 | def tearDown(self): 64 | 65 | # Close log files 66 | self.fd_log_gen_err.close() 67 | 68 | ############################################# 69 | # TEST FUNCTIONS # 70 | ############################################# 71 | def test_case_0_200(self): 72 | self.logger.info("-> Starting Test Case [200] : System level throughput testing with synthetic data") 73 | 74 | # -> Assume <- 75 | frame_count = 10000 76 | 77 | # -> Action 78 | # Start the daq chain 79 | daq_chain_start = subprocess.Popen(["./daq_start_sm.sh"], 80 | stdout=subprocess.DEVNULL) 81 | daq_chain_start.wait() 82 | 83 | # Start ethernet IQ frame receiver 84 | IQ_rec_inst0 = IQRecorder(frame_count) 85 | time.sleep(2) 86 | IQ_rec_inst0.listen() 87 | self.logger.info("IQ frame reception interval finished") 88 | time.sleep(2) 89 | 90 | frame_drop_rate = IQ_rec_inst0.dropped_frames/IQ_rec_inst0.max_received_frames 91 | self.logger.info("IQ Frame drop statistic:") 92 | self.logger.info("------------------------") 93 | self.logger.info("total / dropped frames: [{:d}/{:d}], drop rate {:.2f}".format(IQ_rec_inst0.max_received_frames,IQ_rec_inst0.dropped_frames,frame_drop_rate)) 94 | 95 | # -> Assert <- 96 | self.assertTrue(IQ_rec_inst0.dropped_frames < 50) 97 | 98 | ############################################# 99 | # TEST RUN WRAPPERS # 100 | ############################################# 101 | 102 | ############################################# 103 | # RESULT CHECKER FUNCTIONS # 104 | ############################################# 105 | 106 | -------------------------------------------------------------------------------- /Firmware/daq_chain_config.ini: -------------------------------------------------------------------------------- 1 | [meta] 2 | ini_version = 7 3 | config_name = kraken_default 4 | 5 | [hw] 6 | name = kraken5 7 | unit_id = 0 8 | ioo_type = 0 9 | num_ch = 5 10 | en_bias_tee = 0,0,0,0,0 11 | 12 | [daq] 13 | log_level = 5 14 | daq_buffer_size = 262144 15 | center_freq = 700000000 16 | sample_rate = 2400000 17 | gain = 0 18 | en_noise_source_ctr = 1 19 | ctr_channel_serial_no = 1000 20 | 21 | [pre_processing] 22 | cpi_size = 1048576 23 | decimation_ratio = 1 24 | fir_relative_bandwidth = 1.0 25 | fir_tap_size = 1 26 | fir_window = hann 27 | en_filter_reset = 0 28 | 29 | [calibration] 30 | corr_size = 65536 31 | std_ch_ind = 0 32 | en_iq_cal = 1 33 | amplitude_cal_mode = channel_power 34 | en_gain_tune_init = 0 35 | gain_lock_interval = 0 36 | unified_gain_control = 0 37 | require_track_lock_intervention = 0 38 | cal_track_mode = 2 39 | cal_frame_interval = 687 40 | cal_frame_burst_size = 10 41 | amplitude_tolerance = 2 42 | phase_tolerance = 1 43 | maximum_sync_fails = 10 44 | iq_adjust_source = explicit-time-delay 45 | iq_adjust_amplitude = 0,0,0,0 46 | iq_adjust_time_delay_ns = 0, 0, 0, 0 47 | 48 | [adpis] 49 | en_adpis = 0 50 | adpis_proc_size = 8192 51 | adpis_gains_init = 0,0,0,0,0 52 | 53 | [data_interface] 54 | out_data_iface_type = shmem 55 | 56 | -------------------------------------------------------------------------------- /Firmware/daq_start_sm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # DAQ chain start srcipt 4 | # 5 | # Project : HeIMDALL DAQ Firmware 6 | # License : GNU GPL V3 7 | # Authors: Tamas Peto, Carl Laufer 8 | 9 | # Check config file 10 | #res=$(python3 ini_checker.py 2>&1) #comment out ini checker for now since it is very slow 11 | echo -e "\e[33mConfig file check bypassed [ WARNING ]\e[39m" 12 | #if test -z "$res" 13 | #then 14 | # echo -e "\e[92mConfig file check [ OK ]\e[39m" 15 | #else 16 | # echo -e "\e[91mConfig file check [ FAIL ]\e[39m" 17 | # echo $res 18 | # exit 19 | #fi 20 | 21 | sudo sysctl -w kernel.sched_rt_runtime_us=-1 22 | 23 | # Read config ini file 24 | out_data_iface_type=$(awk -F'=' '/out_data_iface_type/ {gsub (" ", "", $0); print $2}' daq_chain_config.ini) 25 | 26 | # (re) create control FIFOs 27 | rm _data_control/fw_decimator_in 2> /dev/null 28 | rm _data_control/bw_decimator_in 2> /dev/null 29 | 30 | rm _data_control/fw_decimator_out 2> /dev/null 31 | rm _data_control/bw_decimator_out 2> /dev/null 32 | 33 | rm _data_control/fw_delay_sync_iq 2> /dev/null 34 | rm _data_control/bw_delay_sync_iq 2> /dev/null 35 | 36 | rm _data_control/fw_delay_sync_hwc 2> /dev/null 37 | rm _data_control/bw_delay_sync_hwc 2> /dev/null 38 | 39 | mkfifo _data_control/fw_decimator_in 40 | mkfifo _data_control/bw_decimator_in 41 | 42 | mkfifo _data_control/fw_decimator_out 43 | mkfifo _data_control/bw_decimator_out 44 | 45 | mkfifo _data_control/fw_delay_sync_iq 46 | mkfifo _data_control/bw_delay_sync_iq 47 | 48 | mkfifo _data_control/fw_delay_sync_hwc 49 | mkfifo _data_control/bw_delay_sync_hwc 50 | 51 | # Remove old log files 52 | rm _logs/*.log 2> /dev/null 53 | 54 | # Useful to set this on low power ARM devices 55 | #sudo cpufreq-set -g performance 56 | 57 | # Set for Tinkerboard with heatsink/fan 58 | #sudo cpufreq-set -d 1.8GHz 59 | 60 | # The Kernel limits the maximum size of all buffers that libusb can allocate to 16MB by default. 61 | # In order to disable the limit, you have to run the following command as root: 62 | sudo sh -c "echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb" 63 | 64 | # This command clear the caches 65 | echo '3' | sudo tee /proc/sys/vm/drop_caches > /dev/null 66 | 67 | # Check ports(IQ server:5000, Hardware controller:5001) 68 | while true; do 69 | port_ready=1 70 | lsof -i:5000 >/dev/null 71 | out=$? 72 | if test $out -ne 1 73 | then 74 | port_ready=0 75 | fi 76 | lsof -i:5001 >/dev/null 77 | out=$? 78 | if test $out -ne 1 79 | then 80 | port_ready=0 81 | fi 82 | if test $port_ready -eq 1 83 | then 84 | break 85 | else 86 | echo "WARN:Ports used by the DAQ chain are not free! (5000 & 5001)" 87 | ./daq_stop.sh 88 | sleep 1 89 | fi 90 | done 91 | 92 | # Generating FIR filter coefficients 93 | python3 fir_filter_designer.py 94 | out=$? 95 | if test $out -ne 0 96 | then 97 | echo -e "\e[91mDAQ chain not started!\e[39m" 98 | exit 99 | fi 100 | 101 | # Start main program chain -Thread 0 Normal (non squelch mode) 102 | echo "Starting DAQ Subsystem" 103 | chrt -f 99 _daq_core/rtl_daq.out 2> _logs/rtl_daq.log | \ 104 | chrt -f 99 _daq_core/rebuffer.out 0 2> _logs/rebuffer.log & 105 | 106 | # Decimator - Thread 1 107 | chrt -f 99 _daq_core/decimate.out 2> _logs/decimator.log & 108 | 109 | # Delay synchronizer - Thread 2 110 | chrt -f 99 python3 _daq_core/delay_sync.py 2> _logs/delay_sync.log & 111 | 112 | # Hardware Controller data path - Thread 3 113 | chrt -f 99 sudo env "PATH=$PATH" python3 _daq_core/hw_controller.py 2> _logs/hwc.log & 114 | # root priviliges are needed to drive the i2c master 115 | 116 | if [ $out_data_iface_type = eth ]; then 117 | echo "Output data interface: IQ ethernet server" 118 | chrt -f 99 _daq_core/iq_server.out 2>_logs/iq_server.log & 119 | elif [ $out_data_iface_type = shmem ]; then 120 | echo "Output data interface: Shared memory" 121 | fi 122 | 123 | # IQ Eth sink used for testing 124 | #sleep 3 125 | #python3 _daq_core/iq_eth_sink.py 2>_logs/iq_eth_sink.log & 126 | 127 | echo -e " ) ( " 128 | echo -e " ( ) ) " 129 | echo -e " ) ( ( " 130 | echo -e " _______)_ " 131 | echo -e " .-'---------|" 132 | echo -e " ( |/\/\/\/\/|" 133 | echo -e " '-./\/\/\/\/|" 134 | echo -e " '_________'" 135 | echo -e " '-------' " 136 | echo -e " " 137 | echo -e "Have a coffee watch radar" 138 | -------------------------------------------------------------------------------- /Firmware/daq_stop.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh! 2 | echo "Shut down DAQ chain .." 3 | #sudo kill -64 $(ps aux | grep 'rtl' | awk '{print $2}') 4 | #sudo killall -s 9 rtl* 5 | 6 | sudo pkill -64 rtl_daq.out 7 | sudo kill -64 $(ps ax | grep "[p]ython3 _testing/test_data_synthesizer.py" | awk '{print $1}') 2> /dev/null 8 | sudo pkill -64 sync.out 9 | sudo pkill -64 decimate.out 10 | sudo pkill -64 rebuffer.out 11 | sudo kill -64 $(ps ax | grep "[p]ython3 _daq_core/delay_sync.py" | awk '{print $1}') 2> /dev/null 12 | sudo kill -64 $(ps ax | grep "[p]ython3 _daq_core/hw_controller.py" | awk '{print $1}') 2> /dev/null 13 | sudo kill -64 $(ps ax | grep "[p]ython3 _daq_core/iq_eth_sink.py" | awk '{print $1}') 2> /dev/null 14 | sudo pkill -64 iq_server.out 15 | -------------------------------------------------------------------------------- /Firmware/daq_synthetic_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # DAQ chain start srcipt 4 | # 5 | # Project : HeIMDALL DAQ Firmware 6 | # License : GNU GPL V3 7 | # Authors: Tamas Peto, Carl Laufer 8 | 9 | # Check config file 10 | res=$(python3 ini_checker.py no_hw 2>&1) 11 | if test -z "$res" 12 | then 13 | echo -e "\e[92mConfig file check [OK]\e[39m" 14 | else 15 | echo -e "\e[91mConfig file check [FAIL]\e[39m" 16 | echo $res 17 | exit 18 | fi 19 | 20 | # Read config ini file 21 | out_data_iface_type=$(awk -F "=" '/out_data_iface_type/ {print $2}' daq_chain_config.ini) 22 | 23 | # (re) create control FIFOs 24 | rm _data_control/fw_decimator_in 2> /dev/null 25 | rm _data_control/bw_decimator_in 2> /dev/null 26 | 27 | rm _data_control/fw_decimator_out 2> /dev/null 28 | rm _data_control/bw_decimator_out 2> /dev/null 29 | 30 | rm _data_control/fw_delay_sync_iq 2> /dev/null 31 | rm _data_control/bw_delay_sync_iq 2> /dev/null 32 | 33 | rm _data_control/fw_delay_sync_hwc 2> /dev/null 34 | rm _data_control/bw_delay_sync_hwc 2> /dev/null 35 | 36 | mkfifo _data_control/fw_decimator_in 37 | mkfifo _data_control/bw_decimator_in 38 | 39 | mkfifo _data_control/fw_decimator_out 40 | mkfifo _data_control/bw_decimator_out 41 | 42 | mkfifo _data_control/fw_delay_sync_iq 43 | mkfifo _data_control/bw_delay_sync_iq 44 | 45 | mkfifo _data_control/fw_delay_sync_hwc 46 | mkfifo _data_control/bw_delay_sync_hwc 47 | 48 | # Remove old log files 49 | rm _logs/*.log 2> /dev/null 50 | 51 | # Useful to set this on low power ARM devices 52 | #sudo cpufreq-set -g performance 53 | 54 | # Set for Tinkerboard with heatsink/fan 55 | #sudo cpufreq-set -d 1.8GHz 56 | 57 | # Generating FIR filter coefficients 58 | python3 fir_filter_designer.py 59 | out=$? 60 | if test $out -ne 0 61 | then 62 | echo -e "\e[91mDAQ chain not started!\e[39m" 63 | exit 64 | fi 65 | # Start main program chain -Thread 0 Normal (non squelch mode) 66 | echo "Starting DAQ Subsystem with synthetic data source" 67 | python3 _testing/test_data_synthesizer.py 2>_logs/synthetic.log | \ 68 | _daq_core/rebuffer.out 0 2> _logs/rebuffer.log & 69 | 70 | # Decimator - Thread 1 71 | chrt -f 99 _daq_core/decimate.out 2> _logs/decimator.log & 72 | 73 | # Delay synchronizer - Thread 2 74 | python3 _daq_core/delay_sync.py 2> _logs/delay_sync.log & 75 | 76 | # Hardware Controller data path - Thread 3 77 | sudo python3 _daq_core/hw_controller.py 2> _logs/hwc.log & 78 | # root priviliges are needed to drive the i2c master 79 | 80 | if [ $out_data_iface_type = eth ]; then 81 | echo "Output data interface: IQ ethernet server" 82 | _daq_core/iq_server.out 2>_logs/iq_server.log & 83 | elif [ $out_data_iface_type = shmem ]; then 84 | echo "Output data interface: Shared memory" 85 | fi 86 | 87 | # IQ Eth sink used for testing 88 | #sleep 3 89 | #python3 _daq_core/iq_eth_sink.py 2>_logs/iq_eth_sink.log & 90 | 91 | echo -e " ) ( " 92 | echo -e " ( ) ) " 93 | echo -e " ) ( ( " 94 | echo -e " _______)_ " 95 | echo -e " .-'---------|" 96 | echo -e " ( |/\/\/\/\/|" 97 | echo -e " '-./\/\/\/\/|" 98 | echo -e " '_________'" 99 | echo -e " '-------' " 100 | echo -e " " 101 | echo -e "Have a coffee watch radar" 102 | 103 | -------------------------------------------------------------------------------- /Firmware/fir_filter_designer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Designs an FIR filter for the decimator module based on specified parameters found 5 | in the "daq_chain_config.ini" file 6 | 7 | For the proper FIR configuration please check the documentation of the firmware. 8 | 9 | After starting the daq chain you can check the transfer characteristics of the generated 10 | FIR filter at : "_logs/Decimator_filter_transfer.html" 11 | 12 | Project: HeIMDALL DAQ Firmware 13 | Author : Tamas Peto 14 | """ 15 | import sys 16 | from scipy import signal 17 | import numpy as np 18 | #import plotly.graph_objects as go 19 | from configparser import ConfigParser 20 | 21 | parser = ConfigParser() 22 | found = parser.read(["daq_chain_config.ini"]) 23 | if not found: 24 | print("Configuration file not found. exiting") 25 | exit(-1) 26 | decimation_ratio = parser.getint('pre_processing', 'decimation_ratio') 27 | bandwidth = parser.getfloat('pre_processing', 'fir_relative_bandwidth') 28 | tap_size = parser.getint('pre_processing', 'fir_tap_size') 29 | window = parser.get('pre_processing', 'fir_window') 30 | fs = parser.getfloat('daq', 'sample_rate') 31 | fc = parser.getfloat('daq','center_freq') 32 | 33 | if decimation_ratio <1: 34 | print("ERROR: Decimation ratio can not be smaller than 1, exiting..") 35 | exit(-1) 36 | 37 | if tap_size <= decimation_ratio and decimation_ratio !=1 : 38 | print("ERROR: FIR tap size must be higher than the decimation ratio. Please consider increasing the tap size, exiting..") 39 | exit(-1) 40 | 41 | print("Desig FIR filter with the following parameters: ") 42 | print("Decimation ratio: {:d}".format(decimation_ratio)) 43 | print("Bandwidth: {:1.2f}".format(bandwidth)) 44 | print("Tap size: {:d}".format(tap_size)) 45 | print("Window function:", window) 46 | 47 | transfer_fname = "_logs/Decimator_filter_transfer.html" 48 | coeffs_fname = "_data_control/fir_coeffs.txt" 49 | # Fir filter parameters 50 | cut_off = bandwidth/decimation_ratio 51 | if cut_off < 1: 52 | # Design band pass FIR Filter 53 | b = signal.firwin(tap_size, cut_off, window=window) 54 | else: 55 | b=np.array([1]) 56 | 57 | # Uncomment for debugging 58 | """ 59 | # Plot transfer function 60 | w, h = signal.freqz(b=b, a=1, worN=2**16, whole=True) 61 | h+= 10**-10 # To avoid operations with zero 62 | h =np.fft.fftshift(h) 63 | h_log = 20*np.log10(np.abs(h)) 64 | 65 | w= np.linspace(-fs/2, fs/2, 2**16)+fc 66 | fig = go.Figure() 67 | fig.add_trace(go.Scatter(x=w/10**6, y=h_log, line=dict(width=2, dash='solid'))) 68 | 69 | # Export figure 70 | fig.update_layout( 71 | title = "Decimator filter transfer function", 72 | xaxis_title = "Frequency [kHz]", 73 | yaxis_title = "Amplitude[dB]", 74 | font=dict(size=18), 75 | hovermode='x') 76 | 77 | fig.write_html(transfer_fname) 78 | """ 79 | np.savetxt(coeffs_fname, b) 80 | print("FIR filter ready") 81 | print("Transfer funcfion is exported to : ", transfer_fname) 82 | print("Coefficients are exported to: ",coeffs_fname) 83 | exit(0) 84 | -------------------------------------------------------------------------------- /Firmware/unit_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Start unit testing.." 3 | echo "Internal warnings are ignored" 4 | 5 | rm _testing/test_logs/*.log 2> /dev/NULL 6 | rm _testing/test_logs/*.html 2> /dev/NULL 7 | 8 | # Start unit test for the rebuffer module 9 | #sudo python3 -W ignore -m unittest -v _testing/unit_test/test_rebuffer.py 10 | 11 | # Start unit test for the decimator module 12 | sudo python3 -W ignore -m unittest -v _testing/unit_test/test_decimator.py 13 | 14 | # Start unit test for the squelch module 15 | #sudo python3 -W ignore -m unittest -v _testing/unit_test/test_squelch.py 16 | 17 | # Start unit test for the delay synchronizer module 18 | #sudo python3 -W ignore -m unittest -v _testing/unit_test/test_delay_sync.py 19 | 20 | # Start unit test for the iq server module - Not Implemented 21 | #sudo python3 -W ignore -m unittest -v _testing/unit_test/test_iq_server.py 22 | 23 | #Start system level testing 24 | #sudo python3 -m unittest -v _testing/unit_test/test_sys.py 25 | #./daq_stop.sh -------------------------------------------------------------------------------- /config_files/kerberos_default/daq_chain_config.ini: -------------------------------------------------------------------------------- 1 | [meta] 2 | ini_version = 7 3 | config_name = kerberos_default 4 | 5 | [hw] 6 | name = kraken5 7 | unit_id = 0 8 | ioo_type = 0 9 | num_ch = 4 10 | en_bias_tee = 0,0,0,0 11 | 12 | [daq] 13 | log_level = 5 14 | daq_buffer_size = 262144 15 | center_freq = 700000000 16 | sample_rate = 2400000 17 | gain = 0 18 | en_noise_source_ctr = 1 19 | ctr_channel_serial_no = 1000 20 | 21 | [pre_processing] 22 | cpi_size = 1048576 23 | decimation_ratio = 1 24 | fir_relative_bandwidth = 1.0 25 | fir_tap_size = 1 26 | fir_window = hann 27 | en_filter_reset = 0 28 | 29 | [calibration] 30 | corr_size = 65536 31 | std_ch_ind = 0 32 | en_iq_cal = 1 33 | amplitude_cal_mode = channel_power 34 | en_gain_tune_init = 0 35 | gain_lock_interval = 0 36 | unified_gain_control = 0 37 | require_track_lock_intervention = 0 38 | cal_track_mode = 0 39 | cal_frame_interval = 687 40 | cal_frame_burst_size = 10 41 | amplitude_tolerance = 2 42 | phase_tolerance = 1 43 | maximum_sync_fails = 10 44 | iq_adjust_source = explicit-time-delay 45 | iq_adjust_amplitude = 0,0,0,0 46 | iq_adjust_time_delay_ns = 0, 0, 0, 0 47 | 48 | 49 | [adpis] 50 | en_adpis = 0 51 | adpis_proc_size = 8192 52 | adpis_gains_init = 0,0,0,0 53 | 54 | [data_interface] 55 | out_data_iface_type = shmem 56 | 57 | -------------------------------------------------------------------------------- /config_files/kraken_default/daq_chain_config.ini: -------------------------------------------------------------------------------- 1 | [meta] 2 | ini_version = 7 3 | config_name = kraken_default 4 | 5 | [hw] 6 | name = kraken5 7 | unit_id = 0 8 | ioo_type = 0 9 | num_ch = 5 10 | en_bias_tee = 0,0,0,0,0 11 | 12 | [daq] 13 | log_level = 5 14 | daq_buffer_size = 262144 15 | center_freq = 700000000 16 | sample_rate = 2400000 17 | gain = 0 18 | en_noise_source_ctr = 1 19 | ctr_channel_serial_no = 1000 20 | 21 | [pre_processing] 22 | cpi_size = 1048576 23 | decimation_ratio = 1 24 | fir_relative_bandwidth = 1.0 25 | fir_tap_size = 1 26 | fir_window = hann 27 | en_filter_reset = 0 28 | 29 | [calibration] 30 | corr_size = 65536 31 | std_ch_ind = 0 32 | en_iq_cal = 1 33 | amplitude_cal_mode = channel_power 34 | en_gain_tune_init = 0 35 | gain_lock_interval = 0 36 | unified_gain_control = 0 37 | require_track_lock_intervention = 0 38 | cal_track_mode = 2 39 | cal_frame_interval = 687 40 | cal_frame_burst_size = 10 41 | amplitude_tolerance = 2 42 | phase_tolerance = 1 43 | maximum_sync_fails = 10 44 | iq_adjust_source = explicit-time-delay 45 | iq_adjust_amplitude = 0,0,0,0 46 | iq_adjust_time_delay_ns = 0, 0, 0, 0 47 | 48 | [adpis] 49 | en_adpis = 0 50 | adpis_proc_size = 8192 51 | adpis_gains_init = 0,0,0,0,0 52 | 53 | [data_interface] 54 | out_data_iface_type = shmem 55 | 56 | -------------------------------------------------------------------------------- /config_files/kraken_development/daq_chain_config.ini: -------------------------------------------------------------------------------- 1 | [meta] 2 | ini_version = 7 3 | config_name = kraken_dev 4 | 5 | [hw] 6 | name = kraken5 7 | unit_id = 0 8 | ioo_type = 0 9 | num_ch = 5 10 | en_bias_tee = 1,1,1,1,1 11 | 12 | [daq] 13 | log_level = 5 14 | daq_buffer_size = 262144 15 | center_freq = 700000000 16 | sample_rate = 2400000 17 | gain = 0 18 | en_noise_source_ctr = 1 19 | ctr_channel_serial_no = 1000 20 | 21 | [pre_processing] 22 | cpi_size = 1048576 23 | decimation_ratio = 1 24 | fir_relative_bandwidth = 1.0 25 | fir_tap_size = 1 26 | fir_window = hann 27 | en_filter_reset = 0 28 | 29 | [calibration] 30 | corr_size = 65536 31 | std_ch_ind = 0 32 | en_iq_cal = 0 33 | amplitude_cal_mode = channel_power 34 | en_gain_tune_init = 0 35 | gain_lock_interval = 0 36 | unified_gain_control = 0 37 | require_track_lock_intervention = 0 38 | cal_track_mode = 2 39 | cal_frame_interval = 200 40 | cal_frame_burst_size = 20 41 | amplitude_tolerance = 2 42 | phase_tolerance = 2 43 | maximum_sync_fails = 10 44 | iq_adjust_source = touchstone 45 | iq_adjust_amplitude = 0,0,0,0 46 | iq_adjust_time_delay_ns = 0, 0, 0, 0 47 | 48 | [adpis] 49 | en_adpis = 0 50 | adpis_proc_size = 8192 51 | adpis_gains_init = 0,0,0,0,0 52 | 53 | [data_interface] 54 | out_data_iface_type = eth 55 | 56 | -------------------------------------------------------------------------------- /config_files/unit_test_k4/daq_chain_config.ini: -------------------------------------------------------------------------------- 1 | [meta] 2 | ini_version = 7 3 | config_name = unit_test_k4 4 | 5 | [hw] 6 | name = k4 7 | unit_id = 0 8 | ioo_type = 0 9 | num_ch = 4 10 | en_bias_tee = 0,0,0,0,0 11 | 12 | [daq] 13 | log_level = 2 14 | daq_buffer_size = 262144 15 | center_freq = 600000000 16 | sample_rate = 1000000 17 | gain = 0 18 | en_noise_source_ctr = 1 19 | ctr_channel_serial_no = 1000 20 | 21 | [pre_processing] 22 | cpi_size = 262144 23 | decimation_ratio = 1 24 | fir_relative_bandwidth = 1.0 25 | fir_tap_size = 1 26 | fir_window = hann 27 | en_filter_reset = 0 28 | 29 | [calibration] 30 | corr_size = 65536 31 | std_ch_ind = 0 32 | en_iq_cal = 1 33 | amplitude_cal_mode = default 34 | en_gain_tune_init = 0 35 | gain_lock_interval = 20 36 | unified_gain_control = 0 37 | require_track_lock_intervention = 0 38 | cal_track_mode = 2 39 | cal_frame_interval = 10 40 | cal_frame_burst_size = 5000 41 | amplitude_tolerance = 10 42 | phase_tolerance = 5 43 | maximum_sync_fails = 5 44 | iq_adjust_source = explicit-time-delay 45 | iq_adjust_amplitude = 0,0,0,0 46 | iq_adjust_time_delay_ns = 0, 0, 0, 0 47 | 48 | 49 | [adpis] 50 | en_adpis = 0 51 | adpis_proc_size = 8192 52 | adpis_gains_init = 0,0,0,0 53 | 54 | [data_interface] 55 | out_data_iface_type = eth 56 | 57 | -------------------------------------------------------------------------------- /util/cfg_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This script generates config file for the DAQ chain based on the given signal parameters. 4 | The signal is assumed to be burst like with the following parameters: 5 | - burst repetition interval [ms] 6 | - burst length [ms] 7 | - signal bandwidth [kHz] 8 | 9 | Project: HeIMDALL DAQ Firmware 10 | Author : Tamas Peto 11 | """ 12 | import numpy as np 13 | import logging 14 | from configparser import ConfigParser 15 | import argparse 16 | import sys, os 17 | 18 | current_path = os.path.dirname(os.path.realpath(__file__)) 19 | firmware_path = os.path.join(os.path.dirname(current_path),"Firmware") 20 | sys.path.insert(0, firmware_path) 21 | 22 | import ini_checker 23 | 24 | """ 25 | 26 | Prepare config file template 27 | 28 | """ 29 | #[meta] 30 | meta={"ini_version":"2", 31 | "config_name":"autogenV1" 32 | } 33 | #[hw] 34 | hw={"name" :"k5", 35 | "unit_id" :"0", 36 | "ioo_type":"0", 37 | "num_ch" :"5" 38 | } 39 | #[daq] 40 | daq = { 41 | "log_level" :"3", 42 | "daq_buffer_size" :"262144", 43 | "center_freq" :"600000000", 44 | "sample_rate" :"1000000", 45 | "gain" :"0", 46 | "en_noise_source_ctr" :"1", 47 | "ctr_channel_serial_no" :"1004" 48 | } 49 | #[squelch] 50 | squelch = { 51 | "en_squelch" :"1", 52 | "amplitude_threshold":"0.5" 53 | } 54 | #[pre_processing] 55 | pre_processing = { 56 | "cpi_size" :"262144", 57 | "decimation_ratio" :"1", 58 | "fir_relative_bandwidth" :"1", 59 | "fir_tap_size" :"1", 60 | "fir_window" :"hann", 61 | "en_filter_reset" :"0" 62 | } 63 | 64 | #[calibration] 65 | calibration = { 66 | "corr_size" :"65536", 67 | "std_ch_ind" :"0", 68 | "en_frac_cal" :"0", 69 | "en_iq_cal" :"1", 70 | "amplitude_cal_mode" :"channel_power", 71 | "gain_lock_interval" :"20", 72 | "unified_gain_control" :"0", 73 | "require_track_lock_intervention" :"0", 74 | "cal_track_mode" :"2", 75 | "cal_frame_interval" :"100", 76 | "cal_frame_burst_size" :"30", 77 | "amplitude_tolerance" :"1", 78 | "phase_tolerance" :"2", 79 | "maximum_sync_fails" :"5", 80 | } 81 | #[adpis] 82 | adpis = { 83 | "en_adpis" :"0", 84 | "en_gain_tune_init":"0", 85 | "adpis_proc_size" :"8192", 86 | "adpis_gains_init" :"0,0,0,0,0", 87 | } 88 | #[data_interface] 89 | data_interface = { 90 | "out_data_iface_type" : "shmem" 91 | } 92 | 93 | daq_chain_ini_cfg = {"meta" : meta, 94 | "hw" : hw, 95 | "daq" : daq, 96 | "squelch" : squelch, 97 | "pre_processing" : pre_processing, 98 | "calibration" : calibration, 99 | "adpis" : adpis, 100 | "data_interface" : data_interface 101 | } 102 | 103 | 104 | """ 105 | 106 | Calculate signal specific parameters 107 | 108 | """ 109 | parser = argparse.ArgumentParser(description='Automatic configuration file generation for burst type signals') 110 | 111 | parser.add_argument('--bri', type=float, nargs=1, required=True, 112 | help='Burst repetition interval in miliseconds [ms]') 113 | 114 | parser.add_argument('--burst_length', type=float, nargs=1, required=True, 115 | help='Burst length in miliseconds [ms]') 116 | 117 | parser.add_argument('--bw', type=float, nargs=1, required=True, 118 | help='Signal bandwidth in kilohertz [kHz]') 119 | 120 | args =vars( parser.parse_args()) 121 | 122 | # ----------------> MANDATORY PARAMETERS <---------------- 123 | BRI = args['bri'][0] # burst repetition interval [ms] 124 | burst_length = args['burst_length'][0]# [ms] 125 | bw = args['bw'][0] # signal bandwidth [kHz] 126 | # ----------------> Extracted from CLI <---------------- 127 | 128 | minimum_FIR_tap_size = 16 129 | logging.info("Preparing config file..") 130 | cfg_fname="autogen.ini" 131 | 132 | fs = 2400000 # Set sampling rate to maximum to be able to increase the ENOBs as much as possible 133 | decimation_ratio = int(fs/(bw*10**3)) 134 | fir_tap_size = 2**(int(np.log2(decimation_ratio))+1) 135 | if fir_tap_size < minimum_FIR_tap_size: fir_tap_size=minimum_FIR_tap_size 136 | fs_dec = fs/decimation_ratio # Decimated sample rate 137 | cpi_size = int(burst_length*10**-3 * fs_dec) 138 | 139 | corr_size = 2**int(np.log2(cpi_size)) 140 | 141 | daq_chain_ini_cfg['daq']['sample_rate'] = str(fs) 142 | daq_chain_ini_cfg['pre_processing']['decimation_ratio'] = str(decimation_ratio) 143 | daq_chain_ini_cfg['pre_processing']['fir_tap_size'] = str(fir_tap_size) 144 | daq_chain_ini_cfg['pre_processing']['cpi_size'] = str(cpi_size) 145 | 146 | daq_chain_ini_cfg['calibration']['corr_size'] = str(corr_size) 147 | 148 | logging.info("Decimation ratio :{:d}".format(decimation_ratio)) 149 | logging.info("FIR filter tap size :{:d}".format(fir_tap_size)) 150 | logging.info("CPI length :{:.2f} ms - {:d} sample".format(cpi_size*decimation_ratio/fs*10**3, cpi_size)) 151 | """ 152 | 153 | Check and write config file 154 | 155 | """ 156 | 157 | error_list = ini_checker.check_ini(daq_chain_ini_cfg, en_hw_check=False) 158 | if len(error_list): 159 | for e in error_list: 160 | logging.error(e) 161 | logging.critical("Config file generation failed") 162 | 163 | else: 164 | parser = ConfigParser() 165 | parser.read_dict(daq_chain_ini_cfg) 166 | with open(cfg_fname, 'w') as configfile: 167 | parser.write(configfile) 168 | logging.info("Config file writen to :{0}".format(cfg_fname)) 169 | -------------------------------------------------------------------------------- /util/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Installing dependencies and build HeIMDALL DAQ Firmware" 3 | cd .. 4 | cd .. 5 | sudo apt install git 6 | echo "6/1 Install build dependencies for the realtek driver" 7 | sudo apt install cmake 8 | sudo apt install libusb-1.0-0-dev 9 | echo "6/2 Build and install rtl-sdr driver" 10 | git clone https://github.com/krakenrf/librtlsdr 11 | 12 | cd librtlsdr 13 | mkdir build 14 | cd build 15 | cmake ../ -DINSTALL_UDEV_RULES=ON 16 | make 17 | sudo make install 18 | sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ 19 | sudo ldconfig 20 | cd .. 21 | cd .. 22 | 23 | echo "6/3 Disable built-in rtl-sdr driver" 24 | echo 'blacklist dvb_usb_rtl28xxu' | sudo tee --append /etc/modprobe.d/blacklist-dvb_usb_rtl28xxu.conf 25 | echo "6/4 Install SIMD FIR filter DSP library" 26 | 27 | HOST_ARCH=$(uname -m) 28 | if [ "$HOST_ARCH" = "x86_64" ]; then 29 | echo "X86 64 platform." 30 | elif [ "$HOST_ARCH" = "armv7l" ]; then 31 | git clone https://github.com/projectNe10/Ne10 32 | cd Ne10 33 | mkdir build 34 | cd build 35 | export NE10_LINUX_TARGET_ARCH=armv7 36 | cmake -DGNULINUX_PLATFORM=ON .. 37 | make 38 | cp modules/libNE10.a ../../heimdall_daq_fw/Firmware/_daq_core 39 | cd .. 40 | cd .. 41 | else 42 | echo "Architecture not recognized!" 43 | exit 44 | fi 45 | echo "6/5 Install the required python3 packages" 46 | sudo apt install python3-pip 47 | sudo python3 -m pip install numpy 48 | sudo python3 -m pip install configparser 49 | sudo apt-get install libatlas-base-dev gfortran 50 | sudo python3 -m pip install scipy 51 | sudo python3 -m pip install pyzmq 52 | sudo python3 -m pip install scikit-rf 53 | # For testing 54 | sudo python3 -m pip install plotly 55 | 56 | 57 | sudo apt install libzmq3-dev -y 58 | echo "6/6 Build HeIMDALL DAQ Firmware" 59 | cd heimdall_daq_fw/Firmware/_daq_core 60 | make 61 | 62 | # TODO: Check installed versions: 63 | # Scipy: 1.8 or later -------------------------------------------------------------------------------- /util/kerberos_eeprom_init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #NOTE: Use for KerberosSDR Only 3 | 4 | echo "This software will initialze KerberosSDR EEPROM content and set serial numbers" 5 | device_count=$(lsusb | grep "Realtek" | wc -l) 6 | echo "Found $device_count receivers" 7 | while true; do 8 | read -p "Do you wish to overwrite the current EEPROM content?" yn 9 | case $yn in 10 | [Yy]* )echo "yes"; break;; 11 | [Nn]* )echo "no"; exit;; 12 | *) echo "Please answer yes or no" 13 | esac 14 | done 15 | device_cntr=$((device_count-1)) 16 | for i in $(eval echo "{0..$device_cntr}") 17 | do 18 | dip_sw_count=$((i+1)) 19 | read -p "Please turn off all DIP switches except #$dip_sw_count and press enter" dummy 20 | # Check the number of online devices 21 | curr_device_count=$(lsusb | grep "Realtek" | wc -l) 22 | if test $curr_device_count -ne 1 23 | then 24 | echo "More than one online device has been detected, exiting" 25 | #exit 26 | fi 27 | serial=$((i+1000)) 28 | rtl_eeprom -d 0 -g realtek_oem 29 | rtl_eeprom -d 0 -s $serial -m RTL-SDR -p KerberosSDR 30 | done 31 | echo "EEPROM writing script finished. Plese perform a full power cycle." 32 | --------------------------------------------------------------------------------