/// <summary>
        /// https://docs.google.com/spreadsheets/d/1tB0OXeDGpwxoqD_LBXcdUXCvs6o8JoNoyXLs8IM4N0M/edit#gid=0
        /// </summary>
        /// <param name="writer"></param>
        protected override void pack(BinaryBitWriter writer)
        {
            bool extended = false;

            if (this.Version >= ProtocolVersion.v3__WeatherExtension)
            {
                extended = Forecast.Forecasts.Any(x => x.WindGust != null || x.CloudHeight != null || x.Visibility != null);
                writer.Write(extended);
            }


            writer.Write((uint)Forecast.TimeOffset + 12, 5); //[-12...14]h часовой пояс

            if (Forecast.Forecasts.Count != 16)
            {
                throw new ArgumentOutOfRangeException("Forecasts != 16");
            }

            i360Forecast prev = null;


            foreach (var ff in Forecast.Forecasts)
            {
                bool sameDay = prev?.Date.Date == ff.Date.Date;
                prev = ff;

                ///Дата не изменилась?
                writer.Write(sameDay);

                ///Если изменилась - сохраняем день от начала месяца
                if (!sameDay)
                {
                    writer.Write((uint)ff.Date.Day, 5);
                }


                writer.Write((uint)ff.HourOffset, 5);       //[0...24]h час
                writer.Write((uint)ff.Temperature + 70, 7); //[-70...50]C температура

                ///->

                double?_pressure = ff.Pressure;

                if (_pressure != null)
                {
                    _pressure -= 580;
                }

                writer.Write((uint?)_pressure, 8);  //[0...221] == [580...800]мм рт давление

                ///->

                int?_cloud = ff.Cloud;

                if (_cloud != null)
                {
                    _cloud = (int)Math.Round(_cloud.Value / 15d);
                }

                writer.Write((uint?)_cloud, 4); //[0...15] == [0...100]% облачность

                ///->

                double?_precipitation = ff.Precipitation;

                if (_precipitation.Value >= 0.05 && _precipitation.Value <= 0.1)
                {
                    _precipitation = 1;
                }
                else
                {
                    _precipitation = (int)Math.Round(_precipitation.Value * 4d);
                }

                writer.Write((uint?)_precipitation, 8); //[0...240] == [0...60]мм осадки

                ///->

                int?_windDirection = ff.WindDirection;

                if (_windDirection != null)
                {
                    _windDirection = (int)(_windDirection.Value / 45d);
                }

                writer.Write((uint?)_windDirection, 4); //[0...8] == [0...360] направление ветра

                ///->

                double?_windSpeed = ff.WindSpeed;

                if (_windSpeed != null)
                {
                    _windSpeed = (int)Math.Round(_windSpeed.Value);
                }

                writer.Write((uint?)_windSpeed, 6); //[0...60]м/с скорость ветра


                ///->

                writer.Write(ff.SnowRisk);  //[0...1] вероятность снега



                if (this.Version >= ProtocolVersion.v3__WeatherExtension && extended)
                {
                    ///->

                    int?cloudHeight = ff.CloudHeight;

                    if (cloudHeight != null)
                    {
                        cloudHeight = (int)(cloudHeight.Value / 70d);
                    }

                    writer.Write((uint?)cloudHeight, 8);  //[0..256] == [0..18000] м высота облаков

                    ///->

                    int?visibility = ff.Visibility;

                    if (visibility != null)
                    {
                        visibility = (int)(visibility.Value / 10d);
                    }

                    writer.Write((uint?)visibility, 10);

                    ///->

                    double?_gust = ff.WindGust;

                    if (_gust != null)
                    {
                        _gust = (int)Math.Round(_gust.Value);
                    }

                    writer.Write((uint?)_gust, 6); //[0...60]м/с порывы ветра

                    ///->
                }
            }

            if (this.Version >= ProtocolVersion.v4__WeatherExtension)
            {
                if (PointKey != null)
                {
                    writer.Write(true);
                    writer.Write((uint)PointKey, 4);
                }
                else
                {
                    writer.Write(false);
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="payload"></param>
        protected override void unpack(BinaryBitReader reader)
        {
            Forecast = new i360PointForecast();

            bool extended = false;

            if (this.Version >= ProtocolVersion.v3__WeatherExtension)
            {
                extended = reader.ReadBoolean();
            }


            Forecast.TimeOffset = (int)reader.ReadUInt(5) - 12;


            var dates     = new int[16];
            var forecasts = new List <i360Forecast>();
            int month     = 0;

            for (int j = 0; j < 16; j++)
            {
                try
                {
                    var forecast = new i360Forecast();

                    ///->

                    ///Дата не изменилась?
                    bool sameDay = reader.ReadBoolean();

                    ///Если изменилась - сохраняем день от начала месяца
                    if (!sameDay)
                    {
                        dates[j] = (int)reader.ReadUInt(5);
                    }
                    else
                    {
                        dates[j] = dates[j - 1];
                    }


                    if (j > 0 && dates[j] < dates[j - 1])
                    {
                        month++;
                    }


                    int hourOffset = (int)reader.ReadUInt(5);

                    forecast.Date = new DateTime(DateTime.Now.Year, DateTime.Now.Month + month, dates[j], hourOffset, 0, 0, DateTimeKind.Utc);

                    ///->

                    forecast.Temperature = (int)reader.ReadUInt(7) - 70;

                    ///->

                    uint?_pressure = reader.ReadUIntNullable(8);

                    if (_pressure != null)
                    {
                        forecast.Pressure = (int)_pressure + 580;
                    }

                    ///->

                    uint?_cloud = reader.ReadUIntNullable(4);

                    if (_cloud != null)
                    {
                        forecast.Cloud = Math.Min(100, (int)_cloud * 15);
                    }

                    ///->
                    ///
                    uint?_precipitation = reader.ReadUIntNullable(8);

                    if (_precipitation != null)
                    {
                        forecast.Precipitation = _precipitation / 4d;
                    }

                    ///->

                    uint?_windDirection = reader.ReadUIntNullable(4);

                    if (_windDirection != null)
                    {
                        forecast.WindDirection = (int)Math.Round(_windDirection.Value * 45d);
                    }

                    ///-->

                    forecast.WindSpeed = reader.ReadUIntNullable(6);

                    ///->

                    forecast.SnowRisk = reader.ReadBoolean();

                    ///->


                    if (this.Version >= ProtocolVersion.v3__WeatherExtension && extended)
                    {
                        ///->

                        uint?_cloudHeight = reader.ReadUIntNullable(8);

                        if (_cloudHeight != null)
                        {
                            forecast.CloudHeight = (int)(_cloudHeight.Value * 70d);
                        }

                        ///->

                        uint?_visibility = reader.ReadUIntNullable(10);

                        if (_visibility != null)
                        {
                            forecast.Visibility = (int)(_visibility.Value * 10d);
                        }

                        ///->

                        forecast.WindGust = reader.ReadUIntNullable(6);

                        ///->
                    }



                    forecasts.Add(forecast);
                }
                catch (Exception e)
                {
                    Debugger.Break();
                }
            }

            Forecast.DayInfos = forecasts.GroupBy(x => x.Date.Date).Select(x => new i360DayInfo()
            {
                Date = x.Key
            }).ToList();
            Forecast.Forecasts = forecasts;

            if (this.Version >= ProtocolVersion.v4__WeatherExtension)
            {
                if (reader.ReadBoolean())
                {
                    PointKey = (byte)reader.ReadUInt(4);
                }
            }
        }