Skip to content

Development guidelines

Édouard Delaire edited this page Oct 17, 2023 · 10 revisions

We try to follow a doc-driven and test-driven development approach. A good start is to write a dedicated wiki page describing the process one is about to code. It should specify at least:

  • what the process is doing
  • the reference publications if any
  • the description of input parameters
  • the description of data inputs and outputs
  • the authors

After that, unit tests can be written and put in nirstorm/test. All tests regarding one process should be put in the same file. Each unit test in this file should be as minimal as possible and involve the smallest possible data set. It is highly recommended to forge test data within unit tests (as opposed to expect data to be downloaded or provided by the user). Refer to these matlab guidelines on unit testing.

File organization

All Nistorm files should follow one of these two patterns:

  • nst_process_{module}_{process name}.m for all the Nirstorm process.
  • nst_{module}_{function name}.m for all Nirstorm functions that are not brainstorm processes

The file organized using the following organization:

  • bst_plugin -- all processes and functions to be installed into brainstorm
  • scripts -- contains stand-alone scripts (not installed), especially to reproduce tutorials
  • test -- unit tests

Inside bst_plugin, each file should be one of these 8 modules depending on its main functions :

  • io -> for input/output
  • bst* -> For database helpers, manipulating brainstorm objects
  • core* -> for core features, like format definitions, enumerations,
  • math* -> for mathematical calculations
  • preproc_* -> for preprocessing
  • glm* -> for GLM-related functions
  • ppl* -> for pipelines. Each pipeline should be accompanied by a corresponding script in scripts
  • misc* -> unsorted

Naming conventions

Code for others before coding for yourself.

Write code as if a crazy maniac will review it

  • only lower case alphanumericals and underscore for mutable variable and function names. However, we tend to comply with brainstorm coding style when brainstorm-specific code is used, eg function OutputFiles = Run(sProcess, sInput)
  • Java-style upper-case-to-start-words for class definitions, eg MyClassDefinition.m
  • upper case to indicate that a variable is a constant (should be immutable). This is typically used to define an enumeration:
my_enum.CHOICE_1 = 0
my_enum.CHOICE_2 = 1
  • we try to limit to 80 chars/line when possible
  • explicit variable and function names, eg avoid one-letter variable like w = 100, use instead weight = 100. Even for counters, it is better to use isample, ivertex than ii, j, k...

Development workflow

  1. Write a wiki page with a tentative specification of the process. It is better to ask for someone's review. This page should be hosted in the nirstorm wiki if you have access or else in your own github fork of nirstorm. The name of the page should start with '[WIP]' while it's being developed.
  2. Create a dedicated git branch (locally) with a name reflecting the feature to be implemented
  3. Write a test and put it in test/MyProcessTest.m.
  4. Write the actual process in bst_plugin/process_nst_my_process.m
  5. While it's in development stage, add the process filename to bst_plugin/MANIFEST.wip. Then it can be installed by nst_install('link', 'wip')
  6. Alternatively, you can tag this file to be part of a dedicated installation scenario by creating eg MANIFEST.my_wip_feature then it will be only installed if my_wip_feature is specified as an extra installation scenario, eg using nst_install('link', 'my_wip_feature').
  7. Push your feature branch to your upstream
  8. Once it's ready, make a pull request into the master branch of the nirstorm project on github
  9. Wait for review and merge
  10. Pull changes from origin into your local master to get the merged changes
  11. You can delete the feature branch in local and upstream repository
  12. When a process is finalized and considered stable, move its filename to the main MANIFEST file.

Brainstorm process API

Refer to this brainstorm tutorial for the basic API.

NIRS data model

Brainstorm separates the channel definition from the measured signals.

The channel definition mainly holds the sensor positions, head points and a processing history. In brainstorm, there is only one channel per sensor whereas NIRS requires several channels per sensor pair. Brainstorm is able to recognize NIRS channels using a convention for the channel name. The channel editor of brainstorm gives a good overview: channel table

Programmatically, the channel definition is stored in a channelmat structure that can be initialized by:

ChannelMat = db_template('channelmat');
ChannelMat.Channel = repmat(db_template('channeldesc'), 1, nb_channels);

You can review how NIRS data are loaded into brainstorm by checking the file in_fopen_nirs_brs.m from Brainstorm.

Unit testing