Skip to content

Commit

Permalink
Bump version; add/update docstrings; reformat README line lengths
Browse files Browse the repository at this point in the history
  • Loading branch information
halleysfifthinc committed Aug 14, 2024
1 parent 2c1a5d0 commit 4cdd7c0
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "C3D"
uuid = "de436766-0b2a-5f82-bc5b-1ccc5f599b83"
authors = ["Allen Hill <[email protected]>"]
version = "0.8.0-DEV"
version = "0.8.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
45 changes: 36 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,23 @@
[![codecov](https://codecov.io/gh/halleysfifthinc/C3D.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/halleysfifthinc/C3D.jl)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)

C3D is a common file format for motion capture and other biomechanics related measurement systems (force plate data, EMG, etc). This package completely implements the [C3D file spec](https://www.c3d.org), and can read files from all major manufacturers where they might differ from or extend the C3D file spec.

C3D.jl is exhaustively tested against sample data found on the [C3D website](https://www.c3d.org/sampledata.html) and can read many technically out-of-spec files.
C3D is a common file format for motion capture and other biomechanics related measurement
systems (force plate data, EMG, etc). This package completely implements the [C3D file
spec](https://www.c3d.org), and can read files from all major manufacturers where they might
differ from or extend the C3D file spec.

C3D.jl is exhaustively tested against sample data found on the [C3D
website](https://www.c3d.org/sampledata.html) and can read many technically out-of-spec
files.
Please open an issue if you have a file that is not being read correctly. Pull requests welcome!

## Usage

### Reading data

Marker and analog data are accessed through the `point` and `analog` fields. Note that all data is converted to Float32 upon reading, regardless of the original type (eg DEC types).
Marker and analog data are accessed through the `point` and `analog` fields. Note that all
data is converted to Float32 upon reading, regardless of the original type (eg DEC types).
(See the docstring for additional keyword arguments.)

```julia
julia> # The artifacts with the test data can only be used from the `C3D.jl` directory when `LazyArtifacts` has been loaded
Expand Down Expand Up @@ -48,24 +55,40 @@ julia> pc_real.analog["FZ1"]

### Writing data

Write a C3D file using the `writec3d` function. The groups and parameters of a .c3d file describe the data contained by the file. As of v0.8, there are no C3D.jl functions that coordinate modifying a `C3DFile` object, therefore, it is your responsibility to ensure that any modifications (adding/removing a marker or analog channel, etc) produce a internally-consistent (i.e. groups/parameters have been correctly updated to match the modified data, etc) file before writing.
Write a C3D file using the `writec3d` function. The groups and parameters of a .c3d file
describe the data contained by the file. As of v0.8, there are no C3D.jl functions that
coordinate modifying a `C3DFile` object, therefore, it is your responsibility to ensure that
any modifications (adding/removing a marker or analog channel, etc) produce a
internally-consistent (i.e. groups/parameters have been correctly updated to match the
modified data, etc) file before writing.

```julia
julia> writec3d("myfile.c3d", pc_real)
307200 # number of bytes written
```

Writing c3d files is exhaustively tested against the corpus of sample data from the C3D.org website, and `writec3d` is tested to ensure that all files that are written are functionally[^1] and/or bitwise identical to the original at the binary file level in the vast majority[^2] of cases, and in all cases, the groups, parameters, and data for a `C3DFile` that was "copied" with `writec3d` will be exactly identical to the groups, parameters, and data from the original `C3DFile`.
Writing c3d files is exhaustively tested against the corpus of sample data from the C3D.org
website, and `writec3d` is tested to ensure that all files that are written are
functionally[^1] and/or bitwise identical to the original at the binary file level in the
vast majority[^2] of cases, and in all cases, the groups, parameters, and data for a
`C3DFile` that was "copied" with `writec3d` will be exactly identical to the groups,
parameters, and data from the original `C3DFile`.

[^1]: Many manufacturers include unnecessary trailing whitespace in string parameters. C3D.jl strips trailing whitespace when reading .c3d files; this results in slightly different (smaller) parameters when written to file, but the parameter data is otherwise the same.

[^2]: There are only two situations in which the binary data in the file will differ from the original file:
[^2]: There are only two situations in which the binary data in the file will differ from the original file:
1. Some manufacturers write residuals as unsigned integers; this is incorrect according to the file-spec and C3D.jl follows the spec when writing the residuals back to file. However, the actual residual data is unchanged.
2. Limitations of [floating-point arithmetic](https://en.wikipedia.org/wiki/Floating-point_arithmetic) mean that some analog samples may not convert exactly back after un-scaling (i.e. slightly different in the file), but the scaled values are exactly identical.

#### Point residuals, invalid and calculated points

According to the C3D format documentation, invalid data points are signified by setting the residual word to `-1.0`. This convention is respected in C3D.jl by changing the residual and coordinates of invalid points/frames to `missing`. If your C3D files do not respect this convention, or if you wish to ignore this for some other reason, this behavior can be disabled by setting keyword arg `missingpoints=false` in the `readc3d` function. Convention is to signify calculated points (e.g. filtered, interpolated, etc) by setting the residual value to `0.0`.
According to the C3D format documentation, invalid data points are signified by setting the
residual word to `-1.0`. This convention is respected in C3D.jl by changing the residual and
coordinates of invalid points/frames to `missing`. If your C3D files do not respect this
convention, or if you wish to ignore this for some other reason, this behavior can be
disabled by setting keyword arg `missingpoints=false` in the `readc3d` function. Convention
is to signify calculated points (e.g. filtered, interpolated, etc) by setting the residual
value to `0.0`.

```julia
julia> bball = readc3d(artifact"sample16/basketball.c3d")
Expand Down Expand Up @@ -158,7 +181,11 @@ julia> pc_real.groups[:POINT][Int, :USED]

# Advanced: Debugging

Set the `JULIA_DEBUG` environment variable to `"C3D"` (e.g. from within Julia, `ENV["JULIA_DEBUG"] = "C3D"`) to enable debug logging. In addition, there are two keyword arguments to `readc3d` which may be useful if a file is error'ing when being read: `paramsonly=true` will only read the parameter section and skip reading the data, and `validate=false` will disable parameter validation.
Set the `JULIA_DEBUG` environment variable to `"C3D"` (e.g. from within Julia,
`ENV["JULIA_DEBUG"] = "C3D"`) to enable debug logging. In addition, there are two keyword
arguments to `readc3d` which may be useful if a file is error'ing when being read:
`paramsonly=true` will only read the parameter section and skip reading the data, and
`validate=false` will disable parameter validation.

```julia
julia> pc_real = readc3d("data/sample01/Eb015pr.c3d"; paramsonly=true)
Expand Down
5 changes: 3 additions & 2 deletions src/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,16 @@ end
Read the C3D file at `fn`.
# Keyword arguments
- `paramsonly::Bool = false`: Only reads the header and parameters
- `validateparams::Bool = true`: Validates parameters against C3D requirements
- `strip_prefixes::Bool = false`: Remove subject prefixes from labels in the `point` dictionary
- `missingpoints::Bool = true`: Sets invalid points to `missing`
- `handle_duplicate_parameters::Symbol = :keeplast`: How to handle multiple parameters in a
group with the same name. Options are:
- `:drop`: The first parameter with the duplicated name is used and the rest are dropped.
- `:keeplast`: The last parameter with the duplicated name is kept.
- `:append_position`: All duplicate parameters are kept and their position in the C3D file
is appended to their name.
- `paramsonly::Bool = false`: Only reads the header and parameters
- `validateparams::Bool = true`: Validates parameters against C3D requirements
"""
function readc3d(fn::AbstractString; paramsonly=false, validate=true,
Expand Down
9 changes: 9 additions & 0 deletions src/write.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ function edited_desc()
return string(now(UTC), " UTC by C3D.jl v", pkgversion(@__MODULE__))
end

"""
writec3d(filename::String, f::C3DFile)
writec3d(io::IO, f::C3DFile)
Write a .c3d file to disk at `filename`.
This function will add or append the write date/time and C3D.jl version to the
MANUFACTURER:EDITED parameter.
"""
function writec3d(filename::String, f::C3DFile)
open(filename, "w") do io
writec3d(io, f)
Expand Down

2 comments on commit 4cdd7c0

@halleysfifthinc
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/113098

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.8.0 -m "<description of version>" 4cdd7c0c8a4d12a02f8bd07071b4ca89ef2b4003
git push origin v0.8.0

Please sign in to comment.