IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Raspberry Pi Discussion :

Acquisition de donnée physiologique avec Raspbery Pi 2


Sujet :

Raspberry Pi

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 16
    Points : 7
    Points
    7
    Par défaut Acquisition de donnée physiologique avec Raspbery Pi 2
    Bonjour,
    Je suis en train de réaliser un boitier permettant d’acquérir des données physiologiques telles que l'activité cardiaque et respiratoire. Pour cela j'ai mis en oeuvre un convertisseur analogique-numérique (un MCP3208) et une Raspberry Pi 2 qui communique via un bus SPI. Je doit donc acquérir 3 données analogiques simultanément à une fréquence de 1000Hz. J'ai pour cela mis en oeuvre ce code globalement fonctionnel cependant je me suis rendu compte que la fréquence plafonnait aux environ de 80Hz ce qui est très loin de mes exigences... Auriez-vous donc des conseils à me donner afin d'optimiser ce code afin d'augmenter de façon significative ma fréquence d’échantillonnage ?

    Merci d'avance

    Tom

    Code :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
     
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <time.h>
     
    #include <wiringPi.h>
    #include <wiringPiSPI.h>
     
    #define CS_MCP3208  8
     
    #define SPI_CHANNEL 0
    #define SPI_SPEED   100000000
     
     
    int read_mcp3208_adc(unsigned char adcChannel)
    {
        unsigned char buff[3];
        int adcValue = 0;
     
        buff[0] = 0x06 | ((adcChannel & 0x07) >> 7);
        buff[1] = ((adcChannel & 0x07) << 6);
        buff[2] = 0x00;
     
        digitalWrite(CS_MCP3208, 0);
     
        wiringPiSPIDataRW(SPI_CHANNEL, buff, 3);
     
        buff[1] = 0x0F & buff[1];
        adcValue = ( buff[1] << 8) | buff[2];
     
        digitalWrite(CS_MCP3208, 1);
     
        return adcValue;
    }
     
     
    int main (void)
    {
        int adc1Channel = 0;
        int adc1Value   = 0;
        int adc2Channel = 1;
        int adc2Value   = 0;
        int adc3Channel = 2;
        int adc3Value   = 0;
     
        if(wiringPiSetup() == -1)
        {
            fprintf (stdout, "Unable to start wiringPi: %s\n", strerror(errno));
            return 1 ;
        }
     
        if(wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1)
        {
            fprintf (stdout, "wiringPiSPISetup Failed: %s\n", strerror(errno));
            return 1 ;
        }
     
        FILE *fp;
        fp = fopen("mesureEDP.csv", "a");
        if(fp == NULL){
            printf("cannot open the file");
            return -1;
            }
        fprintf(fp, "Ecg	Resp1	Resp2\n"); 
        pinMode(CS_MCP3208, OUTPUT);
     
        while(1)
        {
            adc1Value = read_mcp3208_adc(adc1Channel);
           	adc2Value = read_mcp3208_adc(adc2Channel);
            adc3Value = read_mcp3208_adc(adc3Channel);
            printf("ECG = %d\t", adc1Value);
           	printf("Resp1 = %d\t", adc2Value);
            printf("Resp2 = %d\n", adc3Value);
            fprintf(fp, "%d\t", adc1Value);
           	fprintf(fp, "%d\t", adc2Value);
            fprintf(fp, "%d\n", adc3Value);
            system("clear");
            usleep(1000);
        }
        fclose(fp);
        return 0;
    }

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    1000 Hz pour une fréquence cardiaque et un rythme respiratoire ?? Ca ne te parait pas un peu délirant ?

    Pour moi, il y a déjà un problème dans ton programme : à chaque tour de boucle, tu fais tes actions (qui prennent donc un certain temps) puis que tu endors ton programme pendant 1000 µs, soit 1 ms. Ainsi, chaque itération dure plus d'une milliseconde, c'est obligatoire. Or, tu souhaites échantillonner à 1000 Hz, c'est-à-dire réaliser un échantillon toutes les millisecondes....

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 16
    Points : 7
    Points
    7
    Par défaut
    Bah je travail avec un cardiologue chercheur qui à besoin d'une fréquence élevé pour ces recherches...

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    OK, on n'est donc pas dans la simple acquisition de la fréquence cardiaque mais de tous les mouvements du coeur.

    J'ai modifié mon précédent message entre temps... Je t'invite à le relire. Je réfléchis un peu d'ici là.

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 16
    Points : 7
    Points
    7
    Par défaut
    Tout d'abord merci pour ta réponse rapide !
    Oui en ce qui concerne la fonction sleep je me suis bien rendu compte qu'elle n'était pas idéale, cependant j'ai cherché à utiliser la technique d'interruption mais je n'ai pas trouvé comment la mettre en œuvre dans un programme C sur Raspberry (si vous avez des idées là dessus je suis preneur)
    Sinon avant de s'attarder plus sur le sujet pensez-vous que la Raspberry est suffisamment puissante pour ce type de projet ?

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Je pense que le Raspberry pourra tout à fait supporter une telle charge. En tout cas, je l'espère ! Je ne connais pas trop les performances de cette plateforme mais ça ne me parait pas fou fou comme tâche. Tu peux regarder quelques articles de C. Blaess, il fait joujou avec les GPIO et atteint de bonnes fréquences :
    http://www.blaess.fr/christophe/2014...-de-frequence/
    http://www.blaess.fr/christophe/2012...-raspberry-pi/

    En revanche, il faudra construire intelligemment ton programme pour y arriver. La solution simple que je vois comme ça est la suivante :
    - Créer un timer qui se déclenche une interruption toutes les millisecondes (ce qui donne une fréquence de 1000 Hz). Tu trouveras des tuto sur le net pour faire ça. Je crois que C. Blaess en utilise un dans le 2e lien.
    - L'action périodique du timer est de faire les lectures qui vont bien et de pousser les résultats dans une file d'attente (queue).
    - En fond, un thread vient dépiler les éléments de la queue : mettre en forme les données, les filtrer, les enregistrer dans un fichier.

    Il est possible que tout fonctionne dans l'action du timer, à tester. Mais il faut alors bannir les affichages en console (cout, printf, puts ou trucs dans le genre), c'est lent comme la mort. Il faut aussi se méfier des écritures vers les fichiers, ça peut prendre du temps.

    L'intérêt de passer par une file est de découpler les traitements.
    - D'un côté, tu as un producteur : il lit les données et les pousse dans la file. Tu pourras éventuellement optimiser la manière de lire (je vois par exemple que tu sembles et allumer ton convertisseur à chaque fois, est-ce nécessaire ?)
    - De l'autre, tu as un consommateur : il récupère les données et les traite. Tu peux ainsi tester plusieurs stratégies de récupération. Notamment, tu pourras facilement adopter une nouvelle stratégie pour bufferiser plusieurs données et les écrire en une seule fois et ainsi limiter les accès aux fichiers.

    Le producteur étant cadencé par le timer, il préemptera le consommateur pour respecter la contrainte temps réel d'un acquisition toutes les millisecondes.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Par curiosité, j'ai tenté de faire un code d'exemple. C'est peut-être un vilain mix de C et de C++, je sais pas trop...

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    #include <iostream>
    #include <queue>
     
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/time.h>
     
    using namespace std;
     
    // Structure to store the data from the sensors
    typedef struct {
    	long id;
    	double heart;
    	double breath;
    } sample;
     
    // A queue to share data between the timer's handler and the main thread
    queue<sample> shared_queue;
     
    // The timer handler
    void timer_handler(int signum) {
    	static long count = 1;
    	cout << "Timer: " << count << "th timer expiration" << endl;
    	count++;
     
    	// Create sample
    	double h = rand();
    	double b = rand();
    	sample s = { count, h, b };
     
    	// Add sample to queue
    	shared_queue.push(s);
    	cout << "Timer: sample count in queue " << shared_queue.size() << endl;
    }
     
    // Main program
    int main() {
    	cout << "Main: Start application" << endl;
     
    	// Set timer_handler as the handler for SIGALARM signals
    	struct sigaction sa = { 0 };
    	sa.sa_handler = &timer_handler;
    	sigaction(SIGALRM, &sa, NULL);
     
    	// Create the timer configuration
    	struct itimerval timer = { 0 };
     
    	// Configure the first timeout to be in 3 seconds
    	timer.it_value.tv_sec = 3;
    	timer.it_value.tv_usec = 0;
    	// Configure for repetitions each 500 ms
    	timer.it_interval.tv_sec = 0;
    	timer.it_interval.tv_usec = 500000;
     
    	// Start the timer so that it generates SIGALARM signals
    	cout << "Main: Start timer" << endl;
    	setitimer(ITIMER_REAL, &timer, NULL);
     
    	// Do some work
    	// Get first sample, display and delete it from queue
    	while (1) {
    		while (shared_queue.empty()) {
    			// wait
    			// It would better to use a semaphore here
    		}
    		cout << "Main: Get sample" << endl;
    		sample s = shared_queue.front();
    		cout << "Main: New sample is " << s.id << " / " << s.heart << " / "
    				<< s.breath;
    		shared_queue.pop();
    	}
    }

    La sortie en console ressemble à ça :
    Main: Start application
    Main: Start timer
    Timer: 1th timer expiration
    Timer: sample count in queue 1
    Main: Get sample
    Main: New sample is 2 / 1.80429e+09 / 8.46931e+08Timer: 2th timer expiration
    Timer: sample count in queue 1
    Main: Get sample
    Main: New sample is 3 / 1.68169e+09 / 1.71464e+09Timer: 3th timer expiration
    Timer: sample count in queue 1
    Main: Get sample
    Main: New sample is 4 / 1.95775e+09 / 4.24238e+08Timer: 4th timer expiration
    Timer: sample count in queue 1
    Main: Get sample
    

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 16
    Points : 7
    Points
    7
    Par défaut
    Vraiment merci pour cette réponse très fournie !! Il me reste à éplucher toutes ces propositions ! Merci !

  9. #9
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 191
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 191
    Points : 11 577
    Points
    11 577
    Par défaut
    Salut,
    Je jette juste quelques réflexions en l'air.

    Citation Envoyé par tom.aubier
    je travail avec un cardiologue chercheur qui à besoin d'une fréquence élevé pour ces recherches
    Le cardiologue chercheur souhaite une fréquence d'acquisition élevée probablement car il a besoin de précision dans les données, je pense qu'il souhaite voir jusqu'à la moindre petite variation rapide. Alors effectivement il faut une fréquence d'échantillonnage au moins deux fois plus élevée que celle de la plus petite variation rapide. C'est le théorème de Nyquist-Shannon (sinon on a des loupés dans l’acquisitions et des phénomènes repliement spectral = aliasing)

    Citation Envoyé par tom.aubier
    j'ai cherché à utiliser la technique d'interruption mais je n'ai pas trouvé comment la mettre en œuvre dans un programme C sur Raspberry
    Excellente idée ! Un timer réglé de manière rapide et qui déclenche une interruption régulière aurait été la meilleure des solutions.

    Citation Envoyé par Bktero
    - Créer un timer qui se déclenche une interruption toutes les millisecondes (ce qui donne une fréquence de 1000 Hz). Tu trouveras des tuto sur le net pour faire ça. Je crois que C. Blaess en utilise un dans le 2e lien.
    - L'action périodique du timer est de faire les lectures qui vont bien et de pousser les résultats dans une file d'attente (queue).
    - En fond, un thread vient dépiler les éléments de la queue : mettre en forme les données, les filtrer, les enregistrer dans un fichier.

    Il est possible que tout fonctionne dans l'action du timer, à tester. Mais il faut alors bannir les affichages en console (cout, printf, puts ou trucs dans le genre), c'est lent comme la mort. Il faut aussi se méfier des écritures vers les fichiers, ça peut prendre du temps.

    L'intérêt de passer par une file est de découpler les traitements.
    - D'un côté, tu as un producteur : il lit les données et les pousse dans la file. Tu pourras éventuellement optimiser la manière de lire (je vois par exemple que tu sembles et allumer ton convertisseur à chaque fois, est-ce nécessaire ?)
    - De l'autre, tu as un consommateur : il récupère les données et les traite. Tu peux ainsi tester plusieurs stratégies de récupération. Notamment, tu pourras facilement adopter une nouvelle stratégie pour bufferiser plusieurs données et les écrire en une seule fois et ainsi limiter les accès aux fichiers.
    Le producteur étant cadencé par le timer, il préemptera le consommateur pour respecter la contrainte temps réel d'un acquisition toutes les millisecondes.
    Ça c'est la perfection !



    Maintenant on peut se demander si le Raspberry, sur le quel on ne peut pas faire d'interruption comme sur un micro-contrôleur et qui possède un système d'exploitation qui va à l'encontre du temps réel, est vraiment la bonne solution pour faire ce projet ?
    Est ce un problème d'avoir une fréquence d'échantillonnage imprécise avec un Raspberry qui est non temps réel ?

    Imaginons, l'inverse, que tu fasses l'acquisition d'une jolie sinusoïde parfaite de manière très régulière et que tu souhaites calculer la période du signal. Comment ferais tu ?
    Voilà par exemple des données que tu pourrais acquérir (toute les 1ms et en Volt)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    0.0 | 1.5 | 2.7 | 3.3 | 2.7 | 1.5 | 0.0 | -1.5 | -2.7 | -3.3 | -2.7 | -1.5 | 0.0 | 1.5 | 2.7 | 3.3 | 2.7 | 1.5 | 0.0 | -1.5 | -2.7 | -3.3 | -2.7 | -1.5 | 0.0
    Il est vrai que si tu prends un crayon et que tu retraces ces valeurs sur une grille alors tu verra un signal triangulaire variant entre -3.3V et +3.3V et pour avoir une vraie sinusoïde, il aurait fallut beaucoup plus de données et un tableau plus grand.

    Et la période dans tout ça ?
    On sait que les données ont été acquise toutes les 1ms et on sait qu'une période c'est lorsque le signal se reproduit identique à lui même
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    0.0 | 1.5 | 2.7 | 3.3 | 2.7 | 1.5 | 0.0 | -1.5 | -2.7 | -3.3 | -2.7 | -1.5 |
    Lorsque le signal passe 3 fois par 0 on a une période. On a une période mais il ne faut pas compter le troisième passage par 0 car on entre dans la période suivante. Chaque échantillon a été mesuré toutes les 1ms donc il suffit de compter le nombre d'échantillons finalement.... et on trouve 12 échantillons donc 12ms de période et donc 1/12ms = 83.3Hz

    Juste avec un tableau de valeurs issues de l'ADC et pour peu qu'elles sont acquises de manière très régulière tu peux avec un bon algorithme connaître la période du signal que tu es entrain d'acquérir (et donc sa fréquence)

    Et si tu n'es pas sur de ces 1ms justement ? Un coup le signal est acquis a 1ms, le coup d'après à 1.5ms, le coup d'après à 0.8ms, le coup d'après à 1.3ms ...... Comment calculer la période ? C'est plus problématique et il faut déployer d'autres artifices plus lourd. Tu peux aussi être amené à calculer une intégrale du signal (Aire sous la courbe / période) ou la dérivée du signal à un instant donnée (dv/dt) mais là aussi on a besoin de connaître la période et pour la dérivée il nous faut "dv" qui n'est d'autre que ce fameux 1ms pas stable.....

    Finalement est ce que le Raspberry est adapté ? Peut être mais à voir avec la précision désirée aussi. Est ce que Arduino n'aurait pas été plus adapté ? Les interruptions y sont possibles, on a pas de systèmes d'exploitation qui va amener des temps de latence etc...


    C'est toute la théorie du traitement du signal tout ça est c'est super intéressant !
    A+
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    J'ai fait un test pour voir la régularité d'exécution du timer en modifiant mon code précédent. Je suis sans voix devant tant d'imprécision

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    #include <iostream>
    #include <queue>
     
    #include <limits.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <time.h>
     
    using namespace std;
     
    #define BILLION		(1000000000ULL)
    #define MAX_ID		(10)
    #define INTERVAL_US	(500 * 1000)
     
    // Structure to store the data from the sensors
    typedef struct {
    	long id;
    	double heart;
    	double breath;
    	struct timespec timestamp;
    } sample;
     
    // A queue to share data between the timer's handler and the main thread
    queue<sample> shared_queue;
     
    // The timer handler
    void timer_handler(int signum) {
    	static long count = 0;
    	count++;
     
    	// Create sample
    	double h = rand();
    	double b = rand();
    	struct timespec t = { 0 };
    	clock_gettime(CLOCK_REALTIME, &t);
    	sample s = { count, h, b, t };
     
    	// Add sample to queue
    	shared_queue.push(s);
    }
     
    // Main program
    int main() {
    	cout << "Main: Start application" << endl;
     
    	// Set timer_handler as the handler for SIGALARM signals
    	struct sigaction sa = { 0 };
    	sa.sa_handler = &timer_handler;
    	sigaction(SIGALRM, &sa, NULL);
     
    	// Create the timer configuration (starts in 1 sec, repeats each INTERVAL_US usec)
    	struct itimerval timer = { 0 };
    	timer.it_value.tv_sec = 1;
    	timer.it_value.tv_usec = 0;
    	timer.it_interval.tv_sec = 0;
    	timer.it_interval.tv_usec = INTERVAL_US;
     
    	// Start the timer so that it generates SIGALARM signals
    	cout << "Main: Start timer with interval " << INTERVAL_US << endl;
    	setitimer(ITIMER_REAL, &timer, NULL);
     
    	// Consume samples generated by the timer's handler
    	struct timespec time_previous = { 0 };
    	unsigned long long max_delta = 0;
    	unsigned long long min_delta = ULLONG_MAX;
     
    	while (1) {
    		while (shared_queue.empty()) {
    			// wait
    			// It would better to use a semaphore here
    		}
     
    		// Get a sample and delete it from the queue
    		sample s = shared_queue.front();
    		shared_queue.pop();
     
    		// Analyze time delta between two samples
    		if (s.id == 1) {
    			time_previous = s.timestamp;
    		} else {
    			unsigned long long delta = BILLION
    					* (s.timestamp.tv_sec - time_previous.tv_sec)
    					+ s.timestamp.tv_nsec - time_previous.tv_nsec;
     
    			if (delta > max_delta) {
    				max_delta = delta; // delta has never been so big
    			} else if (delta < min_delta) {
    				min_delta = delta; // delta has never been so small
    			}
     
    			//cout << (delta / (1000.0)) << endl;
     
    			time_previous = s.timestamp;
    		}
     
    		// Stop the program when even samples have been processed for the test
    		if (s.id == MAX_ID) {
    			cout << "Main: program will stop after " << s.id << " samples"
    					<< endl;
    			cout << "Max = " << (max_delta / 1000.0) << " us" << endl;
    			cout << "Min = " << (min_delta / 1000.0) << " us" << endl;
     
    			break; // exit the loop
    		}
    	}
    }


    $ sudo nice -n -20 ./linux_timer_2 
    Main: Start application
    Main: Start timer with interval 500000
    Main: program will stop after 10 samples
    Max = 501998 us
    Min = 498101 us
    
    $ sudo nice -n -20 ./linux_timer_2 
    Main: Start application
    Main: Start timer with interval 10000
    Main: program will stop after 1000 samples
    Max = 26807.1 us
    Min = 80.508 us
    
    C'est la première fois que je fais joujou avec les timer et les clocks, j'espère que c'est mon code qui est imparfait...

    Note : testé sur un Xubuntu dans une VirtualBox.

  11. #11
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Avril 2014
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Avril 2014
    Messages : 16
    Points : 7
    Points
    7
    Par défaut
    Salut,
    En ce qui concerne la gestion du temps il me semble qu'il est possible de générer un signal carré grâce aux sortie PWM, j'imagine que cette sortie est cadencée de façon régulière, si c'est le cas il me serait donc possible de mettre dans mon programme un interruption qui lit mes entrées analogiques et stocke les valeurs provisoirement dans une liste à chaque "tic" de la sortie PWM pour par la suite enregistrer cette liste dans un fichier tous les 10 tours par exemple...
    Je ne sais pas si cette solution est optimale, dites moi ce que vous en pensez

  12. #12
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 191
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 191
    Points : 11 577
    Points
    11 577
    Par défaut
    Citation Envoyé par tom.aubier Voir le message
    En ce qui concerne la gestion du temps il me semble qu'il est possible de générer un signal carré grâce aux sortie PWM, j'imagine que cette sortie est cadencée de façon régulière
    Normalement oui, du moins j'ose espérer.

    Citation Envoyé par tom.aubier Voir le message
    si c'est le cas il me serait donc possible de mettre dans mon programme un interruption qui lit mes entrées analogiques et stocke les valeurs provisoirement dans une liste à chaque "tic" de la sortie PWM pour par la suite enregistrer cette liste dans un fichier tous les 10 tours par exemple...
    Tu ne peux malheureusement pas faire d'interruption sur un Raspberry car tu as un système d'exploitation (multitâches) dessus. Si tu préfères, un système d'exploitation c'est l'inverse complet d'un programme avec interruption.
    Un système d'exploitation lance des tâches en parallèle (soit disant en parallèle) pour que l'utilisateur ait l'impression de fluidité et que tu peux faire plein de truc en même temps. Avec un OS, tu peux lancer en un intervalle de temps très court Word, Excle, Power Point et tu as l'impression qu'il se lance en même temps. Autrement dit, tu n'es pas obligé d'attendre que le premier se lance entièrement, puis le deuxième et enfin le troisième. C'est ça le multitâche.

    Mettre des interruptions c'est casser tout ça et c'est stopper ce qu'on est entrain de faire pour traiter l'interruption puis revenir a ce qu'on faisait. C'est presque complètement l'inverse.

    Le mieux qu'on puisse faire dès qu'il y a un OS, c'est ce que Bktero t'a montré par l'utilisation de thread, timer, clock mais tu as la précision que l'OS et ces différentes tâches veulent bien te donner (et si je ne m'abuse c'est aussi un peu en fonction de la charge du Raspberry lance plein d'applications et ça devrait dégrader encore plus les timers).

    Tu peux aussi faire en sorte que ta tâche soit prioritaire sur les autres tâches et en plus faire de ton OS un OS temps réel voir Xénomai (jamais utilisé) avec tout ça tu vas améliorer grandement la précision de la fréquence et t'affranchir un peu du problème de latence (phénomène du temps de réaction face a un événement) mais jamais tu n'égaleras la qualité d'une véritable interruption qui elle stop intégralement le programme principal pour s'exécuter.


    Une solution demandant plus d'électronique et un autre type d'ADC serait, grâce a une horloge indépendante, de faire faire à l'ADC des acquisitions régulières et de stocker ça dans une mémoire externe. Calculer le temps que mettrait cette mémoire à se remplir (comme un connait l'horloge c'est facile) imaginons 500us. Et faire un programme dans le Raspberry qui vient lire et vider la mémoire toutes les 300us environ, car à 500us elle sera pleine et on sait aussi que c'est pas précis côté Raspberry. Là ça fonctionne ! Tu remarquera aussi que c'est ce que Bktero avait dit de faire (l'histoire du buffer) mais en soft mais le problème c'est que c'est pas précis sur le Raspberry.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  13. #13
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 191
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 191
    Points : 11 577
    Points
    11 577
    Par défaut
    Il y a un moyen de t'en sortir avec le Raspberry et la technique de Bktero, il faut qu'a chaque fois que tu lises l'ADC, que tu lises aussi l'heure (en supposant que tu puisses aller jusqu'au µs) du Raspberry.

    Au quel cas, tu peux stocker dans un fichier les échantillons + l'heure a laquelle ils ont été pris = calcul possible puisque tu t'affranchies du fait que les échantillons ne seront pas super régulier.

    Petite question perso :
    Tu utilises quoi comme sonde physiologique ? Je veux dire ceux des sondes toutes faites où bien c'est un truc que tu as fait aussi ? Je me suis toujours demandé comment c'était fait ce genre de sonde.... ?
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

Discussions similaires

  1. Réponses: 10
    Dernier message: 04/08/2015, 20h59
  2. Acquisition de données avec un ActiveX External Interface
    Par Mjama dans le forum Interfaces Graphiques
    Réponses: 0
    Dernier message: 14/09/2010, 23h43
  3. Acquisition de données Agilent PSA E4440 avec Labview
    Par savoma2000 dans le forum LabVIEW
    Réponses: 1
    Dernier message: 04/08/2008, 16h55
  4. Probleme avec l'acquisition des données dans un port dans le serveur
    Par soufian2290 dans le forum Serveurs (Apache, IIS,...)
    Réponses: 12
    Dernier message: 04/11/2007, 12h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo