//-----------------------------------------------------------------------------***************************************************************************************************** // 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 earth’s 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>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(2, index, buf); } //----------------------------------------------------------------------------- uint16_t GetBuf2ByteUInt(int &index, const unsigned char *buf) { return GetBuf(2, index, buf); } //----------------------------------------------------------------------------- uint32_t GetBuf3ByteUInt(int &index, const unsigned char *buf) { return GetBuf(3, index, buf); } //----------------------------------------------------------------------------- uint32_t GetBuf4ByteUInt(int &index, const unsigned char *buf) { return GetBuf(4, index, buf); } //----------------------------------------------------------------------------- uint64_t GetBuf8ByteUInt(int &index, const unsigned char *buf) { return GetBuf(8, index, buf); } //----------------------------------------------------------------------------- double GetBuf1ByteDouble(double precision, int &index, const unsigned char *buf, double def) { int8_t vl = GetBuf(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(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(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(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(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(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(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(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(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(4, index, buf); if (vl==0xffffffff) return def; return vl * precision; } */