public List <SessionMessages> ParseSessions() { if (!IsActivityFile) { throw new Exception($"Expected FIT File Type: Activity, recieved File Type: {_messages?.FileId?.GetType()}"); } // When there are no Sessions but there are Records create a Session message to recover as much data as possible if (_messages.Sessions.Count == 0 && _messages.Records.Count > 0) { Dynastream.Fit.DateTime startTime = _messages.Records[0].GetTimestamp(); Dynastream.Fit.DateTime timestamp = _messages.Records[_messages.Records.Count - 1].GetTimestamp(); var session = new SessionMesg(); session.SetStartTime(startTime); session.SetTimestamp(timestamp); session.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); session.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); _messages.Sessions.Add(session); } int recordsTaken = 0; var sessions = new List <SessionMessages>(_messages.Sessions.Count); foreach (SessionMesg sessionMesg in _messages.Sessions) { var session = new SessionMessages(sessionMesg) { Laps = _messages.Laps.Skip(sessionMesg.GetFirstLapIndex() ?? 0).Take(sessionMesg.GetNumLaps() ?? 0).ToList(), ClimbPros = _messages.ClimbPros.Where(climb => climb.Within(sessionMesg)).ToList(), Events = _messages.Events.Where(evt => evt.Within(sessionMesg)).ToList(), DeviceInfos = _messages.DeviceInfos.Where(deviceInfo => deviceInfo.Within(sessionMesg)).ToList(), Lengths = _messages.Lengths.Where(length => length.Overlaps(sessionMesg)).ToList(), Records = _messages.Records.Skip(recordsTaken).Where(record => record.Within(sessionMesg)).ToList(), SegmentLaps = _messages.SegmentLaps.Where(segmentLap => segmentLap.Overlaps(sessionMesg)).ToList(), TimerEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.Timer && evt.Within(sessionMesg)).ToList(), FrontGearChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.FrontGearChange && evt.Within(sessionMesg)).ToList(), RearGearChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.RearGearChange && evt.Within(sessionMesg)).ToList(), RiderPositionChangeEvents = _messages.Events.Where(evt => evt.GetEvent() == Event.RiderPositionChange && evt.Within(sessionMesg)).ToList(), Activity = _messages.Activity, FileId = _messages.FileId, RecordFieldNames = _messages.RecordFieldNames, RecordDeveloperFieldNames = _messages.RecordDeveloperFieldNames, UserProfile = _messages.UserProfile, Workout = _messages.Workout, WorkoutSteps = _messages.WorkoutSteps, ZonesTarget = _messages.ZonesTarget, }; recordsTaken += session.Records.Count; sessions.Add(session); } return(sessions); }
public static void Stop() { var now = new DateTime(System.DateTime.Now); TerminateLap(); sessionMesg.SetTimestamp(now); sessionMesg.SetSport(Sport.Cycling); sessionMesg.SetSubSport(SubSport.VirtualActivity); sessionMesg.SetTotalDistance(State.TripTotalKm * 1000); sessionMesg.SetTotalElapsedTime(State.TripTotalTime); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(numLaps); sessionMesg.SetEvent(Event.Session); sessionMesg.SetEventType(EventType.Stop); sessionMesg.SetEventGroup(0); activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(now); activityMesg.SetTotalTimerTime(State.TripTotalTime); activityMesg.SetNumSessions(1); activityMesg.SetType(Activity.Manual); activityMesg.SetEvent(Event.Activity); activityMesg.SetEventType(EventType.Stop); activityMesg.SetEventGroup(0); encoder.Write(sessionMesg); encoder.Write(activityMesg); encoder.Close(); fitDest.Close(); }
public static void AddRecord() { var now = new DateTime(System.DateTime.Now); if (lastRecordTimeStamp == now.GetTimeStamp()) { return; // do not record twice with same timestamp } try { var newRecord = new RecordMesg(); var hr = State.CyclistHeartRate > 0 ? (byte?)State.CyclistHeartRate : null; var cad = State.BikeCadence > 0 ? (byte?)State.BikeCadence : null; newRecord.SetTimestamp(now); newRecord.SetHeartRate(hr); newRecord.SetCadence(cad); newRecord.SetPower((ushort)State.CyclistPower); newRecord.SetGrade(State.BikeIncline); newRecord.SetDistance(State.TripTotalKm * 1000); newRecord.SetSpeed(State.BikeSpeedKmh / 3.6f); newRecord.SetAltitude(RaceState.Instance.CarPositions[0].Y); encoder.Write(newRecord); lastRecordTimeStamp = now.GetTimeStamp(); } catch (Exception e) { Console.Write("Failed to write record."); Console.WriteLine(e.Message); } }
static void OnFileIDMesg(object sender, MesgEventArgs e) { Console.WriteLine("FileIdHandler: Received {1} Mesg with global ID#{0}", e.mesg.Num, e.mesg.Name); FileIdMesg myFileId = (FileIdMesg)e.mesg; try { Console.WriteLine("\tType: {0}", myFileId.GetType()); Console.WriteLine("\tManufacturer: {0}", myFileId.GetManufacturer()); Console.WriteLine("\tProduct: {0}", myFileId.GetProduct()); Console.WriteLine("\tSerialNumber {0}", myFileId.GetSerialNumber()); Console.WriteLine("\tNumber {0}", myFileId.GetNumber()); Console.WriteLine("\tTimeCreated {0}", myFileId.GetTimeCreated()); //Make sure properties with sub properties arent null before trying to create objects based on them if (myFileId.GetTimeCreated() != null) { Dynastream.Fit.DateTime dtTime = new Dynastream.Fit.DateTime(myFileId.GetTimeCreated().GetTimeStamp()); } } catch (FitException exception) { Console.WriteLine("\tOnFileIDMesg Error {0}", exception.Message); Console.WriteLine("\t{0}", exception.InnerException); } }
protected DeviceInfoMesg GetDeviceInfoMesg(GarminDeviceInfo deviceInfo, Dynastream.Fit.DateTime startTime) { var deviceInfoMesg = new DeviceInfoMesg(); deviceInfoMesg.SetTimestamp(startTime); deviceInfoMesg.SetSerialNumber(deviceInfo.UnitId); deviceInfoMesg.SetManufacturer(deviceInfo.ManufacturerId); deviceInfoMesg.SetProduct(deviceInfo.ProductID); deviceInfoMesg.SetDeviceIndex(0); deviceInfoMesg.SetSourceType(SourceType.Local); deviceInfoMesg.SetProductName(deviceInfo.Name); if (deviceInfo.Version.VersionMinor <= 0) { deviceInfoMesg.SetSoftwareVersion(deviceInfo.Version.VersionMajor); } else { var adjustedMinor = deviceInfo.Version.VersionMinor < 10 ? deviceInfo.Version.VersionMinor * 10 : deviceInfo.Version.VersionMinor; var minor = adjustedMinor / 100; deviceInfoMesg.SetSoftwareVersion((float)(deviceInfo.Version.VersionMajor + minor)); } return(deviceInfoMesg); }
/// <summary> /// Gets a system date/time from a Dynastream date/time. /// </summary> /// <param name="dateTime">Dynastream date/time.</param> /// <returns>System date/time.</returns> public static System.DateTime?GetDateTime(Dynastream.Fit.DateTime dateTime) { if (dateTime != null) { return(dateTime.GetDateTime().ToLocalTime()); } return(null); }
static void CreateActivityFile(List <Mesg> messages, String filename, Dynastream.Fit.DateTime startTime) { // The combination of file type, manufacturer id, product id, and serial number should be unique. // When available, a non-random serial number should be used. Dynastream.Fit.File fileType = Dynastream.Fit.File.Activity; ushort manufacturerId = Manufacturer.Development; ushort productId = 0; float softwareVersion = 1.0f; Random random = new Random(); uint serialNumber = (uint)random.Next(); // Every FIT file MUST contain a File ID message var fileIdMesg = new FileIdMesg(); fileIdMesg.SetType(fileType); fileIdMesg.SetManufacturer(manufacturerId); fileIdMesg.SetProduct(productId); fileIdMesg.SetTimeCreated(startTime); fileIdMesg.SetSerialNumber(serialNumber); // A Device Info message is a BEST PRACTICE for FIT ACTIVITY files var deviceInfoMesg = new DeviceInfoMesg(); deviceInfoMesg.SetDeviceIndex(DeviceIndex.Creator); deviceInfoMesg.SetManufacturer(Manufacturer.Development); deviceInfoMesg.SetProduct(productId); deviceInfoMesg.SetProductName("FIT Cookbook"); // Max 20 Chars deviceInfoMesg.SetSerialNumber(serialNumber); deviceInfoMesg.SetSoftwareVersion(softwareVersion); deviceInfoMesg.SetTimestamp(startTime); // Create the output stream, this can be any type of stream, including a file or memory stream. Must have read/write access FileStream fitDest = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); // Create a FIT Encode object Encode encoder = new Encode(ProtocolVersion.V20); // Write the FIT header to the output stream encoder.Open(fitDest); // Write the messages to the file, in the proper sequence encoder.Write(fileIdMesg); encoder.Write(deviceInfoMesg); foreach (Mesg message in messages) { encoder.Write(message); } // Update the data size in the header and calculate the CRC encoder.Close(); // Close the output stream fitDest.Close(); Console.WriteLine($"Encoded FIT file {fitDest.Name}"); }
public ICollection <LapMesg> GetLapsBasedOnSegments(WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime, Sport sport, SubSport subSport) { using var tracing = Tracing.Trace($"{nameof(FitConverter)}.{nameof(GetLapsBasedOnSegments)}") .WithTag(TagKey.Format, FileFormat.Fit.ToString()); var stepsAndLaps = new List <LapMesg>(); if (workoutSamples is null) { return(stepsAndLaps); } ushort stepIndex = 0; var speedMetrics = GetSpeedSummary(workoutSamples); if (workoutSamples.Segment_List.Any()) { var totalElapsedTime = 0; foreach (var segment in workoutSamples.Segment_List) { var lapStartTime = new Dynastream.Fit.DateTime(startTime); lapStartTime.Add(segment.Start_Time_Offset); totalElapsedTime += segment.Length; var lapMesg = new LapMesg(); lapMesg.SetStartTime(lapStartTime); lapMesg.SetMessageIndex(stepIndex); lapMesg.SetEvent(Event.Lap); lapMesg.SetLapTrigger(LapTrigger.Time); lapMesg.SetSport(sport); lapMesg.SetSubSport(subSport); lapMesg.SetTotalElapsedTime(segment.Length); lapMesg.SetTotalTimerTime(segment.Length); var startIndex = segment.Start_Time_Offset; var endIndex = segment.Start_Time_Offset + segment.Length; var lapDistanceInMeters = 0f; for (int i = startIndex; i < endIndex; i++) { if (speedMetrics is object && i < speedMetrics.Values.Length) { var currentSpeedInMPS = ConvertToMetersPerSecond(speedMetrics.GetValue(i), workoutSamples); lapDistanceInMeters += 1 * currentSpeedInMPS; } } lapMesg.SetTotalDistance(lapDistanceInMeters); stepsAndLaps.Add(lapMesg); stepIndex++; } } return(stepsAndLaps); }
void OnFileIDMesg(object sender, MesgEventArgs e) { FileIdMesg myFileId = (FileIdMesg)e.mesg; try { Dynastream.Fit.DateTime dtTime = new Dynastream.Fit.DateTime(myFileId.GetTimeCreated().GetTimeStamp()); tDevice.Text = getGarminProduct(myFileId.GetProduct()); tSerialNumber.Text = myFileId.GetSerialNumber().ToString(); } catch (FitException exception) { Debug.WriteLine(string.Format("\tOnFileIDMesg Error {0}", exception.Message)); Debug.WriteLine(string.Format("\t{0}", exception.InnerException)); } }
static void OnFileIDMesg(object sender, MesgEventArgs e) { Console.WriteLine("FileIdHandler: Received {1} Mesg with global ID#{0}", e.mesg.Num, e.mesg.Name); FileIdMesg myFileId = (FileIdMesg)e.mesg; try { Console.WriteLine("\tType: {0}", myFileId.GetType()); Console.WriteLine("\tManufacturer: {0}", myFileId.GetManufacturer()); Console.WriteLine("\tProduct: {0}", myFileId.GetProduct()); Console.WriteLine("\tSerialNumber {0}", myFileId.GetSerialNumber()); Console.WriteLine("\tNumber {0}", myFileId.GetNumber()); Dynastream.Fit.DateTime dtTime = new Dynastream.Fit.DateTime(myFileId.GetTimeCreated().GetTimeStamp()); } catch (FitException exception) { Console.WriteLine("\tOnFileIDMesg Error {0}", exception.Message); Console.WriteLine("\t{0}", exception.InnerException); } }
/// <summary> /// Terminates the current lap in the FIT recording. /// Use cases : ingame lap (if no workout in progress), start/end of workout, end of activity. /// </summary> public static void TerminateLap() { var now = new DateTime(System.DateTime.Now); currentLapMesg.SetTimestamp(now); currentLapMesg.SetSport(Sport.Cycling); currentLapMesg.SetTotalElapsedTime(now.GetTimeStamp() - currentLapMesg.GetStartTime().GetTimeStamp()); currentLapMesg.SetTotalTimerTime(now.GetTimeStamp() - currentLapMesg.GetStartTime().GetTimeStamp()); currentLapMesg.SetTotalDistance(State.TripTotalKm * 1000 - alreadyLappedDistance); currentLapMesg.SetEvent(Event.Lap); currentLapMesg.SetEventType(EventType.Stop); currentLapMesg.SetEventGroup(0); encoder.Write(currentLapMesg); numLaps++; currentLapMesg = new LapMesg(); alreadyLappedDistance = State.TripTotalKm * 1000; currentLapMesg.SetStartTime(now); }
static public void Start() { var assettoFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Assetto Corsa\\SimCyclingActivities"; if (!Directory.Exists(assettoFolder)) { Directory.CreateDirectory(assettoFolder); } var filepath = assettoFolder + "\\" + System.DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".fit"; var now = new DateTime(System.DateTime.Now); // Create file encode object encoder = new Encode(ProtocolVersion.V20); fitDest = new FileStream(filepath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); encoder.Open(fitDest); var fileIdMesg = new FileIdMesg(); // Every FIT file MUST contain a 'File ID' message as the first message fileIdMesg.SetType(Dynastream.Fit.File.Activity); fileIdMesg.SetManufacturer(Manufacturer.Dynastream); // Types defined in the profile are available fileIdMesg.SetProduct(22); fileIdMesg.SetSerialNumber(1234); fileIdMesg.SetTimeCreated(now); // Encode each message, a definition message is automatically generated and output if necessary encoder.Write(fileIdMesg); sessionMesg = new SessionMesg(); sessionMesg.SetStartTime(now); currentLapMesg = new LapMesg(); currentLapMesg.SetStartTime(now); }
public static void EncodeCourse() { const string filename = "CourseEncodeRecipe.fit"; // Example Record Data Defining a Course var courseData = new List <Dictionary <string, object> >() { new Dictionary <string, object>() { { "timestamp", 961262849U }, { "position_lat", 463583114 }, { "position_long", -1131028903 }, { "altitude", 329f }, { "distance", 0f }, { "speed", 0f } }, new Dictionary <string, object>() { { "timestamp", 961262855U }, { "position_lat", 463583127 }, { "position_long", -1131031938 }, { "altitude", 328.6f }, { "distance", 22.03f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262869U }, { "position_lat", 463583152 }, { "position_long", -1131038159 }, { "altitude", 327.6f }, { "distance", 67.29f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262876U }, { "position_lat", 463583164 }, { "position_long", -1131041346 }, { "altitude", 327f }, { "distance", 90.52f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262876U }, { "position_lat", 463583164 }, { "position_long", -1131041319 }, { "altitude", 327f }, { "distance", 90.72f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262891U }, { "position_lat", 463588537 }, { "position_long", -1131041383 }, { "altitude", 327f }, { "distance", 140.72f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262891U }, { "position_lat", 463588549 }, { "position_long", -1131041383 }, { "altitude", 327f }, { "distance", 140.82f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262897U }, { "position_lat", 463588537 }, { "position_long", -1131038293 }, { "altitude", 327.6f }, { "distance", 163.26f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262911U }, { "position_lat", 463588512 }, { "position_long", -1131032041 }, { "altitude", 328.4f }, { "distance", 208.75f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262918U }, { "position_lat", 463588499 }, { "position_long", -1131028879 }, { "altitude", 329f }, { "distance", 231.8f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262918U }, { "position_lat", 463588499 }, { "position_long", -1131028903 }, { "altitude", 329f }, { "distance", 231.97f }, { "speed", 3.0f } }, new Dictionary <string, object>() { { "timestamp", 961262933U }, { "position_lat", 463583127 }, { "position_long", -1131028903 }, { "altitude", 329f }, { "distance", 281.96f }, { "speed", 3.0f } }, }; // Create the output stream, this can be any type of stream, including a file or memory stream. Must have read/write access. FileStream fitDest = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); // Create a FIT Encode object Encode encoder = new Encode(ProtocolVersion.V10); // Write the FIT header to the output stream encoder.Open(fitDest); // Reference points for the course var firstRecord = courseData[0]; var lastRecord = courseData[courseData.Count - 1]; var halfwayRecord = courseData[courseData.Count / 2]; var startTimestamp = (uint)firstRecord["timestamp"]; var endTimestamp = (uint)lastRecord["timestamp"]; var startDateTime = new Dynastream.Fit.DateTime(startTimestamp); var endDateTime = new Dynastream.Fit.DateTime(endTimestamp); // Every FIT file MUST contain a File ID message var fileIdMesg = new FileIdMesg(); fileIdMesg.SetType(Dynastream.Fit.File.Course); fileIdMesg.SetManufacturer(Manufacturer.Development); fileIdMesg.SetProduct(ProductId); fileIdMesg.SetTimeCreated(startDateTime); fileIdMesg.SetSerialNumber(startDateTime.GetTimeStamp()); encoder.Write(fileIdMesg); // Every FIT file MUST contain a Course message var courseMesg = new CourseMesg(); courseMesg.SetName("Garmin Field Day"); courseMesg.SetSport(Sport.Cycling); encoder.Write(courseMesg); // Every FIT COURSE file MUST contain a Lap message var lapMesg = new LapMesg(); lapMesg.SetStartTime(startDateTime); lapMesg.SetTimestamp(startDateTime); lapMesg.SetTotalElapsedTime(endTimestamp - startTimestamp); lapMesg.SetTotalTimerTime(endTimestamp - startTimestamp); lapMesg.SetStartPositionLat((int)firstRecord["position_lat"]); lapMesg.SetStartPositionLong((int)firstRecord["position_long"]); lapMesg.SetEndPositionLat((int)lastRecord["position_lat"]); lapMesg.SetEndPositionLong((int)lastRecord["position_long"]); lapMesg.SetTotalDistance((float)lastRecord["distance"]); encoder.Write(lapMesg); // Timer Events are REQUIRED for FIT COURSE files var eventMesgStart = new EventMesg(); eventMesgStart.SetTimestamp(startDateTime); eventMesgStart.SetEvent(Event.Timer); eventMesgStart.SetEventType(EventType.Start); encoder.Write(eventMesgStart); // Every FIT COURSE file MUST contain Record messages foreach (var record in courseData) { var timestamp = (uint)record["timestamp"]; var latitude = (int)record["position_lat"]; var longitude = (int)record["position_long"]; var distance = (float)record["distance"]; var speed = (float)record["speed"]; var altitude = (float)record["altitude"]; var recordMesg = new RecordMesg(); recordMesg.SetTimestamp(new Dynastream.Fit.DateTime(timestamp)); recordMesg.SetPositionLat(latitude); recordMesg.SetPositionLong(longitude); recordMesg.SetDistance(distance); recordMesg.SetSpeed(speed); recordMesg.SetAltitude(altitude); encoder.Write(recordMesg); // Add a Course Point at the halfway point of the route if (record == halfwayRecord) { var coursePointMesg = new CoursePointMesg(); coursePointMesg.SetTimestamp(new Dynastream.Fit.DateTime(timestamp)); coursePointMesg.SetName("Halfway"); coursePointMesg.SetType(CoursePoint.Generic); coursePointMesg.SetPositionLat(latitude); coursePointMesg.SetPositionLong(longitude); coursePointMesg.SetDistance(distance); encoder.Write(coursePointMesg); } } // Timer Events are REQUIRED for FIT COURSE files var eventMesgStop = new EventMesg(); eventMesgStop.SetTimestamp(endDateTime); eventMesgStop.SetEvent(Event.Timer); eventMesgStop.SetEventType(EventType.StopAll); encoder.Write(eventMesgStop); // Update the data size in the header and calculate the CRC encoder.Close(); // Close the output stream fitDest.Close(); Console.WriteLine($"Encoded FIT file {fitDest.Name}"); }
public DeviceInfoMesg GetDeviceInfo(GarminDeviceInfo deviceInfo, Dynastream.Fit.DateTime startTime) { return(this.GetDeviceInfoMesg(deviceInfo, startTime)); }
private SessionMesg GetSessionMesg(Workout workout, WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime, Dynastream.Fit.DateTime endTime, ushort numLaps) { var sessionMesg = new SessionMesg(); sessionMesg.SetTimestamp(endTime); sessionMesg.SetStartTime(startTime); var totalTime = workoutSamples.Duration; sessionMesg.SetTotalElapsedTime(totalTime); sessionMesg.SetTotalTimerTime(totalTime); sessionMesg.SetTotalDistance(GetTotalDistance(workoutSamples)); sessionMesg.SetTotalWork((uint)workout.Total_Work); sessionMesg.SetTotalCalories((ushort)GetCalorieSummary(workoutSamples)?.Value); var outputSummary = GetOutputSummary(workoutSamples); sessionMesg.SetAvgPower((ushort?)outputSummary?.Average_Value); sessionMesg.SetMaxPower((ushort?)outputSummary?.Max_Value); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(numLaps); sessionMesg.SetThresholdPower((ushort)workout.Ftp_Info.Ftp); sessionMesg.SetEvent(Event.Lap); sessionMesg.SetEventType(EventType.Stop); sessionMesg.SetSport(GetGarminSport(workout)); sessionMesg.SetSubSport(GetGarminSubSport(workout)); var hrSummary = GetHeartRateSummary(workoutSamples); sessionMesg.SetAvgHeartRate((byte?)hrSummary?.Average_Value); sessionMesg.SetMaxHeartRate((byte?)hrSummary?.Max_Value); var cadenceSummary = GetCadenceSummary(workoutSamples); sessionMesg.SetAvgCadence((byte?)cadenceSummary?.Average_Value); sessionMesg.SetMaxCadence((byte?)cadenceSummary?.Max_Value); sessionMesg.SetMaxSpeed(GetMaxSpeedMetersPerSecond(workoutSamples)); sessionMesg.SetAvgSpeed(GetAvgSpeedMetersPerSecond(workoutSamples)); sessionMesg.SetAvgGrade(GetAvgGrade(workoutSamples)); sessionMesg.SetMaxPosGrade(GetMaxGrade(workoutSamples)); sessionMesg.SetMaxNegGrade(0.0f); // HR zones if (_config.Format.IncludeTimeInHRZones && workoutSamples.Metrics.Any()) { var hrz1 = GetHeartRateZone(1, workoutSamples); if (hrz1 is object) { sessionMesg.SetTimeInHrZone(1, hrz1?.Duration); } var hrz2 = GetHeartRateZone(2, workoutSamples); if (hrz2 is object) { sessionMesg.SetTimeInHrZone(2, hrz2?.Duration); } var hrz3 = GetHeartRateZone(3, workoutSamples); if (hrz3 is object) { sessionMesg.SetTimeInHrZone(3, hrz3?.Duration); } var hrz4 = GetHeartRateZone(4, workoutSamples); if (hrz4 is object) { sessionMesg.SetTimeInHrZone(4, hrz4?.Duration); } var hrz5 = GetHeartRateZone(5, workoutSamples); if (hrz5 is object) { sessionMesg.SetTimeInHrZone(5, hrz5?.Duration); } } // Power Zones if (_config.Format.IncludeTimeInPowerZones && workoutSamples.Metrics.Any()) { var zones = GetTimeInPowerZones(workout, workoutSamples); if (zones is object) { sessionMesg.SetTimeInPowerZone(1, zones.Zone1.Duration); sessionMesg.SetTimeInPowerZone(2, zones.Zone2.Duration); sessionMesg.SetTimeInPowerZone(3, zones.Zone3.Duration); sessionMesg.SetTimeInPowerZone(4, zones.Zone4.Duration); sessionMesg.SetTimeInPowerZone(5, zones.Zone5.Duration); sessionMesg.SetTimeInPowerZone(6, zones.Zone6.Duration); sessionMesg.SetTimeInPowerZone(7, zones.Zone7.Duration); } } return(sessionMesg); }
public ICollection <LapMesg> GetLaps(PreferredLapType preferredLapType, WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime, Sport sport, SubSport subSport) { using var tracing = Tracing.Trace($"{nameof(FitConverter)}.{nameof(GetLaps)}") .WithTag(TagKey.Format, FileFormat.Fit.ToString()); if ((preferredLapType == PreferredLapType.Default || preferredLapType == PreferredLapType.Class_Segments) && workoutSamples.Segment_List.Any()) { return(GetLapsBasedOnSegments(workoutSamples, startTime, sport, subSport)); } return(GetLapsBasedOnDistance(workoutSamples, startTime, sport, subSport)); }
static void OnMesg(object sender, MesgEventArgs e) { string activityType = e.mesg.Name; Console.WriteLine("OnMesg: Received Mesg with global ID#{0}, its name is {1}", e.mesg.Num, activityType); Trackpoint tp = new Trackpoint(); for (byte i = 0; i < e.mesg.GetNumFields(); i++) { foreach (var field in e.mesg.fields) { string fieldValue = field.GetValue().ToString(); string fieldName = field.GetName().ToString(); string recordType = e.mesg.fields[i].Num.ToString(); Console.WriteLine("\tField{0} Index{1} (\"{2}\" Field#{4}) Value: {3}", i, 0, fieldName, fieldValue, recordType); if (activityType == "FileId") { switch (fieldName) { case "TimeCreated": Dynastream.Fit.DateTime dt = new Dynastream.Fit.DateTime(uint.Parse(fieldValue)); tcx.Id = Trackpoint.ConvertDate(dt.GetDateTime()); tcx.StartTime = dt.GetDateTime(); break; default: break; } } if (activityType == "Record") { switch (fieldName) { case "Timestamp": Dynastream.Fit.DateTime dt = new Dynastream.Fit.DateTime(uint.Parse(fieldValue)); tp.Time = dt.GetDateTime(); break; case "HeartRate": tp.HeartRateBpm = int.Parse(fieldValue); break; case "PositionLat": tp.LatitudeDegrees = double.Parse(fieldValue); break; case "PositionLong": tp.LongitudeDegrees = double.Parse(fieldValue); break; case "Altitude": tp.AltitudeMeters = 300; //double.Parse(fieldValue); break; default: break; } } } } if (activityType == "Record") { tcx.TrackpointList.Add(tp); } }
public ICollection <LapMesg> GetLapsBasedOnDistance(WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime, Sport sport, SubSport subSport) { using var tracing = Tracing.Trace($"{nameof(FitConverter)}.{nameof(GetLapsBasedOnDistance)}") .WithTag(TagKey.Format, FileFormat.Fit.ToString()); var stepsAndLaps = new List <LapMesg>(); if (workoutSamples is null) { return(stepsAndLaps); } var speedMetrics = GetSpeedSummary(workoutSamples); if (speedMetrics is null) { return(stepsAndLaps); } var speedUnit = GetDistanceUnit(speedMetrics?.Display_Unit); var lapMeters = 0; switch (speedUnit) { case DistanceUnit.Kilometers: lapMeters = 1000; break; default: lapMeters = 1600; break; } LapMesg lap = null; ushort stepIndex = 0; var lapDistanceInMeters = 0f; float lapLengthSeconds = 0; for (var secondsSinceStart = 0; secondsSinceStart < speedMetrics.Values.Length; secondsSinceStart++) { if (lap is null || lap.GetTotalElapsedTime() is not null) { // Start new Lap var lapStartTime = new Dynastream.Fit.DateTime(startTime); lapStartTime.Add(secondsSinceStart); lap = new LapMesg(); lap.SetStartTime(lapStartTime); lap.SetMessageIndex(stepIndex); lap.SetEvent(Event.Lap); lap.SetLapTrigger(LapTrigger.Time); lap.SetSport(sport); lap.SetSubSport(subSport); lapLengthSeconds = 0; lapDistanceInMeters = 0f; } var currentSpeedInMPS = ConvertToMetersPerSecond(speedMetrics.GetValue(secondsSinceStart), workoutSamples); lapDistanceInMeters += 1 * currentSpeedInMPS; lapLengthSeconds++; if (lapDistanceInMeters >= lapMeters || secondsSinceStart == speedMetrics.Values.Length - 1) { lap.SetTotalElapsedTime(lapLengthSeconds); lap.SetTotalTimerTime(lapLengthSeconds); lap.SetTotalDistance(lapDistanceInMeters); stepsAndLaps.Add(lap); stepIndex++; } } return(stepsAndLaps); }
static void OnFileIDMesg(object sender, MesgEventArgs e) { Console.WriteLine("FileIdHandler: Received {1} Mesg with global ID#{0}", e.mesg.Num, e.mesg.Name); FileIdMesg myFileId = (FileIdMesg)e.mesg; try { Console.WriteLine("\tType: {0}", myFileId.GetType()); Console.WriteLine("\tManufacturer: {0}", myFileId.GetManufacturer()); Console.WriteLine("\tProduct: {0}", myFileId.GetProduct()); Console.WriteLine("\tSerialNumber {0}", myFileId.GetSerialNumber()); Console.WriteLine("\tNumber {0}", myFileId.GetNumber()); Console.WriteLine("\tTimeCreated {0}", myFileId.GetTimeCreated()); Dynastream.Fit.DateTime dtTime = new Dynastream.Fit.DateTime(myFileId.GetTimeCreated().GetTimeStamp()); } catch (FitException exception) { Console.WriteLine("\tOnFileIDMesg Error {0}", exception.Message); Console.WriteLine("\t{0}", exception.InnerException); } }
static public void CreateLapSwimActivity() { // Example Swim Data representing a 500 yard pool swim using different strokes and drills. var swimData = new List <Dictionary <string, object> >() { new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Freestyle" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Freestyle" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Freestyle" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Freestyle" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Backstroke" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Backstroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Backstroke" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Backstroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Breaststroke" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Breaststroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Breaststroke" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Breaststroke" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 20U }, { "stroke", "Butterfly" }, { "strokes", 30U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 25U }, { "stroke", "Butterfly" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 30U }, { "stroke", "Butterfly" }, { "strokes", 10U } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 35U }, { "stroke", "Butterfly" }, { "strokes", 20U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Idle" }, { "duration", 60U } }, new Dictionary <string, object>() { { "type", "Lap" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Active" }, { "duration", 40U }, { "stroke", "Drill" } }, new Dictionary <string, object>() { { "type", "Lap" } }, }; const string FileName = "ActivityEncodeRecipeLapSwim.fit"; var messages = new List <Mesg>(); // The starting timestamp for the activity var startTime = new Dynastream.Fit.DateTime(System.DateTime.UtcNow); // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStart = new EventMesg(); eventMesgStart.SetTimestamp(startTime); eventMesgStart.SetEvent(Event.Timer); eventMesgStart.SetEventType(EventType.Start); messages.Add(eventMesgStart); // // Create a Length or Lap message for each item in the sample swim data. Calculate // distance, duration, and stroke count for each lap and the overall session. // // Session Accumulators uint sessionTotalElapsedTime = 0; float sessionDistance = 0; ushort sessionNumLengths = 0; ushort sessionNumActiveLengths = 0; ushort sessionTotalStrokes = 0; ushort sessionNumLaps = 0; // Lap accumulators uint lapTotalElapsedTime = 0; float lapDistance = 0; ushort lapNumActiveLengths = 0; ushort lapNumLengths = 0; ushort lapFirstLengthIndex = 0; ushort lapTotalStrokes = 0; var lapStartTime = new Dynastream.Fit.DateTime(startTime); var poolLength = 22.86f; var poolLengthUnit = DisplayMeasure.Statute; var timestamp = new Dynastream.Fit.DateTime(startTime); ushort messageIndex = 0; foreach (var swimLength in swimData) { string type = (string)swimLength["type"]; if (type.Equals("Lap")) { // Create a Lap message, set its fields, and write it to the file var lapMesg = new LapMesg(); lapMesg.SetMessageIndex(sessionNumLaps); lapMesg.SetTimestamp(timestamp); lapMesg.SetStartTime(lapStartTime); lapMesg.SetTotalElapsedTime(lapTotalElapsedTime); lapMesg.SetTotalTimerTime(lapTotalElapsedTime); lapMesg.SetTotalDistance(lapDistance); lapMesg.SetFirstLengthIndex(lapFirstLengthIndex); lapMesg.SetNumActiveLengths(lapNumActiveLengths); lapMesg.SetNumLengths(lapNumLengths); lapMesg.SetTotalStrokes(lapTotalStrokes); lapMesg.SetAvgStrokeDistance(lapDistance / lapTotalStrokes); lapMesg.SetSport(Sport.Swimming); lapMesg.SetSubSport(SubSport.LapSwimming); messages.Add(lapMesg); sessionNumLaps++; // Reset the Lap accumulators lapFirstLengthIndex = messageIndex; lapNumActiveLengths = 0; lapNumLengths = 0; lapTotalElapsedTime = 0; lapDistance = 0; lapTotalStrokes = 0; lapStartTime = new Dynastream.Fit.DateTime(timestamp); } else { uint duration = (uint)swimLength["duration"]; var lengthType = (LengthType)Enum.Parse(typeof(LengthType), type); // Create a Length message and its fields var lengthMesg = new LengthMesg(); lengthMesg.SetMessageIndex(messageIndex++); lengthMesg.SetStartTime(timestamp); lengthMesg.SetTotalElapsedTime(duration); lengthMesg.SetTotalTimerTime(duration); lengthMesg.SetLengthType(lengthType); timestamp.Add(duration); lengthMesg.SetTimestamp(timestamp); // Create the Record message that pairs with the Length Message var recordMesg = new RecordMesg(); recordMesg.SetTimestamp(timestamp); recordMesg.SetDistance(sessionDistance + poolLength); // Is this an Active Length? if (lengthType == LengthType.Active) { // Get the Active data from the model string stroke = swimLength.ContainsKey("stroke") ? (String)swimLength["stroke"] : "Freestyle"; uint strokes = swimLength.ContainsKey("strokes") ? (uint)swimLength["strokes"] : 0; SwimStroke swimStroke = (SwimStroke)Enum.Parse(typeof(SwimStroke), stroke); // Set the Active data on the Length Message lengthMesg.SetAvgSpeed(poolLength / (float)duration); lengthMesg.SetSwimStroke(swimStroke); if (strokes > 0) { lengthMesg.SetTotalStrokes((ushort)strokes); lengthMesg.SetAvgSwimmingCadence((byte)(strokes * 60U / duration)); } // Set the Active data on the Record Message recordMesg.SetSpeed(poolLength / (float)duration); if (strokes > 0) { recordMesg.SetCadence((byte)((strokes * 60U) / duration)); } // Increment the "Active" accumulators sessionNumActiveLengths++; lapNumActiveLengths++; sessionDistance += poolLength; lapDistance += poolLength; sessionTotalStrokes += (ushort)strokes; lapTotalStrokes += (ushort)strokes; } // Write the messages to the file messages.Add(recordMesg); messages.Add(lengthMesg); // Increment the "Total" accumulators sessionTotalElapsedTime += duration; lapTotalElapsedTime += duration; sessionNumLengths++; lapNumLengths++; } } // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStop = new EventMesg(); eventMesgStop.SetTimestamp(timestamp); eventMesgStop.SetEvent(Event.Timer); eventMesgStop.SetEventType(EventType.StopAll); messages.Add(eventMesgStop); // Every FIT ACTIVITY file MUST contain at least one Session message var sessionMesg = new SessionMesg(); sessionMesg.SetMessageIndex(0); sessionMesg.SetTimestamp(timestamp); sessionMesg.SetStartTime(startTime); sessionMesg.SetTotalElapsedTime(sessionTotalElapsedTime); sessionMesg.SetTotalTimerTime(sessionTotalElapsedTime); sessionMesg.SetTotalDistance(sessionDistance); sessionMesg.SetSport(Sport.Swimming); sessionMesg.SetSubSport(SubSport.LapSwimming); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(sessionNumLaps); sessionMesg.SetPoolLength(poolLength); sessionMesg.SetPoolLengthUnit(poolLengthUnit); sessionMesg.SetNumLengths(sessionNumLengths); sessionMesg.SetNumActiveLengths(sessionNumActiveLengths); sessionMesg.SetTotalStrokes(sessionTotalStrokes); sessionMesg.SetAvgStrokeDistance(sessionDistance / sessionTotalStrokes); messages.Add(sessionMesg); // Every FIT ACTIVITY file MUST contain EXACTLY one Activity message var activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(timestamp); activityMesg.SetNumSessions(1); var timezoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalSeconds; activityMesg.SetLocalTimestamp((uint)((int)timestamp.GetTimeStamp() + timezoneOffset)); activityMesg.SetTotalTimerTime(sessionTotalElapsedTime); messages.Add(activityMesg); CreateActivityFile(messages, FileName, startTime); }
private Dynastream.Fit.DateTime AddMetrics(ICollection <Mesg> messages, WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime) { var allMetrics = workoutSamples.Metrics; var hrMetrics = allMetrics.FirstOrDefault(m => m.Slug == "heart_rate"); var outputMetrics = allMetrics.FirstOrDefault(m => m.Slug == "output"); var cadenceMetrics = allMetrics.FirstOrDefault(m => m.Slug == "cadence"); var speedMetrics = GetSpeedSummary(workoutSamples); var resistanceMetrics = allMetrics.FirstOrDefault(m => m.Slug == "resistance"); var inclineMetrics = GetGradeSummary(workoutSamples); var locationMetrics = workoutSamples.Location_Data?.SelectMany(x => x.Coordinates).ToArray(); var altitudeMetrics = allMetrics.FirstOrDefault(m => m.Slug == "altitude"); var recordsTimeStamp = new Dynastream.Fit.DateTime(startTime); if (workoutSamples.Seconds_Since_Pedaling_Start is object) { for (var i = 0; i < workoutSamples.Seconds_Since_Pedaling_Start.Count; i++) { var record = new RecordMesg(); record.SetTimestamp(recordsTimeStamp); if (speedMetrics is object && i < speedMetrics.Values.Length) { record.SetSpeed(ConvertToMetersPerSecond(speedMetrics.GetValue(i), workoutSamples)); } if (hrMetrics is object && i < hrMetrics.Values.Length) { record.SetHeartRate((byte)hrMetrics.Values[i]); } if (cadenceMetrics is object && i < cadenceMetrics.Values.Length) { record.SetCadence((byte)cadenceMetrics.Values[i]); } if (outputMetrics is object && i < outputMetrics.Values.Length) { record.SetPower((ushort)outputMetrics.Values[i]); } if (resistanceMetrics is object && i < resistanceMetrics.Values.Length) { var resistancePercent = resistanceMetrics.Values[i] / 1; record.SetResistance((byte)(254 * resistancePercent)); } if (altitudeMetrics is object && i < altitudeMetrics.Values.Length) { var altitude = ConvertDistanceToMeters(altitudeMetrics.GetValue(i), altitudeMetrics.Display_Unit); record.SetAltitude(altitude); } if (inclineMetrics is object && i < inclineMetrics.Values.Length) { record.SetGrade((float)inclineMetrics.GetValue(i)); } if (locationMetrics is object && i < locationMetrics.Length) { // unit is semicircles record.SetPositionLat(ConvertDegreesToSemicircles(locationMetrics[i].Latitude)); record.SetPositionLong(ConvertDegreesToSemicircles(locationMetrics[i].Longitude)); } messages.Add(record); recordsTimeStamp.Add(1); } } return(recordsTimeStamp); }
static public void CreateTimeBasedActivity() { const double TwoPI = Math.PI * 2.0; const double SemicirclesPerMeter = 107.173; const string FileName = "ActivityEncodeRecipe.fit"; var messages = new List <Mesg>(); // The starting timestamp for the activity var startTime = new Dynastream.Fit.DateTime(System.DateTime.UtcNow); // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStart = new EventMesg(); eventMesgStart.SetTimestamp(startTime); eventMesgStart.SetEvent(Event.Timer); eventMesgStart.SetEventType(EventType.Start); messages.Add(eventMesgStart); // Create the Developer Id message for the developer data fields. var developerIdMesg = new DeveloperDataIdMesg(); // It is a BEST PRACTICE to reuse the same Guid for all FIT files created by your platform byte[] appId = new Guid("00010203-0405-0607-0809-0A0B0C0D0E0F").ToByteArray(); for (int i = 0; i < appId.Length; i++) { developerIdMesg.SetApplicationId(i, appId[i]); } developerIdMesg.SetDeveloperDataIndex(0); developerIdMesg.SetApplicationVersion(110); messages.Add(developerIdMesg); // Create the Developer Data Field Descriptions var doughnutsFieldDescMesg = new FieldDescriptionMesg(); doughnutsFieldDescMesg.SetDeveloperDataIndex(0); doughnutsFieldDescMesg.SetFieldDefinitionNumber(0); doughnutsFieldDescMesg.SetFitBaseTypeId(FitBaseType.Float32); doughnutsFieldDescMesg.SetFieldName(0, "Doughnuts Earned"); doughnutsFieldDescMesg.SetUnits(0, "doughnuts"); doughnutsFieldDescMesg.SetNativeMesgNum(MesgNum.Session); messages.Add(doughnutsFieldDescMesg); FieldDescriptionMesg hrFieldDescMesg = new FieldDescriptionMesg(); hrFieldDescMesg.SetDeveloperDataIndex(0); hrFieldDescMesg.SetFieldDefinitionNumber(1); hrFieldDescMesg.SetFitBaseTypeId(FitBaseType.Uint8); hrFieldDescMesg.SetFieldName(0, "Heart Rate"); hrFieldDescMesg.SetUnits(0, "bpm"); hrFieldDescMesg.SetNativeFieldNum(RecordMesg.FieldDefNum.HeartRate); hrFieldDescMesg.SetNativeMesgNum(MesgNum.Record); messages.Add(hrFieldDescMesg); // Every FIT ACTIVITY file MUST contain Record messages var timestamp = new Dynastream.Fit.DateTime(startTime); // Create one hour (3600 seconds) of Record data for (uint i = 0; i <= 3600; i++) { // Create a new Record message and set the timestamp var recordMesg = new RecordMesg(); recordMesg.SetTimestamp(timestamp); // Fake Record Data of Various Signal Patterns recordMesg.SetDistance(i); // Ramp recordMesg.SetSpeed(1); // Flatline recordMesg.SetHeartRate((byte)((Math.Sin(TwoPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine recordMesg.SetCadence((byte)(i % 255)); // Sawtooth recordMesg.SetPower((ushort)((i % 255) < 127 ? 150 : 250)); // Square recordMesg.SetAltitude((float)Math.Abs(((double)i % 255.0) - 127.0)); // Triangle recordMesg.SetPositionLat(0); recordMesg.SetPositionLong((int)Math.Round(i * SemicirclesPerMeter)); // Add a Developer Field to the Record Message var hrDevField = new DeveloperField(hrFieldDescMesg, developerIdMesg); recordMesg.SetDeveloperField(hrDevField); hrDevField.SetValue((byte)((Math.Sin(TwoPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine // Write the Rercord message to the output stream messages.Add(recordMesg); // Increment the timestamp by one second timestamp.Add(1); } // Timer Events are a BEST PRACTICE for FIT ACTIVITY files var eventMesgStop = new EventMesg(); eventMesgStop.SetTimestamp(timestamp); eventMesgStop.SetEvent(Event.Timer); eventMesgStop.SetEventType(EventType.StopAll); messages.Add(eventMesgStop); // Every FIT ACTIVITY file MUST contain at least one Lap message var lapMesg = new LapMesg(); lapMesg.SetMessageIndex(0); lapMesg.SetTimestamp(timestamp); lapMesg.SetStartTime(startTime); lapMesg.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); lapMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); messages.Add(lapMesg); // Every FIT ACTIVITY file MUST contain at least one Session message var sessionMesg = new SessionMesg(); sessionMesg.SetMessageIndex(0); sessionMesg.SetTimestamp(timestamp); sessionMesg.SetStartTime(startTime); sessionMesg.SetTotalElapsedTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); sessionMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); sessionMesg.SetSport(Sport.StandUpPaddleboarding); sessionMesg.SetSubSport(SubSport.Generic); sessionMesg.SetFirstLapIndex(0); sessionMesg.SetNumLaps(1); // Add a Developer Field to the Session message var doughnutsEarnedDevField = new DeveloperField(doughnutsFieldDescMesg, developerIdMesg); doughnutsEarnedDevField.SetValue(sessionMesg.GetTotalElapsedTime() / 1200.0f); sessionMesg.SetDeveloperField(doughnutsEarnedDevField); messages.Add(sessionMesg); // Every FIT ACTIVITY file MUST contain EXACTLY one Activity message var activityMesg = new ActivityMesg(); activityMesg.SetTimestamp(timestamp); activityMesg.SetNumSessions(1); var timezoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalSeconds; activityMesg.SetLocalTimestamp((uint)((int)timestamp.GetTimeStamp() + timezoneOffset)); activityMesg.SetTotalTimerTime(timestamp.GetTimeStamp() - startTime.GetTimeStamp()); messages.Add(activityMesg); CreateActivityFile(messages, FileName, startTime); }
private Dictionary <int, Tuple <WorkoutStepMesg, LapMesg> > GetWorkoutStepsAndLaps(WorkoutSamples workoutSamples, Dynastream.Fit.DateTime startTime, Sport sport, SubSport subSport) { var stepsAndLaps = new Dictionary <int, Tuple <WorkoutStepMesg, LapMesg> >(); if (workoutSamples is null) { return(stepsAndLaps); } var cadenceTargets = workoutSamples.Target_Performance_Metrics?.Target_Graph_Metrics?.FirstOrDefault(w => w.Type == "cadence")?.Graph_Data; if (cadenceTargets is null) { return(stepsAndLaps); } uint previousCadenceLower = 0; uint previousCadenceUpper = 0; ushort stepIndex = 0; var duration = 0; float lapDistanceInMeters = 0; WorkoutStepMesg workoutStep = null; LapMesg lapMesg = null; var speedMetrics = GetSpeedSummary(workoutSamples); foreach (var secondSinceStart in workoutSamples.Seconds_Since_Pedaling_Start) { var index = secondSinceStart <= 0 ? 0 : secondSinceStart - 1; duration++; if (speedMetrics is object && index < speedMetrics.Values.Length) { var currentSpeedInMPS = ConvertToMetersPerSecond(speedMetrics.GetValue(index), workoutSamples); lapDistanceInMeters += 1 * currentSpeedInMPS; } var currentCadenceLower = index < cadenceTargets.Lower.Length ? (uint)cadenceTargets.Lower[index] : 0; var currentCadenceUpper = index < cadenceTargets.Upper.Length ? (uint)cadenceTargets.Upper[index] : 0; if (currentCadenceLower != previousCadenceLower || currentCadenceUpper != previousCadenceUpper) { if (workoutStep != null && lapMesg != null) { workoutStep.SetDurationValue((uint)duration * 1000); // milliseconds var lapEndTime = new Dynastream.Fit.DateTime(startTime); lapEndTime.Add(secondSinceStart); lapMesg.SetTotalElapsedTime(duration); lapMesg.SetTotalTimerTime(duration); lapMesg.SetTimestamp(lapEndTime); lapMesg.SetEventType(EventType.Stop); lapMesg.SetTotalDistance(lapDistanceInMeters); stepsAndLaps.Add(stepIndex, new Tuple <WorkoutStepMesg, LapMesg>(workoutStep, lapMesg)); stepIndex++; duration = 0; lapDistanceInMeters = 0; } workoutStep = new WorkoutStepMesg(); workoutStep.SetDurationType(WktStepDuration.Time); workoutStep.SetMessageIndex(stepIndex); workoutStep.SetTargetType(WktStepTarget.Cadence); workoutStep.SetCustomTargetValueHigh(currentCadenceUpper); workoutStep.SetCustomTargetValueLow(currentCadenceLower); workoutStep.SetIntensity(currentCadenceUpper > 60 ? Intensity.Active : Intensity.Rest); lapMesg = new LapMesg(); var lapStartTime = new Dynastream.Fit.DateTime(startTime); lapStartTime.Add(secondSinceStart); lapMesg.SetStartTime(lapStartTime); lapMesg.SetWktStepIndex(stepIndex); lapMesg.SetMessageIndex(stepIndex); lapMesg.SetEvent(Event.Lap); lapMesg.SetLapTrigger(LapTrigger.Time); lapMesg.SetSport(sport); lapMesg.SetSubSport(subSport); previousCadenceLower = currentCadenceLower; previousCadenceUpper = currentCadenceUpper; } } return(stepsAndLaps); }
void BtnSaveFitClick(object sender, EventArgs e) { if (saveFileDlg.ShowDialog() == DialogResult.OK) { FileIdMesg fileIdMesg = new FileIdMesg(); fileIdMesg.SetManufacturer(Manufacturer.Dynastream); fileIdMesg.SetProduct(1000); fileIdMesg.SetSerialNumber(12345); FileStream fitDest = new FileStream(saveFileDlg.FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); // create file encode object Encode encode = new Encode(); // write out header encode.Open(fitDest); // encode each message, a definition message is automatically generated and output if necessary encode.Write(fileIdMesg); SQLiteCommand cmd = new SQLiteCommand(_db); string sql; // TODO: write FIT File Summary Information here, suspect that's why GarminConnect // is rejecting it because the summary data isn't there. sql = string.Format("select fs.*, f.fileActivityDateTime from FileSummary fs join File f on f.idFile = fs.idFile where fs.idFile = {0}", _file); cmd.CommandText = sql; SQLiteDataReader rdr_summary = cmd.ExecuteReader(); if (rdr_summary.HasRows) { rdr_summary.Read(); SessionMesg sess = new SessionMesg(); //"fsMaxHeartRate" INTEGER,"fsMaxCadence" INTEGER,"fsMaxSpeed" FLOAT, "fsAvgPower" FLOAT, "fsMaxPower" FLOAT) sess.SetTotalElapsedTime((float)Convert.ToDouble(rdr_summary["fsDuration"])); sess.SetTotalMovingTime((float)Convert.ToDouble(rdr_summary["fsMovingTime"])); sess.SetTotalTimerTime((float)Convert.ToDouble(rdr_summary["fsMovingTime"])); sess.SetTotalDistance((float)(Convert.ToDouble(rdr_summary["fsDistance"]) * 1609.344)); // convert miles back to metres sess.SetTotalCalories(Convert.ToUInt16(rdr_summary["fsCalories"])); sess.SetAvgHeartRate(Convert.ToByte(Convert.ToInt32(rdr_summary["fsAvgHeart"]))); sess.SetAvgCadence(Convert.ToByte(Convert.ToInt32(rdr_summary["fsAvgCadence"]))); sess.SetAvgSpeed((float)(Convert.ToDouble(rdr_summary["fsAvgSpeed"]) / 2.23693629)); sess.SetTotalAscent(Convert.ToUInt16(Convert.ToDouble(rdr_summary["fsTotalAscent"]) / 3.2808399)); sess.SetTotalDescent(Convert.ToUInt16(Convert.ToDouble(rdr_summary["fsTotalDescent"]) / 3.2808399)); sess.SetMaxHeartRate(Convert.ToByte(Convert.ToInt32(rdr_summary["fsMaxHeartRate"]))); sess.SetMaxCadence(Convert.ToByte(Convert.ToInt32(rdr_summary["fsMaxCadence"]))); sess.SetMaxSpeed(Convert.ToByte(Convert.ToInt32(rdr_summary["fsMaxSpeed"]) / 2.23693629)); Dynastream.Fit.DateTime dt_start = new Dynastream.Fit.DateTime(Convert.ToDateTime(rdr_summary["fileActivityDateTime"])); sess.SetStartTime(dt_start); encode.Write(sess); } rdr_summary.Close(); // load and process the archived trackpoints for file sql = string.Format("select * from FileTrackpoints where idFile = {0}", _file); cmd.CommandText = sql; SQLiteDataReader rdr = cmd.ExecuteReader(); if (rdr.HasRows) { while (rdr.Read()) { // TODO: need to handle `0` lng/lat coordinates so that we're not mapped // as being in the middle of the atlantic ! :-) RecordMesg rec = new RecordMesg(); Dynastream.Fit.DateTime dt = new Dynastream.Fit.DateTime(System.DateTime.Parse((string)rdr["tpTime"])); rec.SetTimestamp(dt); rec.SetDistance((float)Convert.ToDouble(rdr["tpDistance"])); rec.SetHeartRate(Convert.ToByte(Convert.ToInt32(rdr["tpHeart"]))); rec.SetCadence(Convert.ToByte(Convert.ToInt32(rdr["tpCadence"]))); rec.SetTemperature(Convert.ToSByte(Convert.ToInt32(rdr["tpTemperature"]))); rec.SetAltitude((float)(Convert.ToDouble(rdr["tpAltitude"]) / 3.2808399)); // converted back to metres from ft in db rec.SetPositionLong((int)GeoMath.degrees_to_semicircle(Convert.ToDouble(rdr["tpLongitude"]))); rec.SetPositionLat((int)GeoMath.degrees_to_semicircle(Convert.ToDouble(rdr["tpLatitude"]))); rec.SetSpeed((float)Convert.ToDouble(rdr["tpSpeed"])); encode.Write(rec); } } encode.Close(); fitDest.Close(); MessageBox.Show("File Saved Successfully"); System.Diagnostics.Process.Start(saveFileDlg.FileName); } else { } }