Déplacement des .cxx qui n'étaient pas à leur place non plus
Cette révision appartient à :
61
src/application.cxx
Fichier normal
61
src/application.cxx
Fichier normal
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Application.cxx Projet Modulations Numériques
|
||||
* Création : 2026/01/17 17h
|
||||
* Licence GPL v2
|
||||
* Copyright Eric Bachard 2026/01/13
|
||||
*/
|
||||
|
||||
#include "application.h"
|
||||
#include "imgui_themes.h"
|
||||
|
||||
Application::Application()
|
||||
: current_theme(DEFAULT_THEME), current_tab(Amplitude_TAB),
|
||||
windowWidth(DEFAULT_SDL_WINDOW_WIDTH),
|
||||
windowHeight(DEFAULT_SDL_WINDOW_HEIGHT)
|
||||
{
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
}
|
||||
|
||||
void Application::set_current_tab(TAB_name aTab)
|
||||
{
|
||||
if (current_tab == aTab)
|
||||
return;
|
||||
|
||||
current_tab = aTab;
|
||||
}
|
||||
|
||||
|
||||
void Application::setTheme(THEME aTheme)
|
||||
{
|
||||
if (current_theme == aTheme)
|
||||
return;
|
||||
|
||||
switch(aTheme)
|
||||
{
|
||||
case LIGHT_GREEN_THEME:
|
||||
ImGui::StyleColorsLightGreen();
|
||||
break;
|
||||
|
||||
case DARK_THEME:
|
||||
ImGui::StyleColorsDark();
|
||||
break;
|
||||
|
||||
case CLASSIC_THEME:
|
||||
ImGui::StyleColorsClassic();
|
||||
break;
|
||||
|
||||
case LIGHT_BLUE_THEME:
|
||||
ImGui::StyleColorsLight();
|
||||
break;
|
||||
|
||||
case WINDOWS_THEME: ImGui::StyleColorsWindows();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
current_theme = aTheme;
|
||||
}
|
||||
137
src/engine.cxx
Fichier normal
137
src/engine.cxx
Fichier normal
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* engine.cxx, now included in digital modulations, was initialy a file from miniDart project
|
||||
* Author : Eric Bachard / lundi 3 octobre 2016, 14:35:03 (UTC+0200)
|
||||
* This file is under GPL v2 License
|
||||
* See : http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*/
|
||||
|
||||
|
||||
#include "SDL3/SDL.h" // We use SDL3
|
||||
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "engine.h" // class Engine
|
||||
#include "application.h" // WINDOW_WIDTH, WINDOW_HEIGHT,
|
||||
|
||||
#include <GL/gl.h> // OpenGL
|
||||
|
||||
#include "digital_modulation_fr.hpp"
|
||||
|
||||
// TODO later : kept for good reasons (the fact is we currently do not cross-compile for MS Windows)
|
||||
#ifndef NATIVE_BUILD
|
||||
#define NATIVE_BUILD
|
||||
#endif
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
void Engine::sdl_application_abort(const char * msg)
|
||||
{
|
||||
std::cout << SDL_GetError() << std::endl;
|
||||
SDL_Quit();
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
Engine::Engine()
|
||||
{
|
||||
int anErr = init_SDL();
|
||||
|
||||
if (anErr != 0)
|
||||
{
|
||||
std::cerr << "Cannot initialize SDL or OpenGL. Exiting" << std::endl;
|
||||
SDL_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
SDL_GL_DestroyContext(getGL_Context());
|
||||
SDL_DestroyWindow(getWindow());
|
||||
window = nullptr;
|
||||
}
|
||||
|
||||
int Engine::init_SDL()
|
||||
{
|
||||
std::cout << "\n";
|
||||
SDL_Log("AVX %s\n", SDL_HasAVX()? "detected" : "not detected");
|
||||
SDL_Log("AVX2 %s\n", SDL_HasAVX2()? "detected" : "not detected");
|
||||
SDL_Log("AVX512F %s\n", SDL_HasAVX512F()? "detected" : "not detected");
|
||||
std::cout << "\n";
|
||||
|
||||
// ericb 2022 08 19
|
||||
// source :
|
||||
// https://answers.opencv.org/question/120699/can-opencv-310-be-set-to-capture-an-rtsp-stream-over-udp/
|
||||
// setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "rtsp_transport;udp", 1);
|
||||
|
||||
#ifdef NATIVE_BUILD
|
||||
// https://github.com/libsdl-org/SDL/issues/2917
|
||||
putenv((char *)"SDL_PULSEAUDIO_INCLUDE_MONITORS=true");
|
||||
#endif
|
||||
// https://www.gog.com/forum/thimbleweed_park/linux_unable_to_init_sdl_mixer_alsa_couldnt_open_audio_device_no_such_device
|
||||
// https://wiki.libsdl.org/FAQUsingSDL
|
||||
#ifndef NATIVE_BUILD
|
||||
SDL_setenv("SDL_AUDIODRIVER", "DirectSound", true);
|
||||
putenv((char *)"SDL_AUDIODRIVER=DirectSound");
|
||||
#else
|
||||
// SDL_setenv("SDL_AUDIODRIVER", "alsa", true);
|
||||
putenv((char *)"SDL_AUDIODRIVER=alsa");
|
||||
// putenv((char *)"SDL_AUDIODEV=pulse");
|
||||
#endif
|
||||
|
||||
// --------------------- SDL INIT ---------------------
|
||||
// about issues between OpenGL 3.3. and OpenGL 3.0 (only Linux concerned)
|
||||
// https://discourse.libsdl.org/t/confused-about-what-opengl-context-is-being-used-with-sdl/22860
|
||||
// avoid using compatiblity profile
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
fprintf(stdout, "SDL init error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
//const char* glsl_version = "#version 130";
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
setWindow((SDL_Window*)SDL_CreateWindow( MAIN_SDL3_WINDOW_NAME,
|
||||
DEFAULT_SDL_WINDOW_WIDTH,
|
||||
DEFAULT_SDL_WINDOW_HEIGHT,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
|
||||
));
|
||||
|
||||
// check whether the SDL3 window exists
|
||||
if (getWindow() == nullptr)
|
||||
sdl_application_abort("Problem creating the SDL window.\n");
|
||||
else
|
||||
std::cout << "SDL3 Window created " << "\n";
|
||||
|
||||
gl_context = SDL_GL_CreateContext(getWindow());
|
||||
|
||||
std::cout << "GL_Context : " << getGL_Context() << "\n";
|
||||
|
||||
if (getGL_Context() == nullptr)
|
||||
sdl_application_abort("Problem creating GL context.\n");
|
||||
else
|
||||
std::cout << "GL Context created \n" << "\n";
|
||||
|
||||
SDL_GL_MakeCurrent(getWindow(), getGL_Context());
|
||||
|
||||
// 1 == enable VSync ; 0 == disable VSync
|
||||
#define USE_VSYNC
|
||||
#ifdef USE_VSYNC
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
#else
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
#endif
|
||||
|
||||
std::cout << "\nImGui version = " << IMGUI_VERSION << "\n\n";
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
85
src/gl_helpers.cxx
Fichier normal
85
src/gl_helpers.cxx
Fichier normal
@@ -0,0 +1,85 @@
|
||||
/* gl_helpers.cpp */
|
||||
|
||||
// Eric Bachard 2024/11/05 17h01
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <SDL_opengles2.h>
|
||||
#else
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#include "gl_helpers.h"
|
||||
|
||||
// Simple helper function to load an image into a OpenGL texture with common settings
|
||||
bool LoadTextureFromMemory(const void* data, size_t data_size, GLuint* out_texture, int* out_width, int* out_height)
|
||||
{
|
||||
// Load from file
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL, 4);
|
||||
|
||||
if (image_data == NULL)
|
||||
{
|
||||
std::cout << "image_data == NULL" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a OpenGL texture identifier
|
||||
GLuint image_texture;
|
||||
glGenTextures(1, &image_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, image_texture);
|
||||
|
||||
// Setup filtering parameters for display
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Upload pixels into texture
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
*out_texture = image_texture;
|
||||
*out_width = image_width;
|
||||
*out_height = image_height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open and read a file, then forward to LoadTextureFromMemory()
|
||||
bool LoadTextureFromFile(const char* file_name, GLuint* out_texture, int* out_width, int* out_height)
|
||||
{
|
||||
FILE* f = fopen(file_name, "rb");
|
||||
|
||||
if (f == NULL)
|
||||
return false;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t file_size = (size_t)ftell(f);
|
||||
|
||||
if (file_size < 0)
|
||||
return false;
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
void* file_data = IM_ALLOC(file_size);
|
||||
unsigned int taille_image = fread(file_data, 1, file_size, f);
|
||||
|
||||
if (0 >= taille_image)
|
||||
{
|
||||
fprintf(stdout, "Erreur avec le chargement de l'image en mémoire");
|
||||
return false;
|
||||
}
|
||||
bool ret = LoadTextureFromMemory(file_data, file_size, out_texture, out_width, out_height);
|
||||
IM_FREE(file_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
275
src/imgui_utils.cxx
Fichier normal
275
src/imgui_utils.cxx
Fichier normal
@@ -0,0 +1,275 @@
|
||||
/* ImGui utils */
|
||||
|
||||
/* Fichier contenant quelques fonctionnalités basées sur ImGui :
|
||||
* VToggleButton() dérivé de ToggleButton
|
||||
* DrawVToggleButton
|
||||
*
|
||||
* Copyright Eric Bachard 2026/01/14 18h20
|
||||
* License GPL v2
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
|
||||
#include "digital_modulation_fr.hpp"
|
||||
#include "imgui.h"
|
||||
#include "vtoggle_button.h"
|
||||
#include "imgui_utils.h"
|
||||
#include "imgui_themes.h"
|
||||
|
||||
#ifdef M_PI
|
||||
#undef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
void selectThemeMenu(Application * p_app)
|
||||
{
|
||||
static THEME selected_menutheme = p_app->get_current_theme();
|
||||
|
||||
if (ImGui::BeginMenu(THEME_MENU_ENTRY))
|
||||
{
|
||||
if (ImGui::Selectable(TRADITIONAL_GREEN_THEME_MENU_ENTRY))
|
||||
selected_menutheme = LIGHT_GREEN_THEME;
|
||||
|
||||
if (ImGui::Selectable(DARK_THEME_MENU_ENTRY))
|
||||
selected_menutheme = DARK_THEME;
|
||||
|
||||
if (ImGui::Selectable(CLASSIC_THEME_MENU_ENTRY))
|
||||
selected_menutheme = CLASSIC_THEME;
|
||||
|
||||
if (ImGui::Selectable(LIGHT_BLUE_THEME_MENU_ENTRY))
|
||||
selected_menutheme = LIGHT_BLUE_THEME;
|
||||
|
||||
if (ImGui::Selectable(WINDOWS_THEME_MENU_ENTRY))
|
||||
selected_menutheme = WINDOWS_THEME;
|
||||
|
||||
if (selected_menutheme != p_app->get_current_theme())
|
||||
p_app->setTheme(selected_menutheme);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void selectFrameSize(DigitalModulation * p_Dmod)
|
||||
{
|
||||
// by default, we recopy the current value
|
||||
int frame_length = p_Dmod->getWordSize();
|
||||
|
||||
if (ImGui::BeginMenu(FRAME_SIZE))
|
||||
{
|
||||
if (ImGui::Selectable(HEIGHT_BITS_FRAME_SIZE))
|
||||
frame_length = 8;
|
||||
|
||||
if (ImGui::Selectable(SIXTEEN_BITS_FRAME_SIZE))
|
||||
frame_length = 16;
|
||||
|
||||
if (ImGui::Selectable(TWENTY_FOUR_BITS_FRAME_SIZE))
|
||||
frame_length = 24;
|
||||
|
||||
if (frame_length != p_Dmod->getWordSize())
|
||||
p_Dmod->setWordSize(frame_length);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ToogleButton transformé en VToggleButton
|
||||
bool VToggleButton(const char* str_id, bool* v)
|
||||
{
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Taille du toggle
|
||||
// FIXME : elaborate
|
||||
float width = ImGui::GetFrameHeight();
|
||||
//float width = VTOGGLEBUTTON_WIDTH;
|
||||
float height = width * 1.8f;
|
||||
float radius = width * 0.5f;
|
||||
|
||||
ImGui::InvisibleButton(str_id, ImVec2(width, height));
|
||||
|
||||
// Vérifie si le bouton a été cliqué
|
||||
bool clicked = ImGui::IsItemClicked();
|
||||
if (clicked)
|
||||
*v = !*v;
|
||||
|
||||
// Choix de la couleur de fond selon l'état
|
||||
ImU32 col_bg = ImGui::IsItemHovered()
|
||||
? (*v ? IM_COL32(165,231,88,255) : IM_COL32(200,200,200,255)) //ON
|
||||
: (*v ? IM_COL32(145,211,68,255) : IM_COL32(218,218,218,255)); //OFF
|
||||
|
||||
draw_list->AddRectFilled( p, ImVec2(p.x + width, p.y + height), col_bg, radius ); // Dessine le rectangle du Toggle
|
||||
float cy = *v ? (p.y + radius) : (p.y + height - radius); // Position verticale du petit rond
|
||||
draw_list->AddCircleFilled( ImVec2(p.x + radius, cy), radius - 2.0f, IM_COL32(255,255,255,255) ); // Dessine le rond blanc du Toggle
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void drawVToggleButtons(int * dm_bits, int wordSize)
|
||||
{
|
||||
// CHILD2_WIDTH = 300.0f
|
||||
float drawSize = ImGui::GetContentRegionAvail().x - CHILD2_WIDTH;
|
||||
|
||||
// FIXME : calculer sérieusement ces valeurs
|
||||
//TEST
|
||||
|
||||
// La formule :
|
||||
// draw_size = (P+B)wordSize + P + marges // Il y a 1 padding de plus que de boutons dessinés
|
||||
//
|
||||
// 20.0f because fo the left/right margins
|
||||
// On dessine dans la child window 1,
|
||||
// et le nombre de pixels n'est plus le même en plein écran
|
||||
// exemple : drawSize = 664.0f contre 1304.0f en plein écran.
|
||||
// 2 solutions : soit dessiner a valeur définie et testée (pas très pro)
|
||||
// soit calculer précisément le padding. Or la formule ne semble pas fonctionner ...
|
||||
// La valeur de 20.0f est soustraite car il faut tenir compte d'une marge à gauche
|
||||
// et d'une marge à droite pour le canvas.
|
||||
// current best value : 20.0f
|
||||
float padding = ((drawSize - 20.0f) - VTOGGLEBUTTON_WIDTH * (float)wordSize)/((float)wordSize + 1);
|
||||
|
||||
// hack ... le temps de corriger la formule
|
||||
if (wordSize == 8)
|
||||
padding += 32.0f;
|
||||
|
||||
if (wordSize == 16)
|
||||
padding += 7.5f;
|
||||
|
||||
if (drawSize > 1000.0F)
|
||||
{
|
||||
if (wordSize == 8)
|
||||
padding += 7.3f; // best value 7.3f
|
||||
|
||||
if (wordSize == 16)
|
||||
padding += 2.3f; // best value 2.3f
|
||||
else
|
||||
padding += 1.2f; // best value 1.2f
|
||||
}
|
||||
// end hack
|
||||
|
||||
ImGui::Dummy(ImVec2(padding / 2.0f, 0.0f)); ImGui::SameLine(); // Padding pour le premier bouton
|
||||
|
||||
for (int i = 0; i < wordSize ; i++)
|
||||
{
|
||||
ImGui::PushID(i);
|
||||
bool bit = (dm_bits[i] != 0);
|
||||
|
||||
if (VToggleButton("##Button", &bit))
|
||||
dm_bits[i] = bit ? 1 : 0;
|
||||
ImGui::PopID();
|
||||
|
||||
if (i < (wordSize - 1)) // Pas de padding pour le dernier
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(padding, 0.0f)); ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
// debug purpose
|
||||
//#define DEBUG_ME
|
||||
#ifdef DEBUG_ME
|
||||
ImGui::Text(" wordSize : %d", wordSize);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" padding : %f", padding);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" drawSize : %f", drawSize);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("VTOGGLEBUTTON_WIDTH : %f", VTOGGLEBUTTON_WIDTH);
|
||||
#endif /* DEBUG_ME */
|
||||
}
|
||||
|
||||
void highlightFrame(int * dm_bits, int wordSize)
|
||||
{
|
||||
ImVec2 text_pos = ImGui::GetCursorScreenPos();
|
||||
// pour avoir la surbrillance de longueur variable
|
||||
std::string text_trame = "Trame :";
|
||||
|
||||
for (int i = 0; i < wordSize; i++)
|
||||
{
|
||||
if (0 == i % 8 && i != 0) // Découpage par octets
|
||||
text_trame += " ";
|
||||
|
||||
text_trame += " ";
|
||||
text_trame += std::to_string(dm_bits[i]);
|
||||
}
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(text_trame.c_str());
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(
|
||||
text_pos,
|
||||
ImVec2(text_pos.x + text_size.x, text_pos.y + text_size.y),
|
||||
IM_COL32(255, 255, 0, 128) // Jaune semi-transparent
|
||||
);
|
||||
ImGui::Text("%s", text_trame.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// Helper to draw a small modulation example (Témoin visuel). Code proposé par
|
||||
void DrawIndicator(const char * label,
|
||||
float * carrier_freq,
|
||||
DigitalModulationType * p_modulation_type,
|
||||
int * samples_per_bit,
|
||||
float amp_scale,
|
||||
float phase_offset,
|
||||
ImU32 color,
|
||||
ImVec2 size)
|
||||
{
|
||||
ImGui::Text("%s", label);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Fond gris clair pour le témoin
|
||||
draw_list->AddRectFilled(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(245, 245, 245, 255));
|
||||
|
||||
// Bordure
|
||||
draw_list->AddRect(p, ImVec2(p.x + size.x, p.y + size.y), IM_COL32(180, 180, 180, 255));
|
||||
|
||||
float cy = p.y + size.y * 0.5f; // Centre vertical
|
||||
float base_amp = (size.y * 0.35f); // Amplitude de base (laisse une marge)
|
||||
|
||||
ImVec2 prev;
|
||||
bool first = true;
|
||||
|
||||
// On dessine 2 périodes (ou le double si un symbole est codé sur 2 bits),
|
||||
// pour bien visualiser la forme
|
||||
static float k2 = 1.0f;
|
||||
|
||||
switch (*p_modulation_type)
|
||||
{
|
||||
case MOD_MASK_TYPE :
|
||||
case MOD_M_QAM_TYPE:
|
||||
case MOD_4_QAM_TYPE:
|
||||
case MOD_MFSK_TYPE :
|
||||
k2 = 2.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
k2 = 1.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
// tracé de la courbe
|
||||
for (int i = 0; i <= *samples_per_bit; i++)
|
||||
{
|
||||
float t = (float)i / (float)*samples_per_bit; // 0 à 1
|
||||
// Formule du signal : sin(2*PI * f * t + phase)
|
||||
float y = cy - sinf(k2 * 2.0f * M_PI * (* carrier_freq) * t + phase_offset) * (base_amp * amp_scale);
|
||||
float x = p.x + t * size.x;
|
||||
ImVec2 curr(x, y);
|
||||
|
||||
if (!first)
|
||||
draw_list->AddLine(prev, curr, color, 1.5f); // Ligne un peu plus épaisse
|
||||
prev = curr;
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Réserve l'espace pour qu'ImGui ne dessine pas par dessus
|
||||
ImGui::Dummy(size);
|
||||
// Petit espacement vertical après
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
}
|
||||
|
||||
231
src/main.cxx
Fichier normal
231
src/main.cxx
Fichier normal
@@ -0,0 +1,231 @@
|
||||
// Dear ImGui + SDL3 + OpenGL3
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
|
||||
#include "imgui_themes.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
|
||||
#include "digital_modulation_fr.hpp"
|
||||
#include "engine.h"
|
||||
#include "imgui_utils.h"
|
||||
#include "modulations.h"
|
||||
#include "application.h"
|
||||
|
||||
|
||||
static void showApplicationQuitMenu()
|
||||
{
|
||||
if (ImGui::BeginMenu(APPLICATION_MENU_HEAD))
|
||||
{
|
||||
if (ImGui::MenuItem(APPLICATION_QUIT, APPLICATION_QUIT_SHORTCUT) )
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent( &event );
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// démarrage de l'application
|
||||
Application app;
|
||||
Application * p_app = &app;
|
||||
|
||||
// --------------------- SDL INIT ---------------------
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
fprintf(stdout, "SDL init error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
Engine engine;
|
||||
|
||||
// -----------------IMGUI INIT---------------------------
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::StyleColorsLight();
|
||||
|
||||
ImGui_ImplSDL3_InitForOpenGL(engine.getWindow(), engine.getGL_Context());
|
||||
ImGui_ImplOpenGL3_Init(engine.get_glsl_version());
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.FontSizeBase = 22.0f;
|
||||
io.Fonts->AddFontFromFileTTF("../fonts/DroidSans.ttf");
|
||||
|
||||
// Création d'une instance de la classe DigitalModulation
|
||||
// (son initialisation est réalisée par le constructeur de la classe)
|
||||
DigitalModulation dmod;
|
||||
// Pointeur, utilisé par les différents menus
|
||||
DigitalModulation * p_dmod = &dmod;
|
||||
|
||||
// ---------------------- MAIN LOOP ----------------------
|
||||
bool done = false;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_QUIT:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
{
|
||||
SDL_SetWindowSize(engine.getWindow(), event.window.data1, event.window.data2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// Corrige le redimensionnement (se voit quand on change de thème)
|
||||
ImGui::SetNextWindowPos(ImVec2(0,27), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Always);
|
||||
|
||||
ImGui::Begin( "Modulation numerique",
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar
|
||||
| ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||
);
|
||||
|
||||
// some geometry to avoid hidden main application menu
|
||||
// on commence par calculer la place disponible et on soustrait 23.0 pixels en y
|
||||
ImVec2 windowSize(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y - 23.0f);
|
||||
ImGui::SetNextWindowSize(windowSize);
|
||||
|
||||
// On décale ensuite le contenu de 23.0 pixels vers le bas, ce qui représente 1 ligne environ
|
||||
ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos;
|
||||
ImGui::SetNextWindowPos(ImVec2(main_viewport_pos.x,main_viewport_pos.y + 23.0f));
|
||||
|
||||
if (ImGui::BeginMainMenuBar())
|
||||
{
|
||||
showApplicationQuitMenu();
|
||||
selectThemeMenu(p_app);
|
||||
selectFrameSize(p_dmod);
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_NoTooltip;
|
||||
|
||||
int anErr = 0;
|
||||
if (ImGui::BeginTabBar("Modulation Numérique d'Amplitude", tab_bar_flags))
|
||||
{
|
||||
if (ImGui::BeginTabItem("Modulation numérique d'Amplitude"))
|
||||
{
|
||||
app.set_current_tab(Amplitude_TAB);
|
||||
static int am_bits[WORD_SIZE_MAX] = {0};
|
||||
static float am_carrier_freq = 2.0f;
|
||||
|
||||
dmod.setDigitalModulationType(Amplitude_TAB, MOD_OOK_TYPE);
|
||||
static DigitalModulationType aType = dmod.get_AM_DigitalModulationType();
|
||||
|
||||
anErr = dmod.AmplitudeDigitalModulation(&am_carrier_freq, &am_bits[0], &aType);
|
||||
|
||||
if (anErr != EXIT_SUCCESS)
|
||||
{
|
||||
std::cout << "Problème !!" << "\n";
|
||||
return (-1);
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Modulation numérique de fréquence"))
|
||||
{
|
||||
app.set_current_tab(Frequency_TAB);
|
||||
static int fm_bits[WORD_SIZE_MAX] = {0};
|
||||
static float fm_carrier_freq = 2.0f;
|
||||
dmod.setDigitalModulationType(Frequency_TAB, MOD_FSK_TYPE);
|
||||
|
||||
static DigitalModulationType fm_Type = dmod.get_FM_DigitalModulationType();
|
||||
|
||||
anErr = dmod.FrequencyDigitalModulation(&fm_carrier_freq, &fm_bits[0], &fm_Type);
|
||||
|
||||
if (anErr != EXIT_SUCCESS)
|
||||
{
|
||||
std::cout << "Problème !!" << "\n";
|
||||
return (-1);
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Modulation numérique de phase"))
|
||||
{
|
||||
app.set_current_tab(Phase_TAB);
|
||||
static int pm_bits[WORD_SIZE_MAX] = {0};
|
||||
static float pm_carrier_freq = 2.0f;
|
||||
|
||||
dmod.setDigitalModulationType(Phase_TAB, MOD_BPSK_TYPE);
|
||||
static DigitalModulationType pm_Type = p_dmod->get_PM_DigitalModulationType();
|
||||
|
||||
anErr = dmod.PhaseDigitalModulation(&pm_carrier_freq, &pm_bits[0], &pm_Type);
|
||||
|
||||
if (anErr != EXIT_SUCCESS)
|
||||
{
|
||||
std::cout << "Problème !!" << "\n";
|
||||
return (-1);
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Modulation numérique M-QAM"))
|
||||
{
|
||||
app.set_current_tab(M_QAM_TAB);
|
||||
static int pm_bits[WORD_SIZE_MAX] = {0};
|
||||
static float pm_carrier_freq = 2.0f;
|
||||
|
||||
dmod.setDigitalModulationType(M_QAM_TAB, MOD_4_QAM_TYPE);
|
||||
static DigitalModulationType pm_Type = p_dmod->getM_QAM_DigitalModulationType();
|
||||
|
||||
anErr = dmod.M_QAM_DigitalModulation(&pm_carrier_freq, &pm_bits[0], &pm_Type);
|
||||
|
||||
if (anErr != EXIT_SUCCESS)
|
||||
{
|
||||
std::cout << "Problème !!" << "\n";
|
||||
return (-1);
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// -----------------------RENDER ----------------------
|
||||
ImGui::Render();
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
glClearColor(0.95f, 0.95f, 0.95f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_GL_SwapWindow(engine.getWindow());
|
||||
}
|
||||
|
||||
// ------------------ CLEANUP ------------------
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
engine.~Engine();
|
||||
dmod.~DigitalModulation();
|
||||
app.~Application();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
893
src/modulations.cxx
Fichier normal
893
src/modulations.cxx
Fichier normal
@@ -0,0 +1,893 @@
|
||||
/* Fichier d'implémentation de la classe DigitalModulations numériques
|
||||
* - d'amplitude ;
|
||||
* - de fréquence;
|
||||
* - de phase ;
|
||||
* - M-QAM
|
||||
*
|
||||
* Author : Eric Bachard 2026/01/08 18h37
|
||||
* Licence GPL v2
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include "math.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_utils.h"
|
||||
#include "vtoggle_button.h"
|
||||
#include "gl_helpers.h"
|
||||
|
||||
#ifdef M_PI
|
||||
#undef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
// FIXME image
|
||||
static int my_image_width = 0;
|
||||
static int my_image_height = 0;
|
||||
static GLuint my_image_texture = 0;
|
||||
|
||||
DigitalModulation::DigitalModulation()
|
||||
: mdWordSize(DEFAULT_WORD_SIZE)
|
||||
{
|
||||
setDigitalModulationType (Amplitude_TAB, MOD_OOK_TYPE);
|
||||
setDigitalModulationType (Frequency_TAB, MOD_FSK_TYPE);
|
||||
setDigitalModulationType (Phase_TAB, MOD_BPSK_TYPE);
|
||||
// TODO Later
|
||||
//setDigitalModulationType (M_QAM_TAB, MOD_M_QAM_TYPE);
|
||||
};
|
||||
|
||||
DigitalModulation::~DigitalModulation()
|
||||
{
|
||||
};
|
||||
|
||||
short int DigitalModulation::AmplitudeDigitalModulation(float * carrier_freq,
|
||||
int * am_bits,
|
||||
DigitalModulationType * p_aType)
|
||||
{
|
||||
// TODO : améliorer cette partie
|
||||
static DigitalModulationType amplitude_modulation_t = *p_aType;
|
||||
static float c_freq = *carrier_freq;
|
||||
|
||||
// --------------------- LEFT : SIGNAL -----------------
|
||||
ImGui::BeginChild("Signal", ImVec2(ImGui::GetContentRegionAvail().x - CHILD2_WIDTH, 0), false);
|
||||
|
||||
// pour 1 bit, 100 échantillons (400 pour la modulation de fréquence pour fporteuse max)
|
||||
static int samples_per_bit = SAMPLES_PER_BIT_MAX;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::SliderFloat("Frequence porteuse ", &c_freq, 2.0f, 10.0f);
|
||||
#if defined(DEBUG)
|
||||
ImGui::SliderInt("Nombre de points par symbole", &samples_per_bit, DEFAULT_SAMPLES_PER_BIT, 500);
|
||||
#endif
|
||||
ImGui::NewLine();
|
||||
|
||||
// Surbrillance de la trame affichée
|
||||
highlightFrame(am_bits, getWordSize());
|
||||
|
||||
// position du curseur + dimensions du canvas.
|
||||
// canvas_pos contient les coordonnées du curseur (marque l'endroit d'où l'on dessine à ce point du programme)
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Dimensions du cadre dans lequel on dessine la sinusoïde
|
||||
ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, CANVAS_HEIGHT);
|
||||
|
||||
// ce qui va être dessiné
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// On dessine un fond blanc
|
||||
draw_list->AddRectFilled
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(255,255,255,255)
|
||||
);
|
||||
|
||||
// on ajoute par dessus un tour noir
|
||||
draw_list->AddRect
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,0,0,255)
|
||||
);
|
||||
|
||||
// calcul des coordonnées du centre de la frame
|
||||
float center_y = canvas_pos.y + canvas_size.y / 2.0f;
|
||||
|
||||
// amplitude de la sinusoïde de base
|
||||
float base_amp = SINUS_AMPLITUDE;
|
||||
float total_samples = getWordSize() * samples_per_bit;
|
||||
float dx = canvas_size.x / total_samples;
|
||||
float x = canvas_pos.x;
|
||||
|
||||
ImVec2 prev(x, center_y);
|
||||
|
||||
for (int bit_traite = 0; bit_traite < getWordSize(); bit_traite++)
|
||||
{
|
||||
draw_list->AddLine
|
||||
(
|
||||
ImVec2(x, canvas_pos.y),
|
||||
ImVec2(x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,120,255,255)
|
||||
);
|
||||
|
||||
float amplitude = 0.0f;
|
||||
|
||||
switch (amplitude_modulation_t)
|
||||
{
|
||||
case MOD_OOK_TYPE:
|
||||
amplitude = am_bits[bit_traite] ? base_amp : 0.0f;
|
||||
break;
|
||||
|
||||
case MOD_ASK_TYPE:
|
||||
amplitude = am_bits[bit_traite] ? base_amp : base_amp * 0.4f;
|
||||
break;
|
||||
|
||||
case MOD_MASK_TYPE:
|
||||
if ((bit_traite % 2) == 0) // bit pair => bits 0, 2, 4 ou 6
|
||||
amplitude = base_amp * (0.25 + 0.5 * am_bits[bit_traite] + 0.25 * am_bits[bit_traite+1]);
|
||||
else // bit impair => bits 1, 3, 5 ou 7
|
||||
amplitude = base_amp * (0.25 + 0.5 * am_bits[bit_traite-1] + 0.25 * am_bits[bit_traite]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// on dessine 100 échantillons par bit de la trame
|
||||
for (int i = 0; i < samples_per_bit; i++)
|
||||
{
|
||||
float t = (bit_traite * samples_per_bit + i) / (float)samples_per_bit;
|
||||
float y = center_y - sinf(2.0f * M_PI * c_freq * t) * amplitude;
|
||||
|
||||
ImVec2 p(x, y);
|
||||
//if (i != (samples_per_bit -1))
|
||||
// Le dernier raccord peut poser problème dans certains cas
|
||||
// et on ne dessine pas la ligne entre le dernier point du bit précédent
|
||||
// et le premier point du bit suivant. Ceci en M_QAM pour l'instant (pour tester).
|
||||
if (MOD_MASK_TYPE == *p_aType)
|
||||
{
|
||||
if (i != 0)
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
else
|
||||
draw_list->AddLine(p, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
}
|
||||
else
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
|
||||
prev = p;
|
||||
x += dx;
|
||||
}
|
||||
|
||||
// FIXME : d'où viennent ces valeurs 5 et 22 ?
|
||||
draw_list->AddText ( ImVec2(x - samples_per_bit * dx + 5, canvas_pos.y + canvas_size.y - 22),
|
||||
IM_COL32(0,0,255,255),
|
||||
am_bits[bit_traite] ? " 1 " : " 0 "
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::NewLine();
|
||||
ImGui::SeparatorText("Bits");
|
||||
|
||||
drawVToggleButtons(am_bits, getWordSize());
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// ----------------------------- RIGHT : OPTIONS ------------------------
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("Options", ImVec2(CHILD2_WIDTH - 8.0f /* minus the border */, 0), true);
|
||||
|
||||
ImGui::Text("Type de modulation");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::RadioButton("OOK " , (int*)&litude_modulation_t, MOD_OOK_TYPE);
|
||||
ImGui::RadioButton("ASK " , (int*)&litude_modulation_t, MOD_ASK_TYPE);
|
||||
ImGui::RadioButton("4-ASK ", (int*)&litude_modulation_t, MOD_MASK_TYPE);
|
||||
|
||||
// --- AJOUT TÉMOIN ---
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Principe :");
|
||||
ImGui::Spacing();
|
||||
|
||||
ImVec2 ind_size(ImGui::GetContentRegionAvail().x - 10, 50);
|
||||
ImU32 col = IM_COL32(0, 120, 255, 255); // Même bleu que le signal
|
||||
|
||||
switch(amplitude_modulation_t)
|
||||
{
|
||||
case MOD_OOK_TYPE:
|
||||
case MOD_ASK_TYPE:
|
||||
ImGui::Text("1 bit par symbole");
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le bit vaut 1 :", &c_freq, &litude_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator ("Le bit vaut 0 :",
|
||||
&c_freq, &litude_modulation_t,
|
||||
&samples_per_bit, ((MOD_ASK_TYPE == amplitude_modulation_t) ? 0.4f : 0.0f),
|
||||
0.0f,
|
||||
col,
|
||||
ind_size
|
||||
);
|
||||
break;
|
||||
|
||||
case MOD_MASK_TYPE:
|
||||
ImGui::Text("2 bits par symbole");
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 11 : ", &c_freq, &litude_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 10 : ", &c_freq, &litude_modulation_t, &samples_per_bit, 0.75f, 0.0f, col, ind_size);
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 01 : ", &c_freq, &litude_modulation_t, &samples_per_bit, 0.4f, 0.0f, col, ind_size);
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 00 : ", &c_freq, &litude_modulation_t, &samples_per_bit, 0.15f, 0.0f, col, ind_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// ------- FIN AJOUT TEMOIN -------------
|
||||
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
short int DigitalModulation::FrequencyDigitalModulation(float * carrier_freq, int * fm_bits, DigitalModulationType * fm_type)
|
||||
{
|
||||
static DigitalModulationType frequency_modulation_t = *fm_type;
|
||||
static float c_freq = *carrier_freq;
|
||||
|
||||
// --------------------- LEFT : SIGNAL -----------------
|
||||
// FIXME : LAYOUT
|
||||
ImGui::BeginChild("Signal2", ImVec2(ImGui::GetContentRegionAvail().x - CHILD2_WIDTH , 0), false);
|
||||
|
||||
// pour 1 bit, 100 échantillons (400 donne de meilleurs résultats en MFSK)
|
||||
static int samples_per_bit = SAMPLES_PER_BIT_MAX; // limite le sous-échantillonnage
|
||||
static float k = 2.0f; // default
|
||||
ImGui::NewLine();
|
||||
|
||||
// affichage bruité au-delà de 4.0f
|
||||
ImGui::SliderFloat("Frequence porteuse ", carrier_freq, 2.0f, 4.0f);
|
||||
#ifdef DEBUG
|
||||
static int k2 = 2;
|
||||
ImGui::SliderInt("Nombre de points par symbole", &samples_per_bit, DEFAULT_SAMPLES_PER_BIT, 500);
|
||||
ImGui::SliderInt("Rapport fréquence modulée / fréquence de base ", &k2, 4, 10);
|
||||
k = (float)k2;
|
||||
#else
|
||||
k = 2.0f;
|
||||
#endif
|
||||
ImGui::NewLine();
|
||||
|
||||
// Surbrillance
|
||||
highlightFrame(fm_bits, getWordSize());
|
||||
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, CANVAS_HEIGHT);
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddRectFilled
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(255,255,255,255)
|
||||
);
|
||||
|
||||
draw_list->AddRect
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,0,0,255)
|
||||
);
|
||||
|
||||
float center_y = canvas_pos.y + canvas_size.y / 2.0f;
|
||||
float base_amp = 100.0f;
|
||||
float total_samples = getWordSize() * samples_per_bit;
|
||||
float dx = canvas_size.x / total_samples;
|
||||
float x = canvas_pos.x;
|
||||
|
||||
ImVec2 prev(x, center_y);
|
||||
|
||||
for (int bit_traite = 0; bit_traite < getWordSize(); bit_traite++)
|
||||
{
|
||||
draw_list->AddLine
|
||||
(
|
||||
ImVec2(x, canvas_pos.y),
|
||||
ImVec2(x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,120,255,255)
|
||||
);
|
||||
|
||||
switch (frequency_modulation_t)
|
||||
{
|
||||
case MOD_FSK_TYPE:
|
||||
c_freq = fm_bits[bit_traite] ? (*carrier_freq) * k : *carrier_freq ;
|
||||
break;
|
||||
|
||||
case MOD_MFSK_TYPE:
|
||||
// On doit dessiner la même amplitude pour les bits (0,1), (2,3), (4,5) et (6,7)
|
||||
// => la parité de bit_traite nous donne la réponse
|
||||
if ((bit_traite % 2) == 0) // bit pair => bits 0, 2, 4 ou 6
|
||||
c_freq = (*carrier_freq) * ( 1 + k * (2 * fm_bits[bit_traite] + fm_bits[bit_traite+1]));
|
||||
else // bit impair => bits 1, 3, 5 ou 7
|
||||
c_freq = (*carrier_freq) * ( 1 + k * (2 * fm_bits[bit_traite-1] + fm_bits[bit_traite]));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samples_per_bit; i++)
|
||||
{
|
||||
float t = (bit_traite * samples_per_bit + i) / (float)samples_per_bit;
|
||||
float y = center_y - sinf(2.0f * M_PI * c_freq * t) * base_amp;
|
||||
|
||||
ImVec2 p(x, y);
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
prev = p;
|
||||
x += dx;
|
||||
}
|
||||
|
||||
draw_list->AddText ( ImVec2(x - samples_per_bit * dx + 5, canvas_pos.y + canvas_size.y - 22),
|
||||
IM_COL32(0,0,255,255),
|
||||
fm_bits[bit_traite] ? " 1 " : " 0 "
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::NewLine();
|
||||
ImGui::SeparatorText("Bits");
|
||||
|
||||
drawVToggleButtons(fm_bits, getWordSize());
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// ----------------------------- RIGHT : OPTIONS ------------------------
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("Options", ImVec2(CHILD2_WIDTH - 8.0f, 0), true);
|
||||
|
||||
ImGui::Text("Type de modulation");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::RadioButton("FSK " , (int*)&frequency_modulation_t, MOD_FSK_TYPE);
|
||||
ImGui::RadioButton("4-FSK " , (int*)&frequency_modulation_t, MOD_MFSK_TYPE);
|
||||
|
||||
// --- AJOUT TÉMOIN ---
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Principe :");
|
||||
ImGui::Spacing();
|
||||
|
||||
ImVec2 ind_size(ImGui::GetContentRegionAvail().x - 10, 50);
|
||||
ImU32 col = IM_COL32(0, 120, 255, 255); // Même bleu que le signal
|
||||
|
||||
switch(frequency_modulation_t)
|
||||
{
|
||||
case MOD_FSK_TYPE:
|
||||
ImGui::Text("1 bit par symbole");
|
||||
ImGui::NewLine();
|
||||
|
||||
c_freq = (*carrier_freq) * k;
|
||||
|
||||
DrawIndicator("Le bit vaut 1 :", &c_freq, &frequency_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
|
||||
c_freq = *carrier_freq;
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator ("Le bit vaut 0 :",
|
||||
&c_freq, &frequency_modulation_t,
|
||||
&samples_per_bit,
|
||||
1.0f, // amplitude
|
||||
0.0f, // phase
|
||||
col,
|
||||
ind_size
|
||||
);
|
||||
break;
|
||||
|
||||
case MOD_MFSK_TYPE:
|
||||
ImGui::Text("2 bits par symbole");
|
||||
ImGui::NewLine();
|
||||
|
||||
c_freq = (*carrier_freq) * 4.0f * k;
|
||||
DrawIndicator("Le symbole vaut 11 : ", &c_freq, &frequency_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
c_freq = (*carrier_freq) * 2.0f * k;
|
||||
DrawIndicator("Le symbole vaut 10 : ", &c_freq, &frequency_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
c_freq = (*carrier_freq) * k;
|
||||
DrawIndicator("Le symbole vaut 01 : ", &c_freq, &frequency_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
c_freq = (*carrier_freq) ;
|
||||
DrawIndicator("Le symbole vaut 00 : ", &c_freq, &frequency_modulation_t, &samples_per_bit, 1.0f, 0.0f, col, ind_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// ------- FIN AJOUT TEMOIN -------------
|
||||
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
short int DigitalModulation::PhaseDigitalModulation(float * carrier_freq, int * pm_bits, DigitalModulationType * pm_type)
|
||||
{
|
||||
static DigitalModulationType phase_modulation_t = *pm_type;
|
||||
|
||||
// --------------------- LEFT : SIGNAL -----------------
|
||||
ImGui::BeginChild("Signal2", ImVec2(ImGui::GetContentRegionAvail().x - CHILD2_WIDTH , 0), false);
|
||||
|
||||
static int samples_per_bit = DEFAULT_SAMPLES_PER_BIT;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::SliderFloat("Frequence porteuse du signal", carrier_freq, 2.0f, 10.0f);
|
||||
#if defined(DEBUG)
|
||||
ImGui::SliderInt("Nombre de points par symbole", &samples_per_bit, DEFAULT_SAMPLES_PER_BIT, 500);
|
||||
#endif
|
||||
ImGui::NewLine();
|
||||
|
||||
// Surbrillance
|
||||
highlightFrame(pm_bits, getWordSize());
|
||||
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, CANVAS_HEIGHT);
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddRectFilled
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(255,255,255,255)
|
||||
);
|
||||
|
||||
draw_list->AddRect
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,0,0,255)
|
||||
);
|
||||
|
||||
float center_y = canvas_pos.y + canvas_size.y / 2.0f;
|
||||
float base_amp = 100.0f;
|
||||
float total_samples = getWordSize() * samples_per_bit;
|
||||
float dx = canvas_size.x / total_samples;
|
||||
float x = canvas_pos.x;
|
||||
|
||||
ImVec2 prev(x, center_y);
|
||||
|
||||
float phi = 0.0f;
|
||||
float delta_phi = 0.0f;
|
||||
|
||||
for (int bit_traite = 0; bit_traite < getWordSize(); bit_traite++)
|
||||
{
|
||||
draw_list->AddLine
|
||||
(
|
||||
ImVec2(x, canvas_pos.y),
|
||||
ImVec2(x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,120,255,255)
|
||||
);
|
||||
|
||||
switch (phase_modulation_t)
|
||||
{
|
||||
case MOD_BPSK_TYPE:
|
||||
phi = pm_bits[bit_traite] ? 0.0f : M_PI;
|
||||
break;
|
||||
|
||||
case MOD_DPSK_TYPE:
|
||||
if (bit_traite < 1)
|
||||
phi = pm_bits[bit_traite] ? 0.0f : M_PI;
|
||||
else
|
||||
{
|
||||
if ((!pm_bits[bit_traite-1] && pm_bits[bit_traite]) ||
|
||||
((pm_bits[bit_traite-1]) && (pm_bits[bit_traite])))
|
||||
delta_phi = M_PI;
|
||||
else
|
||||
delta_phi = 0.0f;
|
||||
}
|
||||
phi += delta_phi;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samples_per_bit; i++)
|
||||
{
|
||||
float t = (bit_traite * samples_per_bit + i) / (float)samples_per_bit;
|
||||
float y = center_y - sinf(2.0f * M_PI * (*carrier_freq) * t + phi) * base_amp;
|
||||
|
||||
ImVec2 p(x, y);
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
prev = p;
|
||||
x += dx;
|
||||
}
|
||||
|
||||
draw_list->AddText ( ImVec2(x - samples_per_bit * dx + 5, canvas_pos.y + canvas_size.y - 22),
|
||||
IM_COL32(0,0,255,255),
|
||||
pm_bits[bit_traite] ? " 1 " : " 0 "
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::NewLine();
|
||||
ImGui::SeparatorText("Bits");
|
||||
drawVToggleButtons(pm_bits, getWordSize());
|
||||
ImGui::EndChild();
|
||||
|
||||
// ----------------------------- RIGHT : OPTIONS ------------------------
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("Options", ImVec2(CHILD2_WIDTH - 8.0f, 0), true);
|
||||
|
||||
ImGui::Text("Type de modulation");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::RadioButton("BPSK " , (int*)&phase_modulation_t, MOD_BPSK_TYPE);
|
||||
ImGui::RadioButton("DPSK " , (int*)&phase_modulation_t, MOD_DPSK_TYPE);
|
||||
|
||||
// --- AJOUT TÉMOIN ---
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
if (MOD_DPSK_TYPE != phase_modulation_t)
|
||||
ImGui::Text("Principe :");
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImVec2 ind_size(ImGui::GetContentRegionAvail().x - 10, 50);
|
||||
ImU32 col = IM_COL32(0, 120, 255, 255); // Même bleu que le signal
|
||||
|
||||
switch(phase_modulation_t)
|
||||
{
|
||||
case MOD_BPSK_TYPE:
|
||||
ImGui::Text("1 bit par symbole");
|
||||
ImGui::NewLine();
|
||||
|
||||
DrawIndicator("Le bit vaut 1 :",
|
||||
carrier_freq,
|
||||
&phase_modulation_t,
|
||||
&samples_per_bit,
|
||||
1.0f, // amplitude
|
||||
0.0f, // phase
|
||||
col,
|
||||
ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator ("Le bit vaut 0 :",
|
||||
carrier_freq, &phase_modulation_t,
|
||||
&samples_per_bit,
|
||||
1.0f, // amplitude
|
||||
M_PI, // phase
|
||||
col,
|
||||
ind_size
|
||||
);
|
||||
break;
|
||||
case MOD_DPSK_TYPE:
|
||||
ImGui::Text("1 bits par symbole");
|
||||
ImGui::NewLine();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// ------- FIN AJOUT TEMOIN -------------
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
short int DigitalModulation::M_QAM_DigitalModulation(float * carrier_freq, int * qam_bits, DigitalModulationType * qam_type)
|
||||
{
|
||||
// TODO : améliorer cette partie
|
||||
static DigitalModulationType qam_modulation_t = *qam_type;
|
||||
static float c_freq = *carrier_freq;
|
||||
|
||||
// --------------------- LEFT : SIGNAL -----------------
|
||||
ImGui::BeginChild("Signal", ImVec2(ImGui::GetContentRegionAvail().x - CHILD2_WIDTH, 0), false);
|
||||
|
||||
// pour 1 bit, 100 échantillons (400 pour la modulation de fréquence pour fporteuse max)
|
||||
static int samples_per_bit = SAMPLES_PER_BIT_MAX;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::SliderFloat("Frequence porteuse ", &c_freq, 2.0f, 10.0f);
|
||||
#if defined(DEBUG)
|
||||
ImGui::SliderInt("Nombre de points par symbole", &samples_per_bit, DEFAULT_SAMPLES_PER_BIT, 500);
|
||||
#endif
|
||||
ImGui::NewLine();
|
||||
|
||||
// Surbrillance de la trame affichée
|
||||
highlightFrame(qam_bits, getWordSize());
|
||||
|
||||
// position du curseur + dimensions du canvas.
|
||||
// canvas_pos contient les coordonnées du curseur (marque l'endroit d'où l'on dessine à ce point du programme)
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Dimensions du cadre dans lequel on dessine la sinusoïde
|
||||
ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, CANVAS_HEIGHT);
|
||||
|
||||
// ce qui va être dessiné
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// On dessine un fond blanc
|
||||
draw_list->AddRectFilled
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(255,255,255,255)
|
||||
);
|
||||
|
||||
// on ajoute par dessus un tour noir
|
||||
draw_list->AddRect
|
||||
(
|
||||
canvas_pos,
|
||||
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,0,0,255)
|
||||
);
|
||||
|
||||
// calcul des coordonnées du centre de la frame
|
||||
float center_y = canvas_pos.y + canvas_size.y / 2.0f;
|
||||
|
||||
// amplitude de la sinusoïde de base
|
||||
float base_amp = 100.0f;
|
||||
float total_samples = getWordSize() * samples_per_bit;
|
||||
float dx = canvas_size.x / total_samples;
|
||||
float x = canvas_pos.x;
|
||||
|
||||
ImVec2 prev(x, center_y);
|
||||
|
||||
for (int bit_traite = 0; bit_traite < getWordSize(); bit_traite++)
|
||||
{
|
||||
draw_list->AddLine
|
||||
(
|
||||
ImVec2(x, canvas_pos.y),
|
||||
ImVec2(x, canvas_pos.y + canvas_size.y),
|
||||
IM_COL32(0,120,255,255)
|
||||
);
|
||||
|
||||
float amplitude = 0.0f;
|
||||
|
||||
|
||||
float coeff_sin = 1.0f; // terme devant le sinus (première fréquence porteuse)
|
||||
float coeff_cos = 1.0f; // terme devant le cosinus (pour la seconde porteuse en quadrature)
|
||||
// Pour la modulation M-QAM, on se place dans le plan complexe.
|
||||
// Cas de la constellation 4-QAM (cas particulier, où elle est identique à la constellation 4-PSK)
|
||||
/*
|
||||
^ sin
|
||||
|
|
||||
01 | 11
|
||||
+ | +
|
||||
|
|
||||
---------------------> cos
|
||||
|
|
||||
+ | +
|
||||
00 | 10
|
||||
|
|
||||
|
||||
*/
|
||||
// /!\ LIRE d'abord sin, puis cos ci-dessous !!
|
||||
// 00 -> (-1;-1) / 01 -> (1;-1) / 10 -> (-1;1) / 11 -> (1;1)
|
||||
// ce qui donne les tableaux de coefficients suivants :
|
||||
|
||||
// tableau des valeurs de sinus
|
||||
const float tab_sinus[4] = { /* 11 */ 1.0f, /* 01 */ 1.0f, /* 10 */-1.0f, /* 00 */-1.0f};
|
||||
|
||||
// tableau des valeurs de cosinus
|
||||
const float tab_cosinus[4] = { /* 11 */ 1.0f ,/* 01 */-1.0f, /* 10 */ 1.0f, /* 00 */-1.0f};
|
||||
|
||||
// 16-QAM
|
||||
const float tab_sin16[16] = { -3.0f, -1.0f, 3.0f, 1.0f,-3.0f,-1.0f, 3.0f, 1.0f,-3.0f,-1.0f, 3.0f, 1.0f,-3.0f,-1.0f, 3.0f, 1.0f };
|
||||
const float tab_cos16[16] = { -3.0f, -3.0f,-3.0f,-3.0f,-1.0f,-1.0f,-1.0f,-1.0f, 3.0f, 3.0f, 3.0f, 3.0f, 1.0f, 1.0f, 1.0, 1.0f };
|
||||
|
||||
switch (qam_modulation_t)
|
||||
{
|
||||
case MOD_4_QAM_TYPE:
|
||||
// Contraintes :
|
||||
// En MODULATION M-QAM : tous les points sont équi-distants (optimisation du rapport S/(S+B))
|
||||
// - on doit dessiner le même signal pour les bits (0,1), (2,3), (4,5) et (6,7)
|
||||
// - si le bit testé est pair, alors il s'agit du bit 0, 2, 4 ou 6
|
||||
// et il faut tester la valeur du mot binaire 2 *bit traite + bit-traite+1
|
||||
// - si le bit testé est impair, il s'agit du bit 1, 3, 5 ou 7
|
||||
// et il faut tester la valeur du mot 2 *bit_traite -1 + bit-traite
|
||||
// - ce cas simple nous impose de diviser aussi l'amplitude de base par racine de 2
|
||||
// (sinon la sinusoïde ne tient pas dans le canvas)
|
||||
//
|
||||
// ce premier calcul nous donne les coefficients devant le sinus ou le cosinus
|
||||
// De plus n doit dessiner le même signal pour les bits (0,1), (2,3), (4,5) et (6,7)
|
||||
// => la parité de bit_traite nous donne les coefficients
|
||||
|
||||
// Ensuite, Le calcul des coefficients est lié à la valeur des deux bits considérés
|
||||
// L'expression du mot binaire sur 2 bits, nous donne les coefficients
|
||||
// 4 cas sont possibles : 00, 01, 10 et 11, cf la constellation + haut.
|
||||
|
||||
amplitude = 0.707f * base_amp;
|
||||
|
||||
if ((bit_traite % 2) == 0) // bit pair => bits 0, 2, 4 ou 6
|
||||
{
|
||||
coeff_sin = tab_sinus[2*qam_bits[bit_traite]+qam_bits[bit_traite+1]];
|
||||
coeff_cos = tab_cosinus[2*qam_bits[bit_traite]+qam_bits[bit_traite+1]];
|
||||
}
|
||||
else // bit impair => bits 1, 3, 5 ou 7
|
||||
{
|
||||
coeff_sin = tab_sinus[2*qam_bits[bit_traite-1]+qam_bits[bit_traite]];
|
||||
coeff_cos = tab_cosinus[2*qam_bits[bit_traite-1]+qam_bits[bit_traite]];
|
||||
}
|
||||
break;
|
||||
|
||||
case MOD_16_QAM_TYPE:
|
||||
// On code cette fois chaque symbole sur 4 bits
|
||||
// La constellation est la suivante
|
||||
// (+ toutes les permutations circulaire et symétries possibles !!) :
|
||||
//
|
||||
// 0010 0110 | 1110 1010
|
||||
// |
|
||||
// 0011 0111 | 1111 1011
|
||||
// ___-3___-1 _|__ 1____3__
|
||||
// 0001 0101 | 1101 1001
|
||||
// |
|
||||
// 0000 0100 | 1100 1000
|
||||
// |
|
||||
// Horizontalement et verticalement, 4 valeurs possibles:
|
||||
// -3 -1, 1 et 3 pour les 2 sinusoïdes, ce qui donne les 16 cas
|
||||
// 16 coefficients pour cos et sin :
|
||||
// Exemple : on codera 0011 (MSB = 0, LSB = 1 ici), avec les amplitudes -3 pour le cos et +1 pour le sin
|
||||
// etc
|
||||
// De plus il faudra tester le bit traité module 4
|
||||
|
||||
// On normalise l'amplitude : la plus grande pouvant valoir 3 x 1.414 ~ 4.24
|
||||
amplitude = base_amp/4.24f;
|
||||
|
||||
if ((bit_traite % 4 ) == 0) // bit 0 modulo 4 (0,4,8,12,16 ...)
|
||||
{
|
||||
coeff_sin = tab_sin16[8*qam_bits[bit_traite]+4*qam_bits[bit_traite+1]+2*qam_bits[bit_traite+2] + qam_bits[bit_traite+3]];
|
||||
coeff_cos = tab_cos16[8*qam_bits[bit_traite]+4*qam_bits[bit_traite+1]+2*qam_bits[bit_traite+2] + qam_bits[bit_traite+3]];
|
||||
}
|
||||
else if ((bit_traite % 4 ) == 1) // bit 1 modulo 4 (1,5,9,13,17 ...)
|
||||
{
|
||||
coeff_sin = tab_sin16[8*qam_bits[bit_traite-1]+4*qam_bits[bit_traite] + 2*qam_bits[bit_traite+1] + qam_bits[bit_traite+2]];
|
||||
coeff_cos = tab_cos16[8*qam_bits[bit_traite-1]+4*qam_bits[bit_traite] + 2*qam_bits[bit_traite+1] + qam_bits[bit_traite+2]];
|
||||
}
|
||||
else if ((bit_traite % 4 ) == 2) // bit 2 modulo 4 (2,6,10,14,18 ...)
|
||||
{
|
||||
coeff_sin = tab_sin16[8*qam_bits[bit_traite-2]+4*qam_bits[bit_traite-1]+2*qam_bits[bit_traite] + qam_bits[bit_traite+1]];
|
||||
coeff_cos = tab_cos16[8*qam_bits[bit_traite-2]+4*qam_bits[bit_traite-1]+2*qam_bits[bit_traite] + qam_bits[bit_traite+1]];
|
||||
}
|
||||
else if ((bit_traite % 4 ) == 3) // bit 3 [modulo 4] (3,7,11,15 ...)
|
||||
{
|
||||
coeff_sin = tab_sin16[8*qam_bits[bit_traite-3]+4*qam_bits[bit_traite-2]+2*qam_bits[bit_traite-1]+ qam_bits[bit_traite]];
|
||||
coeff_cos = tab_cos16[8*qam_bits[bit_traite-3]+4*qam_bits[bit_traite-2]+2*qam_bits[bit_traite-1]+ qam_bits[bit_traite]];
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// on dessine 100 échantillons par bit de la trame
|
||||
for (int i = 0; i < samples_per_bit; i++)
|
||||
{
|
||||
float t = (bit_traite * samples_per_bit + i) / (float)samples_per_bit;
|
||||
float y = center_y - (coeff_sin * sinf(2.0f * M_PI * c_freq * t) + coeff_cos * cosf(2.0f * M_PI * c_freq * t)) * amplitude;
|
||||
|
||||
ImVec2 p(x, y);
|
||||
//if (i != (samples_per_bit -1))
|
||||
// Le dernier raccord peut poser problème dans certains cas
|
||||
// et on ne dessine pas la ligne entre le dernier point du bit précédent
|
||||
// et le premier point du bit suivant. Ceci en M_QAM pour l'instant (pour tester).
|
||||
if (MOD_M_QAM_TYPE == *qam_type)
|
||||
{
|
||||
if (i != 0)
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
else
|
||||
draw_list->AddLine(p, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
}
|
||||
else
|
||||
draw_list->AddLine(prev, p, IM_COL32(255,0,0,255), 2.0f);
|
||||
|
||||
prev = p;
|
||||
x += dx;
|
||||
}
|
||||
|
||||
// FIXME : d'où viennent ces valeurs 5 et 22 ?
|
||||
draw_list->AddText ( ImVec2(x - samples_per_bit * dx + 5, canvas_pos.y + canvas_size.y - 22),
|
||||
IM_COL32(0,0,255,255),
|
||||
qam_bits[bit_traite] ? " 1 " : " 0 "
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::NewLine();
|
||||
ImGui::SeparatorText("Bits");
|
||||
|
||||
drawVToggleButtons(qam_bits, getWordSize());
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// ----------------------------- RIGHT : OPTIONS ------------------------
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("Options", ImVec2(CHILD2_WIDTH - 8.0f /* minus the border */, 0), true);
|
||||
|
||||
ImGui::Text("Type de modulation");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::RadioButton("4-QAM (= 4-PSK)" , (int*)&qam_modulation_t, MOD_4_QAM_TYPE);
|
||||
ImGui::RadioButton("16-QAM " , (int*)&qam_modulation_t, MOD_16_QAM_TYPE);
|
||||
|
||||
// --- AJOUT TÉMOIN ---
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Principe :");
|
||||
ImGui::Spacing();
|
||||
|
||||
ImVec2 ind_size(ImGui::GetContentRegionAvail().x - 10, 50);
|
||||
ImU32 col = IM_COL32(0, 120, 255, 255); // Même bleu que le signal
|
||||
|
||||
switch(qam_modulation_t)
|
||||
{
|
||||
case MOD_4_QAM_TYPE:
|
||||
ImGui::Text("2 bits par symbole");
|
||||
ImGui::NewLine();
|
||||
// Use 4-QAM rules
|
||||
DrawIndicator("Le symbole vaut 11 : ", &c_freq, &qam_modulation_t, &samples_per_bit, 1.0f, -3.0f * M_PI/4.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 10 : ", &c_freq, &qam_modulation_t, &samples_per_bit, 1.0f, 3.0f * M_PI/4.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 01 : ", &c_freq, &qam_modulation_t, &samples_per_bit, 1.0f, -M_PI/4.0f, col, ind_size);
|
||||
|
||||
ImGui::NewLine();
|
||||
DrawIndicator("Le symbole vaut 00 : ", &c_freq, &qam_modulation_t, &samples_per_bit, 1.0f, M_PI/4.0f, col, ind_size);
|
||||
break;
|
||||
|
||||
case MOD_16_QAM_TYPE:
|
||||
ImGui::Text("4 bits par symbole");
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("Constellation 16-QAM : ");
|
||||
LoadTextureFromFile("../images/Constellation_modulation_numerique_16_QAM.jpg",
|
||||
&my_image_texture,
|
||||
&my_image_width,
|
||||
&my_image_height);
|
||||
ImGui::Image((ImTextureID)(intptr_t)my_image_texture, ImVec2((float)ImGui::GetWindowSize().x - 18.0f, (float)ImGui::GetWindowSize().x - 18.0f));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// ------- FIN AJOUT TEMOIN -------------
|
||||
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void DigitalModulation::setDigitalModulationType(TAB_Name aTab, DigitalModulationType aType)
|
||||
{
|
||||
switch (aTab)
|
||||
{
|
||||
case Amplitude_TAB:
|
||||
md_AM_DigitalModulation_t = aType;
|
||||
break;
|
||||
|
||||
case Frequency_TAB:
|
||||
md_FM_DigitalModulation_t = aType;
|
||||
break;
|
||||
|
||||
case Phase_TAB:
|
||||
md_PM_DigitalModulation_t = aType;
|
||||
break;
|
||||
|
||||
case M_QAM_TAB:
|
||||
md_M_QAM_DigitalModulation_t = aType;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Référencer dans un nouveau ticket
Bloquer un utilisateur