В этой статье я хочу рассказать о том, как я запускал собственную базовую станцию мобильной связи (4G и 2G) при помощи относительно дешевого SDR-трансивера.
Введение
Тема создания собственной базовой станции поднималась на Хабре не раз. В первую очередь, стоит отметить отличные статьи @MaFrance351:
Сам себе сотовый оператор (с использованием двух старых телефонов Motorola)
Как запустить сотовую сеть стандарта AMPS при помощи SDR
Как запустить собственную GSM-сеть за пять минут при помощи SDR
Сам себе мобильный интернет. Запускаем базовую станцию стандарта 4G LTE
Сам себе VoLTE. Запускаем сотовую сеть 4G LTE с поддержкой звонков и SMS
Статьи хороши, но автор использовал в них довольно дорогой трансивер USRP B200-mini - на Озоне такой сейчас стоит в районе 30-50 тыс. рублей (причем речь в основном идет, насколько я понял, про китайские клоны). А есть ли что-то подешевле?
Трансиверы
Главные требования к SDR-трансиверу для создания собственной сотовой сети - поддержка нужных частот, full-duplex (так как нужно принимать и передавать данные одновременно и на разных частотах), и поддержка timestamping.
Достаточно известный HackRF One не подойдет - он half-duplex.
Есть и другие трансиверы USRP, но они обычно тоже достаточно дорогие: самый дешёвый, из тех что я видел - голая плата USRP B210 (тоже, наверно, китайский клон) стоимостью 25тыс. рублей (300$).
Проект OsmoTRX поддерживает и LimeSDR - трансивер на основе LMS7002M+Altera Cyclone IV EP4CE40F23. Тоже довольно дорогой - меньше чем за 67 тыс. рублей (>800$) за версию mini я не видел.
Также OsmoTRX поддерживает BladeRF - тоже LMS6002D+Altera Cyclone IV. Оригинальный bladeRF x40 стоит 520$ (около 42 тыс. рублей).
Вот здесь есть пример сравнения характеристик разных трансиверов: https://signalens.com/compare/
Поэтому стоит перейти к трансиверам, ведущим свою родословную от PlutoSDR.
PlutoSDR (ADALM-PLUTO) - Open Source проект от Analog Devices.

Он сделан на основе микросхемы-трансивера Analog Devices AD9363 и SoC Zynq Z-7010.
Про очень близкий к нему чип трансивера на Хабре есть целая статья: Внутренности SDR чипа AD9361 — когда микроэлектроника выгоднее наркоторговли. Чип этот спроектирован как раз для использования в фемтосотах, и поддерживает full-duplex (причем там поддерживается MIMO 2x2). Zynq используется для передачи данных трансивера через USB 2.0.
К сожалению, PlutoSDR "из коробки" не поддерживает timestamping (но это можно исправить, об этом - далее).
Еще один недостаток PlutoSDR - недостаточно точный кварцевый генератор.
Китайский клон PlutoSDR я видел на Озон за 21 тыс. рублей, на сайте производителя на момент написания статьи он стоит $224 (~18 тыс. рублей, но так просто в России его не купить).
Многие разработчики в свое время явно впечатлились связкой AD9363 и Zynq, так что появилось много аналогов/клонов, к примеру:
HamGeek PLUTO+ (добавлены Gigabit Ethrernet, Micro SD (с нее удобно загружаться), более крупная ПЛИС 7020, выведены все 4 линии антенн). Разброс цен на Озон довольно большой - от 10 до 20 тыс. рублей (125-250$)
AntSDR E200 (имеет VCXO, так что к плате можно подключить внешний опорный генератор, Gigabit Ethernet, Micro SD). Важная особенность - поддерживает API UHD, так что его можно использовать с OsmoTRX. Самый дешевый на Озон - около 35 тыс. руб (440$).
LibreSDR - явно китайское творение, есть ощущение, что они вдохновлялись AntSDR. Про него есть статья: Опыт кастомизации LibreSDR. Тут тоже есть VCXO. К нему есть вот такая прошивка, поддерживающая timestamping: https://github.com/pumatrax/libresdr-fw-timestamps
Самый дешевый на Али - около 18 тыс. руб (225$).Fishball (Hamgeek/OpenSDRLab SDR Zynq), PlutoSky - тоже китайские клоны PlutoSDR. Они бывают разные, разное число разъемов антенн, Ethrernet есть или нет, Zynq 7010/7020. У меня есть ощущение, что их собирают из Б/У деталей, так что они довольно дешевые.
Самый дешевый - PlutoSDR NANO, по устройству похож на оригинальный Pluto, и стоит всего около 7 тыс. рублей (по всяким акциям бывает и меньше). Честно сказать, я не уверен, что схемотехнически он совместим с оригинальным PlutoSDR (хотя софт у него явно взят от Pluto).
Я же решил купить на Озоне OpenSourceSDRLab PlutoSky 7020 - по акции он продавался всего за 8.5 тыс. рублей (106$).

Что тут есть?
Zynq 7020. FPGA (PL) Artix-7, 85K LC, 53K LUT, 220 DSP. ARM Cortex-A9 - 2 ядра, 666МГц (без разгона).
RAM - 1Gb
Два USB Type-C. Как и на оригинальном Pluto - один для скоростной передачи данных, через USB PHY подключен к ARM (PS) Zynq, второй подключен к Zynq через FT2232H - там подключены UART и JTAG. Питание трансивера может идти по любому из них.
Разъем Gigabit Ethernet. Подключен через PHY к ARM (PS) Zynq. Так как в оригинальном Pluto нет Ethernet, не всякая прошивка его поддерживает.
Разъем SD Card. На плате есть переключатели, которые выбирают источник данных для загрузки, по умолчанию используется именно карта. Также на плате есть микросхема памяти W25Q128, ее тоже можно запрограммировать, и использовать для загрузки Zynq.
Микросхема трансивера AD9363 - 2x2 MIMO 380 MHz до 3,8 MHz. При помощи модификации прошивки диапазон можно расширить, но насколько хорошо там будет работать трансивер - вопрос. По фото видно, что в трансиверу можно подключить все 4 антенны. Подключение к Zynq сделано через LVDS. На плате есть посадочные места под усилители для передающих антенн PGA-102+, в продаже есть устройства с уже запаянными усилителями, но я купил трансивер без них. Отключенный усилитель обходится при помощи двух конденсаторов по 1nF, насколько хорошо такое решение работает - сложно сказать. Китайцы в целом хвастаются в описании трансивера, что улучшили разводку платы по сравнению с предыдущими устройствами, но у меня есть сомнения по этому поводу - к примеру, вдоль ВЧ-дорожек нет цепочек via.
TCXO на 40 MHz, заявлено как 0.5ppm.
-
В отличие от многих других плат, тут есть GPIO - они выведены на контактные площадки (на фото платы выше их хорошо видно). Официальная прошивка, поставляемая с трансивером, никак их не использует.
Линий немного - 4 линии 3.3В и 4 пары 1.8В (по они подключены к LVDS буферам FPGA).
На всякий случай, выложу тут распиновку:
Разъем GPIO, Линия PTT подключена к оптрону, который управляется транзистором Q3
Трансивер поставляется с SD картой, на которой уже имеется софт для Zynq - по сути, порт софта Zynq.
Проект Vivado для устройства можно найти здесь: https://github.com/Xiaozhang-code-cloud/Fish-Wan-plutosdr-fw-7020-SDR/tree/master/hdl/projects/pluto
Вот тут есть схема, но без GPIO: https://github.com/OpenSourceSDRLab/PlutoSky_7020_AD936X_SDR/blob/main/hardware/schematic_PlutoSky.pdf
Я писал на почту в opensourcesdrlab.com, они высылали мне и схему с GPIO (правда, почему-то без USB).
Для этого трансивера, как и для многих других, есть альтернативная прошивка, добавляющая определенные улучшения: https://github.com/F5OEO/tezuka_fw
Timestamping
Как я уже писал выше, для возможности работать как базовая станция мобильной связи, трансивер должен иметь возможность поддерживать timestamping, то есть передаваемые по USB пакеты с данными ADC/DAC должны иметь точные временные метки. Используя их, можно обеспечить точную синхронизацию предаваемых и принимаемых данных.
Оригинальный софт для Pluto такой возможности не имеет. Тем не менее, есть вот такой проект, в котором автор (Phil Greenland) добавил поддержку timestamping для оригинального Pluto:
Private LTE with Analog ADALM-PLUTO. Спасибо ему за это!
Также у него есть продолжение: Private LTE with Pluto+ SDR - timestamping для Pluto+. Этот проект куда больше соответствует моему трансиверу, так что именно его я использовал за основу.
Фактически, основное различие моего трансивера, и Pluto+ - схема подключения AD9363 к Zynq.
Я посчитал, что достаточно будет просто поменять имеющийся у меня проект HDL (прошивка FPGA) и добавить туда модули timestamping, как это описано у Phil Greenland, и все заработает. В принципе, так и вышло, но как всегда, вылезли определенные сложности.
Отмечу, что схемотехнику разных трансиверов удобно сравнивать, используя вот этот репозиторий: https://github.com/F5OEO/maia-sdr/tree/refactor/maia-hdl/projects (использование пинов Zynq находится в файлах xdc - Constraints File).
Все операции я выполнял, используя Vivado 2022.2 для Windows.
Для начала, нужно скачать сам HDL-проект, ссылку на него я дал выше.
Сам проект изначально находится в "очищенном" состоянии, фактически, проекта еще нет, так что для открытия его в GUI Vivado нужно создать проект.
Для этого нужно запустить Vivado 2022.2 Tcl Shell (запуск может длится довольно долго).
Перейти к папке нужного проекта: cd D:/my_path/Fish-Wan-plutosdr-fw-7020-SDR/hdl/projects/pluto
и выполнить команду: source ./system_project.tcl
Отмечу, что в этом скрипте есть команда "adi_project_run pluto", которая запустит синтез и имплементацию проекта, которые будут идти довольно долго. С одной стороны, смысла собирать проект без изменений в данном случае нет, а с другой стороны - так можно убедится, что проект создан корректно, и нет никаких проблем с Vivado.
В итоге будет создан проект, которые можно открыть в GUI Vivado, выглядит это так:

Упрощенная схема HDL проекта выглядит так (изображение взято отсюда):

Справа - физическая микросхема трансивера; AD9361 CORE - это IP-блок axi_ad9361.
Слева - часть ARM, данные от которой/в которую идут через DMA. Модули cpack/upack служат для преобразования ширины потоков данных. Interpolator/Decimator используются по желанию.
Для добавления поддержки timestamping Phil Greenland создал два модуля cpack_timestamp/upack_timestamp. Они помещаются между DMA и cpack/upack.
Модуль cpack_timestamp отвечает за добавление временных меток в данные от АЦП, и он просто вставляет значение времени в поток данных, отправляемых на Zynq.
Модуль upack_timestamp обнаруживает временные метки в приходящем на него потоке, и не пропускает поток данных дальше на передачу до тех пор, пока не наступит указанное время.
Исходный код этих модулей выложен тут: https://github.com/pgreenland/plutosdr-hdl-quantulum
Для отсчета времени используется простой 64-битный счетчик, который подключается к входам cpack_timestamp/upack_timestamp. Дополнительно на эти модули заводится 32-битные шины "timestamp_every", значение на которых определяет период, с которым должны идти временные метки, обычно этот период равен размеру блока, предаваемому через DMA. Соответственно, значения на шинах берутся с блока axi_ad9361 - у него есть линии GPIO для пользовательских целей. Модули при timestamp_every=0 (такое состояние после перезагрузки) пропускают данные через себя без модификаций, что обеспечивает полную совместимость со всем софтом для ПК, рассчитанным на работу с Pluto.
То, как именно должны быть подключены модули, у автора показано вот на этом изображении (оно довольно большое). С другой стороны, вот в этом коммите можно посмотреть список изменений, которые нужно внести в HDL.
Для добавления этих модулей в проект, их необходимо добавить в "IP Catalog":

Для первого теста я просто добавил в проект модуль-генератор низкочастотных импульсов, и вывел его сигнал на ножку GPIO - по сути, это классическое "моргание светодиодом". Эта часть у меня заработала.
Тут, правда, стоит отметить следующие нюансы:
Схема system.bd и связанный с ней system_wrapper.v не находится наверху иерархии. Так что при добавлении портов на схему их нужно вручную добавлять и на system_top.v и связывать с system_wrapper.
Конфигурацию FPGA (bitstream) в ее память загружает загрузчик FSBL (он запускается еще до U-Boot). В ходе запуска Linux запускается и драйвер ad9361 - iio, при этом он производит конфигурацию регистров AD9361. Соответственно, если через JTAG загрузить новый bitstream, то регистры AD9361 будут сброшены, и iio не сможет нормально работать. Решение проблемы тоже есть у Phil Greenland - нужно выгрузить драйвер из памяти ARM, загрузить новый bitstream, и после этого заново загрузить драйверы:
Необходимые команды
echo 79024000.cf-ad9361-dds-core-lpc > /sys/bus/platform/drivers/cf_axi_dds/unbind echo 79020000.cf-ad9361-lpc > /sys/bus/platform/drivers/cf_axi_adc/unbind echo 7c400000.dma > /sys/bus/platform/drivers/dma-axi-dmac/unbind echo 7c420000.dma > /sys/bus/platform/drivers/dma-axi-dmac/unbind echo 41600000.i2c > /sys/bus/platform/drivers/xiic-i2c/unbind
echo 41600000.i2c > /sys/bus/platform/drivers/xiic-i2c/bind echo 7c420000.dma > /sys/bus/platform/drivers/dma-axi-dmac/bind echo 7c400000.dma > /sys/bus/platform/drivers/dma-axi-dmac/bind echo 79024000.cf-ad9361-dds-core-lpc > /sys/bus/platform/drivers/cf_axi_dds/bind echo 79020000.cf-ad9361-lpc > /sys/bus/platform/drivers/cf_axi_adc/bind
Дальше необходимо просто привести схему в GUI в соответствие с той, что приведена у Phil Greenland (ссылка выше).
Забегая вперед, отмечу, что доработанный проект собирался, но работал неадекватно. Оказалось, что я допустил следующие ошибки:
На изображении "PlutoSDR HDL" выше видно, что большинство модулей получают тактовый сигнал от модуля axi_ad9361 - "l_clk". Однако Phil Greenland изменил этот подход для adc_dma/dac_dma - теперь он подключен к sys_cpu_clk = CLK0:
-ad_connect axi_ad9361/l_clk axi_ad9361_adc_dma/fifo_wr_clk
-ad_connect axi_ad9361/l_clk axi_ad9361_dac_dma/m_axis_acl
+ad_connect sys_cpu_clk axi_ad9361_adc_dma/fifo_wr_clk
+ad_connect sys_cpu_clk axi_ad9361_dac_dma/m_axis_aclk
Я это не сразу заметил, а без этих изменений прием не работал.-
Что в Pluto, что в Pluto+ AD9363 подключена к Zynq через CMOS, но вот у меня она подключена по LVDS. Соответственно, блок axi_ad9361 у меня тоже сконфигурирован для работы с LVDS.
В случае использования CMOS подключения частота "l_clk" равна выбранной частоте семлирования. А вот в случае LVDS она в 2 или 4 раза выше частоты семлирования - это зависит от выбранного режима работы MIMO - 1r1t / 2r2t (у меня выбран 2r2t). В этом проекте счетчик времени тактируется именно от "l_clk", так что в результате у меня он неверно считал время. Это решается собственным счетчиком со встроенным делителем:Код счетчика
module timestamp_div_counter ( input wire clk_in, // Input clock output reg [63:0] c_out // 64-bit counter output ); // Parameter for division factor N parameter N = 4; //2R2T. // Internal signals reg [5:0] clk_div_counter; // Clock divider counter reg divided_clk; // Divided clock signal reg divided_clk_dly; // Delayed divided clock for edge detection // Clock divider logic always @(posedge clk_in) begin if (clk_div_counter == (N/2 - 1)) begin clk_div_counter <= 0; divided_clk <= ~divided_clk; end else begin clk_div_counter <= clk_div_counter + 1; end end // Delay divided_clk for edge detection always @(posedge clk_in) begin divided_clk_dly <= divided_clk; end always @(posedge clk_in) begin if (divided_clk && !divided_clk_dly) begin // Rising edge detection c_out <= c_out + 1; end end endmodule
Но вот HDL проекта собран, также сгенерирован и bitstream. Что дальше? Я захотел сделать так, чтобы новый уже имевшаяся у меня оригинальная сборка загружала новый bitstream.
На купленной SD карте находятся файлы:
uramdisk.image.gz - образ корневой файловой системы Linux - rootfs (ramdisk)
uEnv.txt - текстовый файл конфигурации, используется U-Boot
uImage - ядро Linux
BOOT.bin - содержит в себе FSBL, собранный U-Boot, Bitstream
devicetree.dtb - собственно, файл devicetree
Соответственно, BOOT.bin и нужно изменить. Напрямую отредактировать его нельзя.
Тут приходились утилиты https://github.com/antmicro/zynq-mkbootimage
С помощью exbootimage можно разобрать BOOT.bin на отдельные файлы: system_top.bit / fsbl.elf / u-boot.elf
Для того, чтобы было собрать единый файл BOOT.bin, нужно создать текстовый файл boot.bif, у меня он имеет такой вид:
the_ROM_image: { [bootloader]fsbl.elf system_top.bit [load=0x04000000]u-boot.elf }
system_top.bit - очевидно, нужно подменить своим файлом.
Команда для сборки файла: ./mkbootimage boot.bif BOOT.bin
И, в итоге, с новым BOOT.bin ничего не заработало - в консоли UART пусто, никаких логов U-Boot. При этом на плате светится светодиод Done - что говорит о том, что FSBL загрузил в FPGA bitstream, на GPIO есть заданная частота - то есть FSBL правильно настроил систему тактирования ARM. Ситуация осложнялась тем, что FSBL по умолчанию никаких логов в UART не выводит, даже сообщения об ошибках.
А вот утилита bootgen из состава Vivado ругалась:
bootgen -image boot.bif -arch zynq -process_bitstream bin
[ERROR] : ELF Parsing Error !!! Wrong Header Size
Что намекало на то, что извлеченные elf файлы имели кривые заголовки. С другой стороны, fsbl.elf явно запускался...
В итоге, я решил пересобрать FSBL с включенным флагом логгирования. Как это сделать, описано тут, хотя есть и другое описание - у меня был именно этот вариант:
Debug prints in FSBL are now disabled by default (except for FSBL banner). To enable debug prints, define symbol: FSBL_DEBUG_INFO
In VITIS this can be done by: right click on FSBL application project -> select “C/C++ Build Settings” -> “Tool Settings” tab ->Symbols (under ARM v7-A gcc compiler)
Click on Add (+) icon and Enter Value: FSBL_DEBUG_INFO, click on “OK” to close the “Enter Value” screen
С таким FSBL после загрузки BOOT.bin выдается такой лог:
FSBL Log
FPGA Done ! In FsblHookAfterBitstreamDload function Partition Number: 2 Header Dump Image Word Len: 0x000196E9 Data Word Len: 0x000196E9 Partition Word Len:0x000196E9 Load Addr: 0x04000000 Exec Addr: 0x00000000 Partition Start: 0x000FE490 Partition Attr: 0x00000011 Partition Checksum Offset: 0x00000000 Section Count: 0x00000001 Checksum: 0xFBEB5442 Application Handoff Address: 0x00000000 In FsblHookBeforeHandoff function No Execution Address JTAG handoff
Видно, что Exec Addr - нулевой. Пробовал открывать файл u-boot.elf просмотрщиком заголовков ELF - и там тоже нулевой exec_addr. По сути, все проблемы были только из-за одного адреса, так что я просто модифицировал исходный код mkbootimage: вот тут заменил
hdr->dest_exec_addr = 0; на hdr->dest_exec_addr = node->load;
После этого загрузка U-Boot и Linux пошла нормально, трансивер работал, как и раньше.
Для повышения скорости передачи данных и поддержки timestamping Phil Greenland разработал два демона:
https://github.com/pgreenland/pluto-sdr-ip-gadget
https://github.com/pgreenland/pluto-sdr-usb-gadget
Оба должны быть запущены на ARM. Я решил попробовать не возиться со сборкой и запуском их у себя, а просто попробовать взять всю rootfs от Phil Greenland, у него выложен уже готовый вариант для Pluto+. Однако этот образ предназначен для записи на встроенную микросхему Flash - все файлы в архиве имеют расширения dfu и frm. Конкретно мне был нужен pluto.frm.
Вот тут нашлась инструкция и скрипт, позволяющие разбирать такой файл: https://github.com/daniestevez/pluto-firmware-modifications
В ходе распаковки скрипт создает файл Ramdisk (gzip file), содержащий rootfs.
Далее нужно установить sudo apt install u-boot-tools и запустить утилиту:
mkimage -A arm -T ramdisk -C gzip -d Ramdisk uramdisk.image.gz
Вот этот uramdisk.image.gz можно поместить на SD карту. После этого Linux у меня успешно загрузился, при этом в логах прямо упоминается Pluto+, а версия iio отображается: v0.38_plutoplus_with_timestamping. Прием и передача через софт на ПК работают нормально, и на этом работа с софтом Zynq завершена.
Далее можно проверить работу timestamping уже в Linux. Для этого Phil Greenland написал тестовую утилиту, которая использует режим loopback AD9363 (данные, отправленные на передачу, сразу же возвращаются назад - в обход DAC/ADC). Ее исходный код выложен здесь.
Прежде чем собирать утилиту, нужно установить на ПК библиотеки SoapySDR. Их назначение - быть универсальной прослойкой между разными SDR-устройствами и софтом для работы с SDR-данными. Про то, как устанавливать SoapySDR, подробно описано у Phil Greenland. Правда, он все нужные библиотеки сбирал из исходников, я пробовал просто устанавливать часть их из репозитория Ubuntu - и это работало. И также есть разница - в своих инструкциях Phil Greenland устанавливал весь софт в отдельную папку "srsRAN", я в своих экспериментах пробовал не делать так - тоже работало.
Соответственно, я устанавливал:
sudo apt install libsoapysdr-dev soapysdr-tools - основные библиотеки SoapySDR (у меня libsoapysdr0.8)
Проверить работу можно командой SoapySDRUtil --info
sudo apt-get install libiio-dev libiio-utils - установка libio (у меня 0.25-4build2)
Сборка и установка libad9361-iio
git clone --branch v0.3 https://github.com/analogdevicesinc/libad9361-iio.git
cd libad9361-iio
mkdir build && cd build
cmake ..
make -jnproc && sudo make install
А вот далее нужно установить библиотеки для работы с Pluto, доработанные Phil Greenland:
git clone --branch sdr_gadget_timestamping https://github.com/pgreenland/SoapyPlutoSDR.git
cd SoapyPlutoSDR
mkdir build && cd build cmake …
make -jnproc && sudo make install
Возможно, потребуется вызвать команду sudo ldconfig.
После этого SoapySDRUtil --info должна показывать plutosdr среди установленных библиотек.
Далее нужно настроить права USB устройства:
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0456", ATTR{idProduct}=="b673", MODE="666"' | sudo tee /etc/udev/rules.d/90-libiio_pluto.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
После этого можно попробовать подключиться к трансиверу:
SoapySDRUtil --make="driver=plutosdr,hostname=192.168.2.1"
Если нет сообщений об ошибках, то все установлено правильно.
Далее можно попробовать воспользоваться вышеупомянутой утилитой для loopback теста (она является частью кода SoapyPlutoSDR, который уже скачан выше).
По умолчанию, она производит большое число повторяющихся тестов, их число можно уменьшить:
./test_timestamp_loopback -b 20
Логи должны быть примерно такие (тут только часть лога):
Скрытый текст
Buffer: 15 - Samples: 1920, Flags: 4, Time: 1102222095833, TimeDiff: 1000000 Buffer: 16 - Samples: 1920, Flags: 4, Time: 1102223095833, TimeDiff: 1000000 Buffer: 17 - Samples: 1920, Flags: 4, Time: 1102224095833, TimeDiff: 1000000 Buffer: 18 - Samples: 1920, Flags: 4, Time: 1102225095833, TimeDiff: 1000000 Buffer: 19 - Samples: 1920, Flags: 4, Time: 1102226095833, TimeDiff: 1000000 Checking channel 0 Buffer 0 doesn't contain timestamp Buffer 1 doesn't contain timestamp Buffer 2 doesn't contain timestamp Buffer 3 doesn't contain timestamp Buffer 4 contains TX TS after 4 buffers, TX TS queue index 0, buffer word index 30 Buffer 5 contains TX TS after 1 buffers, TX TS queue index 1, buffer word index 30 Buffer 6 contains TX TS after 1 buffers, TX TS queue index 2, buffer word index 30 Buffer 7 contains TX TS after 1 buffers, TX TS queue index 3, buffer word index 30
TimeDiff: 1000000 - должен быть равен 1М, это 1мс - такой период между передаваемыми пакетами задан в утилите.
Видно, что первые четыре буфера не приняты - это связано с тем, что в тесте специально задана задержка в 4мс для timestamp передаваемых данных.
Замечу, что в виртуальной машине этот тест у меня работал неверно - вероятно, виртуальная машина дает свои задержки для USB.
4G
4G на таком трансивере запустить несложно - так как у Phil Greenland уже выложена инструкция по запуску srsRAN_4G.
Также, как и он, я собирал srsRAN_4G из исходников.
При сборке выскакивало сообщение об ошибке:
channel_mapping.h:142:8: error: ‘string’ in namespace ‘std’ does not name a type
142 | std::string to_string() const
Я так и не смог найти более-менее приличного способа ее побороть, и просто добавил в начало
srsRAN_4G/lib/include/srsran/radio/channel_mapping.h строку #include <string>
Конфигурация srsRAN_4G находится в папке ~/.config/srsran
Ее нужно поправить так, как указано у Phil Greenland.
В частности, необходимо изменить некоторые настройки трансивера; EARFCN - канал, который будет определять частоту, на которой будет базовая станция; числовые значения mcc и mnc - код страны и оператора; значение n_prb - оно определяет ширину полосы сигнала БС:
n_prb=6 -> 1.4 МГц
n_prb=15 -> 3 МГц
n_prb=25 -> 5 МГц
Внимание - прежде чем указывать EARFCN и n_prb, убедитесь при помощи SDR, что на желаемом участке спектра нет никаких других сигналов, чтобы не создать случайно помехи в эфире!
Мощность передатчика должна быть максимально низкой, менее 1dBm!
Перед запуском можно выполнить echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
Далее из двух консолей нужно выполнить:
cd ${HOME}/.config/srsran
sudo srsepc epc.conf
и
cd ${HOME}/.config/srsran
sudo srsenb enb.conf --- это приложение работает с трансивером
Если сообщений об ошибках нет, то, скорее всего, БС запущена. Её работу можно проверить другим SDR-приемником, посмотрев эфир.
Потом можно попробовать запустить ручной поиск операторов на телефоне. Если передача сигнала работает верно, телефон сможет обнаружить сеть, но подключится к ней не удастся - сеть 4G требует наличия шифрования данных, а шифрование обеспечивается SIM-картой. Из современных SIM-карт считать записанные в них ключи Ki и OPc очень проблематично, так что единственным вариантом остается использовать специальные программируемые SIM.
Я решил попробовать карту, рекомендованную Phil Greenland - производства OYEITIMES (700р = ~9$ с доставкой). Забегая вперед - она у меня заработала. Для программирования использовал древний безымянный считыватель смарт-карт, ранее вообще изначально бывший USB-ключом - он нормально заработал с приложением GRSIMWrite 4.4.10.
Приложение позволяет считывать часть данных из карты, и записывать в программируемые карты свои значения.
У Phil Greenland есть довольно подробная инструкция, как сформировать нужные значения. Замечу, что для настройки srsRAN_4G нужны только значения IMSI, KI, OPc.
Телефон после установки этой карты ее увидел, ну и, конечно, к сетям обычных операторов подключаться не стал.
Для добавления своей SIM-карты в srsRAN_4G нужно изменить файл user_data.csv (хранится среди остальных файлов настроек), переписав строку в его конце:
ue2,mil,MY_IMSI_HERE,MY_KI_HERE,opc,MY_OPC_HERE,8000,0000000012fa,7,dynamic
Несколько подробнее про это - тут (ссылка).
Теперь, после запуска БС, телефон сразу же обнаружил созданную сеть и подключился к ней.
Для проверки работы можно попинговать с телефона ПК, на котором работает srsRAN.
Для того, чтобы с телефона можно было получить доступ к интернету, мне понадобились следующие команды:
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o wlo1 -j MASQUERADE (wlo1 --- сетевой адаптер ПК).
После этого на телефоне появился доступ к интернету.
Я пробовал варианты n_prb=6 и n_prb=15. С последним значением в на расстоянии метра от трансивера, судя по тесту iperf3 удавалось получить скорость канала около 5 Мбит/с upload и 10 Мбит/с download.
Тест скорости Яндекса показывал около 3.6 Мбит/с upload | 6 Мбит/с download.
А вот на удалении в несколько метров скорость download падала в несколько раз.
Вот так выглядит в телефоне информация о подключении:

Так выглядит спектр радиоэфира во время работы трансивера. Стоит отметить, что мощность передачи специально сделана маленькой, и на расстоянии в полметра от трансивера сигнал БС оказывается ниже, чем сигналы "настоящих" БС:

Честно сказать, запуск 4G не был мне очень-то интересен - он и так есть у меня на телефоне, да и скорость хуже, чем у WIFI. Передачи голоса и SMS тут нет.
Куда интересней было запустить GSM - 2G.
2G
@MaFrance351 в своих экспериментах с 2G использовал OsmoNITB/OpenBSC. Однако этот стек на текущий момент уже устарел и заменен набором отдельных утилит Osmocom CNI - насколько я понял, переход произошел еще в 2017 году.
В стеке Osmocom за работу с SDR-трансивером отвечает утилита OsmoTRX. Обмен данными (прием от телефона) выстроен так:

OsmoTRX поддерживает следующие виды SDR-трансиверов: UHD (их много разных), bladeRF, LimeSDR. Поддержки SoapySDR тут нет.
Насколько я понял, когда-то раньше OsmoTRX не имел поддержки LimeSDR напрямую, и некоторые пользователи ухитрялись запустить LimeSDR через прослойку SoapyUHD. Даже сохранилась инструкция по запуску. Данные передавались так:

Я пробовал сделать аналогичным образом, но ничего не заработало - OsmoTRX выдавал кучу ошибок. Подозреваю, что в реализации SoapyPlutoSDR чего-то не хватало. Так что оставался только один путь - добавлять в OsmoTRX прямую поддержку работы через SoapySDR самостоятельно.
Исходный код OsmoTRX выложен тут (это зеркало) https://github.com/osmocom/osmo-trx. Конкретно реализации работы с трансиверами находятся тут: https://github.com/osmocom/osmo-trx/tree/master/Transceiver52M/device. В качестве образца я взял "lms", и делал свою реализацию на его основе. В принципе, ничего прямо очень сложного тут нет, так как уровень абстракции довольно велик.
OsmoTRX рассчитан на то, что трансивер будет работать на частоте дискретизации, жестко привязанной к таймингам GSM. В OsmoTRX есть два варианта настроек частоты дискретизации: 1SPS (Samples Per Symbol) и 4SPS. Я использовал именно второй вариант, это соответствует частоте дискретизации 270833*4=~1.08033MSPS (до сих пор не понимаю, почему в GSM выбрали настолько странные частоты). Соответственно, трансивер должен уметь поддерживать работу на таких частотах. Насколько я знаю, в OsmoTRX есть возможность включить интерполяцию (resampling), чтобы можно было выбирать более круглые частоты дискретизации, но там есть определенные сложности, и процесс интерполяции занимает ресурсы процессора ПК.
Основные задачи программного модуля поддержки конкретного трансивера - его инициализация, прием и передача сырых радио данных (IQ-сигналы) по вызовам из более высокоуровневой части OsmoTRX (которая, фактически, является радиомодемом). Работа с трансивером идет через библиотеки, поставляемые с ним (в моем случае - SoapySDR), так что задача добавления нового трансивера - просто написать прослойку.
Самая хитрая часть - функции передачи данных readSamples/writeSamples. Обе предполагают блокирующее выполнение, блокировка должна занимать столько времени, сколько идет прием/отправка блока радиоданных. В каждую из них OsmoTRX передает необходимое значение timestamp.
В случае с конкретной реализацией SoapySDR это создает сложности для функции readSamples() - она забирает данные от АЦП, и железо в Zynq тоже передает свое значение timestamp, и нет никакой возможности указать железу, в какой момент времени запускать АЦП. Насколько я понял, в lms было также, и я использовал их подход, правда, несколько переделал его. Передача (writeSamples) синхронизирована с приемом - для этого из одной функции в другую передается последнее значение timestamp ADC. Ну а величины timestamp, приходящие в обе функции readSamples/writeSamples, по большей части используются как источник данных о номере текущего принимаемого/передаваемого блока радиоданных (они должны совпадать).
Уже после запуска кода я обнаружил, что телефон мою сеть видит, но подключиться к ней не может. На стороннем SDR приемнике хорошо было видно, что телефон редко-редко отправляет пакеты на БС, и на этом все. Это явно говорило о проблемах синхронизации.
Для проверки я добавил в свой код модуля поддержки SoapySDR тестовый режим (включается через #define SOAPY_LOOPBACK_TEST). В этом режиме IQ-данные, формируемые OsmoTRX, прямо в writeSamples подменяются моими тестовыми посылками - они представляют собой короткие гармонические импульсы, передаваемые через определенное число пакетов.
соответственно, в readSamples была добавлена функция, анализирующая принятые данные (просто поиск подскакивания амплитуды сигнала). Конечно, при включённом тесте нужно поменять частоту приема - сделать ее равной частоте передачи (в GSM прием и передача разнесены на 45МГц).
Используя этот тестовый механизм, я быстро выяснил, что у меня были проблемы и с синхронизацией блоков, и с синхронизацией смещения начала передачи. То есть тестовый сигнал начинался ровно в середине блока, а в принятом блоке он обнаруживался немного позже. Это можно скомпенсировать, добавляя смещение к величине timestamp перед запуском отправки данных.
Сам форк выложен здесь: https://github.com/iliasam/osmo-trx_soapy
Osmocom CNI
Нынешний стек Osmocom представляет из себя набор отдельных программ:
Радиосеть (RAN - Radio Access Network):
• OsmoTRX: GSM радио-модем, как раз упомянутый выше.
• OsmoBTS: программная реализация базовой станции (Base Transceiver Station).
• OsmoBSC: контроллер базовых станций, управляющий ресурсами радиосети.
Ядро сети (Core Network):
• OsmoMSC: центр мобильной коммутации, отвечает за вызовы, SMS и USSD.
• OsmoHLR: реестр собственных абонентов сети.
• OsmoMGW: медиашлюз для обработки пользовательского трафика (голоса).
• OsmoSTP: точка передачи сигналов в стеке SS7/SIGTRAN.
Передача данных (GPRS/3G):
• OsmoPCU: контроллер пакетных данных для GPRS.
• OsmoSGSN: узел поддержки обслуживания GPRS.
• OsmoGGSN: шлюзовой узел поддержки GPRS для выхода в интернет
Вместе они образуют вот такую структуру:

А есть еще такая картинка, менее абстрактная:

В современной Ubuntu (я использовал 24.04) они просто скачиваются:
sudo apt install osmo-bts (остальные - аналогично).
После установки все они сразу устанавливаются как службы systemd.
Мне это было не нужно, так что службы приходится отключать:
systemctl stop osmo-bsc
systemctl stop osmo-bts-trx
....
systemctl disable osmo-bts-trx
systemctl disable osmo-bsc
....
Соответственно, запускать их можно из терминала: osmo-bsc -c osmo-bsc.cfg - конфиг обязателен, у каждой программы он свой. Написание конфига - дело муторное, настроек очень много, а еще, если я верно понял, в Osmocom периодически меняли формат конфигов.
Я не буду как-то подробно описывать в этой статье, как эти конфиги править, так как это очень обширная тема, и я сам не уверен, что я настроил все верно. Кое-что можно найти в этом цикле статей: https://nickvsnetworking.com/gsm-with-osmocom-part-3-bts-in-practice-with-limesdr-osmo-bts-trx/
Для запуска базовой станции в самом простейшем случае достаточно запустить OsmoTRX+OsmoBTS+OsmoBSC.
В настройках OsmoTRX (test1.cfg) ничего менять не нужно - их немного, и они все относятся к параметрам SDR.
Настройки OsmoBTS (osmo-bts-trx.cfg) тоже не нужно менять.
Параметр "osmotrx tx-attenuation 10" задает коэффициент ослабления мощности передачи. В OsmoTRX сейчас "мощность" передачи по умолчанию - 70dB, с ослаблением устанавливается 60dB, при этом в самом PlutoSDR пределом является значение 89.75dB, и оно соответствует мощности +2..+5 dBm (1.5-3 mW).
Соответственно, при 60dB мощность будет около -30dBm или около 0.001 mW. Это очень маленькое значение, при таком значении сигнал будет приниматься только в пределах комнаты, зато никому не помешает.
Самый сложный конфиг - у OsmoBSC.
network country code 250 (MCC)
mobile network code 07 (MNC)
Пара значений, характеризующие оператора. Значения 250 07 - сеть уже несуществующего оператора SMARTS. Для тестов также можно использовать 001 02, в списке операторов телефона такая сеть будет отображаться как "Test PLMN"
location_area_code 13415 - код зоны расположения БС, здесь это случайное число.
arfcn - наиболее важный параметр, номер канала, определяющий частоту передачи и приема БС.
Для GSM 900 значение частоты, на котором будет предавать сигнал, определяется по формуле Fdownlink = 935.0 + 0.2·arfcn
Внимание - прежде чем указывать arfcn, убедитесь при помощи SDR, что на желаемом участке спектра нет никаких других сигналов, чтобы не создать случайно помехи в эфире!
В итоге, после запуска OsmoTRX+OsmoBTS+OsmoBSC телефон должен "увидеть" указанную сеть (конечно же, в случае ручного поиска сетей). Подключится к ней пока невозможно.
Вот так выглядит спектр радиоэфира во время работы трансивера. Также как и в случае 4G, уровень сигнала очень быстро снижается даже при небольшом удалении приемника от трансивера.

Для полноценного запуска БС и передачи голоса потребуются дополнительные программы OsmoMSC+OsmoHLR+OsmoMGW+OsmoSTP.
В конфиге OsmoMSC указывается:
Тоже MCC и MNC.
authentication optional - для того, чтобы не нужно было прописывать ключи SIM в базе HLR.
В конфиге OsmoHLR указывается:
database hlr.db -- имя базы данных пользователей, в данном случае предполагается, что база лежит рядом с конфигом.
ussd route prefix *#100# internal own-msisdn -- по USSD команде *#100# владелец телефона может узнать свой номер
ussd route prefix *#101# internal own-imsi -- по USSD команде *#101# владелец телефона может узнать свой идентификатор SIM-карты (IMSI).
subscriber-create-on-demand 10 cs+ps -- включает автоматическую регистрацию новых абонентов в сети (и, соответственно, добавление их в базу данных). 10 - количество цифр номера телефона (MSISDN), который будет автоматически присвоен новой SIM-карте. cs+ps: Определяет типы услуг, которые будут разрешены новому абоненту: cs (Circuit Switched): Голосовые вызовы и SMS (голосовой трафик). ps (Packet Switched): Мобильный интернет (GPRS/EDGE/3G).
По-моему, база данных сама по себе у меня не создавалась, и в первый раз ее нужно было создать вручную: osmo-hlr-db-tool create
Остальные конфиги я особо не правил, и использовал настройки, найденные в интернете.
Для того, чтобы запускать программы было проще, я написал два скрипта, которые запускают программы в отдельных окнах, используя tmux.
Выглядит это так (тут запущены 5 программ):

Конфиги и скрипты я выложил тут: https://github.com/iliasam/osmo-rx_soapy/tree/my_tests/Osmocom_configs
Посоле запуска всех программ к созданной базовой станции можно подключить несколько телефонов (при использовании обычных симок сеть придется выбирать вручную). Через *#100# можно узнать номер каждого из телефонов и звонить с одного на другой. Также можно пересылать SMS.
Можно отправить SMS прямо с ПК.
Для этого нужно подключится к терминалу OsmoMSC: telnet localhost 4254
И в отправить в терминал
subscriber imsi 250070000000001 sms sender imsi 250070000000001 send "HELLO WORLD".
Здесь в качестве отправителя указан imsi SIM-карты, на который отправляется сообщение - так проще всего, так как не нужно создавать отдельные сущности в базе.
GPRS/EDGE
Конечно, мне было очень интересно запустить доступ к Интернету на своей БС. Честно скажу, результат вышел так себе, непонятно, правда, по какой именно причине - что-то с железом или софтом.
Для запуска сетевых возможностей нужны еще три программы - OsmoPCU+OsmoSGSN+OsmoGGSN.
В настройках OsmoPCU можно выбрать тип кодирования, который будет использоваться для передачи данных.
В настройках OsmoSGSN я особо ничего не менял.
В настройках OsmoGGSN нужно указать название apn, которое должно совпадать с тем, что указывается на телефоне.
Значение tun-device tun4 - имя создаваемого туннеля. Также тут указываются имена DNS серверов и диапазон IP-адресов, которые будут выдаваться телефону.
Также нужно поменять настройки OsmoBTS:
gprs mode egprs - режим работы EDGE или gprs mode gprs - режим работы GPRS.
Также я добавлял такие строки:
Скрытый текст
gprs routing area 1 gprs nsei 101 gprs nsvc 0 nsvci 101 gprs nsvc 0 local udp port 23001 gprs nsvc 0 remote udp port 23000 gprs nsvc 0 remote ip 127.0.0.1
Также нужно поменять некоторые значения phys_chan_config - с TCH/F (голос или данные CSD) на PDCH ("Packet Data Channel"). Чем больше таких каналов, тем большей скорости можно добиться.
Не уверен точно, но вроде бы перед первым запуском osmo-ggsn я выполнял:
sudo setcap cap_net_admin=ep /usr/bin/osmo-ggsn
Это нужно для того, чтобы в дальнейшем не нужно было запускать osmo-ggsn от root.
Для более удобного запуска этих программ я сделал еще один скрипт. В итоге, для работы сети нужно иметь работающими все 10 программ:

Свои варианты конфигов я выложил тут: https://github.com/iliasam/osmo-trx_soapy/tree/my_tests/Osmocom_configs
После запуска всех программ БС телефон может подключится к EDGE.
Возможно, на телефоне нужно будет разрешить передачу данных в роуминге. Пока есть доступ только к сети ПК, на котором работает БС - то есть можно попинговать его с телефона (по адресу вроде 192.168.X.X), можно запустить iperf3 -s на ПК.
Вот так выглядит информация о сети в Android:

Пинг ПК получается довольно медленным, скорость передачи данных - тоже не высокая.

Выше показаны результаты iperf3 для теста Download - телефон получал данные от сервера. Скорость Upload вышла намного ниже - на уровне 5 кБит/с. Думаю, что это слишком низко для нормально работающей сети.
Следующий этап - выход в Интернет. Для этого нужно перенаправить данные от Osmocom (tun4) на сетевой интерфейс системы.
Я делал это так:
sudo sysctl -w net.ipv4.ip_forward=1 --- разрешение пересылки пакетов между интерфейсами.
sudo iptables -t nat -F --- очитстка таблицы NAT
Далее, собственно, создание правил для пересылки пакетов, wlp5s0 - имя сетевого интерфейса, на который они будут перенаправляться:
sudo iptables -t nat -A POSTROUTING -s 172.16.222.0/24 -o wlp5s0 -j MASQUERADE
sudo iptables -A FORWARD -i tun4 -o wlp5s0 -j ACCEPT
sudo iptables -A FORWARD -i wlp5s0 -o tun4 -m state --state ESTABLISHED,RELATED -j ACCEPT
После применения этих правил ping 8.8.8.8 со стороны телефона заработал (хотя и очень медленно, 1.5-4 секунды). А вот сайты вообще не открывались.
Помогло это:
sudo iptables -t mangle -I FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 400 --- ограничение размера пакетов до 400 байт.
После этого можно попробовать открыть какой-нибудь небольшой сайт, я пробовал old-dos.ru.
По каким-то причинам он открывался у меня не с первого раза - возможно, сам Android, обнаружив сеть, забивал uplink канал.
В целом, на современном телефоне проверять работу EDGE не интересно. Куда интересней для меня лично было попробовать проверить работу старых телефонов.
Первым делом я попробовал старый Samsung SGH-i200 (ОС - Win Mobile 6.1) купленный в 2008 году:

Как видно, родной Internet Explorer все еще работает, хотя и открывает только старые сайты (без шифрования).
Opera Mini, установленная еще в те давние времена, все еще работает:

[1] - просто интерфейс Windows Mobile 6.1. Видно значок передачи данных (E) и название оператора.
[2] - так выглядит hpc.ru в Opera.
[3] - ya.ru нормально в Opera не открылся.
[4] - а вот поиск Яндекса, встроенный в Opera, еще как-то работает.
[5],[6] - встроенный в Opera поиск Google тоже работает, даже LLM-пересказ результатов включился. В 2008 о таком даже и не думали...

[1],[2] - попробовал открыть собственную статью на Хабре (с разными настройками). Статья загрузилась, даже картинки отображались.
[3] - в GSPlayer (в WM6.1 он отображается криво) запустил воспроизведение онлайн-радио. В принципе, при скорости 64kbit/s оно работает, слушать можно. К сожалению, воспроизведение все равно периодически прерывалось - видимо, что-то не так со связью.
А для родного Media Player я не смог найти радио-трансляций - он не поддерживает современные кодеки.
[4],[5] - попробовал также запустить Opera Mobile. Но тут ситуация аналогична Internet Explorer - мало какие сайты открываются. Поиск в Google я запустить так и не смог.
Sony Ericsson K310i
Еще более старый телефон - родом из 2006 года, так что ему уже больше 20 лет.
Размер экрана 1.8" 128x160. По нынешним временам - микроскопический, в фитнес-трекеры ставят экраны с большим разрешением, а когда-то с таких активно пользовались Интернетом...
Моя сеть тоже заработала на нем без проблем.

Родной браузер с трудом, но заработал, все его родные ссылки не открываются, но перейти на некоторые старые сайты можно:

К сожалению, родной RSS-ридер не смог ничего открыть, а я так и не смог найти ни одного rss-feed, который в нем открылся бы.
Дальше я решил попробовать разные варианты Opera.
Первой идет Opera Mini 4.1. К сожалению, работает она так себе.
Поиск Google в ней не заработал, встроенный Яндекс при попытке запустить поиск перекидывал на капчу.

[1] - внешний вид браузера
[2] - Можно почитать новости в "Каналах"
[3] - Можно зайти на главную страницу mail.ru, там тоже видны новости, но поиск не заработал
[4] - Можно искать по Википедии, она открывается нормально
Следующей идет Opera Mini 4.5, она работает получше, хотя встроенный Яндекс тут перекидывает на капчу.

[1] - внешний вид браузера
[2] - Можно почитать новости в "Breaking News", это сайт, а не RSS
[3],[4] - Google работает
А дальше - Opera Mini 5.1. Она даже поддерживала несколько вкладок. Она работает лучше, чем предыдущие версии, но GUI не очень удобен для такого мелкого экрана.

[1] - внешний вид браузера
[2],[3] - поиск в Google
[4] - удалось открыть свою статью на Хабре
Честно сказать, во многом эксперименты с трансивером я начинал именно в расчете на запуск CSD - и провалился с ним.
CSD (Circuit Switched Data) - самая старая технология передачи данных в 2G. Передача данных идет на скорости 9,6 кбит/с и очень похожа на передачу голоса (по сути, вместо голосового трафика идут данные). Соединение устанавливается через звонок на другой телефон с модемом - очень похоже на dial-up (но в CSD данные сразу идут в цифре).
Технология настолько древняя, что многие операторы сейчас ее отключают, или не предоставляют физическим лицам.
Тестировать CSD я планировал на телефоне Siemens C35i (купленном в 2002). У него есть WAP-браузер, и я надеялся проверить его работу.
Телефон без проблем увидел мою сеть, но вот CSD соединение не устанавливалось - CSD вызов даже не доходил до второго телефона. Исследование выдаваемых OsmoMSC ошибок ("We only support voice calls") и настроек телефона показало, что телефон поддерживает только Not-transparent режим CSD, а Osmocom поддерживает только Transparent - про это прямо указано здесь: "TODO/limitations - non-transparent calls (V.110 RLP, V.120)".
В режиме Transparent данные передаются в "голом" виде, а в случае "Not-transparent" - они дополнительно кодируются (добавляются коррекция ошибок, номера пакетов, что делает передачу данных надежнее). Соответственно, без доработок OsmoMSC запустить CSD на своем телефоне я не смогу.
RRLP и AML
RRLP - это протокол сигнализации, используемый в сетях GSM для обмена данными о местоположении между телефоном и центром обслуживания определения местоположения (SMLC).
Включается он правкой конфига osmo-msc.cfg
network
rrlp mode ms-based (есть еще варианты ms-preferred / ass-preferred).
Если включить этот режим, то при каждой попытке телефона совершить звонок MSC будет отправлять на него запрос APDU (RRLP Measure Position Request). Соответственно, телефон должен передать в ответ на этот запрос свои координаты (используя GPS или E-OTD (та самая триангуляция)).
Я попробовал включить этот режим и посмотреть пакеты в Wireshark (фильтр “rrlp”).
Запросы действительно отправляются, а вот в ответах я постоянно получал "gpsAssDataMissing".
Похоже, что без полноценного SMLC, способного передать GPS Assistance Data, работать это не будет.
AML - Advanced Mobile Location - метод определения местоположения абонента, совершившего вызов службы спасения по единому номеру экстренных оперативных служб (112 или 911). Когда человек совершает звонок на такой номер, операционная система телефона это видит, скрыто запускает механизмы определения местоположения и отправляет SMS с координатами на определенный номер. Насколько я понял, в России технология пока не работает, так что я проверял ее работу, имитируя британского оператора: mcc = 234 mnc = 10.
В настройках OsmoBSC нужно установить:
network
bts 0
rach emergency call allowed 1
В настройках OsmoMSC нужно установить:
msc
emergency-call route-to-msisdn MY_NUMBER --- перенаправление звонка в службу помощи на указанный номер
При таких настройках при звонке на 999 (или 112) звонок переадресуется на второй телефон, а в Wireshark при включенном фильтре "gsm_sms" действительно появляются SMS (хотя и с определённой задержкой):

Собственно, lt/lg тут - координаты телефона, pm=W - координаты определены через WIFI, rd=33 - радиус (точность определения координат), top - время сообщения, si - IMSI, ei - IMEI.
Как видно, метод вполне себе работает.
Описание процесса выглядит очень длинным, однако для упомянутого трансивера достаточно заменить файлы на флешке выложенными ниже, чтобы получить возможность использовать timestamp.
SoapyPlutoSDR придется собрать из исходников. Далее, с случае 4G все достаточно просто, хотя придется покупать программируемую SIM-карту.
С 2G несколько сложней - придется еще собирать мой вариант osmo-trx_soapy и разбираться с Osmocom.
Зачем это вообще нужно? В первую очередь, просто возможность поэкспериментировать со старыми телефонами, не думая о том, как у оператора работает EDGE/GSM. Важно то, что благодаря использованию своей БС можно полностью контролировать трафик, например, пустить его через прокси (httsp->http). Можно запустить свой сервер в локальной сети, и телефон его увидит. И я все еще надеюсь когда-то запустить CSD.
Контроль трафика интересен и для исследований безопасности - только так можно увидеть, что на самом деле телефон отсылает в сотовую сеть - это касается и данных, и скрытых SMS.
Можно обходить определенные региональные ограничения, и сымитировать нахождение устройства в другой стране - как в этой статье: Мирное применение фейковой базовой станции.
У нас в стране, говорят, планируют вводить списки разрешенных IMEI, и регистрация нового IMEI может стать платной. В таком случае своя БС тоже может пригодится для экспериментов со старыми телефонами или даже в разработке IoT устройств.
Готовые файлы для упомянутого в статье трансивера OpenSourceSDRLab PlutoSky 7020 https://disk.yandex.ru/d/FHKYtRvY0no_hQ
Софт OsmoTRX с моими доработками и конфиги для Osmocom: https://github.com/iliasam/osmo-trx_soapy