
[](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/LICENSE)
[](https://pypi.org/project/static-fire-toolkit/)
[](https://semver.org/)
[](https://pypi.python.org/pypi/static-fire-toolkit)
[](https://github.com/snu-hanaro/static-fire-toolkit/issues)
[](https://github.com/snu-hanaro/static-fire-toolkit/actions?query=workflow%3ACI)
**HANARO SFT (Static-Fire Toolkit)** is an open-source command-line toolkit developed by the [Seoul National University Rocket Team **HANARO**](https://hanaro.snu.ac.kr/).
It provides a standardized workflow for processing **static-fire test data** from amateur and research solid rocket motors, focusing on **data cleaning, performance analysis, burn rate estimation, and visualization**.
While the library can be imported in Python, the **initial releases focus on the CLI interface**, making it straightforward to use as a standalone tool in test workflows.
## Features
- **CLI-based workflow** — run analysis directly from the terminal
- **Data processing** — clean and normalize raw thrust/pressure sensor logs
- **Performance metrics** — compute impulse, burn time, chamber pressure statistics
- **Burn rate estimation** — regression-based analysis for solid propellants
- **Visualization** — generate thrust/pressure plots for reports and documentation
## Requirements
- Python 3 (3.10+ required)
- Packages:
- numpy (>=2.0)
- scipy (>=1.13)
- pandas (>=2.0)
- matplotlib (>=3.10)
- openpyxl (>=3.1) # pandas read_excel engine
## Installation
From PyPI:
```bash
python3 -m pip install static-fire-toolkit
```
Or install from source:
```bash
git clone https://github.com/snu-hanaro/static-fire-toolkit.git
cd static-fire-toolkit
python3 -m pip install -e .
```
## Usage
### Required Directory Layout
```sh
root/ # run sft here (or specify this path using the --root option)
├─ global_config.xlsx # global configs
├─ config.xlsx # per-test configs
├─ data/
│ ├─ _pressure_raw/ # input pressure raw CSVs
│ └─ _thrust_raw/ # input thrust raw CSVs
├─ results/
│ ├─ burnrate/ # calculated burnrate CSVs
│ ├─ burnrate_graph/ # burnrate PNG/GIF plots
│ ├─ pressure/ # processed pressure CSVs
│ ├─ pressure_graph/ # pressure PNG plots
│ ├─ thrust/ # processed thrust CSVs
│ └─ thrust_graph/ # thrust PNG plots
└─ logs/ # logs for debugging
```
All commands assume the root contains `data/_pressure_raw`, `data/_thrust_raw`, and `config.xlsx`.
### CLI
Basic workflow (see `examples/` for a runnable set):
```bash
# End-to-end: thrust -> pressure -> burnrate
sft [--root <path>] process [--expt <expt_file_name>] # e.g. sft --root examples process [--expt KNSB_250220]
# Stage-by-stage
sft [--root <path>] thrust [--expt <expt_file_name>] # e.g. sft --root examples thrust --expt KNSB_250220
sft [--root <path>] pressure [--expt <expt_file_name>] # e.g. sft --root examples pressure --expt KNSB_250220
sft [--root <path>] burnrate [--expt <expt_file_name>] # e.g. sft --root examples burnrate --expt KNSB_250220
```
Run `sft --help` and `sft [--root <path>] info` for more details.
#### Examples from this repo:
- Input samples: [`examples/data/_thrust_raw/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/data/_thrust_raw), [`examples/data/_pressure_raw/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/data/_pressure_raw/), [`examples/config.xlsx`](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/config.xlsx), [`examples/global_config.py`](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/global_config.py)
- Output samples: [`examples/results/thrust/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/thrust), [`examples/results/pressure/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/pressure), [`examples/results/burnrate/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/burnrate)
#### Output preview (from `examples/`):





### Global Configuration: `global_config.py`
Define runtime parameters for parsing and processing. The following load-cell parameters are device-specific and must be explicitly set:
- sensitivity_mv_per_v (mV/V)
- rated_capacity_kgf (kgf)
- gain_internal_resistance_kohm (kOhm)
- gain_offset (V)
> [!NOTE]
> **How to Convert Voltage[V] to Thrust[N]**:
> - `gain = gain_offset + (gain_internal_resistance_kohm * 1000)[Ω] / gain_resistance[Ω]`
> - `bridge_output_mv [mV] = sensitivity_mv_per_v[mV/V] * excitation_voltage[V] * thrust[N] / (rated_capacity_kgf[kgf] * g[N/kgf])`
> - `measured_output_v = (bridge_output_mv / 1000)[V] * gain`
>
> ```
> thrust[N] = (bridge_output_mv / sensitivity_mv_per_v)[V] / excitation_voltage[V] * (rated_capacity_kgf * g)[N]
> = (measured_output_v * 1000 / gain)[mV] / sensitivity_mv_per_v[mV/V] / excitation_voltage[V]
> * (rated_capacity_kgf * g)[N]
> ```
>
Optional parsing controls (fallbacks apply if unspecified):
- thrust_sep, pressure_sep (CSV delimiter, default:`,`)
- thrust_header, pressure_header (header row index or None, default: `0`)
- thrust_time_col_idx, thrust_col_idx, pressure_time_col_idx, pressure_col_idx
[Example](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/global_config.py):
```python
# ------------ Load Cell (Required) ------------
rated_capacity_kgf = 500 # 정격하중: rated capacity of load cell, kgf
sensitivity_mv_per_v = 3 # 감도: sensitivity of load cell, mV/V
gain_internal_resistance_kohm = (
49.4 # amplifier-specific internal resistor of load cell, kΩ
)
gain_offset = 1 # gain offset of load cell
# ----------- Thrust Data Processing -----------
thrust_sep = "[,\t]" # separator for thrust data, character or Regex
thrust_header = None # header for thrust data (row number or None)
thrust_time_col_idx = 0 # index of time column
thrust_col_idx = 1 # index of thrust column
# ---------- Pressure Data Processing ----------
pressure_sep = ";" # separator for pressure data, character or Regex
pressure_header = 0 # header for pressure data (row number or None)
pressure_time_col_idx = 0 # index of datetime column
pressure_col_idx = 2 # index of pressure column
# ------------ Processing Params ---------------
frequency = 100 # Sampling rate, Hz
cutoff_frequency = 30 # LPF, Hz
lowpass_order = 5 # order for low pass filter
gaussian_weak_sigma = 1.5 # sigma for weak gaussian filter
gaussian_strong_sigma = 10 # sigma for strong gaussian filter
start_criteria = 0.2 # Criteria for the starting point of a meaningful interval in thrust data processing
end_criteria = 0.1 # Criteria for the ending point of a meaningful interval in thrust data processing
```
### Per-Test Configuration: `config.xlsx`
Record one row per test; the latest row is processed by default. Required columns:
| Column | Description | Example |
| :--- | :--- | :--- |
| index | Zero-based test index | 17 |
| date | Date in YYMMDD | 250220 |
| type | Propellant type | KNSB |
| expt_file_name | Experiment base name | KNSB_250220 |
| expt_excitation_voltage [V] | DAQ excitation voltage | 11.94 |
| expt_resistance [Ohm] | DAQ potentiometer resistance | 200.4 |
| totalmass [g] | Propellant total mass | 4996.3 |
| Nozzlediameter [mm] | Throat diameter | 20 |
| Outerdiameter [mm] | Grain OD | 90 |
| Innerdiameter [mm] | Grain ID | 30 |
| singlegrainheight [mm] | Single grain height | 104.5 |
| segment | Grain count | 5 |
> [!NOTE]
> `expt_file_name` (if present) is auto-filled based on the values of date and type — do not edit. Notes/remarks are optional.
> [!NOTE]
> If your sheet uses the legacy column name `expt_input_voltage [V]` instead of `expt_excitation_voltage [V]`, it will be used automatically as a fallback.
### Data I/O Format & Processing Pipeline
- **Inputs**:
- Raw Thrust Data: CSV
- Raw Pressure Data: CSV
- **Outputs**:
- Uniform-step processed CSVs at Δt = 1/`frequency` s: `time` + `thrust [N]` or `pressure [bar]`
- PNG plots of thrust and pressure curves
- **Filtering**:
- Thrust → low-pass filter + Gaussian smoothing
- Pressure → no filter (typically smooth enough)
- **Pressure Normalization**: adjust for local vs. standard atmospheric pressure at test time
- **Config**: `config.xlsx` stores test conditions (date/nozzle/grain, etc.)
> [!NOTE]
> **File-Naming Summary**:
> - Thrust raw: `TYPE_YYMMDD_thrust_raw.csv`
> - Thrust outputs: `TYPE_YYMMDD_thrust.csv`, `TYPE_YYMMDD_thrust.png`
> - Pressure raw: `TYPE_YYMMDD_pressure_raw.csv`
> - Pressure outputs: `TYPE_YYMMDD_pressure.csv`, `TYPE_YYMMDD_pressure.png`
### Thrust Data Processing
#### Thrust raw (`data/_thrust_raw/`)
- Filename: `TYPE_YYMMDD_thrust_raw.csv` (e.g., `KNSB_250220_thrust_raw.csv`)
- Default Format: comma-separated, 2 columns, with column labels (Configurable via [`global_config.py`](#global-configuration-global_configpy))
1. time (s)
2. voltage (V) (must be 1:1 linearly convertible to thrust)
- Important: treat raw CSV as read-only. Re-saving in third-party editor such as Excel may change encoding/separators.
Example (header + excerpt):
```csv
time,voltage(V)
246.42052460007835,1.34765625
246.42483200004790,1.455078125
```
#### Pipeline
1. Read the latest test row from `config.xlsx`
2. Load and Parse the matching raw thrust CSV from `_thrust_raw/`
3. Convert voltage to thrust
4. Extract combustion window; handle spikes/outliers
5. PCHIP interpolation to Δt = 1/`frequency` s
6. Apply low-pass + Gaussian filters
7. Save processed thrust CSV → `results/thrust/TYPE_YYMMDD_thrust.csv`
8. Save thrust plot PNG → `results/thrust_graph/TYPE_YYMMDD_thrust.png`
#### Output CSV schema
| time [s] | thrust [N] |
| :--- | :--- |
| 0.00 | 2.757… |
| 0.01 | 16.772… |
| 0.02 | 32.070… |
| … | … |
### Pressure Data Processing
#### Pressure raw (`data/_pressure_raw/`)
- Filename: `TYPE_YYMMDD_pressure_raw.csv`
- Default Format: comma-separated, 2 columns, with column labels (Configurable via [`global_config.py`](#global-configuration-global_configpy))
1. Datetime ([ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format recommended, not necessarily in exactly the same format. For more details, see the [`pandas.to_datetime` documentation](https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html).)
2. Pressure (Bar)
Example (header + excerpt):
```csv
Datetime,Pressure (Bar)
2025.2.20 22:34,1.159
2025.2.20 22:34,1.132
```
#### Pipeline
1. Read the latest test row from config.xlsx
2. Load the matching raw pressure CSV from `_pressure_raw/`
3. Load the processed thrust CSV to synchronize burn window
4. PCHIP interpolation to Δt = 1/`frequency` s
5. Atmospheric correction: adjust for local vs. standard atmospheric pressure at test time
6. No filtering (pressure changes are typically smooth)
7. Save processed pressure CSV → `results/pressure/TYPE_YYMMDD_pressure.csv`
8. Save pressure plot PNG → `results/pressure_graph/TYPE_YYMMDD_pressure.png`
#### Output CSV schema
| time [s] | pressure [bar] |
| :--- | :--- |
| 0.00 | 1.447… |
| 0.01 | 1.500… |
| 0.02 | 1.560… |
| … | … |
## Troubleshooting & Best Practices
- Do not edit raw CSVs. Excel re-save can alter encoding/delimiters → corrupted data. Keep raw files read-only.
- Configure your `global_config.py` and `config.xlsx` correctly.
- “Latest row” logic. The CLI processes the most recent test by default. To reprocess an older test, update `config.xlsx` or pass `--expt`. We plan to add batch processing feature in a future release.
- Debugging order: follow the stage order — load → windowing → interpolation → filters → correction → save. Most issues are path/filename mismatches, delimiter/headers, or NaNs from partial rows.
- Reproducibility: do not overwrite raw CSVs; version `config.xlsx`; keep outputs auto-versioned by type/date in filenames.
### How to Report Issues
If you encounter a problem, please open an issue with:
- The output of `sft info --root <your-root>` (global configurations, environment, and package details)
- The corresponding logs in the `logs/` directory
- If possible, a minimal sample (subset of `data/_thrust_raw`, `data/_pressure_raw`, and `config.xlsx`) that reproduces the issue
## FAQ
### Q1. Why filter thrust but not pressure?
Thrust often contains transient spikes/noise (mechanical shocks, DAQ artifacts), so smoothing helps. Pressure changes are typically gradual; avoiding filters prevents distortion of real variations.
### Q2. What does the pressure correction do?
It compensates for the difference between local atmospheric pressure at test time and standard atmosphere, enabling apples-to-apples comparisons across sessions.
## Development
### Tools used
- Ruff: linting & formatting
- pytest: testing
- coverage: test coverage reports
- pre-commit — enforce style checks before commits
- GitHub Actions — CI/CD (matrix testing across Python 3.10–3.13)
### Local setup
```bash
# Run linting
ruff check .
ruff format .
# Run tests
pytest -q
```
### CI/CD Strategy
- Branching: trunk-based development (main protected)
- Matrix testing: Python 3.10–3.13, both latest and [minimum dependencies](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/constraints-min.txt)
- Tags:
- Signed tags by default
- Annotated tags allowed with --no-sign
- Structured tag messages including Summary / Highlights / Breaking / Fixes / Docs / Thanks / Artifacts
## Contributing
Please use Issues/PRs with templates. Recommended:
- Feature request & bug report templates
- Code style (e.g., black, ruff) & type hints
- Sample data policy (strip sensitive metadata)
## Author & Maintainers
- Author: Seoul National University Rocket Team HANARO
- Maintainer: [@yunseo-kim](https://github.com/yunseo-kim)
## License
This project is licensed under the [MIT License](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/LICENSE).
Raw data
{
"_id": null,
"home_page": null,
"name": "static-fire-toolkit",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "Yunseo Kim <yunseo@snu.ac.kr>",
"keywords": "rocket, rocketry, static-fire, thrust, pressure, data-processing, hanaro, snu",
"author": null,
"author_email": "SNU Rocket Team HANARO <hanarodrive@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/89/b4/ca75957c3d95e63eedf8468595136b09c94cdc537136f49701d8f8e7b392/static_fire_toolkit-1.0.1.tar.gz",
"platform": null,
"description": "\n\n[](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/LICENSE)\n[](https://pypi.org/project/static-fire-toolkit/)\n[](https://semver.org/)\n[](https://pypi.python.org/pypi/static-fire-toolkit)\n\n[](https://github.com/snu-hanaro/static-fire-toolkit/issues)\n[](https://github.com/snu-hanaro/static-fire-toolkit/actions?query=workflow%3ACI)\n\n**HANARO SFT (Static-Fire Toolkit)** is an open-source command-line toolkit developed by the [Seoul National University Rocket Team **HANARO**](https://hanaro.snu.ac.kr/). \nIt provides a standardized workflow for processing **static-fire test data** from amateur and research solid rocket motors, focusing on **data cleaning, performance analysis, burn rate estimation, and visualization**.\n\nWhile the library can be imported in Python, the **initial releases focus on the CLI interface**, making it straightforward to use as a standalone tool in test workflows.\n\n## Features\n- **CLI-based workflow** \u2014 run analysis directly from the terminal \n- **Data processing** \u2014 clean and normalize raw thrust/pressure sensor logs \n- **Performance metrics** \u2014 compute impulse, burn time, chamber pressure statistics \n- **Burn rate estimation** \u2014 regression-based analysis for solid propellants \n- **Visualization** \u2014 generate thrust/pressure plots for reports and documentation \n\n## Requirements\n- Python 3 (3.10+ required)\n- Packages:\n - numpy (>=2.0)\n - scipy (>=1.13)\n - pandas (>=2.0)\n - matplotlib (>=3.10)\n - openpyxl (>=3.1) # pandas read_excel engine\n\n## Installation\n\nFrom PyPI:\n\n```bash\npython3 -m pip install static-fire-toolkit\n```\n\nOr install from source:\n\n```bash\ngit clone https://github.com/snu-hanaro/static-fire-toolkit.git\ncd static-fire-toolkit\npython3 -m pip install -e .\n```\n\n## Usage\n\n### Required Directory Layout\n\n```sh\nroot/ # run sft here (or specify this path using the --root option)\n\u251c\u2500 global_config.xlsx # global configs\n\u251c\u2500 config.xlsx # per-test configs\n\u251c\u2500 data/\n\u2502 \u251c\u2500 _pressure_raw/ # input pressure raw CSVs\n\u2502 \u2514\u2500 _thrust_raw/ # input thrust raw CSVs\n\u251c\u2500 results/\n\u2502 \u251c\u2500 burnrate/ # calculated burnrate CSVs\n\u2502 \u251c\u2500 burnrate_graph/ # burnrate PNG/GIF plots\n\u2502 \u251c\u2500 pressure/ # processed pressure CSVs\n\u2502 \u251c\u2500 pressure_graph/ # pressure PNG plots\n\u2502 \u251c\u2500 thrust/ # processed thrust CSVs\n\u2502 \u2514\u2500 thrust_graph/ # thrust PNG plots\n\u2514\u2500 logs/ # logs for debugging\n```\n\nAll commands assume the root contains `data/_pressure_raw`, `data/_thrust_raw`, and `config.xlsx`.\n\n### CLI\nBasic workflow (see `examples/` for a runnable set):\n\n```bash\n# End-to-end: thrust -> pressure -> burnrate\nsft [--root <path>] process [--expt <expt_file_name>] # e.g. sft --root examples process [--expt KNSB_250220]\n\n# Stage-by-stage\nsft [--root <path>] thrust [--expt <expt_file_name>] # e.g. sft --root examples thrust --expt KNSB_250220\nsft [--root <path>] pressure [--expt <expt_file_name>] # e.g. sft --root examples pressure --expt KNSB_250220\nsft [--root <path>] burnrate [--expt <expt_file_name>] # e.g. sft --root examples burnrate --expt KNSB_250220\n```\n\nRun `sft --help` and `sft [--root <path>] info` for more details.\n\n#### Examples from this repo:\n- Input samples: [`examples/data/_thrust_raw/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/data/_thrust_raw), [`examples/data/_pressure_raw/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/data/_pressure_raw/), [`examples/config.xlsx`](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/config.xlsx), [`examples/global_config.py`](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/global_config.py)\n- Output samples: [`examples/results/thrust/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/thrust), [`examples/results/pressure/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/pressure), [`examples/results/burnrate/`](https://github.com/snu-hanaro/static-fire-toolkit/tree/main/examples/results/burnrate)\n\n#### Output preview (from `examples/`):\n\n\n\n\n\n\n\n\n\n\n\n### Global Configuration: `global_config.py`\n\nDefine runtime parameters for parsing and processing. The following load-cell parameters are device-specific and must be explicitly set:\n\n- sensitivity_mv_per_v (mV/V)\n- rated_capacity_kgf (kgf)\n- gain_internal_resistance_kohm (kOhm)\n- gain_offset (V)\n\n> [!NOTE]\n> **How to Convert Voltage[V] to Thrust[N]**: \n> - `gain = gain_offset + (gain_internal_resistance_kohm * 1000)[\u03a9] / gain_resistance[\u03a9]`\n> - `bridge_output_mv [mV] = sensitivity_mv_per_v[mV/V] * excitation_voltage[V] * thrust[N] / (rated_capacity_kgf[kgf] * g[N/kgf])`\n> - `measured_output_v = (bridge_output_mv / 1000)[V] * gain`\n>\n> ```\n> thrust[N] = (bridge_output_mv / sensitivity_mv_per_v)[V] / excitation_voltage[V] * (rated_capacity_kgf * g)[N]\n> = (measured_output_v * 1000 / gain)[mV] / sensitivity_mv_per_v[mV/V] / excitation_voltage[V]\n> * (rated_capacity_kgf * g)[N]\n> ```\n>\n\nOptional parsing controls (fallbacks apply if unspecified):\n- thrust_sep, pressure_sep (CSV delimiter, default:`,`)\n- thrust_header, pressure_header (header row index or None, default: `0`)\n- thrust_time_col_idx, thrust_col_idx, pressure_time_col_idx, pressure_col_idx\n\n[Example](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/examples/global_config.py):\n\n```python\n# ------------ Load Cell (Required) ------------\nrated_capacity_kgf = 500 # \uc815\uaca9\ud558\uc911: rated capacity of load cell, kgf\nsensitivity_mv_per_v = 3 # \uac10\ub3c4: sensitivity of load cell, mV/V\ngain_internal_resistance_kohm = (\n 49.4 # amplifier-specific internal resistor of load cell, k\u03a9\n)\ngain_offset = 1 # gain offset of load cell\n\n# ----------- Thrust Data Processing -----------\nthrust_sep = \"[,\\t]\" # separator for thrust data, character or Regex\nthrust_header = None # header for thrust data (row number or None)\nthrust_time_col_idx = 0 # index of time column\nthrust_col_idx = 1 # index of thrust column\n\n# ---------- Pressure Data Processing ----------\npressure_sep = \";\" # separator for pressure data, character or Regex\npressure_header = 0 # header for pressure data (row number or None)\npressure_time_col_idx = 0 # index of datetime column\npressure_col_idx = 2 # index of pressure column\n\n# ------------ Processing Params ---------------\nfrequency = 100 # Sampling rate, Hz\ncutoff_frequency = 30 # LPF, Hz\nlowpass_order = 5 # order for low pass filter\ngaussian_weak_sigma = 1.5 # sigma for weak gaussian filter\ngaussian_strong_sigma = 10 # sigma for strong gaussian filter\nstart_criteria = 0.2 # Criteria for the starting point of a meaningful interval in thrust data processing\nend_criteria = 0.1 # Criteria for the ending point of a meaningful interval in thrust data processing\n```\n\n### Per-Test Configuration: `config.xlsx`\nRecord one row per test; the latest row is processed by default. Required columns:\n\n| Column | Description | Example |\n| :--- | :--- | :--- |\n| index | Zero-based test index\t| 17 |\n| date | Date in YYMMDD | 250220 |\n| type | Propellant type | KNSB |\n| expt_file_name | Experiment base name | KNSB_250220 |\n| expt_excitation_voltage [V] | DAQ excitation voltage | 11.94 |\n| expt_resistance [Ohm] | DAQ potentiometer resistance | 200.4 |\n| totalmass [g] | Propellant total mass | 4996.3 |\n| Nozzlediameter [mm] | Throat diameter | 20 |\n| Outerdiameter [mm] | Grain OD | 90 |\n| Innerdiameter [mm] | Grain ID | 30 |\n| singlegrainheight [mm] | Single grain height | 104.5 |\n| segment | Grain count | 5 |\n\n> [!NOTE]\n> `expt_file_name` (if present) is auto-filled based on the values of date and type \u2014 do not edit. Notes/remarks are optional.\n\n> [!NOTE]\n> If your sheet uses the legacy column name `expt_input_voltage [V]` instead of `expt_excitation_voltage [V]`, it will be used automatically as a fallback.\n\n### Data I/O Format & Processing Pipeline\n- **Inputs**:\n - Raw Thrust Data: CSV\n - Raw Pressure Data: CSV\n- **Outputs**:\n - Uniform-step processed CSVs at \u0394t = 1/`frequency` s: `time` + `thrust [N]` or `pressure [bar]`\n - PNG plots of thrust and pressure curves\n- **Filtering**:\n - Thrust \u2192 low-pass filter + Gaussian smoothing\n - Pressure \u2192 no filter (typically smooth enough)\n- **Pressure Normalization**: adjust for local vs. standard atmospheric pressure at test time\n- **Config**: `config.xlsx` stores test conditions (date/nozzle/grain, etc.)\n\n> [!NOTE]\n> **File-Naming Summary**:\n> - Thrust raw: `TYPE_YYMMDD_thrust_raw.csv`\n> - Thrust outputs: `TYPE_YYMMDD_thrust.csv`, `TYPE_YYMMDD_thrust.png`\n> - Pressure raw: `TYPE_YYMMDD_pressure_raw.csv`\n> - Pressure outputs: `TYPE_YYMMDD_pressure.csv`, `TYPE_YYMMDD_pressure.png`\n\n### Thrust Data Processing\n#### Thrust raw (`data/_thrust_raw/`)\n- Filename: `TYPE_YYMMDD_thrust_raw.csv` (e.g., `KNSB_250220_thrust_raw.csv`)\n- Default Format: comma-separated, 2 columns, with column labels (Configurable via [`global_config.py`](#global-configuration-global_configpy))\n\t1. time (s)\n 2. voltage (V) (must be 1:1 linearly convertible to thrust)\n- Important: treat raw CSV as read-only. Re-saving in third-party editor such as Excel may change encoding/separators.\n\nExample (header + excerpt):\n\n```csv\ntime,voltage(V)\n246.42052460007835,1.34765625\n246.42483200004790,1.455078125\n```\n\n#### Pipeline\n1. Read the latest test row from `config.xlsx`\n2. Load and Parse the matching raw thrust CSV from `_thrust_raw/`\n3. Convert voltage to thrust\n4. Extract combustion window; handle spikes/outliers\n5. PCHIP interpolation to \u0394t = 1/`frequency` s\n6. Apply low-pass + Gaussian filters\n7. Save processed thrust CSV \u2192 `results/thrust/TYPE_YYMMDD_thrust.csv`\n8. Save thrust plot PNG \u2192 `results/thrust_graph/TYPE_YYMMDD_thrust.png`\n\n#### Output CSV schema\n\n| time [s] | thrust [N] |\n| :--- | :--- |\n| 0.00 | 2.757\u2026 |\n| 0.01 | 16.772\u2026 |\n| 0.02 | 32.070\u2026 |\n| \u2026 | \u2026 |\n\n### Pressure Data Processing\n#### Pressure raw (`data/_pressure_raw/`)\n- Filename: `TYPE_YYMMDD_pressure_raw.csv`\n- Default Format: comma-separated, 2 columns, with column labels (Configurable via [`global_config.py`](#global-configuration-global_configpy))\n 1. Datetime ([ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format recommended, not necessarily in exactly the same format. For more details, see the [`pandas.to_datetime` documentation](https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html).)\n 2. Pressure (Bar)\n\nExample (header + excerpt):\n\n```csv\nDatetime,Pressure (Bar)\n2025.2.20 22:34,1.159\n2025.2.20 22:34,1.132\n```\n\n#### Pipeline\n1. Read the latest test row from config.xlsx\n2. Load the matching raw pressure CSV from `_pressure_raw/`\n3. Load the processed thrust CSV to synchronize burn window\n4. PCHIP interpolation to \u0394t = 1/`frequency` s\n5. Atmospheric correction: adjust for local vs. standard atmospheric pressure at test time\n6. No filtering (pressure changes are typically smooth)\n7. Save processed pressure CSV \u2192 `results/pressure/TYPE_YYMMDD_pressure.csv`\n8. Save pressure plot PNG \u2192 `results/pressure_graph/TYPE_YYMMDD_pressure.png`\n\n#### Output CSV schema\n\n| time [s] | pressure [bar] |\n| :--- | :--- |\n| 0.00 | 1.447\u2026 |\n| 0.01 | 1.500\u2026 |\n| 0.02 | 1.560\u2026 |\n| \u2026 | \u2026 |\n\n## Troubleshooting & Best Practices\n- Do not edit raw CSVs. Excel re-save can alter encoding/delimiters \u2192 corrupted data. Keep raw files read-only.\n- Configure your `global_config.py` and `config.xlsx` correctly.\n- \u201cLatest row\u201d logic. The CLI processes the most recent test by default. To reprocess an older test, update `config.xlsx` or pass `--expt`. We plan to add batch processing feature in a future release.\n- Debugging order: follow the stage order \u2014 load \u2192 windowing \u2192 interpolation \u2192 filters \u2192 correction \u2192 save. Most issues are path/filename mismatches, delimiter/headers, or NaNs from partial rows.\n- Reproducibility: do not overwrite raw CSVs; version `config.xlsx`; keep outputs auto-versioned by type/date in filenames.\n\n### How to Report Issues\nIf you encounter a problem, please open an issue with:\n- The output of `sft info --root <your-root>` (global configurations, environment, and package details)\n- The corresponding logs in the `logs/` directory\n- If possible, a minimal sample (subset of `data/_thrust_raw`, `data/_pressure_raw`, and `config.xlsx`) that reproduces the issue\n\n## FAQ\n### Q1. Why filter thrust but not pressure?\nThrust often contains transient spikes/noise (mechanical shocks, DAQ artifacts), so smoothing helps. Pressure changes are typically gradual; avoiding filters prevents distortion of real variations.\n\n### Q2. What does the pressure correction do?\nIt compensates for the difference between local atmospheric pressure at test time and standard atmosphere, enabling apples-to-apples comparisons across sessions.\n\n## Development\n\n### Tools used\n- Ruff: linting & formatting\n- pytest: testing\n- coverage: test coverage reports\n- pre-commit \u2014 enforce style checks before commits\n- GitHub Actions \u2014 CI/CD (matrix testing across Python 3.10\u20133.13)\n\n### Local setup\n\n```bash\n# Run linting\nruff check .\nruff format .\n\n# Run tests\npytest -q\n```\n\n### CI/CD Strategy\n- Branching: trunk-based development (main protected)\n- Matrix testing: Python 3.10\u20133.13, both latest and [minimum dependencies](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/constraints-min.txt)\n- Tags:\n - Signed tags by default\n - Annotated tags allowed with --no-sign\n - Structured tag messages including Summary / Highlights / Breaking / Fixes / Docs / Thanks / Artifacts\n\n## Contributing\n\nPlease use Issues/PRs with templates. Recommended:\n- Feature request & bug report templates\n- Code style (e.g., black, ruff) & type hints\n- Sample data policy (strip sensitive metadata)\n\n## Author & Maintainers\n- Author: Seoul National University Rocket Team HANARO\n- Maintainer: [@yunseo-kim](https://github.com/yunseo-kim)\n\n## License\n\nThis project is licensed under the [MIT License](https://github.com/snu-hanaro/static-fire-toolkit/blob/main/LICENSE).\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Command-line toolkit for static-fire test data processing and analysis",
"version": "1.0.1",
"project_urls": {
"About HANARO": "https://hanaro.snu.ac.kr/",
"Changelog": "https://github.com/snu-hanaro/static-fire-toolkit/blob/main/docs/CHANGELOG.md",
"Homepage": "https://github.com/snu-hanaro/static-fire-toolkit",
"Issues": "https://github.com/snu-hanaro/static-fire-toolkit/issues",
"Release Notes": "https://github.com/snu-hanaro/static-fire-toolkit/releases",
"Source": "https://github.com/snu-hanaro/static-fire-toolkit"
},
"split_keywords": [
"rocket",
" rocketry",
" static-fire",
" thrust",
" pressure",
" data-processing",
" hanaro",
" snu"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "b175bfd1c28450c62a4d7362c6bc9a89b31da31097a75131a92770b5920787d4",
"md5": "b2e6f695ec5ed2c693a18accbb7d66dc",
"sha256": "2582f935cf1d2fc237e3b283e8ae577865745a14564b91a1ae8c7c7e3b1dfe2b"
},
"downloads": -1,
"filename": "static_fire_toolkit-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b2e6f695ec5ed2c693a18accbb7d66dc",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 38850,
"upload_time": "2025-09-19T15:27:28",
"upload_time_iso_8601": "2025-09-19T15:27:28.415343Z",
"url": "https://files.pythonhosted.org/packages/b1/75/bfd1c28450c62a4d7362c6bc9a89b31da31097a75131a92770b5920787d4/static_fire_toolkit-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "89b4ca75957c3d95e63eedf8468595136b09c94cdc537136f49701d8f8e7b392",
"md5": "94dc8d8967472e81116a5fdc3761827d",
"sha256": "9f8fbc3dbf18143dfa9d606fa401ca73f2379c8a119c6141a393b36b0b35cef2"
},
"downloads": -1,
"filename": "static_fire_toolkit-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "94dc8d8967472e81116a5fdc3761827d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 39998,
"upload_time": "2025-09-19T15:27:30",
"upload_time_iso_8601": "2025-09-19T15:27:30.050201Z",
"url": "https://files.pythonhosted.org/packages/89/b4/ca75957c3d95e63eedf8468595136b09c94cdc537136f49701d8f8e7b392/static_fire_toolkit-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-19 15:27:30",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "snu-hanaro",
"github_project": "static-fire-toolkit",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "static-fire-toolkit"
}