Exemplo n.º 1
0
        public override void Serialize(XmlNode parentNode, String nodeName, XmlDocument document)
        {
            base.Serialize(parentNode, nodeName, document);
            bool exportAsPercentFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            GarminCategories category = Options.Instance.GetGarminCategory(BaseTarget.ParentStep.ParentWorkout.Category);
            float lastFTP = 0;

            if (category == GarminCategories.Biking)
            {
                GarminBikingActivityProfile profile = (GarminBikingActivityProfile)GarminProfileManager.Instance.UserProfile.GetProfileForActivity(category);

                lastFTP = profile.FTP;
            }
            else
            {
                exportAsPercentFTP = false;
            }

            // This node was added by our parent...
            parentNode = parentNode.LastChild;

            XmlAttribute attribute;
            XmlNode childNode;
            GarminFitnessBool exportAsPercentMax = new GarminFitnessBool(exportAsPercentFTP, Constants.PowerReferenceTCXString[1], Constants.PowerReferenceTCXString[0]);
            GarminFitnessUInt32Range lowValue = new GarminFitnessUInt32Range(0);
            GarminFitnessUInt32Range highValue = new GarminFitnessUInt32Range(0);

            // Type
            attribute = document.CreateAttribute(Constants.XsiTypeTCXString, Constants.xsins);
            attribute.Value = Constants.PowerRangeZoneTCXString[1];
            parentNode.Attributes.Append(attribute);

            if (exportAsPercentMax)
            {
                float baseMultiplier = Constants.MaxHRInPercentMax / lastFTP;

                lowValue.Value = (UInt32)Utils.Clamp(Math.Round(Zone.Low * baseMultiplier, 0, MidpointRounding.AwayFromZero), Constants.MinPowerInPercentFTP, Constants.MaxPowerInPercentFTP);
                highValue.Value = (UInt32)Utils.Clamp(Math.Round(Zone.High * baseMultiplier, 0, MidpointRounding.AwayFromZero), Constants.MinPowerInPercentFTP, Constants.MaxPowerInPercentFTP);
            }
            else
            {
                lowValue.Value = (UInt32)Utils.Clamp(Zone.Low, Constants.MinPowerInWatts, Constants.MaxPowerWorkoutInWatts);
                highValue.Value = (UInt32)Utils.Clamp(Zone.High, Constants.MinPowerInWatts, Constants.MaxPowerWorkoutInWatts);
            }

            // Low
            childNode = document.CreateElement("Low");
            parentNode.AppendChild(childNode);
            exportAsPercentMax.SerializeAttribute(childNode, Constants.XsiTypeTCXString, Constants.xsins, document);
            lowValue.Serialize(childNode, Constants.ValueTCXString, document);

            // High
            childNode = document.CreateElement("High");
            parentNode.AppendChild(childNode);
            exportAsPercentMax.SerializeAttribute(childNode, Constants.XsiTypeTCXString, Constants.xsins, document);
            highValue.Serialize(childNode, Constants.ValueTCXString, document);

            // Extension
            Utils.SerializeSTZoneInfoXML(BaseTarget.ParentStep,
                                         Options.Instance.PowerZoneCategory,
                                         Zone, document);
        }
Exemplo n.º 2
0
        public void Deserialize_V3(Stream stream, DataVersion version)
        {
            // Call base deserialization
            Deserialize(typeof(BasePowerTarget.IConcretePowerTarget), stream, version);

            IZoneCategory zones = Options.Instance.PowerZoneCategory;

            GarminFitnessString categoryRefID = new GarminFitnessString();
            categoryRefID.Deserialize(stream, version);

            GarminFitnessInt32Range zoneIndex = new GarminFitnessInt32Range(0);
            zoneIndex.Deserialize(stream, version);

            GarminFitnessBool dirty = new GarminFitnessBool(IsDirty);
            dirty.Deserialize(stream, version);

            if (categoryRefID == zones.ReferenceId && zoneIndex < zones.Zones.Count)
            {
                Zone = zones.Zones[zoneIndex];

                // Was the step dirty on last save?
                IsDirty = dirty;
            }
            else
            {
                Debug.Assert(zones.Zones.Count > 0);
                Zone = zones.Zones[0];

                // We can't find saved zone, force dirty
                IsDirty = true;
            }
        }
Exemplo n.º 3
0
        public override void Serialize(Stream stream)
        {
            base.Serialize(stream);

            IZoneCategory zones = Options.Instance.PowerZoneCategory;
            String zoneRefID = zones.ReferenceId;

            GarminFitnessString categoryRefID = new GarminFitnessString(zoneRefID);
            categoryRefID.Serialize(stream);

            GarminFitnessInt32Range zoneIndex = new GarminFitnessInt32Range(zones.Zones.IndexOf(Zone));
            zoneIndex.Serialize(stream);

            GarminFitnessBool dirty = new GarminFitnessBool(IsDirty);
            dirty.Serialize(stream);
        }
        public override void Serialize(Stream stream)
        {
            base.Serialize(stream);

            IZoneCategory zones = BaseTarget.ParentStep.ParentConcreteWorkout.Category.HeartRateZone;
            String zoneRefID = zones.ReferenceId;

            GarminFitnessString categoryRefID = new GarminFitnessString(zoneRefID);
            categoryRefID.Serialize(stream);

            GarminFitnessInt32Range zoneIndex = new GarminFitnessInt32Range(zones.Zones.IndexOf(Zone));
            zoneIndex.Serialize(stream);

            GarminFitnessBool dirty = new GarminFitnessBool(IsDirty);
            dirty.Serialize(stream);
        }
        public override void Serialize(XmlNode parentNode, String nodeName, XmlDocument document)
        {
            base.Serialize(parentNode, nodeName, document);

            // This node was added by our parent...
            parentNode = parentNode.LastChild;

            float lastMaxHR = GarminProfileManager.Instance.UserProfile.GetProfileForActivity(Options.Instance.GetGarminCategory(BaseTarget.ParentStep.ParentWorkout.Category)).MaximumHeartRate;
            XmlAttribute attribute;
            XmlNode childNode;
            GarminFitnessBool exportAsPercentMax = new GarminFitnessBool(Options.Instance.ExportSportTracksHeartRateAsPercentMax, Constants.HeartRateReferenceTCXString[1], Constants.HeartRateReferenceTCXString[0]);
            GarminFitnessByteRange lowValue = new GarminFitnessByteRange(0);
            GarminFitnessByteRange highValue = new GarminFitnessByteRange(0);

            if (float.IsNaN(lastMaxHR))
            {
                exportAsPercentMax.Value = false;
            }

            if (exportAsPercentMax)
            {
                float baseMultiplier = Constants.MaxHRInPercentMax / lastMaxHR;

                lowValue.Value = (Byte)Utils.Clamp(Math.Round(Zone.Low * baseMultiplier, 0, MidpointRounding.AwayFromZero), Constants.MinHRInPercentMax, Constants.MaxHRInPercentMax);
                highValue.Value = (Byte)Utils.Clamp(Math.Round(Zone.High * baseMultiplier, 0, MidpointRounding.AwayFromZero), Constants.MinHRInPercentMax, Constants.MaxHRInPercentMax);
            }
            else
            {
                lowValue.Value = (Byte)Utils.Clamp(Zone.Low, Constants.MinHRInBPM, Constants.MaxHRInBPM);
                highValue.Value = (Byte)Utils.Clamp(Zone.High, Constants.MinHRInBPM, Constants.MaxHRInBPM);
            }

            // Type
            attribute = document.CreateAttribute(Constants.XsiTypeTCXString, Constants.xsins);
            attribute.Value = Constants.HeartRateRangeZoneTCXString[1];
            parentNode.Attributes.Append(attribute);

            // Low
            childNode = document.CreateElement("Low");
            parentNode.AppendChild(childNode);
            exportAsPercentMax.SerializeAttribute(childNode, Constants.XsiTypeTCXString, Constants.xsins, document);
            lowValue.Serialize(childNode, Constants.ValueTCXString, document);

            // High
            childNode = document.CreateElement("High");
            parentNode.AppendChild(childNode);
            exportAsPercentMax.SerializeAttribute(childNode, Constants.XsiTypeTCXString, Constants.xsins, document);
            highValue.Serialize(childNode, Constants.ValueTCXString, document);

            // Extension
            Utils.SerializeSTZoneInfoXML(BaseTarget.ParentStep,
                                         BaseTarget.ParentStep.ParentConcreteWorkout.Category.HeartRateZone,
                                         Zone, document);
        }
Exemplo n.º 6
0
        public new void Deserialize_V0(Stream stream, DataVersion version)
        {
            GarminFitnessBool isRestingStep = new GarminFitnessBool(false);
            // Call base deserialization
            Deserialize(typeof(IStep), stream, version);

            byte[] intBuffer = new byte[sizeof(Int32)];

            m_Name.Deserialize(stream, version);
            isRestingStep.Deserialize(stream, version);

            if (isRestingStep)
            {
                m_Intensity = StepIntensity.Rest;
            }
            else
            {
                m_Intensity = StepIntensity.Active;
            }

            // Duration
            stream.Read(intBuffer, 0, sizeof(Int32));
            DurationFactory.Create((IDuration.DurationType)BitConverter.ToInt32(intBuffer, 0), stream, version, this);

            // Target
            stream.Read(intBuffer, 0, sizeof(Int32));
            Int32 type = BitConverter.ToInt32(intBuffer, 0);

            // This sucks but I changed the order of the enum between version 0 and 1,
            //  so make sure the version is right
            if (version.VersionNumber == 0)
            {
                if (type == 3)
                {
                    // Null was #3, now #0
                    type = 0;
                }
                else
                {
                    // Everything else is pushed up 1 position
                    ++type;
                }
            }

            TargetFactory.Create((ITarget.TargetType)type, stream, version, this);
        }
Exemplo n.º 7
0
        public override void Serialize(XmlNode parentNode, String nodeName, XmlDocument document)
        {
            bool addNodeToExtensions = false;

            if (Target.Type == ITarget.TargetType.Power)
            {
                // Power was added to the format as an extension which gives me a headache
                //  So we need to serialize this target as having a NullTarget and then
                //  add a step extension that uses the real target with power info
                ITarget realTarget = Target;

                // Create the fake target
                TargetFactory.Create(ITarget.TargetType.Null, this);
                Serialize(parentNode, nodeName, document);

                // Remove the step extension that was added so there's no duplicate
                ParentWorkout.STExtensions.RemoveAt(ParentWorkout.STExtensions.Count - 1);

                // Restore old target
                Target = realTarget;

                // Create new parent node and add it to the extensions
                nodeName = "Step";
                addNodeToExtensions = true;
            }

            // Ok now this the real stuff but the target can either be the fake one or the real one
            base.Serialize(parentNode, nodeName, document);

            if (Name != String.Empty && Name != null)
            {
                m_Name.Serialize(parentNode.LastChild, "Name", document);
            }

            // Duration
            Duration.Serialize(parentNode.LastChild, "Duration", document);

            // Intensity
            GarminFitnessBool isRestingStep = new GarminFitnessBool(Intensity == StepIntensity.Active ||
                                                                    (Intensity == StepIntensity.Warmup && Options.Instance.TCXExportWarmupAs == StepIntensity.Active) ||
                                                                    (Intensity == StepIntensity.Cooldown && Options.Instance.TCXExportCooldownAs == StepIntensity.Active),
                                                                    Constants.StepIntensityZoneTCXString[0],
                                                                    Constants.StepIntensityZoneTCXString[1]);
            isRestingStep.Serialize(parentNode.LastChild, "Intensity", document);

            // Target
            Target.Serialize(parentNode.LastChild, "Target", document);

            if (addNodeToExtensions)
            {
                XmlNode extensionNode = parentNode.LastChild;

                parentNode.RemoveChild(extensionNode);
                ParentWorkout.AddStepExtension(extensionNode);
            }
        }
        private void ReadHRZone(int index, XmlNode parentNode)
        {
            Debug.Assert(index >= 0 && index < Constants.GarminHRZoneCount);

            bool viewAsRead = false;
            bool lowRead = false;
            bool highRead = false;
            GarminFitnessByteRange lowLimit = new GarminFitnessByteRange(0);
            GarminFitnessByteRange highLimit = new GarminFitnessByteRange(0);

            for (int i = 0; i < parentNode.ChildNodes.Count; ++i)
            {
                XmlNode currentChild = parentNode.ChildNodes[i];

                if (currentChild.Name == Constants.ViewAsTCXString)
                {
                    GarminFitnessBool HRIsInPercentMax = new GarminFitnessBool(true,
                                                                               Constants.PercentMaxTCXString,
                                                                               Constants.BPMTCXString);
                    HRIsInPercentMax.Deserialize(currentChild);

                    // When we read % max, make sure we don't overwrite the BPM vs %Max/HRR since there
                    //  is no difference between both of these in the TCX file
                    if (!HRIsInPercentMax ||
                        HRZonesReferential == HRReferential.HRReferential_BPM)
                    {
                        HRZonesReferential = HRIsInPercentMax ? HRReferential.HRReferential_PercentMax : HRReferential.HRReferential_BPM;
                    }
                    viewAsRead = true;
                }
                else if (currentChild.Name == Constants.LowTCXString &&
                         currentChild.ChildNodes.Count == 1 &&
                         currentChild.FirstChild.Name == Constants.ValueTCXString)
                {
                    lowLimit.Deserialize(currentChild.FirstChild);
                    lowRead = true;
                }
                else if (currentChild.Name == Constants.HighTCXString &&
                         currentChild.ChildNodes.Count == 1 &&
                         currentChild.FirstChild.Name == Constants.ValueTCXString)
                {
                    highLimit.Deserialize(currentChild.FirstChild);
                    highRead = true;
                }
            }

            // Check if all was read successfully
            if (!viewAsRead || !lowRead || !highRead)
            {
                throw new GarminFitnessXmlDeserializationException("Missing information in heart rate zone XML node", parentNode);
            }

            m_HeartRateZones[index].Lower = new GarminFitnessDoubleRange((double)Math.Min(lowLimit, highLimit) / MaximumHeartRate, 0, 1);
            m_HeartRateZones[index].Upper = new GarminFitnessDoubleRange((double)Math.Max(lowLimit, highLimit) / MaximumHeartRate, 0, 1);
        }
Exemplo n.º 9
0
        public override void Deserialize(XmlNode parentNode)
        {
            base.Deserialize(parentNode);

            bool durationLoaded = false;
            bool targetLoaded = false;
            bool intensityLoaded = false;

            // Reset the name in case it's not in the XML (which means no name)
            m_Name.Value = "";

            for (int i = 0; i < parentNode.ChildNodes.Count; ++i)
            {
                XmlNode child = parentNode.ChildNodes[i];

                if (child.Name == "Name")
                {
                    m_Name.Deserialize(child);
                }
                else if (child.Name == "Duration")
                {
                    durationLoaded = (DurationFactory.Create(child, this) != null);
                }
                else if (child.Name == "Target")
                {
                    targetLoaded = (TargetFactory.Create(child, this) != null);
                }
                else if (child.Name == "Intensity")
                {
                    GarminFitnessBool isActiveStep = new GarminFitnessBool(false,
                                                                            Constants.StepIntensityZoneTCXString[0],
                                                                            Constants.StepIntensityZoneTCXString[1]);
                    isActiveStep.Deserialize(child);

                    if (isActiveStep)
                    {
                        Intensity = StepIntensity.Active;
                    }
                    else
                    {
                        Intensity = StepIntensity.Rest;
                    }

                    intensityLoaded = true;
                }
            }

            if (!durationLoaded || !targetLoaded || !intensityLoaded)
            {
                throw new GarminFitnessXmlDeserializationException("Information missing in the XML node", parentNode);
            }
        }
        public virtual void Serialize(XmlNode parentNode, String nodeName, XmlDocument document)
        {
            XmlAttribute attributeNode;
            XmlNode activityNode, currentChild;

            activityNode = document.CreateElement(nodeName);
            parentNode.AppendChild(activityNode);

            attributeNode = document.CreateAttribute("Sport");
            attributeNode.Value = Constants.GarminCategoryTCXString[(int)Category];
            activityNode.Attributes.Append(attributeNode);

            attributeNode = document.CreateAttribute(Constants.XsiTypeTCXString, Constants.xsins);
            attributeNode.Value = "ProfileActivity_t";
            activityNode.Attributes.Append(attributeNode);

            // Maximum heart rate
            currentChild = document.CreateElement(Constants.MaxHRBPMTCXString);
            activityNode.AppendChild(currentChild);
            m_MaxHeartRate.Serialize(currentChild, Constants.ValueTCXString, document);

            // Resting HR
            currentChild = document.CreateElement(Constants.RestHRBPMTCXString);
            activityNode.AppendChild(currentChild);
            GarminProfileManager.Instance.UserProfile.InternalRestingHeartRate.Serialize(currentChild, Constants.ValueTCXString, document);

            GarminFitnessDoubleRange gearWeightinKg = new GarminFitnessDoubleRange(Weight.Convert(m_GearWeightInPounds, Weight.Units.Pound, Weight.Units.Kilogram));

            gearWeightinKg.Serialize(activityNode, Constants.GearWeightTCXString, document);

            // HR zones
            for (int i = 0; i < Constants.GarminHRZoneCount; ++i)
            {
                currentChild = document.CreateElement(Constants.HeartRateZonesTCXString);
                activityNode.AppendChild(currentChild);

                // Number
                XmlNode numberNode = document.CreateElement("Number");
                numberNode.AppendChild(document.CreateTextNode((i + 1).ToString()));
                currentChild.AppendChild(numberNode);

                // View as BPM or % max
                GarminFitnessBool HRIsInPercentMax = new GarminFitnessBool(HRZonesReferential != HRReferential.HRReferential_BPM,
                                                                           Constants.PercentMaxTCXString,
                                                                           Constants.BPMTCXString);
                HRIsInPercentMax.Serialize(currentChild, Constants.ViewAsTCXString, document);

                // Low
                GarminFitnessByteRange lowLimit = new GarminFitnessByteRange((Byte)(m_HeartRateZones[i].Lower * MaximumHeartRate));
                XmlNode low = document.CreateElement(Constants.LowTCXString);
                currentChild.AppendChild(low);
                lowLimit.Serialize(low, Constants.ValueTCXString, document);

                // High
                GarminFitnessByteRange highLimit = new GarminFitnessByteRange((Byte)(m_HeartRateZones[i].Upper * MaximumHeartRate));
                XmlNode high = document.CreateElement(Constants.HighTCXString);
                currentChild.AppendChild(high);
                highLimit.Serialize(high, Constants.ValueTCXString, document);
            }

            // Speed zones
            for (int i = 0; i < Constants.GarminSpeedZoneCount; ++i)
            {
                currentChild = document.CreateElement(Constants.SpeedZonesTCXString);
                activityNode.AppendChild(currentChild);

                // Number
                XmlNode numberNode = document.CreateElement("Number");
                numberNode.AppendChild(document.CreateTextNode((i + 1).ToString()));
                currentChild.AppendChild(numberNode);

                // Name
                m_SpeedZones[i].InternalName.Serialize(currentChild, "Name", document);

                XmlNode valueChild = document.CreateElement(Constants.ValueTCXString);
                currentChild.AppendChild(valueChild);

                // View as pace or speed
                m_SpeedIsInPace.Serialize(valueChild, Constants.ViewAsTCXString, document);

                // Low
                m_SpeedZones[i].InternalLow.Serialize(valueChild, Constants.LowInMeterPerSecTCXString, document);

                // High
                m_SpeedZones[i].InternalHigh.Serialize(valueChild, Constants.HighInMeterPerSecTCXString, document);
            }
        }
        public void Deserialize_V8(Stream stream, DataVersion version)
        {
            m_MaxHeartRate.Deserialize(stream, version);

            GarminFitnessDoubleRange gearWeightinKg = new GarminFitnessDoubleRange(Weight.Convert(m_GearWeightInPounds, Weight.Units.Pound, Weight.Units.Kilogram));

            gearWeightinKg.Deserialize(stream, version);
            SetGearWeightInUnits(gearWeightinKg, Weight.Units.Kilogram);

            GarminFitnessBool HRIsInPercentMax = new GarminFitnessBool(true);
            HRIsInPercentMax.Deserialize(stream, version);
            HRZonesReferential = HRIsInPercentMax ? HRReferential.HRReferential_PercentMax : HRReferential.HRReferential_BPM;

            for (int i = 0; i < m_HeartRateZones.Count; ++i)
            {
                m_HeartRateZones[i].Lower.Deserialize(stream, version);

                m_HeartRateZones[i].Upper.Deserialize(stream, version);
            }

            m_SpeedIsInPace.Deserialize(stream, version);

            GarminFitnessDoubleRange lowLimit = new GarminFitnessDoubleRange(Constants.MinSpeedMetric, Constants.MinSpeedMetric, Constants.MaxSpeedMetric);
            GarminFitnessDoubleRange highLimit = new GarminFitnessDoubleRange(Constants.MinSpeedMetric, Constants.MinSpeedMetric, Constants.MaxSpeedMetric);
            for (int i = 0; i < m_SpeedZones.Count; ++i)
            {
                lowLimit.Deserialize(stream, version);
                m_SpeedZones[i].Low = Length.Convert(lowLimit, Length.Units.Kilometer, Length.Units.Meter) / Constants.SecondsPerHour;

                highLimit.Deserialize(stream, version);
                m_SpeedZones[i].High = Length.Convert(highLimit, Length.Units.Kilometer, Length.Units.Meter) / Constants.SecondsPerHour;

                m_SpeedZones[i].InternalName.Deserialize(stream, version);
            }
        }
        public void Deserialize_V22(Stream stream, DataVersion version)
        {
            m_MaxHeartRate.Deserialize(stream, version);

            m_GearWeightInPounds.Deserialize(stream, version);

            GarminFitnessBool HRIsInPercentMax = new GarminFitnessBool(true);
            HRIsInPercentMax.Deserialize(stream, version);
            HRZonesReferential = HRIsInPercentMax ? HRReferential.HRReferential_PercentMax : HRReferential.HRReferential_BPM;

            for (int i = 0; i < m_HeartRateZones.Count; ++i)
            {
                m_HeartRateZones[i].Lower.Deserialize(stream, version);

                m_HeartRateZones[i].Upper.Deserialize(stream, version);
            }

            m_SpeedIsInPace.Deserialize(stream, version);

            for (int i = 0; i < m_SpeedZones.Count; ++i)
            {
                m_SpeedZones[i].InternalLow.Deserialize(stream, version);

                m_SpeedZones[i].InternalHigh.Deserialize(stream, version);

                m_SpeedZones[i].InternalName.Deserialize(stream, version);
            }
        }
        public void Deserialize_V10(Stream stream, DataVersion version)
        {
            m_MaxHeartRate.Deserialize(stream, version);

            GarminFitnessDoubleRange gearWeightinKg = new GarminFitnessDoubleRange(Weight.Convert(m_GearWeightInPounds, Weight.Units.Pound, Weight.Units.Kilogram));

            gearWeightinKg.Deserialize(stream, version);
            SetGearWeightInUnits(gearWeightinKg, Weight.Units.Kilogram);

            GarminFitnessBool HRIsInPercentMax = new GarminFitnessBool(true);
            HRIsInPercentMax.Deserialize(stream, version);
            HRZonesReferential = HRIsInPercentMax ? HRReferential.HRReferential_PercentMax : HRReferential.HRReferential_BPM;

            for (int i = 0; i < m_HeartRateZones.Count; ++i)
            {
                m_HeartRateZones[i].Lower.Deserialize(stream, version);

                m_HeartRateZones[i].Upper.Deserialize(stream, version);
            }

            m_SpeedIsInPace.Deserialize(stream, version);

            for (int i = 0; i < m_SpeedZones.Count; ++i)
            {
                m_SpeedZones[i].InternalLow.Deserialize(stream, version);

                m_SpeedZones[i].InternalHigh.Deserialize(stream, version);

                m_SpeedZones[i].InternalName.Deserialize(stream, version);
            }
        }
Exemplo n.º 14
0
        public override void Serialize(XmlNode parentNode, String nodeName, XmlDocument document)
        {
            base.Serialize(parentNode, nodeName, document);

            // This node was added by our parent...
            parentNode = parentNode.LastChild;

            XmlAttribute attribute;

            // Type
            attribute = document.CreateAttribute(Constants.XsiTypeTCXString, Constants.xsins);
            attribute.Value = Constants.SpeedRangeZoneTCXString[1];
            parentNode.Attributes.Append(attribute);

            // View as
            GarminFitnessBool viewAs = new GarminFitnessBool(ViewAsPace, Constants.SpeedOrPaceTCXString[0], Constants.SpeedOrPaceTCXString[1]);
            viewAs.Serialize(parentNode, Constants.ViewAsTCXString, document);

            // Low
            GarminFitnessDoubleRange lowInMetersPerSecond = new GarminFitnessDoubleRange(Utils.Clamp(Zone.Low, Constants.MinSpeedMetersPerSecond, Constants.MaxSpeedMetersPerSecond));
            lowInMetersPerSecond.Serialize(parentNode, Constants.LowInMeterPerSecTCXString, document);

            // High
            GarminFitnessDoubleRange highInMetersPerSecond = new GarminFitnessDoubleRange(Utils.Clamp(Zone.High, Constants.MinSpeedMetersPerSecond, Constants.MaxSpeedMetersPerSecond));
            highInMetersPerSecond.Serialize(parentNode, Constants.HighInMeterPerSecTCXString, document);

            // Extension
            Utils.SerializeSTZoneInfoXML(BaseTarget.ParentStep,
                                         BaseTarget.ParentStep.ParentConcreteWorkout.Category.SpeedZone,
                                         Zone, document);
        }