Немало воды утекло со времени публикации цикла про стриминг видео на Андроид устройствах, но вот ручки добрались и до аудио потоков. Не то, чтобы это была какая-то более заковыристая тема про сравнению с видео, даже наоборот, сложно придумать что-то проще, ибо Audio API не менялось, дай бог памяти, с 2012 года, если не раньше. И не стоило бы, ради этого пилить короткий пост, если бы не зудящая мысля — а на какое расстояние и каким образом можно передать сей аудио поток, если мы будем использовать для этого только два смартфона без всякой мобильной связи и внешних точек доступа.
Если вам интересно узнать, что из этого получилось, то прошу проследовать под кат…
Понятно, что в начале любой здравомыслящий человек посмотрит, собственно, на радиомодуль своего сотового телефона. Действительно, размер соты для уверенного приёма, может составлять несколько километров в поперечнике (у AMPS вообще до 20 км, да и даже LTE ненамного меньше). И если с одной стороны базовая станция может подать весьма мощный сигнал, который легко детектируется, то с другой стороны и сам смартфон обладая, пусть и десятками и сотнями милливатт выходной мощности, но также доносит свой голосок до станции. А если роль базовой станции сыграет другой телефон? Ясно, что расстояние будет в этом случае всё равно меньше, поскольку усилители входных сигналов на станции явно будут помощнее, чем на телефоне, но не в принципиальной же степени (тут мы сразу оговариваемся, что эксперимент проводится в местах, где нет мобильного покрытия, чтобы никому ни в коем разе не мешать).
Другое дело, что API для работы с радиомодулем в современных смартфонах, если и существует (допустим для разработки), то оно наверняка, закопано очень глубоко и совершенно недоступно для пет девелоперов. И если уж делать радиотелефон на базе сотового, то логичнее брать древние модели, по которым уже всплыла внутренняя документация, и на которых ещё можно сыскать какие-нибудь тестовые разъемы и даже к ним подпаяться для оболванивания их электронных мозгов. И таки да, буквально месяц назад на Хабре же вышла прекрасная статья по этой теме, которую я с удовольствием прочитал.
Но как и вы сами можете убедиться, процесс подъёма связи здесь весьма непростой, требует
Поэтому подивившись силе разума гиков, я обратился к тому, что у меня было уже в руках — двум смартфонам фирмы Сяоми, одному поновее, на Android 12 и второму постарше, на десятке.
Ну, блютус отпал сразу, поскольку на расстоянии на котором он стабильно работает, вы без труда можете переговариваться и без использования телефонов. Работа непосредственно с GSM модулем, тоже сами понимаете, была из разряда ненаучной фантастики. Так что оставался лишь старый добрый Wi-Fi. Идея была такова: поднимаем, значит, на одном смарте локальную сеть (или local Hotspot по английски), а вторым смартфоном к этой сети цепляемся и начинаем гнать звук соответственно от их микрофонов к противоположным динамикам двумя аудио потоками — короче устанавливаем голосовую связь! А потом шаг за шагом проверяем, насколько и на каком расстоянии эта связь устойчиво поддерживается.
Итак, задача понятна, приступаем к ее решению. Начнём естественно с аудио. Как уже упоминалось, API это весьма древнее, и если вам хочется асинхронности и колбэков для записи и приёма данных, то это не сюда. Добро пожаловать в отдельный поток и в цикл while do.
Вообще, самый простой способ записи с микрофона и последующего его воспроизведения через динамик — это использование класса MediaRecorder. Там даже с потоками не надо связываться. Просто учреждаем экземпляр MediaRecorder, определяем куда писать и из какого источника (в нашем случае это микрофон.
mAudioRecorder = new MediaRecorder();
mAudioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
mAudioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mCurrentFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
mAudioRecorder.setOutputFile(mCurrentFile.getPath());
mAudioRecorder.start();
А проигрываем соответственно через MediaPlayer():
mAudioPlayer=new MediaPlayer();
mAudioPlayer.setDataSource(mCurrentFile.getPath());
mAudioPlayer.prepare();
mAudioPlayer.start();
Тут настолько всё просто, что даже не требует пояснений. Не забудьте только оформить разрешение на использование микрофона и файлового хранилища.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Но как легко видеть, для передачи живого аудио потока эти классы подходят мало. Здесь нам пригодится другой класс — AudioRecord, который может работать непосредственно с байтовым потоком (raw data). А чтобы это аудио поток так же потом проигрывать в реальном режиме времени, сгодится класс AudioTrack.
Вообще AudioRecord может так же невозбранно писать аудиопоток в файл как и MediaRecorder, но нам такая его способность не понадобится.
Итак, пишем:
private AudioRecord audioRecord;
private int SampleRate = 8000;
private int minBufferSize;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SampleRate,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
minBufferSize*2);
Или можно по модному через билдер, получится то же самое:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
audioRecord = new AudioRecord.Builder()
.setAudioSource(MediaRecorder.AudioSource.MIC)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(SampleRate)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build())
.setBufferSizeInBytes(2 * minBufferSize)
.build();
Как мы видим, у нас там есть переменные ENCODING_PCM_16BIT, SampleRate и minBufferSize. Рассмотрим их чуть подробнее.
Для перевода аналогового потока с микрофона в цифровой вид здесь используется формат PCM — импульсно кодовая модуляция. То есть с определенной частотой дискретизации (SampleRate) уровень аудио сигнала измеряется и переводится в формат 8 или 16 двоичных разрядов (или даже в формат с плавающей запятой для особых гурманов). Какая же должна быть частота и разрядность в нашем случае? В принципе можно взять формат, хоть как для аудио CD. Там, как известно, частота дискретизации равна 44,1 кГц, а разрядность 16 бит. Но тогда наш аудио поток изрядно распухнет в размерах, даже если мы будем записывать звук в одноканальном (моно) режиме ибо:
44100 х 16 = 705 600 бит в секунду (и это не считая всяких служебных).
И именно с такой скоростью нам придется передавать их по сети. Кажется многовато.
Но опять же, мы передаем не симфоническую музыку, а просто голос. Поэтому, памятуя о ширине обычной телефонной линии в 3 кГц и теореме Котельникова, согласно которой, для восстановления сигнала данной ширины спектра, надо всего лишь удвоить частоту дискретизации, то можно ограничиться SampleRate равным 8000. Это вообще полоса в четыре килогерца — даже шире, чем нам нужно.
Можно использовать разрядность 8 бит и добиться скорости передачи потока всего лишь — 8000 x 8 = 64 000 бит в секунду, что как бы является стандартной скоростью цифровой телефонии. Поток несжатый, но нам городить лишние сущности, пока ни к чему. Правда, при уменьшении разрядности до 8 бит, появляются какие-то раздражающие фоновые шумы (хотя голос вполне разборчив), поэтому можно сильно не экономить и оставить разрядность в 16 бит, тем более, что скорость передачи данных даже 16 кбайт в секунду наш смартфон не обессилит.
Что же касается переменной minBufferSize, то это минимальный размер внутреннего буфера, при котором объект AudioRecord сможет работать.
И вычисляется он по формуле:
minBufferSize = AudioRecord.getMinBufferSize(
SampleRate, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
Формула не слишком очевидная, поэтому ради интереса, я вывел значение minBufferSize в лог. Он оказался равен 640. Наверное, байт. Во всяком случае, на фоне формирования двухбайтных отсчетов восемь тысяч раз в секунду, цифра невелика и поэтому не зря, видимо, все советуют её (то есть размер внутреннего буфера) увеличивать раз в десять. Ну, как минимум, в два.
После учреждения же всех начальных параметров, можно переходить собственно к запуску инстанса AudioRecord. Запускается он, естественно, в отдельном потоке в цикле while (true или какой-нибудь триггер).
public void run() {
try {
audioRecord.startRecording();
byte[] outData = new byte[minBufferSize];
try {
while (mIsCapturing) {
// read audio data from internal mic
audioRecord.read(outData,0, outData.length);
}
} finally {
audioRecord.stop();
}
} finally {
audioRecord.release();
}
То есть, пока переменная mIsCapturing установлена, audioRecord по готовности аудио данных, кладёт содержимое заполненного внутреннего буфера в байтовый массив outData. И нам остается лишь взять этот массив (пока он не затёрся новыми значениями) и отправить его, куда душа пожелает. Можно в байтовый поток, а потом в файл, но можно и в датаграммный пакет, который потом поедет по сети Wi Fi к нужному адресату.
Обратный процесс (из цифры в ухо), тоже больших проблем не доставляет. Главное там, задать те же параметры дискретизации, что и при записи. Здесь с байтами работает класс AudioTrack.
Правда ему нужны дополнительные параметры в лице AudioAttributes и AudioFormat поэтому писанины получается несколько больше.
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // defines the type of content being played
.setUsage(AudioAttributes.USAGE_MEDIA) // defines the purpose of why audio is being played in the app
.build();
AudioFormat audioFormat = new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT) // we plan on reading byte arrays of data, so use the corresponding encoding
.setSampleRate(SampleRate)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build();
AudioTrack audioIn = new AudioTrack(audioAttributes, audioFormat,
2*minBufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);
Дальше, так же в отдельном потоке запускаем приём байт (пока мы молчаливо предполагаем, для примера, что они до нас как-то добираются частями, допустим, по 1024 байт).
public void run() {
byte [] inSoundData = new byte[1024];
while (true) {
try
{
audioIn.write(inSoundData, 0, inSoundData.length);
}
catch (Exception e)
{
// handle exception
}
audioIn.play();
}
}
Тут, главное запускать audioIn.play() после прихода каждой порции данных. Потому как в интернете я сыскал пример кода, где автор шутник расположил его после цикла while. И нескоро я разобрался, почему вместо нормального воспроизведения файла (в начале я писал данные в файл), я слышал что-то вроде короткого "«блурп...» или вообще была полная тишина.
Итак, с записью и воспроизведением мы разобрались. Осталось лишь отправить и принять байты аудио данных по сети. Пока упростим задачу и примем, что сама локальная беспроводная сеть у нас есть, наш смартфон к ней подсоединён и имеет свой постоянный IP адрес. Можно конечно взять для демонстрации адрес 127.0.0.1, но мы не будем проявлять малодушие и действительно заставим звуки путешествовать по сети.
Работа с UDP протоколом (TCP, сами понимаете, здесь излишен) была рассмотрена в цикле статей про видео стриминг на Андроид устройствах, поэтому сильно подробно останавливаться на этой теме не будем.
В коде это выглядит так. Сначала учреждаем два UDP сокета, соответственно для отправки и получения данных. Номера портов пока у них одинаковые, поскольку мы работаем с одним устройством и шлём данные, хоть и по сети, но сами себе. Далее, соответственно запускаются два отдельных потока по приему и передаче аудио. И в этих же потоках начинают работать audioRecord.startRecording() и audioIn.play() , записывая и воспроизводя аудио поток.
Итак:
private byte inSoundData[];
DatagramSocket udpSocketOut;
DatagramSocket udpSocketIn;
String ip_address = "IP адрес устройства в локальной сети";
InetAddress address;
int portOut = 40000;// выход для пакетов со звуком/40000
int portIn = 40000;// вход для пакетов со звуком// 40000
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsCapturing = true;
try {
udpSocketOut = new DatagramSocket();
udpSocketIn = new DatagramSocket(portIn);
Log.i(LOG_TAG, " создали udp каналы");
} catch (Exception e) {
Log.i(LOG_TAG, " Не создали udp каналы " + e);
}
new AudioSend();// для отправки звука по UDP
new Udp_recipient();// запускаем прием UDP пакетов
try {
address = InetAddress.getByName(ip_address);
Log.i(LOG_TAG, " есть адрес");
} catch (Exception e) {
Log.i(LOG_TAG, " нет адреса " + e);
}
}
private class Udp_recipient extends Thread {
Udp_recipient() {
Log.i(LOG_TAG, "запустили прием данных по udp");
start();
}
public void run() {
while (true) {
try {
byte buffer[] = new byte[32768];
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
udpSocketIn.receive(p);
byte bBuffer[] = p.getData();
inSoundData = new byte[p.getLength()];
synchronized (inSoundData) {
for (int i = 0; i < inSoundData.length; i++) {
inSoundData[i] = bBuffer[i];
}
}
}
catch (Exception e) {
Log.i(LOG_TAG, e + "hggh ");
}
try
{
audioIn.write(inSoundData, 0, inSoundData.length);
}
catch (Exception e)
{
// handle exception
}
audioIn.play();
}
}
}
public class AudioSend extends Thread {
AudioSend() {
Log.i(LOG_TAG, "запустили отправку данных по udp");
start();
}
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO)
try {
Log.i(LOG_TAG, "запустили отправку ");
audioRecord.startRecording();
byte[] outData = new byte[minBufferSize];
try {
while (mIsCapturing) {
// read audio data from internal mic
audioRecord.read(outData,0, outData.length);// получили в байтовый массив порцию звука
// set audio data to UDP сокет
try{
// Log.i(LOG_TAG, " outDate.length : " + outDate.length);
DatagramPacket packet = new DatagramPacket(outData, outData.length, address, portOut);
udpSocketOut.send(packet);
}
catch (Exception e)
{
Log.i(LOG_TAG, "не отправили UDP пакет");
}
}
} finally {
audioRecord.stop();
}
} finally {
audioRecord.release();
Log.i(LOG_TAG, "остановили tred");
}
}
}
Не забудьте только добавить соответствующие разрешения на работу с сетью в манифесте и в самом приложении. И после можете наслаждаться всеми звуковыми эффектами, эхом, реверберацией и положительной обратной связью переходящей в противный свист. Но ежели, подключить наушники, неважно блютус или проводные, то можно более комфортно слушать свою речь. Ещё удивила чувствительность сяомиевского микрофона: стоишь в беспроводных наушниках в 10 метрах от телефона, говоришь еле слышным шепотом, в противоположную сторону, а из наушников всё отчетливо слышно.
В принципе, для телефонизации поселка и походного стана (где нет мобильного покрытия) этого достаточно. Устанавливаем в сельсовете или шатре предводителя мощный роутер с внешней антенной, а вокруг в избах или палатках дружины размещаем маршрутизаторы попроще (как репитеры). Подключаетесь к сетке главного роутера и можете бесплатно разговаривать по смартфонам хоть сутками. Там надо лишь допилить регистрацию абонентов и опцию выбора кому звонить.
Но поскольку я хотел от лишнего звена, роутера избавиться, то пришлось двигаться дальше, дабы сконфигурировать и запустить беспроводную локалку непосредственно у себя на смартфоне.
Для этой цели современный Android, вроде как, предлагает прекрасное современное (на колбэках!) технологическое решение local-only hotspot, чтобы приложения могли соединяться с друг другом по Wi-Fi для обмена данными. И даже заботливо предупреждает, что мол, сеть будет только внутренняя, а в Сеть на ней не выйдешь. Ну, нам в принципе, этого не требуется. Инициализация же этого дела выглядит примерно так:
wifiManager = (WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
@Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
Log.d(LOG_TAG, "Wifi Hotspot is on now");
mReservation = reservation;
currentConfig = mReservation.getWifiConfiguration();
Log.d(LOG_TAG, "THE PASSWORD IS: "
+ currentConfig.preSharedKey
+ " \n SSID is : "
+ currentConfig.SSID);
После чего вы получаете сетку с уже предустановленными названием (Android плюс чего-то там) и паролем. Нет, ну ладно сеть можно найти по слову Андроид в названии, а вот как бедному приложению пытающемуся к ней присоединиться, получить пароль??? Может можно сконфигурировать свою сеть с нужными названием и паролем? Смотрим страницу SoftApConfiguration (на официальном сайте!), и находим там ссылку SoftApConfiguration.Builder. И видим: «404 | страница не найдена».
Самое интересное, что в интернете примеры работы с этим билдером есть. Есть он и в доках Андроида. А в Android Studio — нет. Я и студию обновил, после чего текущие проекты жестко забаговались и слетели некоторые настройки (как вам такое: чтобы исправить внезапно возникшие ошибки Gradle, надо кое-где в файле сборки вставить слово version. Stack overflow не даст соврать); и поставил версию SDK API 33 Tiramisu, но все было тщетно, SoftApConfiguration.Builder не воспринимался системой никак.
После этого я стал примерно понимать ощущения безвестного участника Stack Overflow, который задал вопрос, а почему мол, эта новая сеть hidden, то есть скрыта. На что бездушный бот ответил ему, что он такие общие вопросы не понимает, пишите дескать пример кода. А я его понял, LocalOnlyHotspot вроде есть, и всё для его конфигурации имеется, а на практике — его нет.
Чуть позже на этом же форуме я нашел что:
- Только системные приложения могут конфигурировать LocalHotspot
- setSoftApConfiguration() это защищенное/ скрытое API, которым вы можете пользоваться только, если у вас кастомный SDK или SDK от третьих лиц, но всё равно в приложении оно работать не будет, если приложение не системное.
То есть копеечный роутер на линуксе сеть поднимает, а пет разработчик на Aндроиде видит вместо этого от Андроид разработчиков шиш. Что там такого в этой беспроводной локалке страшного и ужасного, что энтузиастам палки в колеса суют?
Но может быть, я неправ, и они наоборот вместо копания в IP адресах, просто предоставляют гикам продвинутые инструменты более высокого уровня? Вот например есть же Wi-Fi Direct и еще Wi-Fi Aware. Может надо пользоваться именно ими?
Действительно, на официальной странице прямо так и указано: Wi-Fi Direct (P2P) позволяет устройствам с соответствующим аппаратным обеспечением устанавливать друг с другом связь по Wi Fi без промежуточной точки доступа. А в обсуждениях всё того же Stack Overflow, народ также писал, что Wi-Fi Direct работает вместе с беспроводной сетью (то есть без потери основного Wi Fi соединения) и даже не требует ее отключения. Это, мол, огромное достижение.
Обнадёженный этой информацией, я начал знакомиться Wi-Fi Direct поближе. На первый взгляд всё было прекрасно. Можно сказать, вместо ассемблера работаешь с современным языком высокого уровня: подписчики, чаты, сообщения, группы, публикации и т.д.
В соответствии с руководством я успешно создал экземпляр WifiP2pManager и даже зарегистрировался в Wi-Fi P2P фреймворке вызвав метод initialize(). Но тут, я нечаянно выключил тот самый основной Wi Fi, короче отсоединился от своей домашней сети.
И всё! Тут же пришел интент, что Wi-Fi P2P недоступен!
Как оказалось, мне показалась, что всё это всего лишь обёртка над тривиальным Wi Fi. То есть, в принципе, правильно написали, что Wi-Fi Direct не мешает работе основному Wi Fi соединению. Просто забыли написать, что без основного Wi Fi соединения, Wi-Fi Direct в принципе работать не может.
Но это была ложная тревога. Просто при выходе из домашней локалки я отключил и Wi Fi модуль смартфона. А для Wi-Fi Direct он должен работать, не важно, подключен он при этом к какой-либо сети или нет. Правда, это я уяснил через пару дней, в течении которых я пытался сконфигурировать и поднять беспроводную локалку — дедовскими способами через методы Reflection API.
Это выглядит примерно так, сначала конфигурируем сеть:
WifiConfiguration wcfg = new WifiConfiguration();
wcfg.SSID = " SSID NAME";
wcfg.networkId = Int;
wcfg.preSharedKey = "password";
wcfg.hiddenSSID = false;
wcfg.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wcfg.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wcfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wcfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wcfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
А потом делаем финт ушами:
Method method = mWifiManager.getClass().getMethod("setWifiApConfiguration",
wcfg.getClass());
method.invoke(mWifiManager, wcfg);
И всё, сеть сконфигурирована. И даже можно получить ее параметры стандартными get-методами
Но современные (Android 12) и относительно современные (Android 10) смартфоны эти reflection методы не видят в упор, и код взлетел лишь на стареньком Самсунге с шестым Андроидом.
Но радовался я недолго, так как сеть мало сконфигурировать, её надо ещё и включить!
На это есть метод:
Method method1 = mWifiManager.getClass().getMethod("setWifiApEnabled",
WifiConfiguration.class, boolean.class);
method1.invoke(mWifiManager, wcfg, true);
Но он выдал исключение, которое привело меня к разрешению CONNECTIVITY_INTERNAL, которое, как оказалось, дается только системным приложениям.
Рутовать для этого даже старый телефон мне было лень и дальнейший поиск, так сказать, на этом остановился.
Я уже собирался вернуться к Wi-Fi Direct, но поскольку сокеты там пишутся немного по другому и вообще другая идеология, то вылетал весь код написаный для обычной Wi Fi сети и надо было писать новый. Поэтому радиотелефон на Wi Fi Direct я отложил для нового поста на Хабре, а затем здраво рассудил, что коль для инициализации своей сетки, надо сделать приложение системным, то почему бы не воспользоваться уже действующим системным приложением, которое работает, наверное, на всех современных телефонах. Это — Точка Доступа. Там, правда, надо на нее нажать пальцем, потом вбить свое название и пароль, что конечно, не комильфо. Но для проверки принципа и такой подход сойдёт.
Далее, со второго смарта надо подсоединиться к этой точке доступа (тоже ручками), и уже в параметрах мы увидим там и IP адрес шлюза (это первый смартофон) и свой IP адрес в этой сети. Теперь, если указать эти адреса в текстах программ, то радиотелефон у нас уже, можно сказать, заработал!
Теперь наступила пора полевых исследований. В следующую субботу, поколотив друг друга палками на тренировке по исторической реконструкции, я взял напарника с собой на тихую питерскую улицу и поведал ему свою тайну. Давно не видывал я такого детского восторга в глазах взрослого небритого мужика. Да? Рация на телефоне? Так это ж можно…
Восторги правда поутихли, когда наступили собственно практические испытания. Итог — зона уверенного приема в городе сто метров. На ста десяти/двадцати просто теряется сеть, а с ней, сами понимаете, и возможность живого общения. Это если на частоте 2,4 ГГц. На 5 ГГц ещё меньше, даже уже точно не считали.
На этом, в принципе, исследования завершились. Ради интереса можно ещё попробовать запустить связь на Wi Fi Direct, но скорее всего на дальность это не повлияет, так как всё зависит от мощности и чувствительности Wi Fi модуля, а у нас к нему доступа нет.
Комментарии (65)
shpon
00.00.0000 00:00+5В моём древнем смартфоне есть радиомодуль на 400+/-МГц. Можно с таксистами общаться. Вообще, интересная штука.
iShrimp
00.00.0000 00:00Китаец типа Blackview? Как обстоят дела с качеством и дальностью связи?
shpon
00.00.0000 00:00+3Китаец Runbo X5. По городу в прямой видимости около 4км на связи с мини мотороллой и около 400 метров в городской застройке с baofeng uv5r.
А еще там чат есть, общался с приятелем у которого тоже runbo, но в формате планшета.
okhsunrog
00.00.0000 00:00+1Думаю, будет интересно взглянуть https://meshtastic.org
Правда, там обмен текстовыми сообщениями, но дальность гораздо большеTarson Автор
00.00.0000 00:00+1Лору на смарте не видел, и там расстояние за счёт ретрансляции в пределах группы.
Renatsun
00.00.0000 00:00+1Вот зря наверное Bluetooth отбросили. Интересно было бы посмотреть, как далеко работает. Есть же спецификация Long Range обещает 400 метров и больше. Но не все телефоны поддерживают. List of smartphones that support Bluetooth 5 Long Range - Tracmo Help Center (mytracmo.com)
Tarson Автор
00.00.0000 00:00+3Так я в наушниках блютус в другую комнату зашёл и всё, дальность кончилась... Поэтому пришлось отбросить.
Stanislavvv
00.00.0000 00:00Оба девайса должны уметь long range, насколько я понимаю. Наушниками LR нах не сдался, там bt чисто чтоб проводами не привязываться.
Tarson Автор
00.00.0000 00:00Это надо спонсора найтить, чтобы обеспечил парой таких смартов. А то без них то никак не проверить:(
Stalker_RED
00.00.0000 00:00А я в блютус-наушниках без проблем отхожу от смартфона метров на 25-30, если стен не много.
elfukado
00.00.0000 00:00Какая модель смартфона, наушников и окружающая обстановка?
У меня есть poco f3 и jbl 115. Может они и больше 10 метров потянут, но когда мне нужна связь или музыка - я не буду наворачивать круги вокруг смартфона и считать стены - это уже не нормальное использование, а эксперимент.
strvv
00.00.0000 00:00Самые дешёвые из закрытых sg8, классические наушники с амбюшурами.
В 80+ кв.м. квартире по диагонали, через 3-4 стены работают, хотя вблизи бывают сильные затыки с взаимным расположением. А это метров 25 (на две стороны дома квартира, телефон на балконе и я по всей квартире.
dmitryrf
00.00.0000 00:00Пропускной способности LR не хватит на голос.
saege5b
00.00.0000 00:00Как жать... В принципе, сейчас для голоса канала в 10кбс с запасом хватает.
Но, не каждый современный смарт сможет такое пожать и расжать.
event1
00.00.0000 00:00В интернете обещают 125-500 килобит. Можно 4 голоса уместить
vorphalack
00.00.0000 00:00в цифровой радиосвязи используются кодеки с полосой 2400-4400бит/с до FEC, с такой полосой там каналов 20 можно запихнуть вместе с сигналлингом.
event1
00.00.0000 00:00+2Ну ладно вам. Если уж есть широкая полоса, давайте какой-нибудь телефонный кодек туда пихнём хороший с блекджеком и шлюхами. Зачем людей мучить.
vorphalack
00.00.0000 00:00ну можно и телефонный — там всё равно полоса копеечная, но смысл в том, что чем уже кодек — тем проще насыпать поверх него FEC, и если в этом LRBT есть режим работы «йа UDPшко» — то это будет иметь бОльшую данность, чем если там нужно поддерживать полноценный линк с обработкой ошибок. ну или банально слать по 3-4 пакета подряд хотя бы
event1
00.00.0000 00:00если в этом LRBT есть режим работы «йа UDPшко»
Судя по описанию, указанный режим — это вариант PHY. Через это, видимо, там работают стандартные профили БТ. А среди них совершенно случайно уже есть стандартный профиль именно для таких целей. Правда, он похоже не поддерживается ни в андроиде, ни в блюзе. Что навевает грустные мысли.
vorphalack
00.00.0000 00:00+2судя по гуглу — БЫЛ, на bluetooth.com спека значится как отозванная, к сожалению.
EDIT: наткнулся таки на описание, оно звезда, а не p2p
aectaan
00.00.0000 00:00Опусом пожать и вперёд. Стримил стереопоток с микроконтроллера на ноут. Правда не на LR, а на мегабите. Хватало метров на 50 + 2 стены
andry67
00.00.0000 00:00Long Range ...
Немного погуглил - так и не нашел ни одной програмки под андроид, которая задействует эту фичу.
Tarson Автор
00.00.0000 00:00Сейчас настраиваю WI FI direct, зацепили строчки из официала андроида
Using these APIs, you can discover and connect to other devices when each device supports Wi-Fi P2P, then communicate over a speedy connection across distances much longer than a Bluetooth connection
Чего это они вдруг сами решили вдруг, что дальше?
Sagittarius67
00.00.0000 00:00+5поскольку усилители входных сигналов на станции явно будут помощнее, чем на телефоне, но не в принципиальной же степени
Как известно, самый лучший усилитель - это антенна. На базовой станции стоят антенны с усилением в районе 17 dBi. А если учесть ещё то, что приём ведётся на две антенны (receive diversity), что даёт ещё +3 dB, то общее усиление по приёму на базовой станции будет не менее 20 dBi. Эффект в мощности сами можете посчитать.
Javian
00.00.0000 00:00+1В принципе, для телефонизации поселка и походного стана (где нет мобильного покрытия) этого достаточно. Устанавливаем в сельсовете или шатре предводителя мощный роутер с внешней антенной
Интересно бы проверить дальность при сети на точке доступа TP-Link WA5210 перешитой в аналог Ubiquiti NanoStation 2.
zuek
00.00.0000 00:00+1Странно, что не посетила мысль поднять "точку доступа" на ноутбуке/малинке/смартфоне с запущенным Asterisk, а на смартфонах установить какой-нибудь softphone - сразу и вопрос "адресной телефонизации палаточного городка" решается... правда, и собственных изысканий становится совсем скромнёхонько...
MaFrance351
00.00.0000 00:00+3Интересная статья!
К слову говоря, встречал и промышленную реализацию данной идеи, в ТСД Symbol (позже Motorola, ещё позже Zebra), была функция PTT, позволявшая переговариваться между двумя ТСД в одной сети.
(И огромное спасибо, что мою статью упомянули!)
Tomasina
00.00.0000 00:00А вне города не замеряли зону приёма? Ибо решение для вылазок на природу небольшими группами - самое то.
Dr_Faksov
00.00.0000 00:00+1О том же подумал. У WiFi в отсутствие помех заявленная дальность вроде метров 500.
А еще интересно текст переслать. Пусть медленно, но максимально далеко.
Статья классная, поставил плюсы везде, где можно!
Tarson Автор
00.00.0000 00:00+1спасибо, нет, за город пока холодно... к теплу, наверное, с Wi Fi Direct доделаю, тоже проверю.
inkelyad
00.00.0000 00:00Что-то мне кажется, что, увы, частоты радио интерфейсов, что есть в смартфоне - не подходят ни для какой природы, кроме 'голая степь'. Интереснее было бы понять, какое дополнительное оборудование из доступного и удобного в использовании можно к смартфону прицепить, чтобы было 'со всеми удобствами'.
Я подозреваю - что тупо рацию из ближайшего магазина через разъем для гарнитуры.
mikelavr
00.00.0000 00:00+5Для любого радиоканала известно, что чем выше скорость - тем меньше дальность (при прочих равных условиях). Все стандарты WiFi имеют варианты по скорости (переключаемые автоматически в зависимости от уровня сигнала). При этом высокая скорость в вашем кейсе вообще не нужна, одного мегабита хватит. А вот переключения мешают.
Продвинутые (провайдерские) точки доступа имеют возможность ограничения скорости, и вообще установки конкретного варианта модуляции. Хорошо бы покопаться в потрохах андроида - если удастся ограничить скорость WiFi, дальность и надежность связи заметно вырастет.
Например, для 802.11g: Data rates with varying modulation types: 6, 9, 12, 18, 24, 36, 48 and 54 Mbps; can revert to 1, 2, 5.5, and 11 Mbps using DSSS and CCK.
802.11n: Data rates with varying modulation types: 1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54 MbpsОчевидно, что в ваших целях 1 мегабит предпочтительнее 54.
Didimus
00.00.0000 00:00+1Раньше в телефонах производители пытались занести функцию уоки-токи, но, видимо, под давлением опсосов эту функцию убили
iamkisly
00.00.0000 00:00+1Речь о нокийной Push-to-Talk? Там не все так просто.. емнип технология работала через базовую станцию, но оба абонента должны быть в зоне обслуживания одной и той же bts (может быть даже одной cell, не помню) чтобы базовая станция выступала только ретранслятором. И вот тут начинались сложности, потому что PTT фича не стандартизованная, и кажется работало это только на базовых станциях nsn, к тому же по пакетке (условно через gprs).. а пакетная связь в gsm по умолчанию всегда выделяласm по остаточному принципу. Тоесть, большинство таймслотов всегда заняты голосом, а поставить в сектор еще один trx и зарезервировать слоты под пакетку это дорого. Не знаю можно ли было прикрутить туда билинг, возможно да.. но вероятно проблема была еще и в качестве связи. Тоесть попробуй обьясни абоненту почему у него булькает все в трубке, он скорее уйдет к другому оператору.
Didimus
00.00.0000 00:00И это было. А ещё раньше моторолла или Алкатель это хотели продвигать, вроде. Странно, что сейчас в защищённые телефоны это не вставляют, настоящую рацию
Dr_Faksov
00.00.0000 00:00+1Ну как-бы железо приличной радиостанции занимает приличное место. И прилично кушает батарею. Ну и нужна приличная антенна. Это физика.
А еще есть законы. В США нужна лицензия даже на даже на бытовую радиостанцию. Формальность, заплати и получи, но нужна. В Канаде не нужна. Но и там и там если у вас нет позывного, то вы можете использовать только радиостанции попадающие под описание "бытовые". Один из параметров такой станции - отсутствие клавиатуры.
Didimus
00.00.0000 00:00+1А вы видели китайские телефонги? Там и антенна, и телевизор, и аккумулятор как в авто
Sagittarius67
00.00.0000 00:00У Моторолы были телефоны с кнопкой PTT и соответствующим софтом. Была и PTT платформа. Считалось, что это будет киллер фича для GPRS. Эта штука, в частности, тестировалась для таксистов в Москве на базе МТС.
Didimus
00.00.0000 00:00И чем закончилось?
Sagittarius67
00.00.0000 00:00Это было миллиард лет тому назад, не помню уже точно. Работало, вроде, нормально, но была довольно большая задержка после нажатия кнопки PTT. Ну и телефон нужно было специальный покупать. Тогда же МТС закупил платформу для i-mode у японцев и похорнил потом всё это.
Devadas
00.00.0000 00:00Вот что-то никак у меня не сходится, не могу понять... и даже в этой статье - цифры в начале статьи с результатом в ее конце... искренне хочу разобраться.
размер соты для уверенного приёма, может составлять несколько километров в поперечнике (у AMPS вообще до 20 км, да и даже LTE ненамного меньше). ... и сам смартфон обладая, пусть и десятками и сотнями милливатт выходной мощности, но также доносит свой голосок до станции.
Как смартфон со своей крошечной мощностью добивает до БС через городскую застройку на 20-30 км и всего лишь сто метров до своего сородича (как в этом опыте)?
Tarson Автор
00.00.0000 00:00Так там вроде написали, антенны, антенны на БС. А у Fi Wi они махонькие... А так да: Согласно требованиям к системе LTE, при радиусе соты в 5 км, все требования к спектральной эффективности, пропускной способности и работы с мобильными абонентами должны поддерживаться. При радиусе соты в 30 км допускается ухудшение в показателях производительности.
Tomasina
00.00.0000 00:00+34-10 км в поле, 1-3 км в городе - это пределы для телефончика.
А красивые цифры в спецификациях - традиционно для идеальных условий (отсутсвие помех, радионепрозрачных преград и т.п.).
phosfin
00.00.0000 00:00так gsm мобилы до 5 Вт может мощность поднять, + относительная открытость высокой антенны.
moviq
00.00.0000 00:00через городскую застройку на 20-30 км
наверное это приводилось для открытой местности. Поправьте, у 2G же тоже по стандарту порядка20...30км
begin_end
00.00.0000 00:00+4Дело даже не в размерах антенн непосредственно: узкая направленность антенн специальной конструкции и даваемое при этом усиление (улучшение соотношения сигнал/шум) — вот это работает и достигается еще и размещением антенн. Если обратить внимание, антенны сотовых операторов обычно почти всегда оптически видимы, прикрыты максимум 1-2 препятствиями. Даже если где-то получилась сильная тень для ближайшей соты — соседняя сота помогает.
Смартфон же имеет принципиально всенаправленные антенны, чтобы работать в любом положении. Но мало того, при локальном использовании смартфон-смартфон, как в статье, они оба находятся на «уровне земли», в руках. А еще Wi-Fi сильно зашумлен в городе.
Ну и как выше сказали, надо Data rate минимальный ставить и в 802.11b стандарте работать.
Я проводил ряд экспериментов коннектов со смартфона к точке доступа (старая-добрая DWL-G2100, настроенная в 150мВт, rate 0.25Mbps, b):
• точка доступа с дипольной антенной на высоте 30м, телефон на уровне земли — пока есть прямая видимость, связь доступна на расстоянии 1..1,5км;
без прямой видимости (1 препятствие в виде дерева или человека) — 100..200м;
• точка доступа с рупорной антенной (укороченный рупор с вертикальной поляризацией) на высоте 30м, телефон на уровне земли (на линии от рупора) — пока есть прямая видимость, связь доступна на расстоянии до 3км; для максимально дальней связи нужно учитывать отсутствие препятствий не только на оптическом маршруте, но и по бокам, в зоне Френеля;
без прямой видимости (1 препятствие в виде дерева или человека) — 100..500м;
• то же самое, когда и точка доступа на уровне земли: стабильная связь 20..50м (1 и более препятствие в виде дерева или человека), далее как повезет, фрагментарно до 200..300м максимум; рупорная антенна незначительно увеличивает вероятность успешной связи и кстати, добавочный 5W усилитель на точку доступа тоже неэффективен (весьма логично).
Еще одно существенное замечание: тесты были на старом смартфоне GT-I9300. На более современном Galaxy A70 дальность связи заметно ниже — чувствительность радиотракта Wi-Fi по программам-измерялкам на 6..9db хуже, чем у старой модели. Думаю, с учетом перенасыщенности Wi-Fi производители специально загрубляют чувствительность, оптимизируя исключительно под комнатное использование.
Резюмируя: высота размещения, открытость и направленность антенн — главные факторы для дальности связи.
penetration
00.00.0000 00:00-1Эхх, таких вайфай говорилок полно, интерес вызвал бы именно передача сообщений по операторской линии на несколько километров, с помощью рут конечно.
Вообще давно искал такие приложения, ещё звонилку, которая умеет шифровать голос при разговоре, не нашёл, а тема то важная.Didimus
00.00.0000 00:00Зачем оператор? Один телефон как базовая станция работает
penetration
00.00.0000 00:00Да хоть так, главное чтобы расстояние было приличная и была реализация на андроиде, но как я сказал, таких нет
LevKoshkin
00.00.0000 00:00А можно готовый .apk? Давно искал подобное Р2Р приложение для связи во время велопокатушек, готовые приложения из маркета с избыточным функционалом и регистрацией.
Tarson Автор
00.00.0000 00:00Нет, в этом смысле пока всё очень сырое. Вплоть до ввода данных, всё руками в тексте программы. Интерфейса пользователя нет, все экранные кнопки с левого проекта. Вам такое однозначно не подойдет. Есть планы сделать нормальное приложение, но пока это только планы.
YegorP
Какова задержка от микрофона одного телефона до динамика другого?
Tarson Автор
Сотни миллисекунд. Копейки не считал, но когда говоришь "привет" слышно без наложения
event1
Зависит от
minBufferSize
, который автор довольно легкомысленно увеличил в 10 раз. Хорошая задержка должна быть, до 50 мс. 16 кбайт/с = 16 байт/мс. Соответственно, делать буфер больше 800 байт — опрометчиво.Tarson Автор
Не, там предлагали для записи так делать, чтоб не дёргать часто буфер. А у меня он в 2 раза больше минимального.
YegorP
Всё сложнее: https://source.android.com/docs/core/audio/latency/app