Как стать автором
Обновить

SDR приемник GPS на микроконтроллере

Уровень сложностиСредний
Время на прочтение28 мин
Количество просмотров25K
Всего голосов 128: ↑127 и ↓1+160
Комментарии284

Комментарии 284

Вопрос крик души автору как специалисту в GNSS, пусть и не по данному проекту. Насколько реально при помощи ЦОС отфильтровать сигнал навигации от отходов жизнедеятельности вояк, чтобы в крупных городах снова можно было нормально пользоваться навигатором...?

Так вояки тоже не дураки, там люди явно постарались, чтобы отфильтровать помехи было сильно проблематично.
Так что думаю, что без системы направленных антенн и многоканальных приемников не обойтись.

Сигнал губят не совсем вояки, а переотраженки от зданий и сооружений и иные помехи. Есть специальные антенны, снижающие такие факторы, но они нифига не компактные, называются Choke Ring. Если же речь именно о зонах, попадающих под целенаправленное искажение, то оно для того и сделано, чтоб его не обходили ) Можно сделать невысокое кольцо из меди или иного металла, вокруг антенны, снизив боковые помехи, но при этом уменьшится видимое созвездие, если высокая точность не нужна, то частично поможет. Но это не для карманных решений. Я живу не в очень крупном городе, и по роду профессиональной деятельности работаю с GNSS оборудованием, но еще не встречался с полным отказом обработки данных приёмником. Хотя в Москве и Питере народ жалуется, на систематические высокоскоростные перемещения в пространстве по мнению их навигатора )

В Москве точно глушат, причем не просто генератор шума ставят, а имитируют сигнал спутников (видел ситуацию, когда телефон показывает кучу спутников с последовательно идущими номерами 1-12, уровень сигнала высокий, но координаты приемник определить не может).
Где-то пишут, подмена сигнала сложней - там приемник к аэропортам "перебрасывает".

Именно что. Едешь на машине, попадаешь под говнилку, стрелка начинает рандомно прыгать по району, навигатор судорожно перестраивает маршрут и говорит полную ересь, несколько раз из-за этого пропускал повороты и перестроения. Это большими кусками внутри ТТК и практически сплошняком внутри Садового. Как полагаю, излучается с крыш. А телепортация во Внуково это точечно возле Кремля, она там давно и в принципе особо не мешала.

Понятно что не карманное, но в площадь автомобильной крыши думаю ведь реально вписать? Сделать ФАР, чтобы сделать ДН более острой в небо, и еще окружить ее таким экраном.

"Сделать ФАР"
Подозреваю, что это будет стоить >2000$, а то и больше ...

Ну, пассивная ФАР в принципе ведь вещь не космической сложности, особенно при наличии единичных антенн промышленного производства (а для GPS они есть). Разместить их матрицей с точно выверенным шагом и завести в сумматор кабелями выверенной длины. Нарезать в размер и качественно оконцевать кабеля тоже могут хоть на алике. Сумматор, у которого 1536 МГц попадало бы в рабочий диапазон, можно попробовать поискать в оборудовании для спутникового ТВ или сотовых репитеров. Разве нет?

Думаю можно уложиться в 500-600. В целом nullsteering антенну можно сделать на основе того же KrakenSDR и ПК с N100. Правда с krakensdr я не совсем уверен что сигнал и помеха влезут в его динамический диапазон, там же АЦП всего 8 бит. Но сделать свой приемник на 6-7 когерентных каналов задача не то чтобы плёвая, но выполнимая. Как вариант взять железо от RSP1, там и L-диапазон и 12 бит. Единственное, такой приемник будет работать только по GPS L1C и Galileo E1. ГЛОНАСС с его частотным разделением банально не влезет в дешевый SDR, а кодовое на других частотах чем у GPS.

Мой текущий приемник принимает около 40 спутников при открытом небе, надо сразу десяток ФАР ставить, чтоб получить нормальное решение, ибо они не по центру, а размазаны по всему небосводу. Я еще молчу, что при этом используется 3 частоты сигнала. В общем на крыше ведерко металлическое, невысокое, в центр антенну, дешево и сердито, большую часть помех отсеет, ценой снижения качества навигационного решения. Но для бытовых целей пойдёт.

Зачем Вам десяток ФАР? ФАР тем и отличается от других типов антенн, что позволяет синтезировать желаемую ДН, в том числе и многолучевую.

Что касается "ведерка на крыше", то оно конечно исказит ДН антенны, но не факт, что в желаемую Вами сторону. Лучше просто поставить антенну достаточно низко в центре большой металлической плоскости, коей является крыша авто - это приподнимет ДН над горизонтом. Только желательно, чтобы на этой крыше не было других выступающих металлических конструкций, во избежание малопредсказуемых искажения ДН.

Ведерком просто отсекается боковая помеха, не более, повторюсь, проверено на практике, но не в таких жестких условиях глушения. Правда было не ведёрко, а подобие из пищевой толстой фольги. Хотя это и есть изменение ДН, примитивное и в лоб, на удачу. Ну а про ФАР, даже одной, думаю не всё так просто, ибо надо динамически перестраиваться под изменяющееся созвездие спутников, они ж летят непрерывно. Стоимость такой системы наверняка приблизится к стоимости авто ) Но я не великий спец по ФАР и её работе при таком уровне помехи, как тут описывали.

Да ничего ведерко не отсекает, если, конечно, его размер не больше длинны волны на порядки. Вы рассматриваете ведерко как некий непрозрачный экран, что при его размерах, соизмеримых с длиной волны делать некорректно. Его надо рассматривать как пассивный элемент антенны, переизлучение от которого (в основном от кромок) интерферирует с прямой волной, формируя результирующую ДН, и отнюдь не такую, как представляется при рассмотрении его как некоего непрозрачного экрана.

проверено на практике

снимали ДН получившейся антенной системы?

Но я не великий спец по ФАР

А по остальным типам антенн и принципам их работы? Что то я сомневаюсь, что хоть какой-то вузовский курс по антеннам сейчас обходит стороной ФАР. Я учился в начале 80-х и уже тогда ФАР уделялось очень много внимания (хотя до широкого практического применения тогда было еще далеко, во всяком случае в союзе).

Пробовали такой эксперимент, в итоге выяснили что даже в теории не получится. Внутри ТТК помеха примерно так на 40-50 дБ мощнее сигнала. И это не имитационная помеха, а просто белый шум на всю полосу L1. Никакая антенна не даст такой разницы между главным и боковыми лепестками, при условии что главный лепесток широкий, а не 1-2 градуса.

Ну да, мое предложение не панацея на все случаи, но в некоторых случаях помогало. Но там были именно боковые помехи и не такие мощные, но работать не давали нормально, решение портилось сильно. Всё зависит от помехи и её мощности. Ну и глушилка может стоять на здании, тогда ничего не поможет, сигнал же также сверху, как и спутниковый, не экранируешь.

Обычно такого ада с "помеха на 40-50 дБ мощнее сигнала" по физическим причинам не происходит. Слишком много надо в такое всаживать энергии. Но вот сигнал GPS - это как раз исключение из правил.

Спутники очень далеко, и сигнал от них невыносимо слабый. Поэтому любой мудозвон с дешёвой глушилкой может убить GPS практически наглухо в радиусе пары сотен метров. А если глушением занимается правительство, то дело вообще швах.

>> любой мудозвон с дешёвой глушилкой может убить GPS практически наглухо в радиусе пары сотен метров

С этим действительно беда, дальнобойщики часто глушат трекеры ПЛАТОНа, а заодно всех соседей по дороге и дорожную технику - грейдеры, бульдозеры и прочих геодезистов.

Учитывая что типовой уровень сигнала ГНСС это -125...-130 дБм, а в Москве работают глушилки мощностью по несколько кВт, это вообще не вопрос

Сигнал внеполосный легко отфильтровать чем угодно, но то, о чем вы пишете - там помеха на несущей частоте сигнала. От этого поможет только пространственная фильтрация - т.е. нужна ФАР.

Так как спутники в зените очень редко появляются, то для уверенного определения координат придется направлять лучи на каждый спутник в отдельности - тут уже нужна цифровая ФАР, она позволяет для одной физической решетки сформировать столько диаграмм направленности (и лучей), сколько есть вычислительных ресурсов.

Но эффективнее всего использовать адаптивное пространственное подавление помех - для трех помех достаточно 4х антенн. Однако, работает оно только для мощных помех, а слабые имитационные помехи не увидит под шумами, а значит и не подавит, нужно формировать нули в ДН искусственно, что еще больше усложняет ситуацию.

Подведем итог: для защиты обычного пользователя такая система будет слишком дорогой, дешевле (и полезнее) научиться ездить без навигатора.

Спасибо за статью!

Возможно ли это устройство доработать до RTK модема сантиметровой точности позиционирования?

Не думаю, точность тут получилась довольно низкая.

Расстояние определяется по фазе PRN. Минимальный шаг фазы это длительность семпла ADC.

Получается что точность определения Time Of Fly (TOF) и, как следствие, расстояние до спутника зависит от частоты дискретизации ADC.

В данном случае ADC:16368000 Hz, время ADC семпла получается 61.094819 ns. За время 61ns радиоволна пролетает 18.315766 m

Получается с этим оборудованием в принципе не добиться ошибки координат меньше 18м.

Я правильно рассуждаю?

Ошибка измерения TOF зависит от частоты тактирования ADC. Так?

По логике действительно так, вот только даже в относительно бытовых модулях часто оказывается более высокая точность. Как этого добиваются - не знаю.
Возможные варианты - просто усреднение (аналогично тому как в АЦП используют оверсемплинг) или какая-то интерполяция данных.
Возможно, @GreatGehar что-то подскажет?

Кстати в мск вполне сносно работает gps в диапазоне l5, почти без телепортации. Без него боль.

Посмотрел свой смартфон - к сожалению не умеет L5 ((

Вообще удивляет нежелание производителей делать многочастотные GPS-приёмники. Тому же гражданскому сигналу L2C уже сто лет в обед, имеется на всех спутниках, запущенных после 2005. Источник: New Civil Signals

Так вроде если приемник умеет работать с разными системами (GPS, Глонасс, Beidou и проч.) - он уже по определению многочастотный, нет?
А кстати, где вы смотрели в телефоне поддерживаемые диапазоны? у меня ничего подобного не нашлось, GPS Test тоже такого не знает.

Как раз в GPS Test и смотрел. Однако это интересный вопрос, поищу спецификацию чипа.

>> так вроде если приемник умеет работать с разными системами (GPS, Глонасс, Beidou и проч.)

Это "мультисистемные приёмники". Многочастотные это когда несколько диапазонов L1=>1.5-1.6 GHz, L2=>1.2 GHz и др.

SDR в данном случае означает, что приемник не содержит готовых GPS-модулей или специализированных микросхем для обработки GPS сигналов

MAX2769 - universal GPS reciever.

"для обработки GPS сигналов"
MAX2769 просто переносит частоту принятого сигнала вниз, и оцифровывает сигнал.
Как таковой, обработкой я этот процесс назвать не могу.

Тогда если взять любую ПЛИС вместо МК - это тоже будет "SDR"?)

TCXO - кварцевого генератора

Не просто кварцевый генератор, а термокомпенсированный.

SDR - Software Defined Radio (Программно Определяемое Радио), в то время как в ПЛИС реализуются аппаратные блоки и обработка не программная. Хоть для описания прошивок ПЛИС и используются текстовые языки, отдаленно похожие на обычные языки программирования, принципы работы у них абсолютно другие. Это как песок и вода - и то, и другое можно зачерпнуть в стакан, или в них можно погрузиться, но на этом схожесть заканчивается.

Илья, это огонь! Тысячу лет назад гонял поиск в матлабе по данным с SDR - а теперь в МК влезло )

Систематическая ошибка - может быть из-за того, что из псевдодальности нужно вычесть время распространения сигнала в кабеле? Ну или подобрать задержку эксперементально пока ошибка не снизится.

А насчет шума - если в коммерческих приемниках усреднение убирать - они тоже шумят ппц. Потому фильтруют дико, секунд за 5-10 с моделью движения чтобы в глаза сильно не бросалось.

>> может быть из-за того, что из псевдодальности нужно вычесть время распространения сигнала в кабеле?

Категорическое нет. Для всех спутников (каналов) длина RF-тракта одинакова и она автоматически попадёт в dT (расхождение часов). В этом и прелесть систем GNSS в отличие от старых дальномерных систем от радиогеодезии.

PS: проблема с задержкой сигнала имеется у ГЛОГАСС т.к. у каждого спутника своя частота. Вот там нужны поканальные калибровки.

Статья огонь. Респект и уважение! Но у меня даже повторить за автором не получится я думаю. Были намётки кстати делать подобное с помощью нейросети: сгенерировать набор и скормить, далее пускай находит похожее и говорит координаты но дальше идеи не пошло.

Большое спасибо за статью! За обе части. Четко и подробно изложено. Особенно хочу отметить то, что Вы не пропускали те моменты, где у Вас что-то не получалось, а подробно излагали. Идущим за Вами это поможет. Удачи в последующих проектах!

Спасибо за статью!

Статья отличная! У меня появился вопрос по цифровому квадратурном детектору. А что, такая схема будет работать? Там же во всех схемах, работающих во временной области, нужны ФНЧ в том или ином виде. Согласно таблице здесь. Или такая схема как в статье будет работать исключительно с сигналами ГНСС?

Здесь данные после переноса частоты подаются сразу на коррелятор. А процесс корреляции моно рассматривать как процесc фильтрации: https://ru.wikipedia.org/wiki/Корреляционный_фильтр
А благодаря тому, что данные PRN - имеют широкую полосу, фильтр получается мало чувствительным к помехам.

А где происходит решение системы уравнений сферической тригонометрии которое необходимо для вычисления широты и долготы?

"Чтобы можно было назвать получившуюся конструкцию полноценным GPS приемником, нужно было перенести вычисление координат на микроконтроллер. Я рассмотрел несколько вариантов видов готовых алгоритмов, но в итоге решил использовать ту же RTKLIB."
Код здесь: https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_main/GPS/RTK/solving.c

данные эфемерид это сколько килобайт?

Эфемериды передаются в subframe 2/3.
Один subframe - это 300 бит, но из него нужно вычесть первые два слова (60 бит) - данных именно эфемерид они не содержат. И еще 8*6=48 данных CRC в subframe есть.

Это только у меня именно эта статья выдает ошибку 504 если я пытаюсь открыть по ссылке с comments?

у меня тоже

У меня долго висит, потом открывается.

Вы I или Q сигнал считываете по SPI?

"Данные берутся с вывода I0 MAX2769."
Если что, то это Sign - фактически, полярность сигнала.

Так там не зря OR указано.
В таблице режимов в моей статье видно, что первые два - дифференциальные, остальные - CMOS.

Каким образом одно битный adc поток нулей и единиц в шине i0 превращается в float числа по которым можно увидеть psk модуляцию?

После коррелятора производится суммирование всех установленных битов.
Вот и получается int16. Так как есть деление на I/Q каналы - значений два.
float тут не нужен.

Как это суммирование нулей и единиц может дать отрицательный int16?

Константа (1638/2) из результата суммирования вычитается.

Извините, ошибся, речь про I1.
В статье тоже ошибка была, поправил текст.

Лучше всего для этой подходит интерфейс SPI, работающий в связке с DMA. DMA настроен в режим Circular, т.е. дойдя до конца выделенной памяти, DMA автоматически начнет запись в ее начало. В итоге мы получаем в памяти набор данных, в которых один бит соответствует одной выборке АЦП MAX2769.

У Вас SPI+DMA работает в точности как показано на графике ниже?

Вся обработка по вычислению координат укладывается в 1ms?

SPI+DMA работают действительно так.
Только DMA передает 16-битные слова,
так что счетчик DMA считает: 0 -> 1023 -> 2046 -> 0

"Вся обработка по вычислению координат укладывается в 1ms?"
В статье я все описал - начиная с заголовка "Определение координат на микроконтроллере"

Вообще здорово, когда программист разбирается в конкретной предметной области. Особенно такой ёмкой как GNSS.

Программирование - это всего лишь инструмент, своего рода, отвертка. Настоящую же ценность представляют именно предметные знания.

А эти спутники в GNSS пакетах передают еще какую-нибудь инфу? Например уровень солнечной активности или наработку на отказ.

Знаю только, что параметры ионосферы передают в subframe 4.

Мне как обывателю эту статью не осилить, респект автору.

А можно пару вопросов, для автопилотов.

  1. Какова максимальная точность автономного приемника gnss?

  2. Если получать данные для коррекции раз в день, какую максимальную точность можно получить?

  3. Есть полностью (90%) российское оборудованин для получения высокоточных координат?

  1. Для модуля NEO-M9N производитель заявляет точность 1.5м. В даташит не заглядывал, думаю, это с данными от WAAS.
    У многочастотных приемников точность моет быть выше.

  2. Не слышал, чтобы данные коррекции можно было передавать впрок. Данные передаются или в реальном времени, или производится пост-обработка записанных данных.

  3. Честно сказать, не слышал про такое. У военных, думаю, есть чипы для обработки спутниковых данных, но RTK им, думаю, не интересен - поправки негде взять.
    Зная про приемники Navis, но там вроде есть импортные чипы.

Мне как обывателю эту статью не осилить, респект автору.


Попытка понять GNSS это серьезная тренировка для мозга. Понять какой путь проходит сигнал от спутника до числа с широтой и долготой - это как грызть гранит.

Тут и

—Практическая Астрономия (широта, долгота, эфемериды, Кеплеровы элементы орбиты)
—Физика (кинематика, волны, электро-динамика, квантовая механика (атомные часы)),
—аналоговая электротехника RF, PLL BPSK,
—цифровая схемотехника MCU SPI FPGA DSP,
—Кибернетика (CDMA, TAУ)
—ЦОС (корреляция, FFT, свёртка)
—Математика (Булева алгебра, численные методы,
тригонометрия, линейная алгебра (скалярное произведение))

И может быть еще то, о чем я даже не догадываюсь типа общей теории относительности.

GNSS навигация - огромная междисциплинарная тема. И всё этого для того чтобы тракторы на автопилоте ровно грядки прокладывали для засева картошки.

Понять GNSS с наскока - трудно. Однако при наличии хорошей методички с добротными иллюстрациями, блок-схемами и формулами - вполне возможно.

Ибо всё что придумали люди имеют конечную сложность. Не инопланетяне же рассказали американцам как GPS развернуть?..

Что не скажешь про природу (то что придумала природа - там сложность, видимо, просто бесконечная).

Таки да, эффекты теории относительности влияют на GNSS.

Мне как обывателю эту статью не осилить, респект автору.

На канале Michel van Biezen Существует курс по GNSS 100 уроков.
Названия начинаются на
Special Topics - GPS (1 of 100)
Special Topics - GPS (100 of 100)

Получается, что MCU должен захватывать последовательный синхронный поток данных со скоростью ~16Mbit/s (2 Mbyte/s). Немало.Лучше всего для этой подходит интерфейс SPI, работающий в связке с DMA

Можно ещё RMII интерфейс рассмотреть.

Глубоко в Reference Manual на SM32F4 не вчитывался. Но там MII через встроенный MAC работает. Не думаю, что он позволяет работать с пакетами произвольного формата.

Странно. MII позиционирует себя как Media Independent Interface.

Из Википедии - Независимость от среды передачи означает, что существует возможность использования любых PHY-устройств без необходимости смены или переработки аппаратуры MAC-блока.
Вот в MAC все и упирается.

А какая там модуляция в этом сыром GNSS сигнале?
Частотная, фазовая, амплитудная или ещё какая-то комбинированная?

В предыдущей статье про модуляцию было. При передаче PRN - фазовая манипуляция несущей, при передаче навигационных бит - все чипы PRN инвертируются логически.

1--Насколько сильно упростится код прошивки, если мне надо только время?

2--Достаточно ли одного спутника, чтобы принять из космоса только точное время?

1 - определённо упростится, трекинг в таком режиме у меня работал стабильней.
В репозитории для STM32 лежит проект для одного спутника - https://github.com/iliasam/STM32F4_SDR_GPS/tree/develop/Firmware - "project_single_sat"

2 - точное время - нет, будет неопределенность на время полета сигнала, она около 20 мс.

2 - точное время - нет, будет неопределенность на время полета сигнала, она около 20 мс.

погрешность 20ms для бытовых нужд это нормально.

Можно получить точное время, если известны координаты антенны, тогда остается как раз 1 неизвестная - расхождение шкал времени приемника и спутника. Но остается вопрос про достоверность полученного значения, по сигналу одного спутника достоверность не знаю как проверить.

Получается, что разбирая сигнал Nav Data только от одного спутника можно получить точное время и дату UTC+0 с погрешностью 20ms. Так?

Да, примерно такая погрешность будет.

И на какое битовое поле в NavData надо смотреть, чтобы обнаружить там точное время UTC+0?

В Subframe1 есть Hand Over Word (HOW), в нем есть TOW (time-of-week).
Также в Subframe1 10-битный Week Number.
Объединяя их, можно получить время.
https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_single_sat/GPS/nav_data_decode.c
Только там нужно знать год примерно, чтобы знать, от какой даты будет отсчитываться Week Number. Плюс, вроде там есть какие-то тонкости с leap second.

Поэтому еще есть такое в официальной документации:
20.3.3.5.2.4 Coordinated Universal Time (UTC).
" Page 18 of subframe 4 includes: (1) the parameters needed to relate GPS time to UTC"
Дальше идут параметры и формулы.
Эти параметры я не использовал.

Проект, однозначно, заслуживает уважения, не думал, что на микроконтроллере можно подобное сотворить. Есть замечание про число каналов: для определения координат и расхождения действительно достаточно принимать сигнал четырех спутников, только достоверность решения нельзя проверить: если по какой-то причине по одному или нескольким спутникам будут некорректно определены псевдодальности, решение навигационной задачи по четырем спутникам все равно сойдется, только оказаться можно где угодно, хоть с другой стороны Земли. Для проверки решения нужно принимать минимум 5 спутников.

Кто, собственно, декодирует bpsk модуляцию : микроконтроллер stm32 или asiс max2768?

MAX2769 только переносит частоту вниз и содержит АЦП. Никаких демодуляторов там нет:

Вообще выделение битового потока bpsk это чисто формальная задача. Дискретное преобразование Фурье может показывать фазу.
Неужели нет ASICов для демодуляции bpsk из I/Q сигналов?

Чтобы в прошивке на MCU только payload обрабатывать на более низкой частоте семплирования.

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

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

Чтобы в прошивке на MCU только payload обрабатывать...

Для этого используют FPGA, там все что угодно можно сделать. Удобнее всего использовать систему на кристалле SoC ARM+FPGA - в FPGA демодулировал сигнал, передал демодулированные выборки в ARM, обработал, обновил параметры сигнала в FPGA, опять демодулировал и т.д.

Почитал документацию: https://support.elvees.com/docs/Microchips/1892VM268/manual/
действительно интересная штука.
Микросхема позиционируется как микроконтроллер, и там действительно есть GNSS периферия, доступная пользователю. Причем в документации она довольно подробно описана. Фактически этот самый "коррелятор", который я упоминал в начале статьи, хотя он тут не только корреляцией занимается.

C ПЛИС появляется гибкость - можно сделать приемник с какими угодно параметрами.

Похоже, она еще даже не выпущена в свет. Но суть не в этом.

Действительно, есть подобные решения, но все они (как правило) заточены под определенный алгоритм работы, который выбрали разработчики данного чипа. Если вас этот алгоритм не устраивает, то и этот чип вам не подходит.

Посмотрел регистры управления навигационными каналами, похоже они управляют только частотой несущей опорного сигнала, не управляя начальной фазой. Если нам нужно управлять начальной фазой, то уже ничего не сделаешь с этой микросхемой - она не подходит.

В общем, универсального решения для обработки навигационных сигналов не существует и поэтому каждый изобретает этот велосипед по своему.

Какая у Вас частота на выходе PLL внутри MAX2769ETI+?

Честно - никогда не интересовался. Так как на выходе микросхемы сигнал находится на частоте 4.092 МГц, то частота внутреннего гетеродина должна отличаться от частоты спутников 1575.42 МГц на эти 4.092 МГц, скорее всего вниз.

То есть на выходе PLL примерно полтора GHz.
PLL внутри MAX2769ETI+ умножает частоту с кварца на pin15 в 96,25 раз. Так?

Не видно в спеке на MAX2769ETI+ регистра, который отвечает за умножитель в PLL.

Примерно так.
Прямо в таблице с вариантами конфигурации MAX2769 в статье есть коэффициенты.
Частота генератора 16.368 делится на 16 и умножается на 1536.
Получается 1571.328 МГц. Прибавляем 4.092 МГц -> 1575.42 МГц - частота спутника.
Я так понял, значения загружаются в NDIV и RDIV. Есть еще другие регистры для более точной настройки.

Перемножив исходный сигнал на сформированный нами гармонический сигнал нужной частоты (Carrier NCO), мы получим на выходе умножителя новый сигнал - входной сигнал окажется перенесен на более низкую частоту. Обычно в GPS производят перенос сразу на нулевую частоту - т.е. после умножителя мы получаем сигнал, передаваемый передатчиком. Вот только и сигналы, находящиеся по частоте выше/ниже нашего генератора, тоже оказываются перенесенными вниз, и отделить их друг от друга нельзя.

Смеситель по сути перемножитель сигналов. И тут применимы эти две формулы.

Все верно, так перенос частот и производится. Я не стал добавлять формулы в статью, чтобы она проще читалась.

Почему из смесителя (перемножителя) выходит по два провода?

В учебниках по радио технике обычно смеситель вот так обозначают.

С одним выходом.

Вот в FM-Tuner(е) правильно смеситель изобразили.

Я так понимаю, таким образом изображена дифф. пара.

А каким-таким образом в аналоговой RF схемотехнике делают сдвиг фазы синус сигнала любой произвольной частоты на 90 градусов?
Там, что гигагерцовый дифференциатор на операционнике стоит?

Если смеситель ключевой - достаточно пары триггеров.

Если "классический" - можно использовать такую "лестнично-закольцованную" схему (во втором издании она нарисована красивее) с упорядоченными фазовыми сдвигами (Saraga, W., Proc IRE, 1950).

Ключевому - меандр и нужен.

Где Вы вообще купили плату с ASICом MAX2769ETI+?
На чип дип есть похожая, но очень дорогая.

На СrowdSupply есть пара проектов с ASICом MAX2769. Называется iotSDR
https://www.crowdsupply.com/embedinn/iotsdr
и nut4nt
https://www.crowdsupply.com/amungo-navigation/nut4nt

Вот ещё есть универсальный трансивер AD9361 из которого тоже можно сконфигурировать GNSS Front-End
AD-FMCOMMS3-EBZ AD9361 official radio frequency sub board Software Defined Radio
https://aliexpress.ru/item/32860193290.html?sku_id=65446412212&spm=a2g2w.productlist.search_results.6.44a469eana6Lq0

iotSDR стоит более 1000$.
nut4nt сделана на куда более наворочанном NT1065.

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

Скомпоновал всё, что понял вот в этой схеме.

Линий SPI от MCU к MAX2769 у меня нет. Вся конфигурация MAX2769 - тремя резисторами на ее плате. На фото видно, что платы соединяют всего 4 провода.
Питание усилителя антенны через L1 не показано.
В остальном вроде все верно.

Напомню, что в системе GPS используется повторяющийся дальномерный код (PRN), который спутники передают с периодом 1 мс. За время 1мс микросхема MAX2769 выдаст 16368 выборок АЦП.В то же время, один период PRN состоит из 1023 чипов (единичных участков, где фаза модуляции сигнала не изменяется).16368 / 1023 = 16, то есть одному чипу будут соответствовать ровно 16 выборок АЦП. В моем случае, это ровно 16 бит = 2 байта. Получается, один байт = 0.5 чипа, что в дальнейшем сильно пригодится.

Получается что один чип всегда длится 977 ns. Так?

Да.
Правда, если я не ошибаюсь, доплеровское смещение не только на частоту влияет, но и на длительность чипа, но не сильно.

На какой минимально допустимой частоте дискретизации должен работать ADC внутри MAX2769ETI+ чтобы система в теории могла продолжать работать?

Я не могу точно сказать. Полоса передачи - 2.046МГц. По теореме Котельникова, частота дискретизации должна быть в 2 раза больше.
Но будет ли нормально работать на такой частоте трекинг - большой вопрос.

Если приложить щуп осциллографа ко входу adc, то какой нарисуется сигнал во временной области?

Если бы не шум, то там должна быть синусоида с частотой около 4МГц и BPSK модуляцией.
Только шума там очень много, так что без дополнительной обработки ничего интересного, думаю, там не увидеть.

Я накропал скрипт на Python, который моделирует аналоговую часть.

import matplotlib.pyplot as plt
import matplotlib.pyplot as plt2
import math
import numpy as np
from scipy.fftpack import fft, ifft
from scipy.fftpack import fftfreq
#from numpy.fft import fft, ifft

#https://pythonnumericalmethods.berkeley.edu/notebooks/chapter24.04-FFT-in-Python.html
F_carrier=1575420000
T= 977*10**(-9)
T_fs= 1.0/(F_carrier*40)
t = np.arange(0, 5*T, T_fs)
Fsignal = 1.0/(2.0*T)

BitRate = 1.0/(T)

print ('BitRate {} '.format(BitRate))
print ('Fsignal {} '.format(Fsignal))
print ('F_carrier {} '.format(F_carrier))

code = (np.sign(np.sin(2*math.pi*t*Fsignal))* np.ones(len(t))+1)/2

phase_signal =  np.deg2rad(180*code)
phase0 =  np.deg2rad(0)
phase180 = np.deg2rad(180)
FloSin = np.sin(t*2*math.pi*F_carrier+phase0)
FloCos = np.sin(t*2*math.pi*F_carrier+phase180)

Ysignal =  np.sin(t*2*math.pi*F_carrier+phase_signal)

I1=Ysignal*FloSin
Q1=Ysignal*FloCos

I1_fft = fft(I1)
I_fft_filtered = I1_fft.copy()
freq = fftfreq(len(I1), d=T_fs)

cut_off = 9000000.0
I_fft_filtered[np.abs(freq) > cut_off] = 0
I_filtered = ifft(I_fft_filtered)


plt.plot(t, code)
plt.plot(t, Ysignal
plt.plot(t, I1)
plt.plot(t, I_filtered)


plt.title('signal')
plt.xlabel('Time,[s]')
plt.ylabel('BPSK')
plt.grid()
plt.xticks(rotation=-90)
plt.legend(['code','signal', 'I','I_filtered'])
plt.show()

#plt2.stem(freq, np.abs(I1_fft), 'b',          markerfmt=" ", basefmt="-b")
plt2.stem(freq, np.abs(I_fft_filtered), 'b',          markerfmt=" ", basefmt="-b")

plt2.show()



Получается, что на выходе I должен быть вот такой меандр, симфазный с модулирующим кодом. Это если частота LO точно совпадает с несущей 1575420000 Hz

это под увеличением

То есть в самом лучшем случае самая низкая частота на I должна быть 1.023541 MHz

По сути бит I1 (sign) уже показывает мгновенную фазу.

Все верно, в таком случае в I канале будет BPSK код.
Но в реальности такая ситуация невозможна - частота спутника меняется, фаза - тоже, PLL имеет ограниченный шаг перестройки частоты.
Тем не менее, в некоторых случаях такой метод вроде бы используют. Не зря у MAX есть пара выходов - I/Q - их как раз можно использовать, чтобы определять знак имеющегося рассогласования частот.

В предыдущей статье я упоминал прием сигналов GPS на RTL-SDR.
Там АЦП двухканальный (тоже I/Q), работает на частоте дискретизации 2.048 МГц. Гетеродин приемника настраивается прямо на 1575.42 МГц.
АЦП, правда, 8-битнный.

Тогда получается так, что на выходе из смесителя внутри MAX2769ETI+ спутниковый сигнал ещё до конца с несущей частоты (1575.42 MHz) не снят.

Вот и остаётся ещё программно снять сигнал с несущей второй ступни, которая равна 1575.42 MHz- 1571.328 MHz=4.092 MHz.

Нужен второй каскад смесителя.

Так вот именно этом смеситель я и реализовываю в микроконтроллере.
"MAX2769 переносит сигнал не на нулевую, а на промежуточною частоту. Перемножив исходный сигнал на сформированный нами гармонический сигнал нужной частоты (Carrier NCO), мы получим на выходе умножителя новый сигнал - входной сигнал окажется перенесен на более низкую частоту. Обычно в GPS производят перенос сразу на нулевую частоту - т.е. после умножителя мы получаем сигнал, передаваемый передатчиком. "

 частота спутника меняется, фаза - тоже


С какой максимальной скоростью (радиан в сек / градусов в сек)может меняться фаза несущей сигнала с GPS спутника?

Тот внутренний аналоговый фильтр у Вас работал как полосовой (baseband) или как фильтр нижних частот (lowpass)?

Сигнал после смесителей в MAX находится на частоте 4Мгц - при той конфигурации, что описана в статье.
Конечно, там полосовой фильтр.
Спектр там примерно такой:

Это - только выходы I0/I1.
Спектр построен при помощи железа и софта из предыдущей статьи.
Пик в центре - возможно, связан с плохой работой железа или софта.
Замечу, что подключение антенны не особо влияет на спектр.

В даташите на MAX, правда, несколько по другому спектр показан, возможно, это из-за ограниченного динамического диапазона АЦП:

А если к q1 подключить второй stm32 он тоже координаты сможет вычислить с той же прошивкой?

Если по SPI включить выходы Q - то да, должен.
А так по таблице режимов MAX2769, задаваемых резисторами, везде только I выход работает. Проверил на своей плате - выход Q в Z-state.

Что если сделать отладочную плату с MAX2769ETI+ и MCU (или MAX2769ETI+ & FPGA) которая будет только выдавать де-модулированый BPSK в отдельный SPI на битовой скорости 1.023 Mbit/s? Своего рода GNSS-BPSK-DEV-KIT.

Это будет полезным узлом? Или какая-то важная информация будет потеряна?

Можно ли разгрузить STM32 разбив прошивку на два STM32 ?

Можно ли будет подключаться к этому SPI другим MCU и делать слежение за спутниками и остальную обработку вплоть до вычисления широты и долготы?

"выдавать де-модулированый BPSK в отдельный SPI"
У каждого спутника свой поток данных, как их выдавать вместе, причем синхронно?
Данные о доплеровском сдвиге частоты, если я правильно понял идею, будут потеряны. А они нужны для вычисления скорости (у сея в проекте я это не использую).
Я лично не думаю, что кому-то это будет нужно.

"Можно ли разгрузить STM32 разбив прошивку на два STM32 ?"
Можно, но зачем? Контроллер справляется, дешевый ESP32 даже графику рисует.

" другим MCU и делать слежение за спутниками"
Без слежения BPSK не декодировать.

У каждого спутника свой поток данных, как их выдавать вместе, причем синхронно?

Одна плата на один спутник. В NVRAM по UART-CLI прописывать с каким спутником работать после пересброса питания.

Я лично не думаю, что кому-то это будет нужно.

Этой платой с одного спутника можно будет получать Nav Data. Например точное время, альманах, эфемериды, телеметрию. Плата для изучения основ GNSS для школьников и ВУЗовцов.

Что-то никогда не замечал в трафике NMEA протокола эфемерид, альманаха, смещения атомных часов. Эти NavData вообще выдаются на улицу по NMEA?

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

отличный вопрос! nmea наверное это просто не предусматривает, тем не менее для быстрого старта приемники gnss умеют загружать в себя эти данные из файлов на сайтах производителя модулей (а то и платно) - и внешние и смартфоные - не по nmea, а некоторому расширенному протоколу, в случае ublox, например, много инфы и примеров и софта про ихний протокол ubx

По ubx можно очень много что посмотреть, но вот свой приемник заставить выдать значения эфемерид я не смог. Там есть сообщение "UBX-RXM-SFRBX", это сырые навигационные данные, из них можно легко выделить эфемериды.

Если посмотреть на таблицу истинности инвертированной операции XOR (XNOR), и представить, что значениям "0" соответствует негативный знаковый сигнал "-1", то получается, что XNOR действительно может заменить умножение (без переносов). 

Тут не совсем очевидно. С первого прочтения.
Понимается так
--Ок, "0" заменяем на "-1" а "1" на что заменяется?
Нужно пояснение, что 1 остается 1.

""0" заменяем на "-1" а "1" на что заменяется?"
Если в тексте не указано, что нужна замена, значит она не нужна...

Поэтому я решил использовать несколько другой подход, основанный на том, то формируемая частота очень близка к 1/4 от тактовой частоты. Предельное доплеровское смещение в этом проекте я установил в 7кГц, что означает, что необходимое предельное смещение частоты составляет около 0.2%. Получается, что при этом период формируемого сигнала составляет 4 выборки (бита), а отклонение частоты создает только фазовой сдвиг формируемого сигнала.В итоге, чтобы получить буфер, заполненный данными нужной частоты, достаточно разбить его на 32-битные слова, и записывать в них значения из таблицы:const uint32_t sin_buf32[4] = { 0x33333333, 0x9999999, 0xCCCCCCCC, 0x66666666 };Индекс в таблице - как раз значение фазы, его нужно периодически менять, а методика расчета фазы та же, что и в DDS генераторе.

Я скомпоновал иллюстрацию к этому параграфу.

F=4.092 MHz
F=4.092 MHz

У меня SPI настроен на прием в режиме LSB-first, 0x33 = 0b00110011, так что минуса перед sin/cos не нужны.

Вот так Вас работает SPI?

Я этой таблицы вообще не понимаю.
Первый бит, который примет SPI, будет помещен в LSB (D0), последующие биты будет помещаться "слева", шестнадцатый бит будет помещён в MSB (D15).
0bMSB.....LSB

Получается, что через 16 семплов в принятом SPI word ( 16бит) новые однобитные семплы окажутся слева (<--).

Старые семплы справа (-->).

Так?

Я этой таблицы вообще не понимаю

Это не таблица. Это раскадровка содержимого SPI сдвигового регистра на каждом такте.

значит так

Индекс в таблице - как раз значение фазы, его нужно периодически менять, а методика расчета фазы та же, что и в DDS генераторе.

Кто выбирает какаю фазу несущей использовать из четырёх доступных (- 90 0 90 180) для однобитного LO в программном смесителей второй ступени?

Как вычислить или измерить нужную фазу несущей? Ведь вероятность ошибиться 75%.

Я так понял, что Вы подбираете фазу несущей вручную глядя на сигнальное созвездие.
Так?

Для этого в приемники встраивают системы ФАПЧ (фазовая авто подстройка частоты):

https://ru.wikipedia.org/wiki/Фазовая_автоподстройка_частоты

На периоде накопления GPS сигнала 1 мс частота и фаза меняется линейно, её легко предсказать. Задача системы ФАПЧ определить, на сколько за текущую миллисекунду отклонилась фаза сигнала от рассчитанного значения (используется сигнал ошибки) и выдать скорректированное новое значение. Если ошибка окажется слишком большой, петля ФАПЧ "сорвется" и нужно будет заново выполнять поиск сигнала.

используется сигнал ошибки

Кто вычисляет этот сигнал ошибки? И по какой функции сигнал ошибки пересчитывается в индекс для массива sin_buf32 ?

const uint32_t sin_buf32[4] = { 0x33333333, 0x9999999, 0xCCCCCCCC, 0x66666666 };

Замечу, что в процитированной части теста речь идет только про процесс формирования данных несущей частоты.
Код: https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_single_sat/GPS/gps_misc.c#L325
Можно видеть, что внутри функции значение фазы берется из значения аккумулятора фазы.
https://ru.wikipedia.org/wiki/Цифровой_вычислительный_синтезатор


В коде приемника для одного спутника я оставил тестовый код, который просто заполняет бинарными данными sin/cos массив, это тот самый Carrier NCO:
https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_single_sat/GPS/gps_misc.c#L482

Из картинки видно, что прямого управления значением фазы у меня нет, есть управление частотой NCO, которое влияет на фазу.
https://ru.wikipedia.org/wiki/Фазовая_автоподстройка_частоты
После того, как Carrier NCO отработал, значение аккумулятора фазы нужно сохранить, а потом загрузить его вновь в Carrier NCO перед началом следующего вычисления.
И да, изначально при старте трекинга фаза неизвестна, на IQ диаграмме при этом будет круг.

PLL/FLL - используются для управления частотой генератора несущей частоты (Carrier NCO). Изменяя частоту, можно управлять и фазой сигнала этого генератора. Слежение за фазой сигнала необходимо для извлечения из сигнала навигационных данных.

Я так понял, что нет возможности плавно менять частоту (Carrier NCO) гетеродина на смесителе второй ступени. Только фазу.

Частота Carrier NCO определяется константным массивом и, следовательно, постоянная. Составляет F=4.092 MHz.
Можно лишь слегка пошатать фазу Carrier NCO на +-122.1896 ns с шагом 61.094 ns меняя U32 паттерн в массиве sin_buf32.

Как же Вы меняете частоту Carrier NCO, если паттернов всего 4, и они все на оду частоту 4.092 MHz?

Изменение частоты имитируется через смену фазы.
Ниже я показал пример изменения фазы в процессе формирования данных несущей частоты.
Можно видеть, что в результате такого изменения одна выборка как бы пропускается, если сравнивать данные до/после:

В функцию gps_shift_to_zero_freq() передается значение частоты, на основе которого она вычисляет, как часто нужно манипулировать фазой.

Фаза принятого синуса зависит от положения приёмника относительно источника волны.

Иначе говоря, фаза принятого синуса зависит от расстояния между передатчиком и приемником. Может меняться в диапазоне от 0 до pi.

Для частоты GNSS L1 1575.42МHz длина волны равна 19.0294 cm.

GPS спутник, который движется на скорости 3км/s пролетает эти 19.0294 cm за 63.4us.

Как это можно отслеживать фазу от 0 до pi за 63.4us?

GNSS Front-End следит только за двумя фазами 0 и 90Deg. Получается, что данные правильно принимаются только в два мгновения каждые 63.4us. Так?

Фаза принятого синуса зависит от положения приёмника относительно источника волны.

Так же, фаза зависит от текущего времени. Без модуляции значение принятого сигнала определяется формулой: \sin(\omega t-kx), где ω - циклическая частота, t - время, k = 2π/λ, x - расстояние между спутником и антенной.

Иначе говоря, фаза принятого синуса зависит от расстояния между передатчиком и приемником. Может меняться в диапазоне от 0 до pi.

Фаза меняется от 0 до 2\pi.

GPS спутник, который движется на скорости 3км/s пролетает эти 19.0294 cm за 63.4us.

Скорость GPS спутника относительно неподвижного наземного пользователя около

  • когда спутник на горизонте до 1000 м/с (доплеровский сдвиг частоты ~5 кГц)

  • когда спутник в зените 0 м/с (доплеровский сдвиг частоты 0 Гц)

Как это можно отслеживать фазу от 0 до pi за 63.4us?

Следить за фазой так часто нет необходимости. Для этого есть ФАПЧ. Очень приблизительно работает это так. ФАПЧ раз в 1мс определяет, на сколько сигнал спутника отклонился от прогнозных значений и корректирует прогнозную копию сигнала. Далее вычисляется свертка принятого сигнала со спрогозированной копией сигнала и вновь ФАПЧ определяет величину расхождения принятого сигнала и копии.

GNSS Front-End следит только за двумя фазами 0 и 90Deg. Получается, что данные правильно принимаются только в два мгновения каждые 63.4us. Так?

Ничего подобного. Мощность принятого сигнала слишком слаба по сравнению с тепловыми шумами антенны (шум мощнее сигнала на 30 дБ или в 1000 раз), чтобы позволить терять хоть каплю принятого сигнала. Чтобы приемник работал и выдавал координаты нужно вычислить свертку сигнала и копии минимум на 1 мс, то есть на вход коррелятора поступает 16368 выборки, а на выходе, после накопления, 1 выборка. Если получили большое значение, значит параметры сигнала предсказаны верно, если маленькое, то нужно опять выполнять поиск сигнала.

то есть на вход коррелятора поступает 16368 выборки, а на выходе, после накопления, 1 выборка.

На входе коррелятора 16368 выборки - однобитные (частота выборки 16.368MHz ).

На выходе коррелятора 1 - выборка 16 битная раз в 1 ms (частота выборки 1kHz). Так?

Далее вычисляется свертка принятого сигнала со спрогозированной копией сигнала

E-P-L-коррелятор это тоже своего рода свёртка получается. Только во временной шкале выбрано всего 3 позиции.

Перемножив данные поэлементно, а потом их просуммировав, мы получаем меру подобия сигналов, или результат взаимной корреляции.

GNSS приемник получается, делает свёртку (Convolution) (сумма произведений) PRN кода с окном демодулированных чипов.

Если я не ошибаюсь, в случае свертки одна из функций зеркально отражается:
https://ru.wikipedia.org/wiki/Свёртка_(математический_анализ)
"Операцию свёртки можно интерпретировать как «схожесть» одной функции с отражённой и сдвинутой копией другой."

Внимательный читатель мог заметить, что корреляторов на картинке выше три. Они обрабатывают одни и те же данные, но на них подаются разные величины фазы кода, отличающиеся на 0.5 чипа (-1/2; 0; +1/2). Такой подход называется (EarlyPromptLate), благодаря нему можно оценивать, насколько текущее значение фазы кода программы отличается от принимаемого, включая знак ошибки.

Я тут попробовал скомпоновать иллюстрацию для этого параграфа

Как это можно делать вычисления c Early и Late ? Они же не влезают в принятый буфер.

Вы читали часть статьи "Перемножение и суммирование"?
Она идет явно раньше, чем описание трекинга.

Ваша картинка мне кажется перегруженной, MCU все-таки не ПЛИС, он делает вычисления последовательно.
Так что вычисление E-P-L - это просто вызов одной и той же функции трижды с разными аргументами.

uint16_t offset_p = code_phase_fine / GPS_FINE_RATIO;//present
uint16_t offset_e = offset_p - 1;//early
uint16_t offset_l = offset_p + 1;//late

...

gps_correlation_iq(tmp_prn_data, tmp_data_i, tmp_data_q, offset_e, &IE, &QE);
gps_correlation_iq(tmp_prn_data, tmp_data_i, tmp_data_q, offset_p, &IP, &QP);
gps_correlation_iq(tmp_prn_data, tmp_data_i, tmp_data_q, offset_l, &IL, &QL);

Далее значение ошибки фильтруется, умножается на дополнительный коэффициент и используется для изменения текущего значения кода фазы.

По какой формуле значение Noncoherent преобразуется в коррекцию текущей фазы PRN кода?

Внимательный читатель мог заметить, что корреляторов на картинке выше три. Они обрабатывают одни и те же данные, но на них подаются разные величины фазы кода, отличающиеся на 0.5 чипа (-1/2; 0; +1/2). Такой подход называется (EarlyPromptLate), благодаря нему можно оценивать, насколько текущее значение фазы кода программы отличается от принимаемого, включая знак ошибки.

E-P-L это по сути фазовый детектор. Он же фазовый компаратор, или фазовый дискриминатор.

В книге [3] фазовым дискриминатором называют ту логику, что поддерживает слежение за фазой несущей.
E-P-L там называют "delay lock loop discriminator".

Формирование несущей частоты (Carrier NCO)

Можно посмотреть, как формирование частот сделано в DDS генераторах. А там все довольно просто, и может быть реализовано в целочисленном виде. Так как формируются однобитные данные, то даже таблица поиска (LUT) не нужна - достаточно просто проверять старший бит переменной-аккумулятора фазы. Однако, как оказалось, даже такой простой способ требует слишком много времени. Для вычисления 16368*2 значений бит требовалось 1280мкс.

Глядя на описание DDS похоже на то, что классический DDS может только частоту менять.

А вот возможности менять смещение фазы и амплитуду - нет.

В схемотехнике DDS просто нет регистра (offset) для установки желаемой фазы сигнала sin(2pi*F*t+offset).

Если взять два DDS одинаковых и включить их, то они будут генерировать 2 абсолютно одинаковых синуса. А если надо сдвинуть один синус относительно другого, не меняя частоты, как тут в GNSS, то тогда что делать?

Удивительно, но я ни разу не видел аппаратного DDS в составе периферии какого бы то ни было микроконтроллера.

У DDS генераторов есть аккумулятор фазы, и случае готовых микросхем часто его стартовое значение можно задавать.
Есть и двухканальные DDS.

Было бы здорово спроектировать вот такую PCB

для логирования сырых данных на SD карту для последующего Post-processing(а) на PC

Изначально я боялся, что на STM32F4 приемник сделать не удастся, потребуется куда более мощный STM32H7.


Возьмите MCU Artery семейства F4, там частота ядра 288MHz!
https://habr.com/ru/articles/792590/

Может проще будет больше спутников обрабатывать.

Какое минимальное количество чипов надо записать на условную SD карту памяти, чтобы на основе этого в результате post -processing(а) можно было однозначно вычислить широту и долготу ?

Все зависит от того, какие есть исходные данные.
Думаю, что если записать данные за 60 секунд, этого гарантированно хватит, чтобы принять один кадр целиком.
Благодаря пост-процессингу, acquisition можно проводить для всех спутников сразу, так что номера присутствующих знать не нужно.
Если есть эфемериды и номер недели, хватит 12 секунд, чтобы получить один subframe.

Хотя, как я писал в статье, есть замороченные методы Snapshot GPS, вроде этого - https://snappergps.info/ , там значительно меньше данных нужно.

Про GPS точно не скажу, с ним не так плотно работал. На сколько я помню, ЦИ повторяется в GPS каждые 12,5 минут. Так как мы не знаем в какой момент включился приемник, значит нужно принимать всю ЦИ. Проблема ЦИ GPS в том, что там нет заранее определенного порядка следования строк. Поэтому нужно принимать всю последовательность.

С ГЛОНАСС, в случае сигалов СТ, проще. Там передается 5 кадров, каждый кадр содержит эфемериды и время, длительность кадра 30 секунд. Следовательно, для ГЛОНАСС достаточно записать 30 секунд. Но там частотное разделение, 16 МГц частоты дискретизации должно хватить, но обработка будет немного сложнее, так как частота несущей не будет кратна частоте дискретизации.

Еще раз уточню про GPS: возможно я не достаточно хорошо знаю как устроена ЦИ в GPS и нужно меньше 12,5 минут для определения координат.

В GPS эфемериды для одного спутника полностью передаются в одном кадре (30 сек). Остальное - данные альманаха и некоторые поправки, без которым можно работать.

Как часто этот кадр повторяется?

В ГЛОНАСС первые 5 строк в начале каждого кадра - это эфемериды. Кадр 30 секунд. Строка 2 секунды, 10 секунд передаются эфемериды, 20 секунд альманах. Вот и получается, нужно минимум 10 секунд слежения за сигналом + время на поиск и захват сигнала не менее 4х спутников. Гарантированное определение координат наступает в течении 30 секунд от момента захвата сигнала спутников. Если в постобработке, когда файл можно несколько раз обработать, то 30 секунд записи достаточно.

Тот же телефон, если отключить мобильную связь и WiFi, по GPS определяет координаты единицы минут по моему опыту.

В GPS кадры идут друг за другом. Каждый кадр содержит полные данные эфемерид. В статье есть картинка и описание.
Вообще, если подумать, в GPS тоже должно 30 секунд хватать для получения данных эфемерид и времени.

Получается, что для вычисления широты и долготы надо записать и обработать 60 MByte сырых ADC отсчетов с MAX2769ETI+

Раз уж у Вас есть такая уникальная плата gnss front end, то есть ли возможность, пожалуйста, записать 60секунд непрерывного лога сырых adc I1, семплов в *.bin файл с gnss front end  для post processing(a) ?

Хочется попробовать написать консольную windows утилиту, которая вычислит широту и долготу.

Отдельно мне записывать что-то лень, могу предложить эти данные: https://disk.yandex.ru/d/yxFH-Fexj9NDGA
На них я проверял работу своего кода, в том числе на ПК. Тут около 60 сек записи.
Там данные как-то упакованы, и я уже точно не помню, как.
Поэтому я добавил в архив примеры, как считывать файл.
Результат одиночного считывания - массив данных длительностью 1мс, эти данные можно напрямую использовать вместе с тем кодом, что упомянут в статье.

Есть ли возможность, пожалуйста, назвать первые 6-12 двухбитных семплов (в десятичной системе счисления ) бинарной упаковки source2.bin?
Надо для модульного теста.

Странно работает функция reverse_bits из архива REC_GPS.zip


//set 4 LSB bits
uint8_t reverse_bits(uint8_t data)
{
	uint8_t res = 0;

	for (uint8_t i = 0; i < 4; i++)
	{
		if (data & 128)
			res |= (1 << i);
		data = data << 1;
	}
	return res;
}

Она не все биты инвертирует, а только старшие 4

все возможные варианты входов и выходов

Byte:0x00=0b0000_0000->Rev:0x00=0b0000_0000
Byte:0x01=0b0000_0001->Rev:0x00=0b0000_0000
Byte:0x02=0b0000_0010->Rev:0x00=0b0000_0000
Byte:0x03=0b0000_0011->Rev:0x00=0b0000_0000
Byte:0x04=0b0000_0100->Rev:0x00=0b0000_0000
Byte:0x05=0b0000_0101->Rev:0x00=0b0000_0000
Byte:0x06=0b0000_0110->Rev:0x00=0b0000_0000
Byte:0x07=0b0000_0111->Rev:0x00=0b0000_0000
Byte:0x08=0b0000_1000->Rev:0x00=0b0000_0000
Byte:0x09=0b0000_1001->Rev:0x00=0b0000_0000
Byte:0x0a=0b0000_1010->Rev:0x00=0b0000_0000
Byte:0x0b=0b0000_1011->Rev:0x00=0b0000_0000
Byte:0x0c=0b0000_1100->Rev:0x00=0b0000_0000
Byte:0x0d=0b0000_1101->Rev:0x00=0b0000_0000
Byte:0x0e=0b0000_1110->Rev:0x00=0b0000_0000
Byte:0x0f=0b0000_1111->Rev:0x00=0b0000_0000
Byte:0x10=0b0001_0000->Rev:0x08=0b0000_1000
Byte:0x11=0b0001_0001->Rev:0x08=0b0000_1000
Byte:0x12=0b0001_0010->Rev:0x08=0b0000_1000
Byte:0x13=0b0001_0011->Rev:0x08=0b0000_1000
Byte:0x14=0b0001_0100->Rev:0x08=0b0000_1000
Byte:0x15=0b0001_0101->Rev:0x08=0b0000_1000
Byte:0x16=0b0001_0110->Rev:0x08=0b0000_1000
Byte:0x17=0b0001_0111->Rev:0x08=0b0000_1000
Byte:0x18=0b0001_1000->Rev:0x08=0b0000_1000
Byte:0x19=0b0001_1001->Rev:0x08=0b0000_1000
Byte:0x1a=0b0001_1010->Rev:0x08=0b0000_1000
Byte:0x1b=0b0001_1011->Rev:0x08=0b0000_1000
Byte:0x1c=0b0001_1100->Rev:0x08=0b0000_1000
Byte:0x1d=0b0001_1101->Rev:0x08=0b0000_1000
Byte:0x1e=0b0001_1110->Rev:0x08=0b0000_1000
Byte:0x1f=0b0001_1111->Rev:0x08=0b0000_1000
Byte:0x20=0b0010_0000->Rev:0x04=0b0000_0100
Byte:0x21=0b0010_0001->Rev:0x04=0b0000_0100
Byte:0x22=0b0010_0010->Rev:0x04=0b0000_0100
Byte:0x23=0b0010_0011->Rev:0x04=0b0000_0100
Byte:0x24=0b0010_0100->Rev:0x04=0b0000_0100
Byte:0x25=0b0010_0101->Rev:0x04=0b0000_0100
Byte:0x26=0b0010_0110->Rev:0x04=0b0000_0100
Byte:0x27=0b0010_0111->Rev:0x04=0b0000_0100
Byte:0x28=0b0010_1000->Rev:0x04=0b0000_0100
Byte:0x29=0b0010_1001->Rev:0x04=0b0000_0100
Byte:0x2a=0b0010_1010->Rev:0x04=0b0000_0100
Byte:0x2b=0b0010_1011->Rev:0x04=0b0000_0100
Byte:0x2c=0b0010_1100->Rev:0x04=0b0000_0100
Byte:0x2d=0b0010_1101->Rev:0x04=0b0000_0100
Byte:0x2e=0b0010_1110->Rev:0x04=0b0000_0100
Byte:0x2f=0b0010_1111->Rev:0x04=0b0000_0100
Byte:0x30=0b0011_0000->Rev:0x0c=0b0000_1100
Byte:0x31=0b0011_0001->Rev:0x0c=0b0000_1100
Byte:0x32=0b0011_0010->Rev:0x0c=0b0000_1100
Byte:0x33=0b0011_0011->Rev:0x0c=0b0000_1100
Byte:0x34=0b0011_0100->Rev:0x0c=0b0000_1100
Byte:0x35=0b0011_0101->Rev:0x0c=0b0000_1100
Byte:0x36=0b0011_0110->Rev:0x0c=0b0000_1100
Byte:0x37=0b0011_0111->Rev:0x0c=0b0000_1100
Byte:0x38=0b0011_1000->Rev:0x0c=0b0000_1100
Byte:0x39=0b0011_1001->Rev:0x0c=0b0000_1100
Byte:0x3a=0b0011_1010->Rev:0x0c=0b0000_1100
Byte:0x3b=0b0011_1011->Rev:0x0c=0b0000_1100
Byte:0x3c=0b0011_1100->Rev:0x0c=0b0000_1100
Byte:0x3d=0b0011_1101->Rev:0x0c=0b0000_1100
Byte:0x3e=0b0011_1110->Rev:0x0c=0b0000_1100
Byte:0x3f=0b0011_1111->Rev:0x0c=0b0000_1100
Byte:0x40=0b0100_0000->Rev:0x02=0b0000_0010
Byte:0x41=0b0100_0001->Rev:0x02=0b0000_0010
Byte:0x42=0b0100_0010->Rev:0x02=0b0000_0010
Byte:0x43=0b0100_0011->Rev:0x02=0b0000_0010
Byte:0x44=0b0100_0100->Rev:0x02=0b0000_0010
Byte:0x45=0b0100_0101->Rev:0x02=0b0000_0010
Byte:0x46=0b0100_0110->Rev:0x02=0b0000_0010
Byte:0x47=0b0100_0111->Rev:0x02=0b0000_0010
Byte:0x48=0b0100_1000->Rev:0x02=0b0000_0010
Byte:0x49=0b0100_1001->Rev:0x02=0b0000_0010
Byte:0x4a=0b0100_1010->Rev:0x02=0b0000_0010
Byte:0x4b=0b0100_1011->Rev:0x02=0b0000_0010
Byte:0x4c=0b0100_1100->Rev:0x02=0b0000_0010
Byte:0x4d=0b0100_1101->Rev:0x02=0b0000_0010
Byte:0x4e=0b0100_1110->Rev:0x02=0b0000_0010
Byte:0x4f=0b0100_1111->Rev:0x02=0b0000_0010
Byte:0x50=0b0101_0000->Rev:0x0a=0b0000_1010
Byte:0x51=0b0101_0001->Rev:0x0a=0b0000_1010
Byte:0x52=0b0101_0010->Rev:0x0a=0b0000_1010
Byte:0x53=0b0101_0011->Rev:0x0a=0b0000_1010
Byte:0x54=0b0101_0100->Rev:0x0a=0b0000_1010
Byte:0x55=0b0101_0101->Rev:0x0a=0b0000_1010
Byte:0x56=0b0101_0110->Rev:0x0a=0b0000_1010
Byte:0x57=0b0101_0111->Rev:0x0a=0b0000_1010
Byte:0x58=0b0101_1000->Rev:0x0a=0b0000_1010
Byte:0x59=0b0101_1001->Rev:0x0a=0b0000_1010
Byte:0x5a=0b0101_1010->Rev:0x0a=0b0000_1010
Byte:0x5b=0b0101_1011->Rev:0x0a=0b0000_1010
Byte:0x5c=0b0101_1100->Rev:0x0a=0b0000_1010
Byte:0x5d=0b0101_1101->Rev:0x0a=0b0000_1010
Byte:0x5e=0b0101_1110->Rev:0x0a=0b0000_1010
Byte:0x5f=0b0101_1111->Rev:0x0a=0b0000_1010
Byte:0x60=0b0110_0000->Rev:0x06=0b0000_0110
Byte:0x61=0b0110_0001->Rev:0x06=0b0000_0110
Byte:0x62=0b0110_0010->Rev:0x06=0b0000_0110
Byte:0x63=0b0110_0011->Rev:0x06=0b0000_0110
Byte:0x64=0b0110_0100->Rev:0x06=0b0000_0110
Byte:0x65=0b0110_0101->Rev:0x06=0b0000_0110
Byte:0x66=0b0110_0110->Rev:0x06=0b0000_0110
Byte:0x67=0b0110_0111->Rev:0x06=0b0000_0110
Byte:0x68=0b0110_1000->Rev:0x06=0b0000_0110
Byte:0x69=0b0110_1001->Rev:0x06=0b0000_0110
Byte:0x6a=0b0110_1010->Rev:0x06=0b0000_0110
Byte:0x6b=0b0110_1011->Rev:0x06=0b0000_0110
Byte:0x6c=0b0110_1100->Rev:0x06=0b0000_0110
Byte:0x6d=0b0110_1101->Rev:0x06=0b0000_0110
Byte:0x6e=0b0110_1110->Rev:0x06=0b0000_0110
Byte:0x6f=0b0110_1111->Rev:0x06=0b0000_0110
Byte:0x70=0b0111_0000->Rev:0x0e=0b0000_1110
Byte:0x71=0b0111_0001->Rev:0x0e=0b0000_1110
Byte:0x72=0b0111_0010->Rev:0x0e=0b0000_1110
Byte:0x73=0b0111_0011->Rev:0x0e=0b0000_1110
Byte:0x74=0b0111_0100->Rev:0x0e=0b0000_1110
Byte:0x75=0b0111_0101->Rev:0x0e=0b0000_1110
Byte:0x76=0b0111_0110->Rev:0x0e=0b0000_1110
Byte:0x77=0b0111_0111->Rev:0x0e=0b0000_1110
Byte:0x78=0b0111_1000->Rev:0x0e=0b0000_1110
Byte:0x79=0b0111_1001->Rev:0x0e=0b0000_1110
Byte:0x7a=0b0111_1010->Rev:0x0e=0b0000_1110
Byte:0x7b=0b0111_1011->Rev:0x0e=0b0000_1110
Byte:0x7c=0b0111_1100->Rev:0x0e=0b0000_1110
Byte:0x7d=0b0111_1101->Rev:0x0e=0b0000_1110
Byte:0x7e=0b0111_1110->Rev:0x0e=0b0000_1110
Byte:0x7f=0b0111_1111->Rev:0x0e=0b0000_1110
Byte:0x80=0b1000_0000->Rev:0x01=0b0000_0001
Byte:0x81=0b1000_0001->Rev:0x01=0b0000_0001
Byte:0x82=0b1000_0010->Rev:0x01=0b0000_0001
Byte:0x83=0b1000_0011->Rev:0x01=0b0000_0001
Byte:0x84=0b1000_0100->Rev:0x01=0b0000_0001
Byte:0x85=0b1000_0101->Rev:0x01=0b0000_0001
Byte:0x86=0b1000_0110->Rev:0x01=0b0000_0001
Byte:0x87=0b1000_0111->Rev:0x01=0b0000_0001
Byte:0x88=0b1000_1000->Rev:0x01=0b0000_0001
Byte:0x89=0b1000_1001->Rev:0x01=0b0000_0001
Byte:0x8a=0b1000_1010->Rev:0x01=0b0000_0001
Byte:0x8b=0b1000_1011->Rev:0x01=0b0000_0001
Byte:0x8c=0b1000_1100->Rev:0x01=0b0000_0001
Byte:0x8d=0b1000_1101->Rev:0x01=0b0000_0001
Byte:0x8e=0b1000_1110->Rev:0x01=0b0000_0001
Byte:0x8f=0b1000_1111->Rev:0x01=0b0000_0001
Byte:0x90=0b1001_0000->Rev:0x09=0b0000_1001
Byte:0x91=0b1001_0001->Rev:0x09=0b0000_1001
Byte:0x92=0b1001_0010->Rev:0x09=0b0000_1001
Byte:0x93=0b1001_0011->Rev:0x09=0b0000_1001
Byte:0x94=0b1001_0100->Rev:0x09=0b0000_1001
Byte:0x95=0b1001_0101->Rev:0x09=0b0000_1001
Byte:0x96=0b1001_0110->Rev:0x09=0b0000_1001
Byte:0x97=0b1001_0111->Rev:0x09=0b0000_1001
Byte:0x98=0b1001_1000->Rev:0x09=0b0000_1001
Byte:0x99=0b1001_1001->Rev:0x09=0b0000_1001
Byte:0x9a=0b1001_1010->Rev:0x09=0b0000_1001
Byte:0x9b=0b1001_1011->Rev:0x09=0b0000_1001
Byte:0x9c=0b1001_1100->Rev:0x09=0b0000_1001
Byte:0x9d=0b1001_1101->Rev:0x09=0b0000_1001
Byte:0x9e=0b1001_1110->Rev:0x09=0b0000_1001
Byte:0x9f=0b1001_1111->Rev:0x09=0b0000_1001
Byte:0xa0=0b1010_0000->Rev:0x05=0b0000_0101
Byte:0xa1=0b1010_0001->Rev:0x05=0b0000_0101
Byte:0xa2=0b1010_0010->Rev:0x05=0b0000_0101
Byte:0xa3=0b1010_0011->Rev:0x05=0b0000_0101
Byte:0xa4=0b1010_0100->Rev:0x05=0b0000_0101
Byte:0xa5=0b1010_0101->Rev:0x05=0b0000_0101
Byte:0xa6=0b1010_0110->Rev:0x05=0b0000_0101
Byte:0xa7=0b1010_0111->Rev:0x05=0b0000_0101
Byte:0xa8=0b1010_1000->Rev:0x05=0b0000_0101
Byte:0xa9=0b1010_1001->Rev:0x05=0b0000_0101
Byte:0xaa=0b1010_1010->Rev:0x05=0b0000_0101
Byte:0xab=0b1010_1011->Rev:0x05=0b0000_0101
Byte:0xac=0b1010_1100->Rev:0x05=0b0000_0101
Byte:0xad=0b1010_1101->Rev:0x05=0b0000_0101
Byte:0xae=0b1010_1110->Rev:0x05=0b0000_0101
Byte:0xaf=0b1010_1111->Rev:0x05=0b0000_0101
Byte:0xb0=0b1011_0000->Rev:0x0d=0b0000_1101
Byte:0xb1=0b1011_0001->Rev:0x0d=0b0000_1101
Byte:0xb2=0b1011_0010->Rev:0x0d=0b0000_1101
Byte:0xb3=0b1011_0011->Rev:0x0d=0b0000_1101
Byte:0xb4=0b1011_0100->Rev:0x0d=0b0000_1101
Byte:0xb5=0b1011_0101->Rev:0x0d=0b0000_1101
Byte:0xb6=0b1011_0110->Rev:0x0d=0b0000_1101
Byte:0xb7=0b1011_0111->Rev:0x0d=0b0000_1101
Byte:0xb8=0b1011_1000->Rev:0x0d=0b0000_1101
Byte:0xb9=0b1011_1001->Rev:0x0d=0b0000_1101
Byte:0xba=0b1011_1010->Rev:0x0d=0b0000_1101
Byte:0xbb=0b1011_1011->Rev:0x0d=0b0000_1101
Byte:0xbc=0b1011_1100->Rev:0x0d=0b0000_1101
Byte:0xbd=0b1011_1101->Rev:0x0d=0b0000_1101
Byte:0xbe=0b1011_1110->Rev:0x0d=0b0000_1101
Byte:0xbf=0b1011_1111->Rev:0x0d=0b0000_1101
Byte:0xc0=0b1100_0000->Rev:0x03=0b0000_0011
Byte:0xc1=0b1100_0001->Rev:0x03=0b0000_0011
Byte:0xc2=0b1100_0010->Rev:0x03=0b0000_0011
Byte:0xc3=0b1100_0011->Rev:0x03=0b0000_0011
Byte:0xc4=0b1100_0100->Rev:0x03=0b0000_0011
Byte:0xc5=0b1100_0101->Rev:0x03=0b0000_0011
Byte:0xc6=0b1100_0110->Rev:0x03=0b0000_0011
Byte:0xc7=0b1100_0111->Rev:0x03=0b0000_0011
Byte:0xc8=0b1100_1000->Rev:0x03=0b0000_0011
Byte:0xc9=0b1100_1001->Rev:0x03=0b0000_0011
Byte:0xca=0b1100_1010->Rev:0x03=0b0000_0011
Byte:0xcb=0b1100_1011->Rev:0x03=0b0000_0011
Byte:0xcc=0b1100_1100->Rev:0x03=0b0000_0011
Byte:0xcd=0b1100_1101->Rev:0x03=0b0000_0011
Byte:0xce=0b1100_1110->Rev:0x03=0b0000_0011
Byte:0xcf=0b1100_1111->Rev:0x03=0b0000_0011
Byte:0xd0=0b1101_0000->Rev:0x0b=0b0000_1011
Byte:0xd1=0b1101_0001->Rev:0x0b=0b0000_1011
Byte:0xd2=0b1101_0010->Rev:0x0b=0b0000_1011
Byte:0xd3=0b1101_0011->Rev:0x0b=0b0000_1011
Byte:0xd4=0b1101_0100->Rev:0x0b=0b0000_1011
Byte:0xd5=0b1101_0101->Rev:0x0b=0b0000_1011
Byte:0xd6=0b1101_0110->Rev:0x0b=0b0000_1011
Byte:0xd7=0b1101_0111->Rev:0x0b=0b0000_1011
Byte:0xd8=0b1101_1000->Rev:0x0b=0b0000_1011
Byte:0xd9=0b1101_1001->Rev:0x0b=0b0000_1011
Byte:0xda=0b1101_1010->Rev:0x0b=0b0000_1011
Byte:0xdb=0b1101_1011->Rev:0x0b=0b0000_1011
Byte:0xdc=0b1101_1100->Rev:0x0b=0b0000_1011
Byte:0xdd=0b1101_1101->Rev:0x0b=0b0000_1011
Byte:0xde=0b1101_1110->Rev:0x0b=0b0000_1011
Byte:0xdf=0b1101_1111->Rev:0x0b=0b0000_1011
Byte:0xe0=0b1110_0000->Rev:0x07=0b0000_0111
Byte:0xe1=0b1110_0001->Rev:0x07=0b0000_0111
Byte:0xe2=0b1110_0010->Rev:0x07=0b0000_0111
Byte:0xe3=0b1110_0011->Rev:0x07=0b0000_0111
Byte:0xe4=0b1110_0100->Rev:0x07=0b0000_0111
Byte:0xe5=0b1110_0101->Rev:0x07=0b0000_0111
Byte:0xe6=0b1110_0110->Rev:0x07=0b0000_0111
Byte:0xe7=0b1110_0111->Rev:0x07=0b0000_0111
Byte:0xe8=0b1110_1000->Rev:0x07=0b0000_0111
Byte:0xe9=0b1110_1001->Rev:0x07=0b0000_0111
Byte:0xea=0b1110_1010->Rev:0x07=0b0000_0111
Byte:0xeb=0b1110_1011->Rev:0x07=0b0000_0111
Byte:0xec=0b1110_1100->Rev:0x07=0b0000_0111
Byte:0xed=0b1110_1101->Rev:0x07=0b0000_0111
Byte:0xee=0b1110_1110->Rev:0x07=0b0000_0111
Byte:0xef=0b1110_1111->Rev:0x07=0b0000_0111
Byte:0xf0=0b1111_0000->Rev:0x0f=0b0000_1111
Byte:0xf1=0b1111_0001->Rev:0x0f=0b0000_1111
Byte:0xf2=0b1111_0010->Rev:0x0f=0b0000_1111
Byte:0xf3=0b1111_0011->Rev:0x0f=0b0000_1111
Byte:0xf4=0b1111_0100->Rev:0x0f=0b0000_1111
Byte:0xf5=0b1111_0101->Rev:0x0f=0b0000_1111
Byte:0xf6=0b1111_0110->Rev:0x0f=0b0000_1111
Byte:0xf7=0b1111_0111->Rev:0x0f=0b0000_1111
Byte:0xf8=0b1111_1000->Rev:0x0f=0b0000_1111
Byte:0xf9=0b1111_1001->Rev:0x0f=0b0000_1111
Byte:0xfa=0b1111_1010->Rev:0x0f=0b0000_1111
Byte:0xfb=0b1111_1011->Rev:0x0f=0b0000_1111
Byte:0xfc=0b1111_1100->Rev:0x0f=0b0000_1111
Byte:0xfd=0b1111_1101->Rev:0x0f=0b0000_1111
Byte:0xfe=0b1111_1110->Rev:0x0f=0b0000_1111
Byte:0xff=0b1111_1111->Rev:0x0f=0b0000_1111в

Так и должно быть?

Так и должно быть. Лучше такие вопросы писать в Диалоги Хабра или напрямую мне в какой либо из мессенджеров.

Господа, продолжайте здесь, полезно для истории и образования - может кто то пойдет таким же путем

Что-то в первых 2х миллисекунтах лога захват PRN 5 не совпадает с тем, что указано в файле-подсказке visible_sats.txt

gps_channels[0].prn = 5;
gps_channels[0].acq_data.given_freq_offset_hz = 900;

[GPS] SatInfo PRN:5,
I:Min_:Dopp:1055 Hz,  CorrVal:-728, Offset:14782 Sam,
I:Max_:Dopp:-3690 Hz, CorrVal:703,  Offset:2430 Sam

Q:Min_:Dopp:665  Hz,  CorrVal:-681, Offset:15596 Sam,
Q:Max_:Dopp:4215 Hz,  CorrVal:680,  Offset:14126 Sam

И доплеровское смещение у меня вычислилось другое 1055 Hz для I и 665 Hz для Q.

Значение корреляции с PRN ( длиной 16368 семпла) варьируются в диапазонах -843...703.

Есть подозрение, что я неверно интерпретирован и распаковал BitDump с FPGA source2.bin.

Есть ли возможность, пожалуйста, прислать файл, где на каждый семпл один байт int8_t?
Формат GN3Sv3 для GNSS Front-End MAX2769ETI+ .

Точное значение частоты я сказать не могу, значения, приведённые в файле, округлены примерно до 500 Гц.
Для спутника 5 смещение частоты - около 1200 Гц.
Я не могу дать никаких гарантий, что в первых 2мс данных сигнал от спутников был качественный и стабильный.

Я специально приложил к данным файл, который считывает данные из source2.bin и декодирует их.
У меня нет желания писать конвертер.


Вот здесь попробуйте посмотреть - https://github.com/taroz/GNSS-SDRLIB/blob/master/test/testdata_download_link.txt
В случае SiGe GN3S V3 вроде бы как раз данные частот такие же - 16.368/4.092 МГц и данные - только I в формате int8_t.

Вот такое получилось сигнальное созвездие для PRN14 для первых 4.5 сек из source2.bin

Так и должно быть?

Да, примерно так.

Каких GPS спутников точно нет в файле source2.bin?
На другой стороне Земли были в эти 60сек записи

Распределение было такое:

Черным показан сектор, принимаемый антенной, но в нем некоторые спутники все равно могли быть закрыты домами или иметь слишком низкий уровень сигнала.
Координаты и время указаны, можете попробовать использовать этот сервис: https://www.gnssplanning.com/#/settings

Вот удалось захватить GPS спутник c PRN14. Фаза 8597....8640 семпла, частота смещения Доплера от 3700 Hz до 4900 Hz.
Это первые 2 сек файла source2.bin

Удалось захватить GPS спутник c PRN13. Фаза 4953...4961 семпла, частота смещения Доплера от 3800 Hz до 4700 Hz. ( это первые 2 сек файла source2.bin)

Один бит NavData длится 20ms. Получается, что за это время прилетит 20 PRN.
То есть в одном бите NavData 20 PRN.

Получается, что в Present корреляторе на выходе должны быть непрерывные последовательности I чисел длинной минимум 20 положительных значений или 20 отрицательных значение. Так?

А если количество последовательных непрерывных однознаковых значений I меньше чем 20, то, что это значит, что сопровождение сорвалось. Так?

Есть ли возможность предоставить файл с корректными значениями I и Q на выходе Present-коррелятора для PRN14 для файлы sourse2.bin? Хотя бы для первых 5-10 сек

"А если количество последовательных непрерывных однознаковых значений I меньше чем 20, то, что это значит, что сопровождение сорвалось. Так? "
Не обязательно, возможно, просто одиночная помеха, после которой трекинг продолжит работать.


"Есть ли возможность предоставить файл с корректными значениями I и Q на выходе Present-коррелятора для PRN14 для файлы sourse2.bin? "
Нет.
Весь мой тестовый софт работал последовательно, имитируя работу программы микроконтроллера.
Сначала acquisition, потом трекинг. В итоге данные первой минуты пропускаются, так как ни частота, ни фаза изначально не известны. А переписывать все это дело я не хочу.
GNSS-SDRLIB умеет выдавать значения I/Q в лог, но там тоже нужно дождаться завершения acquisition.

Не обязательно, возможно, просто одиночная помеха, после которой трекинг продолжит работать.

Не похоже на одиночную помеху. Вот интервал для I времени который соответствует 2ум битам NavData (0.04c). И тут этих одиночных помех минимум аж 25 штук. При этом сигнальное созвездие примерно 2 пятна. (Это source2.bin для PRN14)

Я говорил про случай нормального трекинга. А тут явно что-то не так.
Пример, как выглядят нормальные данные, я приводил в статье - https://habr.com/ru/articles/765402/

Есть ли вообще способ проверки правильности выбранного доплеровского смещения частоты гетеродина без корреляции PRNа ?

По-моему - нет. Без корреляции обнаружить сигнал спутника нельзя.

Напомню, что сигнал, принятый со спутника, имеет доплеровское смещение частоты, у каждого спутника оно будет свое. Кроме того, тактовый генератор приемника тоже может иметь свою ошибку частоты, а MAX2769 переносит сигнал не на нулевую, а на промежуточною частоту.

Можно попробовать вот такой алгоритм поиска Доплеровской частоты смещения

вот этой функцией

/*odd means not divided by 2*/
bool is_odd (uint32_t i) {
	bool res = false;
    if (i&1) {
        res = true;
    } else {
    	res = false;
    }
    return res;
}

int32_t zigzag_traversal(uint32_t i, uint32_t step) {
    int32_t value=0;
    int32_t sign = 1;
    bool res = is_odd(i);
    if (res) {
    	 sign = 1;
    } else {
    	 sign = -1;
    }

    value = sign * ceil (((double)i*step)/2.0);
    return value;
}

И чем оно лучше обычного сканирования?
У меня нет статистики, как в среднем распределяются смещения частот в реальной жизни.
Могу только сказать, что из-за того, что TCXO может иметь свою ошибку, то частоты всех принимаемых сигналов будут сдвинуты на единицы кГц относительно нуля.
Интересно, что кроме обычных спутников GPS, есть еще геостационарные WAAS. Они передают свой сигнал на L1, так что их сигнал не должен иметь допплеровского смещения.

Полагаю это учитывает вероятность что частота ушла только на небольшое значение, но не понимаю насчет четного-нечетного, разве не стоит на каждой итерации пробовать одинаковый сдвиг в обе стороны?

"У меня нет статистики, как в среднем распределяются смещения частот в реальной жизни."
У меня есть ощущение, что отклонения частоты разных спутников равномерно распределены по диапазону сканирования, а не сосредоточены ближе к нулю.

 MAX2769 переносит сигнал не на нулевую, а на промежуточною частоту.

А если GNSS Front-End переносит на нулевую частоту (F_LO в PLL выдает F_L1=1575.42 MHz), то нам на post-processing(е) чтобы найти доплеровское смещение генерировать цифровой синус с отрицательной частотой?

Это как?

То тогда придется использовать оба канала I/Q, что выходят из микросхемы.
Соответственно, при переносе частот в софте придется формировать не просто "синус", а пару cos/sin (хотя так и сейчас делается). Смена знака частоты, в таком случае, так понимаю, делается через смену знака одной из составляющих.

 Перемножив данные поэлементно, а потом их просуммировав, мы получаем меру подобия сигналов, или результат взаимной корреляции. 

В случае когда семплы представлены в int8_t корреляция (cумма произведений) может быть как положительная так и отрицательная.

Нам фазу PRN кода искать по какой корреляции? Той, что сильно положительная или по той, что сильно отрицательная?

Я по положительной искал. И не по одному из каналов i/q, а по двум.
Использовал классический корень из суммы квадратов.
Посмотрите gps_correlation8() и correlation_search() здесь.

Я не понимаю, как вы устанавливаете частоты с такой точностью, не зная фазы сигнала.

Я бы рекомендовал, для тестирования кода, анализировать данные одного спутника, к примеру, с PRN=5, задать постоянным смещение частоты - 1000 Гц.
Далее для каждого блока по 1мс перебирать все варианты фазы кода и выводить в консоль или сразу на график фазы кода, где был максимум корреляции. Если по этим данным построить график (именно точками, не линиями), то должна получится картина, аналогичная тому, что была в статье в пункте "Поиск фазы кода". Если на графике есть наклонная линия - значит алгоритм нормально работает и "видит" сигнал спутника.

Я не понимаю, как вы устанавливаете частоты с такой точностью, не зная фазы сигнала.

Имеется в виду фаза цифрового гетеродина?

Я подбираю частоты смещения Доплера перебором.

Я имел в виду фазу кода.
Если перебирать все фазы кода в диапазоне 0-2045 и все частоты в зоне -7000..+7000 Гц с шагом 1 Гц, то нужно 28 миллионов корреляций посчитать. Это очень много. А гарантий, что уровень сигнала в выбранном участке данных высокий - нет.

Я бы рекомендовал, для тестирования кода, анализировать данные одного спутника, к примеру, с PRN=5, задать постоянным смещение частоты - 1000 Гц.



Вот что получилось. PRN5 doppler_offset 1kHz. Семплы из source2.bin

а это на doppler_offset 800Hz

import matplotlib.pyplot as plt
import csv

F_adc_hz = 16368000.0
t_sample = 1.0/F_adc_hz

X = []
Y = []

file_name='Prn5_dopp1000_proc1.csv'
#file_name='Prn5_dopp800_proc1.csv'

with open(file_name, 'r') as datafile:
    plotting = csv.reader(datafile, delimiter=',')
    
    for ROWS in plotting:
        X.append(float(ROWS[0])*t_sample)
        #X.append('{} {}'.format(ROWS[3], ROWS[4]))
        Y.append(float(ROWS[1]))

print ('X {} Nums'.format(len(X)))
print ('Y {} Nums'.format(len(Y)))
 
print ('Type X {} '.format(type(X)))
print ('Type Y {} '.format(type(Y)))

plt.plot(X, Y,'bo',ms=0.1)
plt.title('Seek phase')
plt.xlabel('Time,[s]')
plt.ylabel('phase, [sample]')
plt.xticks(rotation=-90)
plt.legend(['phase'])
plt.show()


Что-то не видно спутника.

Похоже, вы что-то делаете не так.
Вот такой тестовый код (точнее, его часть):

void test(void) 
{
    char data_path[300] = "D:\\PATH\\source2.bin";
    gps_fill_summ_table();

    gps_ch_t gps_channel1;
    memset(&gps_channel1, 0, sizeof(gps_channel1));
    gps_channel1.prn = 5;
    gps_channel1.acq_data.given_freq_offset_hz = 1000;
    gps_channell_prepare(&gps_channel1);

    uint8_t read_buffer[BITS_IN_PRN / 8];

    uint8_t res = data_reader_open_file(data_path);
    if (res == 0)
    {
        printf("Open file fail\n");
        return;
    }

    for (int i = 0; i < 2000; i++)
    {
        //Читаем 1мс данных
        res = data_reader_read_unpack(read_buffer, BITS_IN_PRN);
        if (res == 0)
        {
            printf("Can't read file\n");
            return;
        }
        acquisition_freq_test(&gps_channel1, read_buffer);
    }

    data_reader_close_file();
    printf("Execution end\n");
}

#define GPS_DATA_WORDS_CNT      (PRN_SPI_WORDS_CNT + 1)
uint16_t tmp_prn_data[GPS_DATA_WORDS_CNT];
uint16_t tmp_data_i[GPS_DATA_WORDS_CNT];
uint16_t tmp_data_q[GPS_DATA_WORDS_CNT];

//Генерирует локальный PRN код
//Переносит принятые данные на частоту, близкую к 0
//Перебирает все фазы кода, и выводит фазу максимальной
void acquisition_freq_test(gps_ch_t* channel, uint8_t* data)
{
	gps_generate_prn_data2(channel, tmp_prn_data, 0);
	int16_t freq_offset_hz = channel->acq_data.given_freq_offset_hz;

	gps_shift_to_zero_freq(data, (uint8_t*)tmp_data_i, (uint8_t*)tmp_data_q, IF_FREQ_HZ + freq_offset_hz);
	uint16_t avr_val;
	uint16_t best_phase1 = 0;
	correlation_search(tmp_prn_data, tmp_data_i, tmp_data_q, 0, (PRN_LENGTH * 2), &avr_val, &best_phase1);
	printf("%d\n", best_phase1);
}

Здесь использованы те же функции, что и репозитории:
https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_main/GPS/gps_misc.c

Код выдает такие значения (первые 20 штук):

Hidden text

1360
137
504
2043
2043
1670
1112
2043
2043
450
1153
2043
2043
1775
1266
2043
2043
1840
485
2043

Моно видеть, что значение 2043 часто встречается. Это и есть фаза кода.

При увеличении видно, что фаза меняется:

Прежде чем пробовать вычисления в однобитной арифметике, как у Вас, я хотел бы тоже самое проделать в int8_t.
Так привычнее и проще понять физику процесса.

Вероятно у меня файл adc_sample_i8.bin сформировался не так. Бинарный формат упаковки ADC отсчётов в source2.bin не до конца понятен.

Есть ли возможность, пожалуйста, синтезировать из source2.bin правильный гигабайтный файл adc_sample_i8.bin, где на каждый ADC семпл 1 байт (int8_t)?

Я уже говорил вам на такой же вопрос, что нет у меня такого желания.
https://habr.com/ru/articles/789382/comments/#comment_26671589

И я не понимаю, в чем сложность в вашем софте сделать и одного байта восемь int8_t .

Вот PRN14 на Доплере +900 Hz у меня просматривается

А тот же PRN14 на Доплере +4000 Hz теряется

В целом - нелинейно, также как и доплеровское смещение. Но на небольших интервалах времени нелинейность незаметна.

Зачем нужно обнулять отрицательные корреляции вот тут?

У меня алгоритм стабильней работал в таком варианте.
Замечу, что этот участок кода работает только в acquisition.

В России вообще сколько компаний, которые так или иначе занимаются/занимались разработкой ЦОС для навигационных GNSS приёмников?

Как в виде ASIC исполнения, так и в виде навигационной PCB или шкафа с набором PCB.

Из наиболее известных - АО КБ НАВИС. У них есть свои микросхемы. Правда сайт уже два года "на реконструкции" находится, но фирма вроде жива.

На PCB из рассыпухи никто не делает сейчас. Если имеющиеся на рынке решения не подходят, то берем FPGA (Xilinx или Altera), пишется прошивка с ЦОС, если нет процессора (например Xilinx Spartan) то в эту же прошивку добавляем Microblaze для обработки данных с ЦОС. Но разработка подобного качественного приемника - это ОЧЕНЬ долго и дорого (разработка ASIC еще дороже).

Много фирм делают просто: покупают зарубежный готовый чип (как правило китайский), в лучшем случае как-то его настраивают под определенные задачи и продают под видом своей разработки.

Далее значение ошибки фильтруется, умножается на дополнительный коэффициент и используется для изменения текущего значения кода фазы.

Как выглядит фильтр (полагаю что цифровой) для фильтрации значения noncoherent?

Там вообще DP-регулятор (Proportional–Derivative controller) реализован.
И почему коэффициент пропорциональной части именно 0.3?

void gps_tracking_dll(gps_ch_t* channel, 
                      uint8_t index,
                      int16_t IE, int16_t QE,
                      int16_t IL, int16_t QL)
{
.......
  
  code_err = -code_err;
  
  float dt_s = 0.001f;
  /* 2nd order DLL */
  channel->tracking_data.code_phase_fine += 
    (TRACKING_DLL1_C1 * (code_err - channel->tracking_data.dll_code_err) +
    TRACKING_DLL1_C2 * dt_s * code_err);
  
.......
  
  channel->tracking_data.dll_code_err = code_err;
}

Код выложен, можете посмотреть.


Это не опечатка? Две разные функции (atan2f() atan2())для одного и того же.
float atan2f (float, float);
double atan2 (double, double);

Да, это опечатка, постараюсь поправить, как смогу.

Зачем надо результат atan2f делить еще на PI?
atan2f и так в радианах результат возвращает.

Подстройка частоты FLL основана на производной ошибки фазы частоты по времени.Я в своей реализации алгоритма заметил, что в некоторых случаях PLL/FLL "захватывает" ошибочную частоту. Чтобы справится с этой проблемой, я добавил в код проверку принимаемых данных. Если данные некорректные, частота автоматически перестраивается на случайную, а дальше уже FLL автоматически пытается подстроить частоту. Вполне возможно, что у меня где-то просто ошибка в коде, которая приводит к такой ситуации.


У меня есть подозрение что в коде

https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_single_sat/GPS/tracking.c

неправильно написан фазовый детектор (FLL discriminator)

Что такое фазовый детектор?
По факту - это же угол между векторами, которые образованы двумя комплексными числами вида I + j*Q. Так?

Ваш фазовый детектор можно представить вот этой функцией

float fll_discriminator( int32_t new_i,  int32_t new_q,  
                         int32_t old_i,  int32_t old_q) {
    float freq_diff_rad=0.0;

    /* FLL discriminator */
    float f1 = (new_i == 0) ? (M_PI / 2.0) : atanf((float)new_q / (float)new_i);
    float f2 = (old_i == 0) ? (M_PI / 2.0) : atanf((float)old_q / (float)old_i);
    freq_diff_rad = f1 - f2;

    if(freq_diff_rad > M_PI / 2.0)
        freq_diff_rad = M_PI - freq_diff_rad;
  
    if(freq_diff_rad < -M_PI / 2.0)
        freq_diff_rad = -M_PI - freq_diff_rad;

    return freq_diff_rad;
}

У меня есть текст

Как Найти Угол Между Векторами?
https://habr.com/ru/articles/807641/

Там перечислены три способа вычислить угол между векторами:
1--линейной алгеброй (скалярное произведение + векторное произведение)
2--комплексными числами
3--при помощи функции atan2()

Все три способа дают один и тот же результат.

В Ваших исходниках фазовый детектор явно вычисляет что-то другое (не угол между условными двумя комплексными числами).

В колонке angleTr показан подлинный угол между векторами для простых тестовых данных.

В колонке angleNa показан угол вычисленный функцией fll_discriminator. Заметно очень много несоответствий.

Так и должно быть?

Есть ли возможность, пожалуйста, подсказать, где в коде то место (функция), где реализован вот этот смеситель для Costas loop, который генерирует phi(t)?

Из Википедии: "The outputs of these low-pass filters are inputs to another phase detector"
Это та часть, что вычисляет текущую фазу сигнала.
В статье я писал, что это atan(Q/I)
https://github.com/iliasam/STM32F4_SDR_GPS/blob/f0fa35a44281cc0fcc4c7165a107bfe6f10ea4cc/Firmware/project_main/GPS/tracking.c#L181
Единственно, там действительно неверное название переменной carr_phase_err_rad - она не в радианах.

Задача контура DLL - слежение за фазой кода сигнала, используя данные E-P-L корреляторов. Конкретно у меня, так же, как и в GNSS-SDRLIB, используются данные только E-L корреляторов. Ошибка вычисляется по такой формуле:

Я правильно понимаю, что, если вычислить абсолютные значения [ abs(I+jQ) ] для для E- P -L коррелятора, то LUT-таблица действий будет содержать 4 строчки и выглядеть так:

Что является условием потери сопровождения PRN сигнала? Например, отключили спутник GPS. Как GPS приёмник сможет это зарегистрировать во время tracking?

Есть только один достоверный способ определить, следит ли приемник за сигналом: проверка контрольной суммы цифровой информации. Если контрольная сумма (КС) сходится, значит следим. Если несколько раз подряд КС не совпала с принятой КС, следовательно, сигнал потерян.

Разве условие e>p<l не является критерием срыва трекинга фазы prn?

При низком отношении сигнал/шум условие e>p<l может регулярно возникать, но слежение за сигналом будет еще возможно и выделение цифровой информации тоже будет выполняться без ошибок (либо с редкими ошибками).

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

А отсутствие двух скоплений в сигнальном созвездии не является условием срыва сопровождения сигнала со спутника?

 а фаза успевает "убежать" за то время, когда происходит поиск фазы других спутников. 

С какой максимальной скоростью может меняться фаза принимаемого PRN кода? Сколько это семплов в секунду?

В GPS для слежения за фазой его часто реализуют, используя реализацию Costas Loop. Этот регулятор удобен тем, что не чувствителен к изменению фазы сигнала на 180 градусов, а она как раз появляется при смене знака передаваемых навигационных данных. Задачей этого регулятора является удержание амплитуды канала I максимальной, а Q - минимальной.

Что такое loop filter в цепи Costas Loop PLL?

Так в Википедии (ссылка в цитате) все написано: " The outputs of these low-pass filters are inputs to another phase detector, the output of which passes through a noise-reduction filter before being used to control the voltage-controlled oscillator. "

Раз loop filter предназначен для того чтобы убирать шум, то какая тогда частота среза у loop filter?

Я не могу точно сказать, коэффициенты я подбирал.

Чем в Costas loop Lowpass filter отличается от loop filter?

Если вы про картинку из Википедии, то там LPF нужны, чтобы убрать лишние частоты после смесителя (который дает на выходе (F1-F2) и (F1+F2)).
В случае с GPS сам коррелятор является фильтром (так как в нем идет интегрирование).
Loop filter - определяет полосу пропускания Costas Loop, не дает обратной связи входить в колебания.

Все равно не понятно что такое loop filter. Какое математическое описание? Какова схемотехника loop filter?

Пытаюсь отладить трекинг PRN. Фазовое созвездие вырождается в кгруг.

В каком функции с исходником реализован Costas Loop? Внутри gps_tracking_pll?

Понятно, что аналог VCO это gps_shift_to_zero_freq()

Значение ошибки также фильтруется, умножается на дополнительный коэффициент и используется для изменения текущего значения смещения частоты приема.

Откуда взялись значения констант для коэффициентов(TRACKING_PLL1_C1 TRACKING_PLL1_C2) в PLL?

Firmware/project_single_sat/config.h:40:#define TRACKING_PLL1_C1        (10.0f) 
Firmware/project_single_sat/config.h:41:#define TRACKING_PLL1_C2        (1000.0f)

Подстройка частоты FLL основана на производной ошибки фазы частоты по времени.

В блок-схеме Costas Loop нет дифференциирующих звеньев. LPF- это наоборот первое приближение интегрирующего звена.

Где в коде перемножитель, который перед LoopFilter?

"В каком функции с исходником реализован Costas Loop? Внутри gps_tracking_pll?"
Да.

"Откуда взялись значения констант для коэффициентов Откуда взялись значения констант для коэффициентов".
Они подобраны.

"В блок-схеме Costas Loop нет дифференциирующих звеньев. "
https://github.com/iliasam/STM32F4_SDR_GPS/blob/develop/Firmware/project_main/GPS/tracking.c#L214
Вот эта часть:

 int16_t oldIP = channel->tracking_data.fll_old_i;
  int16_t oldQP = channel->tracking_data.fll_old_q;
  
  /* FLL discriminator */
  float f1 = (IP == 0) ? (M_PI / 2) : atanf((float)QP / (float)IP);
  float f2 = (oldIP == 0) ? (M_PI / 2) : atanf((float)oldQP / (float)oldIP);
  float freq_diff_rad = f1 - f2;

Это взято из https://github.com/iliasam/GNSS-SDRLIB/blob/ac16855961b558cda1aaba1ae45095a600985b58/src/sdrtrk.c#L122

В однобитной арифметике Costas Loop на входе Loop Filter ( ( SxI ) x ( SxQ ) )

показывает то же самое, что просто перемноженный сигнал гетеродина IxQ.

Вот серия алгебраических преобразований

( (SxI)x(SxQ) ) =SxS x ( (I)x(Q) ) = (S^2)x (IxQ) = 1 x (IxQ) = I x Q

То есть полностью детерминированный сигнал, которому вообще антенна даже не нужна. Противоречие.

Это как тогда должен работать Costas Loop в однобитной арифметике?

https://docs.google.com/spreadsheets/d/1qklpbk3-8pogqTWYT9IRc3arGCzShQhP0Uv74p95hKk/edit#gid=0

А при чем тут однобитная арифметика?
После корреляторов значения E-P-L - обычные 16-битные значения (только что комплексные - I/Q).
"Задачей этого регулятора является удержание амплитуды канала I максимальной, а Q - минимальной."

С какой погрешностью следует предварительно найти частоту смещения доплера, чтобы заработала функция gps_tracking_pll?

" В своем проекте я произвожу сканирование диапазона частот [-7000..+7000 Гц] с шагом 500 Гц."

Я правильно понимаю, что нет смысла вызывать функцию gps_tracking_pll , если E-P-L коррелятор потерял фазу PRN?

Пока DLL держит фазу кода, на графике I/Q будет круг (или два пятна, если фаза несущей тоже верна). Круга нет, или он пропал больше чем на секунду - все, фаза кода потеряна, классический E-P-L трекинг ее уже не поймает.
Без трекинга фазы кода (DLL) PLL действительно не имеет смысла.

 Слежение за фазой сигнала необходимо для извлечения из сигнала навигационных данных. В GPS для слежения за фазой его часто реализуют, используя реализацию Costas Loop.

Получается, что задача звена Costas loop - это только лишь в том, чтобы подобрать фазу гетеродина LO (PLL).

Частоту LO подкручивает совершенно другой регулятор FLL.
Так?

Нет, по крайней мере в моей реализации. И PLL, и FLL управляют только частотой LO.
На вашей же картинке VCO - генератор, у которого регулируется выходная частота, тем не менее, в итоге Costas loop подстраивается под фазу входного сигнала.

Они подобраны.

------------------

В случае, если есть сильная ошибка по фазе (PLL не работает или не смог достичь стабилизации фазы), а DLL работает нормально, то изображение(сигнальное созвездие) выглядит как окружность:

По какой методике(алгоритму) следует подбирать коэффициенты (TRACKING_PLL1_C1 TRACKING_PLL1_C2) для PLL?

Тут я использовал только эмпирический подход. Выразить словами его сложно.

Если PLL регулирует частоту NCO, я что ни ставлю в TRACKING_PLL1_C1, TRACKING_PLL1_C2 всегда получается круг в сигнальном созвездии.

Какая будет формула переменной NonCoherent для 5ти корреляторов EE-E-P-L-LL ?

 управлять и фазой сигнала этого генератора

Фазу гетеродина надо подстраивать для каждого конкретного PRN или наоборот последовательно идущими PRN уточнять фазу гетеродина LO?

Конкретно в моей реализации данные обрабатываются блоками по 1мс (1 PRN). После вычисления корреляции получается пара чисел (или 2*3, если речь про E-P-L). Этих данных хватает только на то, чтобы подстроить параметры NCO для последующей обработки.

По моим расчетом у каждого PRN своя собственная фаза несущей гетеродина.
Так и должно быть?

Фаза несущей меняется постоянно, спутник-то движется.

Если для каждого PRN14 в принимаемом сигнале подстраивать лучшую фазу LO из 16 итераций costas-loop, то появляется вот такое сигнально созвездие.

Всё равно не очень.

Тут явно есть серьезные колебания фазы NCO, то есть обратная связь работает неустойчиво. Более того, есть риск, что за время накопления данных фаза несколько раз переворачивалась на 180 градусов, а это ломает прием данных.

У двух соседних prn как сильно может отличаться фаза несущей?

В случае, если есть сильная ошибка по фазе (PLL не работает или не смог достичь стабилизации фазы), а DLL работает нормально, то изображение выглядит как окружность:

А как интерпретировать вот такое сигнальное созвездие для BPSK PRN14?
Что надо подстроить в программе чтобы было 2 пятна на оси I?

Частота точно верная?


Traking показывает, что для PRN14 частота началась на 4327 и через 60 сек закончилась на 4085.

Да. Частота доплеровского смещения выглядит правильно. А сигнальное созвездие 4 пятна по схеме +.

И что в этом случае надо проверять?

Тогда только работу PLL.

Почему тут деление на pi? Оно же нарушает размерность.

Тут, похоже, моя ошибка. Я неправильно переименовал переменную, там не радианы.
Оригинал тут - https://github.com/taroz/GNSS-SDRLIB/blob/3fddc964f718a88fe7f8c0a1384046e7a53b587f/src/sdrtrk.c#L94


В оригинале также нет проверки "if (phase_diff_old > M_PI / 2)".
Проверил на ПК - отключение этой проверки не влияет на работу трекинга.

В учебнике A Software-Defined GPS and Galileo Receiver и вовсе сумма вместо разности в регуляторе PLL для С2 ветви .

Суммируя все свои попытки понять DSP для GPS могу заключить следующее.

Не получается сопровождение PRN. NavData не захватываются.
Сигнальное созвездие это либо 4 пятна (типа X или типа + )либо круг.

Если же для каждого PRN отдельно находить лучшую фазу LO при котором Q->min, I->max то
навигационные биты на графике I(t) не вырисовываются. Хотя сигнальное созвездие - два пятна на оси I.

За время 1/50=0.02c (20 PRNов) появляется 4-6 перепадов от -/+I к +/-I (ожидается же 1 перепад или ни одного перепада знаков).

оранжевая - фаза LO, синяя - I
оранжевая - фаза LO, синяя - I

Захват PRN происходит корректно. В source2.bin увидел PRN20 PRN5 PRN30 PRN13 PRN14. Слежение за фазой PRN (DLL) тоже получилось корректно.

А вот PLL(фаза LO) не сходится.
Если фазу LO не регулировать (оставить в 0), то в сигнальном созвездии получится круг.
Если ошибкой фазы текущего (i) PRN подстраивать фазу несущей для следующего PRN (i+1), то на сигнальном созвездии тоже получаем круг.

Как проверить работу FLL не ясно. С какой точностью FLL находит частоту - не ясно.

Я уже не знаю как извлечь биты навигационных данных.

Не ясно как отлаживать такие вещи как SDR для GNSS.

"Если же для каждого PRN отдельно находить лучшую фазу LO при котором Q->min, I->max тонавигационные биты на графике I(t) не вырисовываются. Хотя сигнальное созвездие - два пятна на оси I."
А вы уверены, что у вас при таком подходе фаза не скачет на +/- 180 градусов?
В таком случае вы будете получать два пятна в созвездии, только данные действительно могут будут потеряны.

"Как проверить работу FLL не ясно. "
У 14 спутника частота была действительно около +4100Гц. Если перед стартом трекинка принудительно указать частоту +4500 или -3800, то FLL должен постепенно вывести частоту трекинга на правильную. У меня уровень колебаний частоты составлял около 10 Гц. Но основные скачки частоты в устоявшемся режиме вроде бы PLL создает.

А вы уверены, что у вас при таком подходе фаза не скачет на +/- 180 градусов?

Добавил сравнение текущей фазы LO для PRN и предыдущей фазой LO для PRN.
Получилось так, что значение фазы LO отличаются в диапазоне на -88...76 Deg.

40.006,508 :I [GPS]   0.51 %,PRN:14,Time:0.34 s,Phase:8580 Sam,Dopp:4083 Hz,Corr:1212,NH:-0.6,NHF:-1.1,A:1212,D:176,LOP:-18 Deg,LO_Pha_diff:-18.000000 Deg,
40.102,509 :I [GPS]   0.51 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4081 Hz,Corr:1168,NH: 0.4,NHF: 0.9,A:1168,D: -2,LOP:-13 Deg,LO_Pha_diff:-13.000000 Deg,
40.197,510 :I [GPS]   0.51 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4082 Hz,Corr: 981,NH: 0.1,NHF: 0.1,A: 981,D:  5,LOP: 75 Deg,LO_Pha_diff:75.000000 Deg,
40.292,511 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4083 Hz,Corr:1174,NH:-0.3,NHF:-0.6,A:1174,D:179,LOP:-13 Deg,LO_Pha_diff:-13.000000 Deg,
40.388,512 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4083 Hz,Corr:1182,NH:-0.2,NHF:-0.4,A:1182,D:179,LOP: 86 Deg,LO_Pha_diff:86.000000 Deg,
40.483,513 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4082 Hz,Corr:1433,NH:-0.1,NHF:-0.2,A:1433,D:  2,LOP:-11 Deg,LO_Pha_diff:-11.000000 Deg,
40.579,514 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4082 Hz,Corr: 618,NH:-0.1,NHF:-0.1,A: 618,D:  0,LOP: 89 Deg,LO_Pha_diff:89.000000 Deg,
40.676,515 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4079 Hz,Corr:1214,NH: 0.5,NHF: 1.0,A:1214,D:179,LOP: -1 Deg,LO_Pha_diff:-1.000000 Deg,
40.773,516 :I [GPS]   0.52 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4079 Hz,Corr:1230,NH: 0.4,NHF: 0.8,A:1230,D:178,LOP: 86 Deg,LO_Pha_diff:86.000000 Deg,
40.870,517 :I [GPS]   0.53 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4081 Hz,Corr: 700,NH:-0.0,NHF:-0.1,A: 700,D: -8,LOP:-27 Deg,LO_Pha_diff:-27.000000 Deg,
40.967,518 :I [GPS]   0.53 %,PRN:14,Time:0.34 s,Phase:8581 Sam,Dopp:4083 Hz,Corr: 698,NH: 0.6,NHF: 1.2,A: 698,D:173,LOP:-80 Deg,LO_Pha_diff:-80.000000 Deg,
41.064,519 :I [GPS]   0.53 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4081 Hz,Corr:1071,NH:-0.3,NHF:-0.6,A:1071,D: -3,LOP: 81 Deg,LO_Pha_diff:81.000000 Deg,
41.161,520 :I [GPS]   0.53 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4080 Hz,Corr:1308,NH: 0.1,NHF: 0.2,A:1308,D:-179,LOP: -1 Deg,LO_Pha_diff:-1.000000 Deg,
41.257,521 :I [GPS]   0.53 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4079 Hz,Corr: 979,NH: 0.0,NHF: 0.1,A: 979,D:176,LOP: 75 Deg,LO_Pha_diff:75.000000 Deg,
41.353,522 :I [GPS]   0.53 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4078 Hz,Corr: 828,NH: 0.5,NHF: 1.1,A: 828,D:  1,LOP: -3 Deg,LO_Pha_diff:-3.000000 Deg,
41.451,523 :I [GPS]   0.53 %,PRN:14,Time:0.35 s,Phase:8579 Sam,Dopp:4078 Hz,Corr:1223,NH:-0.8,NHF:-1.5,A:1223,D:  2,LOP: 24 Deg,LO_Pha_diff:24.000000 Deg,
43.724,524 :I [GPS]   0.54 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4080 Hz,Corr:1452,NH: 0.4,NHF: 0.8,A:1452,D:-178,LOP: 12 Deg,LO_Pha_diff:12.000000 Deg,
43.822,525 :I [GPS]   0.54 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4082 Hz,Corr:1306,NH: 0.1,NHF: 0.2,A:1306,D:  0,LOP:-81 Deg,LO_Pha_diff:-81.000000 Deg,
43.920,526 :I [GPS]   0.54 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4081 Hz,Corr: 862,NH:-0.2,NHF:-0.5,A: 862,D:  0,LOP:  0 Deg,LO_Pha_diff:0.000000 Deg,
44.015,527 :I [GPS]   0.54 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4083 Hz,Corr: 848,NH: 0.1,NHF: 0.2,A: 848,D:178,LOP:-75 Deg,LO_Pha_diff:-75.000000 Deg,
44.112,528 :I [GPS]   0.54 %,PRN:14,Time:0.35 s,Phase:8580 Sam,Dopp:4083 Hz,Corr:1042,NH: 0.1,NHF: 0.2,A:1042,D:179,LOP: 35 Deg,LO_Pha_diff:35.000000 Deg,
44.209,529 :I [GPS]   0.54 %,PRN:14,Time:0.36 s,Phase:8580 Sam,Dopp:4085 Hz,Corr:1340,NH: 0.1,NHF: 0.2,A:1340,D: -4,LOP:-79 Deg,LO_Pha_diff:-79.000000 Deg,
44.306,530 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8580 Sam,Dopp:4085 Hz,Corr:1369,NH: 0.2,NHF: 0.3,A:1369,D: -6,LOP: 12 Deg,LO_Pha_diff:12.000000 Deg,
44.403,531 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8580 Sam,Dopp:4084 Hz,Corr:1128,NH:-0.3,NHF:-0.6,A:1128,D:-179,LOP:-75 Deg,LO_Pha_diff:-75.000000 Deg,
44.501,532 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8580 Sam,Dopp:4084 Hz,Corr:1159,NH: 0.7,NHF: 1.3,A:1159,D:-174,LOP: 21 Deg,LO_Pha_diff:21.000000 Deg,
44.598,533 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8579 Sam,Dopp:4084 Hz,Corr: 897,NH: 0.2,NHF: 0.4,A: 897,D:-174,LOP: 22 Deg,LO_Pha_diff:22.000000 Deg,
44.696,534 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8579 Sam,Dopp:4086 Hz,Corr:1032,NH: 0.0,NHF: 0.1,A:1032,D:  1,LOP:-76 Deg,LO_Pha_diff:-76.000000 Deg,
44.793,535 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8579 Sam,Dopp:4086 Hz,Corr: 901,NH:-0.2,NHF:-0.4,A: 901,D:  9,LOP: 21 Deg,LO_Pha_diff:21.000000 Deg,
44.888,536 :I [GPS]   0.55 %,PRN:14,Time:0.36 s,Phase:8579 Sam,Dopp:4088 Hz,Corr:1167,NH: 0.1,NHF: 0.2,A:1167,D:-177,LOP:-47 Deg,LO_Pha_diff:-47.000000 Deg,

Не знаю я.

Задача контура DLL - слежение за фазой кода сигнала, используя данные E-P-L корреляторов. Конкретно у меня, так же, как и в GNSS-SDRLIB, используются данные только E-L корреляторов. Ошибка вычисляется по такой формуле

Я правильно понимаю, что надо делать извлечение одной двадцатой бита NavData из Present коррелятора только, тогда когда NonCoherent равен нулю?

Я опять не понял вопроса.
Дискриминатор DLL показывает уровень текущей ошибки фазы кода, его данные используются только для управления фазой кода, и все.

>>>Я опять не понял вопроса

Есть 1 бит NavData 20ms . Он состоит из 20ти последовательных PRN по 1ms.
Получается, что 1 PRN - это одна двадцатая от 1 бита NavData.

Смещение Present коррелятора подстраивается DLL регулятором .

Модуляция у тут BPSK. Вот и получается, что надо чтобы arg(P)=arg(I+jQ) в Present корреляторе PRN должен быть либо 0 либо 180 градусов.

Причем ожидается, что максимальная частота NavData будет 50 Hz. Поэтому увидеть надо примерно.

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180 180
и т д

" должен быть либо 0 либо 180 градусов."
Не обязательно, чтобы точность фазы несущей была идеальная.
Результат работы P коррелятора при действующих обратных связях выглядит как два пятна на плоскости XY.
Расстояние между пятнами характеризует амплитуду сигнала. Диаметр пятен - уровень шума. Наклон пятен - ошибка по фазе несущей.

Если вообще нет синхронизации по фазе несущей (PLL), а DLL работает, то угол между двумя пятнами и осью X будет постоянно манятся, в итоге пятна растягиваются в круг.
Если PLL работает, но не успевает за фазой, то будут видны два пятна, но под ощутимым углом к оси X. Если амплитуда приличная, а шум низкий, то это не страшно - каждое из пятен будет попадать в свою половину плоскости (+X | -X), так что данные будут детектироваться нормально (данные берутся из I-ветви P коррелятора).
Если шум большой, амплитуда низкая, или угол слишком большой, то часть результатов начнет попадать на чужую половину плоскости и прием данных сломается.

Трудность в том, что фаза LO для текущего PRN никак не зависит от фазы LO предыдущего PRN и подобрать её трудно.

На самом деле, зависимость там явно есть - спутник не рывками двигается.

Фаза принятого синуса зависит от положения приёмника относительно источника волны.

Иначе говоря, фаза принятого синуса зависит от расстояния между передатчиком и приемником. Может меняться в диапазоне от 0 до 360 градусов.

Для частоты GNSS L1 1575.42МHz длина волны равна 19.0294 cm.

GPS спутник, который движется на скорости 3км/s пролетает эти 19.0294 cm за 63.4us.
Получается что за 63.4us фаза испускаемого L1 на стороне приёмника изменится от 0 до 360 градусов.

GNSS Front-End следит только за двумя фазами 0 и 90Deg. Получается, что данные правильно принимаются только в два мгновения каждые 63.4us.

Точную фазу LO надо подстраивать PLL(ом)

А за время одного PRN (1ms) фаза испускаемого L1 изменится от 0 до 360 уже 1ms/63.4us= 15,7728 раз!

Учитывая, что и скорость спутника относительно приёмника не постоянная, получается, что фаза LO на каждом PRN примет случайное значение и еще меняется во время приёма.

Если бы фаза действительно скакала случайным образом, то слежение за сигналом было бы значительно сложнее. Думаю, что все-таки скорость спутника за 1мс (и даже за 16мс) значительно не меняется, что позволяет "предсказывать" следующую фазу.

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

Напомню, что в системе GPS используется повторяющийся дальномерный код (PRN), который спутники передают с периодом 1 мс. 

У Вас каждые 1 ms появляются I\Q семплы от present коррелятора.

В микроконтроллере stm32f407vg на плате stm32f4_discovery есть 2 аппаратных DAC.

Это значит, что на осциллографе в режиме XY можно в реальном времени рисовать сигнальное созвездие I/Q.

Это очень поможет в отладке.