Karvonen Heart Rate using C# and Spectre.Console
Note: This post is part of the .NET Advent 2022! Check out the rest of the entries here!
As we approach the new year, many people are probably starting to think about getting back into an exercise program. It doesn't matter if you're biking, running, walking, or lifting weights; you need to ensure you're doing it healthfully. I tend to rush into things thinking I'm still 20 when I'm not, which results in enough soreness to make me quit, OR even worse, an injury. As I get older, it's really important that I stay active, but I also want to make sure I'm not overdoing it.
For some background on heart rate, check out How’s your heart rate and why it matters?
So what does all that have to do with .NET? I'm a huge fan of Brian P. Hogan's Exercises for Programmers published back in 2015. I've done several exercises in the book over the years and even did one for the Second Annual C# Advent in 2018. This year one exercise in particular looked fun: #31 - Karvonen Heart Rate.
From the exercise
When getting into a fitness program, you may want to figure out your target heart rate so you don’t overexert yourself. The Karvonen heartrate formula is one method you can use to determine your rate. Create a program that prompts for your age and your resting heart rate. Use the Karvonen formula to determine the target heart rate based on a range of intensities from 55% to 95%. Generate a table with the results as shown in the example output.
I decided to tackle this one as a C# console application, but since I've heard so much about Spectre.Console, I also wanted to use it. I am using .NET 7, but there's nothing specific to .NET 7 or C# 11 in the solution I'm about to present.
The requirements:
- Create a program that prompts for your age and your resting heart rate.
- Use the Karvonen formula to determine the target heart rate based on a range of intensities from 55% to 95%.
- Generate a table with the results as shown in the example output.
You can start by creating a console application:
dotnet new console -o karvonen
dotnet run
The next step is to add Spectre.Console to the project.
dotnet add package Spectre.Console
In order to prompt for the age and resting heart rate, you can use Spectre.Console instead
of rolling your own input-handling code. In the simplest case, you can write something like
this (after adding 'using Spectre.Console;'):
var age = AnsiConsole.Ask<int>("What's your [green]age[/]?");
var age = AnsiConsole.Prompt(
new TextPrompt<int>("How old are you?")
.PromptStyle("green")
.ValidationErrorMessage("[red]That's not a valid age[/]")
.Validate(age =>
age switch
{
<= 16 => ValidationResult.Error("[red]You must at least be 16 years old.[/]"),
>= 101 => ValidationResult.Error("[red]You must be younger than 100.[/]"),
_ => ValidationResult.Success(),
})
);
To request the resting heart rate, you can do something similar:
var restingHeartRate = AnsiConsole.Prompt(
new TextPrompt<int>("What is your resting heart rate?")
.PromptStyle("green")
.ValidationErrorMessage("[red]That's not a valid heart rate[/]")
.Validate(rhr =>
rhr switch
{
<= 20 => ValidationResult.Error("[red]Your resting heart rate should be greater than 20.[/]"),
>= 101 => ValidationResult.Error("[red]Your resting heart rate should NOT be greater than 100.[/]"),
_ => ValidationResult.Success(),
})
);
That hits the first requirement, so now on to using those values and calculating the data that will
be presented in the final step. The formula for Karvonen is:
target = (((220 - age) - restingHeartRate) * intensity) + restingHeartRate
decimal lowerBounds = 55m;
decimal upperBounds = 95m;
int step = 5;
for(decimal counter = lowerBounds; counter <= upperBounds; counter += step)
{
var intensity = counter / 100;
var target = (int)(((220 - age - restingHeartRate) * intensity) + restingHeartRate);
Console.WriteLine($"Intensity: {intensity:P0} : {target} bpm");
}
Ok, so, requirements one and two are done, now to get the output in a tabular format.
For this you can go back to Spectre.Console. It's such a great package that makes this
kind of thing really easy!
var table = new Table();
table.AddColumn("Intensity");
table.AddColumn("Rate");
Going back to the loop where the calculation was performed, you can get rid of the
Console.WriteLine and add a row to the table for each calculated target:
decimal lowerBounds = 55m;
decimal upperBounds = 95m;
int step = 5;
var table = new Table();
table.AddColumn("Intensity");
table.AddColumn("Rate");
for(decimal counter = lowerBounds; counter <= upperBounds; counter += step)
{
var intensity = counter / 100;
var target = (int)(((220 - age - restingHeartRate) * intensity) + restingHeartRate);
table.AddRow($"{intensity:P0}", $"{target} bpm");
}
AnsiConsole.Write(table);
As a bonus, you can add the very simple calculation for max heart rate:
Console.WriteLine($"Your maximum heart rate: {220 - age}");
This was a fun exercise, and I can certainly think of ways to refactor the code, but for now, I'm happy with it. I hope you enjoyed this one as much as I did.
Comments