public static void ExportSchedulesFITFile(Stream exportStream, MemoryStream dataStream)
        {
            MemoryStream tempDataStream = new MemoryStream();

            // Reserve size for header
            tempDataStream.Write(new Byte[12], 0, 12);

            // File id message
            FITMessage fileIdMessage = new FITMessage(FITGlobalMessageIds.FileId);
            FITMessageField fileType = new FITMessageField((Byte)FITFileIdFieldsIds.FileType);
            FITMessageField manufacturerId = new FITMessageField((Byte)FITFileIdFieldsIds.ManufacturerId);
            FITMessageField productId = new FITMessageField((Byte)FITFileIdFieldsIds.ProductId);
            FITMessageField serialNumber = new FITMessageField((Byte)FITFileIdFieldsIds.SerialNumber);
            FITMessageField exportDate = new FITMessageField((Byte)FITFileIdFieldsIds.ExportDate);
            FITMessageField number = new FITMessageField((Byte)FITFileIdFieldsIds.Number);

            manufacturerId.SetUInt16(1);
            fileType.SetEnum((Byte)FITFileTypes.Schedules);

            // Invalid fields
            productId.SetUInt16(0xFFFF);
            serialNumber.SetUInt32z(0);
            exportDate.SetUInt32(0xFFFFFFFF);
            number.SetUInt16(0xFFFF);

            fileIdMessage.AddField(serialNumber);
            fileIdMessage.AddField(exportDate);
            fileIdMessage.AddField(manufacturerId);
            fileIdMessage.AddField(productId);
            fileIdMessage.AddField(number);
            fileIdMessage.AddField(fileType);

            fileIdMessage.Serialize(tempDataStream);

            // Write all passed in data to output stream
            tempDataStream.Write(dataStream.GetBuffer(), 0, (int)dataStream.Length);

            // Write FIT header at the start of the stream
            GarminFitnessByteRange headerSize = new GarminFitnessByteRange(12);
            GarminFitnessByteRange protocolVersion = new GarminFitnessByteRange((Byte)((FITConstants.FITProtocolMajorVersion << 4) | FITConstants.FITProtocolMinorVersion));
            GarminFitnessUInt16Range profileVersion = new GarminFitnessUInt16Range((UInt16)((FITConstants.FITProfileMajorVersion * FITConstants.FITProfileMajorVersionMultiplier) + FITConstants.FITProfileMinorVersion));
            GarminFitnessInt32Range dataSize = new GarminFitnessInt32Range(0);

            tempDataStream.Seek(0, SeekOrigin.Begin);
            dataSize.Value = (int)tempDataStream.Length - 12;

            headerSize.Serialize(tempDataStream);
            protocolVersion.Serialize(tempDataStream);
            profileVersion.Serialize(tempDataStream);
            dataSize.Serialize(tempDataStream);
            tempDataStream.Write(Encoding.UTF8.GetBytes(FITConstants.FITFileDescriptor), 0, 4);

            // Write CRC
            GarminFitnessUInt16Range crc = new GarminFitnessUInt16Range(FITUtils.ComputeStreamCRC(tempDataStream));
            tempDataStream.Seek(0, SeekOrigin.End);
            crc.Serialize(tempDataStream);

            // Write all data to output stream
            exportStream.Write(tempDataStream.GetBuffer(), 0, (int)tempDataStream.Length);
        }
        public void Serialize(Stream stream, bool serializeDefiniton)
        {
            // Definition message
            GarminFitnessByteRange recordHeader = new GarminFitnessByteRange(0x40);

            if (serializeDefiniton)
            {
                GarminFitnessByteRange recordReservedData = new GarminFitnessByteRange(0x00);
                GarminFitnessByteRange endianness = new GarminFitnessByteRange(0x00);
                GarminFitnessUInt16Range messageNumber = new GarminFitnessUInt16Range((UInt16)m_GlobalMessageType);
                GarminFitnessByteRange fieldsCount = new GarminFitnessByteRange((Byte)m_Fields.Count);

                recordHeader.Serialize(stream);
                recordReservedData.Serialize(stream);
                endianness.Serialize(stream);
                messageNumber.Serialize(stream);
                fieldsCount.Serialize(stream);

                foreach (FITMessageField field in m_Fields.Values)
                {
                    field.SerializeDefinition(stream);
                }
            }

            // Data message
            recordHeader.Value = 0x00;
            recordHeader.Serialize(stream);

            foreach (FITMessageField field in m_Fields.Values)
            {
                field.SerializeData(stream);
            }
        }
        public static void ExportWorkoutToFIT(IWorkout workout, Stream exportStream, bool updateExportDate)
        {
            MemoryStream dataStream = new MemoryStream();

            // Reserve size for header
            dataStream.Write(new Byte[12], 0, 12);

            // File id message
            FITMessage fileIdMessage = new FITMessage(FITGlobalMessageIds.FileId);
            FITMessageField fileType = new FITMessageField((Byte)FITFileIdFieldsIds.FileType);
            FITMessageField manufacturerId = new FITMessageField((Byte)FITFileIdFieldsIds.ManufacturerId);
            FITMessageField productId = new FITMessageField((Byte)FITFileIdFieldsIds.ProductId);
            FITMessageField serialNumber = new FITMessageField((Byte)FITFileIdFieldsIds.SerialNumber);
            FITMessageField exportDate = new FITMessageField((Byte)FITFileIdFieldsIds.ExportDate);
            FITMessageField number = new FITMessageField((Byte)FITFileIdFieldsIds.Number);

            fileType.SetEnum((Byte)FITFileTypes.Workout);
            manufacturerId.SetUInt16(1);
            productId.SetUInt16(20119);
            serialNumber.SetUInt32z(0);
            exportDate.SetUInt32(workout.CreationTimestamp);
            number.SetUInt16(0xFFFF);

            fileIdMessage.AddField(serialNumber);
            fileIdMessage.AddField(exportDate);
            fileIdMessage.AddField(manufacturerId);
            fileIdMessage.AddField(productId);
            fileIdMessage.AddField(number);
            fileIdMessage.AddField(fileType);
            fileIdMessage.Serialize(dataStream);

            // File creator message
            FITMessage fileCreatorMessage = new FITMessage(FITGlobalMessageIds.FileCreator);
            FITMessageField software = new FITMessageField((Byte)FITFileCreatorFieldsIds.SoftwareVersion);
            FITMessageField hardware = new FITMessageField((Byte)FITFileCreatorFieldsIds.HardwareVersion);

            software.SetUInt16(3605);
            hardware.SetUInt8(0);

            fileCreatorMessage.AddField(software);
            fileCreatorMessage.AddField(hardware);
            fileCreatorMessage.Serialize(dataStream);

            // Write workout
            workout.SerializeToFIT(dataStream);

            // Write FIT header at the start of the stream
            GarminFitnessByteRange headerSize = new GarminFitnessByteRange(12);
            GarminFitnessByteRange protocolVersion = new GarminFitnessByteRange((Byte)((FITConstants.FITProtocolMajorVersion << 4) | FITConstants.FITProtocolMinorVersion));
            GarminFitnessUInt16Range profileVersion = new GarminFitnessUInt16Range((UInt16)((FITConstants.FITProfileMajorVersion * FITConstants.FITProfileMajorVersionMultiplier) + FITConstants.FITProfileMinorVersion));
            GarminFitnessInt32Range dataSize = new GarminFitnessInt32Range(0);

            dataStream.Seek(0, SeekOrigin.Begin);
            dataSize.Value = (int)dataStream.Length - 12;

            headerSize.Serialize(dataStream);
            protocolVersion.Serialize(dataStream);
            profileVersion.Serialize(dataStream);
            dataSize.Serialize(dataStream);
            dataStream.Write(Encoding.UTF8.GetBytes(FITConstants.FITFileDescriptor), 0, 4);

            // Write CRC
            GarminFitnessUInt16Range crc = new GarminFitnessUInt16Range(FITUtils.ComputeStreamCRC(dataStream));
            dataStream.Seek(0, SeekOrigin.End);
            crc.Serialize(dataStream);

            // Write all data to output stream
            exportStream.Write(dataStream.GetBuffer(), 0, (int)dataStream.Length);

            if (updateExportDate)
            {
                workout.LastExportDate = DateTime.Now;
            }
        }
        public void SerializeDefinition(Stream stream)
        {
            GarminFitnessByteRange definitionNumber = new GarminFitnessByteRange(m_DefinitionNumber);
            GarminFitnessByteRange size = new GarminFitnessByteRange((Byte)(FieldSize * ArraySize));
            GarminFitnessByteRange baseType = new GarminFitnessByteRange((Byte)m_Type);

            definitionNumber.Serialize(stream);
            size.Serialize(stream);
            baseType.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);
        }
        public void DeserializeData(Stream stream, bool isLittleEndian)
        {
            switch (m_Type)
            {
                case DataType.Enum:
                case DataType.UInt8:
                case DataType.UInt8z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessByteRange data = new GarminFitnessByteRange(0);

                            data.Deserialize(stream, Constants.CurrentVersion);
                            m_ByteValues[i] = data;
                        }
                        break;
                    }
                case DataType.SInt8:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessSByteRange data = new GarminFitnessSByteRange(0);

                            data.Deserialize(stream, Constants.CurrentVersion);
                            m_SByteValues[i] = data;
                        }
                        break;
                    }
                case DataType.UInt16:
                case DataType.UInt16z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(UInt16)];

                            stream.Read(data, 0, sizeof(UInt16));
                            m_UInt16Values[i] = BitConverter.ToUInt16(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[1];
                                data[1] = temp;

                                m_UInt16Values[i] = BitConverter.ToUInt16(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.SInt16:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(Int16)];

                            stream.Read(data, 0, sizeof(UInt16));
                            m_Int16Values[i] = BitConverter.ToInt16(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[1];
                                data[1] = temp;

                                m_Int16Values[i] = BitConverter.ToInt16(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.UInt32:
                case DataType.UInt32z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(UInt32)];

                            stream.Read(data, 0, sizeof(UInt32));
                            m_UInt32Values[i] = BitConverter.ToUInt32(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[3];
                                data[3] = temp;
                                temp = data[1];
                                data[1] = data[2];
                                data[2] = temp;

                                m_UInt32Values[i] = BitConverter.ToUInt32(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.SInt32:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(UInt32)];

                            stream.Read(data, 0, sizeof(UInt32));
                            m_Int32Values[i] = BitConverter.ToInt32(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[3];
                                data[3] = temp;
                                temp = data[1];
                                data[1] = data[2];
                                data[2] = temp;

                                m_Int32Values[i] = BitConverter.ToInt32(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.Float32:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(Single)];

                            stream.Read(data, 0, sizeof(Single));
                            m_SingleValues[i] = BitConverter.ToSingle(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[3];
                                data[3] = temp;
                                temp = data[1];
                                data[1] = data[2];
                                data[2] = temp;

                                m_SingleValues[i] = BitConverter.ToSingle(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.Float64:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            Byte[] data = new Byte[sizeof(Double)];

                            stream.Read(data, 0, sizeof(Double));
                            m_DoubleValues[i] = BitConverter.ToDouble(data, 0);

                            if (!isLittleEndian)
                            {
                                Byte temp = data[0];
                                data[0] = data[7];
                                data[7] = temp;
                                temp = data[1];
                                data[1] = data[6];
                                data[6] = temp;
                                temp = data[2];
                                data[2] = data[5];
                                data[5] = temp;
                                temp = data[3];
                                data[3] = data[4];
                                data[4] = temp;

                                m_DoubleValues[i] = BitConverter.ToDouble(data, 0);
                            }
                        }
                        break;
                    }
                case DataType.String:
                case DataType.Byte:
                    {
                        stream.Read(m_ByteValues, 0, m_ByteValues.Length);
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        break;
                    }
            }
        }
        public void SerializeData(Stream stream)
        {
            switch (m_Type)
            {
                case DataType.Enum:
                case DataType.UInt8:
                case DataType.UInt8z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessByteRange data = new GarminFitnessByteRange(m_ByteValues[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.SInt8:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessSByteRange data = new GarminFitnessSByteRange(m_SByteValues[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.UInt16:
                case DataType.UInt16z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessUInt16Range data = new GarminFitnessUInt16Range(m_UInt16Values[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.SInt16:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessInt16Range data = new GarminFitnessInt16Range(m_Int16Values[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.UInt32:
                case DataType.UInt32z:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessUInt32Range data = new GarminFitnessUInt32Range(m_UInt32Values[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.SInt32:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessInt32Range data = new GarminFitnessInt32Range(m_Int32Values[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.Float32:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessFloatRange data = new GarminFitnessFloatRange(m_SingleValues[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.Float64:
                    {
                        for (int i = 0; i < ArraySize; ++i)
                        {
                            GarminFitnessDoubleRange data = new GarminFitnessDoubleRange(m_DoubleValues[i]);

                            data.Serialize(stream);
                        }
                        break;
                    }
                case DataType.String:
                    {
                        Byte[] valueStored = new Byte[FieldSize];

                        for(int i = 0; i < valueStored.Length; ++i)
                        {
                            if (i < m_ByteValues.Length)
                            {
                                valueStored[i] = m_ByteValues[i];
                            }
                            else
                            {
                                valueStored[i] = 0;
                            }
                        }
                        stream.Write(valueStored, 0, valueStored.Length);
                        break;
                    }
                case DataType.Byte:
                    {
                        stream.Write(m_ByteValues, 0, m_ByteValues.Length);
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        break;
                    }
            }
        }
        public override void Serialize(Stream stream)
        {
            GarminFitnessByteRange intensity = new GarminFitnessByteRange((Byte)m_Intensity);
            base.Serialize(stream);

            m_Name.Serialize(stream);
            Duration.Serialize(stream);
            Target.Serialize(stream);
            intensity.Serialize(stream);
        }
        public void Deserialize_V17(Stream stream, DataVersion version)
        {
            GarminFitnessByteRange intensity = new GarminFitnessByteRange(0);

            // Call base deserialization
            Deserialize(typeof(IStep), stream, version);

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

            m_Name.Deserialize(stream, version);

            // 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);

            intensity.Deserialize(stream, Constants.CurrentVersion);
            m_Intensity = (StepIntensity)(Byte)intensity;
        }
        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);
        }
        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_V23(Stream stream, DataVersion version)
        {
            m_MaxHeartRate.Deserialize(stream, version);

            m_GearWeightInPounds.Deserialize(stream, version);

            GarminFitnessByteRange HRZoneReference = new GarminFitnessByteRange(0);
            HRZoneReference.Deserialize(stream, version);
            HRZonesReferential = (HRReferential)(Byte)HRZoneReference;

            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);
            }
        }