Cours:Elen4 TNS TP TpsReel

De troyesGEII
Aller à : navigation, rechercher

Retour à la page du cours

TP6 : Temps réel : Codage d'un bloc en C++ / démodulation

Le but de ce TP est de réaliser l'implémentation temps-réel en C++ de filtres IIR. Cette implémentation sera réalisée en créant un bloc Gnuradio qui execute un traitement codé en C++

Codage de bloc Gnuradio en c++

Ce guide est inspirée du guide Creating an OOT (C++ block example) en l'adaptant pour nos besoins :

Nous allons créer un simple bloc qui recopie son entrée (en float) sur sa sortie. Donc le filtre caractérisé par y(n) = x(n) ou encore H(z)=1. Il s'agit donc d'une sorte de filtre passe-tout.

Suivez bien toutes les étapes, sans aller trop rapidement

  • Vous pourrez utiliser gedit pour éditer les divers fichiers.

Création d'un module externe

Dans les lignes qui suivent, le $ indique que l'on saisi une commande dans un terminal. Le $ n'est pas à saisir.

  • Ouvrez un terminal et placez-vous à la racine de votre dossier personnel :
$ cd $HOME
  • Créez un dossier de travail dédié à gnuradio et déplacez-vous à l'intérieur :
$ mkdir gnuradio
$ cd gnuradio
  • GNU Radio est livré avec gr_modtool, un utilitaire qui permet de créer des modules externes (OOT). Créons un module monModule
$ gr_modtool newmod monModule
  • Le dossier gr-monModule est créé et contient tout le code squelette d'un module OOT, mais il n'a pas encore de blocs. Déplacez-vous dans gr-monModule :
$ cd gr-monModule
  • Vous pouvez afficher le contenu de ce dossier par
$ ls

Création d'un bloc dans ce module

  • Ajoutez un nouveau bloc nommé passetout :
$ gr_modtool add passetout
  • Quelques questions permettent alors de spécifier le type de bloc
GNU Radio module name identified: passetout
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')

Saisissez sync comme type de bloc, car le bloc que nous écrivons produira le même nombre d'échantillons en sortie que le nombre d'échantillons consommés depuis l'entrée.

Enter block type: sync
  • Spécifiez cpp comme langage :
Language (python/cpp): cpp
Language: C++
Block/code identifier: passetout
  • Spécifiez un nom pour le copyright :
Please specify the copyright holder: Bibi
  • Il est possible de spécifier des paramètres d'entrée qui peuvent permettre de régler le fonctionnement du bloc. Ce ne sera pas notre cas, ne rien saisir sur cette question :
Enter valid argument list, including default arguments:
  • De même, on ne donnera pas de code permettant de tester le bon fonctionnement :
Add Python QA code? [Y/n] n
Add C++ QA code? [Y/n] n
  • Plusieurs fichers sont alors crées :
Adding file 'lib/passetout_impl.h'...
Adding file 'lib/passetout_impl.cc'...
Adding file 'include/gnuradio/monModule/passetout.h'...
Adding file 'python/monModule/bindings/docstrings/passetout_pydoc_template.h'...
Adding file 'python/monModule/bindings/passetout_python.cc'...
Adding file 'grc/monModule_passetout.block.yml'...
Editing grc/CMakeLists.txt...

Quelques indications :

  • le fichier passetout_impl.h contient l'entête de la classe de notre bloc
  • le fichier passetout_impl.cc contient l'implémentation de la classe de notre bloc
  • le fichier monModule_passetout.block.yml va contenir des informations faisant le lien entre notre code c++ et le code python utilisé en interne par Gnuradio
  • Le tout sera compilé par un Makefile généré par cmake.

Fichier passetout_impl.h

Le code utile généré est le suivant :

class passetout_impl : public passetout
{
private:
    // Nothing to declare in this block.

public:
    passetout_impl();
    ~passetout_impl();

    // Where all the action really happens
    int work(int noutput_items,
             gr_vector_const_void_star& input_items,
             gr_vector_void_star& output_items);
};

Pour se réveiller les neurones sur la POO, qu'est-ce que sont

  • passetout_impl(),
  • ~passetout_impl(),
  • private, public ?

Le travail sera réalisé dans la méthode work(), mais nous n'avons pas de modification à apporter dans les déclarations par défaut.

Fichier passetout_impl.cc

Le code utile généré est le suivant :

passetout_impl.cc

namespace monModule {

#pragma message("set the following appropriately and remove this warning")
using input_type = float;
#pragma message("set the following appropriately and remove this warning")
using output_type = float;
passetout::sptr passetout::make() { return gnuradio::make_block_sptr<passetout_impl>(); }


/*
 * The private constructor
 */
passetout_impl::passetout_impl()
    : gr::sync_block("passetout",
                     gr::io_signature::make(
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
                     gr::io_signature::make(
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
}

/*
 * Our virtual destructor.
 */
passetout_impl::~passetout_impl() {}

int passetout_impl::work(int noutput_items,
                         gr_vector_const_void_star& input_items,
                         gr_vector_void_star& output_items)
{
    auto in = static_cast<const input_type*>(input_items[0]);
    auto out = static_cast<output_type*>(output_items[0]);

#pragma message("Implement the signal processing in your block and remove this warning")
    // Do <+signal processing+>

    // Tell runtime system how many output items we produced.
    return noutput_items;
}

} /* namespace monModule */

Détaillons :

#pragma message("set the following appropriately and remove this warning")
using input_type = float;
#pragma message("set the following appropriately and remove this warning")
using output_type = float;

    Todos.png On spécifie ici le type des données d'entrée et de sortie. Pas de modification à apporter, il suffit de supprimer les deux #pragma

passetout_impl::passetout_impl()
    : gr::sync_block("passetout",
                     gr::io_signature::make(
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
                     gr::io_signature::make(
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
}

    Todos.png On spécifie ici le nombre d'entrées (1) et le nombre de sorties (1). Pas de modification non plus.

    Todos.png Expliquez la syntaxe passetout_impl::passetout_impl() : gr::sync_block(...)"

int passetout_impl::work(int noutput_items,
                         gr_vector_const_void_star& input_items,
                         gr_vector_void_star& output_items)
{
    auto in = static_cast<const input_type*>(input_items[0]);
    auto out = static_cast<output_type*>(output_items[0]);

#pragma message("Implement the signal processing in your block and remove this warning")
    // Do <+signal processing+>

    // Tell runtime system how many output items we produced.
    return noutput_items;
}

Cette méthode work() implémente véritablement le filtrage.

  • Les tableaux in et out contiennent respectivement les données en entrée et les données en sortie. Il faut donc calculer out en fonction de in
  • Les données d'entrée arrivent par paquets dans un buffer. La variable noutput_items contient le nombre de données disponibles en entrées.
  • à la fin de la méthode, on renvoie le nombre de données en sortie que l'on a calculé (entre 1 et noutput_items). Les données d'entrées éventuellement non consommées seront conservées dans le buffer pour le prochain appel de work()

    Todos.png On souhaite ici seulement recopier l'entrée sur la sortie, donc

  1. Enlever la ligne #pragma
  2. ajouter le code out[0] = in[0]; (on recopie le premier échantillon disponible en entrée)
  3. changer return noutput_items; en return 1; (un seul échantillon a été consommé).

Fichier monModule_passetout.block.yml

Ce fichier au format YAML (yet another markup language) fait le lien entre le code c++ et le code python utilisé en interne par GnuRadio.

monModule_passetout.block.yml

id: monModule_passetout
label: passetout
category: '[monModule]'

templates:
  imports: from gnuradio import monModule
  make: monModule.passetout()

#  Make one 'parameters' list entry for every parameter you want settable from the GUI.
#     Keys include:
#     * id (makes the value accessible as keyname, e.g. in the make entry)
#     * label (label shown in the GUI)
#     * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#     * default
parameters:
- id: parametername_replace_me
  label: FIX ME:
  dtype: string
  default: You need to fill in your grc/monModule_passetout.block.yaml
#- id: ...
#  label: ...
#  dtype: ...

#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.
#  Keys include:
#      * label (an identifier for the GUI)
#      * domain (optional - stream or message. Default is stream)
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#      * vlen (optional - data stream vector length. Default is 1)
#      * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

outputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

#  'file_format' specifies the version of the GRC yml format used in the file
#  and should usually not be changed.
file_format: 1

    Todos.png Mettre à jour la section parameters (pas de paramètres, on commente) :

#parameters:
#- id: parametername_replace_me
#  label: FIX ME:
#  dtype: string
#  default: You need to fill in your grc/customModule_testMod.block.yaml

    Todos.png Mettre à jour la section inputs :

inputs:
- label: in
  domain: stream
  dtype: float

    Todos.png Mettre à jour la section output :

outputs:
- label: out
  domain: stream
  dtype: float

Compilation et installation

Voici les étapes :

$ cd CHEMIN-DE-VOTRE-DOSSIER/gr-monModule
$ mkdir build
$ cd build
$ cmake ..
$ make
$ make install
  • cmake .. lance la création des fichiers Makefile qui permettent la compilation et l'installation
  • make lance la compilation
  • make install lance l'installation (copie des fichiers dans les dossiers gnuradio)

    Todos.png Exécuter chacune de ces lignes, en contrôlant l'absence d'erreur. Dans le cas contraire, contrôler les fichiers .h, .cc et .yaml

    Todos.png Après ces étapes, et avant de lancer gnuRadio, appeler l'enseignant qui va mettre à jours les bibliothèques partagées.. Sinon tenter :

$ export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
$ gnuradio-companion &

Utilisation du bloc et test

  • Dans Gnuradio, rafraichir la liste des bloc (bouton reload)
  • le bloc passetout doit être présent dans la section monModule
  • Tester le bon fonctionnement de ce bloc en lui envoyant une sinusoïde que vous devez observer identique à la sortie du bloc. Vérifier avec des graphes.

Démodulation

En reprenant le signal modulé en amplitude et la fonction de transfert du filtre passe-bas du TP précédent :

  • Obtenir l'équation aux différence du filtre, ie écrire y(n)= en fonction de x(n), x(n-1), x(n-2), y(n-1) et y(n-2).
  • Créer un nouveau bloc C++ (similaire au passe-tout précédent) dans le module monModule :
    • de type sync
    • une entrée en float
    • une sortie en float
  • Écrire le code permettant d'obtenir y(n) dans la méthode work() :
    • x(n) est donné par in0[0]
    • il sera nécessaire d'affecter out0[0] pour produire y(n).
    • il sera nécessaire de stocker dans des attributs les valeurs de in[0] afin de disposer de x(n-1), x(n-2), y(n-1) et y(n-2).
    • On rappelle que les attributs doivent être déclarées dans le fichier .h et initialisés dans le constructeur de la classe.

    Todos.png Réaliser cette implémentation du passe-bas et contrôler que la démodulation est identique à celle obtenue au TP précédent

    Todos.png Modifier votre code pour implémenter le filtre complet de démodulation