...

суббота, 5 июля 2014 г.

Делаем из массивов объекты

image

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

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


Сразу оговорюсь что реализация рабочая для PHPStorm, в других IDE нужно вам проверять.



Часть примеров будет взята с потолка, часть будет взята из Instagram api.


Сначала примеры.


Примеры




Пример 1. Работаем с данными формы.



Мы имеем форму:

<form method="post" action="actionurl?route=myroute">
<label for="id">clientID:</label><input id="id" name="id" type="text" value="6041eee77ac94e6ba83fbcec61ad46c4"><br>
<label for="secret">ClientSecret:</label><input id="secret" name="secret" type="text" value="539141addd854a8c8d431465d6016590"><br>
<label for="tag">Tag:</label><input id="tag" name="tag" type="text" value=""><br>
<input type="submit" value="subscribe" name="subscribe"> | <input type="submit" value="unsubscribe" name="subscribe"> | <input type="submit" value="list" name="subscribe">
</form>


При отправке получаем $_POST. Мне всегда не нравилось такое использование (проверку существования ключей опускаю)

$id = $_POST['id];

Куда интереснее использовать все возможности нашего IDE и знать за ранее о переменных в нашем POST.

Создаем класс для формы:



/* @property $id string*/
/* @property $secret string */
/* @property $tag string*/
/* @property $subscribe string value from MyForm::const*/
class MyForm extends Post {
const SUBSCRIBE = 'subscribe';
const UNSUBSCRIBE = 'unsubscribe';
const LIST_ = 'list';
}




Ну и результат использования такого «класса»

image

Сразу видно с чем имеем дело.
Пример 2. Работаем с сессиями.



Нам нужно работать с сессиями максимально просто.

Наш класс:

/* @property $var1 string */
/* @property $var2 string */
/* @property $var3 string */
class MySession extends Session{
function __construct()
{
parent::__construct('mySession');
}
}


Класс для сессий (код ArrayClass будет в конце):



class Session extends ArrayClass {
function __construct($arrayName = 'session')
{
if(!isset($_SESSION[$arrayName]))
$_SESSION[$arrayName] = array();
parent::__construct($_SESSION[$arrayName]);
}

public static function init(){
session_start();
}
}




Это нам позволяет спокойно работать так:

$s = new MySession(); $s->var1 = 10;

Всё просто и прозрачно.
Пример 3. Instagram, json и сложные массивы



Нам нужно вызвать API. Делаем это примерно так:

$response = file_get_contents("http://ift.tt/1rzhVgc");
$r = new Response(json_decode($response, true));

$body = "Data count = ".$r->data->count();
$body.= "\n";
$tags = $r->data;
$data = new TagData($r->data->get(0));
$url = $data->images->standard_resolution->url;
$body.= $data->id;
$body.= "Image0 url: ".$url;
$body.= '<img src="'.$url.'">';




Код классов:

Немного Instagram API


/**
* Class Response
* @package instagram
*
* @property Meta $meta
* @property \ArrayClass $data
* @property Pagination $pagination
*
*/
class Response extends \ArrayClass {
public function getMeta(){
return new Meta($this->get('meta'));
}

public function getPagination(){
return new Pagination($this->get('pagination'));
}
}

/**
* Class Meta
* @package instagram
*
* @property integer $code
*/
class Meta extends \ArrayClass {

}

/**
* Class Pagination
* @package instagram
*
* @property integer $next_max_tag_id
* @property integer $next_max_id
* @property integer $next_min_id
* @property string $next_url
*/
class Pagination extends \ArrayClass {

}

/**
* Class TagsData
* @package instagram
*
* @property $attribution
* @property \ArrayClass $tags
* @property string $type
* @property $location
* @property $comments
* @property $filter
* @property integer $created_time
* @property $link
* @property $likes
* @property Images $images
* @property \ArrayClass $users_in_photo
* @property Caption $caption
* @property boolean $user_has_liked
* @property integer $id
* @property User $user
*/
class TagData extends \ArrayClass {

public function getImages(){
return new Images($this->get('images'));
}

public function getCaption(){
return new Caption($this->get('caption'));
}

public function getUser(){
return new User($this->get('user'));
}
}

/**
* Class Image
* @package instagram
*
* @property Image $low_resolution
* @property Image $thumbnail
* @property Image $standard_resolution
*/
class Images extends \ArrayClass {
function __get($name)
{
return new Image($this->$name);
}
}

/**
* Class Image
* @package instagram
*
* @property string $url
* @property string $width
* @property string $height
*/
class Image extends \ArrayClass {

}

/**
* Class Caption
* @package instagram
*
* @property integer $created_time
* @property string $text
* @property User $from
* @property int $id
*
*/
class Caption extends \ArrayClass{
public function getFrom(){
return new User($this->get('from'));
}
}

/**
* Class From
* @package instagram
*
* @property string $username
* @property string $website
* @property string $profile_picture
* @property integer $id
* @property string $full_name
* @property string $bio
*/
class User extends \ArrayClass{

}






Как это выглядит в IDE:

image


В 2х словах. Мы получаем json от Instagram и заворачиваем его в наши классы. На выходе получаем структуру классов и помощь от нашей IDE.


Ну а теперь сам ArrayClass:



class ArrayClass implements Iterator {
private $array;
//...
/**
* @param array|ArrayClass|json_string $array
*/
function __construct(&$array);
// ...
public function get($index);
// ...
public function count();
public function ar(){
return $this->array;
}

public function json(){
return json_encode($this->array);
}
}




Полный код ArrayClass


/**
* Created by PhpStorm.
* User: calc
* Date: 02.07.14
* Time: 0:57
*/

class ArrayClass implements Iterator {
private $array;
private $haveNext = false;
/**
* @param array|ArrayClass $array
*/
function __construct(&$array)
{
if($array === null) $array = array();
if($array instanceof ArrayClass){
$this->array = &$array->array;
}
else if(is_string($array)){
$this->array = json_decode($array, true);
}
else{
$this->array = &$array;
}

$this->rewind();
}

function __get($name)
{
return $this->get($name);
}

function __set($name, $value)
{
$this->array[$name] = $value;
}

function __isset($name)
{
return isset($this->array[$name]);
}

function __unset($name)
{
unset($this->array[$name]);
}

public function get($index){
if(isset($this->array[$index])){
if(is_array($this->array[$index])){
return new ArrayClass($this->array[$index]);
}
return $this->array[$index];
}
return null;
//return isset($this->array[$index]) ? $this->array[$index] : null;
}

public function count(){
return count($this->array);
}

public function ar(){
return $this->array;
}

/**
* (PHP 5 >= 5.0.0)<br/>
* Return the current element
* @link http://ift.tt/1rzhVgi
* @return mixed Can return any type.
*/
public function current()
{
return current($this->array);
}

/**
* (PHP 5 >= 5.0.0)<br/>
* Move forward to next element
* @link http://ift.tt/TKmd5C
* @return void Any returned value is ignored.
*/
public function next()
{
$this->haveNext = next($this->array);
}

/**
* (PHP 5 >= 5.0.0)<br/>
* Return the key of the current element
* @link http://ift.tt/1rzhTF9
* @return mixed scalar on success, or null on failure.
*/
public function key()
{
return key($this->array);
}

/**
* (PHP 5 >= 5.0.0)<br/>
* Checks if current position is valid
* @link http://ift.tt/TKmaqL
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
{
return $this->haveNext;
}

/**
* (PHP 5 >= 5.0.0)<br/>
* Rewind the Iterator to the first element
* @link http://ift.tt/TKmaH1
* @return void Any returned value is ignored.
*/
public function rewind()
{
$this->haveNext = $this->array === array() ? false : true;
reset($this->array);
}

public function json(){
return json_encode($this->array);
}
}






Мы создаем обертку массиву и даем этой обертке пару интересных возможностей



  • Универсальный конструктор. Можно создать объект из массива (даже из $_POST, $_GET и т.д), из себе подобного ArrayObject, из json строки

  • метод get для получения значений по ключу, удобно для цифровых значений (0..n), можно использовать в цикле for($i) $a->get($i), для массивов, которые содержат и текстовые ключи.

  • Ну json() — просто завернуть в json


Вот что получаем на выходе:

image


Как вариант можно добавить проверки в дочерние классы.

Плюсы в том, что если вы через год залезете в свой или чужой код IDE вам даст подсказку и вам не придется перечитывать половину кода проекта, чтобы понять что за магические константы в $_GET['magic'] и подобных строчках кода.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


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

image

Это статья-мануал по скрипту резервного копирования, написанному мной. Скрипт написан на python для Linux. Кому интересно прошу под хабракат.


Возможности





  • Создание дифференциальных/полных копий папок

  • Создание дифференциальных/полных копий с файловой системы BTRFS

  • Создание дифференциальных/полных копий LVM томов

  • Создание снапшотов BTRFS

  • Ротирование бэкапов/снапшотов

  • Логгирование хода выполнения резервного копирования

  • Отправка email оповещений

  • Выполнение скриптов до/после резервного копирования


Установка




В /etc/apt/source.list добавить:

deb http://ift.tt/1seJWap precise soft



И выполнить в терминале:

apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 74C7B31B5F4E1715 && apt-get update && apt-get install py4backup



Обновление пакета выполняется командой:

apt-get update && apt-get upgrade py4backup



ИЛИ

Вручную скачать пакет командой:

wget http://ift.tt/1seJWar



и установить его:

dpkg -i ./py4backup_latest.deb



ИЛИ

Для дистрибутивов, отличных от Ubuntu/Debian выполнить:

git clone http://ift.tt/1seJYz7



И скопировать файлы ddd и py4backup в директорию с бинарными файлами (обычно /usr/bin), файл py4backup_lib.py в директорию библиотек python. Также потребуется поставить зависимости вручную. Необходим python 3.x, btrfs-tools (btrfs-progs), lvm2, rsync. В папке examples/ вы найдете примеры конфигурационных файлов. Их необходимо скопировать в /etc/py4backup/

Настройка




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

mv /etc/py4backup/py4backup.conf.example /etc/py4backup/py4backup.conf
mv /etc/py4backup/jobs.conf.example /etc/py4backup/jobs.conf




И откройте файл py4backup.conf на редактирование текстовым редактором.

Для boolean параметров допустимо использование True/False, yes/no или 1/0.

Отделять параметр от его значения можно символами '=' или ':'.

Каждый параметр должен находится в своей секции. Название секции пишется перед набором параметров в квадратных скобках ('[]')

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

Пример конфигурационного файла:


[MAIL]
send_mail_reports = True
login = login@test.com
passwd = password
sendto = recipient@test.com
server = mail.test.com
port = 25
tls = True

[DD]
bs = 4M
ddd_bs = 4096
ddd_hash = md5


[LOGGING]
logpath = /var/log/py4backup.log
enable_logging = True
log_with_time = True
traceback = False
command_output = True

[OTHER]
temp_snap_name = py4backup_temp_snap
host_desc = My Description
pathenv = /sbin:/usr/sbin


Рассмотрим параметры подробней:

[MAIL]: здесь определяются параметры отправки уведомлений через email.

send_mail_reports: включает/выключает отправку email отчетов после выполнения задания.

login: логин для входа на smtp сервер.

passwd: пароль для входа на smtp сервер.

sendto: получатели уведомления. Можно вписать несколько адресов через пробел.

server: доменное имя или IP адрес smtp сервера.

port: порт smtp сервера.

tls: включает/выключает использование TLS шифрования.


[DD]: здесь указываются параметры создания резервных копий с помощью программ DD и DDD.

bs: размер блока для программы DD (Используется для создания полных копий LVM томов). Можно указывать размер в байтах, килобайтах (k) и мегабайтах (M). Влияет на скорость создания копии. Оптимальное значение- 32M.

ddd_bs: размер блока для программы DDD (Используется для создания дифференциальных копий LVM томов). Можно указывать размер в байтах. Чем больше размер, тем больше места занимает дифференциальная копия, но тем быстрее она создается. Оптимальное значение- 4096.

ddd_hash: алгоритм хеширования блоков. Возможен выбор между md5, crc32 и None. MD5 сильнее нагружает процессор, чем crc32 и занимает больше места, но в случае использования md5 намного меньше шанс коллизий.

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


[LOGGING]: настройка ведения журнала заданий.

logpath: путь до журнала. Если вы используете не стандартное размещение журнала не забудьте поменять настройки logrotate.

enable_logging: включает/выключает ведение журнала.

log_with_time: включает/выключает добавление к каждой записи журнала даты и времени.

traceback: включает/выключает добавление traceback'ов в лог при ошибках. Полезно при отладке.

command_output: включает/выключает добавление в лог консольного вывода команд. Полезно при отладке.


[OTHER]: настройки, не вошедшие в другие разделы.

temp_snap_name: имя временных снапшотов. Используется при создании копии LVM томов или папок/файлов на файловой системе BTRFS. Рекомендуется не изменять без необходимости.

host_desc: текстовое описание хоста. Значение этого параметра будет добавлено в файл журнала и email отчет.

pathenv: значение этого параметра будет добавлено к переменной $PATH(если пройдет проверку). Если необходимо добавить несколько папок их необходимо разделить двоеточием (':') Например в Ubuntu для создания копий LVM томов при запуске py4backup через cron необходимо добавить в переменную $PATH папку /sbin. В данном случае путь указывается без последнего слеша (‘/’)


Задания




Общие сведения



Список заданий находится в файле /etc/py4backup/jobs.conf

Пример задания:


[mail-diff]
type = file-diff
sopath = server:/opt/
snpath =
dpath = /mnt/backup_dest/
dayexp = 30
prescript = bash /root/script1.sh
postscript = bash /root/script2.sh
include = test test2
exclude = tests*




Где:

[xxx]: уникальное имя задания.

type: тип задания. Подробности см ниже.

sopath: источник резервной копии. В типах file-full, file-diff в качестве источника можно указывать удаленные хосты.

snpath: где создать снапшот. Используется только типами btrfs-full, btrfs-diff и btrfs-snap

dpath: куда сохранять резервную копию. В типах btrfs-full, btrfs-diff, file-full, file-diff в качестве назначения можно указывать удаленные хосты.

dayexp: через сколько дней удалять старые резервные копии. Если установить -1 резервные копии не будут удаляться никогда.

prescript: скрипт, выполняющийся перед резервным копированием. Пайпы, конвейер и другие операторы bash не работают. Если требуется выполнять сложные команды сохраняйте их виде скрипта и запускайте его.

postscript: скрипт, выполняющийся после резервного копирования. Остальное аналогично параметру prescript.

include: что включать в резервную копию. Подробности см. в описании типов резервного копирования.

exclude: что исключать из резервной копии. Подробности см. в описании типов резервного копирования.

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



В каждом задании в параметре 'type' указывается тип резервной копии. Этот параметр влияет на схему рез. копирования и на функцию некоторых параметров.

Всего в py4backup есть 7 типов резервного копирования:


  • file-full

  • file-diff

  • btrfs-full

  • btrfs-diff

  • btrfs-snap

  • lvm-full

  • lvm-diff


Рассмотрим их поближе.


file-full



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

Особенности:

В переменной sopath и dpath можно указывать не только локальные папки, но и удаленные хосты. Например:

sopath = root@192.168.0.1:/home/admin/ или dpath = server:/home/admin. Во втором случае в файле ~/.ssh/config должна быть корректная запись. Используется авторизация по ключам (доп. инфо. см. в wiki вашего дистрибутива).

Нельзя указывать sopath и dpath удаленными хостами одновременно.

Значение указанное в include и exclude передаются rsync в виде опций --include= и --exclude=. Можно указывать несколько значений через пробел.
file-diff



Создает дифференциальную резервную копию от источника (sopath) и последней полной копией, найденной в папке назначения (dpath). Если полная копия не будет найдена выполнение задания завершиться ошибкой.

Список параметров аналогичен типу 'file-full'.
btrfs-full



Данный тип аналогичен типу 'file-full', но перед созданием резервной копии делается снапшот резервируемой директории и копия снимается уже со снапшота.

Для этого типа резервного копирования необходимо указание параметра snpath. В папке, указанной в snpath будет создан временный снапшот исходной папки (sopath). Причем указанный там путь должен находится на одной файловой системе с папкой, указанной в sopath. Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы. Список остальных параметров аналогичен типу 'file-full'.
btrfs-diff



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

Так же, как и для типа 'btrfs-full' необходимо, что бы папка для снапшота (snpath) находилась на одной файловой системе с исходной папкой (sopath).

Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы. Список остальных параметров аналогичен типу 'file-full'
btrfs-snap



Данный тип создает снапшоты от исходной папки, указанной в sopath в папку снапшотов, указанную в snpath.

Для данного типа не работают параметры exclude, include, dpath. Так же, как и для типа 'btrfs-full' необходимо, что бы папка для снапшотов (snpath) находилась на одной файловой системе с исходной папкой (sopath).

Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы.
lvm-full



Этот тип предназначен для создания полных копий LVM томов. Рассмотрим некоторые особенности данного типа. В параметре sopath указывается путь до Logical Volume Group (VG). Например:

sopath = /dev/main_vg/

По умолчанию скрипт сделает копию всех томов, находящихся в данном VG.

Параметр dpath указывает где сохранять резервную копию. Указывать удаленные хосты в качестве назначения резервной копии нельзя. Для того, что бы сделать копии только нужных томов можно использовать параметры include и exclude.

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

Параметр include указывает какие тома нужно включить в резервную копию. Имеет приоритет над exclude. Например:

exclude = all
include = mail root




сделает резервную копию только томов mail и root. А следующий пример сделает копию всех томов, кроме тома mail:

exclude = mail

lvm-diff



И последний (для версии 1.5) тип резервного копирования предназначен для создания дифференциальных копий LVM томов.

Скрипт ищет в папке назначения (dpath) последнюю полную резервную копию и если находит, создает дифференциальную копию между ней и снапшотом текущего состояния. В папке назначения при этом появятся 2 файла *-diff.dd и *-diff.ddm Они ОБА необходимы для восстановления.

Все параметры аналогичны типу lvm-full
Запуск



Запустить требуемые задания на выполнение очень просто.

Необходимо указать ключ –jobs (или -j) и после него указать имена необходимых заданий. Например:

py4backup --jobs backup_data backup_home backup_media

Все указанные задания выполнятся последовательно в порядке их следования в параметре --jobs. Также запуск скрипта возможен через cron, но помните, что переменное окружен cron может отличатся от пользовательского и может потребоваться указать пути до утилит rm, dd, rsync, btrfs, lvcreate, lvremove в переменной pathenv в конфигурационном файле.

Восстановление




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



Написанное ниже относится как к полным, так и дифференциальным резервным копиям, сделанным заданиями типа btrfs-full, btrfs-diff, file-full, file-diff. Восстанавливать резервную копию необходимо rsync с ключами -aAX. Например:

rsync -aAX /mnt/backup/home/2014-06-21-full/ /home/



или

rsync -aAX /mnt/backup/home/2014-06-22-diff/ /home/



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



Снапшоты, создаваемые типом btrfs-snap можно восстанавливать несколькими способами.


  • Как файловый бэкап, скопировав данные rsync (слишком долго и не интересно)

  • Примонтировав снапшот, вместо папки, которую необходимо восстановить. Этот способ мы и рассмотрим ниже.




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

BTRFS используется в качестве корневой файловой системы. С помощь скрипта создаются снапшоты папки /home и складываются в /snapshots_home. И вот настал день, когда нам необходимо восстановить папку /home из снапшота.

Первым делом необходимо освободить папку /home (переименовать или удалить ее).

Далее мы выбираем нужный нам снапшот (пусть это будет снапшот, за 2014-06-19) и создаем снапшот от него (да, да, снапшот снапшота):

btrfs subvolume snapshot /snapshots_home/2014-06-19 /home



Таким образом мы во первых сделали наши данные доступными для записи и обезопасили их. Даже когда скрипт согласно ротации удалит снапшот от 2014-06-19 наш свежесозданный снапшот будет цел.
Восстановление полных бэкапов LVM



Тут все совсем просто.

Необходимо создать новый LVM том, размера равного или больше резервной копии и скопировать на него резервную копию с помощью dd.

Пример:

dd if=/backups/2014-06-19-old_volume-full of=/dev/main_vg/new_volume bs=32M

Восстановление дифференциальных бэкапов LVM



Для данного восстановления необходимо воспользоваться утилитой ddd, идущей в комплекте с py4backup.

Для восстановления ей необходимо указать опцию –restore, ключ -s с путем до файла БЕЗ РАСШИРЕНИЯ, ключ -r с указанием места восстановления (блочное устройство или файл). ddd запоминает путь до полной резервной копии, но если она была перемещена необходимо вручную указать ей новый путь. Сделать это можно с помощью ключа -f.

Пример:

В папке /backup находятся резервные копии:


root@virtserver / # ls /backup/
2014-06-18-volume-full
2014-06-19-volume-diff.dd
2014-06-19-volume-diff.ddm




И мы хотим восстановить резервную копию за 2014-06-19 на устройство /dev/main_vg/volume Для этого выполним команду:


ddd --restore -s /backup/2014-06-19-volume-diff -r /dev/main_vg/volume




Предположим полная копия была перемещена в папку /backup_old/:


ddd --restore -s /backup/2014-06-19-volume-diff -r /dev/main_vg/volume -f /backup_old/2014-06-18-volume-full




После восстановления ddd выведет список поврежденных блоков с указанием файла, где находится поврежденный блок. Запись full23 указывает на повреждение блока номер 23 в файле полной копии, а запись diff24 на повреждение блока 24 в дифференциальной копии.

Tips & Tricks




Здесь я расскажу о некоторых не очевидных момента и вариантах использования скрипта.

Заключение




Отказ от ответственности: автор скрипта не несет ответственности за действие или бездействие программы, повлекшее потерю или повреждение данных.

В скрипте ЕСТЬ ошибки и я буду благодарен за багрепорты (особенно с traceback’ами и консольным выводом команд).

Данный мануал актуален для версии 1.5.3.

Связаться со мной можно по email, адрес larrabee@nixdi.com или через хабр.

Исходный код на github.

Пакеты в репозитории.

Спасибо за прочтение и буду благодарен за комментарии.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Кикстартер для Krita: новый функционал для Krita 2.9

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

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


Лайн-арт — стабилизация пера с задержкой




Разработчики наконец-то позаботились об удобстве работы художников. Зачастую, процесс создания изображения состоит из следующих этапов:

1) Создание эскиза грубой кистью

2) Аккуратная обводка контуров изображения тонкой, но жесткой кистью.

3) Заливка цветом полученных областей.




Иллюстрация Wolthera van Hövell tot Westerflier


Для облегчения второго этапа, был добавлен новый тип сглаживания линий: «Стабилизатор с задержкой». Он позволяет вести линии ровно и при этом контролировать параметры кисти: силу нажатия, поворот и т.д. Да, в Крите и до этого присутствовало взвешенное сглаживание линий, однако новый метод работает абсолютно иначе. Видео от Павла Гераськина лучше всего демонстрирует, как работает эта функция:




(многие могу заметить сходство с аналогичными инструментами Blender и zBrush)


Прямые линии




Более того, разработчики наконец-то переделали инструмент рисования прямых линий. Теперь он не только имеет предварительный просмотр отрисованной линии и доступен по нажатию клавиши 'V', но и позволяет контролировать силу нажатия во время ведения пера вдоль прямой. В этом заключается основное отличие от функционала, предлагаемого в MyPaint. Видео от Тимоти Гие:


Быстрое редактирование масок и выделений, изолированный режим клавишей Alt




Так уж сложилось, что все выделения в Крите представляют собой всего лишь узлы дерева слоев. Каждое выделение — это узел «Маска выделения» (Selection Mask). Каждая маска определяет выделенную область при редактировании родительского узла и всех его потомков. Такую маску можно редактировать не только специальными инструментами, на ней можно рисовать обычными кистями, фильтровать, заливать заливками и градиентами. База кода для этого функционала существовала уже давно но пользоваться ей было не совсем удобно: глобальные выделения были недоступны в виде масок и было невозможно быстро переводить их в изолированный режим.

Теперь эти проблемы решены. При включении опции «Отображать маски глобальных выделении» (Show Global Selection Masks) маска глобального выделения будет показана в дереве слоев и ее можно будет редактировать любыми доступными средствами.


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


Вместо заключения




Эти нововведения — лишь только начало тех плюшек, которые запланированы для Криты 2.9. К концу года, разработчики реализуют от 12 до 24 новых функций! Сейчас же новый функционал можно испробовать, скачав пакеты для Windows или установив Krita Lime под Linux.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Хитрая канистра

Написать этот пост меня подтолкнул случай с моим знакомым, который «попал» на серьезный ремонт своей старенькой Газели после заправки автомобиля сомнительным бензином. Сам факт поломки двигателя плохим топливом был подтвержден в автосервисе и, конечно, можно было бы начать рутинные разборки с автозаправкой, но в большинстве случаев это не приводит к результату. Что же делать? Попытаемся проанализировать проблему с физической и технической точки зрения.

image


Анализ проблемы



Бензин, как известно, достаточно сложное вещество и не имеет какой-то единой химической формулы. Это смесь легких углеводородов с различными добавками, которые определяют качество и характеристики бензина. На качество топлива в процессе производства влияют примерно 2 миллиона факторов, начиная от качества оборудования, исходного сырья, заканчивая квалификацией персонала на производстве, которые подчас интуитивно управляют процессом, как пекари и повора) Очень точное сравнение. Одни варят суп, другие варят бензин. Именно варят. Ведь это химическое производство и технолог там самая важная персона. Но даже если на НПЗ, которых в России, кстати, немного (порядка 30 крупных против более 200 в США) производят приемлемый по качеству продукт, это еще не гарантия того, что он попадет с этим качеством к вам в бак. Проблемы с ухудшением качества начинаются в процессе перевозки, перекачки и доставки на конечную заправку. Бензин достаточно хлопотное вещество, требующее пристального контроля. Перепады температуры, атмосферного давления, грязные цистерны, водный конденсат и даже микробиология (!) ухудшают бензин, превращая его в настоящую головную боль для водителя. Также на качество бензина влияет наличие примесей. Поэтому в процессе производства его необходимо очищать. Это очень затратный и долгий процесс. Однако требование к качеству топлива возрастает и производители вынуждены это делать. В зависимости от степени очистки и качества присадок, автомобильное топливо делят по экологическому уровню, в соответствии с европейской классификацией: Евро-2 (серы не более 0,005%), Евро-3 (серы до 0,0015%), Евро-4 (серы до 0,0005%) и Евро-5 (серы до 0,0001%). Конечно, помимо серы в бензине имеются и другие примеси, а именно свинец, мышьяк, фосфор, алкилнитраты, нафталин и многое другое.
То бензин, а то дети...



В отличие от героя известного фильма «Джентльмены удачи », который бензин ослиной мочой разбавлял, сейчас существует множество других изящных химических способов улучшить разбодяжить топливо. Что только не добавляют в бензин, переводя его из одной марки, в другую. Начиная от сложных и дорогих химических веществ, заканчивая простым ацетоном, переводящий на несколько часов бензин из АИ-92 в 95-й. Как раз на время, чтобы водитель подальше отъехал от заправки. В одной томской смешной газете вакансий я увидел объявление такого содержания


На автозаправку требуется заведующий с высшим химическим образованием. Опыт работы обязателен.





Что делать специалисту на заправке с высшим химическим образованием? Не понимаю.
Экспресс контроль топлива



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



Поход к друзьям химикам не только освежил воспоминания былой юности (ведь выпить хорошей водки можно только в приличной химической лаборатории), но дал пищу для размышлений. Оказалось, что существует громодье методик определения спектрофотометрическими методами примесей в бензине, начиная от фосфора и свинца, заканчивая водным конденсатом. Выяснилось, также, что многие производители добавляют фирменные добавки, которые делают бензин ярких цветов, тем самым стараясь оградить себя от подделок. Не маловажным было то, что многие методики очень хорошо работают ближе к ультрафиолетовому диапазону, однако анализ можно проводить в видимом и ИК диапазонах, но это хуже. Также было полезно узнать, что качественный бензин должен быть всегда оптически прозрачным и что в большинстве случаев несанкционированного добавления посторонних веществ бензин «слегка» мутнеет, что тоже явно указывает на фальсификацию. Проанализировав указанную информацию, я начал поиск недорогих устройств, способных решить эту задачу.
Чего только не сделано для Ардуино...



После часового прибывания в Интернете я натолкнулся на один забавный шилд. Grove — I2C Color Sensor

image

Не смотря на низкую цену (10$) шильдик имеет приличные характеристики. На борту 16 сверхчувствительных фотодиодов (4 блока по 4 шт.), которые обслуживают 16 разрядные АЦП. В каждом блоке один фотодиод «чистый», т.е. не закрыт соответствующим фильтром. Таким образом, сенсор может работать как анализатор спектра, тем более, что на борту есть выключатель осветительного светодиода, которым можно управлять и программно. Его, кстати, легко перепаять на более «синий», что необходимо для анализа бензиновых примесей. Сенсор имеет достаточно приличные спектральные характеристики. При этом это вовсе не игрушка. В datasheet описываются различные применения. Вплоть до медицины и научных исследований. Кстати, сенсор очень стабилен и показал великолепные результаты по повторяемости измерений. Запуск на Arduino UNO занял 5 минут. Пример кода программы здесь. Думаю, что на основе этого сенсора можно создать немало интересных устройств, а имея набор «эталонных» веществ, к примеру, образцов действительно качественного бензина (где вот только взять)) можно достаточно просто «обучить» конечное устройство сравнивать образцы с заправок с эталонами. Ну а если не бензин? То можно, наверное, делать умные стаканы для определения качества напитков (молока, вина, соков), хотя такие устройства уже появляются.


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


Пожалуйста, выскажите свое мнение о следующем устройстве


image

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


Канистра имеет объем 5 или 10 литров. Для проверки качества бензина необходимо на заправке купить топливо и налить его в канистру. Затем, нажав на кнопку «Проверить», дождаться в течение 10 секунд результатов теста. На экране Вы увидите следующую информацию


1. Количество налитого бензина (в литрах с указанием недолива/перелива в %)

2. Тип бензина ( A-92, 95, 98 и его марку)

3. Прозрачность – (мутный, прозрачный)

4. Качество бензина по 4 бальной шкале — (плохой, относительный, хороший, очень хороший)


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


802.11ad (WiGig) — дальность и полезность

802.11ad (WiGig) - дальность и полезность

На фоне объявления QCA (QuallcomAtheros — один из лидеров в Wi-Fi чипсетах) о покупке Wilocity (один из разработчиков WiGiG чипсетов) поднялась волна энтузиазма по поводу 802.11ad. В частности, возникла мысль построить на 802.11ad систему видеонаблюдения — ведь с 7Gbps пропускной способности можно снимать видео в высоком разрешении с большого числа камер! Я решил на быстую руку прикинуть, насколько это реально:

Посмотрим на RF:



  • Согласно Wiki, минимальный уровень сигнала для работы 802.11ad на минимальной скорости (385Mbps PHY на секундочку!) равен -68dBm.

  • Согласно вот этой статье (хорошая статья по 802.11ad, кстати), максимальная мощность передатчика 802.11ad составляет 10dBm. Это, кстати, в 10 раз меньше, чем в Wi-Fi.

  • Потери при распространении сигнала FSPL на всего лишь 10м для 60GHz (в которых работает 802.11ad) составляют 88dB (посчитано на одном из множества калькуляторов FSPL). Плюс, есть еще дополнительные потери из-за резонанса с молекулами кислорода, но давайте это опустим, этот резонанс важен при расстояниях в 100м и более.


Построив простую формулу бюджета мощности канала, получим, что для работы 802.11ad на минимальной скорости на расстоянии 10м нам нужно усиление в канале порядка 10dB.


Подробности и матан:


  • Бюджет: TXpower + TX/Rxgains – FSPL >= Rxmin. (без учета доп потерь)

  • Для нашего случая: 10dBm + Gains – 88dB >= -68dBm

  • Получаем Gains >= 88-68-10 = 10dB




Откуда же этим 10dB взяться? Либо из усиления антенн, либо от beamforming'а, который так радужно расписывают маркетологи от 802.11ac и 802.11ad. Однако, антенны мобильных устройств обычно отличаются отрицательным усилением, так что тут нам ловить нечего. Beamforming (как показали результаты 802.11ac) может выдать 3-5dB сверху. Т.е. на передающую антенну точки доступа остается 5-7dB.


Найти антенну на 5dBi не проблема, даже всенаправленную (а полезность направленных антенн на точке, сами понимаете, ограничена). Но только у такой антенны угол раскрытия в вертикальной плоскости будет около 15 градусов, что весьма немного, и весьма неудобно.


Так что, уже на расстоянии 10м работа 802.11ad проблематична.

Можно ли его использовать для стриминга видео? Да!



  • Стримить видео с ноутбука на внешний монитор или проектор на расстоянии 2-3м.

  • Стримить видео с планшета на телевизор без кабелей или переходников

  • Стримить видеофайлы на внешнее хранилище для бекапа




Стримить видео с камеры наблюдения? Ну, если от камеры до точки точно нельзя бросить провод… :) Вторым вариантом будет направленная антенна на камере с большим коэффициентом усиления (заявляются компактные направленные антенны для 60GHz с КУ 12-18dBi) — жизнь (и кислород) покажет, насколько это практично.

802.11ad (WiGig) - дальность и полезность


С другой стороны, такая малая дальность — сущее благо для домашнего пользователя: сосед за стеной наконец-то перестанет "забивать мой Wi-Fi своим Wi-Fi". Аналогично, у корпоративных админов WLAN появится шанс увидеть как вся BYODшная мелочь убирается из и без того загруженного диапазона 2.4ГГц (и заодно, не так быстро замусоривает 5ГГц), что тоже неплохо.


В общем, как говорят буржуи: "Right tools for the right job" (картинка кликабельна). Как видно из картинки, 802.11ad никогда и не задумывался для «дальнобойных» применений, так что никаких открытий мы по-сути не сделали. Однако, всегда будут находиться энтузиасты, пытающиеся использовать технологии не по назначению. Надеюсь, эта заметка поможет вам в общении с ними.


Что вы думаете по поводу 802.11ad?


802.11ad (WiGig) - дальность и полезность


Оригинал тут.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Финансы от пасьянсов поют романсы

image

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

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



Укус Суареса



Летние месяцы — время финансового затишья на рынке мобильных игр. Большинство населения Земли смотрит футбол и купается в речках.

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

Да, будущее прекрасно. Напомню, что прекрасное будущее обеспечивают для разработчиков бесплатных игр под iOS два сервиса

Я уже приводил формулу для расчета успешности приложения под iOS в 2014 году.


10000 загруженных копий игры должны приносить не менее $5 в календарные сутки



На основе этой формулы автор должен решать — тратить деньги на продвижение игры в ТОП 25 или насладится любимой женой и итальянской кухней.


Пасьянс Rio 2048



Предлагаю финансовый отчет по первой игре. Напоминаю, что это клон 2048 на треугольной доске. Наличие 3 направлений вместо двух существенно изменяет характер и топологию оригинальной игры.

На продвижение игры не было затрачено ни рубля. Но!

Благодаря публикации на Хабре и удачному названию игра Rio 2048 к сегодняшнему дню набрала необходимое число скачиваний — 11005

image


Каков же календарный доход от игры?

Смотрите на график, почтеннейшая публика


image


Если в стартовые дни приложение приносило желанные $5, то ныне — стабильные 25 рублей в сутки.


Всего приложение заработало за почти два месяца следующую сумму.

image


При этом приложение имеет прекрасные отзывы и за него не стыдно. Сам я не играю в Rio 2048. За неделю тренировок я понял, так сказать, общие принципы успеха и побывал в ТОП 5 лучших игроков.


Вывод



Продвигать приложение не имеет смысла. Успеха не будет с вероятностью 90 процентов.
Пасьянс Bricks Towers



Благодаря публикации на Хабре (2000 загрузок), платному обзору на 4PDA (1000 загрузок) и гнусным ухищрениям автора (2000 загрузок), игра Bricks Towers к сегодняшнему дню не набрала необходимое число скачиваний — 5906.

image


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

Всего приложение заработало за полтора месяца следующую сумму.


image


То есть, платная публикация на 4PDA окупилась — на обзор было затрачено 5000 рублей. Но, честно говоря, не выгодное это дело, парни.


Ладно, теперь самое главное, суточный доход.

На графике доход от игры за календарный месяц по площадке adMob.


image


Ага! Воскликнет внимательный читатель — нету $5, нету!


Пардон, я забыл про Chartboost.

Он тоже вносит свой вклад в ежедневный доход


image


Почему три графика? Это гнусное ухищрение автора — я выпустил три клона игры, для искусственного увеличения числа загрузок.

Суммарно три клона приносят более 3 долларов в день от полноэкранной рекламы Chartboost.


Таким образом, игра на 6000 загрузок приносит 8 долларов в день.

С этой игры я не ухожу. Не могу постичь тайны правильного собирания кирпичного пасьянса.

Мало того, подлец автор каждый день подбрасывает 4 новых расклада и публика со всего света пытается собрать их лучше других.


Особым мастерством выделяется некто IrBisoff.

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

Выяснилось случайно.

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


Однако, нет. От него пришло письмо с вопросом



— Какого черта, чувак? Я живу в центре Киева, а твой geoip сервис причисляет меня к Венгрии.



Прошу прощения, Украину отдал Венгрии не я.

Вот виновник



$ipAddress = $_SERVER['REMOTE_ADDR'];

$xml = file_get_contents('http://ift.tt/KJfS6f' . $ipAddress . '?k=E2g-ХРЕН ВАМ ПАРОЛЬ-3');
$ipCode = $xml;





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



С вероятностью 95 процентов игру Bricks Towers ждет успех.

Теоретически, я нашел дядю с $50000 для закупки траффика и попадания в US ТОП 25. Возможно, к октябрю-ноябрю (самое выгодное время для взлета игр) мы провернем с ним эту аферу. В этом случае, пожелайте ему удачи, парни.


Наиболее часто встречающиеся вопросы

Где, мля, версия под Android?

Андроид-версия на подходе. Лето, вино, футбол — черт их дери. Сроки сдвинулись.


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

Для примера график месячного дохода от всех игр на рекламной площадке Google.

image


Кроме того, iAd — он вдвойне эффективнее, чем adMob. Да, мы помним тебя, Стив!

Chartboost — приносит 60 процентов в сравнении с adMob.


Короче, пенсия неплохая.


Где ссылки на игры



This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Компания Airbus патентует кабину самолёта без окон

image

Кабина авиасимулятора Boeing 737-NG для обучения пилотов. На месте окон — компьютерные дисплеи.

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



Лобовое стекло самолёта должно выдерживать столкновение с птицами на скоростях в несколько сотен километров в час. Поэтому его делают очень толстым, а значит и тяжёлым. А это влечёт за собой соответсвтующее усиление и утяжеление каркаса кабины. Отказ от окон поможет сэкономить драгоценные килограммы. Кроме того, большие обзорные окна «ломают» аэродинамику носа самолёта. Если заменить их на несколько небольших иллюминаторов или убрать полностью, обводы авиалайнера станут гораздо ближе к оптимальным.


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


image


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


image

3D-проекция рельефа местности дополняет картинку на дисплее.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Путь «Луны-1»

2 января 1959 года в полет отправилась станция Луна-1. Это была первая станция достигшая второй космической скорости и первая станция пролетевшая мимо Луны. По данным с ее приборов было показано, что у Луны нет магнитного поля и был открыт солнечный ветер. Даже то, что она не попала в Луну из-за ошибки сейчас воспринимается как плюс. Благодаря этому она в целости и сохранности сейчас летит по своей орбите между Землей и Марсом.

Ну а я решил попробовать восстановить траекторию ее полета по архивным материалам.



Ученые и инженеры, что готовили сообщения ТАСС явно столкнулись с неожиданной проблемой. Как указывать координаты станции во время полета? Ее полет очень подробно освещался и требовалось так указать координаты, чтобы любой человек мог найти на небе точку, в которой находится земной посланник в космос. У астрономов принято показывать координаты по прямому восхождению и склонению (аналог широты и долготы на Земле), но здесь они плохо подходили. Они хороши только для указания очень и очень далеких объектов. В результате, было принято решение привязать координаты не к небу, а к Земле. И указывать над какой из точек станция будет в данный момент времени и на какой высоте.


Для контроля траектории и определения координат использовали автоматизированную систему, разработанную для определения траектории баллистических ракет


Развертка на поверхность Земли выглядела так:


image


А если собрать всю информацию из сообщения ТАСС можно выделить такие точки


3 января в


3 часа: 3 градуса 12 минут ю.ш и 108 градусов в.д. 100 000 км от Земли [1]

6 часов: 4 градуса 30 минут ю.ш и 63.5 градуса в.д. 137 000 км от Земли [3]

13 часов: 7 градусов 33 минуты ю.ш и 40 градусов з.д 209 000 км от Земли [4]

16 часов: 8 градусов 20 минут ю.ш и 86(85) градусов з.д 237 000 км от Земли

19 часов 8 градусов 57 минут ю.ш и 131(130)градус з.д 265 000 км от Земли[5]

21 час 9 градусов 18 минут ю.ш и 160 градусов з.д 284 000 км от Земли [6]


4 января в

0 часов: 9 градусов 45 минут ю.ш и 155 градусов в.д 311 000 км от Земли

3 часа: 10 градусов 7 минут ю.ш и 110 градусов в.д 336 600 км от Земли


В 5 часов 57 минут ракета прошла на минимальном расстоянии от Луны (5-6 тыс км) и стала спутником Солнца [7]. После чего ТАСС стал публиковать ее координаты в астрономических координатах. Хотя часть координат, как видно на схеме, были пересчитаны и в земные


5 января в 10 часов ее аккумуляторы сели и связь с ней прекратилась.


Немного комментариев. В квадратных скобках отмечены номера аналогичных точек на карте Земли.

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


Зная эти данные и скорость вращения Земли можно перестроить их в трехмерные координаты, а потом и визуализировать траекторию.


У меня, получилась такая траектория



Траектория полета очень велика и мне пришлось долго «играть» с коэффициентами для того, чтобы показать искривление траектории. Получилось вот такая картина



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


Немного обработав полученный результат, я нарисовал такую схему:



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Рендеринг меха при помощи алгоритма Shells and Fins

imageПривет, Хабр! Мой сегодняшний пост по программированию графики будет не таким объемным, как предыдущие. Почти в любом сложном деле иногда есть место несерьезному, и сегодня мы будем рендерить котиков. Точнее я хочу рассказать о реализации алгоритма рендеринга меха Shells and Fins (SAF) традиционно для Direct3D 11 и OpenGL 4. За подробностями прошу под кат.



Алгоритм рендеринга меха SAF, как нетрудно догадаться из названия, состоит из 2 частей: рендеринг «панцирей» (shells) и рендеринг «плавников» (fins). Возможно, некоторым покажутся забавными эти наименования, но они в полной отражают то, что создается алгоритмом для создания иллюзии ворсистой поверхности. Подробнее с реализацией алгоритма для Direct3D 10 можно ознакомиться в статье и демке NVidia, мою демку для Direct3D 11 и OpenGL 4 можно найти здесь. Проект называется Demo_Fur. Для сборки вам понадобятся Visual Studio 2012/2013 и CMake.

Алгоритм Shells and Fins




Мех состоит из огромного количества волосков, нарисовать каждый из которых в отдельности на текущий момент не представляется возможным в реальном времени, хотя определённые попытки были у NVidia. Для того, чтобы создать иллюзию ворсистой поверхности применяется технология, чем-то напоминающая воксельный рендеринг. Заготавливается трёхмерная текстура, представляющая собой небольшой участок меховой поверхности. Каждый воксель в ней определяет вероятность прохождения ворсинок через себя, что с графической точки зрения определяет значение прозрачности в той или иной точке при рендеринге. Такую трехмерную текстуру можно сгенерировать (один из способов описан здесь). Возникает закономерный вопрос, как эту текстуру рендерить. Для этого вокруг геометрии рисуются «панцири», т.е. копии исходной геометрии, формируемые путем масштабирования этой геометрии на небольшие значения. Получается своеобразная матрешка, на каждый слой которой накладывается слой из трехмерной текстуры меха. Слои рисуются последовательно с включенным альфа-блендингом, что в результате дает некоторую иллюзию ворсистости. Однако, этого не достаточно, чтобы материал напоминал мех. Для достижения цели необходимо выбрать правильную модель освещения.

Мех относится к категории ярко выраженных анизотропных материалов. Классические модели освещения (например, модель Блинна-Фонга) рассматривают поверхности как изотропные, т.е. свойства поверхности не зависят от ее ориентации. На практике это означает, что при повороте плоскости вокруг ее нормали характер освещения не меняется. Модели освещения такого класса для вычисления затененности используют величину угла между нормалью и направлением падения света. Анизотропные модели освещения используют тангенты (вектора перпендикулярные нормалям, которые вместе с нормалями и бинормалями образуют базис) для вычисления освещенности. Подробнее про анизотропное освещение можно прочитать здесь.

Анизотропное освещение вычисляется отдельно для каждого слоя меха. Значения тангента в той или иной точке поверхности определяется при помощи карты тангентов. Карта тангентов формируется практически так же как широко известная карта нормалей. В случае текстуры меха вектором тангента будет являться нормализованное направление ворсинки. Таким образом, трёхмерная текстура меха будет содержать 4 канала. В RGB будет храниться упакованный вектор тангента, в альфа-канале будет содержаться вероятность прохождения ворсинки через эту точку. Добавим к этому учет самозатенения меха и получим достаточно реалистично выглядящий материал.

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

Реализация на Direct3D 11 и OpenGL 4




Реализации на обоих API в целом идентична, отличаются лишь незначительные детали. Рендеринг будем производить по следующей схеме:


  1. Рендеринг не покрытых мехом частей сцены. В моей демке для таких частей используется стандартная модель освещения Блинна-Фонга.

  2. Рендеринг «плавников». Вытягивание геометрии мы будем реализовывать при помощи геометрического шейдера. Чтобы понять необходимо ли вытягивать ребро между двумя полигонами, надо определить является ли это ребро внешним по отношению к объекту. Признаком этого будут являться значения углов между нормалями к полигонам и нормализованным вектором зрения. Если эти значения будут иметь разный знак, то ребро будет внешним, и значит его нужно вытягивать. Геометрические шейдеры в Direct3D и OpenGL умеют работать с ограниченным числом примитивов. Нам необходимо одновременно обрабатывать 2 смежных полигона с одним общим ребром. Для представления этой структуры минимально необходимо 4 вершины, что наглядно изображено на рисунке ниже слева.



    На правой части рисунка показано вытягивание общего ребра 1-2 и образование двух новых полигонов 1-5-6 и 1-6-2.

    Примитивом, который состоит из 4 вершин, является D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ (GL_LINES_ADJACENCY в OpenGL). Для того чтобы его использовать, нам необходимо подготовить специальный индексный буфер. Такой буфер достаточно просто построить, если есть данные о смежности треугольников в 3D-модели. Индексный буфер будет содержать группы по 4 индекса, описывающие 2 смежных треугольника.

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

    Геометрические шейдеры для вытягивания «плавников» приведены ниже под спойлерами.
    Геометрический шейдер на HLSL для Direct3D 11


    #include <common.h.hlsl>

    struct GS_INPUT
    {
    float4 position : SV_POSITION;
    float2 uv0 : TEXCOORD0;
    float3 normal : TEXCOORD1;
    };

    struct GS_OUTPUT
    {
    float4 position : SV_POSITION;
    float3 uv0 : TEXCOORD0;
    };

    texture2D furLengthMap : register(t0);
    SamplerState defaultSampler : register(s0);

    [maxvertexcount(6)]
    void main(lineadj GS_INPUT pnt[4], inout TriangleStream<GS_OUTPUT> triStream)
    {
    float3 c1 = (pnt[0].position.xyz + pnt[1].position.xyz + pnt[2].position.xyz) / 3.0f;
    float3 c2 = (pnt[1].position.xyz + pnt[2].position.xyz + pnt[3].position.xyz) / 3.0f;
    float3 viewDirection1 = -normalize(viewPosition - c1);
    float3 viewDirection2 = -normalize(viewPosition - c2);
    float3 n1 = normalize(cross(pnt[0].position.xyz - pnt[1].position.xyz, pnt[2].position.xyz - pnt[1].position.xyz));
    float3 n2 = normalize(cross(pnt[1].position.xyz - pnt[2].position.xyz, pnt[3].position.xyz - pnt[2].position.xyz));
    float edge = dot(n1, viewDirection1) * dot(n2, viewDirection2);

    float furLen = furLengthMap.SampleLevel(defaultSampler, pnt[1].uv0, 0).r * FUR_LENGTH;
    if (edge > 0 && furLen > 1e-3)
    {
    GS_OUTPUT p[4];
    p[0].position = mul(pnt[1].position, modelViewProjection);
    p[0].uv0 = float3(pnt[1].uv0, 0);
    p[1].position = mul(pnt[2].position, modelViewProjection);
    p[1].uv0 = float3(pnt[2].uv0, 0);
    p[2].position = mul(float4(pnt[1].position.xyz + pnt[1].normal * furLen, 1), modelViewProjection);
    p[2].uv0 = float3(pnt[1].uv0, 1);
    p[3].position = mul(float4(pnt[2].position.xyz + pnt[2].normal * furLen, 1), modelViewProjection);
    p[3].uv0 = float3(pnt[2].uv0, 1);

    triStream.Append(p[2]);
    triStream.Append(p[1]);
    triStream.Append(p[0]);
    triStream.RestartStrip();

    triStream.Append(p[1]);
    triStream.Append(p[2]);
    triStream.Append(p[3]);
    triStream.RestartStrip();
    }
    }





    Геометрический шейдер на GLSL для OpenGL 4.3


    #version 430 core

    layout(lines_adjacency) in;
    layout(triangle_strip, max_vertices = 6) out;

    in VS_OUTPUT
    {
    vec2 uv0;
    vec3 normal;
    } gsinput[];

    out vec3 texcoords;

    const float FUR_LAYERS = 16.0f;
    const float FUR_LENGTH = 0.03f;

    uniform mat4 modelViewProjectionMatrix;
    uniform sampler2D furLengthMap;
    uniform vec3 viewPosition;

    void main()
    {
    vec3 c1 = (gl_in[0].gl_Position.xyz + gl_in[1].gl_Position.xyz + gl_in[2].gl_Position.xyz) / 3.0f;
    vec3 c2 = (gl_in[1].gl_Position.xyz + gl_in[2].gl_Position.xyz + gl_in[3].gl_Position.xyz) / 3.0f;
    vec3 viewDirection1 = -normalize(viewPosition - c1);
    vec3 viewDirection2 = -normalize(viewPosition - c2);
    vec3 n1 = normalize(cross(gl_in[0].gl_Position.xyz - gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz - gl_in[1].gl_Position.xyz));
    vec3 n2 = normalize(cross(gl_in[1].gl_Position.xyz - gl_in[2].gl_Position.xyz, gl_in[3].gl_Position.xyz - gl_in[2].gl_Position.xyz));
    float edge = dot(n1, viewDirection1) * dot(n2, viewDirection2);

    float furLen = texture(furLengthMap, gsinput[1].uv0).r * FUR_LENGTH;

    vec4 p[4];
    vec3 uv[4];
    if (edge > 0 && furLen > 1e-3)
    {
    p[0] = modelViewProjectionMatrix * vec4(gl_in[1].gl_Position.xyz, 1);
    uv[0] = vec3(gsinput[1].uv0, 0);
    p[1] = modelViewProjectionMatrix * vec4(gl_in[2].gl_Position.xyz, 1);
    uv[1] = vec3(gsinput[2].uv0, 0);
    p[2] = modelViewProjectionMatrix * vec4(gl_in[1].gl_Position.xyz + gsinput[1].normal * furLen, 1);
    uv[2] = vec3(gsinput[1].uv0, FUR_LAYERS - 1);
    p[3] = modelViewProjectionMatrix * vec4(gl_in[2].gl_Position.xyz + gsinput[2].normal * furLen, 1);
    uv[3] = vec3(gsinput[2].uv0, FUR_LAYERS - 1);

    gl_Position = p[2]; texcoords = uv[2];
    EmitVertex();
    gl_Position = p[1]; texcoords = uv[1];
    EmitVertex();
    gl_Position = p[0]; texcoords = uv[0];
    EmitVertex();
    EndPrimitive();

    gl_Position = p[1]; texcoords = uv[1];
    EmitVertex();
    gl_Position = p[2]; texcoords = uv[2];
    EmitVertex();
    gl_Position = p[3]; texcoords = uv[3];
    EmitVertex();
    EndPrimitive();
    }
    }




  3. Рендеринг «панцирей». Очевидно, что для получения должного количества слоев меха геометрию необходимо нарисовать несколько раз. Для многократного рисования геометрии мы воспользуется аппаратным инстансингом. Для того чтобы в шейдере определить, какой именно слой меха рисуется, достаточно воспользоваться семантикой SV_InstanceID в Direct3D и переменной gl_InstanceID в OpenGL.

    Для освещения меха я использовал анизотропную модель Каджия-Кэя (Kajiya-Kay). Важной деталью стало использование специальной текстуры для задания длины меха. Такая текстура необходима для предотвращения появления длинного меха в неожиданных местах (например, вокруг глаз кота). Пиксельный и фрагментный шейдеры для расчёта освещения меха приведены ниже под спойлерами.
    Пиксельный шейдер на HLSL для Direct3D 11


    #include <common.h.hlsl>

    struct PS_INPUT
    {
    float4 position : SV_POSITION;
    float3 uv0 : TEXCOORD0;
    float3 tangent : TEXCOORD1;
    float3 normal : TEXCOORD2;
    float3 worldPos : TEXCOORD3;
    };

    texture2D diffuseMap : register(t1);
    texture3D furMap : register(t2);
    SamplerState defaultSampler : register(s0);

    float4 main(PS_INPUT input) : SV_TARGET
    {
    const float specPower = 30.0;

    float3 coords = input.uv0 * float3(FUR_SCALE, FUR_SCALE, 1.0f);
    float4 fur = furMap.Sample(defaultSampler, coords);
    clip(fur.a - 0.01);

    float4 outputColor = float4(0, 0, 0, 0);
    outputColor.a = fur.a * (1.0 - input.uv0.z);

    outputColor.rgb = diffuseMap.Sample(defaultSampler, input.uv0.xy).rgb;

    float3 viewDirection = normalize(input.worldPos - viewPosition);

    float3x3 ts = float3x3(input.tangent, cross(input.normal, input.tangent), input.normal);
    float3 tangentVector = normalize((fur.rgb - 0.5f) * 2.0f);
    tangentVector = normalize(mul(tangentVector, ts));

    float TdotL = dot(tangentVector, light.direction);
    float TdotE = dot(tangentVector, viewDirection);
    float sinTL = sqrt(1 - TdotL * TdotL);
    float sinTE = sqrt(1 - TdotE * TdotE);
    outputColor.xyz = light.ambientColor * outputColor.rgb +
    light.diffuseColor * (1.0 - sinTL) * outputColor.rgb +
    light.specularColor * pow(abs((TdotL * TdotE + sinTL * sinTE)), specPower) * FUR_SPECULAR_POWER;

    float shadow = input.uv0.z * (1.0f - FUR_SELF_SHADOWING) + FUR_SELF_SHADOWING;
    outputColor.rgb *= shadow;

    return outputColor;
    }





    Фрагментный шейдер на GLSL для OpenGL 4.3


    #version 430 core

    in VS_OUTPUT
    {
    vec3 uv0;
    vec3 normal;
    vec3 tangent;
    vec3 worldPos;
    } psinput;

    out vec4 outputColor;

    const float FUR_LAYERS = 16.0f;
    const float FUR_SELF_SHADOWING = 0.9f;
    const float FUR_SCALE = 50.0f;
    const float FUR_SPECULAR_POWER = 0.35f;

    // lights
    struct LightData
    {
    vec3 position;
    uint lightType;
    vec3 direction;
    float falloff;
    vec3 diffuseColor;
    float angle;
    vec3 ambientColor;
    uint dummy;
    vec3 specularColor;
    uint dummy2;
    };
    layout(std430) buffer lightsDataBuffer
    {
    LightData lightsData[];
    };

    uniform sampler2D diffuseMap;
    uniform sampler2DArray furMap;
    uniform vec3 viewPosition;

    void main()
    {
    const float specPower = 30.0;

    vec3 coords = psinput.uv0 * vec3(FUR_SCALE, FUR_SCALE, 1.0);
    vec4 fur = texture(furMap, coords);
    if (fur.a < 0.01) discard;

    float d = psinput.uv0.z / FUR_LAYERS;
    outputColor = vec4(texture(diffuseMap, psinput.uv0.xy).rgb, fur.a * (1.0 - d));

    vec3 viewDirection = normalize(psinput.worldPos - viewPosition);

    vec3 tangentVector = normalize((fur.rgb - 0.5) * 2.0);
    mat3 ts = mat3(psinput.tangent, cross(psinput.normal, psinput.tangent), psinput.normal);
    tangentVector = normalize(ts * tangentVector);

    float TdotL = dot(tangentVector, lightsData[0].direction);
    float TdotE = dot(tangentVector, viewDirection);
    float sinTL = sqrt(1 - TdotL * TdotL);
    float sinTE = sqrt(1 - TdotE * TdotE);
    outputColor.rgb = lightsData[0].ambientColor * outputColor.rgb +
    lightsData[0].diffuseColor * (1.0 - sinTL) * outputColor.rgb +
    lightsData[0].specularColor * pow(abs((TdotL * TdotE + sinTL * sinTE)), specPower) * FUR_SPECULAR_POWER;

    float shadow = d * (1.0 - FUR_SELF_SHADOWING) + FUR_SELF_SHADOWING;
    outputColor.rgb *= shadow;
    }







В результате мы можем получить таких котиков.


Производительность




Алгоритм SAF достаточно прост в реализации, однако может существенно усложнить жизнь видеокарте. Каждая модель будет рисоваться несколько раз для получения заданного количества слоёв меха (я использовал 16 слоев). В случае сложной геометрии это может дать существенную просадку производительности. В использованной модели кота покрытая мехом часть занимает примерно 3000 полигонов, следовательно, для рендеринга шкуры будет нарисовано порядка 48000 полигонов. При рисовании «плавников» используется не самый простой геометрический шейдер, что тоже может сказаться в случае высоко детализированной модели.

Замеры производительности велись на компьютере следующей конфигурации: AMD Phenom II X4 970 3.79GHz, 16Gb RAM, AMD Radeon HD 7700 Series, ОС Windows 8.1.

Среднее время кадра. 1920x1080 / MSAA 8x / полный экран





















API / Количество котиков125100
Direct3D 112.73615ms14.3022ms42.8362ms
OpenGL 4.32.5748ms13.4807ms34.2388ms



Итого, реализация на OpenGL 4 примерно соответствует реализации на Direct3D 11 по производительности на среднем и малом количестве объектов. На большом количестве объектов реализация на OpenGL работает несколько быстрее.

Заключение




Алгоритм SAF – один из немногих способов реализации меха в интерактивном рендеринге. Однако нельзя сказать, что алгоритм необходим подавляющему числу игр. На сегодняшний день схожий уровень качества (а, возможно, даже более высокий) достигается при помощи арта и умелых рук графического дизайнера. Комбинация полупрозрачных плоскостей с хорошо подобранными текстурами для представления волос и меха – стандарт современных игр, а рассмотренный алгоритм и его вариации, скорее, удел игр будущего.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Игровая станция МКИ: выставка игровых приставок на ВДНХ, где почти все можно потрогать


Думаю, на Хабре немало олд-скул геймеров, которые еще не забыли «теплого лампового Марио» и прочие старые и очень старые игры, а также игровые устройства, оставившие заметный след в эволюции игровых технологий.


Так вот, в Москве сейчас проходит выставка «Игровая станция МКИ», где выставлено более 30 старых игровых устройств, многие из которых можно опробовать. Есть даже первая домашняя игровая приставка Odyssey Magnavox, которая была выпущена в далеком 1972 году.


Кроме того, есть и другие раритетные экспонаты, например, первая приставка от Atari, Pong Madness. В 1975 году это был настоящий прорыв игровой индустрии. Годом позже появилась и первая приставка, которая поддерживала цветную графику, Fairchild Channel F.


Есть и нечто, уже сильно напоминающее ПК, это приставка Intellivision, появившаяся в 1979 году, производства компании Mattel. У этой приставки есть даже дополнительная клавиатура.



Само собой, нельзя было не включить в экспонаты Nintendo Entertainment System, появившуюся в 1983 году. Да, у нас эта консоль появилась в виде клона, со слоником на коробке. Называлась она Dendy.


В том же, 1983 году, появилась «Электроника Видеоспорт-3», советская приставка, где можно было тренировать реакцию :)



Некоторые экспонаты можно не только потрогать, но и играть. В частности, для экспериментов доступна Atari 2600, появившаяся в 1977 году, и Super Nintendo/SNES (1990). Более новые консоли, начиная с первой PlayStation, доступны все. Можно будет оценить и Xbox 360, Wii U, Ouya и PS 4.


Среди игр стоит выделить Donkey Kong, Galaxian, Super Mario Bros, Battle City, Super Sonic, Desert Strike, Doom 2, Fallout, Warcraft, Duke Nukem 3D, Quake, во все это можно поиграть.


Для сравнения организаторы выставки включили в экспонаты и PlayStation 4.


Выставка проводится при поддержке "Музея компьютерных игр" и "Музея советских игровых автоматов".


Выставка проходит с 4 июля по 15 августа в Политехническом музее (павильон № 26, ВДНХ).


Время работы выставки:

вт.-пт.: 10:00 — 20:00

сб.-вс.: 10:00 — 21:00

пн. – выходной день

Кассы закрываются за час до окончания работы выставки.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


ISEE-3. 17 лет сна

В этом году началась третья жизнь станции запущенной 36 лет назад. Станции с очень непростой судьбой.

В 1978 году как научная общественность так и простые любители отслеживали посадку на Венеру станций «Венера-11», «Венера-12» и Pioneer Venus Multiprobe, ожидали пролета станций серии Вояджер возле Юпитера и Пионера-11 возле Сатурна, а также любовались новыми цветными снимками Марса, что передавали орбитальный и спускаемый аппарат станций серии Викинг. На этом фоне, запуск станции для исследования солнечно-земных связей не привлек особого внимания.


image

ISEE-3 в сборочном цехе


ISEE-3 был запущен 12 августа 1978 года, а 21 октября он уже занял свое место недалеко от точки Лагранжа. Эта точка, находящаяся на линии, соединяющей центры Земли и Солнца, отстоит от Земли на 1,5 млн. км. Кстати, это был первый объект выведенный в эту точку.

В 1978 году Солнечная активность приближалась к максимуму и изучить Солнце в этот момент было очень интересно.


Аппарат успешно проработал 4 года, пока в 1982 г. руководство NASA приняло решение использовать ISEE-3 для исследования с пролетной траектории кометы Джакобини—Циннера, а в дальнейшем и кометы Галлея.


Это решение было вызвано исключительно финансовыми проблемами. В НАСА долго прорабатывался проект запуска специализированной станции для изучения комет, но получить под нее финансирование так и не удалось. Пришлось думать, можно ли обойтись уже имеющимися станциями. ISEE-3 для исследования комет подходил не очень идеально (его научное оборудование не предназначалось для этих целей), но это было лучше чем ничего. Благо и солнечная активность пошла на спад.


В результате станция получила новое имя Международный исследователь комет International Cometary Explorer (ICE)), а математики начали просчитывать орбиты по которой станцию можно было вывести к ее новой цели.


В 1983 году станция совершила пять маневров около Луны для использования ее гравитационного поля для увеличения скорости. Во время пятого маневра она прошла всего в 120 км от Луны. Минимальная точка приближения была как раз на Морем Спокойствия, где совершил посадку «Аполлон 11». В результате этих маневров аппарат получил общее приращение скорости в 2.2 км/с и отправился к своей новой цели — Комете Джакобини—Циннера.


image

Над Луной


11 сентября 1985 года эта станции впервые в мире пролетела через голову и хвост кометы. Она прошла на расстоянии 7863 км от ядра кометы Джакобини—Циннера с относительной скоростью 21 км/сек. На расстоянии ~ 187 тыс. км от кометы прибор для исследования волн в плазме зарегистрировал явления, напоминающие прохождение через фронт ударной волны солнечного ветра. Второй раз подобные явления были зарегистрированы на расстоянии~ 135 тыс. км от кометы, причем на этот раз сразу несколькими приборами. Затем АМС вошла в весьма турбулентную область. При входе в голову кометы турбулентность постепенно уменьшилась. При выходе из ионного хвоста наблюдалась обратная последовательность.


Станция без особых проблем пережила пролет, после чего отправилась к комете Галлея.


13 октября 1985 года она прошла на расстоянии 140 млн км от нее, а 28 марта 1986 года на расстоянии 30 млн км. Расстояние было куда больше, чем у советских станций «Вега-1», «Вега-2» или у европейского зонда «Джотто» но ее данные тоже были достаточно полезны.


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


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


Собственно, у НАСА на тот момент не было каких либо планов на этот зонд. В результате она просто подарила все права на него Смитсоновскому институту.


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

Всего нужно было собрать 125 тыс долларов. Эти средства были нужны на аренду времени на радиотелескопе Аресибо. И вот — успех. Нужная сумма было успешно собрана.


В 2014 году началась новая жизнь станции. 29 мая удалось восстановить связь со станцией, а 2 июля, после нескольких попыток, включить двигатели. Этим включением был изменен период обращения станции вокруг своей оси. На момент восстановления связи, аппарат вращался со скоростью 19.16 об/мин вместо штатных 19.75 об/мин.

Следующий шаг — коррекция траектории. Она запланирована на 8 июля.


image

Эти люди пробудили станцию от долгого сна.


Двигатели в последний раз включались 2 февраля 1987 г. Двадцать семь лет назад.

Аппаратом не управляли с 1997 по 2014 год. Семнадцать лет. Станция показала очень и очень завидную живучесть.


Что же будет дальше? И снова целью стала комета. Все надеются отправить ISEE еще к одной комете — комете Виртанена, которая приблизится к Земле в декабре 2018 года. Поживем — увидим!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] TJ Holowaychuk: Прощай Node.js

Я сражался с Node.js достаточно долго, что бы перестать получать от этого удовольствие, так что, по крайней мере пока, это мое официальное прощание! И, что еще важнее, я ищу людей, которые смогут поддерживать мои проекты!

Node отлично справляется с некоторыми вещами, но, к сожалению, это не самый подходящий инструмент для того, что мне сейчас интересно. Я все еще планирую использовать его для сайтов, но если вы хотели бы заняться поддержкой одного из моих проектов, дайте мне знать. Просто оставьте комментарий с вашим именем на гитхабе, ссылкой на npm и название проекта. Как обычно я прошу не привносить больших изменений в сущеcтвующие API: создать новый проект будет проще.


Я также продолжу поддержить Koa.


Святой Грааль




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

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

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


Я не говорю, что Go — «Святой Грааль», он не идеален, но для меня, среди существующих сейчас языков, Go — отличный выбор. Со временем, когда языки следующего поколения, такие как Rust и Julia найдут свое применение и повзрослеют, я уверен, что количество отличных решений увеличится.


Лично меня Go больше всего впечатлил скоростью развития языка. Весьма впечатляет увидеть, как разработчики стремятся к 2.0 и, как я слышал, не боятся поломать обратную совместимость, что отлично.


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


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


Почему Go?




Node все еще отличный инструмент и если вам все нравится, то нету повода переживать. Но если у вас есть хоть какие-то сомнения, не поленитесь оглянуться и посмотреть, что еще есть вокруг — я подсел на Go в первые же несколько часов использования его в производстве.

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


В момент первого знакомства с Go, стандартная библиотека показалась мне просто ужасной, в основном из-за того, что я привык к ультра-модульности в Node, и видел, как гниет стандартная библиотека в Ruby. Когда я начал больше работать с языком, я понял, что большая часть stdlib Go играет важную роль в разработке программного обеспечение: компрессия, JSON, буферизированный ввод/вывод, операции над строками и прочее. Большая часть этих API хорошо продуманы и мощны. Можно писать целые программы, в основном используя лишь стандартную библиотеку.


Сторонные библиотеки Go




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

There’s no central registry for Go packages, so you’ll often see 5 or 6 packages of the same name. This can be a little confusing at times but it has an interesting side-effect, you really have to review each one to determine the best solution. With Node there are usually canonical packages such as “redis”, “mongodb-native”, or “zeromq”, so you may stop right there and just assume those are the best ones.

Не существует единого репозитория для библиотек Go, поэтому часто можно встретить 5 или 6 разных пакетов с одним и тем же именем, что может иногда вызвать путаницу, но у этого есть и полезная сторона: нужно внимательно проверять каждый, чтобы выбрать самое лучшее решение. В Node есть общепринятые модули, вроде “redis”, “mongodb-native”, или “zeromq”, так что можно предположить, что это лучший вариант и остановить поиски.


Go или Node?




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

Обработка ошибок на мой взгляд реализована в Go гораздо лучше. Node позволяет нам принять решения для каждой отдельной взятой ошибки, но есть несколько вещей, где у Node все плохо:



  • У вас могут быть дублированные коллбэки

  • У вас может вообще не быть коллбеков (потерялись)

  • У вас могут быть ошибки вне диапазона

  • Эмиттеры могут получить несколько ошибочных событий

  • Отсутствующие события с ошибками отправляет все в лес

  • Часто непонятно, откуда берутся обработчики ошибок

  • Обработчики ошибок слишком многословны

  • Коллбеки — отстой




В Go если мой код выполнен, он выполнен, нельзя еще раз выполнить оператор. В Node это не так. Вам может показаться, что код закончил выполнение, ровно до того момента, пока библиотека случайно не запустит колбек несколько раз, или неправильно очистит обработчики, что вызовет повторое исполнение кода. С этим непросто разобраться, особенно когда код уже в продакшене, да и зачем? Другие языки не будут причинять вам эту боль.

Node в будущем




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

Наличие ошибок вроде “Error: getaddrinfo EADDRINFO”, по прошествии 4-5 лет, показывает расстановку приоритетов. Понятно, что можно пропустить такие мелочи, если вы концентрируете свои усилия на разработки ядра системы, но пользователи раз за разом напоминают про это а результатов все еще не видно. Мы обычно получаем ответы от людей из «элиты», заявляющих, что то, что есть сейчас идеально, но на самом деле все наобот.


Потоки (Streams) сломаны, работать с колбэками неудобно, сообщения об ошибках расплывчаты и непонятны, инструменты не вызывают восторга, соглашения сообщества вроде есть, но им еще далеко до того, что есть в Go. Учитывая все вышеперечисленное, существует набор задач, для которых я продлолжу использовать node: веб сайты, может какой-нибудь прототип или API. Если Node сможет починить свои основные проблемы, тогда у него есть неплозие шансы оставаться полузным, однако аргумент о предпочтении производительности удобству использования работает на очень хорошо, когда существуют решения, которые быстрее и удобней.


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


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.