23 KiB
This is a project for a museum to demonstrate a simulation of a 1940's to 1960's vintage radar, including the Chain Home radar from early World War 2, marine radar at a marine traffic control station, and marine radar on a boat.
The project will be implemented on a Geekom A8 Max 32 GB RAM AMD Ryzen 9 8945HS w/ Radeon 780M Graphics We need to render to the Radeon 780M Graphics GPU
Tech Stack: We are using C++20, OpenGL 4.3 Core, GLFW, GLAD, FreeType, GDAL (libgdal-dev) Compiler: is g++ (Ubuntu 15.2.0-4ubuntu4) 15.2.0
Coding will be done using the c++ language for the CPU
GPU coding will be GLSL (as required by OpenGL)
FreeType is the text type we use GDAL is used for reading the LIDAR/ENC chart files GLFW (graphics library framework) open-source, multi-platform library used to manage windows GLAD (Multi-Language GL/GLES/WGL/GLX Loader-Generator) Loads the pointers to OpenGL functions (like glDrawArrays or glCompileShader)
PostgreSQL is installed. Database: radar. User: radar. Password: radar. User has full privileges on database radar. Table is target_data.
Operating system details:
Distributor ID: Ubuntu Description: Ubuntu 25.10 Release: 25.10 Codename: questing
Use cmake for building.
[DIRECTIVE: GPU ROBUSTNESS PROTOCOL]
Debug Callback: Enable GL_DEBUG_OUTPUT and glDebugMessageCallback to capture driver-level warnings and errors in real-time.
I will be using SSH from Windows to write code and check with claude. You may compile the code during an SSH session. Please do not try to run the code during SSH session. I will run the code while physically using the Geekom.
Please add MIT license header to each file Please add Author: Mark Allyn to each file
Use snake_case for variables and PascalCase for classes use #pragma once Use // for single line comments use /* */ for multiple block comments spanning multiple lines avoid using auto
==================================================================
SYSTEMWIDE INCLUDE FILE AND GLOBAL DATA FILE
This file is located at /include/data_structure_and_constants.h
There will be a system wide include header file for constants and data structures
For each entry in this file (which may be added piecemeal, a comment needs to be in place with the name of the components that uses that constant or data structure. Note that these comments will be updated as I add new components to be built.
SYSTEMWIDE GLOBAL DATA FILE
There will be a file containing global data items.
The file is at global_data.cpp
For each entry in this file (which may be added piecemeal, a comment needs to be in place with the name of the components that uses that constant or data structure. Note that these comments will be updated as I add new components to be built.
SYSREMWIDE TESTING AND SETTINGS INCLUDE FILE
The system wide testing and configuation file, located at include/settings.h
The values for each component shall be preceded by the session name as a comment. It shall be included in all components.
Please use this file for defines and variables that I can change and do a simple re-compile instead of having the ai re write the code. This is for debugging. For example, I may want to disable the p7 persistance to troubleshoot the actual target processnig before it goes to persistance, disable the land and terrain so that I can see the targets alone. Things like that.
==================================================================
PIECEMEAL BUILDING
This project will be built piecemeal. Each components will be build by itself an integrated with components that have been already built.
Each component will have its own .md file for instructions and specifications.
As a component is built and tested, I wil add a statement at the end of it's .md file, looking like "COMPONENT DONE"
Please note that each component shall have it's own c++ file; its own .h file; and it's own sets for shaders. Full independance.
The name of each component shall have a number, then an undeline, then the name of the component, for example 01_classes, 02_management, 03_chain_Home, and so on.
==================================================================
TEMPORARY TESTING FILES
As each component if built, I may ask for temporary files to be created for construction component. For example, I may ask for targets on a radar before any target handling code is made.
I will indicated what is needed to be built. And when I am done testing, I will instruct that that file be deleted. These instructions will be done during the dialog with claude code.
===================================================================
HANDLING COMPILE AND BUILD OF SHADEARS DURIANG PIECEMEAL BUILDING
As you know, all shaders are to be built and loaded at the beginning of the application.
I propose that each components will have it's own code for making the linking its shaders as a function that will be called by the system's initialization code.
====================================================================
Summary of project:
This is a museum exhibit displaying and providing some interaction of vintage 1940's, 1950's, and 1960's radars. A key objective is to provide interaction with and viewing of radars from that era.
The name of the application (the name of it's executable file) shall be radar_simulator. Therefore, if you are in the directory in which the executable is compiled, which in this case would be the build subdirectory of the project directory, you would type ./radar_simulator, or use a full path name of {PROJECT_DIR}/build/radar_simulator.
In the CMakeLists.txt, plese use the name radar_simulator for the add_executable call.
There will be three screen areas (viewports):
-
Left text panel (upper-left): displays the description of the current scope and the keyboard controls for that scope. Text is white; control labels are red; keystrokes are pink. This panel occupies the upper portion of the left side of the window. At some point, pending a decision with the museum, we may purchase components to mount the controls on a panel. Until that is done, the controls will be on the keyboard.
-
Status bar (lower-left): sits directly below the left text panel. It shows the current control values — maximum range, range cursor, bearing cursor, and scope identification — as appropriate to whichever scope is active. This text will be yellow.
-
Scope (right side, full height): the radar scope takes the entire right side of the window from top to bottom. There is no status bar below the scope.
Use glViewport, glScissor, and glEnable(GL_SCISSOR_TEST) to define each of the three areas so that each can render with its own coordinate system.
The left side dimensions are defined in settings.h: LEFT_PANEL_WIDTH — horizontal width of the left side STATUS_BAR_HEIGHT — height of the status bar (bottom of left side) LEFT_TEXT_HEIGHT — height of the text panel (top of left side; = WINDOW_HEIGHT - STATUS_BAR_HEIGHT) The scope fills the remaining right side at full window height (SCOPE_WIDTH x WINDOW_HEIGHT).
Scopes in the right panel
- Introduction of Exhibit (Explanation of the project on the left hand text panel.
- A-scope for Chain Home Radar in the 1940's (first radar and could be tricky)
- A-scope for marine radar in the 1940's (Before PPI radar); was a bit tedious to operate
- PPI scope for marine traffic control (uses beam sweeping in all 360 degrees of rotation); Easier to use than a scope
- PPI scope on board a boat. Shows how movement of a boat affects the radar display
Please note that these scopes will not appear all at once. The selection of which scope the visitor sees is done by pressing a forward control and a reverse control to go around the loop of scopes. The first display when the system is turned on or booted up is the Introduction of the exhibit.
=====================================================================
CONTROL DEFINITIONS
Please note that the first iteration of the project will have only minimal controls. This is a suggestion I got after meeting with the museum staff. Perhaps later we may add more controls.
Also, please note that the state of the controls of each scope is independent of any other scope. Furthermore, the controls will reset when a scope is exited and then re-entered.
Controls to affect the behavior of the scopes; (these first implemented using keyboard strokes; later when and if physical controls are completed, the keyboard controls will be removed) These controls will affect the state variables and the uniform variables of the shaders.
Please note that identical controls will be the same keyboard strikes or physical controls for all scopes.
======================================================= Overall control information
These are knobs on the panel once that is built. Keyboard keys until then
- Radar Selection - which radar to run. Selection is done with up or down; the radars are to be in the selection order as follows 1, chain_home; 2, marine_ascope; 3, marine_traffic; 4, police boat. When the application is started, chain_home is current selection. You go up, then marine_ascope is selected. And so on. Then you hit enter to activate the selected radar. The Keyboard is 1 for going forward; 2 for going backward. Note that when a radar is operating, you can go back to the selection bt using the 1 key to go back to the selection.
- Intensity - scope overall intensity - Keyboard 3 is lower; 4 is higher - uniform is float intensity;
- Receiver Sensitivity - signal intensity - Keyboard 5 is lower; 6 is higher - uniform is float sensitivity;
- STC Sensitivity - sensitivity for closer in targets which can overwhelm overall sensitivity - keyboard q for lower; w for higher - uniform is float stc_sensitivity;
- STC Range - range to which sensitivity to closer targets is effective keyboard e for lower; r for higher - uniform is float stc_range;
- Noise filter for rain noise. This is for the ppi scopes. keyboard t for decrease filtering and y to increase filtering - uniform is float noise_filter;
- Radiogoniometer knob for Chain Home; keyboard u for left turn; i for right turn. - uniform is float radiogoniometer;
- Maximum Range for all scopes except for chain home, which is fixed. This control will not operate for Chain Home. These maximum range selections will be defined for each scope; If you try to go beyond the stated maximum ranges, the control will have no effect; note that the default maximum range would be the highest for that scope; keyboard o for lower; p for higher uniform is float max_range;
- Range Cursor (for ppi scopes, not for a scopes) keyboard a for lower; s for higher - uniform is float range_cursor;
- Bearing Cursor (for all ppi scopes; not for a scopes) keyboard d for counterclockwise f for clockwise uniform is float bearing_cursor;
- Bearing for Marine A Scope. This is not for a cursor, but this operates the motor that revolves the antenna unit keyboard g for counterclockwise and h for clockwise uniform is float marine_a_bearing;
Notes; I am maintaining control separation between radiogon, marine a scope bearing, and ppi bearing. However, when I make the control panel, those will be combined as one physical knob with three labels, chain home radiogon, marine a scope antenna rotation, and ppi scope bearing to save on hardware; the same physical control can be used for multiple scopes.
Note that the range cursor is different from the maximum range. Maximum range is the maximum radar range setting and range cursor is the range portion of the ppi cursor.
Note that these are to be implimented as I work on each component. You cannot impliment all of these at the beginning as there will be no shaders for the uniforms
Note that a copy of each uniform shall be maintained in the global_data.cpp file.
===================================================================
Some data structures from my previoius work that should be relavent:
All targets from the network pipeline and simulator map to this C master structure:
struct target_data_structure { double target_longitude; double target_latitude; std::string vessel_name; // will be null for no available name std::string registration; // will be null for no registration float length; // in meters float beam; // in meters int vessel_type; // AIS type code or aircraft type uint32_t mmsi; // AIS unique identifier; ICAO hex address for aircraft float course; // course over ground, degrees, based on true north float speed; // speed over ground, knots float altitude; // meters, but 0 for boats int type; // type of target; vessel or aircraft; 0 = vessel int police_boat // 1 is police boat; 0 is not time_t timestamp; };
Of this heavy structure, only the following lean mathematical footprint is submitted to the graphics shaders via an OpenGL Shader Storage Buffer Object (SSBO) layout(std430):
ssbo_target_information { // This is what is sent to the shaders as SSBO float target_x; // Pre-converted local Cartesian offset X (meters relative to radar) float target_y; // Pre-converted local Cartesian offset Y (meters relative to radar) float length; // Vessel length bounds (meters) float beam; // Vessel beam bounds (meters) float course; // Course over ground vector (radians relative to True North) float altitude; // Target altitude profile (meters = 0 for boats) float current_reflectivity; // derivied from vessel_type (see table below) float height_estimate; // derived from vessel_type (see table below) float noiseglint; // derived from vessel_type (see table below) int32_t police_boat; // this indicates that the target is for the roaming police boat. 1 = police boat uint32_t mmsi; // AIS unique identifier; ICAO hex address for aircraft int type; // target type (0 for boat 1 for aircraft) };
This is the structure sent to the software to send to the shaders. It does include the text (string) values, but those are stripped off prior to being sent to the shaders.
struct target_data_to_thread_1_structure { float target_x; // Pre-converted local Cartesian offset X (meters relative to radar) float target_y; // Pre-converted local Cartesian offset Y (meters relative to radar) std::string vessel_name; // will be null for no available name std::string registration; // will be null for no registration float length; // Vessel length bounds (meters) float beam; // Vessel beam bounds (meters) float course; // Course over ground vector (radians relative to True North) float altitude; // Target altitude profile (meters = 0 for boats) time_t timestamp; // Data aging tracking identifier float current_reflectivity; // derived from calculation based on material - is dynamic float height_estimate; // derived from vessel_type (see table below) float noiseglint; // derived from vessel_type (see table below) int32_t police_boat; // this indicates that the target is for the roaming police boat. 1 = police boat int type; // target type (0 for boat 1 for aircraft) uint32_t mmsi; // AIS unique identifier; ICAO hex address for aircraft };
Please note that height and material are not included in the data received from the simulator nor the raspberry pis. This has to be derived. That will be discussed when I talk about the traffic cop component.
Please note that the ssbo_target_information data structure does not include the vessel_name nor the registration. Those do not go to the shaders.
Here is the data items for the table in the Postgres database:
- float length
- float beam
- int32_t may_need_update (this if a randome value is applied to any part of the target and that the user may want to update the database. This will not force the user to do the update, a later database editing program will use this.
- int vessel_type (AIS type code or aircraft type)
- int type (1 for airplane, 0 for boat)
- uint32_t mmsi (unique identification for boat or ICAL for aircraft
- float reflectivity (derived from target type)
- float height_estimate (derived from target type)
- float noiseglint (derived from target type_
- std::string vessel_name;
- std::string registration;
- int32_t police_boat (this indicates that this is for the police boat - 1 is true)
Please note that this database is only for the physical characteristics. Dynamic information which is created by the simulator (or raspberry pis) as that data changes.
=====================================================================
THE THREAD INFORMATION
I don't know how the mutex data is handled in an include file or global data;
This is the outline of the threads that I know so far. We add those as needed when we get to the components that need them.
Now, lets discuss the threads. There will be five threads:
Thread 1 = Operation of setting up the system; ie; initializeing Opengl, compiling, building, and loading all of the shaders. It also handles the keyboard operation of the radar controls as those are callbacks from the OpenGL. Accepting target traffic from the Traffic Cop will be performed, however, the Traffic Cop will need to assert mutex-target-data in order to write any target data to this Thread 1.
Please note that this thread also includes the hit_test as noted below.
Thread 2 = Traffic Cop. The Traffic Cop will accept Target Data from the Simulator (which will be discussed here) ; and the AIS and ADS(b) data from the raspberry pis. At this time, we need to provide only a stub withing the traffic cop to access the Raspberry pis. The traffic cop will need to set a mutex-target-data before handing radar traffic to the Thread 1 software which will send the radar data on to the shaders. Note that the traffic cop needs to do the following processing with target data:
1. Convert the GPS longitude information to the local coordinates for each radar.
2. Check and discard targets that are outside the range of the radar that is
currently being used.
3. Discard targets whose timestamp is older than 1 hour
Thread 3 = Simulator. The simulator creates and manages the simulated targets. It needs to assert mutex-simulator-to-traffic-cop to transfer target information to the Traffic cop
Thread 4 = Physical controls, knobs and switches. This has not been developed and will not be developed with the current version of the project. It will be developed later. It does need to set a mutex-control-data-to-shaders in order to send control information to Thread 1 so that thread can set the state variables and the uniforms for the controls in the shaders.
Thread 5 = Raspberry pi data recever = this receives the data from the raspberry pis. It will not be developed for the current version of the project. The Raspberry PI receiver will need to set mutex-raspberry-pi in order to transfer target data to the traffic cop.
Thread 6 = Physical control panel (to be defined later) This will handle control operations from the physical control panel. It will need to make appropriate changes to the CPU globals and the uniforms in the shades, similar to the Keyboard controls. However, since this thread 6, it will need to assert the mutex-to-pass-controls-to-thread-1 mutex before sending values to thread 1.
================================================================================
RADAR STATUS INFORMATION AND CONTROL
The curent exhibit proposal has four discrete radars:
Chain Home : Radar developed by United Kingdom for defending the south west coast (Vetnor) from attacks from the continent. It is called chain_home. All variables and function names and shader names shall be prefixed with "chain_home". For this exhibit, it's location is at RAF Vetnor (Isle Of Wight) at: Decimal Degrees (Latitude / Longitude): 50.600° N, 1.183° W
Marine A-Scope : Manually Pointable Radar used by United Kingdom to discover germain submarines when they come up to charge their batteries. It's key feature is a microwave dish antenna that is manually pointed by the operator using a servo control to electrically rotate the microwave dish antenna. This radar preceded the development of the modern Plan Poing Indicator radars of todky. All variables and function names and shader names shall be prefixed with "marine_ascope" For this exhibit the location is on a boat moored at the dock of the Community Boating Center (not movable) at: latitude (48.70529^{\circ}\text{ N}) and longitude (-122.50547^{\circ}\text{ W})
Marine Traffic Control PPI Radar : This is a PPI radar using the plan position indictor. This is a more powerful stationary radar that is used for marine traffic control. The Coast Guard uses them. All variables, function names, and shader names shall be prefixed with "marine_traffic". For this project, the marine traffic control radar is located in the center of Bellingham Bay at: latitude (48^\circ 43' 25" \text{ N}) and longitude (122^\circ 34' 35" \text{ W})
Marine Radar On Police Boat : This is a PPI radar that is located on a police boat. The purpose of this radar (which behaves the same as the Marine Traffic Control Radar) is to demonstrate to the museum guest how a radar scope changes as a bost moves and changes direction. It is lower power than the Marine Traffic Control PPI Radar. All variables, function names, and sharder names shall be prefixed with "police_boat". This radar and boat shall be moveable like any other target. For this exhibit, the starting location, when the exhibit is started, is at the dock at the end of Tayor Street and the Boulevard Park boardwalk at: 48.7253874° N latitude and -122.5077482° W longitude.
Selection and availability of the radars:
The museum operator, upon startup of the exhibit shall be able to select which radars are to be avaailable for use by the museum visitors. This selection shall be via passing parameters when starting the exhibit.
The operational status for each radar shall be noted by a variable indicating whether the radar is working within a global data structure.
The currently selelction of which radar is currently operating shall also be noted by a global data structure.
Here is a suggested global data for managing the radars:
struct radar_management_structure { int radar_type // 1 is chain_home; 2 is marine_ascope; 3 = marine_traffic 4 = police_boat int operatable; // 1 is yes, 0 is no int available; // 1 is yes, 0 is no - this is the one controled by the exhibit owner int selected; // 1 is currently operating, 0 is not operating };
struct radar_management_structure radar_management;
===========================================================================