গণনাপদ্ধতি · জন্মছক ও ভাব · প্রবন্ধ ৩৭
ভাবমধ্য, ভাবসন্ধি ও ভাবচালিত: গ্রহের ভাবস্থিতি কীভাবে নির্ধারিত হয়?
রাশি চক্রে গ্রহের longitude অপরিবর্তিত থাকে, অথচ ভাবচালিত চক্রে সেই গ্রহ পাশের ভাবে চলে যেতে পারে। এর কারণ গ্রহ সরেছে নয়; ৩০°-এর সমান রাশিসীমা এবং location-time নির্ভর ভাবসীমা এক নয়। এই প্রবন্ধে house-system profile থেকে ভাবমধ্য, ভাবসন্ধি এবং planet-to-bhava assignment পর্যন্ত সম্পূর্ণ পদ্ধতি ব্যাখ্যা করা হয়েছে।
এক বাক্যে মূলনীতি: Planetary longitude দিয়ে গ্রহের রাশি নির্ধারিত হয়; নির্বাচিত house system-এর দুই sandhi-র মধ্যবর্তী circular interval দিয়ে তার ভাব নির্ধারিত হয়।
রাশি এবং ভাব একই grid নয়
নিরয়ণ রাশিচক্র ৩৬০°-কে বারোটি সমান ৩০° অংশে ভাগ করে। মেষ সবসময় ০°–৩০°, বৃষ ৩০°–৬০°—এইভাবে মীন পর্যন্ত। কিন্তু ভাব পৃথিবীর একটি নির্দিষ্ট স্থান ও মুহূর্তের horizon-meridian geometry থেকে আসে।
| বৈশিষ্ট্য | রাশি | ভাব |
|---|---|---|
| সংখ্যা | ১২ | ১২ |
| আকার | প্রতিটি ঠিক ৩০° | System অনুযায়ী সমান বা অসমান |
| শুরু কোথায় | নিরয়ণ ০° মেষ | লগ্ন/MC এবং selected system |
| Location dependence | Geocentric longitude নিজে নয় | Latitude ও longitude-নির্ভর |
| Time sensitivity | গ্রহের নিজস্ব গতির হারে | পৃথিবীর দৈনিক rotation-এর কারণে দ্রুত |
| গ্রহের ফল | রাশিস্থিতি | জীবনের ক্ষেত্র বা house placement |
ভাবচালিত তৈরি করার সময় গ্রহের nirayana longitude পরিবর্তন করা হয় না। ফলে তার রাশি, নক্ষত্র, পদ, dignity বা planetary conjunction পুনর্গণনা করার প্রশ্ন নেই। শুধু house membership আলাদা হয়।
ভাবমধ্য, ভাবসন্ধি এবং “cusp” শব্দের সমস্যা
বাংলা/সংস্কৃত ব্যবহার এবং software documentation-এ একই শব্দ সবসময় একই অর্থ বহন করে না।
- ভাবমধ্য: একটি ভাবের central/reference longitude;
- পূর্বসন্ধি: আগের ভাব শেষ হয়ে বর্তমান ভাব শুরু হওয়ার boundary;
- পরসন্ধি: বর্তমান ভাব শেষ হয়ে পরের ভাব শুরু হওয়ার boundary;
- Cusp: Western software-এ সাধারণত house-এর beginning boundary; কিছু ভারতীয় table-এ এটি bhava-madhya অর্থেও ব্যবহৃত হয়।
Implementation rule: Database বা model-এ শুধু Cusp নাম ব্যবহার করবেন না। StartSandhi, Madhya এবং EndSandhi—তিনটি field স্পষ্টভাবে রাখুন।
একই degree-কে কোনো system-এ cusp/boundary এবং অন্য calculation convention-এ madhya বলা হতে পারে। তাই UI-তে house-system code ও semantic profile না লিখলে দুই সঠিক calculation-ও আপাতদৃষ্টিতে বিরোধী মনে হবে।
প্রথমে house-system profile নির্বাচন করুন
| System | Swiss code | মূল নিয়ম | মধ্য/সীমা |
|---|---|---|---|
| Whole Sign | W | লগ্ন যে রাশিতে, সম্পূর্ণ সেই রাশি ১ম ভাব | রাশিসীমাই boundary |
| Equal | A/E | Ascendant থেকে প্রতিটি ৩০° | Ascendant ১ম house beginning |
| Porphyry | O | চার angular quadrant-কে তিন equal arc-এ ভাগ | Asc, IC, Desc, MC boundaries/cusps |
| Sripati | S | Porphyry division-এর প্রতিটি house point আগের house-এর মাঝখানে সরানো | Swiss output boundaries; Porphyry points traditional madhya হিসেবে ব্যবহারযোগ্য |
| Placidus | P | Diurnal/nocturnal time division | Unequal cusp boundaries |
“ভাবচালিত” কোনো একক universal algorithm নয়। আপনার project-এর calculation profile-এ system নির্বাচন করতে হবে। এই প্রবন্ধের worked implementation হলো traditional Sripati-style madhya ও half-distance sandhi:
- Porphyry-এর ১২ angular division point নিন;
- সেগুলোকে ১২ ভাবের madhya হিসেবে সংরক্ষণ করুন;
- দুই adjacent madhya-র forward arc-এর halfway point-কে sandhi করুন;
- প্রতিটি গ্রহ কোন start/end sandhi interval-এ পড়ছে তা নির্ণয় করুন।
Sripati-style ভাবমধ্য কীভাবে পাওয়া যায়?
চারটি angular reference:
- ১ম ভাবমধ্য = Ascendant বা Lagna;
- ৪র্থ ভাবমধ্য = IC;
- ৭ম ভাবমধ্য = Descendant;
- ১০ম ভাবমধ্য = MC.
Ascendant→IC, IC→Descendant, Descendant→MC এবং MC→Ascendant—প্রতিটি forward zodiacal quadrant-কে তিন সমান arc-এ ভাগ করলে ১২টি madhya পাওয়া যায়। এটি ecliptic longitude-এর arc division; clock hour বা ৩০° ধরে ভাগ নয়।
Δ(a,b) = normalize(b − a), যেখানে result ০° ≤ Δ < ৩৬০°
m2 = normalize(m1 + Δ(m1,m4) ÷ ৩)
m3 = normalize(m1 + ২Δ(m1,m4) ÷ ৩)
একইভাবে বাকি তিন quadrant ভাগ করা হয়। Swiss Ephemeris-এর Porphyry code O এই ১২টি division point সরাসরি দেয়; project profile অনুযায়ী এগুলোকে madhya array হিসেবে ব্যবহার করা যায়।
Sandhi formula
StartSandhi(i) = forward midpoint(Madhya(i−1), Madhya(i))
EndSandhi(i) = forward midpoint(Madhya(i), Madhya(i+1))
১২তম ও ১ম ভাবের মাঝে ৩৬০° crossing থাকায় সাধারণ arithmetic average ব্যবহার করা যাবে না। যেমন ৩৫০° ও ১০°-এর midpoint ১৮০° নয়; forward midpoint ০°।
Circular longitude-এর চারটি অপরিহার্য operation
- Normalize: যেকোনো degree-কে ০°–৩৬০° range-এ আনা;
- Forward distance: a থেকে zodiacal forward direction-এ b পর্যন্ত arc;
- Forward midpoint: ঐ arc-এর অর্ধেক;
- Wrapped interval membership: interval ০° পার হলে OR-condition ব্যবহার।
static double Normalize360(double value)
{
value %= 360.0;
return value < 0.0 ? value + 360.0 : value;
}
static double ForwardArc(double from, double to)
{
return Normalize360(to - from);
}
static double ForwardMidpoint(double from, double to)
{
return Normalize360(from + ForwardArc(from, to) / 2.0);
}
গ্রহকে কোন ভাবে বসানো হবে?
প্রতিটি ভাবকে half-open interval হিসেবে ধরুন:
Bhava(i) = [StartSandhi(i), EndSandhi(i))
অর্থাৎ start boundary অন্তর্ভুক্ত, end boundary অন্তর্ভুক্ত নয়। এর ফলে adjacent দুই ভাব কোনো degree-কে একসঙ্গে দাবি করবে না। End sandhi-র exact degree পরের ভাবে যাবে।
static bool ContainsForward(
double start, double end, double longitude)
{
start = Normalize360(start);
end = Normalize360(end);
longitude = Normalize360(longitude);
if (start < end)
return longitude >= start && longitude < end;
// Interval crosses 360°/0°.
return longitude >= start || longitude < end;
}
Rahu/Ketu retrograde হলেও membership rule বদলায় না। আপনি নির্দিষ্ট instant-এর normalized longitude দেখছেন; motion direction house lookup-এর input নয়।
গ্রহ যদি sandhi-র ঠিক ওপর থাকে?
Floating-point calculation-এ “ঠিক একই” বলা কঠিন। তিনটি policy আলাদা রাখুন:
- Machine assignment: half-open interval অনুযায়ী একটিমাত্র ভাব;
- Display warning: sandhi থেকে configurable tolerance-এর মধ্যে হলে “ভাবসন্ধির নিকট”;
- Interpretive policy: উভয় ভাবের প্রভাব বলা হবে কি না—এটি calculation নয়, interpretation layer.
উদাহরণস্বরূপ tolerance ০°০১′ রাখা যেতে পারে, কিন্তু সেটিকে hidden constant করবেন না। Output-এ sandhi_tolerance_arcmin লিখুন।
Rounding order: আগে longitude-কে degree-minute-এ round করে তারপর house assign করবেন না। Raw double দিয়ে membership নির্ণয় করে শেষে display format করুন।
C# 5 implementation
১. Models
public sealed class PlanetPoint
{
public string Key { get; set; }
public string NameBn { get; set; }
public double NirayanaLongitude { get; set; }
}
public sealed class BhavaBand
{
public int Number { get; set; }
public string NameBn { get; set; }
public double StartSandhi { get; set; }
public double Madhya { get; set; }
public double EndSandhi { get; set; }
public double Width { get; set; }
public IList<PlanetPoint> Planets { get; set; }
}
public sealed class BhavaChalitaResult
{
public string SystemKey { get; set; }
public string AyanamsaKey { get; set; }
public double NirayanaLagna { get; set; }
public double NirayanaMc { get; set; }
public IList<BhavaBand> Bhavas { get; set; }
}
২. Porphyry points-কে madhya হিসেবে নিন
static double[] CalculateSripatiMadhyas(
SwissEph swe,
double julianDayUt,
double latitude,
double longitude)
{
swe.swe_set_sid_mode(SwissEph.SE_SIDM_LAHIRI, 0.0, 0.0);
double[] porphyry = new double[13];
double[] ascmc = new double[10];
int flags = SwissEph.SEFLG_SIDEREAL;
int status = swe.swe_houses_ex(
julianDayUt, flags, latitude, longitude,
'O', porphyry, ascmc);
if (status < 0)
throw new InvalidOperationException(
"Porphyry/Sripati house calculation failed.");
double[] madhya = new double[13];
for (int i = 1; i <= 12; i++)
madhya[i] = Normalize360(porphyry[i]);
return madhya;
}
এখানে porphyry[i]-কে সরাসরি “Swiss Sripati cusp” বলা হচ্ছে না। Project-এর traditional profile-এ Porphyry angular division point-কে ভাবমধ্য এবং adjacent midpoint-কে ভাবসন্ধি হিসেবে নাম দেওয়া হয়েছে।
৩. ১২টি ভাব band তৈরি করুন
static readonly string[] BhavaNamesBn = {
"", "প্রথম", "দ্বিতীয়", "তৃতীয়", "চতুর্থ",
"পঞ্চম", "ষষ্ঠ", "সপ্তম", "অষ্টম", "নবম",
"দশম", "একাদশ", "দ্বাদশ"
};
static IList<BhavaBand> BuildBhavaBands(double[] madhya)
{
if (madhya == null || madhya.Length < 13)
throw new ArgumentException("Twelve bhava madhyas are required.");
List<BhavaBand> result = new List<BhavaBand>(12);
for (int i = 1; i <= 12; i++)
{
int previous = i == 1 ? 12 : i - 1;
int next = i == 12 ? 1 : i + 1;
double start = ForwardMidpoint(madhya[previous], madhya[i]);
double end = ForwardMidpoint(madhya[i], madhya[next]);
result.Add(new BhavaBand {
Number = i,
NameBn = BhavaNamesBn[i] + " ভাব",
StartSandhi = start,
Madhya = Normalize360(madhya[i]),
EndSandhi = end,
Width = ForwardArc(start, end),
Planets = new List<PlanetPoint>()
});
}
return result;
}
৪. Planet assignment
static BhavaBand FindBhava(
IList<BhavaBand> bhavas,
double nirayanaLongitude)
{
double value = Normalize360(nirayanaLongitude);
for (int i = 0; i < bhavas.Count; i++)
{
BhavaBand bhava = bhavas[i];
if (ContainsForward(
bhava.StartSandhi, bhava.EndSandhi, value))
return bhava;
}
throw new InvalidOperationException(
"Longitude was not covered by the twelve bhavas.");
}
static void AssignPlanets(
IList<BhavaBand> bhavas,
IEnumerable<PlanetPoint> planets)
{
foreach (PlanetPoint planet in planets)
FindBhava(bhavas, planet.NirayanaLongitude)
.Planets.Add(planet);
}
৫. Sandhi proximity
static double ShortestArc(double a, double b)
{
double d = Math.Abs(Normalize360(a) - Normalize360(b));
return Math.Min(d, 360.0 - d);
}
static bool IsNearSandhi(
BhavaBand bhava,
double longitude,
double toleranceArcMinutes)
{
double tolerance = toleranceArcMinutes / 60.0;
return ShortestArc(longitude, bhava.StartSandhi) <= tolerance ||
ShortestArc(longitude, bhava.EndSandhi) <= tolerance;
}
৬. Complete service method
public BhavaChalitaResult Calculate(
double jdUt,
double latitude,
double longitude,
IEnumerable<PlanetPoint> planets,
SwissEph swe)
{
double[] madhyas = CalculateSripatiMadhyas(
swe, jdUt, latitude, longitude);
IList<BhavaBand> bhavas = BuildBhavaBands(madhyas);
AssignPlanets(bhavas, planets);
return new BhavaChalitaResult {
SystemKey = "sripati-madhya-half-sandhi",
AyanamsaKey = "lahiri",
NirayanaLagna = madhyas[1],
NirayanaMc = madhyas[10],
Bhavas = bhavas
};
}
Swiss Ephemeris native calls thread-safe না হলে আপনার existing global lock-এর ভিতরে swe_set_sid_mode এবং swe_houses_ex দুটিই চালান। Mode set এবং calculation-এর মাঝখানে অন্য thread mode বদলে দিলে silent mismatch হতে পারে।
ConversionTools.aspx-এ table rendering
static string RenderBhavaTable(BhavaChalitaResult value)
{
StringBuilder html = new StringBuilder();
html.Append("<table class='research-table bhava-table'>")
.Append("<thead><tr>")
.Append("<th>ভাব</th>")
.Append("<th>পূর্ব সন্ধি</th>")
.Append("<th>ভাব মধ্য</th>")
.Append("<th>পর সন্ধি</th>")
.Append("<th>অবস্থিত গ্রহ</th>")
.Append("</tr></thead><tbody>");
foreach (BhavaBand b in value.Bhavas)
{
string planets = b.Planets.Count == 0
? "—"
: String.Join(", ", b.Planets.Select(x => x.NameBn));
html.Append("<tr><td><strong>")
.Append(H(b.NameBn)).Append("</strong></td><td>")
.Append(H(FormatRasiLongitude(b.StartSandhi)))
.Append("</td><td class='highlight'>")
.Append(H(FormatRasiLongitude(b.Madhya)))
.Append("</td><td>")
.Append(H(FormatRasiLongitude(b.EndSandhi)))
.Append("</td><td>").Append(H(planets))
.Append("</td></tr>");
}
return html.Append("</tbody></table>").ToString();
}
এখানে H() অবশ্যই HTML-encode করবে। Planet name XML/user data থেকে এলে raw append করবেন না। বাংলা digit conversion presentation layer-এ করুন; calculation value double-এই রাখুন।
Chart-এ কী দেখাবেন?
- রাশি চক্রে গ্রহকে তার zodiac sign cell-এ রাখুন;
- ভাবচালিত চক্রে একই planet label-কে assigned bhava cell-এ রাখুন;
- Planet longitude tooltip-এ অপরিবর্তিত রাখুন;
- Chart heading-এ system ও ayanāṃśa লিখুন;
- Sandhi-near planet-এ ছোট warning marker দিন;
- রাশি বদলেছে—এমন visual impression দেবেন না।
API ও XML schema
{
"profile": {
"house_system": "sripati-madhya-half-sandhi",
"source_points": "Swiss Porphyry O",
"ayanamsa": "Lahiri",
"sandhi_interval": "start-inclusive-end-exclusive",
"sandhi_tolerance_arcmin": 1.0
},
"bhavas": [
{
"number": 1,
"start_sandhi": 0.0,
"madhya": 0.0,
"end_sandhi": 0.0,
"planets": []
}
]
}
উপরের zero values schema placeholder; astronomical example নয়। Actual response-এ raw decimal degree এবং formatted Bengali label—দুটিই রাখা ভালো।
<bhava_chalita system="sripati-madhya-half-sandhi"
ayanamsa="lahiri"
interval="start-inclusive-end-exclusive">
<bhava number="1"
start_sandhi="..."
madhya="..."
end_sandhi="...">
<planet key="sun" longitude="..." />
</bhava>
</bhava_chalita>
Validation checklist
- House-system key required;
- Ayanāṃśa key required;
- JD must be UT;
- East longitude positive;
- Swiss sidereal mode set explicitly;
- Sidereal flag present;
- Porphyry code exactly uppercase
O; - Cusp/madhya array length ১৩;
- Indices ১–১২ used;
- All madhyas normalized ০°–৩৬০°;
- All sandhis normalized;
- ৩৫০°→১০° midpoint = ০°;
- ১০°→৩৫০° midpoint follows forward arc;
- ১২→১ wrap handled;
- Start boundary included;
- End boundary excluded;
- Every degree covered once;
- No overlapping bands;
- Sum of ১২ widths = ৩৬০°;
- No width ≤ ০°;
- ১ম madhya equals nirayana Lagna;
- ৭ম madhya opposite ১ম;
- ১০ম madhya equals selected nirayana MC;
- ৪র্থ madhya opposite ১০ম;
- Each quadrant trisected;
- Planet longitude normalized;
- Sun assigned exactly once;
- Moon assigned exactly once;
- Rahu assigned exactly once;
- Ketu assigned exactly once;
- Retrograde flag does not alter assignment;
- Planet on exact start boundary;
- Planet on exact end boundary;
- Planet near ০°;
- Planet near ৩৬০°;
- Sandhi tolerance configurable;
- Raw value used before rounding;
- Degree-minute-second carry safe;
- Rashi position unchanged after chalita;
- Nakshatra unchanged after chalita;
- Bhava chart uses assigned house;
- Table and chart agree;
- API and HTML agree;
- XML and HTML agree;
- Kolkata reference snapshot;
- Dhaka reference snapshot;
- New York reference snapshot;
- High-latitude warning;
- Native ephemeris call locked;
- Profile included in cache key;
- Swiss-version regression retained.
সাধারণ ভুল
| ভুল | ফল | সমাধান |
|---|---|---|
| সব ভাব ৩০° ধরে নেওয়া | Unequal system হারিয়ে যায় | Selected system-এর boundaries |
cusp-কে না বুঝে madhya label | অর্ধ-house shift | Semantic profile লিখুন |
| ৩৫০° ও ১০°-এর average ১৮০° | Wrap-around failure | Forward midpoint |
| End boundary দুই ভাবে include | Duplicate planet | Half-open interval |
| Tropical planet বনাম sidereal bhava | Reference mismatch | একই Lahiri profile |
| Bhava Chalita-তে planet longitude বদল | রাশি/নক্ষত্রও ভুল বদলে যায় | শুধু house membership বদলান |
| Rounded label দিয়ে assignment | Sandhi-তে ভুল house | Raw longitude ব্যবহার |
উপসংহার
ভাবচালিত হলো নতুন planetary position calculation নয়। এটি একই nirayana longitude-কে selected house geometry-এর মধ্যে শ্রেণিবদ্ধ করা। তাই রাশি চক্র এবং ভাবচালিত চক্র পাশাপাশি দেখালে planet-এর longitude এক থাকবে, কেবল house number বদলাতে পারে।
Project-এর traditional Sripati-style profile-এ Porphyry angular division points ভাবমধ্য এবং adjacent forward midpoint ভাবসন্ধি। Start-inclusive/end-exclusive interval ব্যবহার করলে ৩৬০°-এর প্রতিটি longitude ঠিক একটি ভাবে পড়ে।
সবচেয়ে গুরুত্বপূর্ণ হলো terminology ও metadata: house-system key, ayanāṃśa, cusp semantics, boundary policy এবং tolerance প্রকাশ করুন। এতে ConversionTools.aspx-এর table, circular chart, API, XML এবং future Assamese/multilingual interface একই calculation truth ব্যবহার করবে।
তথ্যসূত্র ও আরও পাঠ
- Swiss Ephemeris—Programming Interface; house codes,
swe_houses_ex(), sidereal flag ওswe_house_pos(). - Astrodienst—Tables and descriptions of house systems; Equal, Whole Sign, Porphyry ও Sripati definitions.
- Swiss Ephemeris official
swehouse.c; house calculation reference implementation. - Swiss Ephemeris Programmer’s Documentation; input/output arrays এবং sidereal house calculation.
- সূর্য সিদ্ধান্ত পঞ্জিকা project profile; Lahiri nirayana longitude, Sripati-style madhya/sandhi table এবং Bengali Rashi/Bhava Chalita renderer.
মন্তব্য, আলোচনা ও প্রশ্ন