Магнитный указатель курса (с компенсацией наклона) для картплоттера

mikelari
Offline
Зарегистрирован: 14.05.2015

На форуме http://www.rusfishing.ru/forum/showthread.php?t=247235 тема про самоделки для картплоттеров.

#include <Wire.h>
#include <HMC5883L.h>
#include <MPU6050.h>
#include <String.h>
#include <SoftwareSerial.h>

SoftwareSerial GarminSerial(2, 3, 1); // RX, TX ,Logic invretor

HMC5883L compass;
MPU6050 mpu;

float heading1;
float heading2;

#define Filter_read 20  //!!! только четное число сколько читать значений из АЦП для работы фильтра.Выбрано для частоты 10 Гц
#define Filter_get  1  //!!! только четное число или единица сколько взять  значений  для расчета среднего фильтром
long filter_heading2;  //для получения множества значений raw из регистра преобразования АЦП после чтения по значениям Filter_read и Filter_get

String str_hdt;

long last_nmea_send;
//____________________________________________________________________________________________________________________________________
void setup()
{
  Serial.begin(4800);
  GarminSerial.begin(4800);
  //GarminSerial.begin(38400);

  // Initialize MPU6050
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
  {
    delay(500);
  }
/*
  // Enable bypass mode
  mpu.setI2CMasterModeEnabled(false);
  mpu.setI2CBypassEnabled(true) ;
  mpu.setSleepEnabled(false);
*/
  // Initialize Initialize HMC5883L
  while (!compass.begin())
  {
    delay(500);
  }

  // Set measurement range
  compass.setRange(HMC5883L_RANGE_1_3GA);

  // Set measurement mode
  compass.setMeasurementMode(HMC5883L_CONTINOUS);

  // Set data rate
  compass.setDataRate(HMC5883L_DATARATE_30HZ);

  // Set number of samples averaged
  compass.setSamples(HMC5883L_SAMPLES_8);

  // Set calibration offset. See HMC5883L_calibration.ino
  compass.setOffset(-4,-125); 
}
//_________________________________________________________________________________________________________________________________

// No tilt compensation
float noTiltCompensate(Vector mag)
{
  float heading = atan2(mag.YAxis, mag.XAxis);
  return heading;
}
 
//__________________________________________________________________________________________________________________________________ 
// Tilt compensation
float tiltCompensate(Vector mag, Vector normAccel)
{
  // Pitch & Roll 
  
  float roll;
  float pitch;
  
  roll = asin(normAccel.YAxis);
  pitch = asin(-normAccel.XAxis);

  if (roll > 0.78 || roll < -0.78 || pitch > 0.78 || pitch < -0.78)
  {
    return -1000;
  }
  
    // Some of these are used twice, so rather than computing them twice in the algorithem we precompute them before hand.
  float cosRoll = cos(roll);
  float sinRoll = sin(roll);  
  float cosPitch = cos(pitch);
  float sinPitch = sin(pitch);
  
  // Tilt compensation
  float Xh = mag.XAxis * cosPitch + mag.ZAxis * sinPitch;
  float Yh = mag.XAxis * sinRoll * sinPitch + mag.YAxis * cosRoll - mag.ZAxis * sinRoll * cosPitch;
 
  float heading = atan2(Yh, Xh);
    
  return heading;
}
//______________________________________________________________________________________________________________________________

// Correct angle
float correctAngle(float heading)
{
  if (heading < 0) { heading += 2 * PI; }
  if (heading > 2 * PI) { heading -= 2 * PI; }

  return heading;
}
//_______________________________________________________________________________________________________________________________
void loop()
{
  double StartMillis = millis();
  filter_heading_calc ();

  // Output
  /*
  Serial.print(millis()- StartMillis,0 );
  Serial.print(" mS: ");
  Serial.print(heading1,0);      //noTiltCompensate(mag)
  Serial.print(" : ");
  Serial.println(heading2,0);    //tiltCompensate(mag, acc)
  */
  
  //if (millis() - last_nmea_send >= 10){
    hdtSentenceMake();
    
    Serial.println(str_hdt);
    GarminSerial.print(str_hdt);
   // last_nmea_send = millis();
  //}
  
  
  
}
//_____________________________________________________________________________________________________________________________
 void get_heading(){
  // Read vectors
  Vector mag = compass.readNormalize();
  Vector acc = mpu.readScaledAccel();  

  // Calculate headings
  heading1 = noTiltCompensate(mag);
  heading2 = tiltCompensate(mag, acc);
  
  if (heading2 == -1000)
  {
    heading2 = heading1;
  }

  // Set declination angle on your location and fix heading
  // You can find your declination on: http://magnetic-declination.com/
  // (+) Positive or (-) for negative
  // For Bytom / Poland declination angle is 4'26E (positive)
  // Formula: (deg + (min / 60.0)) / (180 / M_PI);
  //float declinationAngle = 0;//(4.0 + (26.0 / 60.0)) / (180 / M_PI);
  //heading1 += declinationAngle;
  //heading2 += declinationAngle;
  
  // Correct for heading < 0deg and heading > 360deg
  heading1 = correctAngle(heading1);
  heading2 = correctAngle(heading2);

  // Convert to degrees
  heading1 = heading1 * 180/M_PI; //noTiltCompensate(mag)
  heading2 = heading2 * 180/M_PI; //tiltCompensate(mag, acc)
   
 }

//______________________________________________________________________________________________________________________________
void filter_heading_calc () {                  // подпрограмма считывания и фильтрации значений  
  
  float sortedValues[Filter_read];              //назначили  массив с sortedValues[Filter_read] 
  for (int i = 0; i < Filter_read; i++) {       //цикл от 0 до Filter_read
      
again: get_heading();                            // идем на подпрограмму получения данных из АЦП в переменную ltw
      
      if ((heading2 / heading2) != 1 ) {
        //Serial.println("*************fuck***************");
        goto again;
      }
     // Serial.print(" adc[");
     // Serial.print(i);
     // Serial.print("] = ");
     // Serial.println(ltw);
      
      float value = heading2 ;                         //value получает значение одного измерения raw ads  
      int j;                                    //переменная "j" куда вставлять значение при сортировке
      if (value < sortedValues[0] || i == 0) {  //если полученое значение valve < sortedValues[0] или это самое начало "i",то 
         j = 0;                                 //"j"= 0 это первая позиция 
      }                                         //конец  if
       else {                                   //иначе
          for (j = 1; j < i; j++) {             //цикл j от 1 до j < i искать "j"
           if (sortedValues[j - 1] <= value && sortedValues[j] >= value) { //ищем место"j" в sortedValues[]куда вставить value
             break;                                                        //"j" найдено,остановить выполнение цикла
           }                                    //конец if
          }                                     //конец цикла j
       }                                        //конец else
         
       for (int k = i; k > j; k--) {            //все значения от "i" до "j" (вниз к--) нужно поднять на одну позицию вверх
       sortedValues[k] = sortedValues[k - 1];   //чтобы освободить место "j" для valve
       }                                        //конец цикла
       sortedValues[j] = value;                 //вставить считаное valve в sortedValues[] в позицию "j"
  }                                             //конец цикла от 0 до Filter_read
  
  
  /*
  for (int i = 0; i < Filter_read; i++) {       //цикл от 0 до Filter_read
      Serial.print(" sortedValues[");
      Serial.print(i);
      Serial.print("] = ");
      Serial.println(sortedValues[i]);
  }
  */
  
 //расчет среднего знач из Filter_get. Эти значения взяты после отброса лишних значений
  float filter_heading = 0;
  
  if (Filter_get%2 == 0)                       //если Filter_get четное, то считаем так
     // четное
      { 
      for (int i = Filter_read / 2 - Filter_get/2; i < (Filter_read / 2 + Filter_get/2); i++) {
      filter_heading += sortedValues[i];
      }
      heading2 = filter_heading / Filter_get;
      }
  if (Filter_get == 1)                        ////если Filter_get =1, то не считаем а берем из середины одно значение
  {
    byte nomb = Filter_read / 2;  
    heading2 = sortedValues[nomb];
  }
  
     
}
//__________________________________________________________________________________________________
 void hdtSentenceMake(){
   char Sentence [20];
   byte XOR;
   str_hdt = "$HCHDM,";
   str_hdt += String(heading2,1);
   str_hdt += ",M*";
   str_hdt.toCharArray(Sentence, str_hdt.length() + 1);
   
   //считать XOR все символы кроме $ , и стоп на *
   int i;
   for (XOR = 0, i = 0; i < sizeof(Sentence); i++)
   {
   if (Sentence[i] == '*') break;
   if (Sentence[i] != '$' ) XOR ^= Sentence[i];
  
   }
  
   if (XOR < 0x10) str_hdt +="0";  
   str_hdt += String(XOR, HEX); 
    
 }
//__________________________________________________________________________________________________

Появилось время, я сделал макет своего магнитного указателя, и поженил его с Garmin Echomap chirp 73SV.

Макет сделан на Arduino UNO , будет работать и на других платах. На куске полистирола приклеил магнитометр HMC5983 и MPU 6050 для измерения гиро и ускорения. Магнитометр на точном поворотном устройстве отцентровал (офсет X ,Y). Получилось достаточно точно. На 360 градусов поворота поворотного устройства, разница показаний магнитометра и поворотки была не более 1 градуса, при том, что на моей широте магнитные линии падают под углом около 60 градусов. Затем в софте добавил MPU6050 для компенсации наклона, и это очень хорошо работает до наклона 45 градусов. Что я увидел? Все работает. Есть особенности. 

1.Пробовал разные скорости (4800 и 38400) и разный интервал передачи данных ( от 1 до 10 ГЦ) в картплоттер. При любых комбинациях часть NMEA предложений на странице диагностика связи прибор пропускает часть сообщений как сообщения с неверной контрольной суммой, хотя с этим все в порядке. Пока не понял причину. Возможно это нормально, т.е. так устроен софт прибора.

2. Пробовал разные фильтры использовать (и калмана тоже библиотечного ардуиновского). Не нравиться, и не проходит тест на стресс. Начинаю свой указатель быстро крутить/вертеть/стучать/кидать и происходит глюк или переполнение фильтра. Поэтому использовал программный фильтр, который использую при аналоговых измерениях. Он меня устраивает.

3. У Garmin для подключения NMEA 0183 нет диф.пары. У него RX/TX(-) соединяется с минусом питания. Синий провод передает данные из картплоттера, коричневый - принимать данные. Но сигнал логики на RX нужно инвертировать. Это легко делается использованием библиотеки <SoftwareSerial.h> и указать при объявлении единичку после номеров пинов. Это приводит к инвертированию логики на данном сериал порте. Вот примерно так SoftwareSerial GarminSerial(2, 3, 1); // RX, TX ,Logic_Inv

4. При таком подключении прибор нормально принимает сигнал от ардуины с логикой 3.3В. А значит можно использовать платы типа PRO MICRO 3.3V.

5. Разные экраны/режимы работы картплоттера с разной скоростью отображают изменения показаний от магнитного указателя курса. Экран «окружающая среда» очень быстро реагирует на поворот датчика, т.е. практически не отстает от него. Экран «Данные» и «Карта» отображают изменения медленно, ступенчато. Причем вращение карты иногда идет быстрее , чем изменение показаний направления в цифрах. Вращение карты без рывков, скорость поворота приемлемая.

Калибровка,библиотеки и методы взял у поляка видео https://www.youtube.com/watch?v=UQntG3JQvOA

Выкладываю ссылку на архив со своим черновиком софта для ардуины и использованные библиотеки https://yadi.sk/d/eaYfuaEZ3JmSZF

Видео https://youtu.be/KFqytMOUzq8 и https://youtu.be/911eIVlXlhM

Удачи.

Aleksk
Offline
Зарегистрирован: 08.06.2016

Поляк, конечно же молодец, я тоже его библиотеками пользовался для создания наклонно компенсированного компаса. Однако он не калибрует смещение (оффсет) по оси Z. После того, как я изменил скетч и стал калибровать  ось Z (переворачивая магнитометр "вверх ногами"), компенсация стала равномерно работать при любых (0-360)  по азимуту направлениях наклона (даже больше 45 градусов) (была однобокость, это многие пользователи заметили). Ограничения 45 градусов - программное, я его убрал, т.к. прибор компенсирует и большие углы. Очень полезно для уменьшения  флуктуацй включить внутренний НЧ фильтр в самом  MPU6050 с самой низкой полосой пропускания..

Измеренная погрешность определения азимута не превышает 3 градуса.

 

(фильтр Калмана не стал использовать, когда казалось бы все настроил он самовозбудился через час работы. Использую программную фильтрацию - программный НЧ фильтр. Для лодки вполне годное решение.)

APJ
Offline
Зарегистрирован: 25.03.2015

Если не трудно, скиньте те данные, которые передаёт компас в навигатор, хочу на своём Humminbird 998 попробовать.

Без  компаса, просто с ардино через серийный порт посылать данные

mikelari
Offline
Зарегистрирован: 14.05.2015
$HCHDM,192.6,M*25
$HCHDM,192.7,M*24
$HCHDM,191.5,M*25
$HCHDM,192.4,M*27
$HCHDM,192.5,M*26
$HCHDM,192.0,M*23
$HCHDM,187.2,M*25
$HCHDM,160.1,M*2f
$HCHDM,139.9,M*2b
$HCHDM,118.8,M*29
$HCHDM,106.9,M*27
$HCHDM,92.2,M*10
$HCHDM,81.2,M*12
$HCHDM,67.7,M*1f
$HCHDM,62.7,M*1a
$HCHDM,80.7,M*16
$HCHDM,104.1,M*2d
$HCHDM,123.7,M*2e
$HCHDM,137.5,M*29
$HCHDM,165.5,M*2e
$HCHDM,188.6,M*2e
$HCHDM,224.3,M*2e
$HCHDM,242.8,M*25
$HCHDM,231.8,M*21
$HCHDM,207.3,M*2f
$HCHDM,250.0,M*2e
$HCHDM,285.6,M*20
$HCHDM,329.9,M*28
$HCHDM,12.5,M*1f
$HCHDM,36.8,M*14
$HCHDM,50.6,M*1a
$HCHDM,40.0,M*1d
$HCHDM,19.9,M*18
$HCHDM,9.1,M*21
$HCHDM,353.7,M*2b
$HCHDM,343.2,M*2f
$HCHDM,333.9,M*23
$HCHDM,315.7,M*29
$HCHDM,299.5,M*2e
$HCHDM,282.7,M*26
$HCHDM,265.4,M*2c
$HCHDM,246.1,M*28
$HCHDM,227.9,M*27
$HCHDM,202.5,M*2c
$HCHDM,182.9,M*2b
$HCHDM,166.5,M*2d
$HCHDM,148.6,M*22
$HCHDM,131.8,M*22
$HCHDM,119.1,M*21
$HCHDM,116.7,M*28
$HCHDM,113.9,M*23
$HCHDM,101.8,M*21
$HCHDM,86.0,M*17
$HCHDM,72.1,M*1d
$HCHDM,59.0,M*15
$HCHDM,44.6,M*1f
$HCHDM,26.9,M*14
$HCHDM,19.2,M*13
$HCHDM,13.2,M*19
$HCHDM,330.0,M*29
$HCHDM,333.1,M*2b
$HCHDM,330.1,M*28
$HCHDM,38.4,M*16
$HCHDM,58.1,M*15
$HCHDM,70.3,M*1d
$HCHDM,81.4,M*14
$HCHDM,94.1,M*15
$HCHDM,106.9,M*27
$HCHDM,120.0,M*2a
$HCHDM,137.8,M*24
$HCHDM,161.2,M*2d
$HCHDM,201.9,M*23
$HCHDM,170.1,M*2e
$HCHDM,174.8,M*23
$HCHDM,173.6,M*2a
$HCHDM,173.8,M*24
$HCHDM,173.8,M*24
$HCHDM,173.2,M*2e
$HCHDM,173.3,M*2f
$HCHDM,173.2,M*2e
$HCHDM,173.3,M*2f

 

mikelari
Offline
Зарегистрирован: 14.05.2015

Исправлен баг в подсчете контрольной суммы. Не забываем менять свои коэф. калибровки магнитометра в 


compass.setOffset(-4,-125); 

 

#include <Wire.h>
#include <HMC5883L.h>
#include <MPU6050.h>
#include <String.h>
#include <SoftwareSerial.h>

SoftwareSerial GarminSerial(2, 3, 1); // RX, TX ,Logic invretor

HMC5883L compass;
MPU6050 mpu;

float heading1;
float heading2;

#define Filter_read 200  //!!! только четное число сколько читать значений из АЦП для работы фильтра.Выбрано 20 для частоты 10 Гц или 200 для 1Гц
#define Filter_get  1  //!!! только четное число или единица сколько взять  значений  для расчета среднего фильтром
long filter_heading2;  //для получения множества значений raw из регистра преобразования АЦП после чтения по значениям Filter_read и Filter_get

String str_hdm;
String STR_CRC_XOR ="";

//____________________________________________________________________________________________________________________________________
void setup()
{
  Serial.begin(9600);
  Serial.println("HMC5883L_compensation_MPU6050_next_7_1000ms.ino");//*******************************************************************************
  GarminSerial.begin(4800);
  //GarminSerial.begin(38400);

  // Initialize MPU6050
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
  {
    delay(500);
  }

  // Initialize Initialize HMC5883L
  while (!compass.begin())
  {
    delay(500);
  }

  // Set measurement range
  compass.setRange(HMC5883L_RANGE_1_3GA);

  // Set measurement mode
  compass.setMeasurementMode(HMC5883L_CONTINOUS);

  // Set data rate
  compass.setDataRate(HMC5883L_DATARATE_30HZ);

  // Set number of samples averaged
  compass.setSamples(HMC5883L_SAMPLES_8);

  // Set calibration offset. See HMC5883L_calibration.ino
  compass.setOffset(-4,-125); 
}
//_________________________________________________________________________________________________________________________________

// No tilt compensation
float noTiltCompensate(Vector mag)
{
  float heading = atan2(mag.YAxis, mag.XAxis);
  return heading;
}
 
//__________________________________________________________________________________________________________________________________ 
// Tilt compensation
float tiltCompensate(Vector mag, Vector normAccel)
{
  // Pitch & Roll 
  
  float roll;
  float pitch;
  
  roll = asin(normAccel.YAxis);
  pitch = asin(-normAccel.XAxis);

  if (roll > 0.78 || roll < -0.78 || pitch > 0.78 || pitch < -0.78)
  {
    return -1000;
  }
  
    // Some of these are used twice, so rather than computing them twice in the algorithem we precompute them before hand.
  float cosRoll = cos(roll);
  float sinRoll = sin(roll);  
  float cosPitch = cos(pitch);
  float sinPitch = sin(pitch);
  
  // Tilt compensation
  float Xh = mag.XAxis * cosPitch + mag.ZAxis * sinPitch;
  float Yh = mag.XAxis * sinRoll * sinPitch + mag.YAxis * cosRoll - mag.ZAxis * sinRoll * cosPitch;
 
  float heading = atan2(Yh, Xh);
    
  return heading;
}
//______________________________________________________________________________________________________________________________

// Correct angle
float correctAngle(float heading)
{
  if (heading < 0) { heading += 2 * PI; }
  if (heading > 2 * PI) { heading -= 2 * PI; }

  return heading;
}
//_______________________________________________________________________________________________________________________________
void loop()
{
  double StartMillis = millis();
    filter_heading_calc ();
    hdmSentenceMake();
    Serial.println(str_hdm);
    GarminSerial.print(str_hdm);
   
  
  
  
}
//_____________________________________________________________________________________________________________________________
 void get_heading(){
  // Read vectors
  Vector mag = compass.readNormalize();
  Vector acc = mpu.readScaledAccel();  

  // Calculate headings
  heading1 = noTiltCompensate(mag);
  heading2 = tiltCompensate(mag, acc);
  
  if (heading2 == -1000)
  {
    heading2 = heading1;
  }
 
  
  // Correct for heading < 0deg and heading > 360deg
  heading1 = correctAngle(heading1);
  heading2 = correctAngle(heading2);

  // Convert to degrees
  heading1 = heading1 * 180/M_PI; //noTiltCompensate(mag)
  heading2 = heading2 * 180/M_PI; //tiltCompensate(mag, acc)
   
 }

//______________________________________________________________________________________________________________________________
void filter_heading_calc () {                  // подпрограмма считывания и фильтрации значений  
  
  float sortedValues[Filter_read];              //назначили  массив с sortedValues[Filter_read] 
  for (int i = 0; i < Filter_read; i++) {       //цикл от 0 до Filter_read
      
again: get_heading();                            // идем на подпрограмму получения данных из АЦП в переменную ltw
      
      if ((heading2 / heading2) != 1 ) {
        //Serial.println("*************fuck***************");
        goto again;
      }
    
      
      float value = heading2 ;                         //value получает значение одного измерения raw ads  
      int j;                                    //переменная "j" куда вставлять значение при сортировке
      if (value < sortedValues[0] || i == 0) {  //если полученое значение valve < sortedValues[0] или это самое начало "i",то 
         j = 0;                                 //"j"= 0 это первая позиция 
      }                                         //конец  if
       else {                                   //иначе
          for (j = 1; j < i; j++) {             //цикл j от 1 до j < i искать "j"
           if (sortedValues[j - 1] <= value && sortedValues[j] >= value) { //ищем место"j" в sortedValues[]куда вставить value
             break;                                                        //"j" найдено,остановить выполнение цикла
           }                                    //конец if
          }                                     //конец цикла j
       }                                        //конец else
         
       for (int k = i; k > j; k--) {            //все значения от "i" до "j" (вниз к--) нужно поднять на одну позицию вверх
       sortedValues[k] = sortedValues[k - 1];   //чтобы освободить место "j" для valve
       }                                        //конец цикла
       sortedValues[j] = value;                 //вставить считаное valve в sortedValues[] в позицию "j"
  }                                             //конец цикла от 0 до Filter_read
  
   
 //расчет среднего знач из Filter_get. Эти значения взяты после отброса лишних значений
  float filter_heading = 0;
  
  if (Filter_get%2 == 0)                       //если Filter_get четное, то считаем так
     // четное
      { 
      for (int i = Filter_read / 2 - Filter_get/2; i < (Filter_read / 2 + Filter_get/2); i++) {
      filter_heading += sortedValues[i];
      }
      heading2 = filter_heading / Filter_get;
      }
  if (Filter_get == 1)                        ////если Filter_get =1, то не считаем а берем из середины одно значение
  {
    byte nomb = Filter_read / 2;  
    heading2 = sortedValues[nomb];
  }
  
     
}
//__________________________________________________________________________________________________
 void hdmSentenceMake(){
   char Sentence [20];
   byte XOR;
   STR_CRC_XOR ="";
   str_hdm = "$HCHDM,";
   str_hdm += String(heading2,1);
   str_hdm += ",M*";
   str_hdm.toCharArray(Sentence, str_hdm.length() + 1);
   
   //считать XOR все символы кроме $ , и стоп на *
   int i;
   for (XOR = 0, i = 0; i < sizeof(Sentence); i++)
   {
   if (Sentence[i] == '*') break;
   if (Sentence[i] != '$' ) XOR ^= Sentence[i];
  
   }
  
   if (XOR < 0x10) str_hdm +="0";  
   STR_CRC_XOR = String(XOR, HEX);
   STR_CRC_XOR.toUpperCase();
   str_hdm += STR_CRC_XOR;
  
    
 }
//__________________________________________________________________________________________________

 

APJ
Offline
Зарегистрирован: 25.03.2015

mikelari

А как вы подаёте данные с компаса на навигатор? На нём есть отдельный вход nmea0183 ? У меня humminbird 998, там нет такого,  только интернет. Есть выход 0183, там такие данные-

<LF>$INGLL,5927.5245,N,02440.8840,E,171156,A*36 [len=44]				
<LF>$INVTG,108.2,T,99.5,M,0.2,N,0.3,K*61 [len=37]					
<LF>$INMTW,,*49 [len=12]
<LF>$INDPT,,*47 [len=12]
<LF>$INHDG,0.0,0.0,$INHDT,0.0,T*25 [len=31]

С гпс приёмника идё такое-

<LF>$GPGSV,3,3,12,06,08,037,28,09,01,022,24,34,00,000,,21,04,200,*76 [len=65]
<LF>$GPRMC,162517.600,A,5927.5170,N,02440.8863,E,0.15,332.53,300318,,,A*6A [len=71]
<LF>$PSRFEPE,162517.800,A,1.1,0.65,3.35,0.3,180.0*18 [len=49]
<LF>$GPGSA,A,3,29,25,31,02,12,26,05,23,06,,,,2.0,1.1,1.7*3F [len=56]
<LF>$GPGGA,162518.600,5927.5171,N,02440.8865,E,1,09,1.1,-10.8,M,19.8,M,,0000*44 [len=76]

Пробовал на этот вход подавать данные с эмулятора-

<LF>$GPRMC,194037.93,A,5931.83122,N,02422.54950,E,30.0,291.0,300318,006.0,E*63 [len=75]

в таком виде показывает курс.

Какие есть варианты? Разбивать сообщения с гпс и подсовывать туда данные с магнитного компаса?

mikelari
Offline
Зарегистрирован: 14.05.2015

Да у моего эхолота есть отдельные вход/выход для nmea0183  и для nmea2000.

Если у Вас нет входа nmea0183, можно использовать конвертор уровней для получения нормальной дифф. пары и библиотеку nmea2000 . Этот вход/выход  надеюсь имеется у  humminbird 998.

$GPRMC это "рекомендуемый минимум специальных данных GNSS" в котором нет данных магнитного указателя курса

 

RMC Recommended Minimum Navigation Information
$--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh
1) Time (UTC)
2) Status, V = Navigation receiver warning
3) Latitude
4) N or S
5) Longitude
6) E or W
7) Speed over ground, knots
8) Track made good, degrees true
9) Date, ddmmyy
10) Magnetic Variation, degrees
11) E or W
12) Checksum
 
Для магнитного указателя курса нужно использовать следующее предложение
HDM Heading – Magnetic
$--HDM,x.x,M*hh
1) Heading Degrees, magnetic
2) M = magnetic
3) Checksum

Что значит разбивать ? Формируется и передается в эхолот отдельное  предложение.

Удачи.

atman
Offline
Зарегистрирован: 07.05.2020

Добрый день. Вы не могли бы подсказать точную распиновку подключения Ардуино к порту NMEA2000 в устройстве  Garmin Echomap chirp 73SV?

mikelari
Offline
Зарегистрирован: 14.05.2015

Используется подключение к NMEA0183. Для подключения к NMEA2000 нужно серьезно переработать всё.