Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.
Application
Trames météo, codage BCD et contrôle de parité
Des modules DEMETER transmettent température et humidité par radio au serveur de PlovaNuviam. Chaque trame contient 40 bits : identifiant sur 8 bits, clé de sécurité sur 8 bits, température sur 12 bits, humidité sur 8 bits, puis quatre bits de parité. Le serveur doit décoder, contrôler et filtrer les données avant le graphique. La classe est construite dès la lecture, donc la robustesse doit commencer avant toute conversion numérique.
| Champ | Tranche Python | Exemple | Interprétation |
|---|---|---|---|
| ID | [0:8] | 00101010 | 42 |
| Clé | [8:16] | 11001000 | Bloc contrôlé par parité |
| Température | [16:28] | 010010001100 | 1164 puis(1164−900)/10=26,4°C |
| Humidité | [28:36] | 01100010 | BCD :6 et 2, donc 62% |
| Contrôle | [36:40] | 1101 | Parités des quatre blocs précédents |
Un bit de contrôle vaut 0 pour un nombre pair de 1 et 1 pour un nombre impair. Une température sous−10°C est une erreur de mesure selon le sujet. L’humidité utilise deux chiffres BCD, sauf 100%, codé 10100000. int(chaine,2) convertit une écriture binaire en entier. Le fichier analyse.py conserve uniquement les transmissions dont est_valide renvoieTrue.
Le dossier comporte le sujet et les fichiers listés dans les téléchargements. Les codes de départ sont reproduits ci-dessous. Travaillez sur une copie. Les appels professeur signalés dans les questions servent à présenter votre démarche ; vous pouvez aussi demander de l’aide en cas de difficulté.
Code fourni : analyse.py
import matplotlib.pyplot as plt
from transmission import Transmission
# Extraction des données
with open("data.txt", "r") as f:
trames = f.read().split("\n")
trames.pop() # La dernière ligne est vide, on la supprime
# Création d'une liste de températures pour les transmissions valides
transmissions = [Transmission(t) for t in trames]
temperatures = [t.get_temperature() for t in transmissions if t.est_valide()]
print(f"Nombre de trames reçues : {len(trames)}")
print(f"Nombre de trames valides : {len(temperatures)}")
# Affichage des températures
plt.figure(figsize=(10, 5))
plt.plot(temperatures, label="Température (°C)")
plt.title("Évolution de la température")
plt.xlabel("Mesures")
plt.ylabel("Température (°C)")
plt.legend()
plt.grid(True)
plt.show()
Code fourni : transmission.py
class Transmission:
def __init__(self, trame):
self._id = None
self._temperature = None
self._humidite = None
self._trame = trame
self.decoder()
def __repr__(self):
""" Méthode permettant l'affichage """
return f"ID : {self._id} / Temp. : {self._temperature}°C / Hum. : {self._humidite}%"
def decoder(self):
self.decoder_id()
self.decoder_temperature()
self.decoder_humidite()
def decoder_id(self):
# int(s, 2) : conversion binaire -> décimal
self._id = int(self._trame[0:8], 2)
def decoder_temperature(self):
pass # À compléter
def decoder_humidite(self):
pass # À compléter
def get_id(self):
return self._id
def get_temperature(self):
return self._temperature
def get_humidite(self):
return self._humidite
def est_valide(self):
return False # À compléter
Question 1
#Implémenter decoder_temperature et decoder_humidite. Elles ne renvoient rien, mais modifient les propriétés de l’objet. Tester 26.4°C et 62% sur la trame 0010101011001000010010001100011000101101.
Indice
Les deux champs n’utilisent pas le même codage.
Comprendre la correction
def decoder_temperature(self):
valeur = int(self._trame[16:28], 2)
self._temperature = (valeur - 900) / 10def decoder_humidite(self):
bits = self._trame[28:36]
if bits == '10100000':
self._humidite = 100
else:
dizaines = int(bits[:4], 2)
unites = int(bits[4:], 2)
self._humidite = 10 * dizaines + unitest = Transmission('0010101011001000010010001100011000101101')
assert t.get_temperature() == 26.4
assert t.get_humidite() == 62
assert t.get_id() == 42La tranche de température comprend 12 bits et s’arrête avant l’indice 28. Pour l’humidité, lire les huit bits comme un entier binaire donnerait 98 au lieu de 62 : il faut lire deux quartets, puis composer 10×dizaines+unités. Le cas 100% est traité avant cette règle. Ces méthodes stockent leurs résultats dans _temperature et _humidite pour que les accesseurs et l’affichage fournis puissent les utiliser.
Question 2
#Écrire est_valide pour comparer chacun des quatre bits de contrôle à la parité de son bloc. RenvoyerTrue si tous correspondent, False sinon.
Indice
La parité est le reste du nombre de 1 dans la division par 2.
Comprendre la correction
def est_valide(self):
blocs = [self._trame[0:8], self._trame[8:16],
self._trame[16:28], self._trame[28:36]]
for i in range(4):
parite = blocs[i].count('1') % 2
if parite != int(self._trame[36+i]):
return False
return TrueLes blocs n’ont pas tous la même longueur :8,8,12 et 8 bits. Il faut donc respecter les découpages du tableau au lieu de couper systématiquement par 8. Sur l’exemple, leurs nombres de 1 sont 3,3,4 et 3 : les restes modulo 2 forment 1101. Un seul écart suffit à rejeter la trame. Cette version suppose encore la structure bien formée ; la question 4 lèvera cette hypothèse.
Question 3
#Exécuter analyse.py sur les plus de 300 transmissions de data.txt et identifier les erreurs possibles.
Comprendre la correction
Le fichier réel contient 314 lignes. Trois ont une longueur incorrecte : lignes 73 et 126 de 41 bits, ligne 111 de 38 bits. Les conversions de tranches courtes peuvent parfois réussir silencieusement, puis l’accès à un bit de contrôle absent provoquer IndexError. Une chaîne non binaire provoquerait ValueError dans int(...,2), même si ce fichier ne contient pas ce cas. Il faut aussi distinguer les trames bien formées mais invalides : trois parités sont fausses (lignes 8,53,114) et une température vaut−28,6°C (ligne 255), sous la limite du sujet.
Le filtrage intervient après la construction de tous les objets. Un constructeur qui lève une exception interrompt donc la liste complète avant même est_valide. Attraper uniquement une erreur au moment du tracé serait trop tard. L’export fourni se termine par un saut de ligne, donc trames.pop() supprime bien une ligne vide ici ; pour un autre fichier sans ce saut final, cette stratégie supprimerait une vraie trame. splitlines() serait une amélioration indépendante du lecteur.
Question 4
#Rendre la classe Transmission plus robuste pour permettre l’analyse complète, puis commenter le graphique obtenu.
PythonRefuser une trame défectueuse avant qu’elle fasse tomber l’analyseÉcrivez votre solution et mettez-la à l’épreuve
Complétez structure_valide et est_valide dans la classe Transmission. Exigez une chaîne de 40 bits ; une température d’au moins -10 °C ; une humidité BCD valide, avec l’exception 10100000 pour 100 % ; et les quatre parités sur les blocs de 8, 8, 12 et 8 bits. Les méthodes de décodage sont fournies et ignorent une structure invalide. Une mauvaise trame doit renvoyer False, sans bloquer le traitement des suivantes. Le graphique et la lecture de fichier ne sont pas exécutés ici.
class Transmission:
def __init__(self, trame):
self._trame = trame
self._id = None
self._temperature = None
self._humidite = None
self.decoder()
def structure_valide(self):
# Complétez les contrôles demandés
return False
def decoder(self):
if not self.structure_valide():
return
self.decoder_id()
self.decoder_temperature()
self.decoder_humidite()
def decoder_id(self):
self._id = int(self._trame[:8], 2)
def decoder_temperature(self):
self._temperature = (int(self._trame[16:28], 2) - 900) / 10
def decoder_humidite(self):
bits = self._trame[28:36]
dizaines, unites = int(bits[:4], 2), int(bits[4:], 2)
if bits == '10100000':
self._humidite = 100
elif dizaines <= 9 and unites <= 9:
self._humidite = 10 * dizaines + unites
else:
self._humidite = None
def est_valide(self):
# Complétez les contrôles demandés
return False
def get_id(self):
return self._id
def get_temperature(self):
return self._temperature
def get_humidite(self):
return self._humidite
def __repr__(self):
return f'ID : {self._id} / Temp. : {self._temperature}°C / Hum. : {self._humidite}%'
Les cas de test proposés :
- La trame officielle : Ce cas fixe contrôle conjointement le découpage, le décodage et les parités.
- Formes incorrectes, sans exception : La structure doit être vérifiée avant toute conversion ou indexation.
- Chacune des 40 inversions isolées : Une inversion dans un champ ou dans son bit de contrôle doit être détectée.
- La parité ne détecte pas tout : Inverser deux bits du premier bloc conserve sa parité. Ce contrôle ne prouve pas l’authenticité du message.
- Humidité BCD et exception 100 % : Les chiffres 10 à 15 sont invalides en BCD, sauf l’unique codage spécial de 100 %.
- Température sur la borne : -10 °C est autorisé ; une température plus basse ne l’est pas, même si les parités sont correctes.
Indice
Contrôler la forme avant les conversions et la signification après.
Comprendre la correction
On valide d’abord la forme : chaîne de 40 caractères tous binaires. Une trame mal formée conserve des attributsNone et est_valide renvoieFalse, sans faire échouer le constructeur. Pour une trame bien formée, on décode, puis on vérifie les contraintes de mesure et la parité. Un quartet BCD de 10 à 15 est refusé, sauf l’exception exacte 10100000. Voici une classe complète compatible avec analyse.py :
class Transmission:
def __init__(self, trame):
self._trame = trame
self._id = None
self._temperature = None
self._humidite = None
self.decoder()
def structure_valide(self):
return (isinstance(self._trame, str) and len(self._trame) == 40
and all(bit in '01' for bit in self._trame))
def decoder(self):
if not self.structure_valide():
return
self.decoder_id()
self.decoder_temperature()
self.decoder_humidite()
def decoder_id(self):
self._id = int(self._trame[:8], 2)
def decoder_temperature(self):
self._temperature = (int(self._trame[16:28], 2) - 900) / 10
def decoder_humidite(self):
bits = self._trame[28:36]
dizaines, unites = int(bits[:4], 2), int(bits[4:], 2)
if bits == '10100000':
self._humidite = 100
elif dizaines <= 9 and unites <= 9:
self._humidite = 10 * dizaines + unites
else:
self._humidite = None
def est_valide(self):
if not self.structure_valide():
return False
if self._temperature is None or self._temperature < -10:
return False
if self._humidite is None:
return False
blocs = [self._trame[:8], self._trame[8:16],
self._trame[16:28], self._trame[28:36]]
return all(bloc.count('1') % 2 == int(self._trame[36+i])
for i, bloc in enumerate(blocs))
def get_id(self):
return self._id
def get_temperature(self):
return self._temperature
def get_humidite(self):
return self._humidite
def __repr__(self):
return f'ID : {self._id} / Temp. : {self._temperature}°C / Hum. : {self._humidite}%'L’analyse affiche 314 trames reçues et 307 valides. Les trois erreurs de taille, trois erreurs de parité et une température trop basse sont distinctes dans ce fichier. Les températures retenues vont de 22,7 à 34,2°C ; la série commence vers 26,4°C et se termine à 24,3°C. Le graphique montre l’ordre des mesures retenues, pas une échelle temporelle connue : les lignes rejetées disparaissent de l’abscisse.
graphique obtenu après validation des trames. L’abscisse est un indice de mesure valide, pas un temps horodaté.La parité détecte toute inversion isolée dans un bloc ou son bit de contrôle, mais pas forcément deux inversions dans le même bloc : leur effet sur la parité peut s’annuler. Elle ne garantit donc ni l’exactitude absolue ni l’authenticité du message. La présence d’un champ appelé clé de sécurité ne suffit pas à fournir une vérification cryptographique ; le sujet ne donne aucun protocole d’authentification à inventer.
Les validations s’organisent dans un ordre qui évite les exceptions : forme du message, conversions, contraintes des champs, puis cohérence des bits de contrôle. Un bon jeu de tests isole chaque niveau. Une trame de 39 bits doit être refusée sans accès hors limites ; une trame contenant x doit être refusée avant int ; une humidité BCD avec un chiffre 11 doit être rejetée ; une température exactement −10 doit rester acceptable ; une inversion unique doit faire échouer la parité. Le décompte sur le fichier réel complète ces tests ciblés, mais ne remplace pas les cas limites absents de ce fichier.
Trois niveaux de validationUn atelier pour expérimenter
Choisissez une réponse puis confrontez votre intuition à l’explication. Vous pouvez modifier vos choix.
Lire les réponses et leurs explications
- Une trame contient 38 bits. À quel moment la rejeter ?
Avant le décodage.. La structure doit être sûre avant les accès.
- Deux bits inversés dans le même bloc sont-ils toujours détectés ?
Non, leur parité peut rester identique.. Deux changements conservent la parité du nombre de 1.
Appuyez votre explication sur les valeurs et les conditions exactes du programme.
Altérer une trame et éprouver la paritéUn atelier pour expérimenter
Partez de la trame exemple. Activez une inversion, puis une deuxième. Choisissez deux positions distinctes dans le même bloc pour observer une erreur que la parité ne détecte pas.
Lire le résultat de l’expérience initiale
Les quatre parités concordent.
Cet atelier isole le contrôle de parité. Une concordance ne garantit pas que la trame est identique à l’original ni que toutes les mesures sont valides.
| Bloc | Bits | Nombre de1 | Parité calculée | Bit reçu | Contrôle |
|---|---|---|---|---|---|
| ID | 00101010 | 3 | 1 | 1 | Accord |
| Clé | 11001000 | 3 | 1 | 1 | Accord |
| Température | 010010001100 | 4 | 0 | 0 | Accord |
| Humidité | 01100010 | 3 | 1 | 1 | Accord |
Un contrôle d’intégrité a un modèle d’erreur précis : il faut connaître ce qu’il détecte et ce qu’il peut laisser passer.
Du sujet à la méthode
Votre prochaine séance de révision
- Contrôler structure, codage et contraintes métier séparément.
- Un test de parité positif n’est pas une preuve d’authenticité.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 26_BCG_NSI_23 (PDF). Corrigé et explications pédagogiques proposés par Sofien.
