Skip to content

Commit ff30429

Browse files
Merge pull request #7 from PAMGuard/v2
Merge V2. Major update including revised (i.e. organised) folder stucture and namespaces.
2 parents 15afbe7 + 5938932 commit ff30429

1,131 files changed

Lines changed: 6998 additions & 529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.MATLABDriveTag

Lines changed: 0 additions & 1 deletion
This file was deleted.
File renamed without changes.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This workflow will automatically create a ZIP file
2+
# of the distribution files, and add them to a GitHub
3+
# release. It only runs, on creation of the GitHub
4+
# release.
5+
6+
name: Build Package
7+
8+
on:
9+
release:
10+
types: [published]
11+
12+
permissions:
13+
contents: write
14+
15+
jobs:
16+
build:
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
23+
- name: Zip code to distribute
24+
run: |
25+
zip -r "pgmatlab-${{ github.event.release.tag_name }}.zip" pgmatlab/* LICENSE README.md
26+
27+
- name: Upload code to distribute
28+
uses: actions/upload-artifact@v4
29+
with:
30+
name: dist-files
31+
path: "pgmatlab-${{ github.event.release.tag_name }}.zip"
32+
33+
- name: Append distribution code to release
34+
uses: softprops/action-gh-release@v2
35+
with:
36+
files: "pgmatlab-${{ github.event.release.tag_name }}.zip"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Build MATLAB Toolbox
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout repository
12+
uses: actions/checkout@v3
13+
14+
- name: Set MATLAB version
15+
uses: matlab-actions/[email protected]
16+
with:
17+
release: 'R2025a'
18+
19+
- name: Determine toolbox version
20+
id: version
21+
run: |
22+
# Remove leading 'v' from git tag
23+
echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
24+
25+
- name: Build toolbox
26+
run: |
27+
matlab -batch "
28+
opts = matlab.addons.toolbox.ToolboxOptions('pgmatlab_toolbox.prj');
29+
opts.ToolboxVersion = getenv('VERSION');
30+
matlab.addons.toolbox.packageToolbox(opts, sprintf('pgmatlab_%s.mltbx', getenv('VERSION')));"
31+
32+
- name: Attach to GitHub release
33+
uses: softprops/action-gh-release@v2
34+
with:
35+
files: "pgmatlab_*.mltbx"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: MATLAB Testing
2+
3+
# Controls when the workflow will run
4+
on:
5+
# Triggers the workflow on push or pull request events but only for the "main" branch
6+
push:
7+
branches: [ "v2", "main" ]
8+
pull_request:
9+
branches: [ "v2", "main" ]
10+
11+
# Allows you to run this workflow manually from the Actions tab
12+
workflow_dispatch:
13+
14+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
15+
jobs:
16+
# This workflow contains a single job called "build"
17+
build:
18+
# The type of runner that the job will run on
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
matrix:
22+
matlab-version: [r2020a, r2021a, r2022a, r2023a, r2025a]
23+
os: [ubuntu-latest, windows-latest, macos-latest]
24+
25+
26+
steps:
27+
- name: Check out repository
28+
uses: actions/checkout@v4
29+
30+
- name: Set up MATLAB
31+
uses: matlab-actions/[email protected]
32+
with:
33+
version: ${{ matrix.matlab-version }}
34+
35+
- name: Run MATLAB Tests
36+
uses: matlab-actions/[email protected]
37+
with:
38+
select-by-folder: tests

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ pgmatlab/Array/.MATLABDriveTag
1111
.MATLABDriveTag
1212
pgmatlab/.MATLABDriveTag
1313
pgmatlab/Array/.MATLABDriveTag
14+
*.mltbx

CONTRIBUTING.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Contributing Guidelines
2+
3+
PAMGuardMatlab is an open-source project. Whilst out license does not require it, we welcome you to contribute any changes you make back to the original repository.
4+
5+
## Getting Started
6+
7+
If you're planning on making contributions to PAMGuardMatlab, we highly recommend that you fork the repository, and then clone it into your machine. In the MATLAB editor, on the left sidebar, click on the 'Project' button then select [pgmatlab.prj](pgmatlab.prj) in the root of your cloned repository. This will automatically set-up the development environment, including the MATLAB path.
8+
9+
## Testing
10+
11+
There is a comprehensive testing suite located in the [tests](tests) folder. To run these tests, run the following commands.
12+
13+
```commandline
14+
cd tests;
15+
runtests;
16+
```
17+
18+
If you add new functionality to PAMGuardMatlab, please ensure you write appropriate unit tests in the testing suite.
19+
20+
> If you find that the changes you have made are failing existing tests (due to an existing bug in the program or the testing suite), you are welcome to change the testing suite.
21+
22+
## Making a Pull Request
23+
24+
Once you are satisfied with your tested changes, you should make a pull request, linking an issue and with a detailed commit history (if there is one), changelog, and details and any new tests written.
25+
26+
The GitHub repository will automatically run the unit tests in MacOS, Linux, and Windows - and you can see this by viewing your pull request.
27+
28+
## Creating a New Release
29+
30+
Stable code is maintained through new releases. This allows users to download a lightweight copy of the code without development tools such as tests.
31+
32+
Upon the creation of a release, the following CI action is executed (allow 30-60 seconds for this to complete):
33+
34+
- The user-facing code (README.md, LICENCE, pgmatlab/*) is put in an archive and attached to the release.
35+
36+
Releases should be semantically named and tagged like so. These tags are dynamically inserted in the tarball and wheel uploaded to PyPI.
37+
38+
- V1.2.3
39+
- Tag: v1.2.3
40+
- V1.2.3 Beta 1
41+
- Tag: v1.2.3-b1
42+
- V1.2.3 Alpha 1
43+
- Tag: v1.2.3-a1
44+
45+
## Structure
46+
47+
All the source code is found in the [pgmatlab/+pgmatlab](pgmatlab/+pgmatlab/) folder.
48+
49+
Folders use the plus (+) prefix to be treated as a 'package' (where [+pgmatlab](pgmatlab/+pgmatlab/) is the root). By adding [pgmatlab/](pgmatlab/) only the namespace `pgmatlab` is added to the MATLAB path. All classes and functions are accessible through sub-packages, such as: `pgmatlab.utils.millisToDateNum()`. We have temporarily kept three legacy entry points in the root source code folder to allow existing users to continue using the updated code.
50+
51+
PAMGuardMatlab has three main sub-packages:
52+
53+
1. [+core](pgmatlab/+pgmatlab/+core/): contains classes for reading chunks from data files.
54+
55+
2. [+db](pgmatlab/+pgmatlab/+db/): contains functions for interacting with the database (legacy).
56+
57+
3. [+utils](pgmatlab/+pgmatlab/+utils/): contains functions
58+
for utilities used by the rest of the library.
59+
60+
## Adding New Modules
61+
62+
The object-oriented structure of PAMGuardMatlab allows you to easily create new modules by extending the base classes. This section provides templates and instructions for creating new module types.
63+
64+
### Creating a New Module Class
65+
66+
To create a new module, you need to extend the `StandardModule` class and implement the required abstract methods.
67+
68+
#### Template for a New Module
69+
70+
Create a new file in `pgmatlab/+pgmatlab/+core/+modules/` with the following template:
71+
72+
```matlab
73+
classdef YourModuleName < pgmatlab.core.standard.StandardModule
74+
properties (Access = public)
75+
objectType = 'Your Object Type'; % Set this to match PAMGuard's object type
76+
end
77+
78+
methods
79+
function obj = YourModuleName()
80+
% Constructor - set custom header/footer classes if needed
81+
obj.header = @pgmatlab.core.standard.StandardModuleHeader;
82+
obj.footer = @pgmatlab.core.standard.StandardModuleFooter;
83+
obj.background = -1; % Set to a background class if needed
84+
end
85+
86+
function [data, selState] = readImpl(obj, fid, data, fileInfo, length, identifier, selState)
87+
% Read module-specific data from the binary file
88+
% This is where you implement the actual data reading logic
89+
90+
% Example: Read some custom fields
91+
data.customField1 = fread(fid, 1, 'int32');
92+
data.customField2 = fread(fid, 1, 'double');
93+
94+
% Additional processing can be done here
95+
96+
% Return selState (1 = keep, 0 = skip, 2 = stop if sorted)
97+
selState = 1;
98+
end
99+
100+
function [data, selState] = readBackgroundImpl(obj, fid, data, fileInfo, length, identifier, selState)
101+
% Optional: Implement background data reading if your module has background data
102+
% Leave empty if no background data
103+
end
104+
end
105+
end
106+
```
107+
108+
#### Creating Custom Header Classes
109+
110+
If your module requires a custom header format, create a class extending `StandardModuleHeader`:
111+
112+
```matlab
113+
classdef YourModuleHeader < pgmatlab.core.standard.StandardModuleHeader
114+
methods
115+
function data = readImpl(obj, fid, data, fileInfo, length, identifier)
116+
% Call parent implementation first
117+
data = [email protected](obj, fid, data, fileInfo, length, identifier);
118+
119+
% Read custom header fields
120+
data.customHeaderField = fread(fid, 1, 'int32');
121+
122+
% Process additional header data as needed
123+
end
124+
end
125+
end
126+
```
127+
128+
#### Creating Custom Footer Classes
129+
130+
Similarly, for custom footers, extend `StandardModuleFooter`:
131+
132+
```matlab
133+
classdef YourModuleFooter < pgmatlab.core.standard.StandardModuleFooter
134+
methods
135+
function data = readImpl(obj, fid, data, fileInfo, length, identifier)
136+
% Call parent implementation first
137+
data = [email protected](obj, fid, data, fileInfo, length, identifier);
138+
139+
% Read custom footer fields
140+
data.customFooterField = fread(fid, 1, 'int32');
141+
end
142+
end
143+
end
144+
```
145+
146+
#### Creating Custom Background Classes
147+
148+
For modules with background data, extend `StandardBackground`:
149+
150+
```matlab
151+
classdef YourModuleBackground < pgmatlab.core.standard.StandardBackground
152+
properties (Access = public)
153+
objectType = 'Your Background Object Type';
154+
end
155+
156+
methods
157+
function [data, selState] = readImpl(obj, fid, data, fileInfo, length, identifier, selState)
158+
% Read background-specific data
159+
data.backgroundField1 = fread(fid, 1, 'double');
160+
data.backgroundField2 = fread(fid, [1, 10], 'int16');
161+
162+
selState = 1;
163+
end
164+
end
165+
end
166+
```
167+
168+
### Registering Your Module
169+
170+
After creating your module class, you need to register it in the main loading function. Add your module to the switch statement in `loadPamguardBinaryFile.m`:
171+
172+
```matlab
173+
% In the file header case (-1) switch statement:
174+
case 'Your Module Type'
175+
switch fileInfo.fileHeader.streamName
176+
case 'Your Stream Name'
177+
moduleObj = pgmatlab.core.modules.YourModuleName();
178+
% Add additional stream cases if needed
179+
end
180+
```
181+
182+
The module type should match the string used by PAMGuard's module (found in the Java code), and the stream name should match the data stream name used by your PAMGuard module.
183+
184+
### Testing Your Module
185+
186+
1. Create test data using your PAMGuard module
187+
2. Add test cases to the appropriate test file in the `tests/` folder
188+
3. Run the tests to ensure your module loads data correctly:
189+
190+
```matlab
191+
cd tests;
192+
runtests('YourModuleTest');
193+
```
194+
195+
### Example: Complete Module Implementation
196+
197+
Here's a complete example of a simple module:
198+
199+
```matlab
200+
classdef ExampleModule < pgmatlab.core.standard.StandardModule
201+
properties (Access = public)
202+
objectType = 'Example Detection';
203+
end
204+
205+
methods
206+
function obj = ExampleModule()
207+
obj.header = @pgmatlab.core.standard.StandardModuleHeader;
208+
obj.footer = @pgmatlab.core.standard.StandardModuleFooter;
209+
obj.background = -1;
210+
end
211+
212+
function [data, selState] = readImpl(obj, fid, data, fileInfo, length, identifier, selState)
213+
% Read example-specific fields
214+
data.detectionType = fread(fid, 1, 'int32');
215+
data.confidence = fread(fid, 1, 'double');
216+
data.frequency = fread(fid, 1, 'double');
217+
218+
% Validate data
219+
if data.confidence < 0 || data.confidence > 1
220+
warning('Invalid confidence value: %f', data.confidence);
221+
end
222+
223+
selState = 1;
224+
end
225+
end
226+
end
227+
```
228+
229+
Then register it in `loadPamguardBinaryFile.m`:
230+
231+
```matlab
232+
case 'Example Detector'
233+
switch fileInfo.fileHeader.streamName
234+
case 'Example Detections'
235+
moduleObj = pgmatlab.core.modules.ExampleModule();
236+
end
237+
```

0 commit comments

Comments
 (0)