Blog Entry © Tuesday, May 27, 2025, Translation of a Siacci’s Method C# App to C/C++ by James Pate Williams, Jr., BA, BS, MSWE, PhD

Blog Entry © Saturday, May 25, 2025, Return to Baseball Ballistics by James Pate Williams, Jr. BA, BS, Master of Software Engineering, PhD Computer Science Return to Baseball Ballistics

Blog Entry © Friday, May 23, 2025, NASA Geopotential Altitude and the Variation of the Gravitational Acceleration by James Pate Williams, Jr., BA, BS, Master of Software Engineering, PhD Computer Science

More source code is available upon request.
Reference: 19770009539.pdf
Win32 C/C++ application output to a Win32 window:
H Z1 Z2 Z3 g
80000 79005.7 79005.7 79005.7 9.56735
80500 79493.3 79493.3 79493.3 9.56590
81000 79980.9 79980.9 79980.9 9.56446
81500 80468.3 80468.3 80468.3 9.56301
82000 80955.7 80955.7 80955.7 9.56156
82500 81443.0 81443.0 81443.0 9.56011
83000 81930.2 81930.2 81930.2 9.55867
83500 82417.4 82417.4 82417.4 9.55722
84000 82904.5 82904.5 82904.5 9.55577
84500 83391.5 83391.5 83391.5 9.55433
85000 83878.4 83878.4 83878.4 9.55288
85500 84365.3 84365.3 84365.3 9.55144
H Geopotential Altitude
Z1 Geopotential Gauss-Legendre 128 Steps
Z2 Geopotential Simpson's Rule 256 Steps
Z3 Geopotential Trapezoidal Rule 512 Steps
g gravitational acceleration in m/s/s
https://ntrs.nasa.gov/api/citations/19770009539/downloads/19770009539.pdf
See Table 8. Page 9
// NASAGeopotentialAltitude.cpp : Defines the entry point for the application.
// Copyright (c) Friday, May 23, 2025 by James Pate Williams, Jr.
// BA, BS, MSWE, PhD All Applicable Rights Reserved

#include "stdafx.h"
#include "GeopotentialAltitude.h"
#include "Integration1d.h"
#include "NASAGeopotentialAltitude.h"
#include <math.h>
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <vector>
using namespace std;

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_NASAGEOPOTENTIALALTITUDE, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_NASAGEOPOTENTIALALTITUDE));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NASAGEOPOTENTIALALTITUDE));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_NASAGEOPOTENTIALALTITUDE);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_CREATE   - creates a multiple line edit control
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//

#define ID_EDITCHILD 100
char asciiData[16384];
char asciiLine[16384];
TCHAR textData[16384];

void ConvertCharToTChar(const char* charArray, TCHAR* tcharArray, size_t tcharSize) {
#ifdef _UNICODE
	MultiByteToWideChar(CP_ACP, 0, charArray, -1, tcharArray, (int)tcharSize);
#else
    strncpy(tcharArray, charArray, tcharSize - 1);
    tcharArray[tcharSize - 1] = '\0'; // Ensure null-termination
#endif
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	static HWND hwndEdit;
	static double zArray[] = {
		80000, 80500, 81000, 81500, 82000, 82500,
		83000, 83500, 84000, 84500, 85000, 85500 };
	static vector<double> z(12);
	static vector<double> potential1(12);
	static vector<double> potential2(12);
	static vector<double> potential3(12);
	static vector<double> potential4(12);
	size_t numberPoints = z.size();

	for (size_t i = 0; i < z.size(); i++)
	{
		z[i] = zArray[i];
	}

	switch (message)
	{
	case WM_CREATE:
	GeopotentialAltitude::GaussianLegendre(
		numberPoints, z, potential1);
	GeopotentialAltitude::SimpsonsRule(
		numberPoints, z, potential2);
	GeopotentialAltitude::TrapezoidalRule(
		numberPoints, z, potential3);

	strcpy_s(asciiData, 16384, "H\tZ1\tZ2\tZ3\tg\r\n");

	for (size_t i = 0; i < z.size(); i++)
	{
		double zi = z[i];
		double p1 = potential1[i];
		double p2 = potential2[i];
		double p3 = potential3[i];
		double g = g0 * pow(r0 / (r0 + p1), 2.0);

		sprintf_s(
			asciiLine, 16384,
			"%5.0lf\t%5.1lf\t%5.1lf\t%5.1lf\t%7.5lf\r\n",
			zi, p1, p2, p3, g);
		strcat_s(asciiData, 16384, asciiLine);
	}

	strcat_s(asciiData, "H Geopotential Altitude\r\n");
	strcat_s(asciiData, "Z1 Geopotential Gauss-Legendre 128 Steps\r\n");
	strcat_s(asciiData, "Z2 Geopotential Simpson's Rule 256 Steps\r\n");
	strcat_s(asciiData, "Z3 Geopotential Trapezoidal Rule 512 Steps\r\n");
	strcat_s(asciiData, "g gravitational acceleration in m/s/s\r\n");
	strcat_s(asciiData, "https://ntrs.nasa.gov/api/citations/19770009539/downloads/19770009539.pdf\r\n");
	strcat_s(asciiData, "See Table 8. Page 9\r\n");

	ConvertCharToTChar(asciiData, textData, sizeof(textData));

	// https://learn.microsoft.com/en-us/windows/win32/controls/use-a-multiline-edit-control

	hwndEdit = CreateWindowEx(
		0, L"EDIT",   // predefined class 
        NULL,         // no window title 
        WS_CHILD | WS_VISIBLE | WS_VSCROLL | 
        ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 
        0, 0, 0, 0,   // set size in WM_SIZE message 
        hWnd,         // parent window 
        (HMENU) ID_EDITCHILD,   // edit control ID 
		hInst,
        /*(HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE),*/ 
		NULL);        // pointer not needed 
    // Add text to the window. 
	SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) textData); 
    return 0; 
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_SIZE: 
            // Make the edit control the size of the window's client area. 
		MoveWindow(hwndEdit, 
			0, 0,                  // starting x- and y-coordinates 
            LOWORD(lParam),        // width of client area 
            HIWORD(lParam),        // height of client area 
            TRUE);                 // repaint window 
        return 0; 
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

Blog Entry © Thursday, May 22, 2025, Baseball Ballistics with Simple Drag Computation by James Pate Williams, Jr.

// https://phys.libretexts.org/Bookshelves/University_Physics/Physics_(Boundless)/3%3A_Two-Dimensional_Kinematics/3.3%3A_Projectile_Motion

#pragma once

class ClassicalBallistics
{
public:
	ClassicalBallistics(void);
	~ClassicalBallistics(void);
	// analytical formula for maximum height
	double H(double V0, double theta0, double g);
	// analytical formula for flight time
	double T(double H, double g);
	// analytical formula for velocity at apex
	double Va(double V0, double theta0);
	// analytical formula for the range
	double L(double Va, double T);
	// analytical formula for time to apex
	double ta(double T);
	// analytical formula for range to apex
	double xa(double L);
	double xa(double H, double L, double theta0);
	double Theta1(double theta0);
	double Theta1(double H, double L, double xa);
	double ClassicalBallistics::V1(double V0);
};

#include "StdAfx.h"
#include "ClassicalBallistics.h"
#include <math.h>

ClassicalBallistics::ClassicalBallistics(void)
{
}
ClassicalBallistics::~ClassicalBallistics(void)
{
}
// analytical formula for maximum height
double ClassicalBallistics::H(double V0, double theta0, double g)
{
	double V02 = V0 * V0, sinTheta0 = sin(theta0);
	double sinTheta02 = sinTheta0 * sinTheta0;

	return V02 * sinTheta02 / (g * 2.0);
}
// analytical formula for flight time
double ClassicalBallistics::T(double H, double g)
{
	return 2.0 * sqrt(2.0 * H / g);
}
// analytical formula for velocity at apex
double ClassicalBallistics::Va(double V0, double theta0)
{
	double cosTheta0 = cos(theta0);
	double V02 = V0 * V0;

	return V0 * cosTheta0;
}
// analytical formula for the range
double ClassicalBallistics::L(double Va, double T)
{
	return Va * T;
}
// analytical formula for time to apex
double ClassicalBallistics::ta(double T)
{
	return 0.5 * T;
}
// analytical formula for range to apex
double ClassicalBallistics::xa(double L)
{
	return 0.5 * L;
}
double ClassicalBallistics::xa(double H, double L, double theta0)
{
	return sqrt(L * H / tan(theta0));
}
double ClassicalBallistics::Theta1(double theta0)
{
	return -theta0;
}
double ClassicalBallistics::Theta1(double H, double L, double xa)
{
	return atan(L * H / pow(L - xa, 2.0));
}
double ClassicalBallistics::V1(double V0)
{ 
	return V0;
}

// http://www.lajpe.org/sep13/04-LAJPE-782_Chudinov.pdf

#pragma once

const double g = 9.81;
// accleration due to gravity in Metric (SI) Units
const double k = 0.000625;
// k is typical for a baseball

class DragBallistics
{

public:

	DragBallistics(void);
	~DragBallistics(void);
    double f(double theta);
    // analytical formula for maximum height
    double H(double V0, double theta0, double g, double k);
    // analytical formula for flight time
    double T(double H, double g);
    // analytical formula for velocity at apex
    double Va(double V0, double theta0, double k);
    // analytical formula for the range
	double L(double Va, double T);
    double ta(double H, double T, double Va, double k);
    double xa(double H, double L, double theta0);
    double Theta1(double H, double L, double xa);
    double V1(double V0, double theta0, double theta1, double k);
};

#include "StdAfx.h"
#include "DragBallistics.h"
#include <math.h>

DragBallistics::DragBallistics(void)
{
}
DragBallistics::~DragBallistics(void)
{
}
double DragBallistics::f(double theta)
{
	double pi = 4.0 * atan(1.0);
	double sinTheta = sin(theta), cosTheta = cos(theta),
		cosTheta2 = cosTheta * cosTheta;
    return (sinTheta / cosTheta2) + log(tan(0.5 * theta + pi / 4.0));
}
// analytical formula for maximum height
double DragBallistics::H(double V0, double theta0, double g, double k)
{
	double V02 = V0 * V0, sinTheta0 = sin(theta0),
		sinTheta02 = sinTheta0 * sinTheta0;
    return V02 * sinTheta02 / (g * (2.0 + k * V02 * sinTheta0));
}
// analytical formula for flight time
double DragBallistics::T(double H, double g)
{
	return 2.0 * sqrt(2.0 * H / g);
}
// analytical formula for velocity at apex
double DragBallistics::Va(double V0, double theta0, double k)
{
	double pi = 4.0 * atan(1.0);
	double cosTheta0 = cos(theta0), cosTheta02 = cosTheta0 * cosTheta0;
    double V02 = V0 * V0, sinTheta0 = sin(theta0);
    return V0 * cosTheta0 / sqrt(1.0 + k * V02 * (sinTheta0 + 
		cosTheta02 * log(tan(0.5 * theta0 + pi / 4.0))));
}
// analytical formula for the range
double DragBallistics::L(double Va, double T)
{
	return Va * T;
}
double DragBallistics::ta(double H, double T, double Va, double k)
{
	return 0.5 * (T - k * H * Va);
}
double DragBallistics::xa(double H, double L, double theta0)
{
	return sqrt(L * H / tan(theta0));
}
double DragBallistics::Theta1(double H, double L, double xa)
{
	return -atan(L * H / pow(L - xa, 2.0));
}
double DragBallistics::V1(double V0, double theta0, double theta1, double k)
{
	double V02 = V0 * V0;
    double sinTheta0 = sin(theta0), cosTheta0 = cos(theta0),
		cosTheta02 = cosTheta0 * cosTheta0;
    return V0 * cosTheta0 / (cos(theta1) * sqrt(1.0 + k * 
		V02 * cosTheta02 * (f(theta0) - f(theta1))));
}

// BaseballBallisticsWin32Console.cpp
// Translated from August 2017 C# application
// May 21, 2025 (c) James Pate Williams, Jr.

#include "stdafx.h"
#include "ClassicalBallistics.h"
#include "DragBallistics.h"
#include <iomanip>
#include <iostream>
#include <math.h>

void PrintResults(char title[],
				  double H, double T, double Va,
				  double L, double Ta, double xa,
				  double Theta1, double V1)
{
	std::cout << title << std::endl;
	std::cout << std::fixed << std::setprecision(2);
	std::cout << "H      = " << H << std::endl;
	std::cout << "T      = " << T << std::endl;
	std::cout << "Va     = " << Va << std::endl;
	std::cout << "L      = " << L << std::endl;
	std::cout << "Ta     = " << Ta << std::endl;
	std::cout << "xa     = " << xa << std::endl;
	std::cout << "theta1 = " << Theta1 << std::endl;
	std::cout << "V1     = " << V1 << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	while (true)
	{
		char line[128] = { };
		std::cout << "V0 (m / s) or 0 to quit: ";
		std::cin.getline(line, 128);
		double V0 = atof(line);
		if (V0 == 0)
		{
			break;
		}
		std::cout << "Enter angle in degrees: ";
		std::cin.getline(line, 128);
		double theta0 = atof(line);
		double pi = 4.0 * atan(1.0);
		theta0 *= pi / 180.0;
		ClassicalBallistics cBall;
		DragBallistics dBall;
		double cH = cBall.H(V0, theta0, g);
        double dH = dBall.H(V0, theta0, g, k);
        double cT = cBall.T(cH, g);
        double dT = dBall.T(dH, g);
        double cVa = cBall.Va(V0, theta0);
        double dVa = dBall.Va(V0, theta0, k);
        double cL = cBall.L(cVa, cT);
        double dL = dBall.L(dVa, dT);
        double cta = cBall.ta(cT);
        double dta = dBall.ta(dH, dT, dVa, k);
        double cxa = cBall.xa(cL);
        double dxa = dBall.xa(dH, dL, theta0);
        double cTheta1 = 180.0 * cBall.Theta1(theta0) / pi;
        double dTheta1 = 180.0 * dBall.Theta1(dH, dL, dxa) / pi;
        double cV1 = cBall.V1(V0);
        double dV1 = dBall.V1(V0, theta0, pi * dTheta1 / 180.0, k);
		PrintResults("Classical Ballistics",
			cH, cT, cVa, cL, cta, cxa, cTheta1, cV1);
		PrintResults("Drag Ballistics",
			dH, dT, dVa, dL, dta, dxa, dTheta1, dV1);
	}
	return 0;
}

Blog Entry © Wednesday, May 21, 2025, Backpropagation Artificial Neural Network Experiments by James Pate Williams, Jr.

Learning a Simple Function Using an Artificial Backpropagation Neural Network © Wednesday, May 14, 2025, by James Pate Williams, Jr., BA, BS, Master of Software Engineering, and PhD Computer Science

Blog Entry © Monday, May 12, 2025, by James Pate Williams, Jr., BA Chemistry, BS Computer Science, Master of Software Engineering, PhD Computer Science Two Minor Catastrophes

Blog Entry © Monday 5, 2025, by James Pate Williams, Jr. BA, BS, MSWE, PhD Somewhat a Spoiled Brat

A Necessary Aside Happy Cinco de Mayo

I was raised by two doting parents and was treated like an only child even though I had two older sisters. Sometimes in the years 1957 to 1960, I would accompany my mother to LaGrange College (LC) and sit in her classes. If she could not get me in a class, I would sit in the parking lot of LaGrange College and work on plastic models. In my early childhood years, my mother and I would climb-into a passenger train bound for Roanoke, Alabama, to visit my grandmother and/or my uncle Lt. Col. US Army retired Charles “Worth” Jordan.
My mother was majoring in psychology on her way to earn another teaching certificate. Her first teaching certificate was granted by the State of Alabama. My mother first graduated from Jacksonville State University (Jax State - The Friendliest Campus in the South) in Alabama. I think Jax State was a female only university during my mother’s degree time at Jax. Anyway, she was having trouble with the New Math at LC that included Betrand Russell’s Axiomatic Set Theory. She brought a couple of library math and philosophy books home from the LC library for me to decipher. Later, when I was in fourth of fifth grade, I declared myself as an agnostic and my parents allowed me to quit going to Sunday School and Church Services at the little mill village Dixie Methodist Church on Fair Street in LaGrange, GA. Back to my mother somewhere in my filing cabinet of memories I have my mother’s typing certificate from Troup Technical School. Speaking of typing, I flunked out of that class when I was in my final years of high school. However, my father did teach me how to use a slide-rule, Roentgen dosimeter, and a Geiger counter.
My parents equipped me with all the toys necessary to aid in my scientific development. I had a late 1950s Lionel O gauge train set. Later, a Lionel chemistry set. I have a framed Lionel Periodic Chart of the Chemical Elements. My parents also purchased for me a microscope and reflecting telescope. Along the way, I had a small-sized slot-car racing set.
I also managed to crash two fuel powered string-controlled model planes, namely, a Curtiss P-40 Warhawk and a Boeing P-26 Peashooter. I could not get used to using the strings to control the model airplanes.
I used to hang out at the private (Colemen) and public (LaGrange Memorial) libraries. I seem to recall that in the 1960s, the Colemen libraries engineering section on the second floor was restricted for adults, but I was allowed to venture and peruse volumes in the forbidden section.
When I was a senior at LaGrange High School, I would cut up in my Physics class so the teacher would send me to the lab to work on an oscilloscope and other physics devices.
I built a good number of plastic model airplanes, jets, military ships, battle tanks, etc. I especially liked Revell and Monogram model kits before their merger. I built a model of the Confederate privateer, CSS Alabama which Raphael Semmes commanded.