Metadata-Version: 2.5
Name: suns
Version: 0.1.0
Summary: Interpolate digitized optical-component loss curves and use them to correct a SUNS spectrometer/telescope calibration spectrum, per component and combined.
Project-URL: Homepage, https://github.com/guicavazzana/suns
Project-URL: Repository, https://github.com/guicavazzana/suns
Author: Guilherme Cavazzana
License: MIT
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: matplotlib>=3.5
Requires-Dist: numpy>=1.21
Requires-Dist: pandas>=1.3
Provides-Extra: test
Requires-Dist: pytest; extra == 'test'
Description-Content-Type: text/markdown

# suns

Biblioteca para corrigir um espectro de calibração do SUNS (espectrômetro/
telescópio) das perdas ópticas de cada elemento no caminho do feixe
(divisores de feixe, filtros, lentes, dicroicas, fibra, ...), cujas curvas
de transmitância/refletância foram digitalizadas de gráficos de datasheet
(ex.: com PlotDigitizer).

Publicada no PyPI como `suns` — mesmo nome pra instalar e pra importar:

```bash
pip install suns
```

```python
from suns import OpticalElement, generate_report, read_suns_spectrum
```

---

## ✅ Status: publicada e acessível de qualquer computador

**Sim, dos dois jeitos.** De **qualquer computador** com Python, pip e
acesso à internet (não precisa da pasta local, não precisa do OneDrive, não
precisa desta máquina) — no VS Code de qualquer lugar, terminal, o que for:

**PyPI** (recomendado — mais simples):

```bash
pip install suns
```

**GitHub** (código-fonte, issues, histórico): **https://github.com/guicavazzana/suns**

```bash
pip install "git+https://github.com/guicavazzana/suns.git"
```

```python
from suns import OpticalElement, generate_report, read_suns_spectrum
```

Ambos os caminhos foram testados (desinstalado localmente e reinstalado a
partir de cada um) e funcionam.

### Se quiser instalar em modo "dev" (editando o código)

```bash
git clone https://github.com/guicavazzana/suns.git
cd suns
py -3 -m pip install -e ".[test]"
```

(Nesta máquina especificamente, `-e` não funciona por causa do caminho
acentuado — ver aviso mais abaixo. Num clone limpo em outro caminho, sem
acento, `-e` funciona normalmente.)

---

## Instalar (nesta máquina, ou em qualquer outra que tenha a pasta)

Da pasta `suns_loss_calib/`:

```bash
py -3 -m pip install .
```

> **Não use `pip install -e .` nesta máquina.** A instalação editável fica
> silenciosamente quebrada aqui: o caminho da pasta contém "Dissertação"
> (com `ç`/`ã`), e o `site.py` do Windows lê o arquivo `.pth` do install
> editável usando o codepage ANSI (cp1252) em vez de UTF-8 — o caminho vem
> corrompido, `import suns` falha com `ModuleNotFoundError` e **nenhum
> erro é mostrado**. Uma instalação normal (`pip install .`, sem `-e`)
> copia os arquivos de verdade e funciona sem problema; só é preciso rodar
> de novo depois de editar o código da biblioteca. Ver `tests/conftest.py`
> para o diagnóstico completo.

Para desenvolver/rodar os testes sem precisar reinstalar a cada mudança,
os testes já resolvem isso sozinhos (veja "Testes" abaixo).

Dependências: `numpy`, `pandas`, `matplotlib` (instaladas automaticamente).

---

## Conceitos

### `OpticalElement` — um elemento óptico no caminho do feixe

```python
from suns import OpticalElement

elemento = OpticalElement(
    name="ND filter OD 1.0",       # nome (aparece em títulos, legendas e nomes de arquivo)
    path="dados/filtro_od10.csv",  # CSV digitalizado (qualquer um dos dois formatos, ver abaixo)
    kind="direct",                 # "direct" ou "reflection_loss" (ver abaixo)
    percent=True,                  # valores digitalizados estão em escala 0-100 (padrão) ou já 0-1
)
```

O parâmetro **`kind`** diz o que o valor digitalizado *significa* para a luz
que chega no SUNS:

- **`"direct"`**: o valor digitalizado já É a fração de luz que chega ao
  SUNS através desse elemento (transmissão de um filtro, transmissão de
  uma fibra, ou a refletância do lado *de trabalho* de um divisor de feixe
  — quando é o lado refletido que segue para o SUNS).
- **`"reflection_loss"`**: o valor digitalizado é uma refletância que
  *tira* luz do caminho de transmissão desejado (ex.: refletância residual
  do revestimento AR de uma lente); a transmitância é `1 - valor`.

```python
T = elemento.transmittance(wl_grid)  # np.ndarray, mesma forma de wl_grid
```

Chama `1 - warnings.warn(...)` (aviso, não erro) se o `wl_grid` pedido for
mais largo que o range digitalizado (extrapola constante nas pontas), e
outro aviso se der T fora de `[0, 1]` antes de recortar (`clip=True` por
padrão).

### `read_digitized_csv` — leitor único para os dois formatos de CSV

```python
from suns import read_digitized_csv
df = read_digitized_csv("dados/qualquer_arquivo.csv")  # -> DataFrame(wavelength, value)
```

Não precisa saber de antemão qual dos dois formatos o arquivo usa:

- **"limpo"**: primeira linha já é o cabeçalho real (`WAVELENGTH (nm),PERCENT`).
- **"bruto"** (exportação nativa do PlotDigitizer): linha de metadado entre
  aspas, linha "Date: ...", linhas em branco, uma linha solta só com a
  contagem de pontos, o cabeçalho, e então as linhas de dado — que podem
  terminar com vírgula sobrando.

O leitor trata qualquer linha cujos dois primeiros tokens (separados por
vírgula) sejam ambos conversíveis pra float como uma linha de dado
`(wavelength, value)`; todo o resto (metadado, data, contagem, cabeçalho,
linha em branco) é ignorado silenciosamente.

### `read_suns_spectrum` — carrega o espectro médio do SUNS

```python
from suns import read_suns_spectrum
suns = read_suns_spectrum("espectro_medio.csv")  # -> DataFrame(wavelength, dn)
```

### `compute_losses` — combina vários elementos

```python
from suns import compute_losses
import numpy as np

wl_grid = suns["wavelength"].values
result = compute_losses([elemento1, elemento2, ...], wl_grid)

result.per_item   # dict: nome do elemento -> np.ndarray de transmitância
result.total       # np.ndarray: produto de todas as transmitâncias (perda total do sistema)
```

### `correct_spectrum` — aplica a correção

```python
from suns import correct_spectrum
espectro_corrigido = correct_spectrum(suns["dn"].values, result.total)
```

### `generate_report` — gera tudo de uma vez (por item + combinado)

```python
from suns import generate_report

result, corrigido_total = generate_report(
    suns_wl=suns["wavelength"].values,
    suns_dn=suns["dn"].values,
    components=[elemento1, elemento2, ...],
    out_dir="resultados",
    lang="en",              # "en" ou "pt"
    xlim=(340, 820),        # opcional, range do eixo X nos gráficos
)
```

Gera em `out_dir/`:

```
resultados/
  per_item/
    <slug>_transmittance.png      # curva digitalizada, interpolada no grid do SUNS
    <slug>_suns_corrected.png     # SUNS original vs. corrigido só por ESSE elemento
  combined/
    total_loss.png                          # produto de todos os elementos
    spectrum_original_vs_corrected.png       # original vs. corrigido (tudo aplicado)
    spectrum_original_vs_corrected_log.png   # idem, escala log
    spectrum_normalized.png                  # comparação normalizada
```

(`<slug>` = nome do elemento normalizado — ex.: `"ND filter OD 1.0"` vira
`nd_filter_od_1_0`.)

---

## Exemplo completo

Veja `graficos_digitalizados/gerar_correcoes_suns.py` no repositório da
dissertação para o exemplo real e completo (6 elementos ópticos do SUNS,
com as referências ao relatório técnico que justificam cada escolha de
`kind`). Versão resumida:

```python
from pathlib import Path
from suns import OpticalElement, generate_report, read_suns_spectrum

DADOS = Path("dados")

elementos = [
    OpticalElement(name="Divisor PDOT-2", path=DADOS / "divisor.csv", kind="direct"),
    OpticalElement(name="Filtro OD 1.0", path=DADOS / "filtro_1_0.csv", kind="direct"),
    OpticalElement(name="Dubleto acromático", path=DADOS / "dubleto.csv", kind="reflection_loss"),
]

suns = read_suns_spectrum("espectro_medio.csv")

result, corrigido = generate_report(
    suns["wavelength"].values, suns["dn"].values, elementos,
    out_dir="resultados", lang="en", xlim=(340, 820),
)
```

---

## Testes

```bash
cd suns_loss_calib
py -3 -m pytest -q
```

Os testes **não dependem** de `pip install`/`pip install -e` — um
`tests/conftest.py` insere `src/` no `sys.path` diretamente, então rodam
mesmo sem instalar nada (contorna o mesmo problema de caminho acentuado
descrito acima).

---

## Referência rápida da API

| Nome | O quê |
|---|---|
| `OpticalElement(name, path, kind="direct", percent=True)` | Um elemento óptico digitalizado |
| `.transmittance(wl_grid, clip=True) -> np.ndarray` | Transmitância interpolada no grid pedido |
| `.slug() -> str` | Nome normalizado (usado em nomes de arquivo) |
| `read_digitized_csv(path) -> pd.DataFrame` | Lê um CSV digitalizado (qualquer um dos dois formatos) |
| `read_suns_spectrum(path) -> pd.DataFrame` | Lê o espectro médio do SUNS |
| `compute_losses(elements, wl_grid) -> LossResult` | Combina vários elementos (por item + total) |
| `correct_spectrum(spectrum, transmittance) -> np.ndarray` | `spectrum / transmittance` |
| `generate_report(suns_wl, suns_dn, elements, out_dir, lang="en", xlim=None)` | Gera todas as figuras (por item + combinado) |

Ver `src/suns/pipeline.py` para a orquestração completa e `tests/` para
exemplos executáveis de cada peça.
