...

суббота, 24 июля 2021 г.

Учёные впервые описали внутреннюю структуру Марса на основе сейсмических данных


Представление художника о внутренней структуре Марса

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

Используемые данные были собраны за первую половину всего времени, которое спускаемый аппарат проработал на Марсе. InSight спустиля на планету в 2018 году и начал вести записи марсотрясений в феврале 2019 года. При этом его миссию уже продлили до 2022 года. НАСА даже выложило на SoundCloud звукозаписи этой сейсмической активности.
По словам Симона Сталера, ведущего автора одного из исследований, сейсмолога из Швейцарского геофизического института, значительную часть данных можно было бы получить и с орбиты, однако спускаемый аппарат помог подтвердить достоверность сведений.

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

В работе, посвящённой ядру, указано, что, измерив время отражения сейсмических волн от марсианского ядра, удалось установить, что оно находится на глубине в 1559 км – глубже, чем ожидалось. По сравнению с Землёй, плотность ядра гораздо ниже – от 5,7 до 6,3 г/см3. Вероятно, его железо-никелевый сплав богат лёгкими элементами типа серы. А по силе отражённых волн можно сделать вывод, что и у Марса ядро жидкое.

Также исследователи установили, что мантия у Марса относительно тонкая, вероятно из-за отсутствия бриджманитового (силикат-перовскитного) слоя, как у Земли. Из-за отсутствия такого изолирующего слоя планета вскоре после появления могла быстро охладиться и приобрести магнитное поле: судя по данным магнетометра InSight, намагниченность планеты в 10 раз больше, чем показывают расчёты на основе данных с орбитального модуля. Вероятно, на заре развития магнитное поле Марса было таким же сильным, как у Земли.

Скорость волн позволяет предположить, что толщина литосферы планеты составляет порядка 500 км, а в том месте, где сел InSight, кора Марса либо имеет два слоя и толщину в 20 км, либо три слоя и толщину в 32 км.

Делать выводы о составе планеты помогает то, что в её недрах проходят волны двух разных типов P-волны (продольные) и S-волны (поперечные). Их разные свойства (к примеру, S-волны не проходят через жидкость, в том числе, и через жидкое ядро) и скорость распространения помогают получать более полную картину происходящего.


S-волны от источника марсотрясения (красный кружок) проходят через литосферу Марса, отражаются от ядра и попадают в датчик InSight (белый кружок)

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

Конечно, у аппарата были свои ограничения. Это пока единственный сейсмометр на Марсе, поэтому его возможности по измерению сейсмических волн, распространяющихся по всей планете, далеко не полны. Из-за этого, например, сложно измерять расстояние до точки их происхождения и разбираться в том, что послужило их источником. Сильные ветра и низкая температура также ограничивают возможности InSight по круглогодичному измерению волн.

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

Adblock test (Why?)

[Перевод] Как писать на C# аккуратно: память и производительность

К старту курса о разработке на C# делимся переводом статьи о типичных ошибках программирования на C# от Кристофа Насарре — технического рецензента книг Microsoft Press в целом и, конечно, книги CLR via C# в частности. Кроме того, Кристоф Насарре — один из авторов книги Windows via C++.


Разработчики не контролируют работу сборщика мусора (далее — GC — Garbage Collector), но, чем меньше выполняется выделений памяти, тем меньше GC влияет на приложение, поэтому главная цель — избежать выделяющего ненужные объекты кода или кода, который ссылается на них слишком долго. Помогая командам Criteo очистить код, я собрал и задокументировал несколько антипаттернов C#, подобных паттернам из публикации Кевина о коде с неприятным запахом. Вот выдержка о хороших и плохих моделях памяти.

Финализация и IDisposable

Начнём со внедрения «финализатора» — скрытого способа ссылаться на объект. Пишется метод, имя которого — это имя класса с пре́фиксом ~. Компилятор генерирует переопределение виртуального метода Object.Finalize, а экземпляр типа с таким методом обрабатывается GC особым образом:

  • После выделения памяти ссылка сохраняется во внутренней очереди Finalization.

  • Если на Finalization больше не ссылаются, эта ссылка перемещается в другую внутреннюю очередь — fReachable — и рассматривается как корневая, пока выделенный поток не вызовет код её финализатора.

Конрад Кокоса в одном из своих бесплатных видео о GC изнутри подробно рассказывает, что экземпляры типа, где реализован финализатор, остаются в памяти намного дольше необходимого, ожидая следующей сборки мусора поколения, где экземпляр оставила предыдущая сборка мусора, т. е. gen1, если он находился в gen0, или gen2, если в gen1, что ещё хуже. Поэтому первый вопрос часто касается того, действительно ли необходим финализатор. В большинстве случаев ответ должен быть отрицательным. Финализатор очищает только неуправляемые ресурсы, то есть обычно полученное от вызовов нативных функций взаимодействия с COM или P/Invoke «нечто»: дескрипторы, нативную или выделенную через Marshal-хелперы память.

Если в классе есть поля IntPtr, это хороший знак того, что время их жизни заканчивается в финализаторе через хелперы Marshal или очистки после вызова P/Invoke. Ищите наследованный от SafeHandle класс, если вам нужно манипулировать дескрипторами объектов ядра вместо необработанных IntPtr, избегайте финализаторов. Итак, в 99,9% случаев финализатор не нужен.

Второй вопрос: как реализация финализатора связана с реализацией IDisposable? В отличие от финализатора реализация уникального метода Dispose() интерфейса IDisposable для сборщика мусора ничего не значит, то есть время жизни не продлевается никаким побочным эффектом. Dispose позволяет пользователям экземпляров класса не ждать сборку мусора, вместо этого в определённый момент явно очистив память от экземпляра.

Пример: при записи в файл за кулисами .NET вызывает нативные API, работающие с реальным файлом через дескрипторы объектов ядра в Windows с ограниченным одновременным доступом: два процесса не повредят файл одновременной записью разных байтов. Это взгляд на ситуацию с высоты птичьего полёта, однако конкретно в этом обсуждении он допустим.

Другой класс позволяет получить доступ к базам данных через ограниченное количество соединений, освобождать их нужно как можно скорее. Во всех этих ситуациях как пользователь этих классов вы хотите «освобождать» ресурсы за кулисами как можно быстрее. Это переводится в хорошо известный паттерн using:

using (var disposableInstance = new MyDisposable())
{
   DoSomething(disposableInstance);
}; // the instance will be cleanup and its resources released

Компилятор преобразует код выше в такой:

var disposableInstance = new MyDisposable();
try
{
   DoSomething(disposableInstance);
}
finally
{
   disposableInstance?.Dispose();
}

Когда нужен IDisposable? Мой ответ прост: он нужен, когда класс владеет полями реализующих IDisposable классов и если он реализует финализатор (причина объяснялась выше). Не используйте IDisposable.Dispose по другим причинам, например при логировании, как это делалось в деструкторе C++. Предпочтите для этого реализовать другой явный интерфейс.

Что касается реализации, я не понимаю, почему код в документации Microsoft настолько сложный. Чтобы «освободить» неуправляемые или управляемые ресурсы, нужно реализовать метод ниже. Вызываться он должен как финализатором, так и IDisposable.Dispose():

var disposableInstance = new MyDisposable();
try
{
   DoSomething(disposableInstance);
}
finally
{
   disposableInstance?.Dispose();
}

Чтобы без проблем вызывать IDisposable.Dispose() несколько раз, необходимо поле _disposed. Если _disposed равно true, чтобы поймать использование утилизированных объектов, не забудьте во всех методах и свойствах класса бросить исключение ObjectDisposedException.

Спросите нескольких разработчиков, когда disposing должно быть true или false: не считая сомневающихся, половина скажет, что при вызове из финализатора, а другая половина — из Dispose. Зачем давать аналогичное имя методу, который уже есть в IDisposable? Почему параметр называется disposing? Я думаю, избыток Dispose убивает паттерн и проблема решается проще. Вот моя версия:

class DisposableMe : IDisposable
{
    private bool _disposed = false;

    // 1. field that implements IDisposable
    // 2. field that stores "native resource" (ex: IntPtr)

    ~DisposableMe()
    {
        Cleanup("called from GC" != null);
    }           // = true

    public void Dispose()
    {
        Cleanup("not from GC" == null);
    }           // = false
    
    ...
}

Я также переименовал Dispose(bool disposing) в Cleanup(bool fromGC):

 private void Cleanup(bool fromGC)
 {
     if (_disposed)
         return;

     try
     {
         // always clean up the NATIVE resources
         if (fromGC)
             return;

         // clean up managed resources ONLY if not called from GC
     }
     finally
     {
         _disposed = true;

         if (!fromGC)
             GC.SuppressFinalize(this);
     }
 }

Правила просты. Запомните их:

  • Нативные ресурсы, т. е. поля IntPtr, должны очищаться всегда.

  • Управляемые ресурсы, т. е. поля IDisposable, должны утилизироваться при вызове из Dispose, но не из GC.

Логическое поле _disposed используется для очистки ресурсов только один раз. Здесь оно установлено в true, даже когда брошено исключение, поскольку я предполагаю, что, если исключение произошло, оно произойдёт и позднее. И последняя важная деталь: вызов GC.SuppressFinalize(this) просто сообщает GC, что ему нужно удалить утилизируемый объект из Finalization:

  • Вызывать его имеет смысл исключительно из Dispose (не из GC), чтобы не продлевать время жизни объекта.

  • Это означает, что финализатор не вызывается, иначе он вызовет Cleanup, который немедленно перейдёт к возврату, поскольку _disposed истинно.

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

Увеличивайте вместимость списка, когда это возможно

Создавая List или экземпляр коллекции, дайте ему запас вместимости. Реализация классов списков и коллекций в .NET обычно хранит значения в массиве, размер которого необходимо изменять при добавлении новых элементов; это означает, что:

  1. Выделяется новый массив.

  2. Прежние значения копируются в него.

  3. На прежний массив больше не ссылаются.

var resultList = new List<...>();
foreach (var item in otherList)
{ 
   resultList.Add(...);
}

StringBuilder лучше +/+=

Создание вре́менных объектов увеличит количество сборок мусора и снизит производительность. Класс string иммутабелен, поэтому каждый раз, когда нужна новая версия строки символов, .NET Framework создаёт новую строку.

При конкатенации строк избегайте Concat, + и +=. Особенно важно это для циклов и часто вызываемых методов. В коде ниже эффективнее StringBuilder:

var productIds = string.Empty;
while (match.Success)
{
   productIds += match.Groups[2].Value + "\n";
   match = match.NextMatch();
}

В циклах старайтесь не создавать временных строк. Ниже не изменяется SearchValue.ToUpper():

if (SelectedColumn == Resources.Journaux.All && !String.IsNullOrEmpty(SearchValue))
    source = model.DataSource.Where(x => x.ItemId.Contains(SearchValue)
        || x.ItemName.ToUpper().Contains(SearchValue.ToUpper())
        || x.ItemGroupName.ToUpper().Contains(SearchValue.ToUpper())
        || x.CountingGroupName.ToUpper().Contains(SearchValue.ToUpper()));
 
 
if (SelectedColumn == Resources.Journaux.ItemNumber)
    source = model.DataSource.Where(x => x.ItemId.ToUpper().Contains(SearchValue.ToUpper()));
 
 
if (SelectedColumn == Resources.Journaux.ItemName)
    source = model.DataSource.Where(x => x.ItemName.ToUpper().Contains(SearchValue.ToUpper()));
 
 
if (SelectedColumn == Resources.Journaux.ItemGroup)
    source = model.DataSource.Where(x => x.ItemGroupName.ToUpper().Contains(SearchValue.ToUpper()));
 
if (SelectedColumn == Resources.Journaux.CountingGroup)
    source = model.DataSource.Where(x => x.CountingGroupName.ToUpper().Contains(SearchValue.ToUpper()));

Эффект усугубляется Where(), где строка в верхнем регистре создаётся для каждого элемента последовательности! Это верно и для типов, предоставляющих доступ на основе строки:

if (!uriBuilder.ToString().EndsWith(".", true, invCulture))

ToString() не нужен, доступ к последнему элементу можно получить напрямую:

if (uriBuilder[uriBuilder.Length - 1] != '.')

Интернирование и кэширование строк

Лучше статически кешировать объекты только для чтения, а не создавать их при каждом вызове:

var allCampaignStatuses = 
   ((CampaignActivityStatus[])Enum.GetValues(typeof(CampaignActivityStatus)))
   .ToList();

Элементы перечисления не изменятся, поэтому замените перечисление статическим списком.

И последнее, но не менее важное: применяя строковые ключи с несколькими разными значениями, вы можете попросить CLR кешировать значение и всегда возвращать одну и ту же ссылку. Это называют интернированием. Подробности читайте в Microsoft Docs.

Не создавайте объекты повторно

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

var allCampaignStatuses = 
   ((CampaignActivityStatus[])Enum.GetValues(typeof(CampaignActivityStatus)))
   .ToList();
   // use allCampaignStatuses in the rest of the method

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

В работе с циклом избегайте повторных вызовов и сохраняйте значения в локальных переменных; об этом особенно легко забыть, имея дело с методами строк ToLower() и ToUpper(). Новая временная строка создаётся в key.ToLower() каждой проверкой:

var found = elements.Any(
// ToLower() is called in each test
k => string.Compare(
       k.ToLower(), 
       key.ToLower(), 
       StringComparison.OrdinalIgnoreCase
       ) == 0);

Чтобы избежать вызова ToLower() или ToUpper() только ради сравнения строк, как ниже, работайте с методом String.Compare (...,... StringComparison.OrdinalIgnoreCase). Этот код:

if (transactionIdAsString != null && transactionIdAsString.ToLowerInvariant() == "undefined")

преображается в такой:

if (transactionIdAsString != null && string.Compare(transactionIdAsString, "undefined", StringComparison.OrdinalIgnoreCase) == 0)

Лучшие практики LINQ

LINQ работает везде, снижающие общую производительность детали встречаются очень часто.

Предпочитайте IEnumerable IList

Большинство методов итерируют представленные IEnumerable последовательности через foreach() или благодаря методам расширения System.Linq.Enumerable. С IList работайте, когда последовательность нужно модифицировать:

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

FirstOrDefault и Any могут не понадобиться

Нет необходимости перед foreach вызывать Any или, ещё хуже, ToList().Count > 0, как ниже:

if (sequence != null && sequence.Any())
{
   foreach (var item in sequence)
   ...
}

Избегайте избыточных ToList() и ToArray()

Пока соответствующая последовательность не будет итерирована , к примеру, через foreach, запросы LINQ должны откладывать выполнение. То же верно, когда на таком запросе вызываются ToList() или ToArray():

var resourceNames = resourceAssembly
.GetManifestResourceNames()
.Where(r => r.StartsWith($"{resourcePath}.i18n"))
.ToArray();

foreach (var resourceName in resourceNames)
{
   ...
}

Метод ToList() создаёт экземпляр List<> с элементами заданной последовательности. Из-за реализации добавления элементов в List<> стоимость создания списка из большой последовательности объектов может оказаться высокой в смысле памяти и производительности, поэтому используйте ToList() с осторожностью. Вот рекомендуемые варианты его применения:

  1. Оптимизация, чтобы избегать многократного выполнения основного запроса, если он дорог.

  2. Удаление и добавление элементов последовательности.

  3. Хранение результата запроса в поле класса.

Часто при итерации по IEnumerable вызывать ToList() не нужно: его вызовом из-за ненужного ListenAddress вы навредите выполнению с точки зрения потребления памяти, но это временно; также последовательность будет итерироваться дважды, что снизит производительность.

Основа LINQ to Object — интерфейс IEnumerable для итерации по последовательности объектов. Все методы расширения LINQ в качестве параметра принимают экземпляры IEnumerable в дополнение к конструкциям foreach. Также ToList() не нужен, когда ожидается IEnumerable. В этом заключается достойная причина предпочесть IEnumerable IList/List/[] в сигнатурах методов.

Некоторые методы вызывают ToList() перед применением Where к IEnumerable. Эффективнее располагать Where цепочкой и вызывать ToList() в конце.

И последнее, но только по порядку: не нужно вызывать ToList(), чтобы выяснить длину последовательности, то есть писать так:

productInfos
  .Select(p => p.Split(DisplayProductInfoSeparator)[0])
  .Distinct()
  .ToList()
  .Count;

Эффективнее такой код:

productInfos
  .Select(p => p.Split(DisplayProductInfoSeparator)[0])
  .Distinct()
  .Count();

IEnumerable<>.Any лучше List<>.Exists

При работе с IEnumerable вместо ToList().Exists() рекомендуется Any:

if (sequence.ToList().Exists(…))

превращается в такой код:

if (sequence.Any(...))

В проверке того, что список пуст, Any лучше Count

Методы расширения Any следует предпочесть вычислениям на IEnumerable, потому что итерация на последовательности останавливается, как только условие (если оно есть) выполнено, без выделения временного списка. Этот код:

var nonArchivedCampaigns = 
   campaigns
   .Where(c => c.Status != CampaignActivityStatus.Archived)
   .ToList();
if (nonArchivedCampaigns.Count == 0)

преображается в такой:

if (!campaigns.Where(c => c.Status != CampaignActivityStatus.Archived).Any())

Обратите внимание: допустимо написать if (!campaigns.Any (filter)).

Порядок в методах расширения может иметь значение

Порядок применяемых к последовательностям IEnumerable операторов может повлиять на производительность. Одно из важных правил — всегда сначала фильтровать, чтобы перед итерацией последовательности становились всё меньше и меньше. Именно поэтому запрос LINQ рекомендуется начинать с фильтров Where. В LINQ код определения запроса в смысле выполнения может быть обманчивым. Например, в чём разница между:

var filteredElements = sequence
  .Where(first filter)
  .Where(second filter)
  ;

и:

var filteredElements = sequence
  .Where(first filter && second filter)
  ;

Это зависит от исполнителя запроса. В случае LINQ for Objects, похоже, после фильтрации в строках нет никакой разницы: оба фильтра выполняются одинаковое число раз:

 var integers = Enumerable.Range(1, 6);
 var set1 = integers
 .Where(i => IsEven(i))
 .Where(i => IsMultipleOf3(i));

 foreach (var current in set1)
 {
     Console.WriteLine($"--> {current}");
 }

 Console.WriteLine("--------------------------------");

 var set2 = integers
 .Where(i => IsEven(i) && IsMultipleOf3(i))
 ;

 foreach (var current in set1)
 {
     Console.WriteLine($"--> {current}");
 }

Запустив код, в консоли вы увидите одинаковые строки:

IsEven(1)
IsEven(2)
   IsMultipleOf3(2)
IsEven(3)
IsEven(4)
   IsMultipleOf3(4)
IsEven(5)
IsEven(6)
   IsMultipleOf3(6)
--> 6
--------------------------------
IsEven(1)
IsEven(2)
   IsMultipleOf3(2)
IsEven(3)
IsEven(4)
   IsMultipleOf3(4)
IsEven(5)
IsEven(6)
   IsMultipleOf3(6)
--> 6

Однако под Benchmark.NET у «объединённого» выражения Where результаты значительно лучше:

 private int[] _myArray;

 [Params(10, 1000, 10000)]
 public int Size { get; set; }

 [GlobalSetup]
 public void Setup()
 {
     _myArray = new int[Size];

     for (var i = 0; i < Size; i++)
         _myArray[i] = i;
 }

 [Benchmark(Baseline = true)]
 public void Original()
 {
     var set = _myArray
         .Where(i => IsEven(i))
         .Where(i => IsMultipleOf3(i))
         ;

     int i;
     foreach (var current in set)
     {
         i = current;
     }
 }

 [Benchmark]
 public void Merged()
 {
     var set = _myArray
         .Where(i => IsEven(i) && IsMultipleOf3(i))
         ;

     int i;
     foreach (var current in set)
     {
         i = current;
     }
 }

Изучив реализацию в .NET Framework с моим коллегой Джином-Филипе, мы увидели, что дополнительные затраты, похоже, имеют отношение к базовому IEnumerator, связанному с первым Where, [но] никогда не предполагайте, вместо этого всегда измеряйте.

И приходите на наш курс по разработке на С#, где вы сможете выйти на новый уровень владения этим языком или изучить его с нуля и поработать с HR, чтобы стать Junior-разработчиком. Также вы можете узнать, как изменить карьеру в других направлениях:

Data Science и Machine Learning

Python, веб-разработка

Мобильная разработка

Java и C#

От основ — в глубину

А также:

Adblock test (Why?)

[Перевод] Шпаргалка по метрикам производительности cURL: как измерить задержку сервера

Эту шпаргалку я написал в первую очередь, потому что был несколько озадачен, когда в течение более, чем 10 минут пытался найти в Google значение time_pretransfer. Попробуйте сами, я подожду. Это настоящая загадка!

Первым результатом выдачи по запросу “curl time_pretransfer meaning” оказывается следующий пост со StackOverflow, в котором автор цитирует страницу из мануала cURL, но при этом остается в недоумении (как и я):

Каково значение time_pretransfer? Я просмотрел страницу мануала curl, где говорится «время, пройденное от начала до момента запуска передачи файла». Мне это объяснение не совсем понятно.
Все верно – даже если вы “RTFM” (прочтете гребаное руководство), то соответствующая страница мануала совершенно не разъясняет значение time_pretransfer.

Тут можно подумать, что кто-нибудь уже написал хорошую статью по метрикам cURL.

В конечном итоге я все же выяснил значение time_pretransfer….в чем мне помог дедуктивный анализ и… скорость света (0.299 км/мкс). Больше я нигде верного ответа не нашел. Ну а раз теперь он есть у меня, то я и делюсь им с вами в этой статье.

Несмотря на то, что ниже приведена полноценная памятка, я также пояснил каждую метрику отдельно. Если вам интересно, как я разгадал значение time_pretransfer, пролистайте к разделу “Что значит time_pretransfer?”

  • time_namelookup: длительность просмотра DNS (перевод имени домена в IP-адрес).
  • time_connect: длительность рукопожатия TCP (выполненного как при HTTP, так и при HTTPS-запросах).
  • time_appconnect: длительность рукопожатия SSL (только для HTTPS).
  • time_redirect: длительность перенаправления. Значение равно 0, если перенаправления не было. В cURL для активации перенаправления требуется флаг -L.
  • time_pretransfer: по-сути, это псевдоним для time_appconnect либо time_connect (в зависимости от HTTP или HTTPS). Он пригождается только в качестве указателя на то, когда начался конкретный запрос к серверу.
  • time_starttransfer: обозначает готовность сервера к передаче байтов. То же, что и TTFB (Time To First Byte). Он включает в себя time_pretransfer. Чтобы получить продолжительность этой фазы, нужно просто вычесть time_pretransfer из time_starttransfer.
  • time_total: общая продолжительность всего вызова cURL. Для получения времени передачи данных (их скачивания) нужно вычислить time_totaltime_starttransfer.

Согласно разделу мануала cURL по Linux, все эти метрики также доступны для вывода.

Пример теста скорости в cURL


Перед вами типичный вывод теста скорости в cURL. Важно отметить, что он выполнен в отношении HTTPS URL. Этот фактор относится к time_appconnect и time_pretransfer.

Скачайте curl-format.txt с GitHub и выполните следующую команду:

$ curl -L -w "@curl-format.txt" -o tmp -s $YOUR_URL
time_namelookup: 0.001654s
time_connect: 0.054957s
time_appconnect: 0.156267s
time_pretransfer: 0.156494s
time_redirect: 0.000000s
time_starttransfer: 0.219013s
________________________________________
time_total: 0.443749s

Все показатели времени с момента запуска cURL суммируются.

Пара существенных примечаний по вышеприведенной команде:

  • Флаг -o tmp важно использовать, чтобы действительно скачать файл. Если же попытаться действовать умнее и передать -o /dev/null, то cURL пропустит скачивание файла, так как /dev/null для нее означает, что этого делать не нужно. В итоге время скачивания получится близким к 0 секунд (дельта между time_total и time_starttransfer).
  • Флаг -L нужен, чтобы cURL следовала перенаправлениям.

Что значит time_namelookup?


Эту метрику проанализировать несложно. Она отражает время просмотра DNS. Конечным результатом здесь будет IP-адрес целевого сервера, к которому должна обратиться cURL. Соединений с этим сервером пока не установлено.

Обратите внимание, что эта метрика является важным фактором в тесте скорости. Вы можете удивиться, если считаете, что это просто банальный просмотр DNS.

Как правило, поиск по DNS происходит очень быстро, потому что компьютер зачастую уже имеет кэшированный результат. Кэширование делается, потому что результаты поиска изменяются редко. Например, если для домена есть запись А, которая разрешается в IP, то компьютер может легко ее кэшировать – вся запись максимум займет 32 байта (а большинство ПК располагают гигабайтами памяти).

Однако просмотр DNS может занять и больше времени, если кэшированного результата в памяти компьютера нет. Это особенно актуально, когда у целевого сайта невысокий траффик, так как это означает, что на промежуточных маршрутизаторах результат просмотра DNS не кэширован, и они не могут вернуть его быстро. Водном из проведенных мной тестов скорости этот процесс занял 56% от всего времени скачивания (0.122108/0.217073 с.). И это невероятно высокий показатель. Я удивлен, что он получился столь велик. Тестирование проводилось в отношении моего собственного нового сайта, траффик которого реально очень низок. Следующий поиск по DNS занял всего 0.001408 с. Здесь уже определенно сработало кэширование.

Для тестов скорости CDN очень важно, чтобы операция просмотра DNS выполнялась. Поиск по DNS – это процесс, в котором CDN находит ближайший к вам IP-адрес сервера. Поэтому для одного и того же пути домена IP-адрес будет сильно отличаться в зависимости от вашего местоположения. Несмотря на то, что есть способ исключить просмотр DNS из операции cURL, указав IP-адрес как часть входных данных, так делать не стоит. Поиск DNS очень важен для работы CDN.

Писать об этом было довольно интересно. Я, наверное, как-нибудь займусь тестированием быстродействия DNS, хотя это будет уже посложнее, нежели просто тестировать скорость CDN.

Что значит time_connect?


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

Обратите внимание, что эта процедура выполняется при каждом запросе, как для HTTP, так и для HTTPS, независимо от того, с какого сервера запрос поступает (Google, Facebook, Microsoft или любого рядового сервера на WordPress).

Что значит time_appconnect?


Эта метрика показывает продолжительность рукопожатия SSL, измеряемого только для HTTPS-запросов. Если выполнить запрос к HTTP URL, то cURL сообщит для time_appconnect значение 0.000…

Выполняется эта процедура для каждого HTTPS-запроса, независимо от его цели или отвечающего сервера, даже если это Google.

Хотя не совсем понятно, почему метрика называется time_appconnect. Похоже на ошибочный термин. Возможно, точнее было бы time_sslconnect.

Что значит time_pretransfer?


По существу, time_pretransfer равнозначна либо time_appconnect (HTTPS), либо time_connect (HTTP) — в зависимости от того, выполняется HTTPS-запрос или HTTP. В этом вся ее суть. Между time_pretransfer и предыдущей стадией ничего не происходит.

Именно эта метрика меня по истине смутила. Мне никак не удавалось понять ее смысл, пока я не осознал, что…промежуток времени с предыдущей стадии слишком мал для наличия в нем какой-либо сетевой активности.

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

Давайте посчитаем. Первым делом отметим, что в примере вывода cURL разница во времени между time_pretransfer и time_appconnect составляет всего 227 мкс. Если вы проверите сами, то она может составить даже всего 27 мкс (как видно из вывода cURLв соответствующем ишью на GitHub).

Второе, что мы возьмем в расчет – это скорость света в мкс. Скорость света составляет 300 000 км/с. В одной секунде содержится 1 миллион мкс, значит 300 000 км/с * 1 с/1 000 000 мкс = 0.299 км/мкс.

В завершении умножаем получаемое значение 227 мкс * 0.299 км/мкс = 67.87 км.

Стоп, 67 км?

И это в оба конца, значит в одну сторону будет всего около 34 км.

Да уж… Я сильно сомневаюсь, что в такой близости от меня располагается дата центр. Хоть я и нахожусь в крупном городе США, но все же не в одном из типичных вариантов вроде Сан-Франциско, Нью-Йорка, Лос-Анжелеса, Чикаго или Далласа.

Я считаю, что на данной стадии cURL скорее всего просто подготавливается к получению байтов – и все. В промежутке между time_pretransfer и time_appconnect вряд ли может происходить какой-то обмен байтами.

Поэтому time_pretransfer – это то же, что и time_connect или time_appconnect. В таком случае единственное очевидное назначение этой метрики – упростить анализ того, сколько времени потребовалось серверу для начала передачи байтов. Вычисляется она так: time_starttransfertime_pretransfer, что плавно переносит нас к следующей метрике, time_starttransfer.

Что значит time_starttransfer?


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

Time_starttransfer по своему существу аналогична Time To First Byte (TTFB), что отражает длительность подготовки сервера к ответу. Для бэкенд-инженеров данный термин тоже определенно известен. Это просто задержка, а уменьшение задержки очень важно.

Давайте рассмотрим пример оценки метрики time_starttransfer для CDN. Если запрашивать файл из CDN, то данная фаза будет отражать длительность подготовки сетью этого файла. В случае же, когда CDN ранее кэшировала файл в память, она его сможет вернуть быстро. Если же он был кэширован в слой SSD, то потребуется чуть больше времени. А может он вообще не кэширован, тогда сети потребуется выполнить собственный HTTP/HTTPS-запрос к серверу-источнику, где этот файл размещен. Итак, данный шаг очень важен при анализе отличий между разными CDN – все остальные показатели будут плюс-минус схожи.

Вот еще один пример. Предположим, что вы отправили запрос к Google Translate API для получения французского перевода английского предложения. В этом случае этап time_starttransfertime_pretransfer отразит длительность выполнения перевода сервисом Google. Все предыдущие шаги будут представлять просто обычную настройку TCP-соединения, типичную для любого другого запроса.

Так что time_starttransfer – это просто задержка сервера. Мы уже прошли почти по всем метрикам, и последние две – это time_redirect и time_total, которые вполне ясны.

Что значит time_redirect?


Если перенаправления нет, то и значение этого показателя будет 0.000.
Если же перенаправление присутствует, то отразится его продолжительность.

Вот простые входные данные для cURL, на примере которых мы увидим, сколько может занять перенаправление: google.com, что ведет к перенаправлению на www.google.com.

Для наших целей тестирования скорости CDN важно, чтобы показатель time_redirect был равен 0, потому что перенаправление не относится к факторам, влияющим на скорость скачивания CDN.

Стоит также отметить, что если вам требуется измерить время перенаправления, то нужно убедиться в том, что оно происходит на соответствующем шаге. Когда я выполнил операцию cURL для google.com, продолжительность time_redirect должна была измеряться на шаге 2 (сразу после time_namelookup). Я же получил следующий вывод:

curl -L -w "@curl-format.txt" -o /dev/null -s google.com
time_namelookup: 0.006038s
time_connect: 0.069728s
time_appconnect: 0.000000s
time_pretransfer: 0.069885s
time_redirect: 0.069052s
time_starttransfer: 0.161169s
________________________________________
time_total: 0.179716s

Еще важный момент – нужно использовать флаг -L, чтобы cURL следовала перенаправлениям, в противном случае она этого делать не будет. Эту ошибку легко допустить, потому что cURL не будет ругаться, если получит перенаправление, которому не последует. Единственный ориентир здесь – это, если показатель time_redirect будет равен 0.000 при ожидании ненулевого значения.

Что значит time_total?


Эта метрика очень прозрачна. Она просто отражает продолжительность всего процесса скачивания – начиная с запроса на просмотр DNS и вплоть до отправки клиентом разрыва соединения FIN ввиду завершения получения всех байтов.

Кстати, если вы используете cURL для тестирования скоростей CDN, то должны знать, что одного только выполнения cURL недостаточно для измерения скорости доставки CDN. Вам нужно оценить две метрики, а именно «1-ю скорость скачивания» и «скорость кэшированного скачивания», то есть выполнить около 11 вызовов cURL к одному URL. У меня есть более углубленная статья, где я объясняю свой метод тестирования (англ.).

Adblock test (Why?)

Отсутствующий амперсанд в обновлении Chrome OS сломал логин в хромбуках

В начале этой недели Google выпустила обновление Chrome OS 91.0.4472.165. Пользователи хромбуков после его установки столкнулись с проблемой входа в систему после перезагрузки. Они не могли зайти под своей учетной записью или система вообще не давала возможности для входа и уходила в перезагрузку.
Через двое суток разработчики Google выпустили исправление для стабильной версии Chrome OS под номером 91.0.4472.167.

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

Часть пользователей, которые ранее все же установили версию .165 и перезагрузились или выключали устройство, оказались с нерабочим хромбуком. Они делились на форумах Reddit своими проблемами и попытками обновить якобы стабильную версию Chrome OS до рабочей. Основное решение, если нет доступа к возможности обновить Chrome OS даже через гостевой вход, сброс устройства к заводским настройкам или загрузка через ранее созданный USB-диск для восстановления. Причем в этом случае все данные пользователя при откате системы до рабочего состояния удаляются.

Пользователь Reddit обнаружил, что разработчики Google по какой-то причине в стабильной версии Chrome OS 91.0.4472.165 пропустили один амперсанд в коде функции проверки типа ключей пользователя при входе.

Вместо логического оператора "&&" в коде функции VaultKeyset стоял "&", что приводило к некорректной работе алгоритма входа пользователя в систему.

Вышеописанный баг прошел три ревизии при тестировании в канале разработчиков и был оставлен в стабильной версии Chrome OS. Причина, по которой это произошло, Google не раскрыла. Причем компания после жалоб пользователей сначала удалила сборку 91.0.4472.165 из своей матрицы обновлений, оставив там предыдущую стабильную версию Chrome OS 91.0.4472.147. С 22 июля Google выпустила необходимый патч в версии 91.0.4472.167.

Adblock test (Why?)

Что нам стоит ЦОД построить: необычные места для размещения дата-центров


С каждым годом растет объем информации, генерируемой человечеством. Ее нужно как-то обрабатывать, хранить, анализировать. А значит, нужны центры обработки данных, которых тоже становится все больше. Их строят повсюду, причем не только на суше, но и на воде, в виде плавучих платформ. Есть даже подводные ЦОД, хотя они пока что работают в качестве тестовых объектов.

Все дата-центры объединяет одно — им нужны вода, электричество, сетевая инфраструктура и прочие ресурсы. Поэтому строить ЦОД можно далеко не везде. В некоторых случаях в качестве строительства выбирают места, где уже есть какие-то сооружения, их модифицируют и размещают внутри оборудование. Либо же комбинируют системы дата-центра с другими системами — например, заводов, фабрик, ферм. Вот о таких объектах сегодня и поговорим.

Обогрев фермы для омаров в Норвегии



Похоже, что сотрудники дата-центра пригласили омара на пивную вечеринку
Реализация этого проекта стартовала буквально на днях. Оператор дата-центров Green Mountain заключил договор с фермой по выведению омаров Norwegian Lobster Farm. Большинство видов омаров — теплолюбивые, а значит, ферме нужно либо располагаться в регионе с высоким уровнем инсоляции, либо подогревать водоем, где живут подопечные.
Норвежцы выбрали второй вариант. Согласно условиям договора, ферма будет получать тепло из ЦОД DC1-Stavanger. Конечно, не бесплатно, но, во всяком случае, стоимость получаемого тепла будет низкой. Во всяком случае, ниже, чем если бы пришлось тратить дорогое электричество или другие ресурсы на обогрев водоема.

Ферма, к слову, очень продвинутая, на ней используются современные технологии, включая компьютерное зрение, мониторинг каждого ракообразного и роботы, которые выполняют технические работы. Тепло, поступающее от дата-центра, будет использоваться для нагрева воды до температуры в 20 °С.
DC1-Stavanger расположен в относительно удаленном от населенных пунктов регионе, поэтому пустить тепло на обогрев домов не получится. Так что радоваться теплой воде будут исключительно обитатели фермы.

Tomorrow Water Project — ЦОД на отстойниках



Это проект компании Tomorrow Water, оператора очистных сооружений. Вероятно, девизом проекта можно сделать что-то вроде «Данные не пахнут». И действительно, очистные сооружения в большинстве своем отлично подходят для размещения ЦОД, больших и малых. У них несколько преимуществ:
  • Строятся они, как правило, рядом с населенными пунктами.
  • Рядом много воды, есть энергетическая инфраструктура.
  • Несмотря на близость к населенным пунктам, во многих случаях вокруг очистных сооружений есть место для возведения дополнительных зданий.
  • Современные технологии позволяют снизить объем полезного пространства, занимаемых такими сооружениями, что позволяет разместить больше оборудования в ЦОД.

Отдельные проекты дата-центров, стоящих около очистных сооружений, существуют (один такой проект реализован Microsoft лет 10 назад). Но это, как правило, не очень большие объекты. Компания Tomorrow Water стремится развивать концепцию «ЦОД на очистных сооружениях», и проводит собственные тесты. Максимальную площадь обычно занимают отстойники. Компания Tomorrow Water заменяет их системами биофильтрации, которым требуется на 80% меньше пространства. А на освободившейся территории строят разного рода сооружения. В 2017 году в Сеуле был построен музей, здание которого довольно велико.

Сейчас Tomorrow Water привлекает к своей инициативе операторов дата-центров. Компания предлагает партнерам поучаствовать в модернизации отстойников, в обмен на полезную площадь, воду и, соответственно, охлаждение. На призыв компании уже откликнулись компании из Калифорнии, США и Сингапура. В этих регионах вода и полезная площадь в дефиците, так что подобные проекты могут стать для операторов ЦОД идеальным вариантом.

Дата-центр рядом с терминалом СПГ



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

Кроме того, во время разжижения газа терминалу нужно тепло, которое способны поставлять дата-центры, причем в огромных количествах. Комбинация системы охлаждения ЦОД и терминала СПГ — неплохой вариант.

Сейчас его прорабатывает национальный университет Сингапура (NUS) и компания Keppel. Согласно планам, размещаться новый дата-центр будет на базе Singapore LNG Corporation (SLNG) на острове Джуронг. Проект поддерживает правительство Сингапура в рамках программы Green Data Centre Research Programme. В общем, если у проекта столько участников, возможно, он не такой уж и сумасшедший, как может показаться.

Плавучий дата-центр от Nautilus



Компания в начале года запустила свой дата-центр Stockton I. Сейчас он пришвартован в порте Стоктон, штат Калифорния, США. Это уже полностью функционирующий инфраструктурный объект, а не баржа с нагроможденным на ней оборудованием.

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

Что касается энергопотребления, то оно ниже, чем у обычных ЦОД, благодаря воде. Ее не нужно качать по трубопроводам издалека, энергопотребление ДЦ ниже, чем у стандартного объекта аналогичной мощности. У плавучего дата-центра PUE составил 1,15. По расчетам, проведенным специалистами по охране окружающей среды, негативное влияние объекта на окружающую минимально. Локальная и тем более глобальная экосистемы не пострадают.

Забортная вода пропускается через сложную систему трубопроводов из нержавеющей стали. Система также включает турбинные насосы, теплообменники, клапаны и прочие элементы. В минуту система прокачивает около 17 000 литров воды, при необходимости этот объем можно увеличить до 45 000 литров.

Что касается мощности, то она превышает 100 кВт на стойку, а общая мощность дата-центра достигает 7 МВт. Общая площадь для размещения оборудования составляет около 90 м2.

ЦОД на форелевой ферме



Кроме омаров, занимаются операторы ЦОД и выращиванием форели. Конечно, не сами, а при помощи партнеров. В 2021 году компания Green Mountain, довольно крупный оператор ЦОД и компания Hima Seafood заключили партнерское соглашение. Его суть — предоставление тепла дата-центром DC2-Telemark в Рьюкане, Норвегия форелевой ферме, принадлежащей Hima Seafood. Эта ферма — самая большая в мире.

Тепло направляется на обогрев водоемов с рыбой. Форель не очень любит тепло, но все же в условиях промерзающих резервуаров она существовать не может. Поэтому тепло от дата-центра обеспечит постоянный микроклимат в водоемах с форелью. К 2023 году объем поставляемой партнерами рыбы составит около 9000 тонн в год.


Дата-центр построил систему трубопроводов, по которым нагретая вода идет к резервуарам, охлаждается и возвращается в систему охлаждения ЦОД. Стоит отметить, что этот же дата-центр получает энергию от возобновляемых источников — прежде всего, микро ГЭС.

Дата-центр и ТЭЦ



В г. Фалун, Швеция, ЦОД построен рядом с местной теплоэлектроцентралью. ТЭЦ сжигает древесные отходы промышленности, так же здесь производятся пеллеты (древесные гранулы), которые используются шведами в качестве топлива для отопления домов.

Дата-центр, который получил название EcoDataCenter, получает энергию от этой ТЭЦ. Генерируемое тепло он отправляет для сушки пеллет, которые производит ТЭЦ. В год получается около 10 МВт тепловой энергии, чего достаточно для сушки первичного материала для пеллет, в результате экономится энергия.

Вероятно, масштабировать такой проект не получится, но в качестве тестового варианта — почему бы и нет.

Возможно, вам известны другие варианты необычных ЦОД? Если да, давайте обсудим в комментариях.

Adblock test (Why?)

[Перевод] Кунг-фу стиля Linux: базы данных — это файловые системы нового уровня

Забавно наблюдать за тем, как компьютерные технологии, которые, в момент их появления, кажутся необычными, в итоге либо уходят в небытие, либо становятся привычными и распространёнными. Например, в своё время, если на компьютере имелось больше одного пользователя, это иначе как «хай-теком» и назвать было нельзя. Были ещё и разработки, которые не завоевали широкой популярности, вроде векторных дисплеев, или памяти, адресуемой содержимым. А вот использование в компьютерах накопителей данных, особенно — жёстких дисков — стало весьма распространённой практикой. Но было время, когда накопители данных были экзотическими устройствами, пользоваться которыми было далеко не так просто, как в наши дни.

Меня, если говорить о накопителях данных, удивляет то, что понятие «файловая система», в том виде, в котором мы его знаем, за годы его существования изменилось не слишком сильно. Конечно, если сравнить то, что есть сейчас, с тем, что было, скажем, в 1960-е годы, то можно сказать, что в наши дни файловые системы дают нам гораздо более широкий функционал, чем прежде. В наши дни всё гораздо лучше в плане скорости, способов кодирования, шифрования, сжатия данных и так далее. Однако фундаментальная природа того, как мы храним файлы, и того, как с ними работаем в компьютерных программах, практически не изменилась. А всё должно быть не так. Нам известны более эффективные способы организации данных, но по каким-то причинам большинство из нас не пользуется этими возможностями в своих программах. Оказывается, правда, что пользоваться ими достаточно просто, и я собираюсь это продемонстрировать на экспериментальном приложении, которое вполне может стать отправной точкой разработки базы данных электронных компонентов для моей лаборатории.
Основой для базы данных, подобной той, о которой я хочу рассказать, может быть файл, где в роли разделителя данных применяются запятые (CSV-файл). Тут можно воспользоваться и чем-то вроде формата JSON. Но я собираюсь задействовать полномасштабную базу данных SQLite для того чтобы избежать необходимости в «тяжёлом» сервере баз данных и сложностей, сопряжённых с его поддержкой. Можно ли, используя мой подход, сделать что-то, что способно заменить базу данных, на которой основана какая-нибудь система резервирования авиабилетов? Нет. А подойдёт ли он для решения большинства задач, которые всем нам так или иначе приходится решать? Готов поспорить, что подойдёт.

Абстракция


Если подумать о файловых системах, то окажется, что это — не более чем абстракция над физическим устройством для хранения данных. Обычно мы не знаем или попросту не беспокоимся о том, где именно хранится нечто вроде файла hello.c. Нас даже не интересует то, сжат ли этот файл, то, зашифрован ли он. Он, возможно, был загружен по сети, а, может, его части хаотично разбросаны по всему жёсткому диску. Обычно нас это не волнует. А что если абстрагировать саму файловую систему?

Это, в общем-то, идея, лежащая в основе баз данных. Если имеется, например, список электронных компонентов, я могу сохранить его в CSV-файле и прочитать этот файл с помощью программы для работы с электронными таблицами. Или могу использовать полноценную базу данных. Проблема баз данных заключается в том, что для работы с ними обычно требуется некое серверное ПО, вроде MySQL, SQL Server или Oracle. Можно абстрагировать интерфейс базы данных, но это — весьма тяжеловесное решение, если сравнить его с простой операцией открытия файла и с обычной работой с этим файлом.

Правда, существует, например, популярная библиотека, называемая SQLite, которая даёт весьма надёжный механизм работы с базой данных, размещённой в единственном файле. При этом для работы с такой базой данных не нужен специальный сервер, её не нужно как-то по-особенному поддерживать. В работе с подобной БД, конечно, есть и ограничения. Правда, при её применении во многих простых программах можно пользоваться полезными возможностями баз данных, но при этом не тратить системные и финансовые ресурсы на поддержку инфраструктуры, обеспечивающей работу обычной СУБД.

Правильный инструмент для правильной работы


У SQLite, конечно, есть ограничения. Но если, например, некто занимается созданием собственного файлового формата для хранения неких данных, ему, возможно, стоит рассмотреть возможность перехода на SQLite и обработки этих данных средствами СУБД. В соответствии с данными, приведёнными на сайте SQLite, такой ход вполне может привести к экономии дискового пространства и к увеличению скорости доступа к данным. И ещё — после того, как программист освоится с этой технологией, окажется, что она упрощает работу с данными. И небольшое приложение, основанное на SQLite, если решено будет перевести его на что-то более серьёзное, лучше поддастся такому переходу.

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

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


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

О моей задаче


Мне не хотелось бы тут рассказывать обо всём, что связано с разработкой приложения, я не собираюсь делать из этой статьи учебный курс по SQL — языку структурированных запросов, используемому в большинстве СУБД, включая SQLite. Мне, вместо всего этого, хочется показать то, как легко приступить к работе над простым приложением, применяющим возможности базы данных для хранения сведений об электронных компонентах, используя язык C. При этом написание кода на C будет представлять собой самую простую из наших задач. У нас есть две основных задачи, с которыми разобраться уже гораздо сложнее. Это — задача структурирования базы данных, то есть — проектирование схемы БД, и задача первоначального ввода данных в систему. Даже если БД планируется заполнять данными в процессе работы с программой, неплохо будет, если в самом начале в ней уже что-то будет, чтобы соответствующая программа с самого начала была бы работоспособной.

Основы работы с базами данных


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

Строки каждой таблицы имеют уникальные идентификаторы (ID). Если программист не предусмотрел механизм формирования этого ID, СУБД обычно может сама его сформировать. В частности, речь идёт об автоматическом инкрементировании подобных идентификаторов и об обеспечении того, что каждой строке таблицы будет назначен уникальный идентификатор.

Если бы возможности СУБД ограничивались тем, о чём я только что рассказал, то у них не было бы особых преимуществ перед обычными CSV-файлами. Но после того, как данные организованы вышеописанным образом, многие задачи можно решать лучше, чем при отсутствии такой структуры. Например, легко попросить систему выполнить сортировку элементов, или, например, выбрать из таблицы строки, содержащие три самых больших значения напряжения.

Надо отметить, что одной из главных сильных сторон баз данных является возможность создавать соединения данных. Предположим, у меня есть список компонентов (Components): печатная плата (PCB), резистор (Resistor), держатель аккумуляторной батареи (Battery Holder) и светодиод (LED). Имеется таблица, в которой для каждого из этих компонентов предусмотрена отдельная строка. Теперь представим, что у меня есть таблица с описанием сборных изделий (Assembly), сделанных из этих компонентов. Тут можно применить простой подход:

Таблица Component
ID    Name
===========
1     PCB
2     Resistor
3     LED
4     Battery Holder

Таблица Assembly
ID    Name       Components
============================
1     Blink1     PCB, Resistor, LED, Battery Holder
2     Blink2     PCB, Resistor, LED, Resistor, LED, Battery Holder

Выглядит это некрасиво, такому подходу свойственно нерациональное использование системных ресурсов. Куда лучше будет использовать три таблицы:
Таблица Component
ID Name
===========
1 PCB
2 Resistor
3 LED
4 Battery Holder

Таблица Assembly
ID Name 
=========
1 Blink1 
2 Blink2 

Таблица Assembly_Parts
ID    Component    Quan
=======================
1     1            1
1     2            1
1     3            1
1     4            1
2     1            1
2     2            2
2     3            2
2     4            1

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

В результате я, в своей базе данных, планирую создать три таблицы. В таблице parts будет храниться список имеющихся у меня компонентов. В таблице partnums — сведения о типах компонентов (например — 7805, 2N2222 или CDP1802). И наконец — в таблице locations будут сведения о том, где именно хранится тот или иной компонент. Мои данные можно структурировать и по-другому. Скажем, может иметься таблица, в которой хранятся сведения о способах монтажа компонента. Например, компоненту 2N2222 может быть назначено значение TO92, или может быть указано, что он предназначен для поверхностного монтажа. Кроме того, я собираюсь создать механизм для просмотра данных, представление (view), в котором все данные будут показаны в развёрнутом виде — как в первом примере. Представление — это нечто такое, что в базе данных не хранится, но, для удобства, выглядит как таблица. А на самом деле это — всего лишь результат выполнения запроса к базе данных, с которым можно что-то делать.

Конечно, работа с БД этим не ограничивается. Существуют, например, внутренние и внешние соединения, имеется множество других деталей и нюансов работы с данными. Но, к нашему счастью, есть много хороших материалов о базах данных. Например — документация по SQLite.

Ровно столько SQL, сколько нужно для дела


Нам, для решения наших задач, понадобится сравнительно немного SQL-инструкций: create, insert и select. Имеется программа, sqlite3, которая позволяет выполнять команды в применении к базе данных. Этой программе, средствами командной строки, можно передать имя базы данных, а потом можно работать с этой базой данных, что совсем несложно. Для выхода из программы используется команда .exit.

Вот команды создания таблиц базы данных. Полагаю, устроены они достаточно просто и понятно.

create table part ( id integer not null primary key, name text, partnum integer, value text, 
   units text, quantity integer, photo blob, data text, location integer, footprint text);
create table partnums (id integer not null primary key, partnum text, desc text);

create table locations (id integer not null primary key, location text, desc text);

create view full as select part.id, name, partnums.partnum as part_number, value, units, 
   quantity, data, locations.location as location, footprint from part 
   inner join partnums on part.partnum = partnums.id inner join locations on locations.id=part.location

Я выполнил эти команды с помощью sqlite3, в командной строке, но их можно было бы выполнить и из программы с графическим интерфейсом. А если бы мне это было нужно, я мог бы сделать так, чтобы они были бы выполнены из моей C-программы. Средства командной строки я использовал и для того, чтобы добавить в базу данных несколько тестовых записей. Например:
insert into locations (location,desc) values ("Shop - storage II","Storage over computer desk in shop");
insert into partnums(partnum,desc) values("R.25W","Quarter Watt Resistor");
insert into part(partnum,quantity,location,value,units) values (2,111,1,"10K","ohms");

Для получения данных из базы используется команда select:
select * from part;

select partnum, quantity from part where quantity<5;

Если вы хотите глубже разобраться в этих и других командах — существует множество учебных руководств по SQL.

Программирование!


До сих пор мы, готовя основу программы, не нуждались, собственно, в программировании. При этом, исходя из предположения о том, что в нашем распоряжении имеется пакет наподобие libsqlite3-dev, для того чтобы оснастить C-программу функциями, направленными на работу с базой данных, нам не придётся прилагать особенно больших усилий. В частности, в коде нужно будет подключить sqlite3.h. Если такого заголовочного файла найти не удаётся — это может означать то, что в системе не установлено то, что нужно для SQLite-разработки. Ещё понадобится связь с libsqlite3. Если говорить о простом однофайловом проекте, то, чтобы приступить к работе над ним, скорее всего, достаточно будет такого makefile:
CC=gcc
CFLAGS+=-std=c99 -g
LDFLAGS=-g
LDLIBS+=-lsqlite3

edatabase : main

main : main.c

Сам код очень прост. Сначала нужно открыть файл базы данных (с использованием функции sqlite3_open). Вместо имени файла можно передать функции :memory. Тогда в нашем распоряжении окажется база данных, расположенная в памяти, которая будет существовать столько же времени, сколько будет работать программа. Этот вызов возвращает дескриптор базы данных. Далее — нужно подготовить SQL-запрос, который планируется выполнить. Это может быть запрос, подобный тем, которые мы уже выполняли, или — любой другой запрос. В моём случае мне нужно взять все данные из представления full и вывести их. Поэтому я собираюсь выполнить такой запрос:
select * from full;

И наконец — мы пользуемся функцией sqlite3_step. До тех пор, пока она возвращает SQLITE_ROW, мы можем обрабатывать строки, пользуясь функциями вроде sqlite3_column_text. В конце выполняется финализация и закрытие базы данных. Вот готовый код, из которого удалены механизмы обработки ошибок:
#include <sqlite3.h>
#include <stdio.h>
 
int main(int argc, char *argv[])
   {
   sqlite3 *db;
   sqlite3_stmt *sql;
   int rv;
 
   rv=sqlite3_open("parts.db",&db);
   rv=sqlite3_prepare_v2(db, "SELECT * from full", -1, &sql, NULL);
   do
     {
     rv=sqlite3_step(sql);
     if (rv==SQLITE_ROW)
        {
        printf("%s,",sqlite3_column_text(sql,0));
        printf("%s\n",sqlite3_column_text(sql,2));
        }
     } while (rv==SQLITE_ROW); 
   sqlite3_finalize(sql);
   sqlite3_close(db);
   return 0;
}

Если хотите — вот полный код. А в ситуации, когда перебор строк нас не интересует, можно воспользоваться функцией sqlite3_exec. Даже в документации говорится, что это — лишь обёртка вокруг функций prepare, step и finalize. Поэтому данной функции можно просто передать соответствующие входные данные и всё должно заработать.

Конечно, существует и множество других функций. Например, можно воспользоваться функцией sqlite_column_int, или, для получения других типов, можно применить другие вызовы. Можно прикреплять к SQL-вызовам параметры для установки значений, а не пользоваться строками. Здесь я лишь продемонстрировал то, насколько простым делом может быть создание программы, в которой используется SQLite.

Итоги


Когда вы в следующий раз поймёте, что занимаетесь выдумыванием нового файлового формата — поразмыслите о том, чтобы, вместо этого, воспользоваться SQLite. К вашим услугам будут бесплатные инструменты, а после того, как вы освоите SQL, вы поймёте, что можете сделать очень многое, не написав никакого программного кода, кроме, разве что, кода различных SQL-команд. Можно даже использовать систему для хранения разных версий базы данных, подобную той, что применяется в Git. И, кстати, некоторые используют в роли базы данных Git, но мы этого делать не рекомендуем.

Пользуетесь ли вы СУБД в своих программах?

Adblock test (Why?)

Как построить свой самолет со своим двигателем на сверхпроводниках и жидким азотом

Подсказка: сделать все маленьким.

Вступление

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

Начало

Так получилось что весной 2019 я начал работать инженером в компании СуперОкс. Я принимал участие в разработке двигателя на сверхпроводниках (Фото). Работа по началу была интересная и иногда пыльная в перемешку со стружкой из различных материалов. Столько проблем удалось решить :). Спустя пол года я понял что путь до полноценного самолета с двигателем на сверхпроводниках будет долгим и сложным. Тогда родилась идея сделать свой двигатель, поставить его на маленький беспилотник или радиоуправляемый самолет и совершить хотя бы один полет.

Для справки. Когда я только узнал об этой компании, то я знал что такое сверхпроводник, но не предполагал что производство такого продукта существует в РФ. Реально было приятно, что такие производства существуют и развиваются.

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

Где-то в январе 2021 года появилось чувство, что если ничего не начать делать, то момент может быть упущен навсегда (пока семь раз отмеришь, другие уже давно отрежут). На этот момент я считал что накопил достаточное количество знаний для реализации всего проекта и начал параллельно с основной работой занимать своим двигателем.

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

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

Но на этом пути были такие явные сложности:

  1. трудоемкость намотки обмоток на статор. (Дело в том что сверхпроводник представляет собой металлическую ленту с несколькими слоями напыления. Сам по себе сверхпроводник это тонкий слой керамики. Если ленту сильно гнуть, то можно повредить слой сверхпроводника (https://kokos-asi-production-hot.hb.bizmrg.com/blockeditor/cfd/cfd21b82899873e22f77f08b0916d74b/5_2_1x.jpg );

  2. статорное железо (магнитопровод) будет в жидком азоте. На его охлаждение требуется потратить дополнительный объем азота. Также статорное железо при работе электродвигателя будет греться и поэтому азот будет испаряться интенсивнее;

  3. сложность вывода вала воздушного винта. Нужно обеспечить теплоизоляцию между ротором и криостатором. Так-же необходимо защитить ротор от намерзания льда и конденсации кислорода на поверхности ротора;

  4. сложность при коммутации обмоток. Лента проводника тонкая и гнется с некоторыми ограничениями, паять 24 контакта пытаясь уложить в малый объем должно быть довольно сложно.

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

Дальнейшие размышления были о возможности перейти к бесщеточным электродвигателям с внутренним ротором на постоянных магнитах (инранер). Эта идея выглядела более привлекательнее. Важное изменение это решение продувать испаряющимся азотом полость ротора. Благодаря этому при постоянном потоке испаряющегося азота внутрь недолжен проникать воздух вместе с влагой и кислородом. и поэтому недолжны замерзать подшипники. Если бы внутрь начал проникать воздух, то влага из него стала конденсировать на поверхности внутренней части корпуса ротора. Образовавшийся лёд мог легко прихватить как подшипники, так и сам ротор к внутренней стенке. Но не меньшую опасность мог представлять кислород. Дело в том, что он конденсируется при температуре порядка -167 градусов Цельсия (для сравнения азот кипит при -196,5 градусах Цельсия). Это приводит к более интенсивному испарению азота при поглощении энергии конденсации кислорода и его охлаждении, через тонкую стенку. И сам факт наличия жидкости, в полости где должен вращаться ротор, отрицательно влияет на работоспособность двигателя.

Схема электродвигателя вертикальной компоновки с внутренним ротором
Схема электродвигателя вертикальной компоновки с внутренним ротором

У этой конструкции плюсов было больше чем у предыдущего решения. Однако наличие магнитопровода статора также грозило существенным увеличением трудоемкости. Самый простой выход из этой ситуации это избавиться от магнитопровода. Так пришла идея сделать двигатель без магнитопровода (статорного железа).

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

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

План "А"

На виде с верху это выглядело так:

Схема электродвигателя с шестью катушками и двухполюсным ротором.
Схема электродвигателя с шестью катушками и двухполюсным ротором.

Далее я начал прорисовку идеи в 3d-cad T-Flex. К этому времени удалось раздобыть несколько постоянных магнитов, геометрия которых определила габариты ротора и как следствие габариты всего двигателя в последующем.

Так появилась примерно такая конструкция:

Первый разработанный вариант магнитной системы с катушками из сверхпроводника.
Первый разработанный вариант магнитной системы с катушками из сверхпроводника.

Пришлось потратить кучу времени на разработку рамки для катушки. Основная проблема заключалась в том, лента ВТСП фактически гнется только в одной плоскости, но допускает некоторое кручение. Сочетание этих двух факторов ограничивают траекторию по которой эта лента может быть уложена. Но в конце концов удалось начертить и напечатать на 3д принтере первый прототип рамки, на который удалось успешно закрепить ленту сверхпроводника.

Первая напечатанная рамка для намотки ленты из сверхпроводника
Первая напечатанная рамка для намотки ленты из сверхпроводника

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

Для отладки двигателя, на рамки вместо ленты ВТСП, была намотана обмоточная медная проволока. На каждую рамку уместилось 4 витка (по рекомендации человека который занимался расчетами двигателя необходимо было 5 витков).

Медный аналог двигателя для проверки работоспособности двигателя.
Медный аналог двигателя для проверки работоспособности двигателя.

Все было собрано, спаяно и подключено к регулятору скорости ESC Castle Talon 90.

Первая попытка запуска медного аналога показала очевидное - низкий крутящий момент. В первоначальный момент контроллер начинает принудительно вращать ротор без обратной связи. После того как начинает работать обратная связь контроллер может нормально управлять двигателем. Но из-за отсутствия магнитопровода обратная связь по обратной ЭДС была затруднена, а низкий крутящий момент и момент инерции ротора приводили к тому, что ротор не успевал раскручиваться и совершал колебательные повороты около некоторого равновесного положения.

Но после принудительной раскрутки ротора двигатель запустился. И начал вращаться так лихо, что у меня возникло опасение, что напечатанный ротор разлетится на сегменты и магниты как шрапнель полетят в стороны. Тогда удалось намерять 14 тыс.об./мин и замечена ещё одна особенность: на больших оборотах момент был больше чем на малых. В следующем эксперименте я поднял входное напряжение до с 12 В до 24 В и тогда двигатель начал самостоятельно запускаться.

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

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

Разрез двигателя в вертикальной плоскости. Стрелками показано направление движения газообразного азота. (Термоизоляция корпуса не показана)
Разрез двигателя в вертикальной плоскости. Стрелками показано направление движения газообразного азота. (Термоизоляция корпуса не показана)

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

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

Передняя опора ротора была напечатана из PLA-пластика на 3д принтере и в неё вставлялся керамический подшипник. Задняя опора также напечатана 3Д принтере и также с подшипником.

Комплект рамок с намотаным сверхпроводником и латунные токовыводы токарно-ручной работы.
Комплект рамок с намотаным сверхпроводником и латунные токовыводы токарно-ручной работы.

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

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

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

Корпус ротора в развале :(
Корпус ротора в развале :(

Корпус ротора был напечатан заново. После установки на вал и запрессовки подшипников корпус был обклеен стеклонитью на циакрине.

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

Было решено ставить на самолет и лететь.

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

Было решено не заморачиваться с автопилотом, а ставить радиоуправление. Итоговый вес самолета оказался в районе 3,6 кг.

У меня уже был опыт использования аппарата такой массы и на обычном бесколлекторном электрическом двигателе с резиновой катапультой этот самолет взлетал и летал продолжительное время и поэтому я решил что полет возможен (https://youtu.be/XcgAAbhTyxM).

Договорился с пилотом, выехал в поле и в итоге полет не получился.

Этому провалу сопутствовало несколько факторов:

  1. Большой вес самолета при низкой мощности двигателя. Резино-катапульта разогнала самолет до начальной скорости, но низкая мощность двигателя и резкий набор высоты сразу после взлета привел к падению скорости и как следствие к падению самолета.

  2. Некачественная аэродинамика. Стандартный фюзеляж планера весьма "пухлый". Также аэродинамичности не прибавляет торчащий кусок силового шпангоута к которому крепился двигатель и две вертикальные плоскости в задней части фюзеляжа образованные срезом и задней стенкой двигателя.

  3. Есть ещё один фактор который по своему мог влиять на работу двигателя. Дело в том что с наружи двигатель был покрыт алюминиевой лентой на самоклеящейся основе. А учитывая небольшое расстояние между ротором и стенкой переменные магнитные поля при вращении ротора создают противо ЭДС в тонком слое фольги. И при увеличении оборотов этот эффект только усиливается (демонстрация данного эффекта на примере колебания постоянного магнита над алюминиевой плитой).

Решения были следующие:

Чтобы снизить массу самолета все детали были свешаны, измерены и создана весовая модель самолета. В итоге было решено переставить двигател\\ь ещё ближе к передней части самолета. Убрать две АКБ общей массой 1000 г. Вместо неё будет установлена одна батарея массой примерно 300 г. Для соблюдения балансировки батарею должна быть выдвинута ещё вперед на 150 мм и для этого требовался новый фюзеляж.

Весовая модель самолета. Сверху старая модель, снизу модель с новым фюзеляжем и новой АКБ
Весовая модель самолета. Сверху старая модель, снизу модель с новым фюзеляжем и новой АКБ

Новый фюзеляж так-же должен улучшить аэродинамику самолета.

Потратив ещё несколько дней. Фюзеляж был спроектирован, вырезан на станке ЧПУ. Обклеен стеклотканью и покрашен.

Изготовление нового планер на фрезерном станке с ЧПУ
Изготовление нового планер на фрезерном станке с ЧПУ
Примерка нового фюзеляжа.
Примерка нового фюзеляжа.
Предварительная сборка самолета
Предварительная сборка самолета

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

Помимо фюзеляжа были вырезаны силовые шпангоуты для усиления и подставка под самолет с новым фюзеляжем.

Подставка под самолет
Подставка под самолет

Все было собрано и снова готово к полету.

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

Печаль.

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

Но самое важно что корпус из напечатанного пластика уже не подлежал восстановлению. На этот момент доступа к 3Д принтеру у меня не было.

Но был фрезерный станок ЧПУ и так настала очередь плана Б.

План «Б»

Было решено сделать корпус двигателя из пеноплекса на фрезерном станке с ЧПУ. В двигателе напечатанном на 3Д принтере пластик выступал как прочная оболочка обеспечивающая прочность и герметичность емкости, а поверхность теплоизолировалась криогелем толщиной 5 мм.

В новой конструкции внешний корпус статора изготовлен из пеноплекса. Он же должен обеспечивать герметичность,теплоизоляцию и частичную прочность (прямтаки ТРИЗ). Для повышения прочности корпуса снаружи пеноплекс обклеивался стеклотканью на эпоксидной смоле. В передней части двигателя также крепился силовой шпангоут из 2 мм стеклотекстолита. Внутри проходила труба для ротора и в верхней части емкости трубка для отвода газообразного азота. Обе трубки изготовлены из стеклоткани на эпоксидной смоле ЭТАЛ-Карбон Light.

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

Разрез двигателя с корпусом из пеноплекса
Разрез двигателя с корпусом из пеноплекса

Переднюю опору пришлось доработать так как в новой конструкции она вставлялась со стороны винта.

Криостат с корпусом из пеноплекса в процессе изготовления.
Криостат с корпусом из пеноплекса в процессе изготовления.

Полученная конструкция была собрана и готова к проверке.

В этот раз я решил сделать предварительное опробование на стенде без выезда в поле. И в процессе проверок двигатель сгорел опять.

Снова печаль.

Катушка перегорела полностью.
Катушка перегорела полностью.

Спустя два дня, моря разочарования, литра пива и 4 литров отборного чая решил предпринять последнюю попытку создать двигатель на сверх проводниках. Настала очередь плана В.

План «В»

Доработанная конструкция двигателя
Доработанная конструкция двигателя

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

За неделю восстановлен медный аналог, для предварительной проверки, отработки и настроек контроллера.

Двигатель ВТСП, который я до этого собирал и испытывал обозначен как №1. Он был разобран на отдельные элементы. Перегоревшие две катушки были заменены. Обмотки были заново скоммутированы. Была внедрена новая конструкция токовыводов.

Статоры в процессе изготовления. По порядку слава медный аналог, по центру новые катушки статора для двигателя №2, справа восстановленные катушки для двигателя №1
Статоры в процессе изготовления. По порядку слава медный аналог, по центру новые катушки статора для двигателя №2, справа восстановленные катушки для двигателя №1

Для медного аналога были выпилены стенки спереди и сзади, Чтобы двигатель можно было крепить аналогично криогенному. Два криогенных двигателя собирались параллельно с таким расчетом, чтобы большую часть настроек и испытаний сделать на №1, а окончательную настройку и полет выполнить на №2.

Обклеивание криостатов стеклотканью с эпоксидной смолой
Обклеивание криостатов стеклотканью с эпоксидной смолой

Ротор для всех трех двигателей был один.

Готовые статоры всех двигателей и единый ротор для них.
Готовые статоры всех двигателей и единый ротор для них.

Также на станке ЧПУ сделал простой стенд для измерения тяги и проверена его работа на обычном бесколлекторном электродвигателе.

Стенд ля проверки двигателя и отработки параметров электронного контроллера управления частотой вращения двигателя
Стенд ля проверки двигателя и отработки параметров электронного контроллера управления частотой вращения двигателя

После склейки и сборки, ещё два дня занимался настройкой различных контроллеров. Довольно неплохо подходил контроллер Marcus SL110, но после нескольких испытаний все же он сгорел. Есть особенность таких контроллеров. При запуске двигателя они могут давать длительные серии импульсов. При запуске обычного медного бесколлекторного двигателя ток ограничен сопротивлением проводов и обмоток, но в случает с двигателем ВТСП ток ограничивается сопротивлением одних подводящих проводов. По этой причине предположительно и сгорел данный контроллер.

В итоге трех дней настроек удалось получить рабочую схему из батареи литий-полимерных аккумуляторов напряжением 24 В и ёмкостью 3,5 А/ч, контроллера Castle Fenix Edge Lite, и двигателя на сверхпроводниках №2.

Все было проверено на стенде со штатной батарей и приемником радиоуправления, установлено на самолет и проверено ещё раз в сборе.

Испарения азота истекающие из под задней опоры двигателя
Испарения азота истекающие из под задней опоры двигателя

Итак настал май

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

Самолет с электродвигателем на сверхпроводниках готовый к полетам
Самолет с электродвигателем на сверхпроводниках готовый к полетам

И первый полет закончился аварийной посадкой.

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

Быстрый осмотр показал что внешне все целое и было принято решение сделать вторую попытку, но на меньшей мощности.

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

На это раз полет оказался успешным.

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

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

Что дальше? Можно улучшить конструкцию ротора для увеличения крутящего момента. Есть идея как сделать опоры в которых не будут замерзать и клинить подшипники. Сделать безрамочные катушки для улучшения охлаждения. Все это приведет к увеличению надежности, мощности и времени работы двигателя. А после этого прикрепить шасси, поставить на него автопилот для сбора информации об эффективности двигателя и сделать ещё несколько полетов..., но это уже будет второй или очередной полет - первый полет уже состоялся.

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


П.С. Хочу выразить благодарность сотрудникам компании «СуперОкс» за помощь, консультации и поддержку, а также за то что не пытались ограничить моё творческое безумие. Успехов Вам и хорошего финансирования на вашем долгом пути внедрения ВТСП в повседневную жизнь. Отдельна благодарность компании «SuperCam» г.Ижевск за предоставленный планер. Так-же спасибо компании «ЭНПЦ Эпитал» за консультации по эпоксидной смоле для жидкого азота.

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

Модели для скачивания

Adblock test (Why?)