From faff4eec76eb8ccb0671a5a00abaae5a5a186a1d Mon Sep 17 00:00:00 2001 From: Marek Blok <Marek.Blok@pg.edu.pl> Date: Wed, 15 Sep 2021 12:46:49 +0200 Subject: [PATCH] Added Ex6 --- .gitignore | 15 + Ex3/.vscode/launch.json | 2 +- Ex4/.vscode/launch.json | 2 +- Ex5/.vscode/launch.json | 2 +- Ex6/.vscode/c_cpp_properties.json | 65 ++++ Ex6/.vscode/launch.json | 29 ++ Ex6/.vscode/settings.json | 8 + Ex6/.vscode/tasks.json | 70 ++++ Ex6/Ex6_task1.cbp | 38 ++ Ex6/Ex6_task1.cpp | 254 ++++++++++++ Ex6/Ex6_task1b.cbp | 38 ++ Ex6/Ex6_task1b.cpp | 239 ++++++++++++ Ex6/Ex6_task2.cbp | 38 ++ Ex6/Ex6_task2.cpp | 270 +++++++++++++ Ex6/Ex6_task2_test.cbp | 38 ++ Ex6/Ex6_task2_test.cpp | 74 ++++ Ex6/Makefile | 85 +++++ Ex6/Makefile.main | 60 +++ Ex6/SymbolMapper_test.cbp | 38 ++ Ex6/SymbolMapper_test.cpp | 229 +++++++++++ Ex6/docs/Notes.docx | Bin 0 -> 54160 bytes Ex6/matlab - test/eyediagram2.m | 69 ++++ Ex6/matlab - test/load_filter_coef.m | 107 ++++++ Ex6/matlab - test/readaudiofile.m | 215 +++++++++++ Ex6/matlab - test/test_symbol_mapper.m | 134 +++++++ Ex6/matlab - test/test_task1.m | 143 +++++++ Ex6/matlab - test/test_task1_signal.m | 304 +++++++++++++++ Ex6/matlab - test/test_task1b_new.m | 84 ++++ Ex6/matlab - test/test_task2.m | 170 +++++++++ Ex6/matlab - test/test_task2_constelation.m | 115 ++++++ Ex6/matlab - test/trajekt_.m | 403 ++++++++++++++++++++ Ex6/matlab/design_task_1.m | 42 ++ Ex6/matlab/ex6_task1_h_rc.coef | Bin 0 -> 1290 bytes Ex6/matlab/eyediagram2.m | 69 ++++ Ex6/matlab/load_filter_coef.m | 107 ++++++ Ex6/matlab/rcos4.m | 51 +++ Ex6/matlab/readaudiofile.m | 215 +++++++++++ Ex6/matlab/save_filter_coef.m | 226 +++++++++++ Ex6/matlab/test.m | 57 +++ Ex6/matlab/test_DBPSK.m | 32 ++ Ex6/matlab/test_FMD_signal.m | 72 ++++ Ex6/matlab/test_task2.m | 116 ++++++ Ex6/matlab/trajekt_.m | 403 ++++++++++++++++++++ Ex6/rundot.bat | 10 + Ex6/rundot_test.bat | 11 + 45 files changed, 4746 insertions(+), 3 deletions(-) create mode 100644 Ex6/.vscode/c_cpp_properties.json create mode 100644 Ex6/.vscode/launch.json create mode 100644 Ex6/.vscode/settings.json create mode 100644 Ex6/.vscode/tasks.json create mode 100644 Ex6/Ex6_task1.cbp create mode 100644 Ex6/Ex6_task1.cpp create mode 100644 Ex6/Ex6_task1b.cbp create mode 100644 Ex6/Ex6_task1b.cpp create mode 100644 Ex6/Ex6_task2.cbp create mode 100644 Ex6/Ex6_task2.cpp create mode 100644 Ex6/Ex6_task2_test.cbp create mode 100644 Ex6/Ex6_task2_test.cpp create mode 100644 Ex6/Makefile create mode 100644 Ex6/Makefile.main create mode 100644 Ex6/SymbolMapper_test.cbp create mode 100644 Ex6/SymbolMapper_test.cpp create mode 100644 Ex6/docs/Notes.docx create mode 100644 Ex6/matlab - test/eyediagram2.m create mode 100644 Ex6/matlab - test/load_filter_coef.m create mode 100644 Ex6/matlab - test/readaudiofile.m create mode 100644 Ex6/matlab - test/test_symbol_mapper.m create mode 100644 Ex6/matlab - test/test_task1.m create mode 100644 Ex6/matlab - test/test_task1_signal.m create mode 100644 Ex6/matlab - test/test_task1b_new.m create mode 100644 Ex6/matlab - test/test_task2.m create mode 100644 Ex6/matlab - test/test_task2_constelation.m create mode 100644 Ex6/matlab - test/trajekt_.m create mode 100644 Ex6/matlab/design_task_1.m create mode 100644 Ex6/matlab/ex6_task1_h_rc.coef create mode 100644 Ex6/matlab/eyediagram2.m create mode 100644 Ex6/matlab/load_filter_coef.m create mode 100644 Ex6/matlab/rcos4.m create mode 100644 Ex6/matlab/readaudiofile.m create mode 100644 Ex6/matlab/save_filter_coef.m create mode 100644 Ex6/matlab/test.m create mode 100644 Ex6/matlab/test_DBPSK.m create mode 100644 Ex6/matlab/test_FMD_signal.m create mode 100644 Ex6/matlab/test_task2.m create mode 100644 Ex6/matlab/trajekt_.m create mode 100644 Ex6/rundot.bat create mode 100644 Ex6/rundot_test.bat diff --git a/.gitignore b/.gitignore index c53779c..c1f963e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,18 @@ Ex5/matlab/ex5_task1a.flt Ex5/matlab/ex5_task1.flt Ex5/ex5_task1.dot~ Ex5/matlab/ex5_task1.wav + +Ex6/out_win_dbg +Ex6/out_win_rls +Ex6/out_linux_dbg +Ex6/out_linux_rls +Ex6/ex6_task1_ch1.flt +Ex6/Ex6_task1_dbg.exe +Ex6/Ex6_task1.dot +Ex6/ex6_task1.flt +Ex6/ex6_task1.wav +Ex6/Ex6_task1b_dbg.exe +Ex6/Ex6_task2_dbg.exe +Ex6/Ex6_task2_test_dbg.exe +Ex6/log_file.txt +Ex6/SymbolMapper_test_dbg.exe diff --git a/Ex3/.vscode/launch.json b/Ex3/.vscode/launch.json index 3ebdbd9..38fb3bb 100644 --- a/Ex3/.vscode/launch.json +++ b/Ex3/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Ex3 â widnows debug run ", + "name": "(gdb) Ex3 â windows debug run ", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/Ex3_task3_dbg.exe", diff --git a/Ex4/.vscode/launch.json b/Ex4/.vscode/launch.json index 6d8497a..9fbccc5 100644 --- a/Ex4/.vscode/launch.json +++ b/Ex4/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Ex4 â widnows debug run ", + "name": "(gdb) Ex4 â windows debug run ", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/Ex4_task2_dbg.exe", diff --git a/Ex5/.vscode/launch.json b/Ex5/.vscode/launch.json index 244c244..22536e7 100644 --- a/Ex5/.vscode/launch.json +++ b/Ex5/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Ex5 â widnows debug run ", + "name": "(gdb) Ex5 â windows debug run ", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/Ex5_task1_dbg.exe", diff --git a/Ex6/.vscode/c_cpp_properties.json b/Ex6/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..11c576f --- /dev/null +++ b/Ex6/.vscode/c_cpp_properties.json @@ -0,0 +1,65 @@ +{ + "configurations": [ + { + "name": "Windows-Release", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../DSPE_lib_minGW/MinGW-W64_8.1.0/include/**" + ], + "defines": [ + "WIN32", + "__DEBUG__=0" + ], + //"compilerPath": "gcc.exe", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + }, + { + "name": "Linux-Release", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../DSPE_lib_minGW/MinGW-W64_8.1.0/include/**" + ], + "defines": [ + "WIN32", + "__DEBUG__=0" + ], + "compilerPath": "gcc.exe", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + }, + { + "name": "Windows-Debug", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../DSPE_lib_minGW/MinGW-W64_8.1.0/include/**" + ], + "defines": [ + "WIN32", + "__DEBUG__=1" + ], + //"compilerPath": "gcc.exe", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + }, + { + "name": "Linux-Debug", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../DSPE_lib_minGW/MinGW-W64_8.1.0/include/**" + ], + "defines": [ + "WIN32", + "__DEBUG__=1" + ], + "compilerPath": "gcc.exe", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + } +], + "version": 4 +} \ No newline at end of file diff --git a/Ex6/.vscode/launch.json b/Ex6/.vscode/launch.json new file mode 100644 index 0000000..8e2a1d7 --- /dev/null +++ b/Ex6/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Ex6 â windows debug run ", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/Ex6_task1_dbg.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", +// "miDebuggerPath": "d:/CodeBlocks_20_03/MinGW/bin/gdb.exe", + "setupCommands": [ + { + "description": "WĹÄ cz formatowanie kodu dla gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build Ex6_task1.cpp" + } + ] +} \ No newline at end of file diff --git a/Ex6/.vscode/settings.json b/Ex6/.vscode/settings.json new file mode 100644 index 0000000..7539f57 --- /dev/null +++ b/Ex6/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "Makefile": "makefile", + "ostream": "cpp", + "memory": "cpp", + "vector": "cpp" + } +} \ No newline at end of file diff --git a/Ex6/.vscode/tasks.json b/Ex6/.vscode/tasks.json new file mode 100644 index 0000000..d6a8e82 --- /dev/null +++ b/Ex6/.vscode/tasks.json @@ -0,0 +1,70 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + // https://code.visualstudio.com/docs/editor/tasks + // ${command:cpptools.activeConfigName} + "tasks": [ + { + "label": "Build Ex6_task1.cpp", + "type": "shell", + "command": "make build -f Makefile.main FILE=Ex6_task1 VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + }, + { + "label": "Build Ex6_task1b.cpp", + "type": "shell", + "command": "make build -f Makefile.main FILE=Ex6_task1b VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + }, + { + "label": "Build Ex6_task2.cpp", + "type": "shell", + "command": "make build -f Makefile.main FILE=Ex6_task2 VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + }, + { + "label": "Build Ex6_task2_test.cpp", + "type": "shell", + "command": "make build -f Makefile.main FILE=Ex6_task2_test VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + }, + { + "label": "Build SymbolMapper_test.cpp", + "type": "shell", + "command": "make build -f Makefile.main FILE=SymbolMapper_test VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + }, + { + "label": "Clean Ex6", + "type": "shell", + "command": "make clean -f Makefile.main VS_CFG=${command:cpptools.activeConfigName}", + "group": "build", + "presentation": { + "echo": true + }, + "problemMatcher": "$gcc" + } + + ] +} \ No newline at end of file diff --git a/Ex6/Ex6_task1.cbp b/Ex6/Ex6_task1.cbp new file mode 100644 index 0000000..fd1105b --- /dev/null +++ b/Ex6/Ex6_task1.cbp @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="Ex6_task1" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="Ex6_task1" prefix_auto="1" extension_auto="1" /> + <Option type="1" /> + <Option compiler="gcc" /> + </Target> + </Build> + <Compiler> + <Add option="-std=c++0x" /> + <Add option="-m32" /> + <Add option="-g" /> + <Add option="-DWIN32" /> + <Add directory="../DSPE_lib_minGW/MinGW-W64_8.1.0/include" /> + <Add directory="../DSPE_lib_minGW/MinGW-W64_8.1.0/include/dbg" /> + </Compiler> + <Linker> + <Add option="-static-libgcc" /> + <Add option="-m32" /> + <Add library="DSPE" /> + <Add library="winmm" /> + <Add directory="../DSPE_lib_minGW/MinGW-W64_8.1.0/dbg" /> + </Linker> + <Unit filename="Ex6_task1.cpp" /> + <Extensions> + <code_completion /> + <envvars /> + <debugger /> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/Ex6/Ex6_task1.cpp b/Ex6/Ex6_task1.cpp new file mode 100644 index 0000000..719b10e --- /dev/null +++ b/Ex6/Ex6_task1.cpp @@ -0,0 +1,254 @@ +/*! Laboratory: Advanced signal processing of digital telecommunications + * (Zaawansowane przetwarzanie sygnaĹĂłw telekomunikacji cyfrowej) + * Ex. 6. task 1. + * + * \author Marek Blok + * \date 2021.09.15 + */ +#include <DSP_lib.h> +#include <memory> + +int main(int argn, char *args[]) +{ + UNUSED_ARGUMENT(argn); + UNUSED_ARGUMENT(args); + + /*! Idea: + * - three channels are used - different modulations - the same shaping filter + * DPSK, pi/4-QPSK, (MSK), OQPSK, QAM-16, OOK + * - FFT - 32 ==> directly placing at intermediate frequency (oversampling 32) + * - available channels 1..15 (for FMT adjacent channels should not be used) + * using: 8, 10, 13 + * other channels are set to zero: SetConstInput + */ + /*************************************************************/ + // Log file setup + DSP::log.SetLogFileName("log_file.txt"); + DSP::log.SetLogState(DSP::e::LogState::file | DSP::e::LogState::console); + + DSP::log << DSP::lib_version_string() << std::endl << std::endl; + /*************************************************************/ + + long int Fp2, F_symb; + DSP::LoadCoef coef_info; + int N_rc; + unsigned int L; + DSP::Float_vector h_rc; + + coef_info.Open("ex6_task1_h_rc.coef", "matlab"); + N_rc = coef_info.GetSize(0); + if (N_rc < 1) + { + DSP::log << DSP::e::LogMode::Error << "No ex6_task1_h_rc.coef: aborting" << std::endl; + return -1; + } + else + { + coef_info.Load(h_rc); + F_symb = coef_info.Fp; + } + /*************************************************************/ + int bits_per_symbol = 2; + + DSP::Clock_ptr BitClock, SymbolClock, OutputClock; + SymbolClock = DSP::Clock::CreateMasterClock(); + BitClock = DSP::Clock::GetClock(SymbolClock, bits_per_symbol, 1); + + + //DSP::u::WaveInput AudioIn(MasterClock, "test.wav", "."); + //F_symb = AudioIn.GetSamplingRate(); + + + // szybkoĹÄ symbolowa + F_symb = 1500; + + // dĹugoĹÄ transformaty DFT / liczba podkanaĹĂłw + // jeĹźeli K jest caĹkowitÄ potÄgÄ dwĂłjki moĹźliwe jest uĹźycie FFT radix-2 +// int K = 16; + int K = 32; + L = K; + + // wyjĹciowa szybkosÄ prĂłbkowania + Fp2 = K*F_symb; + + + string tekst = "Fsymb = " + to_string(int(F_symb)) + ", Fp2 = " + to_string(int(Fp2)) + ", L = " + to_string(L); + DSP::log << tekst << std::endl; + + OutputClock=DSP::Clock::GetClock(SymbolClock, K, 1); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // Pierwszy kanaĹ danych + DSP::u::FileInput BinData1(BitClock, "Ex6_task1.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData1.SetName("Ex6_task1.cpp (binary)"); + //DSP::u::PSKencoder PSKencoder1(DSP::e::PSK_type::QPSK_A); + DSP::u::Serial2Parallel SPconv1(BitClock, bits_per_symbol); + DSP::u::SymbolMapper PSKencoder1(DSP::e::ModulationType::PSK, bits_per_symbol, 0.0); // QPSK_A + PSKencoder1.SetName("QPSK_A"); + + // ====================================================================== // + DSP::u::FileOutput FileOutCh1("ex6_task1_ch1.flt", DSP::e::SampleType::ST_float, 2, DSP::e::FileType::FT_flt, F_symb); + FileOutCh1.SetName("ex6_task1_ch1.flt"); + + PSKencoder1.Output("out") >> FileOutCh1.Input("in"); + // ====================================================================== // + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // drugi kanaĹ danych + DSP::u::FileInput BinData2(BitClock, "Ex6_task2.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData2.SetName("Ex6_task2.cpp (binary)"); + //DSP::u::PSKencoder PSKencoder2(DSP::e::PSK_type::QPSK_B); + DSP::u::Serial2Parallel SPconv2(BitClock, bits_per_symbol); + DSP::u::SymbolMapper PSKencoder2(DSP::e::ModulationType::PSK, bits_per_symbol, DSP::M_PIf/4); // QPSK_B + PSKencoder2.SetName("QPSK_B"); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // trzeci kanaĹ danych + DSP::u::FileInput BinData3(BitClock, "Ex6_task1b.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData3.SetName("Ex6_task1b.cpp (binary)"); + //DSP::u::PSKencoder PSKencoder3(DSP::e::PSK_type::QPSK_A); + DSP::u::Serial2Parallel SPconv3(BitClock, bits_per_symbol); + DSP::u::SymbolMapper PSKencoder3(DSP::e::ModulationType::PSK, bits_per_symbol, 0.0); // QPSK_A + PSKencoder3.SetName("QPSK_A"); + + //BinData1.Output("out"), PSKencoder1.Input("in")); + BinData1.Output("out") >> SPconv1.Input("in"); + SPconv1.Output("out") >> PSKencoder1.Input("in"); + + //BinData2.Output("out"), PSKencoder2.Input("in")); + BinData2.Output("out") >> SPconv2.Input("in"); + SPconv2.Output("out") >> PSKencoder2.Input("in"); + + //BinData3.Output("out"), PSKencoder3.Input("in")); + BinData3.Output("out") >> SPconv3.Input("in"); + SPconv3.Output("out") >> PSKencoder3.Input("in"); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // FFT z filtrami polifazowymi (tylko dla uĹźywanych kanaĹĂłw) + + int channel1, channel2, channel3; + // numery aktywnych podkanaĹĂłw + // mumeracja podkanaĹĂłw: 0, 1, ..., K-1 + channel1 = 1; channel2 = 4; channel3 = 6; + + auto fft = make_shared<DSP::u::FFT>(K); + for (int ind = 0; ind < K; ind++) + { + if ((ind != channel1) && (ind != channel2) && (ind != channel3)) + { + // definiujemy wejĹcia o staĹej wartoĹci 0+j0 (wszystkie poza uĹźywanymi) + string text = "in" + to_string(ind+1); + fft->SetConstInput(text, 0.0, 0.0); + } + } + + // podĹÄ cz generatory wÄ skopasmowe ST + // kanaĹ nr 8 + tekst = "in" + to_string(channel1+1); + PSKencoder1.Output("out") >> fft->Input(tekst); + // kanaĹ nr 10 + tekst = "in" + to_string(channel2+1); + PSKencoder2.Output("out") >> fft->Input(tekst); + // kanaĹ nr 13 + tekst = "in" + to_string(channel3+1); + PSKencoder3.Output("out") >> fft->Input(tekst); + + // filtry polifazowe + vector<shared_ptr<DSP::u::FIR> > H_g(K, nullptr); + vector<shared_ptr<DSP::u::Zeroinserter> > ZeroIns(K, nullptr); + vector<shared_ptr<DSP::u::Delay> > Z1(K, nullptr); + vector<shared_ptr<DSP::u::Addition> > Add(K, nullptr); + for (int ind = 0; ind < K; ind++ ) + { + // Filtry polifazowe (tworzenie filtrĂłw zintegrowane z dekompozycjÄ polifazowÄ odpowiedzi impulsowej h_rc): + // Filtr o odpowiedzi impulsowej zĹoĹźonej z co K-tej prĂłbki + // odpowiedzi impulsowej h_rc zaczynajÄ c od prĂłbki (K-1)-ind. + H_g[ind] = make_shared<DSP::u::FIR>(true, h_rc, (K-1)-ind, K); + ZeroIns[ind] = make_shared<DSP::u::Zeroinserter>(true, SymbolClock, K); + + string text = "out" + to_string(ind+1); + fft->Output(text) >> H_g[ind]->Input("in"); + H_g[ind]->Output("out") >> ZeroIns[ind]->Input("in"); + } + + for (int ind = 0; ind <K-1; ind++) + { + Z1[ind] = make_shared<DSP::u::Delay>(1,2); // zespolony (!!! potem wariant rzeczywisty) + Z1[ind]->SetName("Z^-1",false); + Add[ind] = make_shared<DSP::u::Addition>(0,2); // zespolony (!!! potem wariant rzeczywisty) + } + + ZeroIns[0]->Output("out") >> Z1[0]->Input("in"); + for (int ind = 1; ind < K; ind++) + { + ZeroIns[ind]->Output("out") >> Add[ind-1]->Input("in1"); + Z1[ind-1]->Output("out") >> Add[ind-1]->Input("in2"); + + if (ind < K-1) + { + Add[ind-1]->Output("out") >> Z1[ind]->Input("in"); + } + } + + + // Output to the soundcard + DSP::u::AudioOutput SoundOut(Fp2, 1, 16); + // Output to the mono 16bit *.wav file + DSP::u::FileOutput FileOut2a("ex6_task1.wav", DSP::e::SampleType::ST_short, 2, DSP::e::FileType::FT_wav, Fp2); + FileOut2a.SetName("ex6_task1.wav"); + DSP::u::FileOutput FileOut2b("ex6_task1.flt", DSP::e::SampleType::ST_float, 2, DSP::e::FileType::FT_flt, Fp2); + FileOut2b.SetName("ex6_task1.flt"); + + Add[K-2]->Output("out.re") >> SoundOut.Input("in"); + Add[K-2]->Output("out") >> FileOut2a.Input("in"); + Add[K-2]->Output("out") >> FileOut2b.Input("in"); + + ///////////////////////////////// + // check if there are signals + // connected to all inputs + DSP::Component::CheckInputsOfAllComponents(); + + // *********************************** // + DSP::Clock::SchemeToDOTfile(SymbolClock, "Ex6_task1.dot"); + + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 5*Fp2 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(OutputClock, SamplesInSegment); + // ********************************************************** // + + /* + if (BinData1.GetBytesRead() > 0) + { + NoOfSamplesProcessed = 0; // Play the whole file + } + else // Play 200ms more + { + if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) + NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; + } + */ + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + } + + /*************************************************************/ + H_g.clear(); + ZeroIns.clear(); + Z1.clear(); + Add.clear(); + + fft.reset(); + + /*************************************************************/ + DSP::Clock::FreeClocks(); + + return 0; +} diff --git a/Ex6/Ex6_task1b.cbp b/Ex6/Ex6_task1b.cbp new file mode 100644 index 0000000..52c25ff --- /dev/null +++ b/Ex6/Ex6_task1b.cbp @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="ex6_task1b" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="ex6_task1b" prefix_auto="1" extension_auto="1" /> + <Option type="1" /> + <Option compiler="gcc" /> + </Target> + </Build> + <Compiler> + <Add option="-std=c++0x" /> + <Add option="-m32" /> + <Add option="-g" /> + <Add option="-DWIN32" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/include" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Compiler> + <Linker> + <Add option="-static-libgcc" /> + <Add option="-m32" /> + <Add library="DSPE" /> + <Add library="winmm" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Linker> + <Unit filename="Ex6_task1b.cpp" /> + <Extensions> + <code_completion /> + <envvars /> + <debugger /> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/Ex6/Ex6_task1b.cpp b/Ex6/Ex6_task1b.cpp new file mode 100644 index 0000000..017400f --- /dev/null +++ b/Ex6/Ex6_task1b.cpp @@ -0,0 +1,239 @@ +/*! Laboratory: Advanced signal processing of digital telecommunications + * (Zaawansowane przetwarzanie sygnaĹĂłw telekomunikacji cyfrowej) + * Ex. 6. task 1. variant b + * + * \author Marek Blok + * \date 2021.09.15 + */ +#include <DSP_lib.h> +#include <memory> + +DSP::Float_ptr read_buffer = NULL; +int buffer_size, read_buffer_pointer = -1; +int No_of_samples=0; + + +int main(int argn, char *args[]) +{ + UNUSED_ARGUMENT(argn); + UNUSED_ARGUMENT(args); + + /*! Idea: + * - three channels - different modulations - the same shaping filter + * DPSK, pi/4-QPSK, (MSK), OQPSK, QAM-16, OOK + * - FFT - 32 ==> direct generation on intermediate frequency (oversampling 32) + * - available channels 1..15 (for FMT adjacent channels should not be used) + * using: 8, 10, 13 + * other channels are set to zero: SetConstInput + */ + /*************************************************************/ + // Log file setup + DSP::log.SetLogFileName("log_file.txt"); + DSP::log.SetLogState(DSP::e::LogState::file | DSP::e::LogState::console); + + DSP::log << DSP::lib_version_string() << std::endl << std::endl; + /*************************************************************/ + + long int Fp2, F_symb; + DSP::LoadCoef coef_info; + int N_rc; + unsigned int L; + DSP::Float_vector h_rc; + + coef_info.Open("ex6_task1_h_rc.coef", "matlab"); + N_rc = coef_info.GetSize(0); + if (N_rc < 1) + { + DSP::log << DSP::e::LogMode::Error << "No ex6_task1_h_rc.coef: aboarding" << std::endl; + return -1; + } + else + { + coef_info.Load(h_rc); + F_symb = coef_info.Fp; + } + /*************************************************************/ + + DSP::Clock_ptr BitClock, SymbolClock, OutputClock; + SymbolClock = DSP::Clock::CreateMasterClock(); + + + //DSP::u::WaveInput AudioIn(MasterClock, "test.wav", "."); + //F_symb = AudioIn.GetSamplingRate(); + + // DFT length == numer of subchannels + // for K is and integer power of two the FFT radix-2 can be used + int K = 32; + //int K = 16; + L = K; + + // symbol rate + F_symb = 1500; // for L = 32 => Fp2 = 48000 + + + // output sample rate + Fp2 = K*F_symb; + + DSP::log << "Fsymb = " << int(F_symb) << ", Fp2 = " << int(Fp2) << ", L = " << L << std::endl; + + BitClock = DSP::Clock::GetClock(SymbolClock, 2, 1); + OutputClock = DSP::Clock::GetClock(SymbolClock, K, 1); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // First data channel + DSP::u::FileInput BinData1(BitClock, "Ex6_task1.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData1.SetName("Ex6_task1.cpp"); + //DSP::u::PSKencoder PSKencoder1(DSP::e::PSK_type::QPSK_A); + + DSP::u::FileOutput BinData_out1("ex6_task1_bin_a_new.flt", DSP::e::SampleType::ST_float, 1U, DSP::e::FileType::FT_flt, F_symb); + BinData_out1.SetName("ex6_task1_bin_a_new.flt"); + BinData1.Output("out") >> BinData_out1.Input("in"); + + DSP::u::Serial2Parallel SPconv1(BitClock, 2); + DSP::u::SymbolMapper PSKencoder1(DSP::e::ModulationType::PSK, 2, 0.0); // QPSK_A + + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // second data channel + DSP::u::FileInput BinData2(BitClock, "Ex6_task2.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData2.SetName("Ex6_task2.cpp"); + DSP::u::Serial2Parallel SPconv2(BitClock, 2); + DSP::u::SymbolMapper PSKencoder2(DSP::e::ModulationType::PSK, 2, DSP::M_PIf/4); // QPSK_B + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // third data channel + DSP::u::FileInput BinData3(BitClock, "Ex6_task1b.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + BinData3.SetName("Ex6_task1.cpp"); + DSP::u::Serial2Parallel SPconv3(BitClock, 2); + DSP::u::SymbolMapper PSKencoder3(DSP::e::ModulationType::PSK, 2, 0.0); // QPSK_A + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + BinData1.Output("out") >> SPconv1.Input("in"); + SPconv1.Output("out") >> PSKencoder1.Input("in"); + + BinData2.Output("out") >> SPconv2.Input("in"); + SPconv2.Output("out") >> PSKencoder2.Input("in"); + + BinData3.Output("out") >> SPconv3.Input("in"); + SPconv3.Output("out") >> PSKencoder3.Input("in"); + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // FFT with polyphase filters (only for used channels) + + int channel1, channel2, channel3; + // subchannels numbering convention 0, 1, ..., K-1 + channel1 = 1; channel2 = 4; channel3 = 6; + + auto fft = make_shared<DSP::u::FFT>(K); + for (int ind = 0; ind < K; ind++) + { + if ((ind != channel1) && (ind != channel2) && (ind != channel3)) + { + string text = "in" + to_string(ind+1); + fft->SetConstInput(text, 0.0, 0.0); + } + } + + // connect narrowband single tone generators (ST) + // channel no 8 + std::string tekst; + + tekst = "in" + to_string(channel1+1); + PSKencoder1.Output("out") >> fft->Input(tekst); + DSP::u::FileOutput SymbData1("ex6_task1a_new.flt", DSP::e::SampleType::ST_float, 2U, DSP::e::FileType::FT_flt, F_symb); + SymbData1.SetName("ex6_task1a_new.flt"); + PSKencoder1.Output("out") >> SymbData1.Input("in"); + + + // channel no 10 + tekst = "in" + to_string(channel2+1); + PSKencoder2.Output("out") >> fft->Input(tekst); + // channel no 13 + tekst = "in" + to_string(channel3+1); + PSKencoder3.Output("out") >> fft->Input(tekst); + + // polyphase filters + vector<shared_ptr<DSP::u::FIR> > H_g(K, nullptr); + + + // *************************************************** // + DSP::u::Parallel2Serial OutputBuffer(SymbolClock, K, 2, true); + + for (int ind = 0; ind < K; ind++) + { + // Polyphase filters (joint filters' creation and polyphase decomposition of impulse response h_rc): + // Filter with impulse response composed of every K-th sample + // of impulse response h_rc starting from (K-1)-th sample. + H_g[ind] = make_shared<DSP::u::FIR>(true, h_rc, (K-1)-ind, K); + + tekst = "out" + to_string(ind+1); + fft->Output(tekst) >> H_g[ind]->Input("in"); + +// tekst = "in" + to_string(2*ind+1); +// H_g[ind]->Output("out.re") >> OutputBuffer.Input(tekst); +// tekst = "in" + to_string(2*ind+2); +// H_g[ind]->Output("out.im") >> OutputBuffer.Input(tekst); + tekst = "in" + to_string(ind+1); + H_g[ind]->Output("out") >> OutputBuffer.Input(tekst); + } + + + // Output to the soundcard + DSP::u::AudioOutput SoundOut(Fp2, 1, 16); + // Output to the mono 16bit *.wav file + DSP::u::FileOutput FileOut2a("ex6_task1b.wav", DSP::e::SampleType::ST_short, 2, DSP::e::FileType::FT_wav, Fp2); + FileOut2a.SetName("ex6_task1b.wav"); + DSP::u::FileOutput FileOut2b("ex6_task1b.flt", DSP::e::SampleType::ST_float, 2, DSP::e::FileType::FT_flt, Fp2); + FileOut2b.SetName("ex6_task1b.flt"); + + OutputBuffer.Output("out1") >> SoundOut.Input("in"); + OutputBuffer.Output("out") >> FileOut2a.Input("in"); + OutputBuffer.Output("out") >> FileOut2b.Input("in"); + + ///////////////////////////////// + // check if there are signals + // connected to all inputs + DSP::Component::CheckInputsOfAllComponents(); + + // *********************************** // + DSP::Clock::SchemeToDOTfile(SymbolClock, "ex6_task1b.dot"); + + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 5*Fp2 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(OutputClock, SamplesInSegment); + // ********************************************************** // + + /* + if (BinData1.GetBytesRead() > 0) + { + NoOfSamplesProcessed = 0; // Play the whole file + } + else // Play 200ms more + { + if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) + NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; + } + */ + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + + //sprintf(tekst, "NoOfSamplesProcessed = %i", int(NoOfSamplesProcessed)); + //DSPf_InfoMessage(tekst); + } + + /*************************************************************/ + H_g.clear(); + fft.reset(); + + /*************************************************************/ + DSP::Clock::FreeClocks(); + + return 0; +} diff --git a/Ex6/Ex6_task2.cbp b/Ex6/Ex6_task2.cbp new file mode 100644 index 0000000..9916a6d --- /dev/null +++ b/Ex6/Ex6_task2.cbp @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="ex6_task2" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="ex6_task2" prefix_auto="1" extension_auto="1" /> + <Option type="1" /> + <Option compiler="gcc" /> + </Target> + </Build> + <Compiler> + <Add option="-std=c++0x" /> + <Add option="-m32" /> + <Add option="-g" /> + <Add option="-DWIN32" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/include" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Compiler> + <Linker> + <Add option="-static-libgcc" /> + <Add option="-m32" /> + <Add library="DSPE" /> + <Add library="winmm" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Linker> + <Unit filename="Ex6_task2.cpp" /> + <Extensions> + <code_completion /> + <envvars /> + <debugger /> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/Ex6/Ex6_task2.cpp b/Ex6/Ex6_task2.cpp new file mode 100644 index 0000000..27ef822 --- /dev/null +++ b/Ex6/Ex6_task2.cpp @@ -0,0 +1,270 @@ +/*! Laboratorium: Zaawansowane przetwarzanie sygna��w telekomunikacji cyfrowej + * ďż˝w. 6. zad. 2. + * + * \author Marek Blok + * \date 2018.03.16 + */ + +#include <sstream> +using namespace std; + +#include <DSP_lib.h> +#include <memory> + +DSP::Float_ptr read_buffer = NULL; +int buffer_size; +int No_of_samples=0; +// //int K = 16; int M = 4; // Oversampling ratio +//int K = 32; int M = 8; // Oversampling ratio : K/M +int K = 32; int M = 1; // Oversampling ratio : K/M + + +int main(int argn, char *args[]) +{ + /*! Koncepcja + * - cztery kanaďż˝y - rďż˝ne modulacje - ten sam filtr ksztaďż˝tujďż˝cy + * DPSK, pi/4-QPSK, (MSK), OQPSK, QAM-16, OOK + * - FFT - 16 ==> od razu lokowanie na PCz (nadporďż˝bkowanie 16) + * - dostďż˝pne kanaďż˝y 1..7 (nie moďż˝na uďż˝yďż˝ dla FMT dwďż˝ch sďż˝siednich ze wzglďż˝du na ICI) + * uďż˝ycie: 2, 5, 7 + * reszta staďż˝e zero !!! SetConstInput (chyba powinno dziaďż˝aďż˝) + */ + /*************************************************************/ + // Log file setup + DSP::log.SetLogFileName("log_file.txt"); + DSP::log.SetLogState(DSP::e::LogState::file | DSP::e::LogState::console); + + DSP::log << DSP::lib_version_string() << std::endl << std::endl; + /*************************************************************/ + + long int Fp2, F_symb; + DSP::LoadCoef coef_info; + int N_rc; + //unsigned int L; + DSP::Float_vector h_rc; + string fft_output_name; + string tekst, tekst2; + + map<string,shared_ptr<DSP::Component> > blocks; + + coef_info.Open("ex6_task1_h_rc.coef", "matlab"); + N_rc = coef_info.GetSize(0); + if (N_rc < 1) + { + DSP::log << DSP::e::LogMode::Error << "No ex6_task1_h_rc.coef: aboarding" << std::endl; + return -1; + } + else + { + coef_info.Load(h_rc); + for (auto n=0U; n<h_rc.size(); n++){ + h_rc[n] *= K; + } + F_symb = coef_info.Fp; + } + /*************************************************************/ + + DSP::Clock_ptr SymbolClock, InputClock; + InputClock=DSP::Clock::CreateMasterClock(); + + + //DSP::u::WaveInput AudioIn(MasterClock, "test.wav", "."); + //F_symb = AudioIn.GetSamplingRate(); + + + F_symb = 1500; // dla L = 32 => Fp2 = 48000 + Fp2 = K*F_symb; + + DSP::log << "Fsymb = " << int(F_symb) << ", Fp2 = " << int(Fp2) << ", K = " << K << ", M = " << M << std::endl; + + SymbolClock=DSP::Clock::GetClock(InputClock, 1, K/M); + + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // Multichannel input signal (for complex valued signal (2U)) + string input_filename; + + char mode = 'A'; + if (argn == 2) { + mode = args[1][0]; + } + unsigned int input_delay = 1U; + // 1 symbol delay at on the transmitting side - because of FFT + // 2 bit delay after decoder + // previous: SetSkip for 10 dibits + // new: 10 dibits + 1 dibit (additional symbol from Serial2Parallel converter located before DSP::u::SymbolMapper) + unsigned int bin_data_skip = (2*(1+ 2+8)); + + switch (mode) { + case 'A': + input_filename = "ex6_task1.flt"; + break; + case 'B': + input_filename = "ex6_task1b.flt"; + // skip additional symbol (compensate for the DSP::u::Parallel2Serial in the modulator) + input_delay += K; + bin_data_skip += 2*1; // skip additional dibit + break; + default: + DSP::log << DSP::e::LogMode::Error << "Unsupported mode" << std::endl; + return -1; + break; + } + blocks["InputSignal"] = make_shared<DSP::u::FileInput>(InputClock, input_filename, 2U, DSP::e::SampleType::ST_float, DSP::e::FileType::FT_flt); + blocks["InputSignal"]->SetName(input_filename); + blocks["DumpImag"] = make_shared<DSP::u::Vacuum>(); + + // "Frame"/ FFT symbol synchronization + blocks["SymbolTimigDelay"] = shared_ptr<DSP::Block>(new DSP::u::Delay (input_delay)); + + blocks["InputSignal"]->Output("out.re") >> blocks["SymbolTimigDelay"]->Input("in"); + blocks["InputSignal"]->Output("out.im") >> blocks["DumpImag"]->Input("in"); + + blocks["OutputBuffer"] = make_shared<DSP::u::Serial2Parallel>(InputClock, K,1); + blocks["SymbolTimigDelay"]->Output("out") >> blocks["OutputBuffer"]->Input("in"); + + blocks["fft"] = make_shared<DSP::u::FFT>(K, false); + // poliphase filters + //DSP::u::FIR *H_g[K]; + vector<shared_ptr<DSP::u::FIR> > H_g(K, nullptr); + + for (int ind = 0; ind < K; ind++) + { + H_g[ind] = make_shared<DSP::u::FIR>(h_rc, (K-1)-ind, K, M); + + tekst = "out" + to_string(ind+1); + tekst2 = "in" + to_string(ind+1); + blocks["OutputBuffer"]->Output(tekst) >> H_g[ind]->Input("in"); + H_g[ind]->Output("out") >> blocks["fft"]->Input(tekst2); + } + + // number of active subchannels + const int NoOfActiveChannels = 3; + + vector<shared_ptr<DSP::u::Vacuum> > Discard(K-NoOfActiveChannels, nullptr); + vector<shared_ptr<DSP::u::RawDecimator> > RawDec(NoOfActiveChannels, nullptr); + vector<shared_ptr<DSP::u::SymbolDemapper> > PSKdecoder(NoOfActiveChannels, nullptr); + vector<shared_ptr<DSP::u::Parallel2Serial> > PSconv(NoOfActiveChannels, nullptr); + vector<shared_ptr<DSP::u::FileOutput> > SymbData(NoOfActiveChannels, nullptr); + vector<shared_ptr<DSP::u::FileOutput> > BinData(NoOfActiveChannels, nullptr); + + // channel1 = 1; channel2 = 4; channel3 = 6; + // subchannels numbering convension: 0, 1, ..., K-1 + int channel_no[NoOfActiveChannels] = {1, 4, 6}; + + int current_discard_block_no = 0; + int active_channel_index = -1; + for (int ind = 0; ind < K; ind++) + { + active_channel_index = -1; + for (int ind2 = 0; ind2 < NoOfActiveChannels; ind2++) + { + if (ind == channel_no[ind2]) + { // memorise indexes of consecative active channels (indexing from zero) + active_channel_index = ind2; + } + } + + fft_output_name = "out" + to_string(ind+1); + if (active_channel_index == -1) + { // subchannel is not on the list of active channels + Discard[current_discard_block_no] = make_shared<DSP::u::Vacuum>(true); + //Discard[current_discard_block_no] = shared_ptr<DSP::u::Vacuum>(new DSP::u::Vacuum(true)); + blocks["fft"]->Output(fft_output_name) >> Discard[current_discard_block_no++]->Input("in"); + } + else + { // subchannel is on the list of active channels + stringstream filename; + +// PSKdecoder[active_channel_index] = make_shared<DSP::u::SymbolDemapper>(SymbolClock, DSP::e::ModulationType::PSK, 2, 0.0); //M_PIx1f/2); // QPSK_A + PSKdecoder[active_channel_index] = make_shared<DSP::u::SymbolDemapper>(DSP::e::ModulationType::PSK, 2, 0.0); // QPSK_A + PSconv[active_channel_index] = make_shared<DSP::u::Parallel2Serial>(SymbolClock, 2); + filename << "ex6_task2_symb_" << mode << "_" << char('a' + active_channel_index) << ".flt"; + SymbData[active_channel_index] = make_shared<DSP::u::FileOutput>(filename.str(), DSP::e::SampleType::ST_float, 2U, DSP::e::FileType::FT_flt, F_symb); + SymbData[active_channel_index]->SetName(filename.str()); + + filename.str(""); filename.clear(); + filename << "ex6_task2_bin_" << mode << "_" << char('a' + active_channel_index) << ".txt"; + BinData[active_channel_index] = make_shared<DSP::u::FileOutput>(filename.str(), DSP::e::SampleType::ST_bit, 1U, DSP::e::FileType::FT_raw); + //BinData[active_channel_index] = make_shared<DSP::u::FileOutput>(filename.str(), DSP::e::SampleType::ST_float, 1U, DSP::e::FileType::FT_raw); + BinData[active_channel_index]->SetName(filename.str()); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // dibit offset - byte start synchronization + BinData[active_channel_index]->SetSkip(bin_data_skip); + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + + // FFT with poliphase filters (only for used channels) + if (M > 1) + { + RawDec[active_channel_index] = make_shared<DSP::u::RawDecimator>(SymbolClock, M, 2U); + blocks["fft"]->Output(fft_output_name) >> RawDec[active_channel_index]->Input("in"); + RawDec[active_channel_index]->Output("out") >> SymbData[active_channel_index]->Input("in"); + RawDec[active_channel_index]->Output("out") >> PSKdecoder[active_channel_index]->Input("in"); + } + else + { + RawDec[active_channel_index] = NULL; + blocks["fft"]->Output(fft_output_name) >> SymbData[active_channel_index]->Input("in"); + blocks["fft"]->Output(fft_output_name) >> PSKdecoder[active_channel_index]->Input("in"); + } + PSKdecoder[active_channel_index]->Output("out") >> PSconv[active_channel_index]->Input("in"); + PSconv[active_channel_index]->Output("out") >> BinData[active_channel_index]->Input("in"); + } + } + + + + // *********************************** // + DSP::Clock::SchemeToDOTfile(SymbolClock, "ex6_task2.dot"); + ///////////////////////////////// + // check if there are signals + // connected to all inputs + DSP::Component::CheckInputsOfAllComponents(); + + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 50000 //1*Fp2 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(InputClock, SamplesInSegment); + // ********************************************************** // + + /* + if (BinData1.GetBytesRead() > 0) + { + NoOfSamplesProcessed = 0; // Play the whole file + } + else // Play 200ms more + { + if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) + NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; + } + */ + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + + //sprintf(tekst, "NoOfSamplesProcessed = %i", int(NoOfSamplesProcessed)); + //DSPf_InfoMessage(tekst); + } + + /*************************************************************/ + H_g.clear(); + blocks.clear(); + Discard.clear(); + PSKdecoder.clear(); + RawDec.clear(); + SymbData.clear(); + BinData.clear(); + + DSP::Clock::ListComponents(); + /*************************************************************/ + DSP::Clock::FreeClocks(); + + return 0; +} diff --git a/Ex6/Ex6_task2_test.cbp b/Ex6/Ex6_task2_test.cbp new file mode 100644 index 0000000..126396b --- /dev/null +++ b/Ex6/Ex6_task2_test.cbp @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="ex6_task2_test" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="ex6_task2_test" prefix_auto="1" extension_auto="1" /> + <Option type="1" /> + <Option compiler="gcc" /> + </Target> + </Build> + <Compiler> + <Add option="-std=c++0x" /> + <Add option="-m32" /> + <Add option="-g" /> + <Add option="-DWIN32" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/include" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Compiler> + <Linker> + <Add option="-static-libgcc" /> + <Add option="-m32" /> + <Add library="DSPE" /> + <Add library="winmm" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Linker> + <Unit filename="Ex6_task2_test.cpp" /> + <Extensions> + <code_completion /> + <envvars /> + <debugger /> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/Ex6/Ex6_task2_test.cpp b/Ex6/Ex6_task2_test.cpp new file mode 100644 index 0000000..9966a29 --- /dev/null +++ b/Ex6/Ex6_task2_test.cpp @@ -0,0 +1,74 @@ +#include <DSP_lib.h> + +int main(void) +{ + DSP::Clock_ptr SymbolClock; + SymbolClock=DSP::Clock::CreateMasterClock(); + + // Tested: DSP_BPSK, DSP_DBPSK, DSP_DEBPSK + DSP::u::FileInput BinData1(SymbolClock, "Ex6_task1.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + DSP::u::PSKencoder PSKencoder1(DSP::e::PSK_type::DEBPSK); + DSP::u::PSKdecoder PSKdecoder1(DSP::e::PSK_type::DEBPSK); + DSP::u::Amplifier PhaseRot(DSP::Complex(0.1,-0.8), 1, true); + DSP::u::FileOutput BinData_out("ex6_task2_text1.dat", DSP::e::SampleType::ST_bit, 1U, DSP::e::FileType::FT_raw); + + DSP::u::FileOutput Data_in("ex6_task2_text_in.flt", DSP::e::SampleType::ST_float, 1U, DSP::e::FileType::FT_flt); +BinData1.Output("out") >> Data_in.Input("in"); + DSP::u::FileOutput Data_en("ex6_task2_text_en.flt", DSP::e::SampleType::ST_float, 2U, DSP::e::FileType::FT_flt); +PhaseRot.Output("out") >> Data_en.Input("in"); + DSP::u::FileOutput Data_de("ex6_task2_text_de.flt", DSP::e::SampleType::ST_float, 1U, DSP::e::FileType::FT_flt); +PSKdecoder1.Output("out") >> Data_de.Input("in"); + +BinData1.Output("out") >> PSKencoder1.Input("in"); +PSKencoder1.Output("out") >> PhaseRot.Input("in"); +PhaseRot.Output("out") >> PSKdecoder1.Input("in"); +PSKdecoder1.Output("out") >> BinData_out.Input("in"); + + + // Tested: DSP::e::PSK_type::QPSK_A, DSP::e::PSK_type::QPSK_B + DSP::u::FileInput BinData2(SymbolClock, "Ex6_task1.cpp", 2U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + DSP::u::PSKencoder PSKencoder2(DSP::e::PSK_type::QPSK_A); + DSP::u::PSKdecoder PSKdecoder2(DSP::e::PSK_type::QPSK_A); + DSP::u::FileOutput BinData_out2("ex6_task2_text2.dat", DSP::e::SampleType::ST_bit, 2U, DSP::e::FileType::FT_raw); + +BinData2.Output("out") >> PSKencoder2.Input("in"); +PSKencoder2.Output("out") >> PSKdecoder2.Input("in"); +PSKdecoder2.Output("out") >> BinData_out2.Input("in"); + +// *********************************** // + DSP::Clock::SchemeToDOTfile(SymbolClock, "ex6_task2_test.dot"); + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 1*1000 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(SymbolClock, SamplesInSegment); + // ********************************************************** // + + /* + if (BinData1.GetBytesRead() > 0) + { + NoOfSamplesProcessed = 0; // Play the whole file + } + else // Play 200ms more + { + if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) + NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; + } + */ + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + + //sprintf(tekst, "NoOfSamplesProcessed = %i", int(NoOfSamplesProcessed)); + //DSPf_InfoMessage(tekst); + } + + DSP::Clock::FreeClocks(); + + return 0; +} diff --git a/Ex6/Makefile b/Ex6/Makefile new file mode 100644 index 0000000..c94c79f --- /dev/null +++ b/Ex6/Makefile @@ -0,0 +1,85 @@ +# Run: make Release +# Run: make Debug +CC=g++ +# comflag = -m32 +# comflag = -m64 +comflag = $(COMFLAG) + +DSPElib_DIR = ../../_DSPE_lib_minGW_/$(DSPElib_SUBDIR) + +SRC_DIR = . +SRC_CPP_SUBDIR = . + +#DFLAGS = -DWIN32 -DDEVCPP + +# -D INCLUDE_DSPE_EXAMPLES # TODO: uĹźycie w ramach kompilacji Main.cpp w trybie DEBUG +ifeq ($(MODE),Release) + CFLAGS = $(comflag) -std=c++0x -O3 -Wall -c -fmessage-length=0 -fno-strict-aliasing + LINKER_FLAGS = $(comflag) -s -static-libgcc -static-libstdc++ $(MISC_LINKER_FLAGS) + INCLUDES := -I"$(DSPElib_DIR)/include" -I"$(DSPElib_DIR)/include/rls" + DSPElib_FULLDIR = $(DSPElib_DIR)/rls + EXE_FILENAME = $(CPP_FILENAME)_rls.exe +else + CFLAGS = $(comflag) -std=c++0x -O0 -g3 -Wall -c -fmessage-length=0 -W -Wshadow -Wconversion -fstrict-aliasing -fmax-errors=5 + LINKER_FLAGS = $(comflag) -static-libgcc -static-libstdc++ $(MISC_LINKER_FLAGS) + INCLUDES := -I"$(DSPElib_DIR)/include" -I"$(DSPElib_DIR)/include/dbg" + DSPElib_FULLDIR = $(DSPElib_DIR)/dbg + EXE_FILENAME = $(CPP_FILENAME)_dbg.exe +endif +# -U__STRICT_ANSI__ jest potrzebne do kompilacji debug_new.cpp, jezeli pominac ten plik to mozna rowniez wyrzucic te opcje +#CFLAGS_debug = $(comflag) -std=c++0x -O0 -g3 -Wall -c -fmessage-length=0 -W -Wshadow -Wco#nversion -fstrict-aliasing -U__STRICT_ANSI__ + +SOURCES_NAMES = +SOURCES_NAMES += $(CPP_FILENAME).cpp +SOURCES = $(addprefix $(SRC_CPP_SUBDIR)/,$(SOURCES_NAMES)) + +SOURCES_DBG = +# SOURCES_DBG += $(SRC_DIR)/Main.cpp + +# ################################################# # +# DEBUG +OBJECTS := $(SOURCES:%.cpp=$(OUT_DIR)/%.o) +DEPENDS := $(SOURCES:%.cpp=$(OUT_DIR)/%.d) + +# ################################################# # +-include $(DEPENDS) + +all: build + + +# ########################################################################################### # +# ########################################################################################### # +build: $(SRC_DIR)/$(EXE_FILENAME) + +$(SRC_DIR)/$(EXE_FILENAME): $(OBJECTS) + @echo $(EXE_FILENAME) + $(CC) -L$(DSPElib_FULLDIR) $(OBJECTS) -o"$(SRC_DIR)/$(EXE_FILENAME)" $(LINKER_FLAGS) -lDSPE $(LIBS) + +# ########################################################################################### # +# ########################################################################################### # +# Z podanej listy usuwany $(OUT_DIR_WIN_RLS)/ oraz '.o' zamieniamy na '.cpp' +$(OBJECTS): $(OUT_DIR)/%.o : %.cpp + @echo $(@D) $< $@ + + #mkdir -p $(OUT_DIR)/$(SRC_CPP_SUBDIR) + mkdir -p $(@D) + $(CC) $(DFLAGS) $(CFLAGS) $(INCLUDES) -MMD $< -o $@ + + +clean: + @echo MODE: $(MODE) + + @if [ -d "$(OUT_DIR)" ]; then \ + echo "cleaning $(OUT_DIR_DBG) ..."; \ + #find $(OUT_DIR)/ -name "*.o" -type f -delete; \ + rm -rf $(OUT_DIR)/*.d; \ + rm -rf $(OUT_DIR)/*.o; \ + rm -rf $(OUT_DIR); \ + echo "cleaned $(OUT_DIR)"; \ + fi + rm -rf "$(SRC_DIR)/$(EXE_FILENAME)"; \ + #rm -rf "$(SRC_DIR)/*.gif"; \ + #rm -rf "$(SRC_DIR)/*.dot"; \ + +.PHONY: all build clean + diff --git a/Ex6/Makefile.main b/Ex6/Makefile.main new file mode 100644 index 0000000..cf6c368 --- /dev/null +++ b/Ex6/Makefile.main @@ -0,0 +1,60 @@ +# (View > Command Palette) => "Convert Indentation to Tabs" + +ifeq ($(VS_CFG),Windows-Debug) + MAKEFILE = "Makefile" + MODE = Debug + COMFLAG = -m64 + + OUT_DIR = ./out_win_dbg + DSPElib_SUBDIR = MinGW-W64_8.1.0 + MISC_LINKER_FLAGS = -static + LIBS = -lwinmm -lws2_32 + DFLAGS = -DWIN32 -DDEVCPP +endif +ifeq ($(VS_CFG),Windows-Release) + MAKEFILE = "Makefile" + MODE = Release + COMFLAG = -m64 + + OUT_DIR = ./out_win_rls + DSPElib_SUBDIR = MinGW-W64_8.1.0 + MISC_LINKER_FLAGS = -static + LIBS = -lwinmm -lws2_32 + DFLAGS = -DWIN32 -DDEVCPP +endif +ifeq ($(VS_CFG),Linux-Debug) + MAKEFILE = "Makefile" + MODE = Debug + COMFLAG = + + OUT_DIR = ./out_linux_dbg + DSPElib_SUBDIR = $(shell gcc -dumpmachine)-gcc_$(shell gcc -dumpversion) + MISC_LINKER_FLAGS = + LIBS := -lasound + DFLAGS = +endif +ifeq ($(VS_CFG),Linux-Release) + MAKEFILE = "Makefile" + MODE = Release + COMFLAG = + + OUT_DIR = ./out_linux_rls + DSPElib_SUBDIR = $(shell gcc -dumpmachine)-gcc_$(shell gcc -dumpversion) + MISC_LINKER_FLAGS = + LIBS := -lasound + DFLAGS = +endif + + + +build: + @echo "Building $(VS_CFG)" + @echo $(VS_CFG): $(MODE) // $(MAKEFILE) + make build CPP_FILENAME=$(FILE) MODE=$(MODE) COMFLAG=$(COMFLAG) DFLAGS="$(DFLAGS)" LIBS="$(LIBS)" OUT_DIR=$(OUT_DIR) DSPElib_SUBDIR=$(DSPElib_SUBDIR) MISC_LINKER_FLAGS="$(MISC_LINKER_FLAGS)" -f $(MAKEFILE) + +clean: + @echo "Cleaning $(VS_CFG)" + @echo $(VS_CFG): $(MODE) // $(MAKEFILE) + make clean MODE=$(MODE) OUT_DIR=$(OUT_DIR) -f $(MAKEFILE) + + diff --git a/Ex6/SymbolMapper_test.cbp b/Ex6/SymbolMapper_test.cbp new file mode 100644 index 0000000..c8a3c05 --- /dev/null +++ b/Ex6/SymbolMapper_test.cbp @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="SymbolMapper_test" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="SymbolMapper_test" prefix_auto="1" extension_auto="1" /> + <Option type="1" /> + <Option compiler="gcc" /> + </Target> + </Build> + <Compiler> + <Add option="-std=c++0x" /> + <Add option="-m32" /> + <Add option="-g" /> + <Add option="-DWIN32" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/include" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Compiler> + <Linker> + <Add option="-static-libgcc" /> + <Add option="-m32" /> + <Add library="DSPE" /> + <Add library="winmm" /> + <Add directory="../DSPElib/CodeBlocks-m32_TDM_5.1.0/dbg" /> + </Linker> + <Unit filename="SymbolMapper_test.cpp" /> + <Extensions> + <code_completion /> + <envvars /> + <debugger /> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/Ex6/SymbolMapper_test.cpp b/Ex6/SymbolMapper_test.cpp new file mode 100644 index 0000000..65d107d --- /dev/null +++ b/Ex6/SymbolMapper_test.cpp @@ -0,0 +1,229 @@ +#include <sstream> +using namespace std; + +#include <DSP_lib.h> +#include <memory> + + +void test_symbol_sampler() +{ + map<string,shared_ptr<DSP::Component> > blocks; + + DSP::Clock_ptr BitClock, SymbolClock; + SymbolClock=DSP::Clock::CreateMasterClock(); + long F_symb = 1500; + int bits_per_symbol = 2; + BitClock = DSP::Clock::GetClock(SymbolClock, bits_per_symbol, 1); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + DSP::log << "test_symbol_sampler()" << std::endl; + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + // First data channel + blocks["BinData1"] = make_shared<DSP::u::FileInput>(BitClock, "ex6_task1.cpp", 1U, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + blocks["BinData1"]->SetName("ex6_task1.cpp"); //DSP::u::Const BinData1(BitClock, DSP::Float(-1.0)); + blocks["BinData_in"] = make_shared<DSP::u::FileOutput>("bits_in.txt", DSP::e::SampleType::ST_bit_text, 1U, DSP::e::FileType::FT_raw); + blocks["BinData_in"]->SetName("bits_in.txt"); + blocks["BinData1"]->Output("out") >> blocks["BinData_in"]->Input("in"); + + //DSP::u::PSKencoder PSKencoder1(DSP::e::PSK_type::QPSK_A); + blocks["SPconv"] = make_shared<DSP::u::Serial2Parallel>(BitClock, bits_per_symbol); + blocks["BinData1"]->Output("out") >> blocks["SPconv"]->Input("in"); + blocks["PSKencoder"] = make_shared<DSP::u::SymbolMapper>(DSP::e::ModulationType::PSK, bits_per_symbol, 0.0); // QPSK_A + blocks["SPconv"]->Output("out") >> blocks["PSKencoder"]->Input("in"); + + blocks["SymbData"] = make_shared<DSP::u::FileOutput>("symbols.flt", DSP::e::SampleType::ST_float, 2U, DSP::e::FileType::FT_flt, F_symb); + blocks["SymbData"]->SetName("symbols.flt"); + blocks["PSKencoder"]->Output("out") >> blocks["SymbData"]->Input("in"); + + blocks["PSKdecoder"] = make_shared<DSP::u::SymbolDemapper>(DSP::e::ModulationType::PSK, bits_per_symbol, 0.0); // QPSK_A + blocks["PSKencoder"]->Output("out") >> blocks["PSKdecoder"]->Input("in"); + blocks["PSconv"] = make_shared<DSP::u::Parallel2Serial>(SymbolClock, bits_per_symbol); + blocks["PSKdecoder"]->Output("out") >> blocks["PSconv"]->Input("in"); + + // SymbolMapper first sends "empty symbol" which is equivalent to bits_per_symbol zero bits + blocks["delay"] = shared_ptr<DSP::u::Delay>(new DSP::u::Delay(8-bits_per_symbol)); // byte boundary alignment + blocks["PSconv"]->Output("out") >> blocks["delay"]->Input("in"); + + blocks["BinData_raw"] = make_shared<DSP::u::FileOutput>("decoded_raw.txt", DSP::e::SampleType::ST_bit, bits_per_symbol, DSP::e::FileType::FT_raw); + blocks["BinData_raw"]->SetName("decoded_raw.txt"); + blocks["PSKdecoder"]->Output("out") >> blocks["BinData_raw"]->Input("in"); + + blocks["BinData_decoded"] = make_shared<DSP::u::FileOutput>("decoded.txt", DSP::e::SampleType::ST_bit, 1U, DSP::e::FileType::FT_raw); + blocks["BinData_decoded"]->SetName("decoded.txt"); + blocks["delay"]->Output("out") >> blocks["BinData_decoded"]->Input("in"); + (dynamic_cast<DSP::u::FileOutput *>(blocks["BinData_decoded"].get()))->SetSkip(8); // skip first 8 additional zero bits + + blocks["BinData_out"] = make_shared<DSP::u::FileOutput>("bits_out.txt", DSP::e::SampleType::ST_bit_text, 1U, DSP::e::FileType::FT_raw); + blocks["BinData_out"]->SetName("bits_out.txt"); + blocks["PSconv"]->Output("out") >> blocks["BinData_out"]->Input("in"); + (dynamic_cast<DSP::u::FileOutput *>(blocks["BinData_out"].get()))->SetSkip(0); + + blocks["BinData3"] = make_shared<DSP::u::FileOutput>("bits3.flt", DSP::e::SampleType::ST_float, 1U, DSP::e::FileType::FT_raw); + blocks["BinData3"]->SetName("bits3.flt"); + blocks["PSconv"]->Output("out") >> blocks["BinData3"]->Input("in"); + (dynamic_cast<DSP::u::FileOutput *>(blocks["BinData3"].get()))->SetSkip(0); + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + + + + // *********************************** // + DSP::Clock::SchemeToDOTfile(SymbolClock, "SymbolMapper-test.dot"); + ///////////////////////////////// + // check if there are signals + // connected to all inputs + DSP::Component::CheckInputsOfAllComponents(); + + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 50000 //1*Fp2 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(SymbolClock, SamplesInSegment); + // ********************************************************** // + +// if (BinData1.GetBytesRead() > 0) +// { +// NoOfSamplesProcessed = 0; // Play the whole file +// } +// else // Play 200ms more +// { +// if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) +// NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; +// } + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + + //sprintf(tekst, "NoOfSamplesProcessed = %i", int(NoOfSamplesProcessed)); + //DSPf_InfoMessage(tekst); + } + + // ************************************************************* + blocks.clear(); + + DSP::Clock::ListComponents(); + // ************************************************************ + DSP::Clock::FreeClocks(); + +} + +void test_parallel_2_serial() +{ + map<string,shared_ptr<DSP::Component> > blocks; + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + DSP::log << "test_parallel_2_serial()" << std::endl; + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + + DSP::Clock_ptr BitClock, SymbolClock; + SymbolClock=DSP::Clock::CreateMasterClock(); + + unsigned int no_of_symbol_components = 2; //complex signals + int bits_per_symbol = 3; + BitClock = DSP::Clock::GetClock(SymbolClock, bits_per_symbol, 1); + + // First data channel + blocks["BinData1"] = make_shared<DSP::u::FileInput>(BitClock, "ex6_task1.cpp", no_of_symbol_components, DSP::e::SampleType::ST_bit, DSP::e::FileType::FT_raw); + blocks["BinData1"]->SetName("ex6_task1.cpp"); + //DSP::u::Const BinData1(BitClock, DSP::Float(-1.0)); + blocks["BinData1_txt"] = make_shared<DSP::u::FileOutput>("bits_in.txt", DSP::e::SampleType::ST_bit_text, no_of_symbol_components, DSP::e::FileType::FT_raw); + blocks["BinData1"]->Output("out") >> blocks["BinData1_txt"]->Input("in"); + + vector<DSP::Float> SP_init; + SP_init.resize(bits_per_symbol*no_of_symbol_components, 1.0); + blocks["SPconv"] = make_shared<DSP::u::Serial2Parallel>(BitClock, bits_per_symbol, no_of_symbol_components, SP_init); + blocks["BinData1"]->Output("out") >> blocks["SPconv"]->Input("in"); + blocks["BinData_SP_txt"] = make_shared<DSP::u::FileOutput>("bits_SP_out.txt", DSP::e::SampleType::ST_bit_text, bits_per_symbol*no_of_symbol_components, DSP::e::FileType::FT_raw); + blocks["SPconv"]->Output("out") >> blocks["BinData_SP_txt"]->Input("in"); + +// blocks["SymbData"] = make_shared<DSP::u::FileOutput>("symbols.flt", DSP::e::SampleType::ST_float, 2U, DSP::e::FileType::FT_flt); +// PSKencoder1.Output("out") >> blocks["SymbData"]->Input("in"); +// +// blocks["PSKdecoder"] = make_shared<DSP::u::SymbolDemapper>(SymbolClock, DSP::e::ModulationType::PSK, bits_per_symbol, 0.0); // QPSK_A +// PSKencoder1.Output("out") >> blocks["PSKdecoder"]->Input("in"); + + blocks["PSconv"] = make_shared<DSP::u::Parallel2Serial>(SymbolClock, bits_per_symbol, no_of_symbol_components); + blocks["SPconv"]->Output("out") >> blocks["PSconv"]->Input("in"); + + blocks["BinData2"] = make_shared<DSP::u::FileOutput>("bits.txt", DSP::e::SampleType::ST_bit, no_of_symbol_components, DSP::e::FileType::FT_raw); + blocks["PSconv"]->Output("out") >> blocks["BinData2"]->Input("in"); + (dynamic_cast<DSP::u::FileOutput *>(blocks["BinData2"].get()))->SetSkip(bits_per_symbol); // skip first bits_per_symbol additional zero symbols + + blocks["BinData2_txt"] = make_shared<DSP::u::FileOutput>("bits_out.txt", DSP::e::SampleType::ST_bit_text, no_of_symbol_components, DSP::e::FileType::FT_raw); + blocks["PSconv"]->Output("out") >> blocks["BinData2_txt"]->Input("in"); + blocks["BinData2b_txt"] = make_shared<DSP::u::FileOutput>("bits_out_b.txt", DSP::e::SampleType::ST_bit_text, no_of_symbol_components, DSP::e::FileType::FT_raw); + blocks["PSconv"]->Output("out") >> blocks["BinData2b_txt"]->Input("in"); + (dynamic_cast<DSP::u::FileOutput *>(blocks["BinData2b_txt"].get()))->SetSkip(bits_per_symbol); // skip first bits_per_symbol additional zero symbols + + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + DSP::Clock::SchemeToDOTfile(SymbolClock, "Seria2Parallel-test.dot"); + ///////////////////////////////// + // check if there are signals + // connected to all inputs + DSP::Component::CheckInputsOfAllComponents(); + + // *********************************** // + int SamplesInSegment = 512; + __int64 NoOfSamplesProcessed = 0; + // 10 seconds + #define MAX_SAMPLES_TO_PROCESS 50000 //1*Fp2 + while(NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS) + { + + // ********************************************************** // + DSP::Clock::Execute(SymbolClock, SamplesInSegment); + // ********************************************************** // + + /* + if (BinData1.GetBytesRead() > 0) + { + NoOfSamplesProcessed = 0; // Play the whole file + } + else // Play 200ms more + { + if (NoOfSamplesProcessed < MAX_SAMPLES_TO_PROCESS - Fp2/5) + NoOfSamplesProcessed = MAX_SAMPLES_TO_PROCESS - Fp2/5; + } + */ + + NoOfSamplesProcessed += SamplesInSegment; + // ********************************************************** // + + //sprintf(tekst, "NoOfSamplesProcessed = %i", int(NoOfSamplesProcessed)); + //DSPf_InfoMessage(tekst); + } + + /*************************************************************/ + blocks.clear(); + + DSP::Clock::ListComponents(); + /*************************************************************/ + DSP::Clock::FreeClocks(); + +} + +int main(int argn, char *args[]) +{ + UNUSED_ARGUMENT(argn); + UNUSED_ARGUMENT(args); + + /*************************************************************/ + // Log file setup + DSP::log.SetLogFileName("log_file.txt"); + DSP::log.SetLogState(DSP::e::LogState::file | DSP::e::LogState::console); + + DSP::log << DSP::lib_version_string() << std::endl << std::endl; + /*************************************************************/ + + test_symbol_sampler(); + + //test_parallel_2_serial(); + return 0; +} diff --git a/Ex6/docs/Notes.docx b/Ex6/docs/Notes.docx new file mode 100644 index 0000000000000000000000000000000000000000..a455bf707fca15eeb61c3b3a897358eebb0a1087 GIT binary patch literal 54160 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 TnCU<bB>@I;V2(;Y58?p;mTSaQ literal 0 HcmV?d00001 diff --git a/Ex6/matlab - test/eyediagram2.m b/Ex6/matlab - test/eyediagram2.m new file mode 100644 index 0000000..0ced67e --- /dev/null +++ b/Ex6/matlab - test/eyediagram2.m @@ -0,0 +1,69 @@ +function hf = eyediagram2(x, offset, Nsymb, hf) + +if nargin == 4, + figure(hf); +else + hf = figure; +end +subplot(1,1,1) +if nargin == 2, + Nsymb = offset; + offset = ceil((Nsymb-1)/2); +% Nsymb = length(x)+1; +end +x(ceil(length(x)/Nsymb)*Nsymb) = 0; +hp = []; +% t_ = linspace(-Nsymb/2, Nsymb/2, Nsymb); +t_ = 0:Nsymb; +ind_ = 1:(Nsymb+1); +% if rem(Nsymb,2) == 1, +% t_ = (0:Nsymb+1) - Nsymb/2 - 0.5; +% +% ind_ = [0:Nsymb+1]; +% else +% t_ = (-1:Nsymb) - Nsymb/2 + 0.5; +% +% ind_ = [-1:Nsymb]; +% end + + +dt = offset - round(offset); +t_ = t_ + dt; offset = round(offset); + +% t_ = t_ / Nsymb; + +if sum(abs(imag(x))) == 0, + subplot(1,1,1); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + % plot(t_, +1+t_*0, 'k:'); + % plot(t_, -1+t_*0, 'k:'); + for ind = offset:Nsymb:length(x)-(Nsymb+1), + x_ = x(ind+ind_); + + hp(end+1) = plot(t_, real(x_)); +% hp(end+1) = plot(t_, imag(x_), 'r'); + end + hold off +else + subplot(1,2,1); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + % plot(t_, +1+t_*0, 'k:'); + % plot(t_, -1+t_*0, 'k:'); + subplot(1,2,2); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + for ind = offset:Nsymb:length(x)-(Nsymb+1), + x_ = x(ind+ind_); + + subplot(1,2,1); + hp(end+1) = plot(t_, real(x_)); + subplot(1,2,2); + hp(end+1) = plot(t_, imag(x_), 'r'); + end + hold off +end diff --git a/Ex6/matlab - test/load_filter_coef.m b/Ex6/matlab - test/load_filter_coef.m new file mode 100644 index 0000000..8c8574c --- /dev/null +++ b/Ex6/matlab - test/load_filter_coef.m @@ -0,0 +1,107 @@ +function [coefficients, file_version] = load_filter_coef(filename) +% [coefficients, file_version] = load_filter_coef(filename) +% +% FIR filter coefficients +% coefficients.h +% - can be cell vector to store multiple FIR impulse responces +% For file_version >= 1 (default == 0), +% coefficients.Fp - sampling frequency +% +% This file is a part of Digital Signal Processing Engine +% \author Marek Blok +% \date 2008.03.28 + +% /*! File format (*.coef) - this is open format, for general use +% * (not only for storing coefficients) +% * +ind = find(filename == '.'); +if length(ind) == 0, + filename = [filename, '.coef']; +end +plik = fopen(filename, 'rb'); + +% * - (uchar) 1B - file version number +file_version = fread(plik, 1, 'uchar') + +switch file_version, + case 0, + case 1, + % * - (uint) 4B - Sampling frequency + coefficients.Fp = fread(plik, 1, 'uint32') + otherwise, + fclose(plik); + error('This version of coefficients file is unsupported'); +end + +% * - data - coefficients data (depends on fle version) +% * . +% * Data segment format: +% * -# (version: 0x00) +% * - (uchar) 1B - number of sample dimensions +% * 1 - real, 2 - complex, ... +no_of_channels = fread(plik, 1, 'uchar'); % complex +if no_of_channels == 1, + isComplex = 0; +elseif no_of_channels == 2, + isComplex = 1; +else + fclose(plik); + error('to many channels'); +end +% * - (uchar) 1B - sample component type +% * - DSP::e::FileType::FT_float (=1) : C++ float (32bit floating point) +% * - DSP::e::FileType::FT_short (=2) : C++ short (16bit signed integer) +% * - DSP::e::FileType::FT_uchar (=3) : C++ unsigned char (8bit unsigned integer with bias (0x80)) +% * - DSP::e::FileType::FT_double (=7) : C++ double (64bit floating point) +% * - DSP::e::FileType::FT_long_double (=8) : C++ long double (80bit floating point) +sample_type = fread(plik, 1, 'uchar'); +switch sample_type, + case 1, + sample_type = 'float'; + case 2, + sample_type = 'short'; + case 3, + sample_type = 'uchar'; + case 7, + sample_type = 'double'; + case 8, + sample_type = 'float80'; + otherwise + fclose(plik); + error('unknown sample type'); +end + +% * - (uchar) 1B - number of vectors +% * - 1 - FIR filter coefficients (one vector) +% * - 2 - IIR filter coefficients (two vectors) +% * - (x number of vectors) +% * - (ushort) 2B - number of samples in vector +% * - (x number of samples) +% * - (x number of sample dimensions) +% * - (sample componet type) xB - sample component +% * e.g. real, imag part +resp_no = fread(plik, 1, 'uchar'); + +if resp_no == 1, + N_FIR = fread(plik, 1, 'uint16'); + if isComplex, + dane = fread(plik, 2*N_FIR, sample_type); + coefficients.h = dane(1:2:2*N_FIR) + j*dane(2:2:2*N_FIR); + else + dane = fread(plik, N_FIR, sample_type); + coefficients.h = dane; + end +else + for ind_resp = 1:resp_no, + N_FIR = fread(plik, 1, 'uint16'); + if isComplex, + dane = fread(plik, 2*N_FIR, sample_type); + coefficients.h{ind_resp} = dane(1:2:2*N_FIR) + j*dane(2:2:2*N_FIR); + else + dane = fread(plik, N_FIR, sample_type); + coefficients.h{ind_resp} = dane; + end + end +end + +fclose(plik); diff --git a/Ex6/matlab - test/readaudiofile.m b/Ex6/matlab - test/readaudiofile.m new file mode 100644 index 0000000..825a9a8 --- /dev/null +++ b/Ex6/matlab - test/readaudiofile.m @@ -0,0 +1,215 @@ +function [x, Fs] = readaudiofile(filename, param) +% [x, Fs] = readaudiofile(filename, param) +% +% returns vector x of the size SIZE=[samples channels]. +% eg. x = x(:,1) + j*x(:,2); +% +% special uses: +% param == 'size': returns SIZE in x +% param == 'cplx': for stereo files returns x as a complex vector instead of matrix +% +% supported file types: +% *.flt +% *.wav +% *.tape +% last modification: 2021.03.29 +% Author: Marek Blok + +return_cplx = 0; +if nargin == 1, + param = inf; +else + if strcmp(param, 'cplx') == 1, + return_cplx = 1; + param = inf; + end +end + +ind = find(filename == '.'); +if length(ind) == 0, + file_type = 'w'; % *.wav +else + ind = ind(end); + + temp = filename(ind+1:end); + + file_type = 'u'; % unknown format + if strcmp(temp, 'flt') == 1, + file_type = 'f'; % two channel floting point + elseif strcmp(temp, 'wav') == 1, + file_type = 'w'; % *.wav + elseif strcmp(temp, 'tape') == 1, + file_type = 't'; % *.tape + end +end + +switch file_type, + case 'w', + if strcmp(param, 'size') == 1, + if exist('audioread','file') == 0 + x = wavread(filename, 'size'); + % siz = [samples channels]. + else + info = audioinfo(filename); + x = [info.TotalSamples, info.NumChannels]; + Fs = info.SampleRate; + end + else + if isfinite(param) + if exist('audioread','file') == 0 + [x, Fs] = wavread(filename, param); + else + if length(param) == 1 + param = [1, param]; + end + [x, Fs] = audioread(filename, param); + end + else + if exist('audioread','file') == 0 + [x, Fs] = wavread(filename); + else + [x, Fs] = audioread(filename); + end + end + end + + case 't' + plik = fopen(filename, 'rb'); + if plik == -1, + error('File does not exist !!!'); + end + + header.size = fread(plik, 1, 'uint32', 0) + 4; + + header.fname = char(fread(plik, 256, 'char', 0).'); + header.cfg_fname = char(fread(plik, 256, 'char', 0).'); + header.sw_rev = fread(plik, 1, 'uint32', 0); + header.hw_rev = fread(plik, 1, 'uint32', 0); + header.file_ = fread(plik, 1, 'uint32', 0); + header.tape_type = fread(plik, 1, 'uint32', 0); + header.start_time = fread(plik, 1, 'int32', 0); % time_t + header.end_time = fread(plik, 1, 'int32', 0); % time_t + + header.total_samples = fread(plik, 1, 'uint32', 0); + file_length = header.total_samples * 4 + header.size + header.current_sample = fread(plik, 1, 'uint32', 0); + header.loop_start = fread(plik, 1, 'int64', 0); + header.loop_end = fread(plik, 1, 'int64', 0); + header.loop = fread(plik, 1, 'int32', 0); + header.group_size_32 = fread(plik, 1, 'uint32', 0); + header.block_size = fread(plik, 1, 'uint32', 0); + header.block_count = fread(plik, 1, 'uint32', 0); + header.fifo_size = fread(plik, 1, 'uint32', 0); + + + header.comment = char(fread(plik, 256, 'char', 0).'); + header.tmp = char(fread(plik, 20, 'char', 0).'); % time_t + header.status = fread(plik, 1, 'uint32', 0); + header.timestamps = fread(plik, 1, 'int32', 0); + header.freq = fread(plik, 1, 'float', 0); + header.cplx_datarate = fread(plik, 1, 'float', 0); + +% ftell(plik) + header.reserved = fread(plik, 128, 'uint32', 0); +% header.reserved.' + + header + ftell(plik) + + header.sample_type = 2; + header.ch_no = 2; + header.Fs = NaN; + + sample_type = 'int16'; + sample_size = 2; + + header_size = header.size; + if strcmp(param, 'size') == 1, + fseek(plik, 0, 'eof'); + size = (ftell(plik) - header_size) / sample_size / header.ch_no; % sizeof(float) *2 + x = size; + else + fseek(plik, header_size, 'bof'); + + len = param(1); + if length(param) > 1, + fseek(plik, sample_size*header.ch_no*(param(1)-1), 'cof'); + len = param(2) - param(1) + 1; + end + +% x = fread(plik, [header.ch_no, len], sample_type); + x = fread(plik, [header.ch_no, len], sample_type, 0); + x = x.'; + end + fclose(plik); + + case 'f' + plik = fopen(filename, 'rb'); + if plik == -1, + error('File does not exist !!!'); + end + + % 3 B - wersja pliku (w tym typ próbek) + % 1 B - liczba kanałów + % 4 B - szybkoć próbkowania + header_size = 8; + header.ver = fread(plik, 1, 'uint8'); + header.sample_type = fread(plik, 1, 'uint16'); + header.ch_no = fread(plik, 1, 'uint8'); + header.Fs = fread(plik, 1, 'uint32'); + + Fs = header.Fs; + + switch (header.ver), + case 0, + switch (header.sample_type), + case 0, + sample_type = 'float'; + sample_size = 4; + case 1, + sample_type = 'uchar'; + sample_size = 1; + case 2, + sample_type = 'short'; + sample_size = 2; + case 3, + sample_type = 'int'; + sample_size = 4; + otherwise + error('Unsupported *.flt sample type !!!'); + end + otherwise + error('Unsupported *.flt file version !!!'); + end + + + if strcmp(param, 'size') == 1, + fseek(plik, 0, 'eof'); + size = (ftell(plik) - header_size) / sample_size / header.ch_no; % sizeof(float) *2 + x = size; + else + len = param(1); + status = 0; + if length(param) > 1, + status = fseek(plik, sample_size*header.ch_no*(param(1)-1), 'cof'); + len = param(2) - param(1) + 1; + end + + if (status == -1) + x = []; + else + x = fread(plik, [header.ch_no, len], sample_type); + x = x.'; + end + end + + fclose(plik); + otherwise + error('Unsupported file format !!!'); +end + +if return_cplx == 1, + if length(x(1,:)) == 2, + x = x(:,1) + j*x(:,2); + end +end \ No newline at end of file diff --git a/Ex6/matlab - test/test_symbol_mapper.m b/Ex6/matlab - test/test_symbol_mapper.m new file mode 100644 index 0000000..f09caac --- /dev/null +++ b/Ex6/matlab - test/test_symbol_mapper.m @@ -0,0 +1,134 @@ +function test_symbol_mapper(offset) +if nargin == 0, + offset = 0; +end + +[symb, Fp] = fileread('../symbols.flt', 'cplx'); +% % [symb, Fp] = fileread('../symbols.flt'); % dla ASK +% [symb, Fp] = fileread('../ex6_task2_symb_a.flt', 'cplx'); + +% [y{3}, Fp] = fileread('../ex6_task2_symb_c.flt', 'cplx'); + +h = fopen('../Ex6_task1.cpp'); +bits_in = fread(h, 80, 'ubit1', 'ieee-be').'; % MSB first +bits_in2 = fread(h, 80, 'ubit1', 'ieee-le').'; % LSB first +fclose(h); + +% !!! kolejno�� wczytywania bitďż˝w jest odmienna od standardowej!!! +h = fopen('../decoded.txt'); +% h = fopen('../ex6_task2_bina.txt'); +bits_out = fread(h, 80, 'ubit1', 'ieee-be').'; % MSB first // here the bits offset is corrected in the *.cpp +fclose(h); +h = fopen('../decoded_raw.txt'); +% h = fopen('../ex6_task2_bina.txt'); +bits_raw = fread(h, 80, 'ubit1', 'ieee-be').'; % MSB first // here the bits offset is corrected in the *.cpp +fclose(h); +h = fopen('../bits_out.txt'); +bits_out2 = fread(h, 80, 'char').'; +fclose(h); +bits_out2 = double(bits_out2-'0') % For SymbolDemapper this gives 2 additional bits at the begining +% h = fopen('../bits3.flt'); +% bits_out3 = fread(h, 80, 'float').'; +% fclose(h); + +figure(1) +subplot(1,1,1) +plot(symb, 'o') + +bits_out_mod = bits_out; +bits_out_mod(1:2:end) = bits_out(2:2:end); +bits_out_mod(2:2:end) = bits_out(1:2:end); +% bits_out wyprzedza o 2bity (bits_per_symbol) strumienie bits_raw/bits_out2 +figure(2) +subplot(4,1,1) +stem(bits_in, 'b') +subplot(4,1,2) +stem(bits_in2, 'rx') +subplot(4,1,3) +stem(bits_out, 'r') +subplot(4,1,4) +stem(bits_out_mod, 'g') +% subplot(3,1,3) +% stem(bits_raw, 'mx') +% hold on +% stem(bits_out2, 'b+') +% hold off +% % subplot(4,1,4) +% % stem(bits_out3, 'g') + +offset1 = 1 +% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ % +% okreďż˝l przyporzďż˝dkowanie bitďż˝w do symboli w mapperze +bits_ = bits_out2; +% bits_ = bits_out; +if offset1 > 0 + y_ref = symb((offset1+1):end); +else + y_ref = symb; +end +if offset1 < 0 + if rem(-offset1, 2) == 0, + bits_ = bits_in((-offset1+1):end); + else + bits_ = bits_in((-offset1+1):end-1); + end +else + bits_ = bits_in; +end +%bin_ref_new = 2*bits_in-1; +bin_ref_new = bits_; + +% \TODO wydobyďż˝ konstelacjďż˝ wejďż˝ciowďż˝ i porďż˝wnaďż˝ jďż˝ z konstelacjďż˝ wyjďż˝ciowďż˝ +symb_ind = 2*bin_ref_new(1:2:end) + bin_ref_new(2:2:end); +figure(23) +subplot(1,2,1) +kolor = 'rmbg'; +scale = [0.9, 0.92, 0.94, 0.96]; +for ind = 0:3 + u{ind+1} = scale(ind+1) * y_ref(find(symb_ind == ind)); + plot(real(u{ind+1}), imag(u{ind+1}), [kolor(ind+1),'o']) + hold on +end +hold off +legend({'00', '10', '01', '11'}) +axis equal +title('mapper constellation') + +% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ % +% okreďż˝l przyporzďż˝dkowanie bitďż˝w do symboli w demapperze +bits_ = bits_out2; +% bits_ = bits_out; +if offset > 0 + y_ref = symb((offset+1):end); +else + y_ref = symb; +end +if offset < 0 + if rem(-offset, 2) == 0, + bits_ = bits_((-offset+1):end); + else + bits_ = bits_((-offset+1):end-1); + end +else + bits_ = bits_; +end +%bin_ref_new = 2*bits_in-1; +bin_ref_new = bits_; + +% pierwszy - starszy bit / drugi - mďż˝odszy bit +% % symb_ind = 2*(bin_ref_new(1:2:end)+1)/2 + (bin_ref_new(2:2:end)+1)/2; +symb_ind = 2*bin_ref_new(1:2:end) + bin_ref_new(2:2:end); +figure(23) +subplot(1,2,2) +kolor = 'rmbg'; +scale = [0.9, 0.92, 0.94, 0.96]; +for ind = 0:3 + u{ind+1} = scale(ind+1) * y_ref(find(symb_ind == ind)); + plot(real(u{ind+1}), imag(u{ind+1}), [kolor(ind+1),'o']) + hold on +end +hold off +legend({'00', '10', '01', '11'}) +axis equal +title('demapper constellation') + diff --git a/Ex6/matlab - test/test_task1.m b/Ex6/matlab - test/test_task1.m new file mode 100644 index 0000000..e7bd8f6 --- /dev/null +++ b/Ex6/matlab - test/test_task1.m @@ -0,0 +1,143 @@ +function test_task1(ind) +if nargin == 0, + ind = 2*(3+2*4); +end +[x, Fp] = fileread('../ex6_task1.flt', 'cplx'); +[y, Fp_y] = fileread('../ex6_task1b.flt', 'cplx'); +[y_ref, Fp] = fileread('../ex6_task1a.flt', 'cplx'); + +channel_no = 1; + +% size(x) +% size(y) +% figure +% plot(x-y) +% return + +N_DFT = 32; M = 8; +Fsymb = Fp_y/N_DFT +% K = 32; M = 8; + +figure(1) +subplot(2,1,1) +plot(x) +axis equal +subplot(2,2,3) +plot(y, 'b') +axis equal +subplot(2,2,4) +plot(y_ref, 'b.') +axis equal +% % plot(y) + +% y = y(21+1:end); + +figure(2) +subplot(2,1,1) +stem(real(y)/max(abs(y)), 'b') +hold on +stem(real(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(real(y), real(y_ref)); +plot(l, c) + +figure(3) +subplot(2,1,1) +stem(imag(y)/max(abs(y)), 'b') +hold on +stem(imag(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(imag(y), imag(y_ref)); +plot(l, c) + +n = [0:length(y)-1].'; +het = exp(-j*channel_no*(2*pi)/N_DFT*n); +% het = 1; +figure(13) +K_psd = 1024; +F_psd = linspace(-Fp_y/2, Fp_y/2, 2*K_psd+1); F_psd(end) = []; +% psd(het.*y) +Pxx = pwelch(het.*y, blackman(K_psd), ceil(0.5*K_psd), F_psd, Fp_y); +Pxx_dB = 10*log10(Pxx); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp_y/2, Fp_y/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(N_DFT/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F_psd/1000,Pxx_dB); +hold off + +% % return +% xt{1} = ((-1).^(1:length(y)).').*y/max(abs(y)); +xt{1} = het.*y/max(abs(y)); +xt{2} = y_ref; +trajekt_(xt, 10, 1:M:length(y), 0, 1) +tmp = xt{1}; +eyediagram2(tmp(1:100*M), 0, M, 15); +pause + +y = y(1:M:end); +% y = y.*exp(j*pi/4); +s_re = real(y) > 0; +s_im = imag(y) > 0; + +bin = zeros(1,2*length(s_re)); +bin(1:2:end) = s_re; +bin(2:2:end) = s_im; + +bits_per_char = 8; +%ind = 0; + +h = fopen('../Ex6_task1.cpp'); +bin2 = double(fread(h, 800, 'ubit1')).'; +fclose(h) +% +figure(4) +subplot(2,1,1) +[c, l] = xcorr(s_re, bin2(1:2:end)); +plot(l, c) +subplot(2,1,2) +[c, l] = xcorr(s_re, bin2(2:2:end)); +plot(l, c) + +% return +figure(5) +subplot(2,1,1) +stem(bin, 'b') +hold on +stem(bin2, 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(bin, bin2); +plot(l, c) + +% bin = bin_root.' +bin = bin((ind+1):end); +% for ind= 1:bits_per_char, +for ind= 1, + bin_ = bin(ind:end); + B = floor(length(bin_)/bits_per_char)*bits_per_char; + + tekst = bin_(1:bits_per_char:B); + for bit = 1:bits_per_char-1, +% tekst = tekst + (2^bit)*bin_((bit+1):bits_per_char:B); + tekst = tekst + (2^((bits_per_char-1)-bit))*bin_((bit+1):bits_per_char:B); + end + char(tekst) +end + + +% +% B_pre = length(pre)*bits_per_char; +% bin_pre = zeros(B_pre,1); +% for ind = 0:bits_per_char-1, +% bin_pre((ind+1):bits_per_char:end) = rem(floor(pre / (2^ind)),2); +% end diff --git a/Ex6/matlab - test/test_task1_signal.m b/Ex6/matlab - test/test_task1_signal.m new file mode 100644 index 0000000..ea619fb --- /dev/null +++ b/Ex6/matlab - test/test_task1_signal.m @@ -0,0 +1,304 @@ +function test_task1_signal(mode,matched, offset_0, phase_offset) +% test_task1_signal('zad1', 'srRC', 0, -2*pi/32) +% test_task1_signal('zad1b', 'srRC', 0, -2*pi/32) +% +% offset_0 - przesuniďż˝cie w prďż˝bkach chwili prďż˝bkowania sybmoli +% phase_offset - korekta fazy poczďż˝tkowej heterodynny + +channel_no = 1; +% channel_no = 4; +% channel_no = 6; + + +% if nargin == 0, +% ind = 2*(3+2*4); +% end + +if nargin == 0 + % matched = 'srRC' + matched = 'rect' + % mode = 'zad1' + mode = 'zad1' + + offset_0 = 0; + phase_offset = 0; +elseif nargin == 2 + offset_0 = 0; + phase_offset = 0; +elseif nargin == 3 + phase_offset = 0; +end + + + +N_DFT = 32; M = 8; +N_symb = N_DFT; + +switch mode + case 'zad1', + [x, Fp] = fileread('../ex6_task1.flt', 'cplx'); + offset = 5*N_symb + offset_0; + case 'zad1b', + [x, Fp] = fileread('../ex6_task1b.flt', 'cplx'); + offset = 6*N_symb + offset_0; +end +% % [y_ref, Fp] = fileread('../ex6_task1a.flt', 'cplx'); + +[x_ch1, F_symb] = fileread('../ex6_task1_ch1.flt', 'cplx'); + + +switch matched, + case 'rect', + h_matched = ones(1,N_DFT); % duďż˝y poziom interferencji ISI/ICI + offset = offset + 0.5*N_symb; + + case 'srRC', + [coef, ver] = load_filter_coef('../matlab/ex6_task1_h_rc.coef') + h_rc = coef.h; + F_symb = coef.Fp; + + h_matched = N_symb*h_rc; + offset = offset + 5*N_symb; +end +N_matched = length(h_matched); + +K_psd = 1024; +N_symb = N_DFT + +figure(11) +subplot(2,1,1) +n = 0:N_matched-1; +plot(n-(N_matched-1)/2, h_matched) +hold on +n = 0:(2*N_matched-1)-1; +plot(n-2*(N_matched-1)/2, conv(h_matched,h_matched), 'r') +plot([-N_matched, N_matched], [0, 0], 'k') +hold off +set(gca,'xlim', [-N_matched, N_matched], 'xtick', N_symb*[-16:16], 'xgrid', 'on') + +subplot(2,1,2) +[H, F_h] = freqz(h_matched, 1, K_psd, F_symb*N_DFT); +[H2, F_h] = freqz(conv(h_matched, h_matched), 1, K_psd, F_symb*N_DFT); +plot(F_h, 20*log10(abs(H))) +hold on +plot(F_h, 20*log10(abs(H2)), 'r') +hold off +set(gca, 'xlim', [0, F_symb/2*N_DFT], 'xtick', [0:N_DFT]*F_symb/2, 'xgrid', 'on') +set(gca, 'Ylim', [-170, 3]) + +% channel_no = 1; + +% size(x) +% size(y) +% figure +% plot(x-y) +% return + +Fsymb = Fp/N_DFT +% K = 32; M = 8; + +figure(1) +subplot(4,1,1) +plot(real(x), 'b') +title('wygenerowany sygnaďż˝ wielokanaďż˝owy') +%axis equal +subplot(4,1,2) +plot(imag(x), 'r') +%axis equal + +subplot(2,1,2) +F_psd = linspace(-Fp/2, Fp/2, 2*K_psd+1); F_psd(end) = []; +% psd(het.*y) +Pxx = pwelch(x, blackman(K_psd), ceil(0.5*K_psd), F_psd, Fp); +Pxx_dB = 10*log10(Pxx); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp/2, Fp/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(N_DFT/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F_psd/1000,Pxx_dB); +hold off +k = [-ceil(N_DFT/2):ceil(N_DFT/2)]'; +ticks = k*Fsymb/1000; +set(gca, 'xtick', ticks, 'xticklabel', num2str(k)) + + +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +n = [0:length(x)-1].'; +het = exp(-j*channel_no*(2*pi)/N_DFT*n + j*phase_offset); +y = het .* x; + +figure(2) +subplot(4,1,1) +plot(real(y), 'b') +title('sygnaďż˝ heterodynowany w dďż˝') +%axis equal +subplot(4,1,2) +plot(imag(y), 'r') +%axis equal + +subplot(2,1,2) +Pxx = pwelch(y, blackman(K_psd), ceil(0.5*K_psd), F_psd, Fp); +Pxx_dB = 10*log10(Pxx); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp/2, Fp/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(N_DFT/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F_psd/1000,Pxx_dB); +hold off +k = [-ceil(N_DFT/2):ceil(N_DFT/2)]'; +ticks = k*Fsymb/1000; +set(gca, 'xtick', ticks, 'xticklabel', num2str(k)) + +% y = y(21+1:end); +y2 = filter(h_matched, 1, y); + +figure(3) +subplot(4,1,1) +plot(real(y2), 'b') +title('sygnaďż˝ heterodynowany w dďż˝ po filtrze dopasowanym') +%axis equal +subplot(4,1,2) +plot(imag(y2), 'r') +%axis equal + +subplot(2,1,2) +Pxx = pwelch(y2, blackman(K_psd), ceil(0.5*K_psd), F_psd, Fp); +Pxx_dB = 10*log10(Pxx); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp/2, Fp/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(N_DFT/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F_psd/1000,Pxx_dB); +hold off +k = (-ceil(N_DFT/2):ceil(N_DFT/2))'; +ticks = k*Fsymb/1000; +set(gca, 'xtick', ticks, 'xticklabel', num2str(k)) + +m = 0:length(x_ch1)-1; +figure(3) +subplot(4,1,1) +hold on +plot(N_DFT*m+offset, real(x_ch1), 'k'); +hold off +subplot(4,1,2) +hold on +plot(N_DFT*m+offset, imag(x_ch1), 'k'); +hold off + +symbols = y2(offset:N_symb:end); +figure(4) +plot(symbols, 'b.') +axis equal + +return + +% figure(2) +% subplot(2,1,1) +% stem(real(x)/max(abs(x)), 'b') +% hold on +% stem(real(y_ref), 'r') +% hold off +% subplot(2,1,2) +% [c, l] = xcorr(real(y), real(y_ref)); +% plot(l, c) +% +% figure(3) +% subplot(2,1,1) +% stem(imag(y)/max(abs(y)), 'b') +% hold on +% stem(imag(y_ref), 'r') +% hold off +% subplot(2,1,2) +% [c, l] = xcorr(imag(y), imag(y_ref)); +% plot(l, c) +% +% % het = 1; +% figure(13) +% +% % % return +% % xt{1} = ((-1).^(1:length(y)).').*y/max(abs(y)); +% xt{1} = het.*y/max(abs(y)); +% xt{2} = y_ref; +% trajekt_(xt, 10, 1:M:length(y), 0, 1) +% tmp = xt{1}; +% eyediagram2(tmp(1:100*M), 0, M, 15); +% pause +% +% y = y(1:M:end); +% % y = y.*exp(j*pi/4); +% s_re = real(y) > 0; +% s_im = imag(y) > 0; +% +% bin = zeros(1,2*length(s_re)); +% bin(1:2:end) = s_re; +% bin(2:2:end) = s_im; +% +% bits_per_char = 8; +% %ind = 0; +% +% h = fopen('../Ex6_task1.cpp'); +% bin2 = double(fread(h, 800, 'ubit1')).'; +% fclose(h) +% % +% figure(4) +% subplot(2,1,1) +% [c, l] = xcorr(s_re, bin2(1:2:end)); +% plot(l, c) +% subplot(2,1,2) +% [c, l] = xcorr(s_re, bin2(2:2:end)); +% plot(l, c) +% +% % return +% figure(5) +% subplot(2,1,1) +% stem(bin, 'b') +% hold on +% stem(bin2, 'r') +% hold off +% subplot(2,1,2) +% [c, l] = xcorr(bin, bin2); +% plot(l, c) +% +% % bin = bin_root.' +% bin = bin((ind+1):end); +% % for ind= 1:bits_per_char, +% for ind= 1, +% bin_ = bin(ind:end); +% B = floor(length(bin_)/bits_per_char)*bits_per_char; +% +% tekst = bin_(1:bits_per_char:B); +% for bit = 1:bits_per_char-1, +% % tekst = tekst + (2^bit)*bin_((bit+1):bits_per_char:B); +% tekst = tekst + (2^((bits_per_char-1)-bit))*bin_((bit+1):bits_per_char:B); +% end +% char(tekst) +% end +% +% +% % +% % B_pre = length(pre)*bits_per_char; +% % bin_pre = zeros(B_pre,1); +% % for ind = 0:bits_per_char-1, +% % bin_pre((ind+1):bits_per_char:end) = rem(floor(pre / (2^ind)),2); +% % end diff --git a/Ex6/matlab - test/test_task1b_new.m b/Ex6/matlab - test/test_task1b_new.m new file mode 100644 index 0000000..70b1016 --- /dev/null +++ b/Ex6/matlab - test/test_task1b_new.m @@ -0,0 +1,84 @@ +function test_task1b_new + +x{1} = fileread('../ex6_task1a.flt', 'cplx'); +x_new{1} = fileread('../ex6_task1a_new.flt', 'cplx'); + +[bin_ref, Fp] = fileread('../ex6_task1_bin_a.flt', 'cplx'); +[bin_ref_new, Fp] = fileread('../ex6_task1_bin_a_new.flt'); % real +B = length(bin_ref_new); +if rem(B,2) == 1, + disp('rem(B,2) == 1'); + bin_ref_new = bin_ref_new(1:end-1); +end + +figure(1) +subplot(2,2,1) +plot(x{1}, 'bo') +axis equal +subplot(2,2,2) +plot(x_new{1}, 'ro') +axis equal +title('Wejďż˝ciowe strumienie symboli dla kanaďż˝u nr 1') + +figure(2) +subplot(2,1,1) +plot(real(x{1}), 'b') +hold on +plot(real(x_new{1}), 'r') +hold off +set(gca, 'Xlim', [0,100], 'Ylim', [-1.1, 1.1]) +subplot(2,1,2) +plot(imag(x{1}), 'b') +hold on +plot(imag(x_new{1}), 'r') +hold off +set(gca, 'Xlim', [0,100], 'Ylim', [-1.1, 1.1]) + +% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ % +% okreďż˝l przyporzďż˝dkowanie bitďż˝w do symboli modulatorze +y_ref = x{1}; +% real - starszy bit / imag - mďż˝odszy bit +symb_ind = (2*real(bin_ref)+1)/2 + (imag(bin_ref)+1)/2; +figure(23) +subplot(1,2,1) +kolor = 'rmbg'; +scale = [0.9, 0.92, 0.94, 0.96]; +for ind = 0:3 + u{ind+1} = scale(ind+1) * y_ref(find(symb_ind == ind)); + plot(real(u{ind+1}), imag(u{ind+1}), [kolor(ind+1),'o']) + hold on +end +hold off +legend({'00', '01', '10', '11'}) +axis equal + +figure(23) +subplot(1,2,2) +% (2+8+1+1)*2 - wyprzedzenie bin data w bitach +% +% bin_ref_new = bin_ref_new(3:end); +% +++++++++++++++++++++++++++++++++++++++++++ +% Bez skip w kodzie 2 bity z przedo za duďż˝o +prefix_size = 2; +bin_ref_new = [-ones(prefix_size,1); bin_ref_new(1:end-prefix_size)]; +% % Przy skip w kodzie powinno byďż˝ (2+8+1+1)*2-2 bitďż˝w za maďż˝o - nie ten kod +% prefix_size = (2+8+1)*2; +% bin_ref_new = bin_ref_new(prefix_size+1:end); +y_ref = x_new{1}; +% % bin_ref_new = bin_ref_new(2:end-1); +% bin_ref_new = bin_ref_new(3:end); + +% pierwszy - starszy bit / drugi - mďż˝odszy bit +symb_ind = 2*(bin_ref_new(1:2:end)+1)/2 + (bin_ref_new(2:2:end)+1)/2; +kolor = 'rmbg'; +scale = [0.9, 0.92, 0.94, 0.96]; +hp = []; +for ind = 0:3 + u{ind+1} = scale(ind+1) * y_ref(find(symb_ind == ind)); + hp(ind+1)=plot(real(u{ind+1}), imag(u{ind+1}), [kolor(ind+1),'o']); + hold on +end +hold off +legend(hp, {'00', '01', '10', '11'}) +axis equal + diff --git a/Ex6/matlab - test/test_task2.m b/Ex6/matlab - test/test_task2.m new file mode 100644 index 0000000..c42155b --- /dev/null +++ b/Ex6/matlab - test/test_task2.m @@ -0,0 +1,170 @@ +function test_task2(symb_offset,phase_offset, bit_offset) +% test_task2(2+4*ile, 0) +% test_task2(10, 0) - % peďż˝na kompensacja opďż˝nienia +% demoulacja pierwszego podkanaďż˝u +% test_task2(10,0,2) dla ex6_task1.flt +% test_task2(11,0,2) dla ex6_task1b.flt +if nargin == 0, +% ind = 2*(3+2*4); + symb_offset = 10; + phase_offset = 0; + bit_offset = 2; +end +phasor = exp(j*phase_offset); + +% dla ex6_task1.flt +[x, Fp] = fileread('../ex6_task1.flt'); +[y, Fp] = fileread('../ex6_task2_symb_A_a.flt', 'cplx'); +% dla ex6_task1b.flt +[x, Fp] = fileread('../ex6_task1b.flt'); +[y, Fp] = fileread('../ex6_task2_symb_B_a.flt', 'cplx'); + +[y_ref, Fp] = fileread('../ex6_task1a.flt', 'cplx'); + +y = y(symb_offset+1:end); + +% y = y_ref; % - przesuniďż˝cie o 4 + + +mode = 'QPSK_A', % 00 - 1, 10 - j, 01 - -j, 11 - -1 + +% % y = y - (0.5+j*0.5); +% y_ref = (real(y_ref) > 0) + j*(imag(y_ref) > 0); +K = 32; M = 32; +% K = 16; M = 8; + +figure(1) +subplot(2,1,1) +plot(x) +subplot(2,2,3) +plot(y, 'b.') +axis equal +subplot(2,2,4) +plot(y_ref, 'b.') +axis equal +% % plot(y) + +% return + +figure(2) +subplot(2,1,1) +stem(real(y)/max(abs(y)), 'b') +hold on +stem(real(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(real(y), real(y_ref)); +plot(l, c) + +figure(3) +subplot(2,1,1) +stem(imag(y)/max(abs(y)), 'b') +hold on +stem(imag(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(imag(y), imag(y_ref)); +plot(l, c) + +n = [0:length(y)-1].'; +% het = exp(-j*(8)*pi/(32)*n); +het = 1; +% het = 1; +figure(13) +psd(het.*y) +% return + +if M < K + % xt{1} = ((-1).^(1:length(y)).').*y/max(abs(y)); + xt{1} = het.*y/max(abs(y)); + xt{2} = y_ref; + trajekt_(xt, 10, 1:M:length(y), 0, 1) + tmp = xt{1}; + eyediagram2(tmp(1:100*M), 0, M, 15); + pause + + y = y(1:M:end); +end +y = y * phasor; + + +switch mode, + case 'QPSK_A', % ďż˝le ? % 00 - 1, 10 - j, 01 - -j, 11 - -1 + % 00 - 1, 01 - j, 10 - -j, 11 - -1 + for n = 1:length(y), + if real(y(n)) > abs(imag(y(n))), % 1 + s_re(n) = 0.0; + s_im(n) = 0.0; + elseif -real(y(n)) > abs(imag(y(n))), % -1 + s_re(n) = 1.0; + s_im(n) = 1.0; + elseif imag(y(n)) > abs(real(y(n))), % j + s_re(n) = 1.0; s_im(n) = 0.0; +% s_re(n) = 0.0; s_im(n) = 1.0; + elseif -imag(y(n)) > abs(real(y(n))), % -j + s_re(n) = 0.0; s_im(n) = 1.0; +% s_re(n) = 1.0; s_im(n) = 0.0; + else + s_re(n) = 0.0; + s_im(n) = 0.0; + end + end + + case 'QPSK_B', % ??? + s_re = real(y) > 0; + s_im = imag(y) > 0; +end + + +bin = zeros(1,2*length(s_re)); +bin(1:2:end) = s_re; +bin(2:2:end) = s_im; + +bits_per_char = 8; +%ind = 0; + +h = fopen('../Ex6_task1.cpp'); +bin2 = fread(h, 800, 'ubit1', 'ieee-be').'; % ??? +fclose(h); +% +figure(4) +subplot(2,1,1) +[c, l] = xcorr(s_re, bin2(1:2:end)); +plot(l, c) +subplot(2,1,2) +[c, l] = xcorr(s_re, bin2(2:2:end)); +plot(l, c) + +% return +figure(5) +subplot(2,1,1) +stem(bin, 'b') +hold on +stem(bin2, 'rx') +hold off +subplot(2,1,2) +[c, l] = xcorr(bin, bin2); +plot(l, c) + +% % bin = bin_root.' +% bin = bin((ind+1):end); +% for ind= 1:bits_per_char, +for ind= bit_offset+1, + bin_ = bin(ind:end); + B = floor(length(bin_)/bits_per_char)*bits_per_char; + + tekst = bin_(1:bits_per_char:B); + for bit = 1:bits_per_char-1, +% tekst = tekst + (2^bit)*bin_((bit+1):bits_per_char:B); + tekst = tekst + (2^((bits_per_char-1)-bit))*bin_((bit+1):bits_per_char:B); + end + char(tekst) +end + + +% +% B_pre = length(pre)*bits_per_char; +% bin_pre = zeros(B_pre,1); +% for ind = 0:bits_per_char-1, +% bin_pre((ind+1):bits_per_char:end) = rem(floor(pre / (2^ind)),2); +% end diff --git a/Ex6/matlab - test/test_task2_constelation.m b/Ex6/matlab - test/test_task2_constelation.m new file mode 100644 index 0000000..3c9f5dc --- /dev/null +++ b/Ex6/matlab - test/test_task2_constelation.m @@ -0,0 +1,115 @@ +function test_task2_constelation(offset) +% test konstelacji dla wszystkich podkana��w +% * test_task2_constelation(2) - dla demodulacji ex6_task1.flt [mode = 'A'] +% * test_task2_constelation(3) - dla demodulacji ex6_task1b.flt [mode = 'B'] +if nargin == 0, + offset = 0; +end + +mode = 'A' % demodulacja ex6_task1.flt +% mode = 'B' % demodulacja ex6_task1b.flt + +% Surowe symbole z poszczegďż˝lnych wyodrďż˝bnionych podkanaĹĂłw Ex6_task_zad2.cpp +[y{1}, Fp] = fileread(['../ex6_task2_symb_', mode, '_a.flt'], 'cplx'); +[y{2}, Fp] = fileread(['../ex6_task2_symb_', mode, '_b.flt'], 'cplx'); +[y{3}, Fp] = fileread(['../ex6_task2_symb_', mode, '_c.flt'], 'cplx'); + +% symbole wyjďż˝ciowe mappera symboli dla pierwszego podkanaĹĂłw Ex6_taskzad1.cpp +%x{1} = fileread('../ex6_task1_ch1.flt', 'cplx'); + +% symbole wyjďż˝ciowe mappera symboli dla pierwszego podkanaĹĂłw Ex6_taskzad1b.cpp +x{1} = fileread('../ex6_task1a_new.flt', 'cplx'); + +y{1} = y{1}((9+1):end); + +bin_ref = fileread('../ex6_task1_bin_a.flt', 'cplx'); + +h = fopen('../ex6_task2_bina.txt'); +bits_out = fread(h, inf, 'ubit1', 'ieee-be').'; +fclose(h); +h = fopen('../ex6_task2_bina.flt'); +bits_out2 = fread(h, inf, 'float').'; +fclose(h); + +N = length(y{1}); +M = length(x{1}); + +figure(1) +subplot(2,3,1) +plot(y{1}, 'b.') +axis equal +subplot(2,3,2) +plot(y{2}, 'b.') +axis equal +subplot(2,3,3) +plot(y{3}, 'b.') +axis equal + +subplot(2,3,4) +plot(x{1}, 'b.') +axis equal + +figure(2) +subplot(2,1,1) +plot(real(y{1}), 'b') +hold on +plot(real(x{1}), 'r') +hold off +subplot(2,1,2) +plot(imag(y{1}), 'b') +hold on +plot(imag(x{1}), 'r') +hold off + + +N1 = min([N,M]); +figure(3) +subplot(2,3,1) +plot(y{1}.*conj(x{1}(1:N1)), 'r.') +axis equal +subplot(2,1,2) +[c, l] = xcorr(y{1}, x{1}(1:N1)); +plot(l, real(c), 'b'); +hold on +plot(l, imag(c), 'r'); +hold off + +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +symb = y{1}; +bits_ = bits_out; +if offset > 0 + y_ref = symb((offset+1):end); +else + y_ref = symb; +end +if offset < 0 + if rem(-offset, 2) == 0, + bits_in = bits_((-offset+1):end); + else + bits_in = bits_((-offset+1):end-1); + end +else + bits_in = bits_; +end +%bin_ref_new = 2*bits_in-1; +bin_ref_new = bits_in; + +if length(bin_ref_new) > 2*length(y_ref), + bin_ref_new = bin_ref_new(1:2*length(y_ref)); +end +% pierwszy - starszy bit / drugi - mďż˝odszy bit +% % symb_ind = 2*(bin_ref_new(1:2:end)+1)/2 + (bin_ref_new(2:2:end)+1)/2; +symb_ind = 2*bin_ref_new(1:2:end) + bin_ref_new(2:2:end); +figure(3) +subplot(2,3,3) +kolor = 'rmbg'; +scale = [0.9, 0.92, 0.94, 0.96]; +for ind = 0:3 + u{ind+1} = scale(ind+1) * y_ref(find(symb_ind == ind)); + plot(real(u{ind+1}), imag(u{ind+1}), [kolor(ind+1),'o']) + hold on +end +hold off +legend({'00', '10', '01', '11'}) +axis equal diff --git a/Ex6/matlab - test/trajekt_.m b/Ex6/matlab - test/trajekt_.m new file mode 100644 index 0000000..696ad41 --- /dev/null +++ b/Ex6/matlab - test/trajekt_.m @@ -0,0 +1,403 @@ +function klawisz=trajekt_(x_s, time, punkty, no_head, pause_on_start) +% \added 2019.10.18 żółte tło okna w trybie pauzy +% \added 2019.10.18 prosty tryb testowania (ustawiajšc test_mode = 1; w linii 11) +% \fixed 2019.10.18 wyłšczenie zoom przed definiowaniem 'KeyPressFcn' +% \added 2017.10.24 dodano obsługę trybu, w którym x_s zawiera dane i +% etykiety (interpreter LaTeX): x_s{1}.data = ..., x_s{1}.label = '?'; +% \fixed 2015.10.27 addapted to matlab 2015b +% \fixed 2006.11.14 fixed axes position in single and double plot mode +% \added 2006.11.14 input option no_head +% \added 2006.11.14 input option pause_on_start + +test_mode = 0; +if (test_mode == 1) & (nargin == 0) + x_s = exp(sqrt(-1)*0.1*[0:1000]); + klawisz = trajekt_(x_s, 100, [], 0, 1); + return; +end + +if nargin < 5, + pause_on_start = 0; +end +if nargin < 4, + no_head = 1; +end +if nargin < 3, + punkty = []; +end +labels = {}; + + +if nargin == 0, +error('Not enough input arguments.'); +else + if isreal(x_s), + ile = 1; + x = x_s; + y = x; x = 1:length(y); + + clear x_s; + x_s{1}.x = x; + x_s{1}.y = y; + max1=max(abs(x(~isinf(x)))); + zakres=[-max1 max1 -max1 max1]; + else + if iscell(x_s) == 0, + ile = 1; + y=imag(x_s); + x=real(x_s); + clear x_s; + x_s{1}.x = x; + x_s{1}.y = y; + max1=max(max(abs(x(~isinf(x)))),max(abs(y(~isinf(y))))); + zakres=[-max1 max1 -max1 max1]; + else + x_s_tmp = x_s; + clear x_s + + ile = length(x_s_tmp); + max1 = eps; + for ind = 1:ile, + if isfield(x_s_tmp{ind}, 'label'), + labels{ind} = x_s_tmp{ind}.label; + end + if isfield(x_s_tmp{ind}, 'data'), + x_s_tmp{ind} = x_s_tmp{ind}.data; + end + x=real(x_s_tmp{ind}); + y=imag(x_s_tmp{ind}); + x_s{ind}.x = x; + x_s{ind}.y = y; + + max1=max([max(abs(x(~isinf(x)))),max(abs(y(~isinf(y)))), max1]); + end + zakres=[-max1 max1 -max1 max1]; + end + end +end + + +% if nargin == 0, +% error('Not enough input arguments.'); +% else +% if isreal(x), +% % y = x; x = 1:length(y); +% % max_y=max(abs(x(~isinf(x)))); +% % min_y=-max_x; +% % max_x=length(y); +% % min_x=1; +% y = x; x = 1:length(y); +% max_y=10; +% min_y=-10; +% max_x=10; +% min_x=10; +% else +% % y=imag(x); +% % x=real(x); +% % max_x=max(abs(x(~isinf(x)))); +% % min_x=-max_x; +% % max_y=max_x; +% % min_y=-max_x; +% y=imag(x); +% x=real(x); +% max_x=10; +% min_x=-10; +% max_y=10; +% min_y=-10; +% +% end +% end +if nargin==1, + time=0.01; +else + time=time/length(x); +end +p = 0.02; + +hf=findobj(0,'tag','trajektoria'); +if isempty(hf) + hf=figure('NextPlot','add', 'Color', 'w'); +else + figure(hf); +end +set(hf,'tag','trajektoria', 'Name', 'Trajektorie - dr inż. Marek Blok (14.11.2006)'); +delete(findobj(hf,'type', 'axes')); + +global klawisz +global trajekt_hf + +if pause_on_start == 0, + klawisz = '~'; % od razu ruszaj +else + klawisz = 13; % pauza na starcie +end +trajekt_hf = hf; +% set(hf, 'KeyPressFcn', 'global klawisz; fig = get(0, ''PointerWindow''); if fig ~= 0, klawisz = get(fig,''CurrentCharacter''); if isempty(klawisz), klawisz = ''~''; end; end;'); +zoom(hf, 'off'); +set(hf, 'KeyPressFcn', 'global klawisz; global trajekt_hf, fig = trajekt_hf; if fig ~= 0, klawisz = get(fig,''CurrentCharacter''); if isempty(klawisz), klawisz = ''~''; end; end;'); + +min_x=-5; +max_x=5; +min_y=-5; +max_y=5; + +if ile == 1, + ax{1}=axes('Units','Normalized','Position',[0, 0, 1, 1]+ [1, 1, -2, -2]*0.05); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= 1 + title(labels{1}, 'Interpreter', 'latex'); + end + set(ax{1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + co = get(ax{1},'colororder'); +elseif ile == 2, + for ind = 0:1, + ax{ind+1}=axes('Units','Normalized','Position',[ind*0.5 0 [0.5 1]]+ [1, 1, -2, -2]*0.05); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= 2 + title(labels{ind+1}, 'Interpreter', 'latex'); + end + set(ax{ind+1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + end + co = get(ax{1},'colororder'); +else + % 12 + for ind = 0:ile-1, + % 4 x 3 + x_ = rem(ind,4); + y_ = (ind-x_)/4; + ax{ind+1}=axes('Units','Normalized','Position',[x_*0.25 1-(y_+1)*0.33 [0.25 0.33]*0.95]); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= ile + title(labels{ind+1}, 'Interpreter', 'latex'); + end + set(ax{ind+1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + end + co = get(ax{1},'colororder'); +end + + + +m = length(x_s{1}.x); +for ind = 1:ile-1, + if m == 0, + m = length(x_s{ind+1}.x); + else + m = min([m, length(x_s{ind+1}.x)]); + end +end +k = ceil(p*m); + +%ax1 = newplot; +% ax1=axes('Units','Pixels','Position',[30 30 300 300]); + +% ax2=axes('Units','Pixels','Position',[360 30 300 300]); +for ind = 0:ile-1, + axes(ax{ind+1}) + + % Choose first three colors for head, body, and tail + if no_head == 0, + head{ind+1} = line('color',co(1,:),'linestyle', 'none', 'marker', 'o', 'linewidth', 3, ... + 'xdata',x_s{ind+1}.x(1),'ydata',x_s{ind+1}.y(1)); + end + %body_x = []; body_y = []; + body_x{ind+1} = NaN*ones(1,m); body_y{ind+1} = NaN*ones(1,m); + body_x{ind+1}(1) = x_s{ind+1}.x(1); body_y{ind+1}(1) = x_s{ind+1}.y(1); + body{ind+1} = line('color','b','linestyle','-', 'marker','none', ... %'erase','none', ... + 'xdata',body_x{ind+1},'ydata',body_y{ind+1}); + %tail_x = []; tail_y = []; + tail_x{ind+1} = NaN*ones(1,k+1); tail_y{ind+1} = NaN*ones(1,k+1); + tail_x{ind+1}(1) = x_s{ind+1}.x(1); tail_y{ind+1}(1) = x_s{ind+1}.y(1); + tail{ind+1} = line('color','r','linestyle','-','marker','none', ... %'erase','none', ... + 'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}); + if no_head == 0, + head2{ind+1} = line('color',co(4,:),'linestyle', 'none', 'marker','o', 'linewidth', 3, ... + 'xdata',x_s{ind+1}.x(1),'ydata',x_s{ind+1}.y(1)); + end + + if ile > 2, + h_punkty{ind+1} = line('color','k','linestyle', 'none', 'marker','.', 'markersize', 20, ... %'erase','none', ... + 'xdata',0,'ydata',NaN); + else + h_punkty{ind+1} = line('color','k','linestyle', 'none', 'marker','.', 'markersize', 24, ... %'erase','none', ... + 'xdata',0,'ydata',NaN); + end; + punkty_x{ind+1} = NaN*punkty; punkty_y{ind+1} = NaN*punkty; + punkty_ind{ind+1} = 1; +end; + +% Grow the body +for i = 2:k+1 + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + if ~isempty(punkty) + if any(punkty == i) + punkty_x{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.x(i); punkty_y{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.y(i); + punkty_ind{ind+1} = punkty_ind{ind+1} + 1; + set(h_punkty{ind+1}, 'Xdata', punkty_x{ind+1}, 'Ydata', punkty_y{ind+1}); + end + end + + j = i-1:i; + if no_head == 0, + set(head{ind+1},'xdata',x_s{ind+1}.x(i),'ydata',x_s{ind+1}.y(i)) + set(head2{ind+1},'xdata',x_s{ind+1}.x(i-1),'ydata',x_s{ind+1}.y(i-1)) + end + %set(body{ind+1},'xdata',x_s{ind+1}.x(j),'ydata',x_s{ind+1}.y(j)) + body_x{ind+1}(i) = x_s{ind+1}.x(i); body_y{ind+1}(i) = x_s{ind+1}.y(i); + set(body{ind+1},'xdata',body_x{ind+1},'ydata',body_y{ind+1}) + tail_x{ind+1}(i) = x_s{ind+1}.x(i); tail_y{ind+1}(i) = x_s{ind+1}.y(i); + set(tail{ind+1},'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}) + end + end + + tic; + while toc<time + drawnow + pause(0) + end; + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end +end + + +% Primary loop +for i = k+2:m + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + if ~isempty(punkty) + if any(punkty == i) + punkty_x{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.x(i); punkty_y{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.y(i); + punkty_ind{ind+1} = punkty_ind{ind+1} + 1; + set(h_punkty{ind+1}, 'Xdata', punkty_x{ind+1}, 'Ydata', punkty_y{ind+1}); + end + end + + j = i-1:i; + if no_head == 0, + set(head{ind+1},'xdata',x_s{ind+1}.x(i),'ydata',x_s{ind+1}.y(i)) + set(head2{ind+1},'xdata',x_s{ind+1}.x(i-1),'ydata',x_s{ind+1}.y(i-1)) + end +% set(body{ind+1},'xdata',x_s{ind+1}.x(j),'ydata',x_s{ind+1}.y(j)) +% set(tail{ind+1},'xdata',x_s{ind+1}.x(j-k),'ydata',x_s{ind+1}.y(j-k)) + tail_x{ind+1}(1:end-1) = tail_x{ind+1}(2:end); tail_y{ind+1}(1:end-1) = tail_y{ind+1}(2:end); + tail_x{ind+1}(end) = x_s{ind+1}.x(i); tail_y{ind+1}(end) = x_s{ind+1}.y(i); + set(tail{ind+1},'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}) + + body_x{ind+1}(i) = x_s{ind+1}.x(i); body_y{ind+1}(i) = x_s{ind+1}.y(i); + set(body{ind+1},'xdata',body_x{ind+1},'ydata',body_y{ind+1}) + end + end + + tic; + while toc<time + drawnow + pause(0); + end + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end; + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end; + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end; + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end; + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end; +end; + +% Clean up the tail +for i = m+1:m+k + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + j = i-1:i; + set(tail{ind+1},'xdata',x_s{ind+1}.x(j-k),'ydata',x_s{ind+1}.y(j-k)) + end + end; + + tic; + while toc<time + drawnow + pause(0); %drawnow; + end; + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end; + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end; + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end; + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end; + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end; + +end; + +set(hf, 'KeyPressFcn', ''); diff --git a/Ex6/matlab/design_task_1.m b/Ex6/matlab/design_task_1.m new file mode 100644 index 0000000..255d1dc --- /dev/null +++ b/Ex6/matlab/design_task_1.m @@ -0,0 +1,42 @@ +function h=design_task_1 +% type = 2; +% +% %interpolation filter +% switch type, +% case 1, +% Fp1 = 22050; Fp2 = 44100; +% +% case 2, +% Fp1 = 8000; Fp2 = 32000; +% F_trans = [3000 5000]; +% end +% L = Fp2 / Fp1; + +% M = 1; +Fsymb = 1500; Fp_out = 32*Fsymb; + +L = Fp_out/Fsymb + + +% filtr dla pierwszego stopnia interpolacji +r = 0.33; +h_rc=rcos4(10*L,L, 0.33); + +K = 8192; +[H_rc, f] = freqz(h_rc/L, 1, K, L*Fsymb); + +figure(1) +subplot(3,1,1) +plot(h_rc) +subplot(3,1,2) +plot(f, abs(H_rc)) +set(gca, 'Ylim', [0, 1.1], 'Xlim', [0, L*Fsymb/2]) +subplot(3,1,3) +plot(f, 20*log10(abs(H_rc))) +set(gca, 'Ylim', [-70, 3], 'Xlim', [0, L*Fsymb/2]) + +dane.h = h_rc/L; +dane.Fp = Fsymb; +save_filter_coef('ex6_task1_h_rc', dane, 1); + + diff --git a/Ex6/matlab/ex6_task1_h_rc.coef b/Ex6/matlab/ex6_task1_h_rc.coef new file mode 100644 index 0000000000000000000000000000000000000000..6ab8d126fd59d681a95262df077eb6d7313e8a5f GIT binary patch literal 1290 zcmW-he@xV69LKp1@HjF7e*|rq1mSiX%_6(!`+d&5q1PWqKP3N1LtJUfuB=uQCq-lH z2*xCsIH(yZP!pC};K@DDv(rW({J|_~IRAjeQZSMgSBTPF_41F~Zg=1B^L#$<A1}vf zLR_4~;aKlDb8VG)_u+UkoY(F3y#KiOhun_vwIqM!eDRsc)bk5f_qb54={wctBO%ql zp-DaV&6nzYeYZ+%jjFMZKDFk+pt>+KsCM1yS6R(x)rO(4danDpa_?(a1<!<3^{wq{ z^{G5HnlT#*p00|NhFA3XoBUpTY{Wb8UXD<=w~3~e)#C3bj*4~dVX^q;fY6bP;?2+% zQRBQWDyM!E+BqpQS6>#pw+xH?kG>P(%N-)Mu}+MP7mMVR%S6D{;NAMHx2N~T!AQ%3 z^{REKPURhos@dbetH3??>UXZM(62Ae*Dqyk)XwT6{q6jA?MmCNC(434`$|yz7wyq^ zE|lm~t|EQ3&98rHU!@DPQuRNTS5@GH7Uk*6QXM}|tv$F=icm#V+z6ytEpY|b=-Pc& zeq*C$g-%%Yp?=Hl8MRI%&REMXIqaUFlk7t~Q|vCW)NWdnZYSqt*q5g7x2xY?Vy86U zXYYL>(JuGQS=-KyS{JLL*7(6stQ*T;u{zsa)=V%{xQ8lL(#l8mAGr-WtA0pVHN=~Q z#o4B()Nej}q{M_`<)*g1*36bPo8nlT8GPt#vp}6TTPvbw;ksUvbFR<CCi{%u-)nrE zqo%MZY@XiHY3g4+V&)2K&GNjxX70dKW>cNpe3<aL{_DX#s%m<NHTy=qJ#u!7T|05u zelqQQdp<j6hk~i{i?=*7cDq2{94nSJGi5S8zDD}z56eJbtIXVVQeG`HGA(~Vc9jmx zL(VZ-K0P5{Uh=1`c<q+#{OMo$Rj$*w>(>O|rw_P%69Y-Uxk<P0<Ne8ZKYZsm{^J>* z<sIJT49?<A&gKs8;!f^n24-O<W@AQXWoBk)2X<j6c4J3&WoLFrgS%S%uL;_q5n7=c z+Myv@qAA*<F<PTJ+T#IU;0fN~5nkaL-r*r$;wj$ZF<#?2-je~cKqklr86hiVhU}0b zvP7oH78xUJWRC2SL9$3D$tD>kt7Mk!l3}t;rpY!LC+lRM?9&0dKqu%19ic09hVIZI zx<sew79FE&bdK)PLAppM=_Vbet8|v`(qXzxr|C8wr|Wc{?!y3B025#XjDQs|19rd= qSOQaE3ygs^FbDR)AXo&GU=xgjRWJ*7!7x|`(_kBngLN<u_WcLo#&K}~ literal 0 HcmV?d00001 diff --git a/Ex6/matlab/eyediagram2.m b/Ex6/matlab/eyediagram2.m new file mode 100644 index 0000000..0ced67e --- /dev/null +++ b/Ex6/matlab/eyediagram2.m @@ -0,0 +1,69 @@ +function hf = eyediagram2(x, offset, Nsymb, hf) + +if nargin == 4, + figure(hf); +else + hf = figure; +end +subplot(1,1,1) +if nargin == 2, + Nsymb = offset; + offset = ceil((Nsymb-1)/2); +% Nsymb = length(x)+1; +end +x(ceil(length(x)/Nsymb)*Nsymb) = 0; +hp = []; +% t_ = linspace(-Nsymb/2, Nsymb/2, Nsymb); +t_ = 0:Nsymb; +ind_ = 1:(Nsymb+1); +% if rem(Nsymb,2) == 1, +% t_ = (0:Nsymb+1) - Nsymb/2 - 0.5; +% +% ind_ = [0:Nsymb+1]; +% else +% t_ = (-1:Nsymb) - Nsymb/2 + 0.5; +% +% ind_ = [-1:Nsymb]; +% end + + +dt = offset - round(offset); +t_ = t_ + dt; offset = round(offset); + +% t_ = t_ / Nsymb; + +if sum(abs(imag(x))) == 0, + subplot(1,1,1); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + % plot(t_, +1+t_*0, 'k:'); + % plot(t_, -1+t_*0, 'k:'); + for ind = offset:Nsymb:length(x)-(Nsymb+1), + x_ = x(ind+ind_); + + hp(end+1) = plot(t_, real(x_)); +% hp(end+1) = plot(t_, imag(x_), 'r'); + end + hold off +else + subplot(1,2,1); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + % plot(t_, +1+t_*0, 'k:'); + % plot(t_, -1+t_*0, 'k:'); + subplot(1,2,2); + plot(t_, t_*0, 'k:'); + hold on + plot([0, 0], [-1.1, 1.1], 'k:'); + for ind = offset:Nsymb:length(x)-(Nsymb+1), + x_ = x(ind+ind_); + + subplot(1,2,1); + hp(end+1) = plot(t_, real(x_)); + subplot(1,2,2); + hp(end+1) = plot(t_, imag(x_), 'r'); + end + hold off +end diff --git a/Ex6/matlab/load_filter_coef.m b/Ex6/matlab/load_filter_coef.m new file mode 100644 index 0000000..8c8574c --- /dev/null +++ b/Ex6/matlab/load_filter_coef.m @@ -0,0 +1,107 @@ +function [coefficients, file_version] = load_filter_coef(filename) +% [coefficients, file_version] = load_filter_coef(filename) +% +% FIR filter coefficients +% coefficients.h +% - can be cell vector to store multiple FIR impulse responces +% For file_version >= 1 (default == 0), +% coefficients.Fp - sampling frequency +% +% This file is a part of Digital Signal Processing Engine +% \author Marek Blok +% \date 2008.03.28 + +% /*! File format (*.coef) - this is open format, for general use +% * (not only for storing coefficients) +% * +ind = find(filename == '.'); +if length(ind) == 0, + filename = [filename, '.coef']; +end +plik = fopen(filename, 'rb'); + +% * - (uchar) 1B - file version number +file_version = fread(plik, 1, 'uchar') + +switch file_version, + case 0, + case 1, + % * - (uint) 4B - Sampling frequency + coefficients.Fp = fread(plik, 1, 'uint32') + otherwise, + fclose(plik); + error('This version of coefficients file is unsupported'); +end + +% * - data - coefficients data (depends on fle version) +% * . +% * Data segment format: +% * -# (version: 0x00) +% * - (uchar) 1B - number of sample dimensions +% * 1 - real, 2 - complex, ... +no_of_channels = fread(plik, 1, 'uchar'); % complex +if no_of_channels == 1, + isComplex = 0; +elseif no_of_channels == 2, + isComplex = 1; +else + fclose(plik); + error('to many channels'); +end +% * - (uchar) 1B - sample component type +% * - DSP::e::FileType::FT_float (=1) : C++ float (32bit floating point) +% * - DSP::e::FileType::FT_short (=2) : C++ short (16bit signed integer) +% * - DSP::e::FileType::FT_uchar (=3) : C++ unsigned char (8bit unsigned integer with bias (0x80)) +% * - DSP::e::FileType::FT_double (=7) : C++ double (64bit floating point) +% * - DSP::e::FileType::FT_long_double (=8) : C++ long double (80bit floating point) +sample_type = fread(plik, 1, 'uchar'); +switch sample_type, + case 1, + sample_type = 'float'; + case 2, + sample_type = 'short'; + case 3, + sample_type = 'uchar'; + case 7, + sample_type = 'double'; + case 8, + sample_type = 'float80'; + otherwise + fclose(plik); + error('unknown sample type'); +end + +% * - (uchar) 1B - number of vectors +% * - 1 - FIR filter coefficients (one vector) +% * - 2 - IIR filter coefficients (two vectors) +% * - (x number of vectors) +% * - (ushort) 2B - number of samples in vector +% * - (x number of samples) +% * - (x number of sample dimensions) +% * - (sample componet type) xB - sample component +% * e.g. real, imag part +resp_no = fread(plik, 1, 'uchar'); + +if resp_no == 1, + N_FIR = fread(plik, 1, 'uint16'); + if isComplex, + dane = fread(plik, 2*N_FIR, sample_type); + coefficients.h = dane(1:2:2*N_FIR) + j*dane(2:2:2*N_FIR); + else + dane = fread(plik, N_FIR, sample_type); + coefficients.h = dane; + end +else + for ind_resp = 1:resp_no, + N_FIR = fread(plik, 1, 'uint16'); + if isComplex, + dane = fread(plik, 2*N_FIR, sample_type); + coefficients.h{ind_resp} = dane(1:2:2*N_FIR) + j*dane(2:2:2*N_FIR); + else + dane = fread(plik, N_FIR, sample_type); + coefficients.h{ind_resp} = dane; + end + end +end + +fclose(plik); diff --git a/Ex6/matlab/rcos4.m b/Ex6/matlab/rcos4.m new file mode 100644 index 0000000..233f780 --- /dev/null +++ b/Ex6/matlab/rcos4.m @@ -0,0 +1,51 @@ +function h_rc=rcos4(N,L, r) +%square root raised cosine + +fp=1000; + +if nargin==2, + r=0.5; +end + +n=(0:N-1)-(N-1)/2; +% t=n/fp; T=L/fp; + +% % L=pi*L; h_rc=sinc(pi*n/L).*cos(pi*r*n/L)./(L-4*(r.^2)*(n.^2)/L); +% h_rc=sinc(t/T).*cos(r*pi*t/T)./(1-4*(r.^2)*(t.^2)/T.^2); +h_rc=(1-r)*sinc((1-r)*n/L) + ... + r*( sinc(r*n/L+1/4).*cos(pi*(n/L+1/4)) + ... + sinc(r*n/L-1/4).*cos(pi*(n/L-1/4)) ); +% % h_rc= 4*r* ( cos((1+r)*pi*n/L) + sin((1-r)*pi*n/L)./(4*r*n/L)) ... +% % ./ (pi*sqrt(1/L)*(1-(4*r*n/L).^2)); +% +ind=~isfinite(h_rc); +h_rc(ind)=0; + +if nargout==0, + figure(1) + subplot(3,1,1) + stem(n/L,h_rc) + subplot(3,1,2) + n=-(N-1):(N-1); +% size(n) +% size(conv(h_rc,h_rc)) + stem(n/L,conv(h_rc,h_rc)) + subplot(3,1,3) + H=freqz(h_rc,1,2048, 'whole'); + H=abs(H(1:end/2)); + f=linspace(0,1000,length(H)); + plot(f,H) + + H=freqz(ones(1,L),1,2048, 'whole'); + H=abs(H(1:end/2)); + f=linspace(0,1000,length(H)); + hold on + plot(f,H,'k') + hold off + H=freqz(conv(h_rc,h_rc)/L,1,2048, 'whole'); + H=abs(H(1:end/2)); + f=linspace(0,1000,length(H)); + hold on + plot(f,H,'m') + hold off +end \ No newline at end of file diff --git a/Ex6/matlab/readaudiofile.m b/Ex6/matlab/readaudiofile.m new file mode 100644 index 0000000..825a9a8 --- /dev/null +++ b/Ex6/matlab/readaudiofile.m @@ -0,0 +1,215 @@ +function [x, Fs] = readaudiofile(filename, param) +% [x, Fs] = readaudiofile(filename, param) +% +% returns vector x of the size SIZE=[samples channels]. +% eg. x = x(:,1) + j*x(:,2); +% +% special uses: +% param == 'size': returns SIZE in x +% param == 'cplx': for stereo files returns x as a complex vector instead of matrix +% +% supported file types: +% *.flt +% *.wav +% *.tape +% last modification: 2021.03.29 +% Author: Marek Blok + +return_cplx = 0; +if nargin == 1, + param = inf; +else + if strcmp(param, 'cplx') == 1, + return_cplx = 1; + param = inf; + end +end + +ind = find(filename == '.'); +if length(ind) == 0, + file_type = 'w'; % *.wav +else + ind = ind(end); + + temp = filename(ind+1:end); + + file_type = 'u'; % unknown format + if strcmp(temp, 'flt') == 1, + file_type = 'f'; % two channel floting point + elseif strcmp(temp, 'wav') == 1, + file_type = 'w'; % *.wav + elseif strcmp(temp, 'tape') == 1, + file_type = 't'; % *.tape + end +end + +switch file_type, + case 'w', + if strcmp(param, 'size') == 1, + if exist('audioread','file') == 0 + x = wavread(filename, 'size'); + % siz = [samples channels]. + else + info = audioinfo(filename); + x = [info.TotalSamples, info.NumChannels]; + Fs = info.SampleRate; + end + else + if isfinite(param) + if exist('audioread','file') == 0 + [x, Fs] = wavread(filename, param); + else + if length(param) == 1 + param = [1, param]; + end + [x, Fs] = audioread(filename, param); + end + else + if exist('audioread','file') == 0 + [x, Fs] = wavread(filename); + else + [x, Fs] = audioread(filename); + end + end + end + + case 't' + plik = fopen(filename, 'rb'); + if plik == -1, + error('File does not exist !!!'); + end + + header.size = fread(plik, 1, 'uint32', 0) + 4; + + header.fname = char(fread(plik, 256, 'char', 0).'); + header.cfg_fname = char(fread(plik, 256, 'char', 0).'); + header.sw_rev = fread(plik, 1, 'uint32', 0); + header.hw_rev = fread(plik, 1, 'uint32', 0); + header.file_ = fread(plik, 1, 'uint32', 0); + header.tape_type = fread(plik, 1, 'uint32', 0); + header.start_time = fread(plik, 1, 'int32', 0); % time_t + header.end_time = fread(plik, 1, 'int32', 0); % time_t + + header.total_samples = fread(plik, 1, 'uint32', 0); + file_length = header.total_samples * 4 + header.size + header.current_sample = fread(plik, 1, 'uint32', 0); + header.loop_start = fread(plik, 1, 'int64', 0); + header.loop_end = fread(plik, 1, 'int64', 0); + header.loop = fread(plik, 1, 'int32', 0); + header.group_size_32 = fread(plik, 1, 'uint32', 0); + header.block_size = fread(plik, 1, 'uint32', 0); + header.block_count = fread(plik, 1, 'uint32', 0); + header.fifo_size = fread(plik, 1, 'uint32', 0); + + + header.comment = char(fread(plik, 256, 'char', 0).'); + header.tmp = char(fread(plik, 20, 'char', 0).'); % time_t + header.status = fread(plik, 1, 'uint32', 0); + header.timestamps = fread(plik, 1, 'int32', 0); + header.freq = fread(plik, 1, 'float', 0); + header.cplx_datarate = fread(plik, 1, 'float', 0); + +% ftell(plik) + header.reserved = fread(plik, 128, 'uint32', 0); +% header.reserved.' + + header + ftell(plik) + + header.sample_type = 2; + header.ch_no = 2; + header.Fs = NaN; + + sample_type = 'int16'; + sample_size = 2; + + header_size = header.size; + if strcmp(param, 'size') == 1, + fseek(plik, 0, 'eof'); + size = (ftell(plik) - header_size) / sample_size / header.ch_no; % sizeof(float) *2 + x = size; + else + fseek(plik, header_size, 'bof'); + + len = param(1); + if length(param) > 1, + fseek(plik, sample_size*header.ch_no*(param(1)-1), 'cof'); + len = param(2) - param(1) + 1; + end + +% x = fread(plik, [header.ch_no, len], sample_type); + x = fread(plik, [header.ch_no, len], sample_type, 0); + x = x.'; + end + fclose(plik); + + case 'f' + plik = fopen(filename, 'rb'); + if plik == -1, + error('File does not exist !!!'); + end + + % 3 B - wersja pliku (w tym typ próbek) + % 1 B - liczba kanałów + % 4 B - szybkoć próbkowania + header_size = 8; + header.ver = fread(plik, 1, 'uint8'); + header.sample_type = fread(plik, 1, 'uint16'); + header.ch_no = fread(plik, 1, 'uint8'); + header.Fs = fread(plik, 1, 'uint32'); + + Fs = header.Fs; + + switch (header.ver), + case 0, + switch (header.sample_type), + case 0, + sample_type = 'float'; + sample_size = 4; + case 1, + sample_type = 'uchar'; + sample_size = 1; + case 2, + sample_type = 'short'; + sample_size = 2; + case 3, + sample_type = 'int'; + sample_size = 4; + otherwise + error('Unsupported *.flt sample type !!!'); + end + otherwise + error('Unsupported *.flt file version !!!'); + end + + + if strcmp(param, 'size') == 1, + fseek(plik, 0, 'eof'); + size = (ftell(plik) - header_size) / sample_size / header.ch_no; % sizeof(float) *2 + x = size; + else + len = param(1); + status = 0; + if length(param) > 1, + status = fseek(plik, sample_size*header.ch_no*(param(1)-1), 'cof'); + len = param(2) - param(1) + 1; + end + + if (status == -1) + x = []; + else + x = fread(plik, [header.ch_no, len], sample_type); + x = x.'; + end + end + + fclose(plik); + otherwise + error('Unsupported file format !!!'); +end + +if return_cplx == 1, + if length(x(1,:)) == 2, + x = x(:,1) + j*x(:,2); + end +end \ No newline at end of file diff --git a/Ex6/matlab/save_filter_coef.m b/Ex6/matlab/save_filter_coef.m new file mode 100644 index 0000000..5a1bf6b --- /dev/null +++ b/Ex6/matlab/save_filter_coef.m @@ -0,0 +1,226 @@ +function save_filter_coef(filename, coefficients, file_version) +% save_filter_coef(filename, coefficients [, file_version]) +% +% FIR filter coefficients +% coefficients.h +% - can be cell vector to store multiple FIR impulse responces +% - if any of the impulse responses is complex then all responses will +% be stored as complex +% IIR filter coefficients +% coefficients.a +% coefficients.b +% +% For file_version >= 1 (default == 0), +% coefficients.Fp - sampling frequency +% +% This file is a part of Digital Signal Processing Engine +% +% File format (*.coef) - this is open format, for general use +% * (not only for storing coefficients) +% +% File format (*.h) - this is C++ header file for DSPElib with hardcoded *.coef equivalent +% +% \author Marek Blok +% \date 2020.02.22 + +if nargin == 2, + file_version = 0; +end + +ind = find(filename == '.'); +if length(ind) == 0, + filename = [filename, '.coef']; +end + +% detect mode based on file extention +[PATHSTR,NAME,EXT] = fileparts(filename); +switch EXT + case '.h' + mode = 'h-file'; + plik = fopen(filename, 'wt'); + + case '.coef' + mode = 'coefs-file'; + plik = fopen(filename, 'wb'); + + otherwise + mode = 'unknown-file'; + error(mode); +end + +switch mode + case 'h-file' + fprintf(plik, '// save_filter_coef output (hard coded coefficients)\n'); + fprintf(plik, '#pragma once\n'); + fprintf(plik, '#include <DSP_lib.h>\n'); + fprintf(plik, '\n'); + + if isfield(coefficients, 'h'), + h = coefficients.h; + + for ind_h = 0:length(h)-1 + fprintf(plik, 'DSP::Float_vector Farrow_coefs_row_%i = {', ind_h); + h_tmp = h{ind_h+1}; + for ind_p = 0:length(h_tmp)-1, + if ind_p > 0 + fprintf(plik, ', '); + end + fprintf(plik, '%.15ff', h_tmp(ind_p+1)); + end + %0.1f, 0.2f, 0.1f + fprintf(plik, '};\n'); + end + fprintf(plik, '\n'); + + %vector <DSP::Float_vector> Farrow_coefs = { coefs_0, coefs_1, coefs_2 }; + fprintf(plik, 'vector <DSP::Float_vector> Farrow_coefs = {'); + for ind_h = 0:length(h)-1 + if ind_h > 0 + fprintf(plik, ', '); + end + if (ind_h < length(h_tmp)) & (rem(ind_h, 5) == 0) + fprintf(plik, '\n '); + end + fprintf(plik, 'Farrow_coefs_row_%i', ind_h); + end + fprintf(plik, '\n };\n'); + + fprintf(plik, '\n'); + fprintf(plik, 'const unsigned int p_Farrow = %i; // should be equal to Farrow_coefs[0].size()-1\n', length(h{1})-1); + fprintf(plik, 'const unsigned int N_FSD = %i; // should be equal to Farrow_coefs.size()\n', length(h)); + end + + if isfield(coefficients, 'b'), + fclose all; + error('unsupported: coefficients.b'); + end + if isfield(coefficients, 'a'), + fclose all; + error('unsupported: coefficients.a'); + end + + case 'coefs-file' + % * - (uchar) 1B - file version number + fwrite(plik, file_version, 'uchar'); + + switch file_version, + case 0, + case 1, + % * - (uint) 4B - Sampling frequency + if isfield(coefficients, 'Fp'), + fwrite(plik, coefficients.Fp, 'uint32'); + else + fclose(plik); + error('Input data does not contain Fp'); + end + otherwise, + fclose(plik); + error('This version of coefficients file is unsupported'); + end + + if isfield(coefficients, 'h'), + isFIR = 1; + if iscell(coefficients.h) + resp_no = length(coefficients.h); + % if resp_no = 1; + % coefficients.h = coefficients.h{1}; + % end + else + resp_no = 1; + end + if resp_no == 1, + isComplex = any(imag(coefficients.h(:))); + else + isComplex = false; + for ind_resp = 1:resp_no, + isComplex = isComplex | any(imag(coefficients.h{ind_resp}(:))); + end + end + else + isFIR = 0; + isComplex = any(imag(coefficients.a(:))) | any(imag(coefficients.b(:))); + end + + % * - data - coefficients data (depends on fle version) + % * . + % * Data segment format: + % * -# (version: 0x00) + % * - (uchar) 1B - number of sample dimensions + % * 1 - real, 2 - complex, ... + if isComplex, + fwrite(plik, 2, 'uchar'); % complex + else + fwrite(plik, 1, 'uchar'); % real + end + % * - (uchar) 1B - sample component type + % * - DSP::e::FileType::FT_float (=1) : C++ float (32bit floating point) + % * - DSP::e::FileType::FT_short (=2) : C++ short (16bit signed integer) + % * - DSP::e::FileType::FT_uchar (=3) : C++ unsigned char (8bit unsigned integer with bias (0x80)) + % * - DSP::e::FileType::FT_double (=7) : C++ double (64bit floating point) + % * - DSP::e::FileType::FT_long_double (=8) : C++ long double (80bit floating point) + fwrite(plik, 1, 'uchar'); + + % * - (uchar) 1B - number of vectors + % * - 1 - FIR filter coefficients (one vector) + % * - 2 - IIR filter coefficients (two vectors) + % * - (x number of vectors) + % * - (ushort) 2B - number of samples in vector + % * - (x number of samples) + % * - (x number of sample dimensions) + % * - (sample componet type) xB - sample component + % * e.g. real, imag part + if isFIR, + fwrite(plik, resp_no, 'uchar'); + + if iscell(coefficients.h) + for ind_resp = 1:resp_no, + N_FIR = length(coefficients.h{ind_resp}); + fwrite(plik, N_FIR, 'uint16'); + if isComplex, + dane(1:2:2*N_FIR) = real(coefficients.h{ind_resp}); + dane(2:2:2*N_FIR) = imag(coefficients.h{ind_resp}); + fwrite(plik, dane, 'float'); + else + fwrite(plik, real(coefficients.h{ind_resp}), 'float'); + end + end + else + N_FIR = length(coefficients.h); + fwrite(plik, N_FIR, 'uint16'); + if isComplex, + dane(1:2:2*N_FIR) = real(coefficients.h); + dane(2:2:2*N_FIR) = imag(coefficients.h); + fwrite(plik, dane, 'float'); + else + fwrite(plik, real(coefficients.h), 'float'); + end + end + + else + fwrite(plik, 2, 'uchar'); + + N_a = length(coefficients.a); + fwrite(plik, N_a, 'uint16'); + if isComplex, + dane(1:2:2*N_a) = real(coefficients.a); + dane(2:2:2*N_a) = imag(coefficients.a); + fwrite(plik, dane, 'float'); + else + fwrite(plik, real(coefficients.a), 'float'); + end + + + N_b = length(coefficients.b); + fwrite(plik, N_b, 'uint16'); + if isComplex, + dane(1:2:2*N_b) = real(coefficients.b); + dane(2:2:2*N_b) = imag(coefficients.b); + fwrite(plik, dane, 'float'); + else + fwrite(plik, real(coefficients.b), 'float'); + end + end + +end +fclose(plik); + diff --git a/Ex6/matlab/test.m b/Ex6/matlab/test.m new file mode 100644 index 0000000..341dc4f --- /dev/null +++ b/Ex6/matlab/test.m @@ -0,0 +1,57 @@ +function test + +Fsymb = 3000; +K = 32; +[x, Fp] = readaudiofile('../ex6_task1.flt', 'cplx'); +[xb, Fp] = readaudiofile('../ex6_task1b.flt', 'cplx'); + +length(x) +Fp + +% y = []; +% while length(xb) >= K +% y = [y; xb(K:-1:1)]; +% xb(1:K) = []; +% end +% xb = [y; xb]; + +figure(1) +subplot(2,1,1) +plot(real(x)) +hold on +plot(real(xb), 'r') +hold off +subplot(2,1,2) +plot(imag(x)) + +K_psd = 8*1024; + +figure(2) +specgram(x) + +figure(3) +% psd(x) +F = linspace(-Fp/2, Fp/2, 2*K_psd+1); F(end) = []; +% pwelch(x, blackman(K_psd), ceil(0.5*K_psd), F, Fp); +[Pxx, F] = pwelch(x, blackman(K_psd), ceil(0.5*K_psd), F, Fp); +[Pxx_b, F] = pwelch(xb, blackman(K_psd), ceil(0.5*K_psd), F, Fp); +Pxx_dB = 10*log10(Pxx); +Pxx_b_dB = 10*log10(Pxx_b); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp/2, Fp/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(K/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F/1000,Pxx_dB); +plot(F/1000,Pxx_b_dB, 'm:'); +hold off +% set(gca,'Xtick', (-(floor(K/2)+1/2):1/2:(ceil(K/2)+1/2))*Fsymb/1000, 'Xgrid', 'on'); + +xlabel('Frequency [kHz]') +ylabel('Power spectral density [dB/Hz]') \ No newline at end of file diff --git a/Ex6/matlab/test_DBPSK.m b/Ex6/matlab/test_DBPSK.m new file mode 100644 index 0000000..cfbdbd0 --- /dev/null +++ b/Ex6/matlab/test_DBPSK.m @@ -0,0 +1,32 @@ +function test_DBPSK + +x = readaudiofile('../ex6_task2_text_in.flt'); +x_en = readaudiofile('../ex6_task2_text_en.flt', 'cplx'); +x_de = readaudiofile('../ex6_task2_text_de.flt'); + +figure(1) +subplot(3,1,1) +stem(x(1:20)) +subplot(3,1,2) +stem(real(x_en(1:20))) +hold on +stem(imag(x_en(1:20)), 'r') +hold off + +subplot(3,1,3) +stem(2*(x_de(1:20)-0.5)) + +return +y(1) = 1; +for ind = 1:length(x_en), + y(ind+1) = y(1)*x_en(ind); +end + +x_en2 = [1; x_en]; +x_en2d = [x_en; 1]; + +y = x_en2 .* conj(x_en2d); + +hold on +stem(real(y(1:20))*0.9, 'r') +hold off diff --git a/Ex6/matlab/test_FMD_signal.m b/Ex6/matlab/test_FMD_signal.m new file mode 100644 index 0000000..883e63e --- /dev/null +++ b/Ex6/matlab/test_FMD_signal.m @@ -0,0 +1,72 @@ +function test_FMD_signal(filename, N_DFT, Fsymb, use_only_real_part) + +if nargin == 0, + % parametry sygnału wielotonowego + Fsymb = 3000; + N_DFT = 16; + filename = '../ex6_task1.flt'; + use_only_real_part = 1; +end + +try + [x, Fp] = readaudiofile(filename, 'cplx'); +catch + [x, Fp] = readaudiofile(filename); +end + +K_psd = 8*1024; +K_spect = 1024; + +if use_only_real_part ~= 0, + x = real(x); + % freq_range = 'onesided'; + if rem(K_spect,2) == 1, + K_spect = K_spect+1; + end + F = linspace(0, Fp/2, K_spect+1); F(end) = []; +else + % freq_range = 'centered'; + F = linspace(-Fp/2, Fp/2, 2*K_spect+1); F(end) = []; +end + +length(x) +Fp + +figure(1) +subplot(2,1,1) +plot(real(x)) +subplot(2,1,2) +plot(imag(x)) + +F_psd = linspace(-Fp/2, Fp/2, 2*K_psd+1); F_psd(end) = []; + +figure(12) +set(12, 'Name', 'Spectrogram analizowanego sygnału'); +% specgram(x) +spectrogram(x, blackman(K_spect), ceil(0.5*K_spect), F, Fp, 'yaxis'); +colormap(jet) + +figure(13) +set(13, 'Name', 'Widmo gęstoci mocy analizowanego sygnału'); +% psd(x) +% pwelch(x, blackman(K_psd), ceil(0.5*K_psd), F, Fp); +Pxx = pwelch(x, blackman(K_psd), ceil(0.5*K_psd), F_psd, Fp); +Pxx_dB = 10*log10(Pxx); +Pxx_dB_max = ceil(max(Pxx_dB)/3)*3; Y_range = [-100, 0]+Pxx_dB_max; +plot([0,0], Y_range, 'r--'); +set(gca, 'Xlim', [-Fp/2, Fp/2]/1000, 'Ylim', Y_range); +hold on +for ind = 1:ceil(N_DFT/2), + plot(ind*Fsymb/1000*[1,1], Y_range, 'r--'); + plot(-ind*Fsymb/1000*[1,1], Y_range, 'r--'); + + plot((ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); + plot(-(ind-0.5)*Fsymb/1000*[1,1], Y_range, 'k-'); +end +plot(F_psd/1000,Pxx_dB); +hold off +% set(gca,'Xtick', (-(floor(K/2)+1/2):1/2:(ceil(K/2)+1/2))*Fsymb/1000, 'Xgrid', 'on'); + +xlabel('Frequency [kHz]') +ylabel('Power spectral density [dB/Hz]') + diff --git a/Ex6/matlab/test_task2.m b/Ex6/matlab/test_task2.m new file mode 100644 index 0000000..150ebf5 --- /dev/null +++ b/Ex6/matlab/test_task2.m @@ -0,0 +1,116 @@ +function test_task2(ind) +if nargin == 0, + ind = 2*(3+2*4); +end +[x, Fp] = readaudiofile('../ex6_task1.flt'); +[y, Fp] = readaudiofile('../ex6_task2a.flt', 'cplx'); +[y_ref, Fp] = readaudiofile('../ex6_task1a.flt', 'cplx'); + +K = 32; M = 8; +% K = 16; M = 8; + +figure(1) +subplot(2,1,1) +plot(x) +subplot(2,2,3) +plot(y, 'b.') +axis equal +subplot(2,2,4) +plot(y_ref, 'b.') +axis equal +% % plot(y) + +% y = y(21+1:end); + +figure(2) +subplot(2,1,1) +stem(real(y)/max(abs(y)), 'b') +hold on +stem(real(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(real(y), real(y_ref)); +plot(l, c) + +figure(3) +subplot(2,1,1) +stem(imag(y)/max(abs(y)), 'b') +hold on +stem(imag(y_ref), 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(imag(y), imag(y_ref)); +plot(l, c) + +n = [0:length(y)-1].'; +het = exp(-j*(8)*pi/(32)*n); +% het = 1; +figure(13) +psd(het.*y) +% return + +% xt{1} = ((-1).^(1:length(y)).').*y/max(abs(y)); +xt{1} = het.*y/max(abs(y)); +xt{2} = y_ref; +trajekt_(xt, 10, 1:M:length(y), 0, 1) +tmp = xt{1}; +eyediagram2(tmp(1:100*M), 0, M, 15); +pause + +y = y(1:M:end); +% y = y.*exp(j*pi/4); +s_re = real(y) > 0; +s_im = imag(y) > 0; + +bin = zeros(1,2*length(s_re)); +bin(1:2:end) = s_re; +bin(2:2:end) = s_im; + +bits_per_char = 8; +%ind = 0; + +h = fopen('../Ex6_task1.cpp'); +bin2 = fread(h, 800, 'ubit1').'; +fclose(h) +% +figure(4) +subplot(2,1,1) +[c, l] = xcorr(s_re, bin2(1:2:end)); +plot(l, c) +subplot(2,1,2) +[c, l] = xcorr(s_re, bin2(2:2:end)); +plot(l, c) + +% return +figure(5) +subplot(2,1,1) +stem(bin, 'b') +hold on +stem(bin2, 'r') +hold off +subplot(2,1,2) +[c, l] = xcorr(bin, bin2); +plot(l, c) + +% bin = bin_root.' +bin = bin((ind+1):end); +% for ind= 1:bits_per_char, +for ind= 1, + bin_ = bin(ind:end); + B = floor(length(bin_)/bits_per_char)*bits_per_char; + + tekst = bin_(1:bits_per_char:B); + for bit = 1:bits_per_char-1, +% tekst = tekst + (2^bit)*bin_((bit+1):bits_per_char:B); + tekst = tekst + (2^((bits_per_char-1)-bit))*bin_((bit+1):bits_per_char:B); + end + char(tekst) +end + + +% +% B_pre = length(pre)*bits_per_char; +% bin_pre = zeros(B_pre,1); +% for ind = 0:bits_per_char-1, +% bin_pre((ind+1):bits_per_char:end) = rem(floor(pre / (2^ind)),2); +% end diff --git a/Ex6/matlab/trajekt_.m b/Ex6/matlab/trajekt_.m new file mode 100644 index 0000000..696ad41 --- /dev/null +++ b/Ex6/matlab/trajekt_.m @@ -0,0 +1,403 @@ +function klawisz=trajekt_(x_s, time, punkty, no_head, pause_on_start) +% \added 2019.10.18 żółte tło okna w trybie pauzy +% \added 2019.10.18 prosty tryb testowania (ustawiajšc test_mode = 1; w linii 11) +% \fixed 2019.10.18 wyłšczenie zoom przed definiowaniem 'KeyPressFcn' +% \added 2017.10.24 dodano obsługę trybu, w którym x_s zawiera dane i +% etykiety (interpreter LaTeX): x_s{1}.data = ..., x_s{1}.label = '?'; +% \fixed 2015.10.27 addapted to matlab 2015b +% \fixed 2006.11.14 fixed axes position in single and double plot mode +% \added 2006.11.14 input option no_head +% \added 2006.11.14 input option pause_on_start + +test_mode = 0; +if (test_mode == 1) & (nargin == 0) + x_s = exp(sqrt(-1)*0.1*[0:1000]); + klawisz = trajekt_(x_s, 100, [], 0, 1); + return; +end + +if nargin < 5, + pause_on_start = 0; +end +if nargin < 4, + no_head = 1; +end +if nargin < 3, + punkty = []; +end +labels = {}; + + +if nargin == 0, +error('Not enough input arguments.'); +else + if isreal(x_s), + ile = 1; + x = x_s; + y = x; x = 1:length(y); + + clear x_s; + x_s{1}.x = x; + x_s{1}.y = y; + max1=max(abs(x(~isinf(x)))); + zakres=[-max1 max1 -max1 max1]; + else + if iscell(x_s) == 0, + ile = 1; + y=imag(x_s); + x=real(x_s); + clear x_s; + x_s{1}.x = x; + x_s{1}.y = y; + max1=max(max(abs(x(~isinf(x)))),max(abs(y(~isinf(y))))); + zakres=[-max1 max1 -max1 max1]; + else + x_s_tmp = x_s; + clear x_s + + ile = length(x_s_tmp); + max1 = eps; + for ind = 1:ile, + if isfield(x_s_tmp{ind}, 'label'), + labels{ind} = x_s_tmp{ind}.label; + end + if isfield(x_s_tmp{ind}, 'data'), + x_s_tmp{ind} = x_s_tmp{ind}.data; + end + x=real(x_s_tmp{ind}); + y=imag(x_s_tmp{ind}); + x_s{ind}.x = x; + x_s{ind}.y = y; + + max1=max([max(abs(x(~isinf(x)))),max(abs(y(~isinf(y)))), max1]); + end + zakres=[-max1 max1 -max1 max1]; + end + end +end + + +% if nargin == 0, +% error('Not enough input arguments.'); +% else +% if isreal(x), +% % y = x; x = 1:length(y); +% % max_y=max(abs(x(~isinf(x)))); +% % min_y=-max_x; +% % max_x=length(y); +% % min_x=1; +% y = x; x = 1:length(y); +% max_y=10; +% min_y=-10; +% max_x=10; +% min_x=10; +% else +% % y=imag(x); +% % x=real(x); +% % max_x=max(abs(x(~isinf(x)))); +% % min_x=-max_x; +% % max_y=max_x; +% % min_y=-max_x; +% y=imag(x); +% x=real(x); +% max_x=10; +% min_x=-10; +% max_y=10; +% min_y=-10; +% +% end +% end +if nargin==1, + time=0.01; +else + time=time/length(x); +end +p = 0.02; + +hf=findobj(0,'tag','trajektoria'); +if isempty(hf) + hf=figure('NextPlot','add', 'Color', 'w'); +else + figure(hf); +end +set(hf,'tag','trajektoria', 'Name', 'Trajektorie - dr inż. Marek Blok (14.11.2006)'); +delete(findobj(hf,'type', 'axes')); + +global klawisz +global trajekt_hf + +if pause_on_start == 0, + klawisz = '~'; % od razu ruszaj +else + klawisz = 13; % pauza na starcie +end +trajekt_hf = hf; +% set(hf, 'KeyPressFcn', 'global klawisz; fig = get(0, ''PointerWindow''); if fig ~= 0, klawisz = get(fig,''CurrentCharacter''); if isempty(klawisz), klawisz = ''~''; end; end;'); +zoom(hf, 'off'); +set(hf, 'KeyPressFcn', 'global klawisz; global trajekt_hf, fig = trajekt_hf; if fig ~= 0, klawisz = get(fig,''CurrentCharacter''); if isempty(klawisz), klawisz = ''~''; end; end;'); + +min_x=-5; +max_x=5; +min_y=-5; +max_y=5; + +if ile == 1, + ax{1}=axes('Units','Normalized','Position',[0, 0, 1, 1]+ [1, 1, -2, -2]*0.05); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= 1 + title(labels{1}, 'Interpreter', 'latex'); + end + set(ax{1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + co = get(ax{1},'colororder'); +elseif ile == 2, + for ind = 0:1, + ax{ind+1}=axes('Units','Normalized','Position',[ind*0.5 0 [0.5 1]]+ [1, 1, -2, -2]*0.05); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= 2 + title(labels{ind+1}, 'Interpreter', 'latex'); + end + set(ax{ind+1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + end + co = get(ax{1},'colororder'); +else + % 12 + for ind = 0:ile-1, + % 4 x 3 + x_ = rem(ind,4); + y_ = (ind-x_)/4; + ax{ind+1}=axes('Units','Normalized','Position',[x_*0.25 1-(y_+1)*0.33 [0.25 0.33]*0.95]); + if ~ishold, + axis([min_x max_x ... + min_y max_y]/5) + axis square + end + if length(labels) >= ile + title(labels{ind+1}, 'Interpreter', 'latex'); + end + set(ax{ind+1},'Xtick', -1:0.5:1, 'Ytick', -1:0.5:1, 'Xgrid','on','Ygrid','on'); + end + co = get(ax{1},'colororder'); +end + + + +m = length(x_s{1}.x); +for ind = 1:ile-1, + if m == 0, + m = length(x_s{ind+1}.x); + else + m = min([m, length(x_s{ind+1}.x)]); + end +end +k = ceil(p*m); + +%ax1 = newplot; +% ax1=axes('Units','Pixels','Position',[30 30 300 300]); + +% ax2=axes('Units','Pixels','Position',[360 30 300 300]); +for ind = 0:ile-1, + axes(ax{ind+1}) + + % Choose first three colors for head, body, and tail + if no_head == 0, + head{ind+1} = line('color',co(1,:),'linestyle', 'none', 'marker', 'o', 'linewidth', 3, ... + 'xdata',x_s{ind+1}.x(1),'ydata',x_s{ind+1}.y(1)); + end + %body_x = []; body_y = []; + body_x{ind+1} = NaN*ones(1,m); body_y{ind+1} = NaN*ones(1,m); + body_x{ind+1}(1) = x_s{ind+1}.x(1); body_y{ind+1}(1) = x_s{ind+1}.y(1); + body{ind+1} = line('color','b','linestyle','-', 'marker','none', ... %'erase','none', ... + 'xdata',body_x{ind+1},'ydata',body_y{ind+1}); + %tail_x = []; tail_y = []; + tail_x{ind+1} = NaN*ones(1,k+1); tail_y{ind+1} = NaN*ones(1,k+1); + tail_x{ind+1}(1) = x_s{ind+1}.x(1); tail_y{ind+1}(1) = x_s{ind+1}.y(1); + tail{ind+1} = line('color','r','linestyle','-','marker','none', ... %'erase','none', ... + 'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}); + if no_head == 0, + head2{ind+1} = line('color',co(4,:),'linestyle', 'none', 'marker','o', 'linewidth', 3, ... + 'xdata',x_s{ind+1}.x(1),'ydata',x_s{ind+1}.y(1)); + end + + if ile > 2, + h_punkty{ind+1} = line('color','k','linestyle', 'none', 'marker','.', 'markersize', 20, ... %'erase','none', ... + 'xdata',0,'ydata',NaN); + else + h_punkty{ind+1} = line('color','k','linestyle', 'none', 'marker','.', 'markersize', 24, ... %'erase','none', ... + 'xdata',0,'ydata',NaN); + end; + punkty_x{ind+1} = NaN*punkty; punkty_y{ind+1} = NaN*punkty; + punkty_ind{ind+1} = 1; +end; + +% Grow the body +for i = 2:k+1 + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + if ~isempty(punkty) + if any(punkty == i) + punkty_x{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.x(i); punkty_y{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.y(i); + punkty_ind{ind+1} = punkty_ind{ind+1} + 1; + set(h_punkty{ind+1}, 'Xdata', punkty_x{ind+1}, 'Ydata', punkty_y{ind+1}); + end + end + + j = i-1:i; + if no_head == 0, + set(head{ind+1},'xdata',x_s{ind+1}.x(i),'ydata',x_s{ind+1}.y(i)) + set(head2{ind+1},'xdata',x_s{ind+1}.x(i-1),'ydata',x_s{ind+1}.y(i-1)) + end + %set(body{ind+1},'xdata',x_s{ind+1}.x(j),'ydata',x_s{ind+1}.y(j)) + body_x{ind+1}(i) = x_s{ind+1}.x(i); body_y{ind+1}(i) = x_s{ind+1}.y(i); + set(body{ind+1},'xdata',body_x{ind+1},'ydata',body_y{ind+1}) + tail_x{ind+1}(i) = x_s{ind+1}.x(i); tail_y{ind+1}(i) = x_s{ind+1}.y(i); + set(tail{ind+1},'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}) + end + end + + tic; + while toc<time + drawnow + pause(0) + end; + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end +end + + +% Primary loop +for i = k+2:m + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + if ~isempty(punkty) + if any(punkty == i) + punkty_x{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.x(i); punkty_y{ind+1}(punkty_ind{ind+1}) = x_s{ind+1}.y(i); + punkty_ind{ind+1} = punkty_ind{ind+1} + 1; + set(h_punkty{ind+1}, 'Xdata', punkty_x{ind+1}, 'Ydata', punkty_y{ind+1}); + end + end + + j = i-1:i; + if no_head == 0, + set(head{ind+1},'xdata',x_s{ind+1}.x(i),'ydata',x_s{ind+1}.y(i)) + set(head2{ind+1},'xdata',x_s{ind+1}.x(i-1),'ydata',x_s{ind+1}.y(i-1)) + end +% set(body{ind+1},'xdata',x_s{ind+1}.x(j),'ydata',x_s{ind+1}.y(j)) +% set(tail{ind+1},'xdata',x_s{ind+1}.x(j-k),'ydata',x_s{ind+1}.y(j-k)) + tail_x{ind+1}(1:end-1) = tail_x{ind+1}(2:end); tail_y{ind+1}(1:end-1) = tail_y{ind+1}(2:end); + tail_x{ind+1}(end) = x_s{ind+1}.x(i); tail_y{ind+1}(end) = x_s{ind+1}.y(i); + set(tail{ind+1},'xdata',tail_x{ind+1},'ydata',tail_y{ind+1}) + + body_x{ind+1}(i) = x_s{ind+1}.x(i); body_y{ind+1}(i) = x_s{ind+1}.y(i); + set(body{ind+1},'xdata',body_x{ind+1},'ydata',body_y{ind+1}) + end + end + + tic; + while toc<time + drawnow + pause(0); + end + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end; + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end; + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end; + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end; + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end; +end; + +% Clean up the tail +for i = m+1:m+k + for ind = 0:ile-1, + if length(x_s{ind+1}.x) > 0, + j = i-1:i; + set(tail{ind+1},'xdata',x_s{ind+1}.x(j-k),'ydata',x_s{ind+1}.y(j-k)) + end + end; + + tic; + while toc<time + drawnow + pause(0); %drawnow; + end; + + if (upper(klawisz) == 'Q') | (upper(klawisz) == 27), + set(hf, 'KeyPressFcn', ''); + return; + end; + if upper(klawisz) == 13, + set(gcf, 'Color', [1, 1, 0.8]); + klawisz = '~'; +% while length(klawisz) == 0, + while klawisz == '~', + drawnow + pause(0); + end; + set(gcf, 'Color', 'w'); + if klawisz ~= 13, + klawisz = '~'; + end + end; + if upper(klawisz) == '+', + time = time / 2; + klawisz = '~'; + end; + if upper(klawisz) == '-', + time = time * 2; + klawisz = '~'; + end; + +end; + +set(hf, 'KeyPressFcn', ''); diff --git a/Ex6/rundot.bat b/Ex6/rundot.bat new file mode 100644 index 0000000..07d1702 --- /dev/null +++ b/Ex6/rundot.bat @@ -0,0 +1,10 @@ +path = "d:\Program Files (x86)\Graphviz2.38\bin\";%path% + +del ex6_task1.gif +del ex6_task1b.gif +del ex6_task2.gif +del ex6_task2_new.gif +dot -Tgif ex6_task1.dot -oex6_task1.gif +dot -Tgif ex6_task1b.dot -oex6_task1b.gif +dot -Tgif ex6_task2.dot -oex6_task2.gif +dot -Tgif ex6_task2_new.dot -oex6_task2_new.gif diff --git a/Ex6/rundot_test.bat b/Ex6/rundot_test.bat new file mode 100644 index 0000000..8b89638 --- /dev/null +++ b/Ex6/rundot_test.bat @@ -0,0 +1,11 @@ +path = "d:\Program Files (x86)\Graphviz2.38\bin\";%path% + +del ex6_task2_test.gif +dot -Tgif ex6_task2_test.dot -oex6_task2_test.gif +del ex6_task2.gif +dot -Tgif ex6_task2.dot -oex6_task2.gif +del Seria2Parallel-test.gif +dot -Tgif Seria2Parallel-test.dot -oSeria2Parallel-test.gif +del SymbolMapper-test.gif +dot -Tgif SymbolMapper-test.dot -oSymbolMapper-test.gif + -- GitLab