|
|
Native external function examples: Wing angle functions |
This topic is intended to be read in conjunction with the main external function documentation, and the example source code.
This ZIP file contains all of the native external function examples, with each example contained in the appropriately named sub-folder.
The wing angle collection of external function source files starts as simply as possible, while still producing a valid external function. This example is therefore ideal for use when establishing your compilation toolchain, and when assembling the required source files from within your OrcaFlex installation.
WingAngleFromTime is a very simple example that demonstrates the use of simulation time to generate the return value of the external function. When a result is required, this example returns the simulation time squared.
This example shows how the requirements described on the introduction page for native external functions are put into practice. Note the use of __stdcall and the TExtFnInfo& input parameter in the function declaration. The outermost try-catch structure ensures that exceptions do not escape the external function. The switch statement is then used to handle the actions that you wish your code to take.
OrcaFlex file WingAngleFromTime.dat uses this function to define the azimuth angle of three wings on a fixed 6D Buoy. Time history graphs of wing angle against time show that the wings' azimuth equals the square of simulation time. The graphs are stepped because the update intervals for wings 1, 2 and 3 are 1.0, 2.0 and 3.0 seconds respectively. This update interval is set via the TargetSpecifiedTimeStep data item on the OrcaFlex variable data form. Note also that the initial value is 0.0, so the graphs will always show this until the first update interval has elapsed.
WingAngleFromTimeWithOffset builds on the previous example, but introduces parameter passing using object tags. OrcaFlex objects maintain a list of name/value pairs, known as tags. These can be edited on the data form for each object, and then read by external functions.
In this example the external function expects a tag named Offset.
double offset = getDoubleFromTag(info.ObjectHandle, L"Offset", 0.0);
Function getDoubleFromTag is found in the Utils source and header files.
If no tag named Offset is defined then the final parameter (the default value, 0.0 in this case) is used. The result is the same as the previous example except that the offset is added to the square of the simulation time.
If you run the OrcaFlex file WingAngleFromTimeWithOffset.dat and plot the time history of 6D Buoy wing azimuth you see the same shape of curve as in the previous example. However, it has been shifted by the specified offset – a value of 23 in this case. Try changing this to other values.
| Note: | Wing three has a minimum value of 24.0, not 23.0 that may be expected. This is because the value used between 1.0 and 4.0 seconds is the one taken at time=1.0 seconds, so the value will be 1.0*1.0+23.0 = 24.0. The value used at time=0.0 seconds is the one taken at time=-2.0. |
In the previous examples the same wing angle (although at different update intervals) was applied to all three wings attached to the 6D buoy. This example shows how to use object tags to specify different offsets for each of the three wings. It is wasteful to read the tag values every time the eaCalculate action is called, especially as these will not change through the simulation. In this example they are read once during initialisation using the function InitialiseWingOffsets and then stored for use when calculating. This function expects the tags to be prefixed with the wing name. So, for Wing1 the tag name would be Wing1Offset, and so on.
This is the first example where we have stored some data. We use a struct named InstanceData for this purpose, and the allocation of memory and use of info.lpData ensure that the stored data is local to each instance of our external function. In situations where multiple OrcaFlex simulations can be processed in parallel, such as OrcaFlex batch processing, ensuring that each external function instance is independent and self-contained is important to avoid unexpected and undesirable sharing of data.
The eaCalculate action is called once for each wing per time step during which the local function CalculateValue calculates simulation time squared plus the offset for the required wing. The name of the required wing is passed to the external function from within the info.lpObjectExtra structure.
return time * time + data.WingOffsets[GetIndexForWing(wingName)];
Where time is the simulation time and WingOffsets is the array of wing offsets obtained during initialisation – the function GetIndexForWing converts from wing name to an index into this array.
Running WingAngleFromTimeWithOffsetPerWing.dat and displaying the time histories for each of the wings shows the different offsets for each along with the different update intervals. Again, the offset of Wing 3 may not be as expected due to the update interval.
This example uses the external function to control the two wing gamma angles of a towed fish as a function of depth.
Each gamma angle, relative to the buoy's axis, is set to the same value as the depth of each wing. That is, if the wing is at 67m then the angle will be set to 67 degrees. Each wing is controlled independently depending on its depth. This is achieved by getting the wing's Z coordinate from the info.lpInstantaneousCalculationData structure. As the external function relates to a wing, this pointer value can be safely cast to a TWingInstantaneousCalculationData, where icd->Position.Z is the wing global Z coordinate. The result is then returned to OrcaFlex in info.Value as the value to be used for the wing gamma angle.
| Note: | It is the responsibility of the DLL code to allow for the various units that OrcaFlex could be using. In this example the units are ignored, so if US units were used then a depth of 67 ft would yield a wing gamma angle of 67 degrees. |
Running WingAngleFromDepth.dat and selecting the time history of Wing Z and Wing Gamma for Wing S or Wing P (the two controlled wings), confirms that the wings' gamma angles track the Z position of the wings. The next example shows how this type of position monitoring can be used to stabilise the depth and orientation of a towed fish.
This example implements the control of two wings attached to a towed fish in order to maintain level flight at a specified depth. To make things more interesting, the control system does not start until after the build-up period is complete. During the build-up period (time < zero) the gamma angles are set to 90 degrees which gives a stable system. At time t=0s the control system is switched on and the towed fish dives to the target depth of 150m and locks onto it. The control system is a simple proportional control, i.e. the wing angle is set to a constant times the error signal. The error signal is the difference between depth and the target depth. Because the wings are controlled independently, each wing seeks out the target depth, and this gives roll control of the towed fish as well as depth control.
Open and run WingAngleDepthControl.dat, and then open the associated workspace file. This shows the time histories of the buoy motion and the relevant buoy and wing variables.
In common with earlier examples, configuration data is obtained from the buoy's tags. These are read into some allocated memory (data) during the initialisation phase using the following call:
GetParameters(info.ObjectHandle, *data);
This decodes the following tags, each of which is a floating point value:
ControlStartsAtTimeTargetDepthWingAngleToHorizontalMaxWingAngleForHorizontalDepthToAngleFactor
During the calculation phase, the angle of each wing, relative to the buoy, is calculated.
Firstly we need to get the wings' depth and the buoy's pitch angle. The wing depths are obtained as in the previous example, but the buoy's pitch angle is not passed into the function, so we obtain it via a TimeHistory call.
There follows a calculation to work out the required wing angle taking into account the depth of the wing and the pitch of the towed fish. The function applied simply rotates the wing to an angle to the global horizontal that is proportional to the difference between wing depth and target depth – limited to a maximum angle.