===== Projet Télécommunication Numérique HF : Mise en place d'une chaîne d'encodage pour la transmission radio ======
Ressource documentaire:
https://fr.wikipedia.org/wiki/Phase-shift_keying
--------------------------------------------------------------------------------------------------------
{{https://bvdp.inetdoc.net/files/iut/tp_pic/warning.jpeg}}
A FAIRE A LA FIN DE CHAQUE SEANCE POUR COPIER VOS FICHIERS SUR UN DISQUE PARTAGE, ET PERMETTRE LA RECUPERATION DES FICHIERS SI UN MEMBRE DU BINOME EST ABSENT, SINON TANT PIS POUR VOUS ! \\
\\
copier coller dans une console:
rsync -av --delete ~/Arduino /mnt/etu/s4
et auto-compléter la ligne jusque votre dossier personnel en appuyant 3 fois sur la touche Tabulation, puis entrée.
--------------------------------------------------------------------------------------------------------
Projet pour programmer les maquettes utilisées dans la partie analogique:
wget https://bvdp.inetdoc.net/files/iut/tp_telecomnum/telecom_num_valid_4.cpp.hex
avrdude -p m328p -c arduino -b 115200 -P /dev/ttyACM0 -U flash:w:telecom_num_valid_4.cpp.hex
(voir https://skyduino.wordpress.com/2011/12/02/tutoriel-avrdude-en-ligne-de-commande/ )
Pour les sources du projet
https://bvdp.inetdoc.net/files/iut/tp_telecomnum/telecom_num_valid_4.zip
pour mettre à jour le binaire (après avoir compilé avec l'ide arduino):
scp /tmp/build726420971845041541.tmp/telecom_num_valid_4.cpp.hex bvandepo@homepages.laas.fr:files/iut/tp_telecomnum/
Pour compiler sans l'ide arduino? http://www.francoistessier.info/blog/2011/07/06/programmation-arduino-en-ligne-de-commande/
__Objectifs :__
- Prendre en main la maquette de génération de signaux Arduino
- Prendre en main le squelette de code
- Générer un signal NRZ (non retour à zéro) à partir d'une source de données
- Appliquer le codage Manchester à un signal binaire
- Coder des symboles de transmission à l'aide d'une table de correspondances
- Appliquer une méthode d'étalement de spectre à un signal binaire
Dans ce module nous appliquerons des méthodes logicielles pour générer des signaux pouvant piloter une chaîne de transmission HF (via un Modulateur I/Q). La maquette Arduino est équipée d'un shield de génération de signaux analogiques (DAC) et d'un module de mise en forme du signal pour piloter le modulateur I/Q (Adaptation d'impédance, atténuation, transformation en signaux symétriques). L'essentiel des TP consistera à écrire le code permettant de générer des modulations BPSK, BPSK + Manchester, BPSK + DSSS, QPSK.
Afin de simplifier votre travail, nous vous fournissons un programme incomplet implémentant le code de gestion de l'interface DAC, la gestion du temps et gestion de la synchronisation du calcul et de l'émission des symboles.
Le code est largement commenté, merci de de tenir compte des indications qui s'y trouvent.
**Vous veillerez à bien copier tous vos fichiers de ~/Arduino/ vers votre compte étudiant en fin de séance pour pouvoir les récupérer aux séances suivantes!**
===== Installation de la librairie permettant d'utiliser le convertisseur Numérique analogique =====
**A faire uniquement à la première séance:**
Afin de pouvoir utiliser le shield DAC (présenté sur: https://bvdp.inetdoc.net/wiki/doku.php?id=tpdacspi ), vous devez installer une librairie. Pour cela, copiez-collez dans une console après l'avoir ouverte avec ALT+F2 lxterm :
echo commence
mkdir -p ~/Arduino
cd ~/Arduino
mkdir -p libraries
cd libraries
wget https://bvdp.inetdoc.net/files/iut/tp_dacspi/Tlv5637.zip
unzip -o Tlv5637.zip
rm Tlv5637.zip
echo fini
=== Installation du projet de base et du système de gestion de version===
**A faire uniquement à la première séance:**
Copier et exécuter les instructions suivantes dans une console lxterm
echo commence
cd ~/Arduino
wget https://bvdp.inetdoc.net/files/iut/tp_telecomnum/telecom_num_a_trou_2.zip
unzip telecom_num_a_trou_2.zip
rm telecom_num_a_trou_2.zip
cd telecom_num_a_trou_2
git init
git add *
git commit -m'initial version'
arduino &
echo fini
Une fois l'IDE Ardiuno ouverte, ouvrez le projet telecom_num_a_trou_2 dans le dossier Arduino/.
Pour les séances suivantes, vous ouvrirez directement le projet telecom_num_a_trou_2 qui aura été complété.
===== Configuration de la base de temps =====
Le code de configuration de la base de temps (Timer) est déjà mis en place. Il ne vous reste qu'à configurer celle-ci pour générer une base de temps à 2kHz. La configuration de celle-ci se fait par l'intermédiaire des symboles:
#define PRESCALER_INDEX
#define TIMER_PERIOD
La fréquence de la base de temps peut être calculée par la formule :
FREQ = (16x10^6)/(PRESCALER*(TIMER_PERIOD + 1))
Le facteur de PRESCALER peut être sélectionné par le PRESCALER_INDEX avec :
PRESCALER_INDEX = 0 -> PRESCALER = 1
PRESCALER_INDEX = 1 -> PRESCALER = 8
PRESCALER_INDEX = 2 -> PRESCALER = 32
PRESCALER_INDEX = 3 -> PRESCALER = 64
PRESCALER_INDEX = 4 -> PRESCALER = 128
PRESCALER_INDEX = 5 -> PRESCALER = 256
PRESCALER_INDEX = 6 -> PRESCALER = 1024
La valeur de TIMER_PERIOD devra être chargée dans un registre 8 bits et prendra donc une valeur ENTIERE entre 0 et 255.
Cette base de temps une fois programmée, déclenchera l’exécution de la fonction (gestionnaire d'interruption) qui se charge de piloter le DAC.
ISR(TIMER2_COMPA_vect)
Cette fonction communique avec votre programme à l'aide de deux FIFOs qui permettent de stocker les valeurs pour piloter les canaux I et Q.
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/interrupt_routine.png?600 }}
--------------------------------------------------------------------------------------------------------
=====Exercice 1: Génération d'un signal carré sur la voie I et contrôle de la base de temps=====
Calculer les valeurs de PRESCALER_INDEX et TIMER_PERIOD pour générer une base de temps à 2kHz.
===Solution:===
PRESCALER=64 donc PRESCALER_INDEX=3 et TIMER_PERIOD=124: 64*(124+1)=8000
Dans cette exercice vous coderez dans la boucle principale du programme Arduino, une fonction qui génère un signal périodique 0->1->0->1 sur la voie I et un signal à 0 sur la voie Q. Afin de palier au problème de synchronisation entre l'écriture sur l'ADC et le calcul du signal, nous utiliserons des FIFOs. La génération des signaux se fera alors par écriture sur les FIFOs, la lecture des FIFOs et la génération des ordres de commande du DAC étant déjà implémentées dans la fonction périodique. Vous utiliserez la fonction bloquante **void write_fifo_I_Q(const char * data_I, const char * data_Q, int data_size, int enable_i, int enable_q)** pour écrire deux valeurs simultanément dans les fifos pilotant I et Q.
La génération d'un signal dans une fifo doit donc se faire de la manière suivante :
appeler write_fifo_I_Q en lui passant :
- Le pointeur sur les données I (tableau contenant les échantillons)
- Le pointeur sur les données Q (tableau contenant les échantillons) ou NULL si pas de données
- La valeur data_size qui correspond au nombre d'échantillons dans dans les données I et Q
- Les valeurs enable_i et enable_q (0 ou 1). Un enable à 0 génère des zero dans la fifo correspondante.
===Solution:===
byte val=0;
void loop() {
write_fifo_I_Q(&val, &val, 1, 1, 0); //ecriture de val dans les deux fifo, avec seulement I activée
val^=1;
}
ou en écrivant directement les 8 cases d'un tableau:
byte val[8]={0,1,0,1,0,1,0,1};
void loop() {
write_fifo_I_Q(val, val, 8, 1, 0); //ecriture de val dans les deux fifo, avec seulement I activée
}
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Après avoir écrit votre code, vérifiez à l'oscilloscope le signal généré en expliquant la fréquence du signal carré généré.
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'timer'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 1bis: Pour Debugguer=====
Arduino ne permet pas facilement l'utilisation d'un débuggueur matériel. Il est donc pénible de détecter les erreurs dans un programme. Pour vous aider, la fonction **void displayBuffer(const char * name, char * tab, char tab_size)** est fournie. Cette fonction permet l'affichage d'un tableau de valeur dont le nom est donné en chaine de caractères **name**, dont l'adresse de départ en mémoire est donné par **tab** et dont le nombre de case est de **tab_size**. Le code de la fonction d'affichage, à copier dans votre programme est donné ci dessous:
//#define DEBUG
void displayBuffer(const char * name, char * tab, char tab_size)
{
#ifdef DEBUG
Serial.print("Etat du buffer ");
Serial.print(name);
Serial.print(" contenant ");
Serial.print(tab_size,DEC);
Serial.println(" case(s) : ");
for (int i=0;i
Par exemple pour afficher le **buffer_i** contenant 8 cases remplies, il faudra exécuter:
displayBuffer("buffer_i", buffer_i,8);
L'affichage se faisant sur la liaison série, il ralentie l'exécution du programme, vous veillerez à ne l'activer que lorsque cela sera nécessaire en utilisant:
#define DEBUG
Vous veillerez à désactivez l'affichage en commentant la ligne définissant le symbole **DEBUG** pour le test en temps réel dans lequel vous observez les signaux à l'oscilloscope.
Pour visualiser le contenu des tableaux dans la console série Arduino, vous veillerez à la configurer à 115200Bauds.
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Appeler la fonction d'affichage pour visualiser les données du tableau utilisé pour stocker les échantillons du signal carré généré à l'exercice 1. Dans les exercices suivants, vous pourrez utiliser cette fonction pour visualiser le contenu des tableaux remplis par les différentes fonctions que vous implémenterez.
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'debugger'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 2: Génération du signal NRZ/BPSK sur la voie I=====
Dans cet exercice, vous allez générer un signal NRZ à partir de données stockées en mémoire ou calculées au fil du temps. Dans un premier temps, vous compléterez la fonction suivante, qui remplit le tableau ptr_i avec des valeurs 0 ou 1 dépendant de la donnée à coder src qui est sur size_src bits **en commençant par le bit de poids faible**. Cette fonction retourne le nombre de symboles générés dans les tableaux I et Q;
int encode_bpsk(char src, char * ptr_i, unsigned char size_src)
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/encode_bpsk.png?400 }}
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Implémentez la fonction encode_bpsk en vous aidant des commentaires de celle-ci. Dans un deuxième temps, vous compléterez la fonction loop (en vous aidant du code de l'exercice précédent) pour :
- générer une donnée à transmettre sur 8-bits (0x5A)
- appeler la fonction encode_bpsk et affecter la valeur retournée par cette fonction à la variable **data_size**. La fonction attend en argument un tableau pour stocker les symboles produits. Vous utiliserez donc le tableau déjà défini **i_buffer**
- écrire le résultat du traitement (les **data_size** cases du tableaux **i_buffer**) dans la FIFO pour I uniquement (désactiver la fifo Q) en utilisant la fonction **write_fifo_I_Q**
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/bpsk_encoding_chain.png?600 }}
Exécuter le même programme mais pour une donnée valant 0xB5. Comparer les signaux observés à l'oscilloscope avec les signaux prévus et conclure.
===Solution:===
int encode_bpsk(char src, char * dst_i, unsigned char size_src){
//Pour n allant de 0 à size_src-1
//1° : isoler le bit numéro n de l'octet src
//2° : écrire dans le tableau dst_i à l'indice i la valeur 1 si le bit isolé vaut 1
// 0 sinon
//retourner size_src
byte val;
for (int n=0; n>n)&1;
dst_i[n] =val;
}
return size_src;
}
char vali[8];
byte nb_symboles;
void loop() {
//executer la chaine de codage
//écrire les valeurs codés dans les fifos I et Q
nb_symboles=encode_bpsk(0x5A, vali, 8);
write_fifo_I_Q(vali, NULL, nb_symboles, 1, 0):
}
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'bpsk'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 3: Codage Manchester de la voie I =====
Dans cet exercice, nous allons ajouter une étape au codage NRZ afin de coder les bits de données au format Manchester.
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Complétez dans un premier temps la fonction **manchester_as_xor** (l'opérateur **XOR** est défini au début du fichier). **Attention, dans le tableau de symboles généré par encode_bpsk, les symboles sont stockés en partant du bit de poids faible.**
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/manchester.png?400 }}
Vous rajouterez ensuite l'appel de cette fonction dans la fonction loop à la suite de la fonction encode_bpsk pour traiter la valeur 0x5A (8-bits). La fonction **manchester_as_xor** devra encoder le tableau issu de **encode_bpsk** vers un nouveau tableau (**manchester_buffer**) dont le contenu sera écrit dans la FIFO I comme dans l'exercice précédent.
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/bpsk_encoding_chain_manchester.png }}
Une fois le programme compilé et chargé, vérifiez à l'oscilloscope le signal généré.
Exécuter le même programme mais pour une donnée valant 0xB5. Comparer les signaux observés à l'oscilloscope avec les signaux prévus et conclure.
===Solution:===
int manchester_as_xor(char * src, char * dst, unsigned int size_src){
//Pour n allant de 0 à size_src-1
//1° : lire le contenu du tableau src à l'indice n
//2° : réaliser le XOR de la valeur lue avec 0 et écrire le résultat dans le tableau dst à l'indice i*2
//3° : réaliser le XOR de la valeur lue avec 1 et écrire le résultat dans le tableau dst à l'indice (i*2)+1
// retourner size_src*2
byte val;
for (int n=0; n
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'manchester'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 4: Codage DSSS de la voie I =====
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Complétez la fonction **dsss_as_table** en utilisant une clé de codage sur 8 bits définie dans le tableau **const char dsss_key2[8] = {0,0,0,0,0,0,0,0};**. La fonction **dsss_as_table** devra, pour chacun des bits qu'elle doit traiter, générer la séquence correspondant à la clef si le bit est à 0 et la séquence complémentaire si le bit est à 1 (utilisation du XOR).
Testez votre programme de la même manière que pour les exercices précédents. Avec la clef fournie, vous devez obtenir le même signal que pour l'encodage BPSK mais avec une durée de bit huit fois plus longue.
Déclarer ensuite une autre clef que vous devez composer vous même dans un autre tableau à 1 dimension en vous assurant qu'elle comporte le même nombre de 0 et de 1. Appelez la fonction **dsss_as_table** en utilisant cette seconde clef et en stockant le résultat de l'encodage dans un nouveau tableau. Modifiez l'appel à la fonction **write_fifo_I_Q** pour observer sur la voie I le signal BPSK ralenti et sur la voie Q le signal encodé avec votre clef. Vérifier le bon fonctionnement de votre programme et retrouver la valeur des bits composant la clef à l'oscilloscope.
===Solution:===
int dsss_as_table(char * src, char * dst, unsigned int size_src){
char tab[8]={0,1,1,0,1,0,0,1};
byte val;
for (int n=0; n
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'DSSS'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 5: Codage QPSK =====
Pour le codage sur deux voies I/Q de la donnée à envoyer, il est nécessaire de découper cette donnée en groupe disjoints de deux bits (duplet de bits). Pour cette fonction, il est conseillé de déclarer un tableau faisant l'association des duplets avec la valeur à envoyer sur les voies I et Q. Cette méthode permettra d'avancer plus rapidement dans les exercices suivants.
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/encode_qpsk.png?400 }}
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Afin de construire ce tableau, complétez le diagramme de constellation fourni avec :
* les positions des combinaisons I/Q sur la constellation
* le tableau associant pour chaque position les tensions (+V/-V) à générer sur les voies I et Q
A partir de ce tableau, construisez deux tableaux associatifs dans votre programme arduino (tableaux **qpsk_i_chan_binary** et **qpsk_q_chan_binary**. Chaque tableau (un pour la voie I et un pour la voie Q) encode la valeur 1 (+V) ou 0 (-V) pour une combinaison IQ.
{{ https://bvdp.inetdoc.net/files/iut/tp_telecomnum/qpsk_encoding_chain.png?600 }}
Testez la fonction **encode_qpsk** comme dans les exercices précédents (attention pour cette exercice il faut penser à mettre le enable de la fifo Q à '1' lors de l'appel de la fonction **write_fifo_I_Q**).
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'QPSK'
gitk &
echo fini
===Solution:===
const char qpsk_i_chan_binary [] = {0, 1, 1, 0};
const char qpsk_q_chan_binary [] = {0, 0, 1, 1} ;
const char qpsk_i_chan_gray [] = {0, 1, 0, 1} ;
const char qpsk_q_chan_gray [] = {0, 0, 1, 1} ;
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_bin(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i +=2){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_binary[two_bits];
dst_q[i/2] = qpsk_q_chan_binary[two_bits];
}
return size_src/2 ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_gray(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src; i +=2 ){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_gray[two_bits];
dst_q[i/2] = qpsk_q_chan_gray[two_bits];
}
return size_src/2 ;
}
--------------------------------------------------------------------------------------------------------
=====Exercice 6: Codage QPSK avec code de Gray=====
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Complétez la fonction **encode_qpsk_gray**. Cette fonction est presque identique à la fonction **encode_qpsk** mais définit une association différente entre les duplets et les symboles I et Q. Complétez le diagramme de constellation fourni puis construisez la table associative. Enfing, codez vos tableaux associatifs dans les variables **qpsk_i_chan_gray** et **qpsk_q_chan_gray**.
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'QPSK gray'
gitk &
echo fini
--------------------------------------------------------------------------------------------------------
=====Exercice 7: 16QAM=====
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}} Sauvez le sketch arduino sous un nouveau nom pour garder une version du code écrit précédemment dans un fichier.
Il vous faut maintenant adapter le programme pour piloter le DAC avec des valeurs autres que 0x000 et 0x3FF. En 16 QAM, les données sont découpées en mots de 4 bits. 2 bits sont envoyés sur le canal I et 2 bits sont envoyés sur le canal Q simultanément. Les voies du DAC doivent donc être pilotées avec des valeurs permettant de représenter 4 niveaux distincts.
viré par bertrand car il n'y a pas de table utilisées mais une fonction linéaire...
DAC.writeDACAB(DAC_SAMPLE_MIN + (i_token*(DAC_SAMPLE_AMP/4)),DAC_SAMPLE_MIN + (q_token*(DAC_SAMPLE_AMP/4)));
Pour cela vous définirez une table de constantes indiquant pour chaque combinaison de 2 bits (entre 0 et 3) la valeur à appliquer en entrée du DAC.
Dans le fichier telecom_lib, dé-commentez la ligne :
//#ENABLE_QAM
Ceci permet de piloter chaque voie du DAC avec quatre valeurs différentes, dépendant de la valeur sur 2 bits stockée dans la fifo.
Le remplissage des fifos sera réalisé par la fonction **int codage16QAM(char src, unsigned int size_src, char * pf_i, char * pf_q)** que vous devez coder. Les données stockées dans chacune des 2 fifos seront des valeurs 0,1,2 ou 3.
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'16QAM'
gitk &
echo fini
=====Exercice 8:Envoi d'un message =====
{{https://bvdp.inetdoc.net/files/iut/tp_gps/TODO.jpg}}
Ajoutez à votre code la déclaration du message suivant.
#define MSG_SIZE 16
char msg[] = {0, 0, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 1, 2, 3} ;
Et modifiez la fonction **loop** pour qu'elle contienne le code suivant.
for(int i = 0 ; i < MSG_SIZE ; i ++){
int data_size = encode_qpsk_bin(msg[i], i_buffer,q_buffer, 2);
write_fifo_I_Q(i_buffer, q_buffer, data_size, 1, 1);
}
Si vous aviez décommenté la ligne dans le fichier telecom_lib, re-commentez la pour obtenir:
//#ENABLE_QAM
{{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois les réponses validées, mettre à jour le suivi de version en saisissant dans une console:
echo commence
cd ~/Arduino/telecom_num_a_trou_2
git commit -a -m'Message 16QAM'
gitk &
echo fini
=====SUITE DU TP EN COURS DE FABRICATION=====
TODO:
FAIRE LES REGLAGES DES POTARS SANS LE DAC sinon risque de connecter sortie du DAC à -9V si les 2 potars sont à 0 tous les 2... sinon, rajouter R cms en série sur RE et RF pour avoir des valeurs minimun
Vérifier si le dac est protégé en sortie
{{https://bvdp.inetdoc.net/files/iut/tp_telecomnum/TPCOMAOP-3parties.png}}
{{https://bvdp.inetdoc.net/files/iut/tp_telecomnum/TPCOMAOP-voieQ.png}}
Schéma simplifié:
{{https://bvdp.inetdoc.net/files/iut/tp_telecomnum/schemasimple.png}}
Schéma complet:
{{https://bvdp.inetdoc.net/files/iut/tp_telecomnum/schema.png}}
fichiers eagle: https://bvdp.inetdoc.net/files/iut/tp_telecomnum/aop-2-compact-V3-4bits2-7-serigraphie.sch et https://bvdp.inetdoc.net/files/iut/tp_telecomnum/aop-2-compact-V3-4bits2-7-serigraphie.brd
message à envoyer stocké dans un tableau + int taille
symbole
débit binaire s'entend avant le codage par manchester par exemple
pour manchester table 01 stockée et faire le xor avec le symbole d'entrée
dss sequence de 8bits pour représenter un bit, on ne pilote que I
//timer interrupts
//by Amanda Ghassaei
//June 2012
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
//timer setup for timer0, timer1, and timer2.
//For arduino uno or any board with ATMEL 328/168.. diecimila, duemilanove, lilypad, nano, mini...
//this code will enable all three arduino timer interrupts.
//timer0 will interrupt at 2kHz
//timer1 will interrupt at 1Hz
//timer2 will interrupt at 8kHz
#define SIZETAB 64
char tableau[SIZETAB]={0x55,0x00,0xaa};
int tailletableau=1;
char tableauCodageManchester[2]={0,1};
int tailletableauCodage=2;
//storage variables
boolean toggle0 = 0;
boolean toggle1 = 0;
boolean toggle2 = 0;
void setup(){
//set pins as outputs
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
/*
cli();//stop interrupts
//set timer2 interrupt at 8kHz
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
//no prescaler jpiat
// TCCR2B |= (1 << CS20);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
// sei();//allow interrupts
*/
digitalWrite(12,HIGH);
digitalWrite(13,HIGH);
}//end setup
int cptbitcodage=0;
int cptbit=0; //compteur pour les bits
int cptoctet=0; //compteur pour les octets du message
byte valeurbit;
byte valeurbitcode;
/*
ISR(TIMER2_COMPA_vect){//timer1 interrupt 8kHz toggles pin 9
//generates pulse wave of frequency 8kHz/2 = 4kHz (takes two cycles for full wave- toggle high then toggle low)
/* if (toggle2){
digitalWrite(13,HIGH);
digitalWrite(12,HIGH);
toggle2 = 0;
}
else{
digitalWrite(13,LOW);
digitalWrite(12,LOW);
toggle2 = 1;
}
*/
/*
//extrait le bit cptbit
if ( cptbitcodage==0)
{
valeurbit=((tableau[cptoctet]>> cptbit ) & 1);
cptbit=(cptbit+1)%8;
if (cptbit==0)
cptoctet=(cptoctet+1)%tailletableau;
}
valeurbitcode= valeurbit ^ tableauCodageManchester[cptbitcodage];
digitalWrite(9,valeurbitcode);
cptbitcodage=(cptbitcodage+1)%tailletableauCodage;
*/
// }
void loop(){
//do other things here
/*
digitalWrite(9,0);
// delayMicroseconds(2);
digitalWrite(9,1);
// delayMicroseconds(2);
*/
int i;
for (i=0;i<16;i++)
{
digitalWrite(10,(i>>3)&1);
digitalWrite(11,(i>>2)&1);
digitalWrite(12,(i>>1)&1);
digitalWrite(13,(i>>0)&1);
delay(1000);
}
}
Projet HF
Descriptif :
Mettre en place une chaîne de transmission radio à base de composants logiciels et matériels.
Objectifs :
Etapes :
Modulation BPSK
- Codage NRZ
- Codage Manchester
- DSSS
Modulation 4-QPSK
- Codage NRZ
- NRZ
Modulation 16-QPSK
- Codage binaire naturel
- Codage gray
- Constellation cercle
- Constellation carré
- OQPSK : Offset QPSK, le changement sur le canal I et Q sont décalés pour éviter le passage par zéro.
Objectifs partie logiciel :
- Définir la source de données (tableau, source dynamique, calculée, liaison série)
- Configuration débit via Timer
- Génération code Manchester à partir du XOR avec une clé de taille donnée
- Génération voie I/Q en offset pour le OQPSK
Objectifs partie RF :
- ??
Présentation : 30min (présentation au TD 5 sur Simulink)
Heures RF : 8h + Optionnel
Heures Soft : 8h + Optionnel
Heures projet : ...
Pour les séances RF? les maquettes doivent être pré-programmés avec le code nécessaire. Utilisation de miniterm.py pour la communication avec la carte.
Etape 1, RF (2h) :
Maquette Arduino émet message NRZ à fréquence donnée (sur I uniquement), et choix du message (pseudo aléatoire ou compteur binaire). Construction de la partie RF modulation/démodulation BPSK. La maquette doit être capable de générer plusieurs combinaisons de I/Q de manière statique. Le choix du message à envoyer est fait par la console série. (débit binaire donné 100/200 bit/s, si possible supérieur pour la visualisation).
Etape 1, Soft (3h) :
Construction du transmetteur NRZ BPSK, ajout du codage Manchester et du DSSS. Construction de l'entrée FI du mélangeur. (configuration du timer et soft pour maintenir débit binaire constant). Pour Manchester et DSSS codage par XOR et codage par indexation. Codage par constantes/define de la taille des tables et donc de la longueur de la clé DSSS. Le codage Manchester s'applique après l'extraction I/Q ainsi que la DSSS. Représenter le débit binaire à caque étage pour avoir une représentation facile de la gestion du temps dans l'Arduino (fréquence d'IT).
Etape 2, RF (3h) :
Etude de l'impact du Manchester et du DSSS sur le spectre. Construction du modulateur 4-QPSK (voie I et Q, mélangeur). Choix du message entre pseudo aléatoire , compteur (0, 1, 2, 3) et activation au choix des valeurs.
Etape 2, Soft (2h):
Mise en place la chaîne 4-QPSK et codage binaire et Gray. A partir de cette étape, le débit binaire passe au débit de DSSS utilisé dans la séance 1.
Etape 3, RF (2h) :
Mise en place du démodulateur QPSK. Etude de l'impact Gray vs Binaire (Optionnel).
Etape 3, Soft (2h, Optionnel) :
Mise en place de la chaîne 16-PSK carré et circulaire. Ajout du OQPSK (optionnel)
Etape 4, RF (2h, Optionnel):
Etude de la constellation 16-PSK carré vs circulaire. Etude de l'impact de la OQPSK
Mode projet :
1) Mise en place de l'étalement de spectre (générique, clé de taille fixée par l'utilisateur, 4, 8, 16) sur la QPSK.
2) Construction du démodulateur BPSK sans front-end RF et sans reconstruction de la synchro.
3) Construction du démodulateur QPSK sans front-end RF et sans reconstruction de la synchro.
4) Construction du démodulateur BPSK avec reconstruction de la synchronisation
/*
tlv5637 DAC demo
Uses the tlv5637 library. For details on the sensor, see:
www.ti.com/cn/lit/gpn/tlv5637
Circuit:
CSB: pin 7
MOSI: pin 11
SCK: pin 13
REF: Apply 2.5 V to get output value from 0 to 5V and use REF_EXTERNAL_TLV5637
or Let open to get output value from 0 to 2V and use REF_1024MV_TLV5637
or Let open to get output value from 0 to 4V and use REF_2048MV_TLV5637
LED: pin 8 , flashes at the frequency of the generated signal
OUTA: plug to a scope to observe a sinus waveform
OUTB: plug to a scope to observe a triangle waveform
created 14 July 20143
by Bertrand VANDEPORTAELE
*/
#include
#include
const int chipSelectPin = 3; //connect to the Chip Select of the TLV5637
//#define SIZETAB 300
#define SIZETAB 64
//corresponds to a period of approximately 625ms
const int boutonPin = 9;
const int ledPin = 8;
int sinus[SIZETAB];
int cosinus[SIZETAB];
int cpt;
//TLV5637 DAC(chipSelectPin,REF_EXTERNAL_TLV5637);
TLV5637 DAC(chipSelectPin,REF_2048MV_TLV5637);
const int XPin = A0; // select the input pin for the potentiometer
int XValue = 0; // variable to store the value coming from the sensor
const int YPin = A1; // select the input pin for the potentiometer
int YValue = 0; // variable to store the value coming from the sensor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// start serial port at 9600 bps:
Serial.begin(9600);
//delay(3O00);
Serial.print("Bonjour");
pinMode(ledPin, OUTPUT);
pinMode(boutonPin, INPUT);
// start the tlv5637 library:
DAC.powerOn();
DAC.speedFast();
//generate arrays of signal values
for(int j = 0; j < SIZETAB; j++)
{
sinus[j]=512+511*sin(j*2*3.14/SIZETAB);
cosinus[j]=512+511*cos(j*2*3.14/SIZETAB);
// Serial.print(j); Serial.print(':'); Serial.print(sinus[j]); Serial.println(' ');
}
/*
for(int j = 0; j < SIZETAB; j++)
{
sinus[j]=((j&3) * 1023) / 3;
cosinus[j]=(((j>>2)&3) * 1023) / 3;
// Serial.print(j); Serial.print(':'); Serial.print(sinus[j]); Serial.println(' ');
}
*/
cpt=0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
digitalWrite(ledPin, digitalRead(boutonPin));
Serial.write('0'+digitalRead(boutonPin));
/* digitalWrite(ledPin, 0);
delay(500);
digitalWrite(ledPin, 1);
delay(500);
*/
// read the value from the sensor:
XValue = analogRead(XPin); //0 à 1023 -> 10bits
YValue = analogRead(YPin);
if (digitalRead(boutonPin)==1)
{
DAC.writeDACAB(sinus[cpt],cosinus[cpt]);
cpt=(cpt+1)%SIZETAB;
}
else
DAC.writeDACAB(XValue,YValue);
}
/*
tlv5637 DAC demo
Uses the tlv5637 library. For details on the sensor, see:
www.ti.com/cn/lit/gpn/tlv5637
Circuit:
CSB: pin 7
MOSI: pin 11
SCK: pin 13
REF: Apply 2.5 V to get output value from 0 to 5V and use REF_EXTERNAL_TLV5637
or Let open to get output value from 0 to 2V and use REF_1024MV_TLV5637
or Let open to get output value from 0 to 4V and use REF_2048MV_TLV5637
LED: pin 8 , flashes at the frequency of the generated signal
OUTA: plug to a scope to observe a sinus waveform
OUTB: plug to a scope to observe a triangle waveform
created 14 July 20143
by Bertrand VANDEPORTAELE
*/
#include
#include
const int chipSelectPin = 3; //connect to the Chip Select of the TLV5637
TLV5637 DAC(chipSelectPin,REF_2048MV_TLV5637);
#define DAC_MAX 0x3FF
#define REVERT 1
const int ledPin = 8;
// Encoding tables
const char qpsk_i_chan_binary [] = {0, 1, 0, 1} ;
const char qpsk_q_chan_binary [] = {0, 0, 1, 1} ;
const char qpsk_i_chan_gray [] = {0, 1, 1, 0} ;
const char qpsk_q_chan_gray [] = {0, 0, 1, 1} ;
const char manchester_table [2][2] = {{1, 0}, {0, 1}};
const int dsss_key_size = 8 ;
const char dsss_key[8] = {0, 0, 1, 0, 1, 1, 0, 1};
//Encoding functions
// input data is encoded as a single 8-bit value with size being 1 : 8
int encode_bpsk(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i ++){
char one_bit = (src >> i) & 0x01;
dst_i[i] = one_bit;
dst_q[i] = 0 ;
}
return size_src ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_bin(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i +=2){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_binary[two_bits];
dst_q[i/2] = qpsk_q_chan_binary[two_bits];
}
return size_src/2 ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_gray(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src; i +=2 ){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_gray[two_bits];
dst_q[i/2] = qpsk_q_chan_gray[two_bits];
}
return size_src/2 ;
}
int manchester_as_xor(char * src, char * dst, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src*2; i ++){
dst[i] = src[i/2] ^ state ;
state = state ^ 0x01 ;
}
return size_src*2 ;
}
int manchester_as_table(char * src, char * dst, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src; i ++){
dst[i*2] = manchester_table[src[i]][0];
dst[(i*2)+1] = manchester_table[src[i]][1];
}
return size_src*2 ;
}
int dsss_as_table(char * src, char * dst, const char * key, unsigned int key_size, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src*key_size; i ++){
dst[i] = src[i/key_size] ^ key[i%key_size] ;
}
return size_src*key_size ;
}
//END OF ENCODING FUNCTIONS
// FIFO management
#define ACC_LENGTH 32
struct myFifo{
char data [ACC_LENGTH];
unsigned int fifo_size ;
unsigned int write_index ;
unsigned int read_index ;
unsigned int nb_available ;
};
struct myFifo global_fifo_for_i ;
struct myFifo global_fifo_for_q ;
void init_fifo(struct myFifo * pf){
pf -> fifo_size = ACC_LENGTH ;
pf -> write_index = 0 ;
pf -> read_index = 0 ;
pf -> nb_available = 0 ;
}
inline char read_fifo(struct myFifo * pf, char * token){
if(pf->nb_available == 0) return -1 ;
*token = pf->data[pf->read_index] ;
pf->read_index = pf->read_index + 1;
if(pf->read_index >= pf->fifo_size) pf->read_index = 0 ;
pf->nb_available = pf->nb_available -1 ;
return 0 ;
}
inline char peek_fifo(struct myFifo * pf, char * token){
if(pf->nb_available == 0) return -1 ;
*token = pf->data[pf->read_index] ;
return 0 ;
}
inline char write_fifo(struct myFifo * pf, char token){
if(pf->nb_available >= pf->fifo_size) return -1 ;
pf->data[pf->write_index] = token ;
pf->write_index = pf->write_index + 1;
if(pf->write_index >= pf->fifo_size) pf->write_index = 0 ;
pf->nb_available = pf->nb_available + 1 ;
return 0 ;
}
//END OF FIFO MANAGEMENT
// TIMER ISR VECTOR and configuration
unsigned char isr_modulo_count, global_isr_modulo = 0 ;
ISR(TIMER2_COMPA_vect){
//transmit interrupt
char i_token, q_token ;
if(isr_modulo_count == 0){
isr_modulo_count = global_isr_modulo ;
if(global_fifo_for_i.nb_available > 0 && global_fifo_for_q.nb_available > 0){
read_fifo(&global_fifo_for_i, &i_token);
read_fifo(&global_fifo_for_q, &q_token);
#ifdef REVERT
i_token = i_token > 0 ? 0 : 1 ;
q_token = q_token > 0 ? 0 : 1 ;
#endif
DAC.writeDACAB(i_token*DAC_MAX,q_token*DAC_MAX);
PORTB &= ~0x01 ;
}
PORTB |= 0x01 ;
}else{
isr_modulo_count = isr_modulo_count - 1 ;
}
}
void setupTimer2(unsigned char prescaler, unsigned int period){
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = period;// = (16*10^6) / (8000*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
switch(prescaler){
case 0 :
// Set CS20 bit for no prescaler
TCCR2B |= (1 << CS20);
case 1 :
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
case 2 :
// Set CS21 bit for 64 prescaler
TCCR2B |= (1 << CS21) | (1 << CS20);
break ;
case 3 :
// Set CS21 bit for 128 prescaler
TCCR2B |= (1 << CS22) ;
break ;
case 4 :
// Set CS21 bit for 256 prescaler
TCCR2B |= (1 << CS22) | (1 << CS21);
break ;
case 5 :
// Set CS21 bit for 1024 prescaler
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
break ;
default :
TCCR2B |= (1 << CS20);
break ;
}
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
}
// END OF TIMER SECTION
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// start serial port at 9600 bps:
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// start the tlv5637 library:
DAC.powerOn();
DAC.speedFast();
randomSeed(analogRead(0)); // initialize random function seed
init_fifo(&global_fifo_for_i); // transmist fifo initialization
init_fifo(&global_fifo_for_q); // transmist fifo initialization
cli();//stop interrupts
setupTimer2(3, 127); // setup interrupt with prescaler (see datasheet for prescaler value, and reload value)
sei();//allow interrupts
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int menu_mode(char * encode_mode, char * data_mode){
char c ;
Serial.println("Salut DD ! :");
Serial.println("Entrez le numero du mode choisi");
Serial.println("0 : BPSK sur canal I");
Serial.println("1 : BPSK sur canal I + Manchester");
Serial.println("2 : BPSK sur canal I + DSSS");
Serial.println("3 : QPSK en mode binaire");
Serial.println("4 : QPSK en mode gray");
while(Serial.available() < 1);
c = Serial.read();
switch(c){
case '0':
*encode_mode = 0;
break;
case '1':
*encode_mode = 1;
break;
case '2':
*encode_mode = 2;
break;
case '3':
*encode_mode = 3;
break;
case '4':
*encode_mode = 4;
break;
default :
Serial.println("Mauvais choix");
return -1 ;
}
Serial.println("Entrez le type de donnees à transmettre");
Serial.println("0 : Compteur binaire");
Serial.println("1 : Bruit blanc");
Serial.println("2 : 0 en continu, OO en QPSK");
Serial.println("3 : 1 en continu, O1 en QPSK");
Serial.println("4 : 10 en continu");
Serial.println("5 : 11 en continu");
while(Serial.available() < 1);
c = Serial.read();
switch(c){
case '0':
*data_mode = 0;
break;
case '1':
*data_mode = 1;
break;
case '2':
*data_mode = 2;
break;
case '3':
*data_mode = 3;
break;
case '4':
*data_mode = 4;
break;
case '5':
*data_mode = 5;
break;
default :
Serial.println("Mauvais choix");
return -1 ;
}
return 0 ;
}
// Global variables to manage encoding mode and 8 bit data
char tx_mode = -1, tx_data = -1;
unsigned char data = 0 ;
int data_size = 0 ;
// Global variables for I and Q buffers, and Manchester/DSSS buffer
char i_buffer [8] ;
char q_buffer [8] ;
char manchester_buffer [32] ;
void loop() {
if(tx_mode < 0 || tx_data < 0){
menu_mode(&tx_mode, &tx_data);
Serial.print("(");
Serial.print(tx_mode, DEC);
Serial.print(", ");
Serial.print(tx_data, DEC);
Serial.println(")");
Serial.println("Envoyer R pour afficher le menu");
}
if(Serial.available() > 0){
if(Serial.read() == 'R'){
tx_mode = -1 ;
tx_data = -1 ;
}
}
data_size = 2 ;
switch(tx_mode){
case 0 : // counter is modulo 4, so need to send two bits at a time for BPSK
global_isr_modulo = dsss_key_size - 1 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
for(int i = 0 ; i < data_size ; i ++){
while(write_fifo(&global_fifo_for_i, i_buffer[i]) == -1);
while(write_fifo(&global_fifo_for_q, 0) == -1);
}
break ;
case 1 :
global_isr_modulo = (dsss_key_size/2) - 1 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
data_size = manchester_as_xor(i_buffer, manchester_buffer, data_size);
for(int i = 0 ; i < data_size ; i ++){
while(write_fifo(&global_fifo_for_i, manchester_buffer[i]) == -1);
while(write_fifo(&global_fifo_for_q, 0) == -1);
}
break ;
case 2 :
global_isr_modulo = 0 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
data_size = dsss_as_table(i_buffer, manchester_buffer, dsss_key, dsss_key_size, data_size);
for(int i = 0 ; i < data_size ; i ++){
while(write_fifo(&global_fifo_for_i, manchester_buffer[i]) == -1);
while(write_fifo(&global_fifo_for_q, 0) == -1);
}
break ;
case 3 :
global_isr_modulo = (dsss_key_size/2) - 1;
data_size = encode_qpsk_bin(data, i_buffer, q_buffer, data_size);
for(int i = 0 ; i < data_size ; i ++){
while(write_fifo(&global_fifo_for_i, i_buffer[i]) == -1);
while(write_fifo(&global_fifo_for_q, q_buffer[i]) == -1);
}
break ;
case 4 :
global_isr_modulo = (dsss_key_size/2) - 1 ;
data_size = encode_qpsk_gray(data, i_buffer, q_buffer, data_size);
for(int i = 0 ; i < data_size ; i ++){
while(write_fifo(&global_fifo_for_i, i_buffer[i]) == -1);
while(write_fifo(&global_fifo_for_q, q_buffer[i]) == -1);
}
break ;
default :
data = 0 ;
tx_mode = -1 ;
break ;
}
switch(tx_data){
case 0 :
data += 1 ;
break ;
case 1 :
data = random(4); //may require more work
break ;
case 2 :
data = 0 ;
break ;
case 3 :
data = 1 ;
break ;
case 4 :
data = 2 ;
break ;
case 5 :
data = 3 ;
break ;
default :
data = 0 ;
tx_data = -1 ;
break ;
}
data_size = 2 ;
}
=====Solutions programme démo pour partie analogique=====
#include
#include
#include
const int chipSelectPin = 3; //connect to the Chip Select of the TLV5637
TLV5637 DAC(chipSelectPin,REF_2048MV_TLV5637);
#define DAC_MAX 0x3FF
#define DAC_SAMPLE_CRETE (0x169)
#define DAC_SAMPLE_CENTER ((DAC_MAX + 1) >> 1)
#define DAC_SAMPLE_MAX (DAC_SAMPLE_CENTER + DAC_SAMPLE_CRETE) //
#define DAC_SAMPLE_MIN (DAC_SAMPLE_CENTER - DAC_SAMPLE_CRETE)
#define DAC_SAMPLE_AMP (DAC_SAMPLE_CRETE * 2)
#define REVERT // la chaine de conditionnement réalise l'inversion ...
//#define ENABLE_QAM // la QAM implique 4 niveaux sur les canaux I et Q
// FIFO management
#define ACC_LENGTH 32
struct myFifo{
char data [ACC_LENGTH];
unsigned int fifo_size ;
unsigned int write_index ;
unsigned int read_index ;
unsigned int nb_available ;
};
struct myFifo global_fifo_for_i ;
struct myFifo global_fifo_for_q ;
void init_fifo(struct myFifo * pf){
pf -> fifo_size = ACC_LENGTH ;
pf -> write_index = 0 ;
pf -> read_index = 0 ;
pf -> nb_available = 0 ;
}
inline char read_fifo(struct myFifo * pf, char * token){
unsigned int avail = get_nb_available(pf);
if(avail == 0) return -1 ;
*token = pf->data[pf->read_index] ;
pf->read_index = pf->read_index + 1;
if(pf->read_index >= pf->fifo_size) pf->read_index = 0 ;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
pf->nb_available = pf->nb_available -1 ;
}
return 0 ;
}
inline char peek_fifo(struct myFifo * pf, char * token){
if(pf->nb_available == 0) return -1 ;
*token = pf->data[pf->read_index] ;
return 0 ;
}
inline char write_fifo(struct myFifo * pf, char token){
unsigned int avail = get_nb_available(pf);
if(avail >= pf->fifo_size) return -1 ;
pf->data[pf->write_index] = token ;
pf->write_index = pf->write_index + 1;
if(pf->write_index >= pf->fifo_size) pf->write_index = 0 ;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
pf->nb_available = pf->nb_available + 1 ;
}
return 0 ;
}
inline unsigned int get_nb_available(struct myFifo * pf){
unsigned int av ;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
av = pf->nb_available ;
}
return av ;
}
//END OF FIFO MANAGEMENT
char calibration_mode = 0 ;
// TIMER ISR VECTOR and configuration
unsigned char isr_modulo_count, global_isr_modulo = 0 ;
ISR(TIMER2_COMPA_vect){
//transmit interrupt
char i_token, q_token ;
if(calibration_mode){
DAC.writeDACAB(0, DAC_SAMPLE_CENTER); // output max ADC range on I and 0 on Q
return ;
}
if(isr_modulo_count == 0){
isr_modulo_count = global_isr_modulo ;
if(global_fifo_for_i.nb_available > 0 && global_fifo_for_q.nb_available > 0){
read_fifo(&global_fifo_for_i, &i_token);
read_fifo(&global_fifo_for_q, &q_token);
//Handle the case of non-binary values
#ifdef ENABLE_QAM
#ifdef REVERT
i_token = (~i_token) & 0x03 ;
q_token = (~q_token) & 0x03 ;
#endif
DAC.writeDACAB(DAC_SAMPLE_MIN + (i_token*(DAC_SAMPLE_AMP/4)),DAC_SAMPLE_MIN + (q_token*(DAC_SAMPLE_AMP/4)));
#else
#ifdef REVERT
i_token = i_token > 0 ? 0 : 1 ;
q_token = q_token > 0 ? 0 : 1 ;
#endif
DAC.writeDACAB(DAC_SAMPLE_MIN + (i_token*DAC_SAMPLE_AMP),DAC_SAMPLE_MIN + (q_token*DAC_SAMPLE_AMP));
#endif
PORTB &= ~0x01 ;
}
PORTB |= 0x01 ;
}else{
isr_modulo_count = isr_modulo_count - 1 ;
}
}
void setupTimer2(unsigned char prescaler, unsigned int period){
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = period;// = (16*10^6) / (8000*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
switch(prescaler){
case 0 :
TCCR2B |= (1 << CS20);
case 1 :
TCCR2B |= (1 << CS21);
case 2 :
TCCR2B |= (1 << CS21) | (1 << CS20);
break ;
case 3 :
TCCR2B |= (1 << CS22) ;
break ;
case 4 :
TCCR2B |= (1 << CS22) | (1 << CS20);
break ;
case 5 :
TCCR2B |= (1 << CS22) | (1 << CS21);
break ;
case 6 :
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
break ;
default :
TCCR2B |= (1 << CS20);
break ;
}
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
}
void setupDAC(){
// start the tlv5637 library:
DAC.powerOn();
DAC.speedFast();
}
extern char calibration_mode ;
// TIMER ISR VECTOR and configuration
extern unsigned char isr_modulo_count, global_isr_modulo ;
extern struct myFifo global_fifo_for_i ;
extern struct myFifo global_fifo_for_q ;
const int ledPin = 8;
// Encoding tables
/*const char qpsk_i_chan_binary [] = {0, 1, 0, 1} ; // A dessiner
const char qpsk_q_chan_binary [] = {0, 0, 1, 1} ;
const char qpsk_i_chan_gray [] = {0, 1, 1, 0} ;
const char qpsk_q_chan_gray [] = {0, 0, 1, 1} ;*/
const char qpsk_i_chan_binary [] = {0, 1, 1, 0};
const char qpsk_q_chan_binary [] = {0, 0, 1, 1} ;
const char qpsk_i_chan_gray [] = {0, 1, 0, 1} ;
const char qpsk_q_chan_gray [] = {0, 0, 1, 1} ;
const char manchester_table [2][2] = {{1, 0}, {0, 1}};
const int dsss_key_size = 8 ;
const char dsss_key[8] = {0, 0, 1, 0, 1, 1, 0, 1};
//Encoding functions
// input data is encoded as a single 8-bit value with size being 1 : 8
int encode_bpsk(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i ++){
char one_bit = (src >> i) & 0x01;
dst_i[i] = one_bit ;
dst_q[i] = 0 ;
}
return size_src ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_bin(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i +=2){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_binary[two_bits];
dst_q[i/2] = qpsk_q_chan_binary[two_bits];
}
return size_src/2 ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_gray(char src, char * dst_i, char * dst_q, unsigned char size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src; i +=2 ){
char two_bits = (src >> i) & 0x03;
dst_i[i/2] = qpsk_i_chan_gray[two_bits];
dst_q[i/2] = qpsk_q_chan_gray[two_bits];
}
return size_src/2 ;
}
int manchester_as_xor(char * src, char * dst, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src*2; i ++){
dst[i] = src[i/2] ^ state ;
state = state ^ 0x01 ;
}
return size_src*2 ;
}
int manchester_as_table(char * src, char * dst, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src; i ++){
dst[i*2] = manchester_table[src[i]][0];
dst[(i*2)+1] = manchester_table[src[i]][1];
}
return size_src*2 ;
}
int dsss_as_table(char * src, char * dst, const char * key, unsigned int key_size, unsigned int size_src){
int i;
char state = 0 ;
for(i = 0; i < size_src*key_size; i ++){
dst[i] = src[i/key_size] ^ key[i%key_size] ;
}
return size_src*key_size ;
}
//END OF ENCODING FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// start serial port at 9600 bps:
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
setupDAC();
randomSeed(analogRead(0)); // initialize random function seed
init_fifo(&global_fifo_for_i); // transmist fifo initialization
init_fifo(&global_fifo_for_q); // transmist fifo initialization
cli();//stop interrupts
setupTimer2(3, 124); // setup interrupt with prescaler (see datasheet for prescaler value, and reload value)
sei();//allow interrupts
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int menu_mode(char * encode_mode, char * data_mode){
char c ;
Serial.println("Bonjour maitre ");
Serial.println("Entrez le numero du mode choisi");
Serial.println("0 : BPSK sur canal I");
Serial.println("1 : BPSK sur canal I + Manchester");
Serial.println("2 : BPSK sur canal I + DSSS");
Serial.println("3 : QPSK en mode binaire");
Serial.println("4 : QPSK en mode gray");
Serial.println("5 : Message etalonnage");
while(Serial.available() < 1);
c = Serial.read();
switch(c){
case '0':
*encode_mode = 0;
break;
case '1':
*encode_mode = 1;
break;
case '2':
*encode_mode = 2;
break;
case '3':
*encode_mode = 3;
break;
case '4':
*encode_mode = 4;
break;
case '5':
*encode_mode = 5;
break;
default :
Serial.println("Mauvais choix");
return -1 ;
}
if((*encode_mode) == 5){
*data_mode = 0 ;
return 0 ;
}
Serial.println("Entrez le type de donnees à transmettre");
Serial.println("0 : Compteur binaire");
Serial.println("1 : Pseudo-aleatoire");
Serial.println("2 : 00 en continu");
Serial.println("3 : 01 en continu");
Serial.println("4 : 10 en continu");
Serial.println("5 : 11 en continu");
Serial.println("6 : Message 00 00 00 00 - 00 01 11 10 - 00 00 00 00 - 00 01 10 11");
while(Serial.available() < 1);
c = Serial.read();
switch(c){
case '0':
*data_mode = 0;
break;
case '1':
*data_mode = 1;
break;
case '2':
*data_mode = 2;
break;
case '3':
*data_mode = 3;
break;
case '4':
*data_mode = 4;
break;
case '5':
*data_mode = 5;
break;
case '6':
*data_mode = 6;
break;
default :
Serial.println("Mauvais choix");
return -1 ;
}
return 0 ;
}
// Global variables to manage encoding mode and 8 bit data
char tx_mode = -1, tx_data = -1;
unsigned char data = 0 ;
int data_size = 0 ;
int msg_index = 0 ;
#define MSG_SIZE 16
char msg[] = {0, 0, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 1, 2, 3} ;
// Global variables for I and Q buffers, and Manchester/DSSS buffer
char i_buffer [8] ;
char q_buffer [8] ;
char manchester_buffer [32] ;
inline void write_fifo_I_Q(const char * data_I, const char * data_Q, int data_size, int enable_i, int enable_q){
int i;
for(int i = 0 ; i < data_size ; i ++){
if(enable_i){
while(write_fifo(&global_fifo_for_i, data_I[i]) == -1);
}else{
while(write_fifo(&global_fifo_for_i, 0) == -1);
}
if(enable_q){
while(write_fifo(&global_fifo_for_q, data_Q[i]) == -1);
}else{
while(write_fifo(&global_fifo_for_q, 0) == -1);
}
}
}
void loop() {
if(tx_mode < 0 || tx_data < 0){
menu_mode(&tx_mode, &tx_data);
Serial.print("(");
Serial.print(tx_mode, DEC);
Serial.print(", ");
Serial.print(tx_data, DEC);
Serial.println(")");
Serial.println("Envoyer R pour afficher le menu");
}
if(Serial.available() > 0){
if(Serial.read() == 'R'){
tx_mode = -1 ;
tx_data = -1 ;
}
}
if(tx_mode == 5){
//Pas besoin d'interruption ...
calibration_mode = 1 ;
return ;
}else{
calibration_mode = 0 ;
}
data_size = 2 ;
switch(tx_mode){
case 0 : // counter is modulo 4, so need to send two bits at a time for BPSK
global_isr_modulo = dsss_key_size - 1 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
write_fifo_I_Q(i_buffer, q_buffer, data_size, 1, 0);
break ;
case 1 :
global_isr_modulo = (dsss_key_size/2) - 1 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
data_size = manchester_as_xor(i_buffer, manchester_buffer, data_size);
write_fifo_I_Q(manchester_buffer, q_buffer, data_size, 1, 0);
break ;
case 2 :
global_isr_modulo = 0 ;
data_size = encode_bpsk(data, i_buffer, q_buffer, data_size);
data_size = dsss_as_table(i_buffer, manchester_buffer, dsss_key, dsss_key_size, data_size);
write_fifo_I_Q(manchester_buffer, q_buffer, data_size, 1, 0);
break ;
case 3 :
global_isr_modulo = (dsss_key_size*2) - 1;
data_size = encode_qpsk_bin(data, i_buffer, q_buffer, data_size);
write_fifo_I_Q(i_buffer, q_buffer, data_size, 1, 1);
break ;
case 4 :
global_isr_modulo = (dsss_key_size*2) - 1 ;
data_size = encode_qpsk_gray(data, i_buffer, q_buffer, data_size);
write_fifo_I_Q(i_buffer, q_buffer, data_size, 1, 1);
break ;
default :
data = 0 ;
tx_mode = -1 ;
break ;
}
switch(tx_data){
case 0 :
data += 1 ;
break ;
case 1 :
data = random(4); //may require more work
break ;
case 2 :
data = 0 ;
break ;
case 3 :
data = 1 ;
break ;
case 4 :
data = 2 ;
break ;
case 5 :
data = 3 ;
break ;
case 6 :
data = msg[msg_index] ;
msg_index ++ ;
if(msg_index > MSG_SIZE){
msg_index = 0 ;
}
break ;
default :
data = 0 ;
tx_data = -1 ;
break ;
}
data_size = 2 ;
}
#define PRESCALER_INDEX 2
#define TIMER_PERIOD 249
#define XOR ^
/*#define MSG_SIZE 16
char msg[] = {0, 0, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 1, 2, 3} ;
*/
#define MSG_SIZE 32
char msg[] = {0,0,0,0,0,0,0,0,0, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0};
extern struct myFifo global_fifo_for_i ;
extern struct myFifo global_fifo_for_q ;
// Encoding tables
const char qpsk_i_chan_binary [4] = {0, 1, 1, 0}; //a définir pour le codage QPSK
const char qpsk_q_chan_binary [4] = {0, 0, 1, 1};
const char qpsk_i_chan_gray [4] = {0, 0, 1, 1}; //a définir pour le codage QPSK en mode GRAY
const char qpsk_q_chan_gray [4] = {0, 1, 0, 1};
const int dsss_key_size = 8 ;
const char dsss_key[8] = {0, 0, 0, 0, 0, 0, 0, 0};//table à définir pour le codage DSSS
const char qam_i_chan_binary[16]= {0, 0, 0, 0, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2};
const char qam_q_chan_binary[16]= {0, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 2};
//Encoding functions
// input data is encoded as a single 8-bit value with size being 1 : 8
int encode_bpsk(char src, char * ptr_i, unsigned char size_src){
for ( int j=0; j>j)&0x1;
ptr_i++;
}
return size_src;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_bin(char src, char * ptr_i, char * ptr_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i +=2){
char two_bits = (src >> i) & 0x03;
ptr_i[i/2] = qpsk_i_chan_binary[two_bits];
ptr_q[i/2] = qpsk_q_chan_binary[two_bits];
}
return size_src/2 ;
}
// input data is encoded as a single 8-bit value with size being 2, 4, 6, 8
// outputs are encoded as array of char which values are binary
int encode_qpsk_gray(char src, char * ptr_i, char * ptr_q, unsigned char size_src){
int i;
for(i = 0; i < size_src; i +=2){
char two_bits = (src >> i) & 0x03;
ptr_i[i/2] = qpsk_i_chan_gray[two_bits];
ptr_q[i/2] = qpsk_q_chan_gray[two_bits];
}
return size_src/2 ;
}
int manchester_as_xor(char * src, char * dst, unsigned int size_src){
byte val;
for (int n=0; n> i) & 0x00F;
pf_i[i/4] = qam_i_chan_binary[four_bits];
pf_q[i/4] = qam_q_chan_binary[four_bits];
}
return size_src/4 ;
}
//END OF ENCODING FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// start serial port at 9600 bps:
Serial.begin(115200);
pinMode(8, OUTPUT);
setupDAC();
init_fifo(&global_fifo_for_i); // transmist fifo initialization
init_fifo(&global_fifo_for_q); // transmist fifo initialization
cli();//stop interrupts
setupTimer2(PRESCALER_INDEX, TIMER_PERIOD); // setup interrupt with prescaler (see datasheet for prescaler value, and reload value)
sei();//allow interrupts
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char data = 0 ;
int data_size = 0 ;
// Global variables for I and Q buffers, and Manchester/DSSS buffer
char i_buffer [8]={0,1,0,1,0,1,0,1} ;
char q_buffer [8] ={0,1,0,1,0,1,0,1};
char manchester_buffer [32] ;
char dsss_buffer[32];
byte nb_symboles;
//fonction d'écriture bloquante et synchronisée dans les FIFO des cancaux I et Q
inline void write_fifo_I_Q(const char * data_I, const char * data_Q, int data_size, int enable_i, int enable_q){
int i;
for(int i = 0 ; i < data_size ; i ++){
if(enable_i){
while(write_fifo(&global_fifo_for_i, data_I[i]) == -1);
}else{
while(write_fifo(&global_fifo_for_i, 0) == -1);
}
if(enable_q){
while(write_fifo(&global_fifo_for_q, data_Q[i]) == -1);
}else{
while(write_fifo(&global_fifo_for_q, 0) == -1);
}
}
}
void loop() {
//data_size = 4 ;
// // Exemple d'appel pour le codage d'un signal en BPSK
// data_size = encode_bpsk(data, i_buffer, data_size);
// nb_symboles = codage16QAM(data, data_size, i_buffer, q_buffer);
//
// write_fifo_I_Q(i_buffer, q_buffer, nb_symboles, 1, 1);
// data++;
//nb_symboles= dsss_as_table(i_buffer, dsss_buffer, nb_symboles);
//write_fifo_I_Q(dsss_buffer,data_size, nb_symboles,1, 0);
for(int i = 0 ; i < MSG_SIZE ; i ++){
// int data_size = encode_qpsk_bin(msg[i], i_buffer,q_buffer, 2);
int data_size = codage16QAM(msg[i], 4, i_buffer, q_buffer);
write_fifo_I_Q(i_buffer, q_buffer, data_size, 1, 1);
}
}
---------------------------------------------