Continuing to improve on my C skills.
This is a calculator program that runs in the terminal.
Given a segment of wire of a certain material, gauge, and length, calculates the total number of free electrons (electrons with high mobility), free charge density, and drift velocity at 1 amp.
Source also available in the repo https://github.com/BitCruncher0/FreeElectron
main.c
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "defines.h"
#include "prompt.h"
#include "convert.h"
#include "geometry.h"
#include "material.h"
#include "wire.h"
double calc_drift_speed(double current, double carrier_density, double area)
{
return current / (carrier_density * ELECTRONIC_CHARGE * area);
}
int main(void)
{
#ifdef RUN_REAL_PROGRAM
/* Todo
1. Prompt for radius instead of AWG */
/* PROGRAM FLOW
1. Prompt for wire material
2. Prompt for AWG
3. Prompt for wire length
4. Give statistics
To quit, press enter at prompts */
while(1) {
char request_program_quit;
const struct Material *material = prompt_material(&request_program_quit);
if(request_program_quit) return 0;
int awg = prompt_awg(&request_program_quit);
if(request_program_quit) return 0;
double length = prompt_length(&request_program_quit);
if(request_program_quit) return 0;
double diameter = awg_diameter(awg);
double radius = diameter / 2.0;
double area = area_circle(radius);
double volume = volume_cylinder(radius, length);
double volume_in_cm3 = mm3_to_cm3(volume);
// mass = density x volume
double mass_in_grams =
material->VOLUMETRIC_DENSITY * volume_in_cm3;
// moles = mass / molar mass
double moles =
mass_in_grams / material->ATOMIC_MASS;
double atoms =
AVOGADROS_NUMBER * moles;
double free_electrons =
material->FREE_ELECTRONS_PER_ATOM * atoms;
double free_charge =
free_electrons * -ELECTRONIC_CHARGE;
double carrier_density =
free_electrons / volume;
double drift_speed =
calc_drift_speed(1.0, carrier_density, area);
fputc('\n', stdout);
printf("%i AWG %s\n", awg, material->NAME);
printf(
"radius: %.*F mm\tdia: %.*F mm\tarea: %.*F mm^2\n",
PRECISION,
radius,
PRECISION,
diameter,
PRECISION,
area);
printf("volume: %.*F mm^3\n", PRECISION, volume);
printf("mass: %.*F g\n", PRECISION, mass_in_grams);
printf("moles: %.*F mol\n", 2 * PRECISION, moles);
printf("atoms: %.*E atom\n", PRECISION, atoms);
printf("free elec: %.*E elec\n", PRECISION, free_electrons);
printf("free charge: %.*E C\n", 2 * PRECISION, free_charge);
printf("carrier density: %.*E elec/mm^3\n", PRECISION, carrier_density);
printf("sdrift @ 1 A: %.*E mm/s\n", PRECISION, drift_speed);
fputc('\n', stdout);
}
#endif
}
defines.h
#pragma once
#define RUN_REAL_PROGRAM
#define PRECISION 3
#define PI 3.14159
#define AVOGADROS_NUMBER 6.022 * pow(10, 23)
#define ELECTRONIC_CHARGE 1.602 * pow(10, -19)
#define INPUT_BUFFER_SIZE 255
prompt.h
#pragma once
/* Prints out a message and prompts the user for input.
The zero-terminated input is stored in the buffer; the newline is stripped.
Characters are read up until the first newline or until
num_chars - 1 are read, whichever comes first.
Returns a pointer to the buffer. */
char *prompt(const char *msg, char *buffer, size_t num_chars);
/* Like prompt(), except a quit string is given.
If the input is the same as quit_str, returns 1.
Returns 0 otherwise. */
char prompt_quit(
const char *msg, char *buffer, size_t num_chars, const char *quit_str);
/* Continually prompts the user for a long-formatted string until one is given
or until the input is equal to quit_str.
If the input was equal to quit_str, then the flag pointed to by req is set
and returns 0.
Otherwise, the flag pointed to by req is cleared
and returns the long value. */
long prompt_long(const char *msg, const char *quit_str, char *req);
double prompt_double(const char *msg, const char *quit_str, char *req);
/* Strips str of the newline character at the end.
Returns 0 on successful removal or 1 if no newline
character is found.
str points to a zero-terminated string that contains at most one
newline character. */
char removeNewline(char *str);
prompt.c
#include <stdio.h>
#include <string.h>
#include "defines.h"
#include "prompt.h"
#include "convert.h"
char *prompt(const char *msg, char *buffer, size_t num_chars)
{
fputs(msg, stdout);
fgets(buffer, num_chars, stdin);
removeNewline(buffer);
return buffer;
}
char prompt_quit(
const char *msg, char *buffer, size_t num_chars, const char *quit_str)
{
if(strcmp(prompt(msg, buffer, num_chars), quit_str)) return 0;
else return 1;
}
long prompt_long(const char *msg, const char *quit_str, char *request_quit)
{
while(1) {
char buf[INPUT_BUFFER_SIZE + 1];
if(prompt_quit(msg, buf, INPUT_BUFFER_SIZE + 1, quit_str)) {
*request_quit = 1;
return 0;
}
// Attempt to convert string to numerical
char conversion_error;
long result = cstr_to_long(buf, &conversion_error);
if(conversion_error) {
fputs("Invalid input\n\n", stdout);
continue;
}
*request_quit = 0;
return result;
}
}
double prompt_double(const char *msg, const char *quit_str, char *request_quit)
{
while(1) {
char buf[INPUT_BUFFER_SIZE + 1];
if(prompt_quit(msg, buf, INPUT_BUFFER_SIZE + 1, quit_str)) {
*request_quit = 1;
return 0;
}
// Attempt to convert string to numerical
char conversion_error;
double result = cstr_to_double(buf, &conversion_error);
if(conversion_error) {
fputs("Invalid input\n\n", stdout);
continue;
}
*request_quit = 0;
return result;
}
}
char removeNewline(char *str)
{
int i;
for(i = 0; str[i] != 0; i++) {
if(str[i] == '\n') {
str[i] = 0;
return 0;
}
}
return 1;
}
convert.h
#pragma once
/* Converts zero-terminated string to long int.
If no conversion could be made, flag at conversion_error is set and
returns 0.
Otherwise, flag at conversion_error is cleared and returns the value. */
long cstr_to_long(const char *, char *conversion_error);
double cstr_to_double(const char *, char *conversion_error);
convert.c
#include <stdlib.h>
long cstr_to_long(const char *str, char *conversion_err)
{
char *end;
long result = strtol(str, &end, 10);
*conversion_err = ((*end) == 0) ? 0 : 1;
return result;
}
double cstr_to_double(const char *str, char *conversion_err)
{
char *end;
double result = strtod(str, &end);
*conversion_err = ((*end) == 0) ? 0 : 1;
return result;
}
geometry.h
#pragma once
double area_circle(double radius);
double volume_cylinder(double radius, double length);
double mm3_to_cm3(double mm3);
geometry.c
#include <math.h>
#include "defines.h"
double area_circle(double radius)
{
return PI * pow(radius, 2);
}
double volume_cylinder(double radius, double length)
{
return length * area_circle(radius);
}
double mm3_to_cm3(double mm3)
{
return mm3 / 1000.0;
}
material.h
#pragma once
struct Material {
const char *NAME;
const double ATOMIC_MASS; //g / mol
const int FREE_ELECTRONS_PER_ATOM; //#
const double VOLUMETRIC_DENSITY; //g / cm3
};
void print_materials_list(void);
const struct Material *str_to_material(const char *str);
const struct Material *prompt_material(char *quit);
material.c
#include <stdio.h>
#include <string.h>
#include "defines.h"
#include "material.h"
#include "prompt.h"
const struct Material COPPER = {
"copper",
63.546,
1,
8.96 };
const struct Material ALUMINIUM = {
"aluminium",
26.982,
3,
2.7 };
const struct Material GOLD = {
"gold",
196.97,
1,
19.3 };
const struct Material NULL_MATERIAL = {
"null_material",
0,
0,
0 };
void print_materials_list(void)
{
fputs("Materials list\n", stdout);
fputs("1. copper\n", stdout);
fputs("2. aluminium\n", stdout);
fputs("3. gold", stdout);
}
const struct Material *str_to_material(const char *str)
{
if(!strcmp(str, "copper")) return &COPPER;
else if(!strcmp(str, "aluminium")) return &ALUMINIUM;
else if(!strcmp(str, "gold")) return &GOLD;
else return &NULL_MATERIAL;
}
const struct Material *prompt_material(char *quit)
{
while(1) {
print_materials_list();
fputs("\n\n", stdout);
char buf[INPUT_BUFFER_SIZE + 1];
if(prompt_quit("Material? ", buf, INPUT_BUFFER_SIZE + 1, "")) {
*quit = 1;
return &NULL_MATERIAL;
}
else {
const struct Material *p_mat = str_to_material(buf);
if(p_mat == &NULL_MATERIAL) {
fputs("Not a valid material\n\n", stdout);
}
else {
*quit = 0;
return p_mat;
}
}
}
}
wire.h
#pragma once
/* Returns diameter (in millimeters) of given AWG */
double awg_diameter(int AWG);
/* Continually prompts the user for a valid wire gauge number until one is
entered or until an empty string is entered.
If an empty string is entered, the flag pointed to by quit is set and
returns 0, otherwise the flag at quit is cleared and returns the wire
gauge. */
int prompt_awg(char *quit);
double prompt_length(char *quit);
void TEST_print_awg_diameters(void);
void TEST_print_awg_areas(void);
wire.c
#include <stdio.h>
#include <math.h>
#include "prompt.h"
#include "geometry.h"
double awg_diameter(int awg)
{
return 0.127 * pow(92, (36.0 - awg) / 39);
}
int prompt_awg(char *quit)
{
while(1) {
long awg = prompt_long("AWG [0 - 36]? ", "", quit);
if(*quit) return 0;
// 0 <= AWG <= 36
else if((awg < 0) || (awg > 36)) fputs("Not a valid AWG\n\n", stdout);
else {
*quit = 0;
return (int)awg;
}
}
}
double prompt_length(char *quit)
{
while(1) {
double length = prompt_double("Wire length (mm) (0 - inf)? ", "", quit);
if(*quit) return 0;
// 0 < Wire length
else if(length <= 0) fputs("Not a valid wire length\n\n", stdout);
else {
*quit = 0;
return length;
}
}
}
void TEST_print_awg_diameters(void)
{
int awg;
for(awg = 0; awg <= 36; awg++) {
printf("%d: %.*F\n", awg, 3, awg_diameter(awg));
}
fputc('\n', stdout);
}
void TEST_print_awg_areas(void)
{
int awg;
for(awg = 0; awg <= 36; awg++) {
printf("%i: %.*F\n", awg, 3, area_circle(awg_diameter(awg) / 2.0));
}
fputc('\n', stdout);
}