Files
shipheart-appli/sources/nmea.c
T

3084 lines
115 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//-----------------------------------------------------------------------------*****************************************************************************************************
// Fichier NMEA.C
//
// Contient toutes les fonctions liées à la gestion des PGN NMEA provenant du Compagnon
// !!!!! Pour intégrer un nouveau PGN, voir PROCEDURE_PGN ci-dessous !!!!!
//-----------------------------------------------------------------------------*****************************************************************************************************/
//#define __DEBUG_NMEA //Décommenter cette ligne pour avoir les messages de debug
// Includes :
#include "main.h"
// Prototypes :
int8_t init_nmea(void);
void gestion_nmea(void);
int8_t traitement_pgn_compagnon(struct Struct_pgn_nmea pgn);
// Variables globales :
struct Struct_nmea nmea;
/**********************************************************************************************************************************************************************************
Fonction INIT_NMEA
Fonction d'initialisation du réseau NMEA
**********************************************************************************************************************************************************************************/
int8_t init_nmea(void)
{
nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_INIT;
nmea.sequenceur.compteur = NMEA_TEMPO_INIT;
nmea.sequenceur.compteur_time_out = 0;
init_annuaire_nmea(); // Contrairement aux autres annuaires (k-bus, Ow), il n'est pas nécessaire d'attendre l'initialisation dans 'systeme.c'
// Affichage du message de débug :
affichage_debug(__FILE__, __LINE__, "Initialisation du réseau NMEA\t: OK", TYPE_MESSAGE_DEBUG_OK);
return(0);
}
//==================================================================================================================================================================
// Fonction : Appelée en tâche de fond, cette fonction gère le réseau NMEA dans son ensemble
// Reçoit : Rien
// Renvoit : Rien
//==================================================================================================================================================================
void gestion_nmea(void)
{
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
switch(nmea.sequenceur.etat)
{
//----------------------------------------------------------------------------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_NMEA_INIT :
//----------------------------------------------------------------------------------------------------------------------------------------------------------
{
if (nmea.sequenceur.compteur == 0)
{
nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_REPOS;
}
break;
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_NMEA_REPOS :
//----------------------------------------------------------------------------------------------------------------------------------------------------------
{
// if (nmea.sequenceur.compteur == 0)
// {
// nmea.sequenceur.compteur = NMEA_PERIODE_CHECK; // A priori, toutes les 100ms
// }
break;
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------
default :
//----------------------------------------------------------------------------------------------------------------------------------------------------------
{
sprintf(tmp, "L'état du séquenceur NMEA est inconnu [%i]. Il est repositionné au REPOS", nmea.sequenceur.etat);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_REPOS;
break;
}
}
}
//==================================================================================================================================================================
// Fonction : Appelée lors de la réception d'une commande 'c_bus_up_commande_etat_pgn_nmea()' dans 'c_commandes.c', cette fonction traite un PGN reçu du Compagnon
// Reçoit : Le PGN à traiter
// Renvoit : 0 si traitement Ok, -1 si erreur.
//==================================================================================================================================================================
int8_t traitement_pgn_compagnon(struct Struct_pgn_nmea pgn)
{
uint16_t i, j;
bool flag_pgn_present_dans_annuaire;
bool flag_pgn_present_dans_catalogue;
uint16_t indice_catalogue;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
//-----------------------------------------------------------------------------------------
// PGN déjà présent dans l'annuaire :
//-----------------------------------------------------------------------------------------
flag_pgn_present_dans_annuaire = false;
for (i = 0; i < NMEA_MAX_NOMBRE_PGN; i++)
{
if (!nmea.annuaire[i].flag_validite) continue;
if (nmea.annuaire[i].pgn == pgn.pgn)
{
flag_pgn_present_dans_annuaire = true;
// Mise à jour des dernières valeurs dans l'annuaire :
nmea.annuaire[i].priority = pgn.priority;
nmea.annuaire[i].source = pgn.source;
nmea.annuaire[i].timestamp = pgn.timestamp;
nmea.annuaire[i].lg_data = pgn.lg_data;
for (j = 0; j < pgn.lg_data; j ++) nmea.annuaire[i].data[j] = pgn.data[j];
//---------------------------
// PGN Actif :
//---------------------------
if (nmea.annuaire[i].actif == 1)
{
for (j = 0; ((j < NMEA_MAX_NOMBRE_PGN) && (catalogue_pgns[j].pgn != 0)); j ++) // Recherche des infos de config dans le catalogue (le PGN s'y trouve forcément car il a été créé dans l'annuaire...)
{
if (catalogue_pgns[j].pgn == pgn.pgn) // Le PGN a été trouvé dans le catalogue
{
// ------------------------------
// Si le PGN a déjà été détecté par le Compagnon (il est donc complètement configuré sur l'Appli et sur le Compagnon) :
if (nmea.annuaire[i].flag_presence)
{
catalogue_pgns[j].fonction_parse_pgn(i, NMEA_PARSE_MODE_MISE_A_JOUR); // Appel de la fonction de parsing de ce PGN en mode MISE A JOUR.
}
//-------------------------------
// Premier retour du Compagnon sur ce PGN :
else
{
#ifdef __DEBUG_NMEA
sprintf(tmp, "PGN %i : Ce PGN est Actif et c'est sa première apparition depuis le reset du Compagnon. Passage à Présent + config du Compagnon", nmea.annuaire[i].pgn);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
c_bus_down_commande_config_pgn_nmea(i);
nmea.annuaire[i].flag_presence = true;
}
break;
}
}
}
//---------------------------
// PGN Inactif :
//---------------------------
else if (nmea.annuaire[i].actif == 0)
{
enregistrement_date_pgn_nmea(i);
}
//---------------------------
// PGN désactivé car non connu dans le catalogue :
//---------------------------
else ;
break; // Il est inutile de poursuivre lorsque le PGN a été trouvé.
}
}
if (flag_pgn_present_dans_annuaire) return (0); // Le traitement est terminé...
//-----------------------------------------------------------------------------------------
// PGN pas encore présent dans l'annuaire :
//-----------------------------------------------------------------------------------------
//----------------------------------
// PGN présent dans le catalogue ? :
flag_pgn_present_dans_catalogue = false;
for (i = 0; ((i < NMEA_MAX_NOMBRE_PGN) && (catalogue_pgns[i].pgn != 0)); i ++)
{
if (catalogue_pgns[i].pgn == pgn.pgn) // Le PGN a été trouvé dans le catalogue :
{
flag_pgn_present_dans_catalogue = true;
indice_catalogue = i;
break; // Sortie de la boucle du catalogue puisqu'on a trouvé l'entrée.
}
}
//----------------------------------
// Ajout à l'annuaire :
for (i = 0; i < NMEA_MAX_NOMBRE_PGN; i ++) // On recherche ensuite le premier emplacement libre :
{
if (!nmea.annuaire[i].flag_validite) // L'emplacement est libre.
{
nmea.annuaire[i].flag_validite = true;
nmea.annuaire[i].flag_presence = true;
nmea.annuaire[i].pgn = pgn.pgn;
//-----------------------------------------------
// PGN présent dans le catalogue :
//-----------------------------------------------
if (flag_pgn_present_dans_catalogue)
{
nmea.annuaire[i].actif = 0; // Par défaut, le PGN est Inactif puisqu'il ne pourra être passé à Actif que par Qt (qui a d'abord besoin qu'il soit créé...)
nmea.annuaire[i].priority = pgn.priority;
nmea.annuaire[i].source = pgn.source;
nmea.annuaire[i].timestamp = pgn.timestamp;
nmea.annuaire[i].type = catalogue_pgns[indice_catalogue].type; // Pas utile tout de suite (car Actif = 0) mais permet de ne pas le faire lors de l'activation qui se fait à plusieurs endroits (et de l'oublier - vécu...)
nmea.annuaire[i].periode_mini = catalogue_pgns[indice_catalogue].periode_mini; // Idem...
nmea.annuaire[i].lg_data = 0;
#ifdef __DEBUG_NMEA
sprintf(tmp, "Nouveau PGN enregistré en position %i : %i", i, nmea.annuaire[i].pgn);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
catalogue_pgns[indice_catalogue].fonction_parse_pgn(i, NMEA_PARSE_MODE_CREATION); // Appel de la fonction de parsing de ce PGN en mode CREATION.
// NB : on ne fait rien vers le Compagnon car, par défaut, les Fields de ce PGN sont tous créés en mode Inactifs.
}
//-----------------------------------------------
// PGN absent du catalogue :
//-----------------------------------------------
else
{
nmea.annuaire[i].actif = -1; // NB : on garde le PGN dans l'annuaire pour qu'on le repère comme étant Désactivé (Actif = -1) à l'avenir.
#ifdef __DEBUG_NMEA
sprintf(tmp, "Le PGN %i n'est pas référencé dans le catalogue NMEA2000. Il est donc désactivé (aucun traitement à l'avenir)...", pgn.pgn);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
#endif
c_bus_down_commande_config_pgn_nmea(i); // Commande de désactivation (Actif = -1) vers le Compagnon pour qe ce dernier ne le traite plus à l'avenir.
return (-1);
}
break; // Sortie de la boucle de recherche d'un emplacement libre.
}
}
//-----------------------------------------------
// Nombre de PGN trop important :
//-----------------------------------------------
if (i == NMEA_MAX_NOMBRE_PGN)
{
sprintf(tmp, "Le nombre maximal de PGN a été atteint (%i). Le PGN %i n'est pas traité...", NMEA_MAX_NOMBRE_PGN, pgn.pgn);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
}
return(0);
}
// PROCEDURE_PGN :
//
// Pour intégrer un nouveau PGN, il faut :
// 1- Dans 'nmea.h', il faut créer les n° de Field qui seront utilisés par ShipHeart. L'affectation des n° est théoriquement libre mais, par principe, on réutilise
// les n° utilisés dans la doc NMEA (Liste PGN NMEA 2024 - V3.002 au moment d'écrire ces lignes)
// 2- Créer une fonction de parsing du type 'Parse_pgn_xxxxxx()'.
// Le plus simple est de dupliquer la fonction template et remplacer xxxxxx par le code du PGN.
// Renseigner la longueur minimale (LG_DATA_MINI) que la trame doit faire pour pouvoir être traitée (a minima, jusqu'au dernier field qui sera décodé car aucun contrôle n'est fait ensuite).
// Dans le case NMEA_PARSE_MODE_CREATION, ajouter les appels correspondant aux fields que l'on souhaite intégrer. Penser à renseigner la désignation (celle
// qui sera affichée par défaut dans la liste des PGNs sur Qt - en Anglais).
// Dans le case NMEA_PARSE_MODE_MISE_A_JOUR, ajouter les différents fields dans l'ordre de lecture. Généralement, après SID. Prendre exemple sur les PGN existants
// pour dupliquer un traitement approchant. IMPORTANT : la valeur retournée DOIT être un nombre (float).
// 3- Ajouter le nouveau PGN dans le "Catalogue des PGNs" (plus bas, dans ce fichier).
// Ajouter une ligne dans l'ordre chrono des PGNs. Les valeurs à renseigner sont :
// - le code du Pgn,
// - le Type (pour cela, s'aider de la liste située en dessous, en commentaires),
// - La Période mini (x10ms). Elle permet de "limiter" les remontées de données de la part du Compagnon et, ainsi, limiter la charge (du Compagnon, de l'Appli, etc...)
// - la fonction de parsing créée au 2.
// Important : terminer la liste par une entrée avec le code de Pgn nul. C'est cette ligne qui est détectée pour terminer la liste.
// La taille maxi du catalogue est NMEA_MAX_NOMBRE_PGN mais un dépassement sera détecté par la compilation.
// 4- Compiler.
// 5- Tester avec le simulateur.
// 6- Lorsque les tests sont concluants, commenter le #define '#define DEBUG_PGN_xxxxxx' pour ne pas surcharger la sortie de debug.
//-----------------------------------------------------------------------------**************************************************************************************
//-----------------------------------------------------------------------------**************************************************************************************
//
// Fonctions de Parsing de PNG :
//
//-----------------------------------------------------------------------------**************************************************************************************
//-----------------------------------------------------------------------------**************************************************************************************
// L'aide sur les fonctions de parsing peut se trouver ici : https://ttlappalainen.github.io/NMEA2000/group__group__msg_parsers.html#ga3472b7dcf7ece7265708e20ee8bcf78a
// Rechercher sur la gauche le N° de PGN...
// Extrait de 'shipheart_sql.sql' :
// (50,2, "Capt_sans_unite", "Sans unité", " ", 1, 0, 1, "2020-01-01 00:00:00"),
// (51,2, "Capt_tension_v", "Tension", "V", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (52,2, "Capt_tension_mv", "Tension", "mV", 10, 0, 1, "2020-01-01 00:00:00"),
// (53,2, "Capt_courant_a", "Intensité", "A", 0.01, 0, 1, "2020-01-01 00:00:00"),
// (54,2, "Capt_courant_ma", "Intensité", "mA", 0.5, 0, 1, "2020-01-01 00:00:00"),
// (55,2, "Capt_puissance_w", "Puissance", "W", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (56,2, "Capt_puissance_va", "Puissance apparente", "VA", 1, 0, 1, "2020-01-01 00:00:00"),
// (57,2, "Capt_puissance_db", "Puissance", "dB", 0.2, 0, 1, "2020-01-01 00:00:00"),
// (58,2, "Capt_frequence", "Fréquence", "Hz", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (59,2, "Capt_resistance", "Résistance", "Ohms", 1, 0, 1, "2020-01-01 00:00:00"),
// (60,2, "Capt_temperature", "Température", "°C", 0.1, 2, 1, "2020-01-01 00:00:00"),
// (61,2, "Capt_pression_atm", "Pression atmosphérique", "hPa", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (62,2, "Capt_humidite", "Humidité", "%HR", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (63,2, "Capt_acceleration", "Accélération", "g", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (64,2, "Capt_position", "Position", "°", 0.0001, 0, 1, "2020-01-01 00:00:00"),
// (65,2, "Capt_pourcentage", "Pourcentage", "%", 1, 0, 1, "2020-01-01 00:00:00"),
// (66,2, "Capt_distance_m", "Distance", "m", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (67,2, "Capt_distance_km", "Distance", "km", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (68,2, "Capt_distance_nq", "Distance", "nq", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (69,2, "Capt_vitesse_kts", "Vitesse", "kts", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (70,2, "Capt_vitesse_kmh", "Vitesse", "km/h", 0.1, 0, 1, "2020-01-01 00:00:00"),
// (71,2, "Capt_angle_degres", "Angle", "°", 1, 0, 1, "2020-01-01 00:00:00");
//==================================================================================================================================================================
// PGN : 126992
// Fonction : System date/time, pri=3, period=1000
//==================================================================================================================================================================
int8_t Parse_pgn_126992(uint16_t rang, uint8_t mode)
{
// #define DEBUG_PGN_126992
const uint8_t LG_DATA_MINI = 8;
uint8_t index;
uint8_t val_u8;
uint16_t val_u16;
uint32_t val_u32;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (nmea.annuaire[rang].pgn != 126992) return (-1);
switch (mode)
{
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_CREATION:
{
creation_pgn_field_nmea(rang, NMEA_126992_TIME_SOURCE, "Capt_sans_unite", 0, 0, "Time source");
creation_pgn_field_nmea(rang, NMEA_126992_SYSTEM_DATE, "Capt_sans_unite", 0, 0, "System date");
creation_pgn_field_nmea(rang, NMEA_126992_SYSTEM_TIME, "Capt_sans_unite", 0, 0, "System time");
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_MISE_A_JOUR:
{
if (nmea.annuaire[rang].lg_data < LG_DATA_MINI)
{
sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
return (-1);
}
index = 0;
//----------------------------------
// Sequence ID :
index ++;
//----------------------------------
// Time source :
val_u8 = nmea.annuaire[rang].data[index ++];
enregistrement_pgn_field_nmea(rang, NMEA_126992_TIME_SOURCE, (val_u8 & 0x0F) );
#ifdef DEBUG_PGN_126992
sprintf(tmp, "PGN %i : Time source = %i", nmea.annuaire[rang].pgn, (val_u8 & 0x0F) );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// System Date :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8;
enregistrement_pgn_field_nmea(rang, NMEA_126992_SYSTEM_DATE, val_u16);
#ifdef DEBUG_PGN_126992
sprintf(tmp, "PGN %i : System Date = %i", nmea.annuaire[rang].pgn, val_u16);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// System Time :
val_u32 = (uint32_t) nmea.annuaire[rang].data[index ++];
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 8;
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 16;
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 24;
enregistrement_pgn_field_nmea(rang, NMEA_126992_SYSTEM_TIME, ((float) val_u32 * 0.0001) );
#ifdef DEBUG_PGN_126992
sprintf(tmp, "PGN %i : System Time = %.4f", nmea.annuaire[rang].pgn, ((float) val_u32 * 0.0001));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_SUPPRESSION:
{
suppression_pgn_nmea(rang);
break;
}
//------------------------------------------------------------------------------
default: break;
}
return (0);
}
//==================================================================================================================================================================
// PGN : 129029
// Fonction : GNSS Position Data, pri=3, period=1000
//==================================================================================================================================================================
int8_t Parse_pgn_129029(uint16_t rang, uint8_t mode)
{
// #define DEBUG_PGN_129029
const uint8_t LG_DATA_MINI = 38;
uint8_t index;
uint8_t val_u8;
uint16_t val_u16;
uint32_t val_u32;
uint64_t val_u64;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (nmea.annuaire[rang].pgn != 129029) return (-1);
switch (mode)
{
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_CREATION:
{
creation_pgn_field_nmea(rang, NMEA_129029_DAYS_SINCE_1970, "Capt_sans_unite", 0, 0, "Numer of days since January 1st, 1970");
creation_pgn_field_nmea(rang, NMEA_129029_SECONDS_SINCE_MIDNIGHT, "Capt_sans_unite", 0, 0, "Number of seconds since midnight");
creation_pgn_field_nmea(rang, NMEA_129029_LATITUDE, "Capt_latitude", 0, 0, "Latitude");
creation_pgn_field_nmea(rang, NMEA_129029_LONGITUDE, "Capt_longitude", 0, 0, "Longitude");
creation_pgn_field_nmea(rang, NMEA_129029_GNSS_TYPE, "Capt_sans_unite", 0, 0, "GNSS Type");
creation_pgn_field_nmea(rang, NMEA_129029_GNSS_METHOD, "Capt_sans_unite", 0, 0, "GNSS Method");
creation_pgn_field_nmea(rang, NMEA_129029_SATELLITES_NUMBER, "Capt_sans_unite", 0, 0, "Satellites number");
creation_pgn_field_nmea(rang, NMEA_129029_HDOP, "Capt_sans_unite", 1, 2, "HDOP");
creation_pgn_field_nmea(rang, NMEA_129029_PDOP, "Capt_sans_unite", 1, 2, "PDOP");
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_MISE_A_JOUR:
{
if (nmea.annuaire[rang].lg_data < LG_DATA_MINI)
{
sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
return (-1);
}
index = 0;
//----------------------------------
// Sequence ID :
index ++;
//----------------------------------
// Number of days since 1970 :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8;
enregistrement_pgn_field_nmea(rang, NMEA_129029_DAYS_SINCE_1970, val_u16);
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Number of days since 1970 = %i", nmea.annuaire[rang].pgn, val_u16);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Seconds since midnight :
val_u32 = (uint32_t) nmea.annuaire[rang].data[index ++];
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 8;
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 16;
val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 24;
enregistrement_pgn_field_nmea(rang, NMEA_129029_SECONDS_SINCE_MIDNIGHT, ((float) val_u32 * 0.0001) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Number of seconds since midnight = %.4f", nmea.annuaire[rang].pgn, ((float) val_u32 * 0.0001));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Latitude :
val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++];
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56;
enregistrement_pgn_field_nmea(rang, NMEA_129029_LATITUDE, ((float) val_u64) * (1e-16) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Latitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-16) );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Longitude :
val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++];
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56;
enregistrement_pgn_field_nmea(rang, NMEA_129029_LONGITUDE, ((float) val_u64) * (1e-16) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Longitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-16));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Altitude :
val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++];
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48;
val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56;
enregistrement_pgn_field_nmea(rang, NMEA_129029_ALTITUDE, ((float) val_u64) * (1e-6) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Altitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-6));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// GNSS Type et GNSS Methode :
val_u8 = nmea.annuaire[rang].data[index ++];
enregistrement_pgn_field_nmea(rang, NMEA_129029_GNSS_TYPE, (val_u8 & 0x0F) );
enregistrement_pgn_field_nmea(rang, NMEA_129029_GNSS_METHOD, ((val_u8 >> 4) & 0x0F) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : GNSS Type = %i", nmea.annuaire[rang].pgn, (val_u8 & 0x0F) );
sprintf(tmp, "PGN %i : GNSS Method = %i", nmea.annuaire[rang].pgn, ((val_u8 >> 4) & 0x0F) );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Integrity 2 bit, reserved 6 bits :
index ++;
//----------------------------------
// Nb satellites :
val_u8 = nmea.annuaire[rang].data[index ++];
enregistrement_pgn_field_nmea(rang, NMEA_129029_SATELLITES_NUMBER, val_u8 );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : Satellites Number = %i", nmea.annuaire[rang].pgn, val_u8 );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// HDOP :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8;
enregistrement_pgn_field_nmea(rang, NMEA_129029_HDOP, ((float) val_u16 * 0.01) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : HDOP = %.4f", nmea.annuaire[rang].pgn, ((float) val_u16 * 0.01));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// PDOP :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8;
enregistrement_pgn_field_nmea(rang, NMEA_129029_PDOP, ((float) val_u16 * 0.01) );
#ifdef DEBUG_PGN_129029
sprintf(tmp, "PGN %i : PDOP = %.4f", nmea.annuaire[rang].pgn, ((float) val_u16 * 0.01));
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Non traités :
// GeoidalSeparation=N2kMsg.Get4ByteDouble(0.01,Index);
// nReferenceStations=N2kMsg.GetByte(Index);
// if (nReferenceStations!=N2kUInt8NA && nReferenceStations>0)
// {
// Note that we return real number of stations, but we only have variabes for one.
// vi=N2kMsg.Get2ByteUInt(Index);
// ReferenceStationType=(tN2kGNSStype)(vi & 0x0f);
// ReferenceSationID=(vi>>4);
// AgeOfCorrection=N2kMsg.Get2ByteUDouble(0.01,Index);
// }
// else
// {
// ReferenceStationType = N2kGNSSt_GPS;
// ReferenceSationID = N2kInt16NA;
// AgeOfCorrection = N2kDoubleNA;
// }
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_SUPPRESSION:
{
suppression_pgn_nmea(rang);
break;
}
//------------------------------------------------------------------------------
default: break;
}
return (0);
}
//==================================================================================================================================================================
// PGN : 129540
// Fonction : GNSS Sats in View
//==================================================================================================================================================================
int8_t Parse_pgn_129540(uint16_t rang, uint8_t mode)
{
//#define DEBUG_PGN_129540
const uint8_t LG_DATA_MINI = 3;
uint8_t index;
uint8_t val_u8;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (nmea.annuaire[rang].pgn != 129540) return (-1);
switch (mode)
{
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_CREATION:
{
creation_pgn_field_nmea(rang, NMEA_129540_SATELLITES_IN_VIEW, "Capt_sans_unite", 0, 0, "Satellites in view");
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_MISE_A_JOUR:
{
if (nmea.annuaire[rang].lg_data < LG_DATA_MINI)
{
sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
return (-1);
}
index = 0;
//----------------------------------
// Sequence ID :
index ++;
//----------------------------------
// Mode :
index ++;
//----------------------------------
// Satellites in view :
val_u8 = nmea.annuaire[rang].data[index ++];
enregistrement_pgn_field_nmea(rang, NMEA_129540_SATELLITES_IN_VIEW, val_u8 );
#ifdef DEBUG_PGN_129540
sprintf(tmp, "PGN %i : Satellites in view = %i", nmea.annuaire[rang].pgn, val_u8 );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_SUPPRESSION:
{
suppression_pgn_nmea(rang);
break;
}
//------------------------------------------------------------------------------
default: break;
}
return (0);
}
//==================================================================================================================================================================
// PGN : 130306
// Fonction : Wind Data
//==================================================================================================================================================================
int8_t Parse_pgn_130306(uint16_t rang, uint8_t mode)
{
// #define DEBUG_PGN_130306
const uint8_t LG_DATA_MINI = 6;
uint8_t index;
uint16_t val_u16;
uint8_t wind_ref;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (nmea.annuaire[rang].pgn != 130306) return (-1);
switch (mode)
{
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_CREATION:
{
creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_NORTH, "Capt_vitesse_kts", 1, 1, "Wind Speed [True North]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_NORTH, "Capt_angle_degres", 0, 1, "Wind Angle [True North]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_MAGNETIC, "Capt_vitesse_kts", 1, 1, "Wind Speed [Magnetic]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_MAGNETIC, "Capt_angle_degres", 0, 1, "Wind Angle [Magnetic]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_APPARENT, "Capt_vitesse_kts", 1, 1, "Wind Speed [Apparent]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_APPARENT, "Capt_angle_degres", 0, 1, "Wind Angle [Apparent]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_BOAT, "Capt_vitesse_kts", 1, 1, "Wind Speed [True boat]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_BOAT, "Capt_angle_degres", 0, 1, "Wind Angle [True boat]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_WATER, "Capt_vitesse_kts", 1, 1, "Wind Speed [True water]");
creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_WATER, "Capt_angle_degres", 0, 1, "Wind Angle [True water]");
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_MISE_A_JOUR:
{
if (nmea.annuaire[rang].lg_data < LG_DATA_MINI)
{
sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
return (-1);
}
// Recherche de la référence des infos de vent (vitesse et direction) qui suivent :
// True_North=0, Magnetic=1, Apparent=2, True_boat=3, True_water=4, Error=6, Unavailable=7
wind_ref = nmea.annuaire[rang].data[5] & 0x07;
if (wind_ref > 4)
{
#ifdef DEBUG_PGN_130306
sprintf(tmp, "La référence de vent du PGN %i n'est pas correcte (reçu : %i, attendu : 4 maxi). Le traitement de mise à jour est annulé...", nmea.annuaire[rang].pgn, wind_ref );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING);
#endif
return(-1);
}
#ifdef DEBUG_PGN_130306
char wind_ref_name[40];
switch(wind_ref)
{
case 0: { strcpy(wind_ref_name, "True North"); break; }
case 1: { strcpy(wind_ref_name, "Magnetic"); break; }
case 2: { strcpy(wind_ref_name, "Apparent"); break; }
case 3: { strcpy(wind_ref_name, "True boat"); break; }
case 4: { strcpy(wind_ref_name, "True water"); break; }
case 6: { strcpy(wind_ref_name, "Error"); break; }
case 7: { strcpy(wind_ref_name, "Unavailable"); break; }
default: { strcpy(wind_ref_name, "True North"); break; }
}
#endif
// Parse des data :
index = 0;
//----------------------------------
// Sequence ID :
index ++;
//----------------------------------
// Wind Speed :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; // En m/s
enregistrement_pgn_field_nmea(rang, (NMEA_130306_WIND_SPEED_TRUE_NORTH + wind_ref), (((float) val_u16 * 0.01) * CONVERSION_MS_TO_KNOTS) ); // En Knots
#ifdef DEBUG_PGN_130306
sprintf(tmp, "PGN %i : Vitesse du vent [%s] = %.2Lf Kts", nmea.annuaire[rang].pgn, wind_ref_name, (((float) val_u16 * 0.01) * CONVERSION_MS_TO_KNOTS) );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
//----------------------------------
// Wind Angle :
val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++];
val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; // En Radians
enregistrement_pgn_field_nmea(rang, (NMEA_130306_WIND_ANGLE_TRUE_NORTH + wind_ref), (((float) val_u16 * 0.0001) * CONVERSION_RAD_TO_DEG) ); // En Degrés
#ifdef DEBUG_PGN_130306
sprintf(tmp, "PGN %i : Angle du vent [%s] = %.1Lf° ", nmea.annuaire[rang].pgn, wind_ref_name, (((float) val_u16 * 0.0001) * CONVERSION_RAD_TO_DEG) );
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
break;
}
//------------------------------------------------------------------------------
case NMEA_PARSE_MODE_SUPPRESSION:
{
suppression_pgn_nmea(rang);
break;
}
//------------------------------------------------------------------------------
default: break;
}
return (0);
}
//-----------------------------------------------------------------------------**************************************************************************************
//-----------------------------------------------------------------------------**************************************************************************************
//
// Catalogue des PGNs :
//
//-----------------------------------------------------------------------------**************************************************************************************
//-----------------------------------------------------------------------------**************************************************************************************
// Pgn, Type, Période mini (x10ms), Fonction de parsing
const struct Struct_entree_catalogue_pgn_nmea catalogue_pgns[NMEA_MAX_NOMBRE_PGN + 1] = {
{ 126992, NMEA_PGN_TYPE_DEFAULT_SINGLE_FRAME, 1000, &Parse_pgn_126992 }, // System date/time, pri=3, period=1000
{ 129029, NMEA_PGN_TYPE_DEFAULT_FAST_PACKET, 480, &Parse_pgn_129029 }, // GNSS Position Data, pri=3, period=1000
{ 129540, NMEA_PGN_TYPE_DEFAULT_FAST_PACKET, 480, &Parse_pgn_129540 }, // GNSS Sats in View, pri=6, period=1000
{ 130306, NMEA_PGN_TYPE_DEFAULT_SINGLE_FRAME, 100, &Parse_pgn_130306 }, // Wind Speed, pri=2, period=100
{ 0, 0, 0, NULL } // Ligne de fin de catalogue (PGN = 0)
};
// Fonctions issues de NMEA2000.cpp : https://ttlappalainen.github.io/NMEA2000/_n_m_e_a2000_8cpp_source.html
/*
//-----------------------------------------------------------------------------
const unsigned long DefTransmitMessages[] PROGMEM = {
59392L, // ISO Acknowledgement, pri=6, period=NA
59904L, // ISO Request, pri=6, period=NA
#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
TP_DT, // Multi packet data transfer, TP.DT, pri=6, period=NA
TP_CM, // Multi packet connection management, TP.CM, pri=6, period=NA
#endif
60928L, // ISO Address Claim, pri=6, period=NA
#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
126208L, // NMEA Request/Command/Acknowledge group function, pri=3, period=NA
#endif
126464L, // PGN List (Transmit and Receive), pri=6, period=NA
#if !defined(N2K_NO_HEARTBEAT_SUPPORT)
126993L, // Heartbeat, pri=7, period=60000
#endif
126996L, // Product information, pri=6, period=NA
126998L, // Configuration information, pri=6, period=NA
0};
//-----------------------------------------------------------------------------
const unsigned long DefReceiveMessages[] PROGMEM = {
59392L, // ISO Acknowledgement, pri=6, period=NA
59904L, // ISO Request, pri=6, period=NA
#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
TP_DT, // Multi packet data transfer, TP.DT
TP_CM, // Multi packet connection management, TP.CM
#endif
60928L, // ISO Address Claim
65240L, // Commanded Address
#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
126208L, // NMEA Request/Command/Acknowledge group function
#endif
0};
//-----------------------------------------------------------------------------
bool IsSingleFrameSystemMessage(unsigned long PGN) {
switch (PGN) {
case 59392L: // ISO Acknowledgement
case TP_DT: // Multi packet data transfer, TP.DT
case TP_CM: // Multi packet connection management, TP.CM
case 59904L: // ISO Request
case 60928L: // ISO Address Claim
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool IsFastPacketSystemMessage(unsigned long PGN) {
switch (PGN) {
case 65240L: // Commanded Address
case 126208L: // NMEA Request/Command/Acknowledge group function
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool IsDefaultSingleFrameMessage(unsigned long PGN) {
switch (PGN) {
case 126993L: // Heartbeat, pri=7, period=60000
case 127245L: // Rudder, pri=2, period=100
case 127250L: // Vessel Heading, pri=2, period=100
case 127251L: // Rate of Turn, pri=2, period=100
case 127257L: // Attitude, pri=3, period=1000
case 127488L: // Engine parameters rapid, rapid Update, pri=2, period=100
case 127493L: // Transmission parameters: dynamic, pri=2, period=100
case 127501L: // Binary status report, pri=3, period=NA
case 127505L: // Fluid level, pri=6, period=2500
case 127508L: // Battery Status, pri=6, period=1500
case 128259L: // Boat speed, pri=2, period=1000
case 128267L: // Water depth, pri=3, period=1000
case 129025L: // Lat/lon rapid, pri=2, period=100
case 129026L: // COG SOG rapid, pri=2, period=250
case 129283L: // Cross Track Error, pri=3, period=1000
case 130310L: // Outside Environmental parameters, pri=5, period=500
case 130311L: // Environmental parameters, pri=5, period=500
case 130312L: // Temperature, pri=5, period=2000
case 130313L: // Humidity, pri=5, period=2000
case 130314L: // Pressure, pri=5, period=2000
case 130316L: // Temperature extended range, pri=5, period=2000
case 130576L: // Small Craft Status (Trim Tab position), pri=2, period=200
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool IsMandatoryFastPacketMessage(unsigned long PGN) {
switch (PGN) {
case 126464L: // PGN List (Transmit and Receive), pri=6, period=NA
case 126996L: // Product information, pri=6, period=NA
case 126998L: // Configuration information, pri=6, period=NA
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool IsDefaultFastPacketMessage(unsigned long PGN) {
switch (PGN) {
case 126983L: // Alert, pri=2, period=1000
case 126984L: // Alert Response, pri=2, period=NA
case 126985L: // Alert Text, pri=2, period=10000
case 126986L: // Alert Configuration, pri=2, period=NA
case 126987L: // Alert Threshold, pri=2, period=NA
case 126988L: // Alert Value, pri=2, period=10000
case 127233L: // Man Overboard Notification(MOB), pri=3, period=NA
case 127237L: // Heading/Track control, pri=2, period=250
case 127489L: // Engine parameters dynamic, pri=2, period=500
case 127490L: // Electric Drive Status (Dynamic), pri=1, period=1500
case 127491L: // Electric Energy Storage Status (Dynamic), pri=7, period=1500
case 127494L: // Electric Drive Information, pri=4, period=NA
case 127495L: // Electric Energy Storage Information, pri=6, period=NA
case 127496L: // Trip fuel consumption, vessel, pri=5, period=1000
case 127497L: // Trip fuel consumption, engine, pri=5, period=1000
case 127498L: // Engine parameters static, pri=5, period=NA
case 127503L: // AC Input Status, pri=6, period=1500
case 127504L: // AC Output Status, pri=6, period=1500
case 127506L: // DC Detailed status, pri=6, period=1500
case 127507L: // Charger status, pri=6, period=1500
case 127509L: // Inverter status, pri=6, period=1500
case 127510L: // Charger configuration status, pri=6, period=NA
case 127511L: // Inverter Configuration Status, pri=6, period=NA
case 127512L: // AGS configuration status, pri=6, period=NA
case 127513L: // Battery configuration status, pri=6, period=NA
case 127514L: // AGS Status, pri=6, period=1500
case 128275L: // Distance log, pri=6, period=1000
case 128520L: // Tracked Target Data, pri=2, period=1000
case 128538L: // Elevator car status, pri=6, period=100
case 129038L: // AIS Class A Position Report, pri=4, period=NA
case 129039L: // AIS Class B Position Report, pri=4, period=NA
case 129040L: // AIS Class B Extended Position Report, pri=4, period=NA
case 129041L: // AIS Aids to Navigation (AtoN) Report, pri=4, period=NA
case 129044L: // Datum, pri=6, period=10000
case 129045L: // User Datum Settings, pri=6, period=NA
case 129284L: // Navigation info, pri=3, period=1000
case 129285L: // Waypoint list, pri=3, period=NA
case 129301L: // Time to/from Mark, pri=3, period=1000
case 129302L: // Bearing and Distance between two Marks, pri=6, period=NA
case 129538L: // GNSS Control Status, pri=6, period=NA
case 129541L: // GPS Almanac Data, pri=6, period=NA
case 129542L: // GNSS Pseudorange Noise Statistics, pri=6, period=1000
case 129545L: // GNSS RAIM Output, pri=6, period=NA
case 129547L: // GNSS Pseudorange Error Statistics, pri=6, period=NA
case 129549L: // DGNSS Corrections, pri=6, period=NA
case 129551L: // GNSS Differential Correction Receiver Signal, pri=6, period=NA
case 129556L: // GLONASS Almanac Data, pri=6, period=NA
case 129792L: // AIS DGNSS Broadcast Binary Message, pri=6, period=NA
case 129793L: // AIS UTC and Date Report, pri=7, period=NA
case 129794L: // AIS Class A Static data, pri=6, period=NA
case 129795L: // AIS Addressed Binary Message, pri=5, period=NA
case 129796L: // AIS Acknowledge, pri=7, period=NA
case 129797L: // AIS Binary Broadcast Message, pri=5, period=NA
case 129798L: // AIS SAR Aircraft Position Report, pri=4, period=NA
case 129799L: // Radio Frequency/Mode/Power, pri=3, period=NA
case 129800L: // AIS UTC/Date Inquiry, pri=7, period=NA
case 129801L: // AIS Addressed Safety Related Message, pri=5, period=NA
case 129802L: // AIS Safety Related Broadcast Message, pri=5, period=NA
case 129803L: // AIS Interrogation PGN, pri=7, period=NA
case 129804L: // AIS Assignment Mode Command, pri=7, period=NA
case 129805L: // AIS Data Link Management Message, pri=7, period=NA
case 129806L: // AIS Channel Management, pri=7, period=NA
case 129807L: // AIS Group Assignment, pri=7, period=NA
case 129808L: // DSC Call Information, pri=8, period=NA
case 129809L: // AIS Class B Static Data: Part A, pri=6, period=NA
case 129810L: // AIS Class B Static Data Part B, pri=6, period=NA
case 129811L: // AIS Single Slot Binary Message, pri=5, period=NA
case 129812L: // AIS Multi Slot Binary Message, pri=5, period=NA
case 129813L: // AIS Long-Range Broadcast Message, pri=5, period=NA
case 130052L: // Loran-C TD Data, pri=3, period=1000
case 130053L: // Loran-C Range Data, pri=3, period=1000
case 130054L: // Loran-C Signal Data, pri=3, period=1000
case 130060L: // Label, pri=7, period=NA
case 130061L: // Channel Source Configuration, pri=7, period=NA
case 130064L: // Route and WP Service - Database List, pri=7, period=NA
case 130065L: // Route and WP Service - Route List, pri=7, period=NA
case 130066L: // Route and WP Service - Route/WP-List Attributes, pri=7, period=NA
case 130067L: // Route and WP Service - Route - WP Name & Position, pri=7, period=NA
case 130068L: // Route and WP Service - Route - WP Name, pri=7, period=NA
case 130069L: // Route and WP Service - XTE Limit & Navigation Method, pri=7, period=NA
case 130070L: // Route and WP Service - WP Comment, pri=7, period=NA
case 130071L: // Route and WP Service - Route Comment, pri=7, period=NA
case 130072L: // Route and WP Service - Database Comment, pri=7, period=NA
case 130073L: // Route and WP Service - Radius of Turn, pri=7, period=NA
case 130074L: // Route and WP Service - WP List - WP Name & Position, pri=7, period=NA
case 130320L: // Tide Station Data, pri=6, period=1000
case 130321L: // Salinity Station Data, pri=6, period=1000
case 130322L: // Current Station Data, pri=6, period=1000
case 130323L: // Meteorological Station Data, pri=6, period=1000
case 130324L: // Moored Buoy Station Data, pri=6, period=1000
case 130330L: // Lighting system settings, pri=7, period=NA
case 130561L: // Lighting zone, pri=7, period=NA
case 130562L: // Lighting scene, pri=7, period=NA
case 130563L: // Lighting device, pri=7, period=NA
case 130564L: // Lighting device enumeration, pri=7, period=NA
case 130565L: // Lighting color sequence, pri=7, period=NA
case 130566L: // Lighting program, pri=7, period=NA
case 130567L: // Watermaker Input Setting and Status, pri=6, period=2500
case 130577L: // Direction Data PGN, pri=3, period=1000
case 130578L: // Vessel Speed Components, pri=2, period=250
// Entertainment PGNs
case 130569L: // Current File and Status, pri=6, period=500
case 130570L: // Library Data File, pri=6, period=NA
case 130571L: // Library Data Group, pri=6, period=NA
case 130572L: // Library Data Search, pri=6, period=NA
case 130573L: // Supported Source Data, pri=6, period=NA
case 130574L: // Supported Zone Data, pri=6, period=NA
case 130580L: // System Configuration Status, pri=6, period=NA
case 130581L: // Zone Configuration Status, pri=6, period=NA
case 130583L: // Available Audio EQ Presets, pri=6, period=NA
case 130584L: // Bluetooth Devices, pri=6, period=NA
case 130586L: // Zone Configuration Status, pri=6, period=NA
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool IsProprietaryFastPacketMessage(unsigned long PGN) {
return ( PGN==126720L ) || ( 130816L<=PGN && PGN<=131071L );
}
bool tNMEA2000::IsProprietaryMessage(unsigned long PGN) {
return IsProprietaryFastPacketMessage(PGN) || ( PGN==61184L ) || ( 65280L<=PGN && PGN<=65535L );
}
*/
// Fonctions issues de :
// https://ttlappalainen.github.io/NMEA2000/_n2k_messages_8cpp_source.html
// https://ttlappalainen.github.io/NMEA2000/_n2k_maretron_8cpp_source.html
// https://ttlappalainen.github.io/NMEA2000/_n_m_e_a2000_8cpp_source.html (en fin de fichier)
// Voir peut-être N2kMessagesEnumToStr.h : https://ttlappalainen.github.io/NMEA2000/_n2k_messages_enum_to_str_8h_source.html
// const char* tN2kTimeSourceStrs[] = { "GPS", "GLONASS", "radio station", "local cesium clock", "local rubidium clock", "local crystal clock" };
// const char* tN2kFluidTypeStrs[] = { "Fuel", "Water", "Gray water", "Live well", "Oil", "Black water", "Gasoline Fuel", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Error", "Unavailable",};
/*
//-----------------------------------------------------------------------------
// ISO Request
bool ParseN2kPGN59904(const tN2kMsg &N2kMsg,
unsigned long &RequestedPGN)
{
int result=((N2kMsg.DataLen>=3) && (N2kMsg.DataLen<=8));
RequestedPGN=0;
if (result) {
int Index=0;
RequestedPGN=N2kMsg.Get3ByteUInt(Index);
}
return result;
}
//-----------------------------------------------------------------------------
// System time
bool ParseN2kPGN126992(const tN2kMsg &N2kMsg,
unsigned char &SID,
uint16_t &SystemDate,
double &SystemTime,
tN2kTimeSource &TimeSource)
{
if (N2kMsg.PGN!=126992L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
TimeSource=(tN2kTimeSource)(N2kMsg.GetByte(Index) & 0x0f);
SystemDate=N2kMsg.Get2ByteUInt(Index);
SystemTime=N2kMsg.Get4ByteUDouble(0.0001,Index);
return true;
}
//-----------------------------------------------------------------------------
// AIS Safety Related Broadcast Message
bool ParseN2kPGN129802(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &SourceID,
tN2kAISTransceiverInformation &AISTransceiverInformation,
char * SafetyRelatedText,
size_t &SafetyRelatedTextMaxSize)
{
if (N2kMsg.PGN!=129802L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index);
MessageID=(vb & 0x3f);
Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
SourceID = N2kMsg.Get4ByteUInt(Index) & 0x3fffffff;
AISTransceiverInformation = (tN2kAISTransceiverInformation)(N2kMsg.GetByte(Index) & 0x1f);
N2kMsg.GetVarStr(SafetyRelatedTextMaxSize, SafetyRelatedText, Index);
return true;
}
//-----------------------------------------------------------------------------
// Product Information
bool ParseN2kPGN126996(const tN2kMsg& N2kMsg,
unsigned short &N2kVersion,
unsigned short &ProductCode,
int ModelIDSize,
char *ModelID,
int SwCodeSize,
char *SwCode,
int ModelVersionSize,
char *ModelVersion,
int ModelSerialCodeSize,
char *ModelSerialCode,
unsigned char &CertificationLevel,
unsigned char &LoadEquivalency)
{
if (N2kMsg.PGN!=N2kPGNProductInformation) return false;
int Index=0;
N2kVersion=N2kMsg.Get2ByteUInt(Index);
ProductCode=N2kMsg.Get2ByteUInt(Index);
N2kMsg.GetStr(ModelIDSize,ModelID,Max_N2kModelID_len,0xff,Index);
N2kMsg.GetStr(SwCodeSize,SwCode,Max_N2kSwCode_len,0xff,Index);
N2kMsg.GetStr(ModelVersionSize,ModelVersion,Max_N2kModelVersion_len,0xff,Index);
N2kMsg.GetStr(ModelSerialCodeSize,ModelSerialCode,Max_N2kModelSerialCode_len,0xff,Index);
CertificationLevel=N2kMsg.GetByte(Index);
LoadEquivalency=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Configuration Information
bool ParseN2kPGN126998(const tN2kMsg& N2kMsg,
size_t &ManufacturerInformationSize,
char *ManufacturerInformation,
size_t &InstallationDescription1Size,
char *InstallationDescription1,
size_t &InstallationDescription2Size,
char *InstallationDescription2)
{
if (N2kMsg.PGN!=N2kPGNConfigurationInformation) return false;
int Index=0;
return ( N2kMsg.GetVarStr(InstallationDescription1Size,InstallationDescription1,Index) &&
N2kMsg.GetVarStr(InstallationDescription2Size,InstallationDescription2,Index) &&
N2kMsg.GetVarStr(ManufacturerInformationSize,ManufacturerInformation,Index) );
}
//-----------------------------------------------------------------------------
// Man Overboard Notification
bool ParseN2kPGN127233(const tN2kMsg &N2kMsg,
unsigned char &SID,
uint32_t &MobEmitterId,
tN2kMOBStatus &MOBStatus,
double &ActivationTime,
tN2kMOBPositionSource &PositionSource,
uint16_t &PositionDate,
double &PositionTime,
double &Latitude,
double &Longitude,
tN2kHeadingReference &COGReference,
double &COG,
double &SOG,
uint32_t &MMSI,
tN2kMOBEmitterBatteryStatus &MOBEmitterBatteryStatus)
{
if (N2kMsg.PGN!=127233L) return false;
int Index = 0;
SID = N2kMsg.GetByte(Index);
MobEmitterId = N2kMsg.Get4ByteUInt(Index);
MOBStatus = (tN2kMOBStatus)(N2kMsg.GetByte(Index) & 0x07);
ActivationTime = N2kMsg.Get4ByteUDouble(0.0001,Index);
PositionSource = (tN2kMOBPositionSource)(N2kMsg.GetByte(Index) & 0x07);
PositionDate = N2kMsg.Get2ByteUInt(Index);
PositionTime = N2kMsg.Get4ByteUDouble(0.0001,Index);
Latitude = N2kMsg.Get4ByteDouble(1e-7,Index);
Longitude = N2kMsg.Get4ByteDouble(1e-7,Index);
COGReference = (tN2kHeadingReference)(N2kMsg.GetByte(Index) & 0x03);
COG = N2kMsg.Get2ByteUDouble(0.0001,Index);
SOG = N2kMsg.Get2ByteUDouble(0.01,Index);
MMSI = N2kMsg.Get4ByteUInt(Index);
MOBEmitterBatteryStatus = (tN2kMOBEmitterBatteryStatus)(N2kMsg.GetByte(Index) & 0x07);
return true;
}
//-----------------------------------------------------------------------------
// Heading/Track control
// Angles should be in radians
bool ParseN2kPGN127237(const tN2kMsg &N2kMsg,
tN2kOnOff &RudderLimitExceeded,
tN2kOnOff &OffHeadingLimitExceeded,
tN2kOnOff &OffTrackLimitExceeded,
tN2kOnOff &Override,
tN2kSteeringMode &SteeringMode,
tN2kTurnMode &TurnMode,
tN2kHeadingReference &HeadingReference,
tN2kRudderDirectionOrder &CommandedRudderDirection,
double &CommandedRudderAngle,
double &HeadingToSteerCourse,
double &Track,
double &RudderLimit,
double &OffHeadingLimit,
double &RadiusOfTurnOrder,
double &RateOfTurnOrder,
double &OffTrackLimit,
double &VesselHeading)
{
if (N2kMsg.PGN!=127237L) return false;
int Index = 0;
unsigned char v;
v = N2kMsg.GetByte(Index);
RudderLimitExceeded = (tN2kOnOff)(v & 0x03);
OffHeadingLimitExceeded = (tN2kOnOff)((v >> 2) & 0x03);
OffTrackLimitExceeded = (tN2kOnOff)((v >> 4) & 0x03);
Override = (tN2kOnOff)((v >> 6) & 0x03);
v = N2kMsg.GetByte(Index);
SteeringMode = (tN2kSteeringMode)(v & 0x07);
TurnMode = (tN2kTurnMode)((v >> 3) & 0x07);
HeadingReference = (tN2kHeadingReference)((v >> 6) & 0x03);
CommandedRudderDirection = (tN2kRudderDirectionOrder)((N2kMsg.GetByte(Index) >> 5) & 0x07);
CommandedRudderAngle = N2kMsg.Get2ByteDouble(0.0001,Index);
HeadingToSteerCourse = N2kMsg.Get2ByteUDouble(0.0001,Index);
Track = N2kMsg.Get2ByteUDouble(0.0001,Index);
RudderLimit = N2kMsg.Get2ByteUDouble(0.0001,Index);
OffHeadingLimit = N2kMsg.Get2ByteUDouble(0.0001,Index);
RadiusOfTurnOrder = N2kMsg.Get2ByteDouble(1,Index);
RateOfTurnOrder = N2kMsg.Get2ByteDouble(3.125e-5,Index);
OffTrackLimit = N2kMsg.Get2ByteDouble(1,Index);
VesselHeading = N2kMsg.Get2ByteUDouble(0.0001,Index);
return true;
}
//-----------------------------------------------------------------------------
// Rudder
// Angles should be in radians
bool ParseN2kPGN127245(const tN2kMsg &N2kMsg,
double &RudderPosition,
unsigned char &Instance,
tN2kRudderDirectionOrder &RudderDirectionOrder,
double &AngleOrder)
{
if (N2kMsg.PGN!=127245L) return false;
int Index=0;
Instance=N2kMsg.GetByte(Index);
RudderDirectionOrder=(tN2kRudderDirectionOrder)(N2kMsg.GetByte(Index)&0x7);
AngleOrder=N2kMsg.Get2ByteDouble(0.0001,Index);
RudderPosition=N2kMsg.Get2ByteDouble(0.0001,Index);
return true;
}
//-----------------------------------------------------------------------------
// Vessel Heading
// Angles should be in radians
bool ParseN2kPGN127250(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &Heading,
double &Deviation,
double &Variation,
tN2kHeadingReference &ref)
{
if (N2kMsg.PGN!=127250L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
Heading=N2kMsg.Get2ByteUDouble(0.0001,Index);
Deviation=N2kMsg.Get2ByteDouble(0.0001,Index);
Variation=N2kMsg.Get2ByteDouble(0.0001,Index);
ref=(tN2kHeadingReference)(N2kMsg.GetByte(Index)&0x03);
return true;
}
//-----------------------------------------------------------------------------
// Rate of turn
// Angles should be in radians
bool ParseN2kPGN127251(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &RateOfTurn)
{
if (N2kMsg.PGN!=127251L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
RateOfTurn=N2kMsg.Get4ByteDouble(3.125E-08,Index); //1e-6/32.0
return true;
}
//-----------------------------------------------------------------------------
// Heave
// - SID Sequence ID. If your device is e.g. boat speed and heading at same time, you can set same SID for different messages
// to indicate that they are measured at same time.
// - Heave Vertical displacement perpendicular to the earths surface in meters
// - Delay Delay added by calculations in seconds
// - DelaySource tN2kDelaySource
// Output:
// - N2kMsg NMEA2000 message ready to be send.
bool ParseN2kPGN127252(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &Heave,
double &Delay,
tN2kDelaySource &DelaySource)
{
if (N2kMsg.PGN!=127252L) return false;
int Index = 0;
SID = N2kMsg.GetByte(Index);
Heave = N2kMsg.Get2ByteDouble(0.01,Index);
Delay = N2kMsg.Get2ByteUDouble(0.01,Index);
uint8_t v=N2kMsg.GetByte(Index);
DelaySource=(tN2kDelaySource)(v & 0x0f);
return true;
}
//-----------------------------------------------------------------------------
// Attitude
// Input:
// - SID Sequence ID. If your device is e.g. boat speed and heading at same time, you can set same SID for different messages
// to indicate that they are measured at same time.
// - Yaw Heading in radians.
// - Pitch Pitch in radians. Positive, when your bow rises.
// - Roll Roll in radians. Positive, when tilted right.
// Output:
// - N2kMsg NMEA2000 message ready to be send.
bool ParseN2kPGN127257(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &Yaw,
double &Pitch,
double &Roll)
{
if (N2kMsg.PGN!=127257L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
Yaw=N2kMsg.Get2ByteDouble(0.0001,Index);
Pitch=N2kMsg.Get2ByteDouble(0.0001,Index);
Roll=N2kMsg.Get2ByteDouble(0.0001,Index);
return true;
}
//-----------------------------------------------------------------------------
// Magnetic variation
bool ParseN2kPGN127258(const tN2kMsg &N2kMsg,
unsigned char &SID,
tN2kMagneticVariation &Source,
uint16_t &DaysSince1970,
double &Variation)
{
if (N2kMsg.PGN!=127258L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
Source=(tN2kMagneticVariation) (N2kMsg.GetByte(Index) & 0x0f);
DaysSince1970=N2kMsg.Get2ByteUInt(Index);
Variation=N2kMsg.Get2ByteDouble(0.0001, Index);
return true;
}
//-----------------------------------------------------------------------------
// Engine rapid param
bool ParseN2kPGN127488(const tN2kMsg &N2kMsg,
unsigned char &EngineInstance,
double &EngineSpeed,
double &EngineBoostPressure,
int8_t &EngineTiltTrim)
{
if (N2kMsg.PGN!=127488L) return false;
int Index=0;
EngineInstance=N2kMsg.GetByte(Index);
EngineSpeed=N2kMsg.Get2ByteUDouble(0.25,Index);
EngineBoostPressure=N2kMsg.Get2ByteUDouble(100,Index);
EngineTiltTrim=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Engine parameters dynamic
bool ParseN2kPGN127489(const tN2kMsg &N2kMsg,
unsigned char &EngineInstance,
double &EngineOilPress,
double &EngineOilTemp,
double &EngineCoolantTemp,
double &AltenatorVoltage,
double &FuelRate,
double &EngineHours,
double &EngineCoolantPress,
double &EngineFuelPress,
int8_t &EngineLoad,
int8_t &EngineTorque,
tN2kEngineDiscreteStatus1 &Status1,
tN2kEngineDiscreteStatus2 &Status2)
{
if (N2kMsg.PGN != 127489L) return false;
int Index = 0;
EngineInstance = N2kMsg.GetByte(Index);
EngineOilPress = N2kMsg.Get2ByteUDouble(100, Index);
EngineOilTemp = N2kMsg.Get2ByteUDouble(0.1, Index);
EngineCoolantTemp = N2kMsg.Get2ByteUDouble(0.01, Index);
AltenatorVoltage = N2kMsg.Get2ByteDouble(0.01, Index);
FuelRate = N2kMsg.Get2ByteDouble(0.1, Index);
EngineHours = N2kMsg.Get4ByteUDouble(1, Index);
EngineCoolantPress=N2kMsg.Get2ByteUDouble(100, Index);
EngineFuelPress=N2kMsg.Get2ByteUDouble(1000, Index);
N2kMsg.GetByte(Index); // reserved
Status1=N2kMsg.Get2ByteUInt(Index); // Discrete Status 1
Status2=N2kMsg.Get2ByteUInt(Index); // Discrete Status 2
EngineLoad=N2kMsg.GetByte(Index);
EngineTorque=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Transmission parameters, dynamic
bool ParseN2kPGN127493(const tN2kMsg &N2kMsg,
unsigned char &EngineInstance,
tN2kTransmissionGear &TransmissionGear,
double &OilPressure,
double &OilTemperature,
unsigned char &DiscreteStatus1)
{
if (N2kMsg.PGN!=127493L) return false;
int Index=0;
EngineInstance=N2kMsg.GetByte(Index);
TransmissionGear=(tN2kTransmissionGear)(N2kMsg.GetByte(Index) & 0x03);
OilPressure=N2kMsg.Get2ByteUDouble(100,Index);
OilTemperature=N2kMsg.Get2ByteUDouble(0.1,Index);
DiscreteStatus1=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Trip Parameters, Engine
bool ParseN2kPGN127497(const tN2kMsg &N2kMsg,
unsigned char &EngineInstance,
double &TripFuelUsed,
double &FuelRateAverage,
double &FuelRateEconomy,
double &InstantaneousFuelEconomy)
{
if (N2kMsg.PGN!=127497L) return false;
int Index=0;
EngineInstance=N2kMsg.GetByte(Index);
TripFuelUsed=N2kMsg.Get2ByteUDouble(1,Index);
FuelRateAverage=N2kMsg.Get2ByteDouble(0.1,Index);
FuelRateEconomy=N2kMsg.Get2ByteDouble(0.1,Index);
InstantaneousFuelEconomy=N2kMsg.Get2ByteDouble(0.1,Index);
return true;
}
//-----------------------------------------------------------------------------
// Binary status
//-----------------------------------------------------------------------------
tN2kOnOff N2kGetStatusOnBinaryStatus(tN2kBinaryStatus BankStatus, uint8_t ItemIndex) {
ItemIndex--;
if (ItemIndex>27) return N2kOnOff_Unavailable;
return (tN2kOnOff)((BankStatus >> (2*ItemIndex)) & 0x03);
}
//-----------------------------------------------------------------------------
void N2kSetStatusBinaryOnStatus(tN2kBinaryStatus &BankStatus, tN2kOnOff ItemStatus, uint8_t ItemIndex) {
ItemIndex--;
if (ItemIndex>27) return;
tN2kBinaryStatus Mask = ~((tN2kBinaryStatus)3 << (2*ItemIndex));
BankStatus = (BankStatus & Mask) | ((tN2kBinaryStatus)ItemStatus << (2*ItemIndex));
}
//-----------------------------------------------------------------------------
void SetN2kPGN127501(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) {
N2kMsg.SetPGN(127501L);
N2kMsg.Priority=3;
BankStatus = (BankStatus << 8) | DeviceBankInstance;
N2kMsg.AddUInt64(BankStatus);
}
//-----------------------------------------------------------------------------
// Binary status report
void SetN2kPGN127501(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance
,tN2kOnOff Status1
,tN2kOnOff Status2
,tN2kOnOff Status3
,tN2kOnOff Status4
) {
tN2kBinaryStatus BankStatus;
N2kResetBinaryStatus(BankStatus);
BankStatus = (BankStatus << 2) | Status4;
BankStatus = (BankStatus << 2) | Status3;
BankStatus = (BankStatus << 2) | Status2;
BankStatus = (BankStatus << 2) | Status1;
SetN2kPGN127501(N2kMsg,DeviceBankInstance,BankStatus);
}
//-----------------------------------------------------------------------------
bool ParseN2kPGN127501(const tN2kMsg &N2kMsg,
unsigned char &DeviceBankInstance,
tN2kOnOff &Status1,
tN2kOnOff &Status2,
tN2kOnOff &Status3,
tN2kOnOff &Status4)
{
if (N2kMsg.PGN!=127501L) return false;
int Index=0;
DeviceBankInstance=N2kMsg.GetByte(Index);
unsigned char b=N2kMsg.GetByte(Index);
Status1=(tN2kOnOff)(b & 0x03);
b>>=2; Status2=(tN2kOnOff)(b & 0x03);
b>>=2; Status3=(tN2kOnOff)(b & 0x03);
b>>=2; Status4=(tN2kOnOff)(b & 0x03);
return true;
}
//-----------------------------------------------------------------------------
bool ParseN2kPGN127501(const tN2kMsg &N2kMsg,
unsigned char &DeviceBankInstance,
tN2kBinaryStatus &BankStatus)
{
if (N2kMsg.PGN!=127501L) return false;
int Index=0;
BankStatus=N2kMsg.GetUInt64(Index);
DeviceBankInstance = BankStatus & 0xff;
BankStatus>>=8;
return true;
}
//-----------------------------------------------------------------------------
// PGN 127502 - Switch Bank Control
bool ParseN2kPGN127502(const tN2kMsg &N2kMsg,
unsigned char &TargetBankInstance,
tN2kBinaryStatus &BankStatus)
{
if (N2kMsg.PGN!=127502L) return false;
int Index=0;
BankStatus=N2kMsg.GetUInt64(Index);
TargetBankInstance = BankStatus & 0xff;
BankStatus>>=8;
return true;
}
//-----------------------------------------------------------------------------
// Fluid level
bool ParseN2kPGN127505(const tN2kMsg &N2kMsg,
unsigned char &Instance,
tN2kFluidType &FluidType,
double &Level,
double &Capacity)
{
if (N2kMsg.PGN!=127505L) return false;
int Index=0;
unsigned char IFt=N2kMsg.GetByte(Index);
Instance=IFt&0x0f;
FluidType=(tN2kFluidType)((IFt>>4)&0x0f);
Level=N2kMsg.Get2ByteDouble(0.004,Index);
Capacity=N2kMsg.Get4ByteUDouble(0.1,Index);
return true;
}
//-----------------------------------------------------------------------------
// DC Detailed Status
bool ParseN2kPGN127506(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &DCInstance,
tN2kDCType &DCType,
uint8_t &StateOfCharge,
uint8_t &StateOfHealth,
double &TimeRemaining,
double &RippleVoltage,
double &Capacity)
{
if (N2kMsg.PGN!=127506L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
DCInstance=N2kMsg.GetByte(Index);
DCType=(tN2kDCType)(N2kMsg.GetByte(Index));
StateOfCharge=N2kMsg.GetByte(Index);
StateOfHealth=N2kMsg.GetByte(Index);
TimeRemaining=N2kMsg.Get2ByteUDouble(60,Index);
RippleVoltage=N2kMsg.Get2ByteUDouble(0.001,Index);
Capacity=N2kMsg.Get2ByteUDouble(3600,Index);
return true;
}
//-----------------------------------------------------------------------------
// Charger Status
// Input:
// - Instance ChargerInstance.
// - BatteryInstance BatteryInstance.
// - Operating State see. tN2kChargeState
// - Charger Mode see. tN2kChargerMode
// - Charger Enable/Disable boolean
// - Equalization Pending boolean
// - Equalization Time Remaining double seconds
//
bool ParseN2kPGN127507(const tN2kMsg &N2kMsg,
unsigned char &Instance,
unsigned char &BatteryInstance,
tN2kChargeState &ChargeState,
tN2kChargerMode &ChargerMode,
tN2kOnOff &Enabled,
tN2kOnOff &EqualizationPending,
double &EqualizationTimeRemaining)
{
if (N2kMsg.PGN!=127507UL) return false;
int Index=0;
Instance=N2kMsg.GetByte(Index);
BatteryInstance=N2kMsg.GetByte(Index);
unsigned char v;
v=N2kMsg.GetByte(Index);
ChargeState=(tN2kChargeState)(v&0x0f);
ChargerMode=(tN2kChargerMode)((v>>4)&0x0f);
v=N2kMsg.GetByte(Index);
Enabled=(tN2kOnOff)(v&0x03);
EqualizationPending=(tN2kOnOff)((v>>2)&0x03);
EqualizationTimeRemaining=N2kMsg.Get2ByteUDouble(60,Index);
return true;
}
//-----------------------------------------------------------------------------
// Battery Status
// Temperatures should be in Kelvins
bool ParseN2kPGN127508(const tN2kMsg &N2kMsg,
unsigned char &BatteryInstance,
double &BatteryVoltage,
double &BatteryCurrent,
double &BatteryTemperature,
unsigned char &SID)
{
if (N2kMsg.PGN!=127508L) return false;
int Index=0;
BatteryInstance=N2kMsg.GetByte(Index);
BatteryVoltage=N2kMsg.Get2ByteDouble(0.01,Index);
BatteryCurrent=N2kMsg.Get2ByteDouble(0.1,Index);
BatteryTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
SID=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Battery Configuration Status
bool ParseN2kPGN127513(const tN2kMsg &N2kMsg,
unsigned char &BatInstance,
tN2kBatType &BatType,
tN2kBatEqSupport &SupportsEqual,
tN2kBatNomVolt &BatNominalVoltage,
tN2kBatChem &BatChemistry,
double &BatCapacity,
int8_t &BatTemperatureCoefficient,
double &PeukertExponent,
int8_t &ChargeEfficiencyFactor)
{
if (N2kMsg.PGN!=127513L) return false;
int Index=0;
unsigned char v;
BatInstance = N2kMsg.GetByte(Index);
v = N2kMsg.GetByte(Index); BatType=(tN2kBatType)(v & 0x0f); SupportsEqual=(tN2kBatEqSupport)((v>>4) & 0x03);
v = N2kMsg.GetByte(Index); BatNominalVoltage=(tN2kBatNomVolt)(v & 0x0f); BatChemistry=(tN2kBatChem)((v>>4) & 0x0f);
BatCapacity=N2kMsg.Get2ByteDouble(3600,Index);
BatTemperatureCoefficient=N2kMsg.GetByte(Index);
PeukertExponent=N2kMsg.Get1ByteUDouble(0.002,Index); PeukertExponent+=1;
ChargeEfficiencyFactor=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Leeway
bool ParseN2kPGN128000(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &Leeway)
{
if (N2kMsg.PGN!=128000L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
Leeway=N2kMsg.Get2ByteDouble(0.0001,Index);
return true;
}
//-----------------------------------------------------------------------------
// Boat speed
bool ParseN2kPGN128259(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &WaterReferenced,
double &GroundReferenced,
tN2kSpeedWaterReferenceType &SWRT)
{
if (N2kMsg.PGN!=128259L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
WaterReferenced=N2kMsg.Get2ByteUDouble(0.01,Index);
GroundReferenced=N2kMsg.Get2ByteUDouble(0.01,Index);
SWRT=(tN2kSpeedWaterReferenceType)(N2kMsg.GetByte(Index)&0x0F);
return true;
}
//-----------------------------------------------------------------------------
// Water depth
bool ParseN2kPGN128267(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &DepthBelowTransducer,
double &Offset,
double &Range)
{
if (N2kMsg.PGN!=128267L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
DepthBelowTransducer=N2kMsg.Get4ByteUDouble(0.01,Index);
Offset=N2kMsg.Get2ByteDouble(0.001,Index);
Range=N2kMsg.Get1ByteUDouble(10,Index);
return true;
}
//-----------------------------------------------------------------------------
// Distance log
bool ParseN2kPGN128275(const tN2kMsg &N2kMsg,
uint16_t &DaysSince1970,
double &SecondsSinceMidnight,
uint32_t &Log,
uint32_t &TripLog)
{
if (N2kMsg.PGN!=128275L) return false;
int Index=0;
DaysSince1970=N2kMsg.Get2ByteUInt(Index);
SecondsSinceMidnight=N2kMsg.Get4ByteUDouble(0.0001,Index);
Log=N2kMsg.Get4ByteUDouble(1,Index);
TripLog=N2kMsg.Get4ByteUDouble(1,Index);
return true;
}
//-----------------------------------------------------------------------------
// PGN128776 - Windlass Control Status
//
bool ParseN2kPGN128776(
const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &WindlassIdentifier,
tN2kWindlassDirectionControl &WindlassDirectionControl,
unsigned char &SpeedControl,
tN2kSpeedType &SpeedControlType,
tN2kGenericStatusPair &AnchorDockingControl,
tN2kGenericStatusPair &PowerEnable,
tN2kGenericStatusPair &MechanicalLock,
tN2kGenericStatusPair &DeckAndAnchorWash,
tN2kGenericStatusPair &AnchorLight,
double &CommandTimeout,
tN2kWindlassControlEvents &WindlassControlEvents)
{
if (N2kMsg.PGN!=128776L) return false;
int Index = 0;
unsigned char field;
SID = N2kMsg.GetByte(Index);
WindlassIdentifier = N2kMsg.GetByte(Index);
field = N2kMsg.GetByte(Index);
WindlassDirectionControl = (tN2kWindlassDirectionControl) (field & 0x03);
AnchorDockingControl = (tN2kGenericStatusPair) ((field >> 2) & 0x03);
SpeedControlType = (tN2kSpeedType) ((field >> 4) & 0x03);
SpeedControl = N2kMsg.GetByte(Index);
field = N2kMsg.GetByte(Index);
PowerEnable = (tN2kGenericStatusPair) (field & 0x03);
MechanicalLock = (tN2kGenericStatusPair) ((field >> 2) & 0x03);
DeckAndAnchorWash = (tN2kGenericStatusPair) ((field >> 4) & 0x03);
AnchorLight = (tN2kGenericStatusPair) ((field >> 6) & 0x03);
CommandTimeout = N2kMsg.Get1ByteUDouble(0.005, Index);
WindlassControlEvents.SetEvents(N2kMsg.GetByte(Index));
return true;
}
//-----------------------------------------------------------------------------
// PGN128777 Windlass Operating Status
//
bool ParseN2kPGN128777(
const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &WindlassIdentifier,
double &RodeCounterValue,
double &WindlassLineSpeed,
tN2kWindlassMotionStates &WindlassMotionStatus,
tN2kRodeTypeStates &RodeTypeStatus,
tN2kAnchorDockingStates &AnchorDockingStatus,
tN2kWindlassOperatingEvents &WindlassOperatingEvents)
{
if (N2kMsg.PGN!=128777L) return false;
int Index = 0;
unsigned char field;
SID = N2kMsg.GetByte(Index);
WindlassIdentifier = N2kMsg.GetByte(Index);
field = N2kMsg.GetByte(Index);
WindlassMotionStatus = (tN2kWindlassMotionStates) (field & 0x03);
RodeTypeStatus = (tN2kRodeTypeStates) ((field >> 2) & 0x03);
RodeCounterValue = N2kMsg.Get2ByteUDouble(0.1, Index);
WindlassLineSpeed = N2kMsg.Get2ByteUDouble(0.01, Index);
field = N2kMsg.GetByte(Index);
AnchorDockingStatus = (tN2kAnchorDockingStates) (field & 0x03);
WindlassOperatingEvents.SetEvents(field >> 2);
return true;
}
//-----------------------------------------------------------------------------
// PGN128778 - Windlass Monitoring Status
//
bool ParseN2kPGN128778(
const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &WindlassIdentifier,
double &TotalMotorTime,
double &ControllerVoltage,
double &MotorCurrent,
tN2kWindlassMonitoringEvents &WindlassMonitoringEvents)
{
if (N2kMsg.PGN!=128778L) return false;
int Index = 0;
SID = N2kMsg.GetByte(Index);
WindlassIdentifier = N2kMsg.GetByte(Index);
WindlassMonitoringEvents.SetEvents(N2kMsg.GetByte(Index));
ControllerVoltage = N2kMsg.Get1ByteUDouble(0.2, Index);
MotorCurrent = N2kMsg.Get1ByteUDouble(1.0, Index);
TotalMotorTime = N2kMsg.Get2ByteUDouble(60.0, Index);
return true;
}
//-----------------------------------------------------------------------------
// Lat long rapid
bool ParseN2kPGN129025(const tN2kMsg &N2kMsg,
double &Latitude,
double &Longitude)
{
if (N2kMsg.PGN!=129025L) return false;
int Index = 0;
Latitude=N2kMsg.Get4ByteDouble(1e-7, Index);
Longitude=N2kMsg.Get4ByteDouble(1e-7, Index);
return true;
}
//-----------------------------------------------------------------------------
// COG SOG rapid
// COG should be in radians
// SOG should be in m/s
bool ParseN2kPGN129026(const tN2kMsg &N2kMsg,
unsigned char &SID,
tN2kHeadingReference &ref,
double &COG,
double &SOG)
{
if (N2kMsg.PGN!=129026L) return false;
int Index=0;
unsigned char b;
SID=N2kMsg.GetByte(Index);
b=N2kMsg.GetByte(Index); ref=(tN2kHeadingReference)( b & 0x03 );
COG=N2kMsg.Get2ByteUDouble(0.0001,Index);
SOG=N2kMsg.Get2ByteUDouble(0.01,Index);
return true;
}
//-----------------------------------------------------------------------------
// GNSS Position Data
bool ParseN2kPGN129029(const tN2kMsg &N2kMsg,
unsigned char &SID,
uint16_t &DaysSince1970,
double &SecondsSinceMidnight,
double &Latitude,
double &Longitude,
double &Altitude,
tN2kGNSStype &GNSStype,
tN2kGNSSmethod &GNSSmethod,
uint8_t &nSatellites,
double &HDOP,
double &PDOP,
double &GeoidalSeparation,
uint8_t &nReferenceStations,
tN2kGNSStype &ReferenceStationType,
uint16_t &ReferenceSationID,double &AgeOfCorrection)
{
if (N2kMsg.PGN!=129029L) return false;
int Index=0;
unsigned char vb;
int16_t vi;
SID=N2kMsg.GetByte(Index);
DaysSince1970=N2kMsg.Get2ByteUInt(Index);
SecondsSinceMidnight=N2kMsg.Get4ByteUDouble(0.0001,Index);
Latitude=N2kMsg.Get8ByteDouble(1e-16,Index);
Longitude=N2kMsg.Get8ByteDouble(1e-16,Index);
Altitude=N2kMsg.Get8ByteDouble(1e-6,Index);
vb=N2kMsg.GetByte(Index); GNSStype=(tN2kGNSStype)(vb & 0x0f); GNSSmethod=(tN2kGNSSmethod)((vb>>4) & 0x0f);
vb=N2kMsg.GetByte(Index); // Integrity 2 bit, reserved 6 bits
nSatellites=N2kMsg.GetByte(Index);
HDOP=N2kMsg.Get2ByteDouble(0.01,Index);
PDOP=N2kMsg.Get2ByteDouble(0.01,Index);
GeoidalSeparation=N2kMsg.Get4ByteDouble(0.01,Index);
nReferenceStations=N2kMsg.GetByte(Index);
if (nReferenceStations!=N2kUInt8NA && nReferenceStations>0) {
// Note that we return real number of stations, but we only have variabes for one.
vi=N2kMsg.Get2ByteUInt(Index); ReferenceStationType=(tN2kGNSStype)(vi & 0x0f); ReferenceSationID=(vi>>4);
AgeOfCorrection=N2kMsg.Get2ByteUDouble(0.01,Index);
} else {
ReferenceStationType = N2kGNSSt_GPS;
ReferenceSationID = N2kInt16NA;
AgeOfCorrection = N2kDoubleNA;
}
return true;
}
//-----------------------------------------------------------------------------
// Date,Time & Local offset
bool ParseN2kPGN129033(const tN2kMsg &N2kMsg,
uint16_t &DaysSince1970,
double &SecondsSinceMidnight,
int16_t &LocalOffset)
{
if ( N2kMsg.PGN != 129033L ) return false;
int Index = 0;
DaysSince1970 = N2kMsg.Get2ByteUInt(Index);
SecondsSinceMidnight = N2kMsg.Get4ByteUDouble(0.0001, Index);
LocalOffset = N2kMsg.Get2ByteInt(Index);
return true;
}
//-----------------------------------------------------------------------------
// GNSS DOP data
bool ParseN2kPgn129539(const tN2kMsg& N2kMsg,
unsigned char& SID,
tN2kGNSSDOPmode& DesiredMode,
tN2kGNSSDOPmode& ActualMode,
double& HDOP,
double& VDOP,
double& TDOP)
{
if(N2kMsg.PGN != 129539L) return false;
unsigned char modes;
int Index = 0;
SID = N2kMsg.GetByte(Index);
modes = N2kMsg.GetByte(Index);
DesiredMode = (tN2kGNSSDOPmode)( modes & 0x07 );
ActualMode = (tN2kGNSSDOPmode)( (modes>>3) & 0x07 );
HDOP = N2kMsg.Get2ByteDouble(0.01, Index);
VDOP = N2kMsg.Get2ByteDouble(0.01, Index);
TDOP = N2kMsg.Get2ByteDouble(0.01, Index);
return true;
}
//-----------------------------------------------------------------------------
// GNSS Satellites in View
#define MaxSatelliteInfoCount 18 // Fast packet can hold this max stellites. Maybe later more with TP message.
bool ParseN2kPGN129540(const tN2kMsg& N2kMsg,
unsigned char& SID,
tN2kRangeResidualMode &Mode,
uint8_t& NumberOfSVs)
{
if(N2kMsg.PGN != 129540L) return false;
int Index = 0;
SID = N2kMsg.GetByte(Index);
Mode = (tN2kRangeResidualMode)(N2kMsg.GetByte(Index) & 0x03);
NumberOfSVs = N2kMsg.GetByte(Index);
return true;
}
bool ParseN2kPGN129540(const tN2kMsg& N2kMsg,
uint8_t SVIndex,
tSatelliteInfo& SatelliteInfo)
{
if(N2kMsg.PGN != 129540L) return false;
int Index = 2;
uint8_t NumberOfSVs=N2kMsg.GetByte(Index);
bool ret=( NumberOfSVs<MaxSatelliteInfoCount && SVIndex<NumberOfSVs );
if ( ret ) {
Index=3+SVIndex*12;
SatelliteInfo.PRN=N2kMsg.GetByte(Index);
SatelliteInfo.Elevation=N2kMsg.Get2ByteDouble(1e-4L,Index);
SatelliteInfo.Azimuth=N2kMsg.Get2ByteUDouble(1e-4L,Index);
SatelliteInfo.SNR=N2kMsg.Get2ByteDouble(1e-2L,Index);
SatelliteInfo.RangeResiduals=N2kMsg.Get4ByteDouble(1e-5L,Index);;
SatelliteInfo.UsageStatus=(tN2kPRNUsageStatus)(N2kMsg.GetByte(Index) & 0x0f);
} else {
SatelliteInfo.PRN=0xff;
SatelliteInfo.Elevation=N2kDoubleNA;
SatelliteInfo.Azimuth=N2kDoubleNA;
SatelliteInfo.SNR=N2kDoubleNA;
SatelliteInfo.RangeResiduals=N2kDoubleNA;
SatelliteInfo.UsageStatus=N2kDD124_Unavailable;
}
return ret;
}
//-----------------------------------------------------------------------------
// AIS position report (class A 129038)
// Latitude and Longitude in degrees (1e7)
// COG and Heading in radians (1e4)
bool ParseN2kPGN129038(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &UserID,
double &Latitude,
double &Longitude,
bool &Accuracy,
bool &RAIM,
uint8_t &Seconds,
double &COG,
double &SOG,
double &Heading,
double &ROT,
tN2kAISNavStatus &NavStatus)
{
if (N2kMsg.PGN!=129038L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
UserID=N2kMsg.Get4ByteUInt(Index);
Longitude=N2kMsg.Get4ByteDouble(1e-07, Index);
Latitude=N2kMsg.Get4ByteDouble(1e-07, Index);
vb=N2kMsg.GetByte(Index); Accuracy=(vb & 0x01); RAIM=(vb>>1 & 0x01); Seconds=(vb>>2 & 0x3f);
COG=N2kMsg.Get2ByteUDouble(1e-04, Index);
SOG=N2kMsg.Get2ByteUDouble(0.01, Index);
vb=N2kMsg.GetByte(Index); // Communication State (19 bits)
vb=N2kMsg.GetByte(Index);
vb=N2kMsg.GetByte(Index); // AIS transceiver information (5 bits)
Heading=N2kMsg.Get2ByteUDouble(1e-04, Index);
ROT=N2kMsg.Get2ByteDouble(3.125E-05, Index); // 1e-3/32.0
vb=N2kMsg.GetByte(Index); NavStatus=(tN2kAISNavStatus)(vb & 0x0f);
vb=N2kMsg.GetByte(Index); // Reserved
return true;
}
//-----------------------------------------------------------------------------
// AIS position report (class B 129039)
bool ParseN2kPGN129039(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &UserID,
double &Latitude,
double &Longitude,
bool &Accuracy,
bool &RAIM,
uint8_t &Seconds,
double &COG,
double &SOG,
tN2kAISTransceiverInformation &AISTransceiverInformation,
double &Heading,
tN2kAISUnit &Unit,
bool &Display,
bool &DSC,
bool &Band,
bool &Msg22,
tN2kAISMode &Mode, bool &State)
{
if (N2kMsg.PGN!=129039L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
UserID=N2kMsg.Get4ByteUInt(Index);
Longitude=N2kMsg.Get4ByteDouble(1e-07, Index);
Latitude=N2kMsg.Get4ByteDouble(1e-07, Index);
vb=N2kMsg.GetByte(Index); Accuracy=(vb & 0x01); RAIM=(vb>>1 & 0x01); Seconds=(vb>>2 & 0x3f);
COG=N2kMsg.Get2ByteUDouble(1e-04, Index);
SOG=N2kMsg.Get2ByteUDouble(0.01, Index);
vb=N2kMsg.GetByte(Index); // Communication State (19 bits)
vb=N2kMsg.GetByte(Index);
vb=N2kMsg.GetByte(Index); // AIS transceiver information (5 bits)
AISTransceiverInformation = (tN2kAISTransceiverInformation)((vb >> 3) & 0x1f);
Heading=N2kMsg.Get2ByteUDouble(1e-04, Index);
vb=N2kMsg.GetByte(Index); // Regional application
vb=N2kMsg.GetByte(Index);
Unit=(tN2kAISUnit)(vb>>2 & 0x01); Display=(vb>>3 & 0x01); DSC=(vb>>4 & 0x01);
Band=(vb>>5 & 0x01); Msg22=(vb>>6 & 0x01); Mode=(tN2kAISMode)(vb>>7 & 0x01);
vb=N2kMsg.GetByte(Index); State=(vb & 0x01);
return true;
}
//-----------------------------------------------------------------------------
// AIS Aids to Navigation (AtoN) Report (PGN 129041)
bool ParseN2kPGN129041(const tN2kMsg &N2kMsg,
tN2kAISAtoNReportData &N2kData)
{
if (N2kMsg.PGN!=129041L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); N2kData.MessageID=(vb & 0x3f); N2kData.Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
N2kData.UserID=N2kMsg.Get4ByteUInt(Index);
N2kData.Longitude=N2kMsg.Get4ByteDouble(1e-07, Index);
N2kData.Latitude=N2kMsg.Get4ByteDouble(1e-07, Index);
vb=N2kMsg.GetByte(Index); N2kData.Accuracy=(vb & 0x01); N2kData.RAIM=(vb>>1 & 0x01); N2kData.Seconds=(vb>>2 & 0x3f);
N2kData.Length=N2kMsg.Get2ByteDouble(0.1, Index);
N2kData.Beam=N2kMsg.Get2ByteDouble(0.1, Index);
N2kData.PositionReferenceStarboard=N2kMsg.Get2ByteDouble(0.1, Index);
N2kData.PositionReferenceTrueNorth=N2kMsg.Get2ByteDouble(0.1, Index);
vb=N2kMsg.GetByte(Index);
N2kData.AtoNType=(tN2kAISAtoNType)(vb & 0x1f);
N2kData.OffPositionIndicator=(vb >> 5 & 0x01);
N2kData.VirtualAtoNFlag=(vb >> 6 & 0x01);
N2kData.AssignedModeFlag=(vb >> 7 & 0x01);
N2kData.GNSSType = (tN2kGNSStype)((N2kMsg.GetByte(Index) >> 1) & 0x0f);
N2kData.AtoNStatus=N2kMsg.GetByte(Index);
N2kData.AISTransceiverInformation = (tN2kAISTransceiverInformation)(N2kMsg.GetByte(Index) & 0x1f);
size_t AtoNNameSize = sizeof(N2kData.AtoNName);
N2kMsg.GetVarStr(AtoNNameSize, (char*)N2kData.AtoNName, Index);
return true;
}
//-----------------------------------------------------------------------------
// Cross Track Error
bool ParseN2kPGN129283(const tN2kMsg &N2kMsg,
unsigned char& SID,
tN2kXTEMode& XTEMode,
bool& NavigationTerminated,
double& XTE)
{
if(N2kMsg.PGN != 129283L)
return false;
int Index = 0;
unsigned char c;
SID = N2kMsg.GetByte(Index);
c = N2kMsg.GetByte(Index);
XTEMode = (tN2kXTEMode)(c & 0x0F);
NavigationTerminated = c & 0x40;
XTE = N2kMsg.Get4ByteDouble(0.01, Index);
return true;
}
//-----------------------------------------------------------------------------
// Navigation info
bool ParseN2kPGN129284(const tN2kMsg &N2kMsg,
unsigned char& SID,
double& DistanceToWaypoint,
tN2kHeadingReference& BearingReference,
bool& PerpendicularCrossed,
bool& ArrivalCircleEntered,
tN2kDistanceCalculationType& CalculationType,
double& ETATime,
int16_t& ETADate,
double& BearingOriginToDestinationWaypoint,
double& BearingPositionToDestinationWaypoint,
uint32_t& OriginWaypointNumber,
uint32_t& DestinationWaypointNumber,
double& DestinationLatitude,
double& DestinationLongitude,
double& WaypointClosingVelocity)
{
if(N2kMsg.PGN != 129284L)
return false;
int Index=0;
unsigned char c;
SID = N2kMsg.GetByte(Index);
DistanceToWaypoint = N2kMsg.Get4ByteUDouble(0.01, Index);
c = N2kMsg.GetByte(Index);
BearingReference = c & 0x01 ? N2khr_magnetic : N2khr_true;
PerpendicularCrossed = c & 0x04;
ArrivalCircleEntered = c & 0x10;
CalculationType = c & 0x40 ? N2kdct_RhumbLine : N2kdct_GreatCircle;
ETATime = N2kMsg.Get4ByteUDouble(0.0001, Index);
ETADate = N2kMsg.Get2ByteUInt(Index);
BearingOriginToDestinationWaypoint = N2kMsg.Get2ByteUDouble(0.0001, Index);
BearingPositionToDestinationWaypoint = N2kMsg.Get2ByteUDouble(0.0001, Index);
OriginWaypointNumber = N2kMsg.Get4ByteUInt(Index);
DestinationWaypointNumber = N2kMsg.Get4ByteUInt(Index);
DestinationLatitude = N2kMsg.Get4ByteDouble(1e-07, Index);
DestinationLongitude = N2kMsg.Get4ByteDouble(1e-07, Index);
WaypointClosingVelocity = N2kMsg.Get2ByteDouble(0.01, Index);
return true;
}
//-----------------------------------------------------------------------------
// Waypoint list : PGN129285
//-----------------------------------------------------------------------------
// AIS static data A
bool ParseN2kPGN129794(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &UserID,
uint32_t &IMOnumber,
char *Callsign,
size_t CallsignBufSize,
char *Name,
size_t NameBufSize,
uint8_t &VesselType,
double &Length,
double &Beam,
double &PosRefStbd,
double &PosRefBow,
uint16_t &ETAdate,
double &ETAtime,
double &Draught,
char *Destination,
size_t DestinationBufSize,
tN2kAISVersion &AISversion,
tN2kGNSStype &GNSStype,
tN2kAISDTE &DTE,
tN2kAISTransceiverInformation &AISinfo)
{
if (N2kMsg.PGN!=129794L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
UserID=N2kMsg.Get4ByteUInt(Index);
IMOnumber=N2kMsg.Get4ByteUInt(Index);
N2kMsg.GetStr(CallsignBufSize, Callsign, 7, '@', Index);
N2kMsg.GetStr(NameBufSize, Name, 20, '@', Index);
VesselType=N2kMsg.GetByte(Index);
Length=N2kMsg.Get2ByteDouble(0.1, Index);
Beam=N2kMsg.Get2ByteDouble(0.1, Index);
PosRefStbd=N2kMsg.Get2ByteDouble(0.1, Index);
PosRefBow=N2kMsg.Get2ByteDouble(0.1, Index);
ETAdate=N2kMsg.Get2ByteUInt(Index);
ETAtime=N2kMsg.Get4ByteUDouble(0.0001, Index);
Draught=N2kMsg.Get2ByteDouble(0.01, Index);
N2kMsg.GetStr(DestinationBufSize, Destination, 20, '@', Index);
vb=N2kMsg.GetByte(Index); AISversion=(tN2kAISVersion)(vb & 0x03); GNSStype=(tN2kGNSStype)(vb>>2 & 0x0f); DTE=(tN2kAISDTE)(vb>>6 & 0x01);
vb=N2kMsg.GetByte(Index); AISinfo=(tN2kAISTransceiverInformation)(vb & 0x1f);
return true;
}
//-----------------------------------------------------------------------------
// AIS static data class B part A
bool ParseN2kPGN129809(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &UserID,
char *Name,
size_t NameBufSize)
{
if (N2kMsg.PGN!=129809L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
UserID=N2kMsg.Get4ByteUInt(Index);
N2kMsg.GetStr(NameBufSize, Name, 20, '@', Index);
return true;
}
//-----------------------------------------------------------------------------
// AIS static data class B part B
bool ParseN2kPGN129810(const tN2kMsg &N2kMsg,
uint8_t &MessageID,
tN2kAISRepeat &Repeat,
uint32_t &UserID,
uint8_t &VesselType,
char *Vendor,
size_t VendorBufSize,
char *Callsign,
size_t CallsignBufSize,
double &Length,
double &Beam,
double &PosRefStbd,
double &PosRefBow,
uint32_t &MothershipID)
{
if (N2kMsg.PGN!=129810L) return false;
int Index=0;
unsigned char vb;
vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03);
UserID=N2kMsg.Get4ByteUInt(Index);
VesselType=N2kMsg.GetByte(Index);
N2kMsg.GetStr(VendorBufSize, Vendor, 7, '@', Index);
N2kMsg.GetStr(CallsignBufSize, Callsign, 7, '@', Index);
Length = N2kMsg.Get2ByteUDouble(0.1, Index);
Beam = N2kMsg.Get2ByteUDouble(0.1, Index);
PosRefStbd = N2kMsg.Get2ByteUDouble(0.1, Index);
PosRefBow = N2kMsg.Get2ByteUDouble(0.1, Index);
MothershipID = N2kMsg.Get4ByteUInt(Index);
return true;
}
//-----------------------------------------------------------------------------
// Waypoint list PGN130074
//-----------------------------------------------------------------------------
// Wind Speed
bool ParseN2kPGN130306(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &WindSpeed,
double &WindAngle,
tN2kWindReference &WindReference)
{
if (N2kMsg.PGN!=130306L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
WindSpeed=N2kMsg.Get2ByteUDouble(0.01,Index);
WindAngle=N2kMsg.Get2ByteUDouble(0.0001,Index);
WindReference=(tN2kWindReference)(N2kMsg.GetByte(Index)&0x07);
return true;
}
//-----------------------------------------------------------------------------
// Outside Environmental parameters
bool ParseN2kPGN130310(const tN2kMsg &N2kMsg,
unsigned char &SID,
double &WaterTemperature,
double &OutsideAmbientAirTemperature,
double &AtmosphericPressure)
{
if (N2kMsg.PGN!=130310L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
WaterTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
OutsideAmbientAirTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
AtmosphericPressure=N2kMsg.Get2ByteUDouble(100,Index);
return true;
}
//-----------------------------------------------------------------------------
// Environmental parameters
bool ParseN2kPGN130311(const tN2kMsg &N2kMsg,
unsigned char &SID,
tN2kTempSource &TempSource,
double &Temperature,
tN2kHumiditySource &HumiditySource,
double &Humidity,
double &AtmosphericPressure)
{
if (N2kMsg.PGN!=130311L) return false;
unsigned char vb;
int Index=0;
SID=N2kMsg.GetByte(Index);
vb=N2kMsg.GetByte(Index); TempSource=(tN2kTempSource)(vb & 0x3f); HumiditySource=(tN2kHumiditySource)(vb>>6 & 0x03);
Temperature=N2kMsg.Get2ByteUDouble(0.01,Index);
Humidity=N2kMsg.Get2ByteDouble(0.004,Index);
AtmosphericPressure=N2kMsg.Get2ByteUDouble(100,Index);
return true;
}
//-----------------------------------------------------------------------------
// Temperature
// Temperatures should be in Kelvins
bool ParseN2kPGN130312(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &TempInstance,
tN2kTempSource &TempSource,
double &ActualTemperature,
double &SetTemperature)
{
if (N2kMsg.PGN!=130312L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
TempInstance=N2kMsg.GetByte(Index);
TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index));
ActualTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
SetTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
return true;
}
//-----------------------------------------------------------------------------
// Humidity
// Humidity should be in percent
bool ParseN2kPGN130313(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &HumidityInstance,
tN2kHumiditySource &HumiditySource,
double &ActualHumidity,
double &SetHumidity)
{
if (N2kMsg.PGN != 130313L) return false;
int Index = 0;
SID=N2kMsg.GetByte(Index);
HumidityInstance=N2kMsg.GetByte(Index);
HumiditySource=(tN2kHumiditySource)N2kMsg.GetByte(Index);
ActualHumidity=N2kMsg.Get2ByteDouble(0.004, Index);
SetHumidity=N2kMsg.Get2ByteDouble(0.004, Index);
return true;
}
//-----------------------------------------------------------------------------
// Actual Pressure
// Pressure should be in Pascals
bool ParseN2kPGN130314(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &PressureInstance,
tN2kPressureSource &PressureSource,
double &ActualPressure)
{
if (N2kMsg.PGN != 130314L) return false;
int Index = 0;
SID=N2kMsg.GetByte(Index);
PressureInstance=N2kMsg.GetByte(Index);
PressureSource=(tN2kPressureSource)N2kMsg.GetByte(Index);
ActualPressure=N2kMsg.Get4ByteDouble(0.1, Index);
return true;
}
//-----------------------------------------------------------------------------
// Set Pressure
// Pressure should be in Pascals PGN130315
//-----------------------------------------------------------------------------
// Temperature extended range
// Temperatures should be in Kelvins
bool ParseN2kPGN130316(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &TempInstance,
tN2kTempSource &TempSource,
double &ActualTemperature,
double &SetTemperature)
{
if (N2kMsg.PGN!=130316L) return false;
int Index=0;
SID=N2kMsg.GetByte(Index);
TempInstance=N2kMsg.GetByte(Index);
TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index));
ActualTemperature=N2kMsg.Get3ByteDouble(0.001,Index);
SetTemperature=N2kMsg.Get2ByteUDouble(0.1,Index);
return true;
}
//-----------------------------------------------------------------------------
// Meteorological Station Data
bool ParseN2kPGN130323(const tN2kMsg &N2kMsg,
tN2kMeteorlogicalStationData &N2kData)
{
if (N2kMsg.PGN!=130323L) return false;
int Index=0;
unsigned char vb;
vb = N2kMsg.GetByte(Index);
N2kData.Mode=(tN2kAISMode)(vb & 0x0f);
N2kData.SystemDate = N2kMsg.Get2ByteUInt(Index);
N2kData.SystemTime = N2kMsg.Get4ByteUDouble(0.0001,Index);
N2kData.Latitude = N2kMsg.Get4ByteDouble(1e-7,Index);
N2kData.Longitude = N2kMsg.Get4ByteDouble(1e-7,Index);
N2kData.WindSpeed = N2kMsg.Get2ByteUDouble(0.01,Index);
N2kData.WindDirection = N2kMsg.Get2ByteUDouble(0.0001,Index);
vb = N2kMsg.GetByte(Index);
N2kData.WindReference = (tN2kWindReference)(vb & 0x07);
N2kData.WindGusts = N2kMsg.Get2ByteUDouble(0.01,Index);
N2kData.AtmosphericPressure = N2kMsg.Get2ByteUDouble(100,Index);
N2kData.OutsideAmbientAirTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
size_t StationIDSize = sizeof(N2kData.StationID);
size_t StationNameSize = sizeof(N2kData.StationName);
N2kMsg.GetVarStr(StationIDSize, (char*)N2kData.StationID, Index);
N2kMsg.GetVarStr(StationNameSize, (char*)N2kData.StationName, Index);
return true;
}
//-----------------------------------------------------------------------------
// Trim Tab Position
// Trim tab position is a percentage 0 to 100% where 0 is fully retracted and 100 is fully extended
bool ParseN2kPGN130576(const tN2kMsg &N2kMsg,
int8_t &PortTrimTab,
int8_t &StbdTrimTab)
{
if (N2kMsg.PGN!=130576L) return false;
int Index=0;
PortTrimTab=N2kMsg.GetByte(Index);
StbdTrimTab=N2kMsg.GetByte(Index);
return true;
}
//-----------------------------------------------------------------------------
// Direction Data
bool ParseN2kPGN130577(const tN2kMsg &N2kMsg,
tN2kDataMode &DataMode,
tN2kHeadingReference &CogReference,
unsigned char &SID,
double &COG,
double &SOG,
double &Heading,
double &SpeedThroughWater,
double &Set,
double &Drift)
{
if (N2kMsg.PGN!=130577L) return false;
int Index=0;
unsigned char v;
v = N2kMsg.GetByte(Index);
DataMode = (tN2kDataMode)(v & 0x0F);
CogReference = (tN2kHeadingReference)((v >> 4) & 0x03);
SID = N2kMsg.GetByte(Index);
COG = N2kMsg.Get2ByteUDouble(0.0001,Index);
SOG = N2kMsg.Get2ByteUDouble(0.01, Index);
Heading = N2kMsg.Get2ByteUDouble(0.0001, Index);
SpeedThroughWater= N2kMsg.Get2ByteUDouble(0.01, Index);
Set = N2kMsg.Get2ByteUDouble(0.0001, Index);
Drift = N2kMsg.Get2ByteUDouble(0.01, Index);
return true;
}
//-----------------------------------------------------------------------------
// Temperature High Range [MARETRON TMP100]
// Temperatures should be in Kelvin
bool ParseN2kMaretronPGN130823(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &TempInstance,
tN2kTempSource &TempSource,
double &ActualTemperature,
double &SetTemperature)
{
if (N2kMsg.PGN!=130823L) return false;
int Index=0;
if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false;
SID=N2kMsg.GetByte(Index);
TempInstance=N2kMsg.GetByte(Index);
TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index));
ActualTemperature=N2kMsg.Get2ByteUDouble(0.1,Index);
SetTemperature=N2kMsg.Get2ByteUDouble(0.1,Index);
return true;
}
//-----------------------------------------------------------------------------
// Fluid Flow Rate [MARETRON FFM100]
// Flow unit is 1 lt/hour
bool ParseN2kMaretronPGN65286(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &FlowRateInstance,
tN2kFluidType &FluidType,
double &FluidFlowRate)
{
if (N2kMsg.PGN!=65286L) return false;
int Index=0;
if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false;
SID=N2kMsg.GetByte(Index);
FlowRateInstance=N2kMsg.GetByte(Index);
FluidType=(tN2kFluidType)(N2kMsg.GetByte(Index));
FluidFlowRate=N2kMsg.Get3ByteDouble(0.1,Index);
return true;
}
//-----------------------------------------------------------------------------
// Trip Volume [MARETRON FFM100]
// Volume unit is 1 litre
bool ParseN2kMaretronPGN65287(const tN2kMsg &N2kMsg,
unsigned char &SID,
unsigned char &VolumeInstance,
tN2kFluidType &FluidType,
double &TripVolume)
{
if (N2kMsg.PGN!=65287L) return false;
int Index=0;
if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false;
SID=N2kMsg.GetByte(Index);
VolumeInstance=N2kMsg.GetByte(Index);
FluidType=(tN2kFluidType)(N2kMsg.GetByte(Index));
TripVolume=N2kMsg.Get3ByteDouble(1,Index);
return true;
}
*/
//===========================================================================================================================================
// Foonctions provenant de N2kMsg.cpp : https://ttlappalainen.github.io/NMEA2000/_n2k_msg_8cpp_source.html
/*
//-----------------------------------------------------------------------------
unsigned char tN2kMsg::GetByte(int &Index) const {
if (Index<DataLen) {
return Data[Index++];
} else return 0xff;
}
//-----------------------------------------------------------------------------
int16_t tN2kMsg::Get2ByteInt(int &Index, int16_t def) const {
if (Index+2<=DataLen) {
return GetBuf2ByteInt(Index,Data);
} else return def;
}
//-----------------------------------------------------------------------------
uint16_t tN2kMsg::Get2ByteUInt(int &Index, uint16_t def) const {
if (Index+2<=DataLen) {
return GetBuf2ByteUInt(Index,Data);
} else return def;
}
//-----------------------------------------------------------------------------
uint32_t tN2kMsg::Get3ByteUInt(int &Index, uint32_t def) const {
if (Index+3<=DataLen) {
return GetBuf3ByteUInt(Index,Data);
} else return def;
}
//-----------------------------------------------------------------------------
uint32_t tN2kMsg::Get4ByteUInt(int &Index, uint32_t def) const {
if (Index+4<=DataLen) {
return GetBuf4ByteUInt(Index,Data);
} else return def;
}
//-----------------------------------------------------------------------------
uint64_t tN2kMsg::GetUInt64(int &Index, uint64_t def) const {
if (Index+8<=DataLen) {
return GetBuf8ByteUInt(Index,Data);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get1ByteDouble(double precision, int &Index, double def) const {
if (Index<DataLen) {
return GetBuf1ByteDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get1ByteUDouble(double precision, int &Index, double def) const {
if (Index<DataLen) {
return GetBuf1ByteUDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get2ByteDouble(double precision, int &Index, double def) const {
if (Index+2<=DataLen) {
return GetBuf2ByteDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get2ByteUDouble(double precision, int &Index, double def) const {
if (Index+2<=DataLen) {
return GetBuf2ByteUDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get3ByteDouble(double precision, int &Index, double def) const {
if (Index+3<=DataLen) {
return GetBuf3ByteDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get4ByteDouble(double precision, int &Index, double def) const {
if (Index+4<=DataLen) {
return GetBuf4ByteDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get4ByteUDouble(double precision, int &Index, double def) const {
if (Index+4<=DataLen) {
return GetBuf4ByteUDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
double tN2kMsg::Get8ByteDouble(double precision, int &Index, double def) const {
if (Index+8<=DataLen) {
return GetBuf8ByteDouble(precision,Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
float tN2kMsg::GetFloat(int &Index, float def) const {
if (Index+4<=DataLen) {
return GetBufFloat(Index,Data,def);
} else return def;
}
//-----------------------------------------------------------------------------
bool tN2kMsg::GetStr(char *StrBuf, size_t Length, int &Index) const {
unsigned char vb;
bool nullReached = false;
StrBuf[0] = '\0';
if ((size_t)Index+Length<=(size_t)DataLen) {
for (size_t i=0; i<Length; i++) {
vb = GetByte(Index);
if (! nullReached) {
if (vb == 0x00 || vb == '@') {
nullReached = true; // either null or '@' (AIS null character)
StrBuf[i] = '\0';
StrBuf[i+1] = '\0';
} else {
StrBuf[i] = vb;
StrBuf[i+1] = '\0';
}
} else {
StrBuf[i] = '\0';
StrBuf[i+1] = '\0';
}
}
return true;
} else return false;
}
//-----------------------------------------------------------------------------
bool tN2kMsg::GetStr(size_t StrBufSize, char *StrBuf, size_t Length, unsigned char nulChar, int &Index) const {
unsigned char vb;
bool nullReached = false;
if ( StrBufSize==0 || StrBuf==0 ) {
Index+=Length;
return true;
}
StrBuf[0] = '\0';
if ((size_t)Index+Length<=(size_t)DataLen) {
size_t i;
for (i=0; i<Length && i<StrBufSize-1; i++) {
vb = GetByte(Index);
if (! nullReached) {
if (vb == 0x00 || vb == nulChar ) {
nullReached = true; // either null or '@' (AIS null character)
StrBuf[i] = '\0';
} else {
StrBuf[i] = vb;
}
} else {
StrBuf[i] = '\0';
}
}
StrBuf[i] = '\0';
for (;i<Length;i++) GetByte(Index); // Stopped by buffer size, so read out bytes from message
for (;i<StrBufSize;i++) StrBuf[i] = '\0'; // Stopped by length, fill buffer with 0
return true;
} else return false;
}
//-----------------------------------------------------------------------------
bool tN2kMsg::GetVarStr(size_t &StrBufSize, char *StrBuf, int &Index) const {
size_t Len=GetByte(Index)-2;
uint8_t Type=GetByte(Index);
if ( Type!=0x01 ) { StrBufSize=0; return false; }
if ( StrBuf!=0 ) {
GetStr(StrBufSize,StrBuf,Len,0xff,Index);
} else {
Index+=Len; // Just pass this string
}
StrBufSize=Len;
return true;
}
//-----------------------------------------------------------------------------
bool tN2kMsg::GetBuf(void *buf, size_t Length, int &Index) const {
bool ret=true;
if ((size_t)Index+Length<=(size_t)DataLen) {
if ( buf!=0 ) {
memcpy(buf,Data+Index,Length);
} else {
Index+=Length; // Just pass this string
}
} else {
Index=DataLen;
ret=false;
}
return ret;
}
//-----------------------------------------------------------------------------
int16_t GetBuf2ByteInt(int &index, const unsigned char *buf) {
return GetBuf<int16_t>(2, index, buf);
}
//-----------------------------------------------------------------------------
uint16_t GetBuf2ByteUInt(int &index, const unsigned char *buf) {
return GetBuf<uint16_t>(2, index, buf);
}
//-----------------------------------------------------------------------------
uint32_t GetBuf3ByteUInt(int &index, const unsigned char *buf) {
return GetBuf<uint32_t>(3, index, buf);
}
//-----------------------------------------------------------------------------
uint32_t GetBuf4ByteUInt(int &index, const unsigned char *buf) {
return GetBuf<uint32_t>(4, index, buf);
}
//-----------------------------------------------------------------------------
uint64_t GetBuf8ByteUInt(int &index, const unsigned char *buf) {
return GetBuf<uint64_t>(8, index, buf);
}
//-----------------------------------------------------------------------------
double GetBuf1ByteDouble(double precision, int &index, const unsigned char *buf, double def) {
int8_t vl = GetBuf<int8_t>(1, index, buf);
if (vl==0x7f) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf1ByteUDouble(double precision, int &index, const unsigned char *buf, double def) {
uint8_t vl = GetBuf<uint8_t>(1, index, buf);
if (vl==0xff) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf2ByteDouble(double precision, int &index, const unsigned char *buf, double def) {
int16_t vl = GetBuf<int16_t>(2, index, buf);
if (vl==0x7fff) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf2ByteUDouble(double precision, int &index, const unsigned char *buf, double def) {
uint16_t vl = GetBuf<uint16_t>(2, index, buf);
if (vl==0xffff) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf8ByteDouble(double precision, int &index, const unsigned char *buf, double def) {
int64_t vl = GetBuf<int64_t>(8, index, buf);
if (vl==0x7fffffffffffffffLL) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBufDouble(int &index, const unsigned char *buf, double def) {
int64_t vl = GetBuf<int64_t>(8, index, buf);
double ret;
// On avr double==float, so we test it also. Currently no handling for avr.
if ( sizeof(double)==8 && !N2kIsNA(vl) ) {
memcpy(&ret,&vl,8);
if ( isnan(ret) ) ret=def;
} else {
ret=def;
}
return ret;
}
//-----------------------------------------------------------------------------
float GetBufFloat(int &index, const unsigned char *buf, float def) {
int32_t vl = GetBuf<int32_t>(4, index, buf);
float ret;
if ( !N2kIsNA(vl) ) {
memcpy(&ret,&vl,4);
if ( isnan(ret) ) ret=def;
} else { // On avr double==float
ret=def;
}
return ret;
}
//-----------------------------------------------------------------------------
double GetBuf3ByteDouble(double precision, int &index, const unsigned char *buf, double def) {
int32_t vl = GetBuf<int32_t>(3, index, buf);
if (vl==0x007fffff) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf4ByteDouble(double precision, int &index, const unsigned char *buf, double def) {
int32_t vl = GetBuf<int32_t>(4, index, buf);
if (vl==0x7fffffff) return def;
return vl * precision;
}
//-----------------------------------------------------------------------------
double GetBuf4ByteUDouble(double precision, int &index, const unsigned char *buf, double def) {
uint32_t vl = GetBuf<uint32_t>(4, index, buf);
if (vl==0xffffffff) return def;
return vl * precision;
}
*/