Когда GPS не видит спутники, как я могу продолжать отмечать плавное время?

Я получаю 10 обновлений в секунду от моего устройства GPS (MTK3339, используемого в коммутационной плате Adafruit) и использую его для обновления дисплея часов. Это работает достаточно хорошо, хотя есть одна небольшая проблема, которую я хотел бы решить, и одна большая проблема, которую мне нужно решить.

Сначала небольшая проблема; Я не получаю обновления каждые 100 мс. Я получаю один примерно каждые 100 мс... плюс-минус около 20-30 мс. Если позднее и раннее обновление поступают одно за другим сразу после того, как дисплей переходит от n к n+1 секундам, заметно, что оно не тикает с обычной частотой. Это не смертельно, но смотреть на аритмическое заикание очень больно.

Основной вопрос: что делать, если GPS не может найти сигнал? Возможно, устройство в течение минуты едет по туннелю или есть какое-то другое препятствие в небе. Я по-прежнему хочу, чтобы часы обновлялись с наибольшей вероятностью.

Нельзя просто обновить отображение секунд, а затем, через 1000 (измеряется Arduino) миллисекунд, обновить часы, если новое сообщение не пришло. 1000 реальных миллисекунд могут составлять от 700 до 1400 мс, измеренных Arduino, в зависимости от платы и качества кристалла. Что еще хуже, это несоответствие далеко не постоянно в пределах одного кристалла. Наблюдать, как процессор ускоряется и замедляется на несколько процентов в течение нескольких минут, не редкость.

Итак, мне бы хотелось иметь механизм, который будет постоянно сглаживать полученное (GPS) время, чтобы давать наилучшую оценку текущего фактического времени. Я согласен с тем, что это секунда или две (максимум) отличается от реального времени, пока частота шагов ощущается как настоящие секунды, а потеря GPS на несколько минут может быть быстро исправлена, когда сигнал повторно получен. .

Я немного знаком с (перечеркнутыми) методами DIP(/перечеркнутыми) PID, но я не знаю, как их применить здесь. Кажется, кто-то решил проблему ради синхронизации времени в Интернете, поэтому мне не следует заново изобретать решение.

, 👍1

Обсуждение

почему бы не использовать библиотеку времени?, @Juraj

Относительно «_[одна истинная секунда] может составлять от 700 до 1400 мс, измеренных Arduino_»: либо ваш Arduino физически сломан, либо вы делаете что-то _ужасно неправильно_ в своем коде., @Edgar Bonet

Многие вещи могут помешать точному подсчету времени Arduino; без контекста невозможно понять, почему вы видите такие результаты - это довольно далеко от всего, что я видел практически на любом Arduino или клоне. Почему бы не добавить RTC?, @Dave Newton


4 ответа


Лучший ответ:

2

То, что вы хотите, а именно что-то, чтобы «сгладить полученное (GPS) время», называется дисциплинированным генератором GPS. Очевидно, вы не после высокой точности коммерческих GPSDO, но принцип работы то, что вы описываете, по сути одно и то же.

Что делать, если GPS не может найти сигнал?

На жаргоне GPSDO это называется состоянием удержания. В этот момент вы полагаться исключительно на ваш локальный осциллятор (здесь, millis() или micros()), и направьте его, чтобы скорректировать естественный дрейф, как определено во время Прием GPS был хорошим. Или вы можете пофантазировать, измерьте чип температуру и примените температурную компенсацию.

Я немного знаком с методами DIP, но не знаю, как их применять. здесь

Полагаю, вы имеете в виду «PID".

Вы применяете это к фазе осциллятора. Если местный осциллятор опережает GPS (положительная фазовая ошибка), вы снижаете его частоту. Для например, вы можете увеличивать количество секунд каждые 1 000 100 микросекунд (измеряется с помощью micros()), а не каждые 1 000 000. Это немного замедлит ваши часы и повторно синхронизирует их с GPS. И наоборот, если ваши часы отстают от GPS, вы немного увеличьте его частоту, чтобы он работал быстрее.

Другими словами, значение ошибки вашего PID – это фазовая ошибка вашего часы, а управляющая переменная — настройка частоты. К применяя технику PID таким образом, вы строите то, что известно как петля фазовой автоподстройки частоты или PLL. Это стандартный способ внедрение GPSDO.

Кажется, кто-то решил проблему ради синхронизация интернет-времени

Конечно, но реализация NTP может быть довольно сложной, поскольку с несколькими серверами, имеющими разные задержки туда и обратно и разные уровни доверия. Я не знаю ни одной библиотеки Arduino, которая делает ничего похожего. Вы можете искать «синхронизация времени» или «PLL». Если вы их не найдете, вы можете выбрать библиотеку PID и применить ее к своему осциллятор.

Несколько заключительных замечаний:

  1. Вы написали: "ускоряйтесь и замедляйтесь на несколько процентов по ходу движения. несколько минут — не редкость». Это показывает, что есть что-то ужасно неправильно либо с вашей Arduino, либо с вашим кодом. За несколько минут, вы ожидаете изменения частоты порядка одной части на миллион на Arduino с керамическим резонатором, и несколько частей на миллиард, если у вас нет кристалла. Видеть это экспериментальное исследование стабильности часов Arduino. Эта проблема со стабильностью — самое первое, что вы должны исследовать. и исправить.

  2. Многие GPS-модули поддерживают скорость 1PPS (один импульс в секунду). второй) сигнал. Если вы можете использовать этот сигнал, вы можете получить довольно точное измерение фазовой ошибки. Опираясь на NMEA предложения вызовут у вас сильное волнение, которое затем придется компенсировать, установив постоянную времени PLL на очень большую значение.

,

0

Точность 700–1400 мс кажется вам большой проблемой. Обычно это +- 10-20 мс. Вы можете предоставить свой код? Или, по крайней мере, важные его части? Возможно ли, что вы использовали функцию задержки?

Если это так, вам обязательно следует прочитать о функции millis и о том, как использовать ее в качестве таймера. Это значительно повысит «точность». Потому что при вызове функции задержки чип не просто ждет заданное количество секунд. Чип также вызывает функцию yield. Это в основном используется, чтобы убедиться, что вся фоновая работа выполнена, прежде чем продолжить.

Что также может вызвать у меня проблему, так это любые модификации микросхемы часов. Я видел код, в котором люди изменяют частоту ШИМ-сигнала. Это также влияет на часы во всех остальных отношениях.

,

Керамический резонатор (например, на Uno) обычно имеет скидку около 0,1% (1 мс в секунду). В худшем случае скидка 0,5%. Кристалл намного лучше., @Edgar Bonet


0

Вы не можете просто обновить отображение секунд, а затем 1000 (измеряется Arduino) миллисекундами позже, обновите часы, если появились новые сообщение не пришло.

Почему бы и нет? Я бы так и сделал:

  • Ведение часов в памяти
  • Обновлять при получении сообщения GPS
  • Каждые 1000 мс (в миллисекундах) отсчитывайте часы, если сообщение не пришло, и в любом случае обновляйте отображение.

Что-то вроде:

SimpleTimer Timers;
unsigned long MyClock_sec = 0;
bool clockIsTicked = false;

void setup()
{
   Timers.setInterval(10L,  GpsCallback);     // если сообщение, обновить MyClock & установить флаг
   Timers.setInterval(1000, DisplayCallback); // если нет флага, ++MyClock; обновить дисплей
}

void loop(){
   Timers.run();
}

void GpsCallback(void){
   // если сообщение GPS {
   // Мои часы = <<GPS_TIME>>;
   // ++часыотмечены;
   // }
}

void DisplayCallback(void){
   if( !clockIsTicked )
      ++MyClock;

   // обновить дисплей
   clockIsTicked = false;
}

(Не скомпилировано и не протестировано — считайте это наброском псевдокода).

,

0

Что, если бы вы начали с самых точных часов Arduino, какие только могли построить, а затем периодически обновляли время через GPS? Для этого вам понадобится Arduino с кристаллом хорошего качества и библиотека TimerOne.

Пример Pro Mini с кристаллом хорошего качества

Pro Mini с кристаллом хорошего качества

Используя этот скетч с модулем часов I2C Pro Mini и TM1637, я смог построить часы, которые «отстают» на 45 секунд в год. GPS или NTP не требуются :)

// Начните с комментирования кода коррекции времени "Fine"
// и сначала определяем "грубую" временную коррекцию.
#include <TimerOne.h>

int8_t secondCounter = 0;
int8_t minuteCounter = 0;
int8_t hourCounter = 12;
volatile int8_t oneSecondElapsed = 0;

// "Грубая" временная коррекция.
// 1000000 ДОЛЖНО = 1 секунда. Отрегулируйте это значение, чтобы компенсировать «быстрое»
// или "медленный" осциллятор. На моем Pro Mini 1000032 медленнее на 0,5 секунды.
// 24 часа, 1000000 слишком быстро на 1,5 секунды за 24 часа. «Исправление» состоит в том, чтобы
// чередуем число между 1000032 и 1000000, затем перезапускаем таймер
// два раза каждую минуту.
// ПРИМЕЧАНИЕ. Использование числа от 1000000 до 1000031 не имеет значения, поскольку
// "зернистость" расчета, выполненного в TimerOne.cpp, setPeriod().
// Это каждые 32, когда изменение действительно будет иметь значение. Используйте кратные
// из 32, например, 999968, 1000000, 1000032, 1000064, 1000096, 1000128 и т. д.
unsigned long oneSecond = 1000032;

void setup(){

  Timer1.initialize(oneSecond);
  Timer1.attachInterrupt(oneSecondTimerISR);

  Serial.begin(9600);
  displayTime();

}

void loop(){

  // Обновлять переменные времени каждую секунду.
  if(oneSecondElapsed){

    // счетчик от 0 до 60 секунд.
    secondCounter++;

    // "Точная" временная коррекция.
    // Чередование между 16 секундами на 1000000 (быстрее, чем фактическое время),
    // и 44 секунды на 1000032 (медленнее реального времени).
    // 15 секунд и 45 секунд стали медленнее на 1/2 секунды через 2 дня.
    // 16 секунд и 44 секунды стали медленнее на 1/2 секунды через 7 дней.
    if(secondCounter == 14){
      oneSecond = 1000000; // Быстрее, чем реальное время.
      Timer1.initialize(oneSecond);
    }
    else if(secondCounter == 30){
      oneSecond = 1000032; // Медленнее реального времени.
      Timer1.initialize(oneSecond);
    }

    // Переменные хронометража.
    if(secondCounter > 59){
      secondCounter = 0;
      minuteCounter++;
      if(minuteCounter > 59){
        minuteCounter = 0;
        hourCounter++;
      }
      if(hourCounter > 23){
        hourCounter = 0;
      }
    }
    oneSecondElapsed = 0;
    displayTime();
  }
}

void oneSecondTimerISR(){
  oneSecondElapsed = 1;
}

void displayTime(){
  if(hourCounter < 10){
    Serial.print("0");
  }
  Serial.print(hourCounter);
  Serial.print(":");
  if(minuteCounter < 10){
    Serial.print("0");
  }
  Serial.print(minuteCounter);
  Serial.print(":");
  if(secondCounter < 10){
    Serial.print("0");
  }
  Serial.println(secondCounter);
}
,