{"id":19287044,"url":"https://github.com/williamyang98/viterbidecodercpp","last_synced_at":"2025-10-07T02:26:33.733Z","repository":{"id":65958324,"uuid":"603408258","full_name":"williamyang98/ViterbiDecoderCpp","owner":"williamyang98","description":"Viterbi decoder with vectorisation written in C++","archived":false,"fork":false,"pushed_at":"2025-02-24T15:57:12.000Z","size":227,"stargazers_count":12,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-22T05:22:09.180Z","etag":null,"topics":["convolutional-encoder","decoding-algorithm","dsp","viterbi-decoder"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/williamyang98.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-18T12:22:33.000Z","updated_at":"2025-03-17T16:14:51.000Z","dependencies_parsed_at":"2025-02-24T16:56:15.876Z","dependency_job_id":null,"html_url":"https://github.com/williamyang98/ViterbiDecoderCpp","commit_stats":null,"previous_names":["williamyang98/viterbidecodercpp"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/williamyang98/ViterbiDecoderCpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamyang98%2FViterbiDecoderCpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamyang98%2FViterbiDecoderCpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamyang98%2FViterbiDecoderCpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamyang98%2FViterbiDecoderCpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/williamyang98","download_url":"https://codeload.github.com/williamyang98/ViterbiDecoderCpp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamyang98%2FViterbiDecoderCpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278708235,"owners_count":26031976,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["convolutional-encoder","decoding-algorithm","dsp","viterbi-decoder"],"created_at":"2024-11-09T22:05:08.199Z","updated_at":"2025-10-07T02:26:33.717Z","avatar_url":"https://github.com/williamyang98.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n[![x86-windows](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/x86-windows.yml/badge.svg)](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/x86-windows.yml)\n[![x86-linux](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/x86-linux.yml/badge.svg)](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/x86-linux.yml)\n[![arm-linux](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/arm-linux.yml/badge.svg)](https://github.com/williamyang98/ViterbiDecoderCpp/actions/workflows/arm-linux.yml)\n\nThis is a C++ port of Phil Karn's Viterbi decoder which can be found [here](https://github.com/ka9q/libfec).\n\n**This is a header only library. Just copy and paste the header files to your desired location.**\n\nSee \u003ccode\u003eexamples/run_simple.cpp\u003c/code\u003e for a common usage scenario.\n\nModifications include:\n- Templated code for creating decoders of any code rate and constraint length\n- Branch table can be specified at runtime and shared between multiple decoders\n- Initial error values and renormalisation threshold can be specified\n- Vectorisation using intrinsics for arbitary constraint lengths (where possible) for significant speedups\n\nPerformance is similar to Phil Karn's original C implementation for provided decoders.\n\nHeavy templating is used for better performance. Compared to code that uses constraint length (K) and code rate (R) as runtime parameters [here](https://github.com/williamyang98/ViterbiDecoderCpp/tree/44cdd3c0a38a748a7084edeff859cf4d54ac911a), the templated version is up to 50% faster. This is because the compiler can perform more optimisations if the constraint length and code rate are known ahead of time.\n\n# Intrinsics support\nFor x86 processors AVX2 or SSE4.1 is required for vectorisation.\n\nFor arm processors aarch64 is required for vectorisation.\n\nThe following intrinsic implementations exist: \n- 16bit error metrics and soft decision values\n- 8bit error metrics and soft decision values\n\nEach vectorisaton type requires the convolution code to have a minimum constraint length (K)\n\n| Type | Width | Kmin | Speedup |\n| --- | --- | --- | --- |\n| Scalar     |       | 2 | 1x  |\n| x86 SSE4.1 | 16bit | 5 | 8x  |\n| x86 SSE4.1 | 8bit  | 6 | 16x |\n| x86 AVX2   | 16bit | 6 | 16x |\n| x86 AVX2   | 8bit  | 7 | 32x |\n| ARM Neon   | 16bit | 5 | 8x  |\n| ARM Neon   | 8bit  | 6 | 16x |\n\nBenchmarks show that the vectorised decoders have significiant speedups that can approach or supercede the theoretical values.\n\nUsing the 16bit and 8bit based intrinsics implementations as a guide you can make your own intrinsics implementation.\n\n# Additional notes\n- **Significant performance improvements can be achieved with using [offset binary](https://en.wikipedia.org/wiki/Offset_binary)**\n    - Soft decision values take the form of offset binary given by: 0 to N\n    - The branch table needs values: 0 and N\n    - Instead of performing a subtract then absolute, you can use an XOR operation which behaves like conditional negation\n    - Refer to the [original Phil Karn code](https://github.com/ka9q/libfec/blob/7c6706fb969c3f8fe6ec7778b2472762e0d88acc/viterbi615_sse2.c#L128) for this improvement\n    - Explanation with example\n        - Branch table has values: 0 or 255\n        - Soft decision values in offset binary: 0 to 255\n        - Consider a soft decision value of x\n        - If branch value is 0, XOR will return x\n        - If branch value is 255, XOR will return 255-x\n- Performance improvements can be achieved with using signed integer types\n    - Using signed integer types allows for the use of modular arithmetic instead of saturated arithmetic. This can provide a up to a 33% speed boost due to CPI decreasing from 0.5 to 0.33.\n    - Unsigned integer types are used since they increase the range of error values after renormalisation, and saturated arithmetic will prevent overflows/underflows.\n- The implementations uses template parameters and static asserts to: \n    - Check if the provided constraint length and code rate meet the vectorisation requirements\n    - Generate aligned data structures and provide the compiler more information about the decoder for better optimisation\n- Unsigned 8bit error metrics have severe limitations. These include:\n    - Limited range of soft decision values to avoid overflowing past the renormalisation threshold.\n    - Higher code rates (such as Cassini) will quickly reach the renormalisation threshold and deteriorate in accuracy. This is because the maximum error for each branch is a multiple of the code rate.\n- If you are only interested in hard decision decoding then using 8bit error metrics is the better choice\n    - Use hard decision values [-1,+1] of type int8_t\n    - Due to the small maximum branch error of 2*R we can set the renormalisation threshold to be quite high. \n    - \u003ccode\u003erenormalisation_threshold = UINT8_MAX - (2\\*R\\*10)\u003c/code\u003e\n    - This is less accurate compared to 16bit soft decision decoding but with up to 2x performance due to the usage of 8bit values.\n    - ![image](https://github.com/williamyang98/ViterbiDecoderCpp/assets/21079869/561262c5-c9fd-4245-8f4c-244fe21b69e2)\n- Depending on your usage requirements changes to the library are absolutely encouraged\n- Additionally check out Phil Karn's fine tuned assembly code [here](https://github.com/ka9q/libfec) for the best possible performance \n- This code is not considered heavily tested and your mileage may vary. Refer to ```/examples``` for applications to benchmark performance and verify accuracy.\n\n# Useful alternatives\n- [Spiral project](https://www.spiral.net/software/viterbi.html) aims to auto-generate high performance code for any input parameters\n- [ka9q/libfec](https://github.com/ka9q/libfec) is Phil Karn's original C implementation\n\n# Benchmarks\nBenchmarks are available in a separate repository located [here](https://github.com/williamyang98/ka9q_viterbi_comparison)\n\n![Symbol update perforrmance](https://raw.githubusercontent.com/williamyang98/ka9q_viterbi_comparison/refs/heads/main/docs/plot_symbol_update.png)\n![Chainback performance](https://raw.githubusercontent.com/williamyang98/ka9q_viterbi_comparison/refs/heads/main/docs/plot_chainback.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamyang98%2Fviterbidecodercpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilliamyang98%2Fviterbidecodercpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamyang98%2Fviterbidecodercpp/lists"}