Blog

  • Windows Security Features Check PowerShell Script

    This PowerShell script provides a quick and colorful overview of key Windows 11 security features, including Microsoft Defender, Firewall, Secure Boot, BitLocker, VBS, Credential Guard, and TPM status. It uses error handling to ensure reliability and presents results in an easy-to-read format, highlighting whether each feature is enabled or disabled.

    Click to view script…
    # Quick Windows 11 Security Features Check
    
    # Check if the script is running with administrator privileges
    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Warning "This script should be run as Administrator for full functionality."
    }
    
    # Microsoft Defender Status
    Write-Host "`nMicrosoft Defender Status:" -ForegroundColor Cyan
    $defenderStatus = Get-MpComputerStatus
    Write-Host "Antivirus Enabled: " -NoNewline
    if ($defenderStatus.AntivirusEnabled) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "Real-Time Protection: " -NoNewline
    if ($defenderStatus.RealTimeProtectionEnabled) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "Tamper Protected: " -NoNewline
    if ($defenderStatus.IsTamperProtected) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    
    # Firewall Status
    Write-Host "`nFirewall Status:" -ForegroundColor Cyan
    $firewallStatus = Get-NetFirewallProfile
    foreach ($profile in $firewallStatus) {
        Write-Host "$($profile.Name) Profile: " -NoNewline
        if ($profile.Enabled) { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    }
    
    # Secure Boot Status
    Write-Host "`nSecure Boot:" -ForegroundColor Cyan
    try {
        $secureBoot = Confirm-SecureBootUEFI
        Write-Host "Status: " -NoNewline
        if ($secureBoot) { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    } catch {
        Write-Host "Status: Not supported or error occurred" -ForegroundColor Yellow
    }
    
    # BitLocker Status
    Write-Host "`nBitLocker Status:" -ForegroundColor Cyan
    $bitLockerStatus = Get-BitLockerVolume
    foreach ($volume in $bitLockerStatus) {
        Write-Host "$($volume.MountPoint) Protection: " -NoNewline
        if ($volume.ProtectionStatus -eq "On") { Write-Host "On" -ForegroundColor Green } else { Write-Host "Off" -ForegroundColor Red }
    }
    
    # VBS and Credential Guard
    Write-Host "`nVBS and Credential Guard:" -ForegroundColor Cyan
    try {
        $deviceGuard = Get-CimInstance -ClassName Win32_DeviceGuard -ErrorAction Stop
        $vbsStatus = if ($deviceGuard.VirtualizationBasedSecurityStatus -eq 2) { "Enabled" } else { "Disabled" }
        $credGuard = if ($deviceGuard.SecurityServicesRunning -contains 1) { "Enabled" } else { "Disabled" }
        Write-Host "VBS: " -NoNewline
        if ($vbsStatus -eq "Enabled") { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
        Write-Host "Credential Guard: " -NoNewline
        if ($credGuard -eq "Enabled") { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    } catch {
        Write-Host "VBS and Credential Guard status not available on this system." -ForegroundColor Yellow
    }
    
    # TPM Status
    Write-Host "`nTPM Status:" -ForegroundColor Cyan
    $tpmStatus = Get-Tpm
    Write-Host "TPM Present: " -NoNewline
    if ($tpmStatus.TpmPresent) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "TPM Ready: " -NoNewline
    if ($tpmStatus.TpmReady) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    
    Write-Host "`nSecurity Features Check Complete" -ForegroundColor Cyan
  • Identify Pending Windows Updates PowerShell Script

    This PowerShell script utilizes the Windows Update Agent API to scan for available updates that are not yet installed, displaying a detailed list of pending updates including their titles, KB numbers, download status, and descriptions. It provides a quick and reliable way to check the update status of a Windows system without requiring manual intervention through the Settings app.

    Click to view script…
    # PowerShell script to check for Windows updates and list pending updates
    
    try {
        Write-Output "Checking for updates..."
        
        # Create a new update session
        $session = New-Object -ComObject Microsoft.Update.Session
        
        # Create an update searcher
        $searcher = $session.CreateUpdateSearcher()
        
        # Set the search criteria to find updates that are not installed and not hidden
        $criteria = "IsInstalled=0 and IsHidden=0"
        
        # Perform the search
        $result = $searcher.Search($criteria)
        
        # Check if the search was successful (ResultCode 2 means Succeeded)
        if ($result.ResultCode -eq 2) {
            $updates = $result.Updates
            if ($updates.Count -gt 0) {
                Write-Output "Found $($updates.Count) pending updates:"
                $counter = 1
                foreach ($update in $updates) {
                    # Get the KB article ID if available
                    $kb = if ($update.KBArticleIDs.Count -gt 0) { $update.KBArticleIDs[0] } else { "N/A" }
                    # Check if the update is downloaded
                    $status = if ($update.IsDownloaded) { "Downloaded" } else { "Not Downloaded" }
                    Write-Output "$counter. Title: $($update.Title) (KB$kb) - $status"
                    Write-Output "   Description: $($update.Description)"
                    Write-Output ""
                    $counter++
                }
            } else {
                Write-Output "No pending updates found."
            }
        } else {
            Write-Output "Update search failed with result code $($result.ResultCode)"
        }
    } catch {
        Write-Output "An error occurred: $($_.Exception.Message)"
    }
  • Retrieve Detailed System Specifications PowerShell Script

    This PowerShell script collects and displays comprehensive system specifications for a Windows machine, including details about the operating system, processor, memory, disk drives, graphics, network adapters, and motherboard/BIOS. It organizes the information into a clear, readable format, making it ideal for system diagnostics, inventory tracking, or personal reference.

    Click to view script…
    # Get OS Information
    $os = Get-WmiObject -Class Win32_OperatingSystem
    $installDate = [System.Management.ManagementDateTimeConverter]::ToDateTime($os.InstallDate)
    Write-Output "Operating System Information:"
    Write-Output "OS Name: $($os.Caption)"
    Write-Output "Version: $($os.Version)"
    Write-Output "Build Number: $($os.BuildNumber)"
    Write-Output "Installation Date: $installDate"
    Write-Output ""
    
    # Get Processor Information
    $processors = Get-WmiObject -Class Win32_Processor
    $totalCores = ($processors | Measure-Object -Property NumberOfCores -Sum).Sum
    $totalThreads = ($processors | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
    $processorName = $processors[0].Name
    $maxClockSpeed = $processors[0].MaxClockSpeed
    Write-Output "Processor Information:"
    Write-Output "Processor: $processorName"
    Write-Output "Total Cores: $totalCores"
    Write-Output "Total Threads: $totalThreads"
    Write-Output "Max Clock Speed: $maxClockSpeed MHz"
    Write-Output ""
    
    # Get Memory Information
    $memory = Get-WmiObject -Class Win32_PhysicalMemory
    $totalRAM = [math]::Round(($memory | Measure-Object -Property Capacity -Sum).Sum / 1GB, 2)
    Write-Output "Memory Information:"
    Write-Output "Total RAM: $totalRAM GB"
    Write-Output ""
    
    # Get Disk Information
    $disks = Get-WmiObject -Class Win32_DiskDrive
    Write-Output "Disk Information:"
    foreach ($disk in $disks) {
        $sizeGB = [math]::Round($disk.Size / 1GB, 2)
        Write-Output "Disk Model: $($disk.Model)"
        Write-Output "Size: $sizeGB GB"
        Write-Output ""
    }
    
    # Get Graphics Information
    $gpus = Get-WmiObject -Class Win32_VideoController
    Write-Output "Graphics Information:"
    foreach ($gpu in $gpus) {
        Write-Output "Graphics Card: $($gpu.Name)"
        Write-Output "Resolution: $($gpu.VideoModeDescription)"
        Write-Output ""
    }
    
    # Get Network Information
    $adapters = Get-NetAdapter | Where-Object {$_.Status -eq 'Up'}
    Write-Output "Network Information:"
    foreach ($adapter in $adapters) {
        Write-Output "Adapter Name: $($adapter.Name)"
        Write-Output "MAC Address: $($adapter.MacAddress)"
        $ipAddresses = Get-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex | Where-Object {$_.AddressFamily -eq 'IPv4'}
        Write-Output "IP Addresses:"
        foreach ($ip in $ipAddresses) {
            Write-Output "  $($ip.IPAddress)"
        }
        Write-Output ""
    }
    
    # Get Motherboard and BIOS Information
    $motherboard = Get-WmiObject -Class Win32_BaseBoard
    $bios = Get-WmiObject -Class Win32_BIOS
    $biosDate = [System.Management.ManagementDateTimeConverter]::ToDateTime($bios.ReleaseDate)
    Write-Output "Motherboard and BIOS Information:"
    Write-Output "Motherboard Manufacturer: $($motherboard.Manufacturer)"
    Write-Output "Motherboard Model: $($motherboard.Product)"
    Write-Output "BIOS Manufacturer: $($bios.Manufacturer)"
    Write-Output "BIOS Version: $($bios.Version)"
    Write-Output "BIOS Release Date: $biosDate"
  • List Windows Version and Updates PowerShell Script

    This PowerShell script retrieves and displays the current Windows operating system name and version, including the major, minor, build, and revision numbers. It also lists all installed updates, sorted by installation date, with details such as the hotfix ID, description, and when each update was applied.

    Click to view script…
    # Get OS name
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    $osName = $os.Caption
    
    # Get OS version
    $osVersion = [System.Environment]::OSVersion.Version
    
    # Display OS name and version
    Write-Output "Operating System: $osName"
    Write-Output "Version: $($osVersion.Major).$($osVersion.Minor).$($osVersion.Build).$($osVersion.Revision)"
    
    # Get and display installed updates
    Write-Output "Installed Updates:"
    Get-HotFix | Sort-Object InstalledOn | Format-Table HotFixID, Description, InstalledOn
  • Interactive Sequential Folder Creator PowerShell Script

    This PowerShell script allows users to select a target directory either by entering a path directly or by navigating through subdirectories with a numbered menu. Once a directory is chosen, the script creates a series of sequentially numbered folders using a base name provided by the user.

    Click to view script…
    # Function: Recursively select a folder by listing subdirectories
    function Select-Folder($startingPath) {
        $currentPath = $startingPath
        while ($true) {
            Write-Host "`nCurrent Directory: $currentPath" -ForegroundColor Cyan
    
            # Get subdirectories (if any)
            $subDirs = Get-ChildItem -Path $currentPath -Directory -ErrorAction SilentlyContinue
    
            if (!$subDirs -or $subDirs.Count -eq 0) {
                Write-Host "No subdirectories found in $currentPath. Using this folder."
                break
            }
    
            # List options: 0 to select current folder, then list each subfolder with a number
            Write-Host "0: [Select this directory]"
            $index = 1
            foreach ($dir in $subDirs) {
                Write-Host ("{0}: {1}" -f $index, $dir.Name)
                $index++
            }
    
            $choice = Read-Host "Enter the number to navigate into a folder, or 0 to select the current directory"
    
            if (-not [int]::TryParse($choice, [ref]$null)) {
                Write-Host "Invalid input. Please enter a number."
                continue
            }
    
            $choice = [int]$choice
    
            if ($choice -eq 0) {
                break
            }
            elseif ($choice -ge 1 -and $choice -le $subDirs.Count) {
                $currentPath = $subDirs[$choice - 1].FullName
            }
            else {
                Write-Host "Invalid selection. Please try again."
            }
        }
        return $currentPath
    }
    
    # Prompt the user to choose the directory selection method
    Write-Host "Select target directory method:" -ForegroundColor Green
    Write-Host "1: Type the full directory path"
    Write-Host "2: Navigate directories using a numbered list"
    $method = Read-Host "Enter 1 or 2"
    
    if ($method -eq "1") {
        $targetDirectory = Read-Host "Enter the target directory path"
        if (-not (Test-Path $targetDirectory)) {
            Write-Host "Directory '$targetDirectory' does not exist. Exiting." -ForegroundColor Red
            exit
        }
    }
    elseif ($method -eq "2") {
        # Optionally allow the user to set a starting directory; default is C:\
        $startingPath = Read-Host "Enter starting directory path (press Enter for default C:\)"
        if ([string]::IsNullOrWhiteSpace($startingPath)) {
            $startingPath = "C:\"
        }
        if (-not (Test-Path $startingPath)) {
            Write-Host "The starting directory '$startingPath' does not exist. Exiting." -ForegroundColor Red
            exit
        }
        $targetDirectory = Select-Folder $startingPath
        Write-Host "Selected Directory: $targetDirectory" -ForegroundColor Green
    }
    else {
        Write-Host "Invalid selection. Exiting." -ForegroundColor Red
        exit
    }
    
    # Prompt user for the base folder name
    $baseName = Read-Host "Enter the base folder name for the folders to be created"
    
    # Prompt user for the number of folders to create
    $numFoldersInput = Read-Host "Enter the number of folders to create"
    if (-not [int]::TryParse($numFoldersInput, [ref]$null)) {
        Write-Host "The number of folders must be a valid integer. Exiting." -ForegroundColor Red
        exit
    }
    $numFolders = [int]$numFoldersInput
    
    # Create folders sequentially
    for ($i = 1; $i -le $numFolders; $i++) {
        # Construct folder name: "BaseName 1", "BaseName 2", etc.
        $folderName = "{0} {1}" -f $baseName, $i
        $folderPath = Join-Path $targetDirectory $folderName
    
        if (-not (Test-Path $folderPath)) {
            New-Item -Path $folderPath -ItemType Directory | Out-Null
            Write-Host "Created folder: $folderPath" -ForegroundColor Yellow
        }
        else {
            Write-Host "Folder already exists: $folderPath" -ForegroundColor DarkYellow
        }
    }
  • Installed Applications Info PowerShell Script

    This PowerShell script scans the Windows registry for installed applications, retrieving key details such as application name, version number, publisher, and installation date. It then displays the information in a neatly formatted table, making it easy to audit and manage software installations on your system.

    Click to view script…
    # Define registry paths for 64-bit and 32-bit applications
    $registryPaths = @(
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
        "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
    )
    
    # Retrieve applications from the registry
    $installedApps = foreach ($path in $registryPaths) {
        Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
            Where-Object { $_.DisplayName -and $_.DisplayVersion }
    }
    
    # Display the results in a formatted table
    $installedApps |
        Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
        Sort-Object DisplayName |
        Format-Table -AutoSize
  • Smart Finder Tabs Launcher for macOS AppleScript

    This AppleScript streamlines your Finder workspace by checking for an existing Finder window and reusing it to open specified folders as individual tabs, thereby avoiding duplicate windows or tabs. Customize the folders by editing the definitions at the top of the script.

    The folder paths are defined near the beginning of the script. For example, you can customize the following lines to change which folders open:

    set docsFolder to (path to documents folder)
    set desktopFolder to (path to desktop folder)
    set downloadsFolder to (path to downloads folder)


    Simply modify these variables to point to your desired folder paths.

    Click to view script…
    -- Title: Finder Tabs Organizer
    -- Description:
    -- This AppleScript opens designated folders as tabs in Finder
    -- Customize the folders to open by modifying the folder definitions below.
    
    -- Handler to open a new tab for a given folder
    on openNewTabWithFolder(theFolder)
        tell application "System Events"
            keystroke "t" using {command down} -- simulate Command+T to open a new tab
        end tell
        delay 0.5
        tell application "Finder"
            set target of front Finder window to theFolder
        end tell
    end openNewTabWithFolder
    
    -- Define the folders to open
    set docsFolder to (path to documents folder)
    set desktopFolder to (path to desktop folder)
    set downloadsFolder to (path to downloads folder)
    
    tell application "Finder"
        activate
        if (count of Finder windows) > 0 then
            -- Use the existing Finder window and set its target to Documents
            set target of front Finder window to docsFolder
        else
            -- No window exists, so open a new window for Documents
            open docsFolder
        end if
    end tell
    
    delay 0.5
    
    -- Open additional tabs for the other folders.
    openNewTabWithFolder(desktopFolder)
    openNewTabWithFolder(downloadsFolder)

  • macOS User Login and Uptime Report Bash Script

    This script provides system administrators with a clear overview of the system’s current uptime and detailed last login information for human user accounts (UID ≥ 500). It displays key details such as the terminal, login date, login time, and session status for each account.

    Click to view script…
    #!/bin/bash
    # Title: Detailed macOS User Last Login Checker with System Uptime
    # Description:
    # This script lists human user accounts (UID ≥ 500) on macOS and displays detailed information about their last login session.
    # It also shows the current system uptime at the top.
    #
    # The output includes:
    #   - System Uptime: How long the machine has been running since the last boot.
    #   - Username     : The account name.
    #   - Terminal     : The terminal device used during login.
    #   - Login Date   : The date of the login (Day Month Date).
    #   - Login Time   : The time when the login occurred.
    #   - Login Status : Indicates if the session is still active or has ended.
    #
    # Note: "Never logged in" is shown if no login record exists.
    #
    # Retrieve and display system uptime
    systemUptime=$(uptime)
    echo "System Uptime: $systemUptime"
    echo ""
    
    # Print header for the login details
    echo "Username | Terminal | Login Date       | Login Time | Login Status"
    echo "---------------------------------------------------------------------"
    
    # List users with their UniqueIDs and process each one.
    dscl . -list /Users UniqueID | while read username uid; do
        if [ "$uid" -ge 500 ]; then
            # Retrieve the most recent login record for the user
            loginInfo=$(last -1 "$username" | head -n 1)
            
            # Check if there is a valid login record
            if echo "$loginInfo" | grep -q "wtmp begins"; then
                echo "$username |    -     |       -        |     -    | Never logged in"
            else
                # Parse the login record:
                #   Field 1: Username (redundant here)
                #   Field 2: Terminal (e.g., ttys000)
                #   Fields 3-5: Login Date (e.g., "Mon Feb 17")
                #   Field 6: Login Time (e.g., "05:44")
                #   Fields 7+: Login Status (e.g., "still logged in" or the session end time)
                terminal=$(echo "$loginInfo" | awk '{print $2}')
                login_date=$(echo "$loginInfo" | awk '{print $3, $4, $5}')
                login_time=$(echo "$loginInfo" | awk '{print $6}')
                login_status=$(echo "$loginInfo" | cut -d' ' -f7-)
                
                # Output the parsed details in a table-like format
                printf "%-8s | %-8s | %-16s | %-10s | %s\n" "$username" "$terminal" "$login_date" "$login_time" "$login_status"
            fi
        fi
    done
    
    # Legend:
    #   System Uptime - How long the system has been running since the last boot.
    #   Username      - The account name.
    #   Terminal      - The terminal device used during login.
    #   Login Date    - The date of the login (Day Month Date).
    #   Login Time    - The time of the login.
    #   Login Status  - The current status of the login session.
  • Enhanced macOS User Account Details Bash Script

    This Bash script retrieves and displays detailed information for all human user accounts (UID ≥ 500) on macOS, including the username, UID, admin privileges, full name, home directory, and default shell. It provides a clear and organized summary that is useful for system administrators to review and manage user configurations.

    Click to view script…
    #!/bin/bash
    # This script lists user accounts (UID >= 500) and shows additional details:
    # Username, UID, Admin Privileges (true/false), Full Name, Home Directory, and Shell
    
    # Print header for clarity
    echo "Username : UID : Has Admin Privileges : Full Name : Home Directory : Shell"
    
    # List all users with their UniqueID and process each line
    dscl . -list /Users UniqueID | while read username uid; do
        # Only process accounts with UID >= 500 (typically non-system, human user accounts)
        if [ "$uid" -ge 500 ]; then
            # Check if the user belongs to the 'admin' group
            if id -Gn "$username" 2>/dev/null | grep -qw "admin"; then
                adminFlag="true"
            else
                adminFlag="false"
            fi
    
            # Get the user's full name (if set). The command outputs a line like "RealName: John Doe"
            fullName=$(dscl . -read /Users/"$username" RealName 2>/dev/null | sed 's/RealName: //')
            
            # Get the user's home directory
            homeDir=$(dscl . -read /Users/"$username" NFSHomeDirectory 2>/dev/null | sed 's/NFSHomeDirectory: //')
            
            # Get the user's default shell
            shell=$(dscl . -read /Users/"$username" UserShell 2>/dev/null | sed 's/UserShell: //')
            
            # Output the collected information in a clear, colon-separated format
            echo "$username : $uid : $adminFlag : $fullName : $homeDir : $shell"
        fi
    done
  • Earth–Mars Orbital Transfer Animation Python Script

    This Python script animates a simplified simulation of Earth and Mars orbiting the Sun along circular paths, while a spacecraft travels between them using a basic interpolation trajectory with a sinusoidal arc. It dynamically updates the planet positions and labels to clearly indicate which is Earth (blue) and which is Mars (red).

    This script uses NumPy for efficient numerical computations and trigonometric functions, and Matplotlib (including mpl_toolkits.mplot3d for 3D rendering and matplotlib.animation for dynamic visualizations) to animate the planetary orbits and spacecraft trajectory. To install these libraries, open your Terminal and run:

    pip install numpy matplotlib

    (Use pip3 if you’re working with Python 3.)

    Click to view script…
    #!/usr/bin/env python3
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D  # needed for 3D projection
    from matplotlib.animation import FuncAnimation
    
    # -----------------------------------------
    # 1) Orbital and Planetary Parameters
    # -----------------------------------------
    # Simplified units: 1 "unit" = 1 Astronomical Unit (AU)
    # Time is in days
    
    # Orbital radii (approx in AU)
    R_EARTH = 1.0
    R_MARS  = 1.52
    
    # Orbital periods (approx in days)
    T_EARTH = 365.0
    T_MARS  = 687.0
    
    # Angular velocities (radians per day)
    w_EARTH = 2.0 * np.pi / T_EARTH
    w_MARS  = 2.0 * np.pi / T_MARS
    
    # Initial phases (starting positions)
    phi_earth0 = 0.0
    phi_mars0  = 0.0
    
    # Radii of the planets (for plotting spheres)
    EARTH_RADIUS = 0.05
    MARS_RADIUS  = 0.03
    
    # -----------------------------------------
    # 2) Define Planet Position Functions
    # -----------------------------------------
    def planet_position(time, R, omega, phi0=0.0):
        """
        Returns the (x, y, z) position of a planet orbiting in the XY plane.
        """
        x = R * np.cos(omega * time + phi0)
        y = R * np.sin(omega * time + phi0)
        z = 0.0
        return np.array([x, y, z])
    
    # -----------------------------------------
    # 3) Spacecraft Trajectory
    # -----------------------------------------
    def find_launch_day(t_range):
        """
        Finds a launch day in t_range where the Earth-Mars distance is minimized.
        """
        best_day = None
        min_dist = 1e9
        for t in t_range:
            earth_pos = planet_position(t, R_EARTH, w_EARTH, phi_earth0)
            mars_pos  = planet_position(t, R_MARS,  w_MARS,  phi_mars0)
            dist = np.linalg.norm(mars_pos - earth_pos)
            if dist < min_dist:
                min_dist = dist
                best_day = t
        return best_day
    
    def spacecraft_trajectory(t, t_launch, t_arrival, home_func, target_func):
        """
        Computes a simple interpolated trajectory between two planets.
        Outside the travel window, it holds the departure or arrival position.
        """
        if t <= t_launch:
            return home_func(t_launch)
        elif t >= t_arrival:
            return target_func(t_arrival)
        else:
            # Fraction of travel completed
            frac = (t - t_launch) / (t_arrival - t_launch)
            pos_home = home_func(t_launch)
            pos_target = target_func(t_arrival)
            # Add a sinusoidal 'arc' in the Z direction for visual flair
            arc_height = 0.2 * np.sin(np.pi * frac)
            interp = (1 - frac) * pos_home + frac * pos_target
            interp[2] += arc_height
            return interp
    
    # -----------------------------------------
    # 4) Set Up the Animation Plot
    # -----------------------------------------
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111, projection='3d')
    
    # Function to draw a sphere (used for Earth and Mars)
    def plot_sphere(ax, center, radius, color, alpha=1.0):
        u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
        x = center[0] + radius * np.cos(u) * np.sin(v)
        y = center[1] + radius * np.sin(u) * np.sin(v)
        z = center[2] + radius * np.cos(v)
        ax.plot_surface(x, y, z, color=color, alpha=alpha)
    
    # Global variables to store our plot elements
    earth_surf = None
    mars_surf = None
    spacecraft_marker, = ax.plot([], [], [], 'go', markersize=6)
    earth_label = None
    mars_label = None
    
    # Time settings for the simulation (e.g., 1200 days)
    total_frames = 1000
    times = np.linspace(0, 1200, total_frames)
    
    # Find a simplistic launch day within the first 300 days
    launch_day = find_launch_day(range(300))
    travel_time = 180.0  # days for Earth-to-Mars travel
    arrival_day = launch_day + travel_time
    
    # Return trip parameters (optional)
    stay_time_on_mars = 100.0
    return_launch_day = arrival_day + stay_time_on_mars
    return_arrival_day = return_launch_day + 200.0
    
    # Utility to get the x, y, z coordinates for a sphere surface
    def _sphere_xyz(center, radius, n_u=20, n_v=10):
        u = np.linspace(0, 2*np.pi, n_u)
        v = np.linspace(0, np.pi, n_v)
        u, v = np.meshgrid(u, v)
        x = center[0] + radius * np.cos(u) * np.sin(v)
        y = center[1] + radius * np.sin(u) * np.sin(v)
        z = center[2] + radius * np.cos(v)
        return x, y, z
    
    # -----------------------------------------
    # 5) Animation Update Function
    # -----------------------------------------
    def update(frame):
        global earth_surf, mars_surf, earth_label, mars_label
    
        # Current time in days
        t = times[frame]
    
        # Update positions for Earth and Mars
        earth_pos = planet_position(t, R_EARTH, w_EARTH, phi_earth0)
        mars_pos  = planet_position(t, R_MARS,  w_MARS,  phi_mars0)
    
        # Remove old spheres if they exist
        if earth_surf is not None:
            earth_surf.remove()
        if mars_surf is not None:
            mars_surf.remove()
    
        # Plot new spheres for Earth and Mars
        earth_surf = ax.plot_surface(*_sphere_xyz(earth_pos, EARTH_RADIUS),
                                     color='blue', alpha=0.6)
        mars_surf = ax.plot_surface(*_sphere_xyz(mars_pos, MARS_RADIUS),
                                    color='red', alpha=0.6)
        
        # Remove old labels if they exist
        if earth_label is not None:
            earth_label.remove()
        if mars_label is not None:
            mars_label.remove()
        
        # Add new text labels above each planet
        earth_label = ax.text(earth_pos[0], earth_pos[1], earth_pos[2] + EARTH_RADIUS + 0.05,
                              'Earth', color='blue', fontsize=10, weight='bold')
        mars_label = ax.text(mars_pos[0], mars_pos[1], mars_pos[2] + MARS_RADIUS + 0.05,
                             'Mars', color='red', fontsize=10, weight='bold')
        
        # Update spacecraft position based on current time
        if t < return_launch_day:
            # Outbound: Earth to Mars
            sc_pos = spacecraft_trajectory(t, launch_day, arrival_day,
                                           lambda tau: planet_position(tau, R_EARTH, w_EARTH, phi_earth0),
                                           lambda tau: planet_position(tau, R_MARS,  w_MARS,  phi_mars0))
        else:
            # Return: Mars to Earth
            sc_pos = spacecraft_trajectory(t, return_launch_day, return_arrival_day,
                                           lambda tau: planet_position(tau, R_MARS, w_MARS, phi_mars0),
                                           lambda tau: planet_position(tau, R_EARTH, w_EARTH, phi_earth0))
        
        # Update the spacecraft marker (green dot)
        spacecraft_marker.set_data([sc_pos[0]], [sc_pos[1]])
        spacecraft_marker.set_3d_properties([sc_pos[2]])
        
        # Return all updated artists
        return earth_surf, mars_surf, spacecraft_marker, earth_label, mars_label
    
    # -----------------------------------------
    # 6) Plot Aesthetics and Animation Setup
    # -----------------------------------------
    ax.set_xlim(-1.6, 1.6)
    ax.set_ylim(-1.6, 1.6)
    ax.set_zlim(-0.6, 0.6)
    ax.set_xlabel('X (AU)')
    ax.set_ylabel('Y (AU)')
    ax.set_zlabel('Z')
    ax.set_title('Earth–Mars Orbits with Spacecraft Launch')
    
    # Draw the Sun at the origin as a yellow sphere
    plot_sphere(ax, [0, 0, 0], 0.1, 'yellow', alpha=0.9)
    
    # Create the animation
    anim = FuncAnimation(fig, update, frames=total_frames, interval=30, blit=False)
    
    plt.show()